from __future__ import annotations from typing import Any, Literal from pydantic import BaseModel, Field StewardTaskType = str StewardAssignedAgent = str StewardPlanningSource = Literal["llm_function_call", "rule_fallback"] StewardPlanNextAction = Literal["confirm_flow", "confirm_task", "delegate_task", "none"] StewardRequestedAction = Literal["preview", "save_draft", "submit"] StewardSlotDecisionSource = Literal["llm_function_call", "rule_fallback"] StewardSlotNextAction = Literal["ask_user", "render_preview"] StewardRuntimeDecisionSource = Literal["llm_function_call", "rule_fallback"] StewardRuntimeNextAction = Literal[ "plan_new_tasks", "continue_selected_flow", "submit_current_application", "continue_next_task", "fill_current_slot", "ask_user", "cancel_current_action", "no_op", ] StewardActionType = str StewardActionStatus = Literal["completed", "planned", "pending_confirmation", "blocked"] StewardActionExecutionStatus = Literal["succeeded", "blocked", "needs_confirmation", "failed"] StewardTaskStatus = Literal[ "planned", "needs_confirmation", "ready_to_delegate", "delegated", "completed", "blocked", ] StewardConfirmationStatus = Literal["pending", "confirmed", "rejected"] StewardFlowId = str StewardPendingFlowStatus = Literal["none", "pending", "confirmed", "rejected"] class StewardAttachmentInput(BaseModel): name: str = Field(description="附件原始文件名。") media_type: str = Field(default="", description="附件 MIME 类型。") ocr_summary: str = Field(default="", description="可选 OCR 摘要。") ocr_fields: dict[str, Any] = Field(default_factory=dict, description="可选 OCR 结构化字段。") class StewardPlanRequest(BaseModel): message: str = Field(description="用户在首页输入的自然语言任务。") user_id: str | None = Field(default=None, description="当前用户 ID。") client_now_iso: str | None = Field(default=None, description="客户端当前时间 ISO 字符串。") attachments: list[StewardAttachmentInput] = Field(default_factory=list, description="随本次输入上传的附件。") context_json: dict[str, Any] = Field(default_factory=dict, description="调用方上下文。") class StewardThinkingEvent(BaseModel): event_id: str = Field(description="过程摘要事件 ID。") stage: str = Field(description="阶段编码。") title: str = Field(description="面向用户展示的阶段标题。") content: str = Field(description="面向用户展示的过程摘要。") status: str = Field(default="completed", description="事件状态。") class StewardActionStep(BaseModel): step_id: str = Field(description="动作步骤 ID。") action_type: StewardActionType = Field(description="白名单动作类型。") label: str = Field(description="用户可见动作名称。") target_task_id: str = Field(default="", description="关联的小财管家任务 ID。") status: StewardActionStatus = Field(default="planned", description="动作规划状态。") requires_confirmation: bool = Field(default=False, description="执行前是否需要用户确认。") depends_on: list[str] = Field(default_factory=list, description="前置动作步骤 ID。") payload: dict[str, Any] = Field(default_factory=dict, description="动作执行需要的确定性载荷。") class StewardTask(BaseModel): task_id: str = Field(description="小财管家任务 ID。") task_type: StewardTaskType = Field(description="任务类型。") assigned_agent: StewardAssignedAgent = Field(description="建议分派的下游助手。") title: str = Field(description="任务标题。") summary: str = Field(description="任务摘要。") status: StewardTaskStatus = Field(default="needs_confirmation", description="任务状态。") confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="识别置信度。") requested_action: StewardRequestedAction = Field(default="preview", description="用户希望执行的动作:预览、保存草稿或提交。") ontology_fields: dict[str, str] = Field(default_factory=dict, description="归一化后的业务本体字段。") missing_fields: list[str] = Field(default_factory=list, description="仍缺失的本体字段。") confirmation_required: bool = Field(default=True, description="执行前是否需要用户确认。") action_steps: list[StewardActionStep] = Field(default_factory=list, description="当前任务的白名单动作步骤。") class StewardAttachmentGroup(BaseModel): group_id: str = Field(description="附件归集组 ID。") target_task_id: str | None = Field(default=None, description="建议归属的任务 ID。") scene: str = Field(description="归集场景编码。") scene_label: str = Field(description="归集场景展示名。") attachment_names: list[str] = Field(default_factory=list, description="建议纳入的附件名称。") excluded_attachment_names: list[str] = Field(default_factory=list, description="建议排除或单独处理的附件名称。") confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="归集置信度。") rationale: str = Field(default="", description="归集依据。") confirmation_required: bool = Field(default=True, description="归集前是否需要用户确认。") class StewardConfirmationAction(BaseModel): confirmation_id: str = Field(description="确认动作 ID。") action_type: str = Field(description="确认动作类型。") label: str = Field(description="确认按钮文案。") description: str = Field(default="", description="确认动作说明。") target_task_id: str | None = Field(default=None, description="关联任务 ID。") attachment_group_id: str | None = Field(default=None, description="关联附件归集组 ID。") status: StewardConfirmationStatus = Field(default="pending", description="确认状态。") payload: dict[str, Any] = Field(default_factory=dict, description="确认后继续执行所需载荷。") class StewardCandidateFlow(BaseModel): flow_id: StewardFlowId = Field(description="候选业务流程。") label: str = Field(description="用户可见候选流程名称。") confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="候选流程置信度。") reason: str = Field(default="", description="候选流程依据。") ontology_fields: dict[str, str] = Field(default_factory=dict, description="候选流程可继承的 canonical ontology 字段。") missing_fields: list[str] = Field(default_factory=list, description="候选流程仍缺失的 canonical ontology 字段。") class StewardPendingFlowConfirmation(BaseModel): status: StewardPendingFlowStatus = Field(default="none", description="候选流程确认状态。") source_message: str = Field(default="", description="触发候选流程确认的用户原始输入。") reason: str = Field(default="", description="需要确认流程方向的原因。") candidate_flows: list[StewardCandidateFlow] = Field(default_factory=list, description="候选业务流程。") class StewardPlanResponse(BaseModel): plan_id: str = Field(description="小财管家计划 ID。") plan_status: str = Field(default="needs_confirmation", description="计划状态。") planning_source: StewardPlanningSource = Field(default="rule_fallback", description="计划生成来源。") next_action: StewardPlanNextAction = Field(default="confirm_task", description="计划完成后的下一步动作。") conversation_id: str = Field(default="", description="持久化会话 ID。") steward_state: dict[str, Any] = Field(default_factory=dict, description="小财管家跨轮业务状态。") summary: str = Field(description="计划摘要。") thinking_events: list[StewardThinkingEvent] = Field(default_factory=list, description="过程摘要事件。") tasks: list[StewardTask] = Field(default_factory=list, description="拆解后的任务。") action_steps: list[StewardActionStep] = Field(default_factory=list, description="本计划的全量白名单动作步骤。") attachment_groups: list[StewardAttachmentGroup] = Field(default_factory=list, description="附件归集建议。") confirmation_groups: list[StewardConfirmationAction] = Field(default_factory=list, description="等待用户确认的动作。") pending_flow_confirmation: StewardPendingFlowConfirmation = Field( default_factory=StewardPendingFlowConfirmation, description="申请/报销流程不明确时等待用户确认的候选流程。", ) candidate_flows: list[StewardCandidateFlow] = Field(default_factory=list, description="等待用户确认的候选流程快捷列表。") model_call_traces: list[dict[str, Any]] = Field(default_factory=list, description="模型工具调用轨迹。") suggested_prompts: list[str] = Field( default_factory=list, description="当 plan_status 为 off_topic 等场景时,给用户的推荐话术示例。", ) class StewardActionExecuteRequest(BaseModel): action_type: str = Field(description="待执行的白名单动作类型。未知动作会被 executor 阻断。") message: str = Field(default="", description="触发该动作的用户原始话术。") plan_id: str = Field(default="", description="关联的小财管家计划 ID。") conversation_id: str | None = Field(default=None, description="关联会话 ID。") task: StewardTask | None = Field(default=None, description="动作所属任务快照。") action_step: StewardActionStep | None = Field(default=None, description="规划侧生成的动作步骤快照。") confirmed: bool = Field(default=False, description="用户是否已确认执行该动作。") context_json: dict[str, Any] = Field(default_factory=dict, description="前端或运行时补充上下文。") client_trace_id: str = Field(default="", description="前端幂等或追踪 ID。") class StewardActionExecuteResponse(BaseModel): action_type: str = Field(description="实际处理的动作类型。") status: StewardActionExecutionStatus = Field(description="动作执行状态。") execution_source: str = Field(default="langgraph_action", description="执行来源或兜底来源。") fallback_used: bool = Field(default=False, description="是否走了规则兜底执行。") requires_confirmation: bool = Field(default=False, description="是否仍需用户确认。") message: str = Field(description="面向用户展示的执行结果。") blocked_reasons: list[str] = Field(default_factory=list, description="阻断原因。") result_payload: dict[str, Any] = Field(default_factory=dict, description="业务服务返回的结构化结果。") trace: list[dict[str, Any]] = Field(default_factory=list, description="动作执行轨迹。") class StewardSlotOption(BaseModel): label: str = Field(description="用户可见选项文案。") value: str = Field(description="写回本体字段的选项值。") field_key: str = Field(description="对应 canonical ontology field。") description: str = Field(default="", description="选项说明。") class StewardSlotDecisionRequest(BaseModel): task_type: StewardTaskType = Field(description="当前小财管家正在推进的任务类型。") user_message: str = Field(description="用户原始话术或小财管家携带的任务上下文。") ontology_fields: dict[str, str] = Field(default_factory=dict, description="当前已抽取的 canonical ontology 字段。") missing_fields: list[str] = Field(default_factory=list, description="上游意图识别给出的 canonical 缺失字段。") task_context: dict[str, Any] = Field(default_factory=dict, description="当前任务、附件、申请预览等上下文。") class StewardSlotDecisionResponse(BaseModel): decision_source: StewardSlotDecisionSource = Field(default="rule_fallback", description="字段决策来源。") next_action: StewardSlotNextAction = Field(description="下一步应追问用户还是展示核对结果。") required_fields: list[str] = Field(default_factory=list, description="模型认为当前业务需要的 canonical 字段。") missing_fields: list[str] = Field(default_factory=list, description="当前仍缺失的 canonical 字段。") question: str = Field(default="", description="需要追问时展示给用户的问题。") options: list[StewardSlotOption] = Field(default_factory=list, description="可直接选择的补充选项。") rationale: str = Field(default="", description="面向用户的简短判断依据,不暴露推理链。") model_call_traces: list[dict[str, Any]] = Field(default_factory=list, description="模型工具调用轨迹。") class StewardRuntimeDecisionRequest(BaseModel): user_message: str = Field(description="用户当前输入。") session_type: str = Field(default="steward", description="当前前端会话类型。") runtime_state: dict[str, Any] = Field(default_factory=dict, description="小财管家运行时上下文。") context_json: dict[str, Any] = Field(default_factory=dict, description="调用方补充上下文。") class StewardRuntimeDecisionResponse(BaseModel): decision_source: StewardRuntimeDecisionSource = Field(default="rule_fallback", description="运行时决策来源。") next_action: StewardRuntimeNextAction = Field(description="小财管家下一步动作。") target_task_id: str = Field(default="", description="关联的小财管家任务 ID。") target_message_id: str = Field(default="", description="关联的前端消息 ID。") field_key: str = Field(default="", description="补字段时对应 canonical ontology field。") field_value: str = Field(default="", description="补字段时用户提供的字段值。") confirmation_required: bool = Field(default=False, description="执行该动作前是否仍需要用户二次确认。") question: str = Field(default="", description="需要追问用户时展示的问题。") response_text: str = Field(default="", description="无需调用工具时给用户的简短回复。") rationale: str = Field(default="", description="面向用户的简短判断依据,不暴露推理链。") steward_state: dict[str, Any] = Field(default_factory=dict, description="小财管家更新后的跨轮业务状态。") model_call_traces: list[dict[str, Any]] = Field(default_factory=list, description="模型工具调用轨迹。") class StewardFlowStatePatch(BaseModel): active_flow: StewardFlowId = Field(description="本轮对话正在推进的业务流程。") flow_id: StewardFlowId = Field(description="需要合并字段的目标业务流程。") intent: str = Field(default="", description="本轮识别出的业务意图。") status: str = Field(default="collecting", description="流程状态。") fields: dict[str, Any] = Field(default_factory=dict, description="待写入流程的本体字段 patch。") missing_fields: list[str] = Field(default_factory=list, description="仍缺失的 canonical ontology 字段。") application_claim_id: str = Field(default="", description="出差申请流程已生成的申请单 ID。") linked_application_claim_id: str = Field(default="", description="报销流程关联的申请单 ID。") attachments: list[dict[str, Any]] = Field(default_factory=list, description="流程关联附件摘要。") evidence: list[dict[str, Any]] = Field(default_factory=list, description="字段来源证据。")