217 lines
9.6 KiB
Python
217 lines
9.6 KiB
Python
"""AI PMO reply rendering runtime."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Optional
|
|
|
|
from .ai_pmo_intent_routing import (
|
|
INTENT_FOLLOWUP_CONFIRM,
|
|
INTENT_FOLLOWUP_CONTINUE,
|
|
INTENT_PROJECT_CREATE,
|
|
INTENT_QUERY,
|
|
INTENT_SIGNAL_CREATE,
|
|
INTENT_SIGNAL_SOURCE_ONBOARD,
|
|
ROUTE_PROJECT_SIGNAL_CANCEL,
|
|
ROUTE_PROJECT_SIGNAL_COMMIT,
|
|
ROUTE_PROJECT_SIGNAL_DISCOVER,
|
|
ROUTE_PROJECT_SIGNAL_REFRESH,
|
|
CanonicalIntent,
|
|
)
|
|
|
|
|
|
def _safe_int(value: Any) -> Optional[int]:
|
|
try:
|
|
if value is None or value == "":
|
|
return None
|
|
return int(value)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
|
|
|
|
def _normalize_signal_id_list(value: Any) -> list[int]:
|
|
ids: list[int] = []
|
|
seen: set[int] = set()
|
|
if isinstance(value, list):
|
|
items = value
|
|
else:
|
|
items = [value]
|
|
for item in items:
|
|
signal_id = _safe_int(item)
|
|
if signal_id is None or signal_id <= 0:
|
|
continue
|
|
if signal_id in seen:
|
|
continue
|
|
seen.add(signal_id)
|
|
ids.append(signal_id)
|
|
return ids
|
|
|
|
|
|
class ReplyRenderer:
|
|
"""唯一回执出口。"""
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
state_waiting_manual: str,
|
|
state_failed: str,
|
|
receipt_phase_action: str,
|
|
receipt_phase_pending: str,
|
|
receipt_phase_error: str,
|
|
) -> None:
|
|
self.state_waiting_manual = state_waiting_manual
|
|
self.state_failed = state_failed
|
|
self.receipt_phase_action = receipt_phase_action
|
|
self.receipt_phase_pending = receipt_phase_pending
|
|
self.receipt_phase_error = receipt_phase_error
|
|
|
|
def render(
|
|
self,
|
|
*,
|
|
work_state: str,
|
|
intent: CanonicalIntent,
|
|
decision_record: dict[str, Any],
|
|
execution_result: dict[str, Any],
|
|
) -> dict[str, Any]:
|
|
if work_state == self.state_waiting_manual:
|
|
if intent.name in {INTENT_FOLLOWUP_CONFIRM, INTENT_FOLLOWUP_CONTINUE}:
|
|
followup_text = str(execution_result.get("followup_reply") or "").strip()
|
|
if followup_text:
|
|
return {
|
|
"receipt_phase": self.receipt_phase_pending,
|
|
"template_key": "pending_manual",
|
|
"payload": {"intent": intent.name, "reason": followup_text},
|
|
"text": followup_text,
|
|
}
|
|
if intent.name == INTENT_SIGNAL_CREATE:
|
|
pending_text = str(execution_result.get("followup_reply") or "").strip()
|
|
if pending_text:
|
|
return {
|
|
"receipt_phase": self.receipt_phase_pending,
|
|
"template_key": "pending_manual",
|
|
"payload": {
|
|
"intent": intent.name,
|
|
"workflow_route": execution_result.get("workflow_route"),
|
|
"reason": pending_text,
|
|
},
|
|
"text": pending_text,
|
|
}
|
|
gate_message = str(((decision_record.get("approval_requirement") or {}).get("message")) or "").strip()
|
|
if not gate_message:
|
|
gate_message = "已进入待人工确认队列。"
|
|
return {
|
|
"receipt_phase": self.receipt_phase_pending,
|
|
"template_key": "pending_manual",
|
|
"payload": {"intent": intent.name, "reason": gate_message},
|
|
"text": gate_message,
|
|
}
|
|
if work_state == self.state_failed:
|
|
if intent.name in {INTENT_FOLLOWUP_CONFIRM, INTENT_FOLLOWUP_CONTINUE}:
|
|
followup_text = str(execution_result.get("followup_reply") or "").strip()
|
|
if followup_text:
|
|
return {
|
|
"receipt_phase": self.receipt_phase_error,
|
|
"template_key": "safe_error",
|
|
"payload": {"intent": intent.name, "reason": followup_text},
|
|
"text": followup_text,
|
|
}
|
|
reason = str(execution_result.get("error_message") or "系统处理中出现异常,本次未执行。").strip()
|
|
return {
|
|
"receipt_phase": self.receipt_phase_error,
|
|
"template_key": "safe_error",
|
|
"payload": {"intent": intent.name, "reason": reason},
|
|
"text": reason,
|
|
}
|
|
if intent.name in {INTENT_FOLLOWUP_CONFIRM, INTENT_FOLLOWUP_CONTINUE}:
|
|
followup_text = str(execution_result.get("followup_reply") or "已继续处理。").strip()
|
|
return {
|
|
"receipt_phase": self.receipt_phase_action,
|
|
"template_key": "action_succeeded",
|
|
"payload": {
|
|
"intent": intent.name,
|
|
"target_work_item_id": _safe_int(execution_result.get("target_work_item_id")),
|
|
"target_state": execution_result.get("target_state"),
|
|
},
|
|
"text": followup_text,
|
|
}
|
|
if intent.name == INTENT_QUERY:
|
|
answer = str(execution_result.get("answer") or "当前暂无可用回答。").strip()
|
|
return {
|
|
"receipt_phase": self.receipt_phase_action,
|
|
"template_key": "action_succeeded",
|
|
"payload": {"intent": intent.name, "answer": answer},
|
|
"text": answer,
|
|
}
|
|
if intent.name == INTENT_PROJECT_CREATE:
|
|
project = execution_result.get("project") if isinstance(execution_result.get("project"), dict) else {}
|
|
project_id = _safe_int(project.get("id"))
|
|
project_title = str(project.get("title") or project.get("name") or "新项目").strip()
|
|
return {
|
|
"receipt_phase": self.receipt_phase_action,
|
|
"template_key": "action_succeeded",
|
|
"payload": {"intent": intent.name, "project_id": project_id, "project_title": project_title},
|
|
"text": f"已立项:项目#{project_id}《{project_title}》已创建完成。" if project_id else f"已立项:《{project_title}》已创建完成。",
|
|
}
|
|
if intent.name == INTENT_SIGNAL_SOURCE_ONBOARD:
|
|
signal_id = _safe_int(execution_result.get("signal_id"))
|
|
workflow = execution_result.get("signal_source_workflow") if isinstance(execution_result.get("signal_source_workflow"), dict) else {}
|
|
status = str(workflow.get("status") or (execution_result.get("signal_source_orchestration") or {}).get("status") or "").strip()
|
|
if status == "pending_manual":
|
|
return {
|
|
"receipt_phase": self.receipt_phase_pending,
|
|
"template_key": "pending_manual",
|
|
"payload": {"intent": intent.name, "signal_id": signal_id, "status": status},
|
|
"text": f"已记录信号源接入请求,当前为待人工确认。信号#{signal_id} 已入库。" if signal_id else "已记录信号源接入请求,当前为待人工确认。",
|
|
}
|
|
return {
|
|
"receipt_phase": self.receipt_phase_action,
|
|
"template_key": "action_succeeded",
|
|
"payload": {"intent": intent.name, "signal_id": signal_id, "status": status},
|
|
"text": f"已接收入库并启动信号源接入流程。信号#{signal_id},状态:{status or 'running'}。",
|
|
}
|
|
workflow_route = str(execution_result.get("workflow_route") or "").strip().lower()
|
|
followup_text = str(execution_result.get("followup_reply") or "").strip()
|
|
if followup_text and workflow_route in {
|
|
ROUTE_PROJECT_SIGNAL_DISCOVER,
|
|
ROUTE_PROJECT_SIGNAL_COMMIT,
|
|
ROUTE_PROJECT_SIGNAL_CANCEL,
|
|
ROUTE_PROJECT_SIGNAL_REFRESH,
|
|
}:
|
|
return {
|
|
"receipt_phase": self.receipt_phase_action,
|
|
"template_key": "action_succeeded",
|
|
"payload": {
|
|
"intent": intent.name,
|
|
"workflow_route": workflow_route,
|
|
},
|
|
"text": followup_text,
|
|
}
|
|
signal_ids = _normalize_signal_id_list(execution_result.get("signal_ids"))
|
|
signal_id = _safe_int(execution_result.get("signal_id"))
|
|
if signal_id is not None and signal_id not in signal_ids:
|
|
signal_ids = [signal_id, *signal_ids]
|
|
primary_signal_id = signal_ids[0] if signal_ids else signal_id
|
|
project_id = _safe_int(execution_result.get("project_id"))
|
|
if len(signal_ids) > 1 and project_id is not None:
|
|
signal_labels = "、".join(f"#{sid}" for sid in signal_ids[:5])
|
|
text = f"已入库:已为项目#{project_id}创建{len(signal_ids)}条信号({signal_labels})。"
|
|
elif len(signal_ids) > 1:
|
|
signal_labels = "、".join(f"#{sid}" for sid in signal_ids[:5])
|
|
text = f"已入库:已创建{len(signal_ids)}条信号({signal_labels})。"
|
|
elif primary_signal_id is not None and project_id is not None:
|
|
text = f"已入库:已为项目#{project_id}创建信号#{primary_signal_id}。"
|
|
elif primary_signal_id is not None:
|
|
text = f"已入库:已创建信号#{primary_signal_id}。"
|
|
else:
|
|
text = "已收到并入库。"
|
|
return {
|
|
"receipt_phase": self.receipt_phase_action,
|
|
"template_key": "action_succeeded",
|
|
"payload": {
|
|
"intent": intent.name,
|
|
"signal_id": primary_signal_id,
|
|
"signal_ids": signal_ids,
|
|
"project_id": project_id,
|
|
},
|
|
"text": text,
|
|
}
|