feat(govdoc): 新增内部公文模块全链路(后端58+前端11文件)
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""规则文件的 Pydantic schema。"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Literal
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
|
||||
CheckType = Literal[
|
||||
"required", "font", "style_match", "line_spacing",
|
||||
"attachment_marker_style",
|
||||
"regex_require", "regex_forbid",
|
||||
"confused_pair", "forbid_phrase", "forbid_chars",
|
||||
"punctuation", "wenzhong_whitelist",
|
||||
"hierarchy", "cross_role", "ai",
|
||||
]
|
||||
|
||||
|
||||
class AppliesTo(BaseModel):
|
||||
role: str | None = None
|
||||
roles: list[str] | None = None
|
||||
paragraph_index: int | None = None
|
||||
|
||||
|
||||
class RuleStage(BaseModel):
|
||||
id: str | None = None
|
||||
check: CheckType
|
||||
when: str | None = None
|
||||
field: str | None = None
|
||||
expect: dict[str, Any] | None = None
|
||||
pattern: str | None = None
|
||||
chars: list[str] | None = None
|
||||
pairs: list[dict[str, Any]] | None = None
|
||||
phrases: list[str] | None = None
|
||||
rules: list[dict[str, Any]] | None = None
|
||||
expected_order: list[dict[str, Any]] | None = None
|
||||
forbid_patterns: list[str] | None = None
|
||||
prompt: str | None = None
|
||||
format: str | None = None
|
||||
|
||||
model_config = {"extra": "allow"}
|
||||
|
||||
|
||||
class Messages(BaseModel):
|
||||
pass_msg: str = Field(alias="pass", default="")
|
||||
fail: str = ""
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
class Rule(BaseModel):
|
||||
rule_id: str
|
||||
name: str
|
||||
severity: Literal["error", "warning", "info"] = "warning"
|
||||
category: str
|
||||
score: int | None = None
|
||||
# 二选一:target 通道(推荐,绑定语义实体)或 applies_to 通道(旧,按 role 选段)
|
||||
applies_to: AppliesTo | None = None
|
||||
target: str | None = None
|
||||
on_missing: Literal["pass", "warn", "fail", "skip"] = "skip"
|
||||
activate_if: str | None = None
|
||||
stages: list[RuleStage]
|
||||
messages: Messages
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_at_least_one_target(self) -> "Rule":
|
||||
if self.applies_to is None and self.target is None:
|
||||
raise ValueError(
|
||||
f"Rule {self.rule_id}: 必须声明 target 或 applies_to 之一"
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
class RuleGroup(BaseModel):
|
||||
group: str
|
||||
rules: list[Rule]
|
||||
|
||||
|
||||
# 8 个内置语义实体名(与 entity_builder.BUILTIN_ENTITY_NAMES 同步)
|
||||
_BUILTIN_ENTITY_NAMES = frozenset({
|
||||
"title", "doc_number", "recipient", "date",
|
||||
"signature", "attachments", "wenzhong", "issuer",
|
||||
})
|
||||
|
||||
|
||||
class EntitySpec(BaseModel):
|
||||
"""声明用户自定义语义实体(builtin 实体由代码自动产出,无需在 yaml 出现)。"""
|
||||
|
||||
name: str
|
||||
type: Literal["string", "number", "list"] = "string"
|
||||
description: str = ""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _no_clash_with_builtin(self) -> "EntitySpec":
|
||||
if self.name in _BUILTIN_ENTITY_NAMES:
|
||||
raise ValueError(
|
||||
f"entity '{self.name}' 与内置实体重名,"
|
||||
f"去掉该声明即可(内置实体自动产出)"
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
class ExtractSpec(BaseModel):
|
||||
entities: list[EntitySpec] = Field(default_factory=list)
|
||||
|
||||
|
||||
class RuleSetMetadata(BaseModel):
|
||||
type_id: str
|
||||
name: str
|
||||
version: str
|
||||
source: str | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class RuleSet(BaseModel):
|
||||
metadata: RuleSetMetadata
|
||||
extract: ExtractSpec = Field(default_factory=ExtractSpec)
|
||||
rules: list[RuleGroup]
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_unique_ids(self) -> "RuleSet":
|
||||
seen: set[str] = set()
|
||||
for g in self.rules:
|
||||
for r in g.rules:
|
||||
if r.rule_id in seen:
|
||||
raise ValueError(f"duplicate rule_id: {r.rule_id}")
|
||||
seen.add(r.rule_id)
|
||||
return self
|
||||
|
||||
def all_rules(self) -> list[Rule]:
|
||||
return [r for g in self.rules for r in g.rules]
|
||||
|
||||
|
||||
class FontCheck(BaseModel):
|
||||
eastasia: str | None = None
|
||||
ascii: str | None = None
|
||||
size_pt: float | None = None
|
||||
|
||||
|
||||
class RegexForbidCheck(BaseModel):
|
||||
pattern: str
|
||||
message: str | None = None
|
||||
Reference in New Issue
Block a user