接续 88dd570,保存 W20–W24 已实现的共享接口、业务入口、迁移、工作台、测试与文档。 W20/W22/W23 保持 in_progress,W21/W24 保持 verified;此提交不宣称方法或规则正式启用、多轮返修、真实角色评测完成。 W25 新增实验、标定、逐调用交付与角色执行及其迁移/测试/索引留在实施工作树,原有私人和旧实现保留项不纳入。 验证:离线 571、前端 33 通过;PG 469 项通过、2 项浏览器未启用,2 项误带入的 W25 用例已移出本提交;最终任务与交付边界 37 项通过。make 检查、最终类型、84 项资源及 diff 检查通过。未重跑浏览器或 Pi 宿主,不以合成调用认证外部模型效果。 独立整体审查四维通过;证据保存在 R2-20260909/提交W20-W24。
90 lines
3.8 KiB
Python
90 lines
3.8 KiB
Python
"""固定旧结构上的字段归一与校验;未知值进入隔离,不调用模型或联网取定义。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from jsonschema import Draft202012Validator
|
|
from jsonschema.exceptions import SchemaError
|
|
from referencing.exceptions import Unresolvable
|
|
|
|
from 建立映射 import 稳定JSON, 迁移错误
|
|
|
|
|
|
def 取一致值(值: dict, 键: tuple[str, ...], *, 默认: Any = None, 原因: str = "field_conflict"):
|
|
已有 = [值[k] for k in 键 if k in 值]
|
|
if not 已有:
|
|
return 默认
|
|
if any(稳定JSON(v) != 稳定JSON(已有[0]) for v in 已有[1:]):
|
|
raise 迁移错误(原因, "冲突键:" + "/".join(键))
|
|
return 已有[0]
|
|
|
|
|
|
def 转换动态字段(字段: Any, type_id: str, schemas: dict) -> dict:
|
|
if not isinstance(字段, dict):
|
|
raise 迁移错误("field_type_mismatch", "动态字段必须是对象")
|
|
合同 = schemas.get(type_id)
|
|
if (
|
|
not isinstance(合同, dict)
|
|
or 合同.get("type") != "object"
|
|
or 合同.get("additionalProperties") is not False
|
|
):
|
|
raise 迁移错误("schema_mapping_missing", "需要明确拒绝未知字段的固定结构")
|
|
|
|
def 检查引用(值):
|
|
if isinstance(值, dict):
|
|
for k, v in 值.items():
|
|
if k in {"$ref", "$dynamicRef"} and (
|
|
not isinstance(v, str) or not v.startswith("#")
|
|
):
|
|
raise 迁移错误("schema_invalid", "迁移不能联网获取结构定义")
|
|
检查引用(v)
|
|
elif isinstance(值, list):
|
|
for v in 值:
|
|
检查引用(v)
|
|
|
|
检查引用(合同)
|
|
try:
|
|
Draft202012Validator.check_schema(合同)
|
|
错误 = next(iter(Draft202012Validator(合同).iter_errors(字段)), None)
|
|
except (SchemaError, RecursionError, Unresolvable) as 错:
|
|
raise 迁移错误("schema_invalid", "固定结构无法解释") from 错
|
|
if 错误 is not None:
|
|
代码 = {
|
|
"additionalProperties": "unknown_fields",
|
|
"required": "missing_required",
|
|
"type": "field_type_mismatch",
|
|
}.get(错误.validator, "field_invalid")
|
|
raise 迁移错误(代码, "动态字段不符合固定结构:" + "/".join(map(str, 错误.path)))
|
|
return 字段
|
|
|
|
|
|
def 转换目标字段(内容: dict, 字段映射: Any) -> dict:
|
|
"""只做显式的一层字段映射;不执行表达式,不补默认值,不漏掉动态字段。"""
|
|
if not isinstance(字段映射, dict) or not 字段映射:
|
|
raise 迁移错误("target_schema_missing", "目标字段映射必须明确给出")
|
|
字段 = 内容.get("fields", {})
|
|
结果, 已用 = {}, set()
|
|
端点字段 = {
|
|
"source": {"甲方", "甲方draft", "甲方名称"},
|
|
"target": {"乙方", "乙方draft", "乙方名称"},
|
|
"relation_type": {"关系类型"},
|
|
}
|
|
for 目标, 源 in 字段映射.items():
|
|
if not isinstance(目标, str) or not 目标 or not isinstance(源, str):
|
|
raise 迁移错误("target_field_invalid", "字段映射需要明确的字段名和来源")
|
|
if 源.startswith("fields."):
|
|
名 = 源.removeprefix("fields.")
|
|
if 名 not in 字段:
|
|
raise 迁移错误("target_field_missing", "原行缺少被选择的动态字段")
|
|
结果[目标] = 字段[名]
|
|
已用.add(名)
|
|
elif 源 in {"name", "brief", "source", "target", "relation_type"} and 源 in 内容:
|
|
结果[目标] = 内容[源]
|
|
已用.update(端点字段.get(源, set()))
|
|
else:
|
|
raise 迁移错误("target_field_invalid", "字段来源不是已归一值或一级动态字段")
|
|
if set(字段) - 已用:
|
|
raise 迁移错误("target_field_unmapped", "有动态字段未映射,不能静默丢弃")
|
|
return 结果
|