aps-agent/server/contracts.py

136 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ============================================================
# 跨层契约的 pydantic 运行时形态(moduleId: server-contracts, 可重生 ✅)
# 与 shared/schemas/*.json 保持一一对应(JSON Schema 为源,py/ts 为镜像)
# 任何跨层数据必须经过这里的模型校验后才能流转(plan.md §12 规范 6)
# ============================================================
from __future__ import annotations # 允许前向类型引用
from typing import Any, Literal, Optional # 类型标注工具
from pydantic import BaseModel, Field # 契约校验基座
# ---------------- 视口命令(shared/viewport_command.schema.json) ----------------
class ViewportCommand(BaseModel):
"""视口命令:驱动右侧可视化的结构化指令(§6.3)。权力等级:P0(纯视图状态)。"""
# 命令名:viewport.* 改视口状态;world.refresh 通知前端重拉世界数据
cmd: Literal[
"viewport.mode", "viewport.filter", "viewport.focus",
"viewport.highlight", "viewport.timescale", "viewport.reset",
"world.refresh",
]
# 目标对象 ID(如聚焦的产线 ID),无目标时为 None
target: Optional[str] = None
# 命令参数(按 cmd 而异,前端按约定读取)
params: dict[str, Any] = Field(default_factory=dict)
# 发起方:LLM 解析产出 / 用户手点 / 系统自动
issuedBy: Literal["LLM", "USER", "SYSTEM"] = "SYSTEM"
# ---------------- UI 块(shared/ui_block.schema.json) ----------------
class UIAction(BaseModel):
"""UI 块内的可触发动作:用户点击后回传后端(§6.2)。"""
actionId: str # 动作 ID(后端据此路由)
label: str # 按钮文案
power: Literal["P0", "P1", "P2", "P3"] # 权力等级(P2/P3 必须过门禁)
payload: dict[str, Any] = Field(default_factory=dict) # 携带参数
class UIBlock(BaseModel):
"""UI 块:后端与前端之间的渲染契约(§6.2)。M3 新增 report(报告预览 §9.10)。"""
blockId: str # 块唯一 ID
type: Literal["text", "confirm-card", "evidence", "scenario-cards", "report",
"flex-schedule", "flex-capacity", "flex-due", "flex-compare",
"plan-trace", "conflict-center",
"kpi-dashboard", "compare-table", "guidance"]
props: dict[str, Any] = Field(default_factory=dict) # 数据载荷
actions: list[UIAction] = Field(default_factory=list) # 块内动作
evidenceRefs: list[str] = Field(default_factory=list) # 关联证据 ID
# ---------------- 意图结果(shared/intent.schema.json) ----------------
# 意图名的封闭枚举:LLM 输出超出此集合即判非法并降级(M3 新增知识查询与报告)
IntentName = Literal[
"schedule.run", "schedule.publish",
"order.upsert", "order.cancel", "order.complete", "order.delete", "order.clear", # M4:订单管理(P2)
"order.submit", "order.approve", "order.reject", # OR-03:订单池审核
"order.pool", # OR-03:订单池摘要(P0)
"rush.evaluate", "rush.apply", # OR-04:紧急插单快评 / 采用
"forecast.query", "forecast.upsert", "forecast.delete", "forecast.convert", # OR-05
"plan.buckets", # PL-01:时间分桶计划
"plan.rccp", # PL-02:有限/无限粗能力对照
"plan.feasibility", # PL-03:交期可行性
"plan.inventory", # PL-04:库存投影
"plan.leveling", # PL-05:产能平衡/削峰
"plan.supply", # PL-06:产供方向决策
"order.decompose", "mrp.release", # M4:订单分解 MRP(P1)/ 建议单下达(P2)
"plan.trace", # M4:计划追溯 SO→主数据→分解→PO/WO→负荷/库存
"master.material.upsert", # 对话新建/编辑物料(P2)
"master.changeover.upsert", # MD-06:换型矩阵写入(P2)
"master.clear", # 一键清理主数据板块(P2)
"master.query", # 自然语言检索订单/物料/BOM/工艺(P0)
"changeover.query", # MD-06:换型矩阵只读
"campaign.preview", # SC-08:战役合并预览
"params.update", # OR-02:排产参数更新(P2)
"params.query", # OR-02:排产参数查询(P0)
"constraint.profile.query", # SC-04:约束剖面查询(P0)
"constraint.profile.save", # SC-04:约束剖面保存(P2)
"data.import", # 自然语言批量导入订单/物料(P2)
"import.commit", # Excel/CSV 导入入库(P2)
"flex.site.load", # 现场完整生产路线 → 替换 flex*(P2)
"flex.schedule", "flex.reschedule", "flex.swap", "flex.capacity", "flex.simulate_due",
"flex.compare", "flex.rush", "flex.fault", "flex.resource.patch", "flex.conflict.resolve",
"conflict.list", "flex.adjust.preview", "flex.adjust.commit",
"schedule.adjust.preview", "schedule.adjust.commit",
"sap.status", "sap.sync.inbound", "sap.sync.outbound",
"mes.status", "mes.dispatch", "mes.report",
"scenario.compare", # M2:多策略沙盒对比(Explore 通道 §5.1)
"scenario.sensitivity", # SC-06:敏感性分析 Tornado
"sop.compile", "sop.apply", # IND-02:SOP→约束编译 / 应用
"checkpoint.create", "checkpoint.rollback", # M2:成对快照与回滚(§4.3)
"knowledge.query", # M3:知识库检索(带出处 §8.2)
"guidance.next", # AG-07:主动引导下一步建议
"report.generate", # M3:报告生成(日报/版本对比 §9.10)
"viewport.mode", "viewport.filter", "viewport.focus",
"viewport.highlight", "viewport.timescale", "viewport.reset",
"query.kpi", "data.reset", "help", "unknown",
]
class IntentResult(BaseModel):
"""意图识别结果:两级管线(规则快路/LLM)的统一输出(§9.6)。"""
intent: IntentName # 意图名(封闭枚举)
params: dict[str, Any] = Field(default_factory=dict) # 槽位参数(策略/模式/客户名…)
confidence: float = Field(ge=0, le=1) # 置信度:<0.5 拒识,0.5-0.85 澄清,>0.85 执行
source: Literal["RULE_FAST", "LLM"] = "RULE_FAST" # 产生来源(审计与调试用)
# ---------------- 排产结果摘要(shared/schedule_result.schema.json) ----------------
class ScheduleResult(BaseModel):
"""排产结果摘要:引擎 solve() 的标准输出(§9.1),回复/审计/KPI 共用。"""
versionId: int # 版本 ID
versionNo: str # 版本号(V+日期+序号)
engineType: Literal["RULE", "CP", "GA", "HYBRID"] # 引擎类型
strategy: str # 策略模板
status: Literal["DRAFT", "PUBLISHED", "ARCHIVED"] = "DRAFT" # 版本状态
orderCount: int # 参与排产的订单项数
poCount: int # 生成生产订单数
woCount: int # 生成工单数
conflictCount: int # 冲突数
totalTardiness: float # 总延迟(小时)
avgUtilization: float # 平均利用率 0-1
totalCost: float = 0 # 预估成本
evidenceRefs: list[str] = Field(default_factory=list) # 证据引用(run-id)
# SC-03:求解器元信息(RULE 可空;CP 填 status/gap/耗时)
solveStatus: str | None = None # OPTIMAL / FEASIBLE / INFEASIBLE / MODEL_INVALID / …
solveTimeSec: float | None = None # 墙钟秒
optimalityGap: float | None = None # |(obj-bound)/obj|;最优时约 0
# ---------------- 智能体回复(SSE 载荷的聚合形态,仅后端内部使用) ----------------
class AgentReply(BaseModel):
"""一轮对话的完整产出:文本 + 视口命令 + UI 块(gateway 将其拆成 SSE 事件流)。"""
text: str # 回复正文(前端流式渲染)
commands: list[ViewportCommand] = Field(default_factory=list) # 要执行的视口命令
blocks: list[UIBlock] = Field(default_factory=list) # 附加 UI 块(如确认卡)
intent: Optional[IntentResult] = None # 本轮识别出的意图(协议证据,前端可展开)