112 lines
4.8 KiB
Python
112 lines
4.8 KiB
Python
"""AI PMO domain workflow adapters。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Optional
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from .signal_source_orchestration_service import SignalSourceOrchestrationService
|
|
|
|
|
|
class SignalSourceDomainWorkflow:
|
|
"""把 signal source onboarding 归一成 domain workflow 结果。"""
|
|
|
|
def __init__(self, db: AsyncSession) -> None:
|
|
self.db = db
|
|
|
|
async def run(
|
|
self,
|
|
*,
|
|
tenant_id: int,
|
|
project_id: int,
|
|
signal_id: int,
|
|
initiating_text: str,
|
|
requested_via: str,
|
|
operator_user_id: Optional[int],
|
|
) -> dict[str, Any]:
|
|
raw_result = await SignalSourceOrchestrationService(self.db).orchestrate_from_signal(
|
|
tenant_id=tenant_id,
|
|
project_id=project_id,
|
|
signal_id=signal_id,
|
|
initiating_text=initiating_text,
|
|
requested_via=requested_via,
|
|
operator_user_id=operator_user_id,
|
|
dry_run=False,
|
|
response_mode="fast",
|
|
)
|
|
normalized = self.normalize(raw_result)
|
|
normalized["raw_result"] = raw_result
|
|
return normalized
|
|
|
|
@classmethod
|
|
def normalize(cls, raw_result: dict[str, Any]) -> dict[str, Any]:
|
|
payload = raw_result if isinstance(raw_result, dict) else {}
|
|
signal_source = payload.get("signal_source") if isinstance(payload.get("signal_source"), dict) else {}
|
|
worker_executions = payload.get("worker_executions") if isinstance(payload.get("worker_executions"), list) else []
|
|
execution_payload = payload.get("execution") if isinstance(payload.get("execution"), dict) else payload.get("openclaw")
|
|
typed_actions = cls._extract_typed_actions(execution_payload)
|
|
material_actions = [
|
|
row
|
|
for row in typed_actions
|
|
if str(row.get("operation_code") or "").strip() != "notification.create"
|
|
and str(row.get("status") or "").strip() in {"applied", "planned"}
|
|
]
|
|
fallback_used = any(bool(item.get("used_fallback")) for item in worker_executions if isinstance(item, dict))
|
|
|
|
raw_status = str(payload.get("status") or "").strip().lower()
|
|
registry_status = str(signal_source.get("status") or "").strip().lower()
|
|
onboarding_status = str(signal_source.get("onboarding_status") or "").strip().lower()
|
|
execution_status = str(
|
|
((payload.get("execution") or payload.get("openclaw") or {}) if isinstance(payload, dict) else {}).get("status") or ""
|
|
).strip().lower()
|
|
|
|
if raw_status == "failed" or execution_status == "failed":
|
|
final_status = "failed_safe"
|
|
elif onboarding_status != "completed":
|
|
final_status = "pending_manual"
|
|
elif not material_actions:
|
|
final_status = "pending_manual"
|
|
elif fallback_used:
|
|
final_status = "pending_manual"
|
|
elif raw_status in {"completed", "partial", "planned"}:
|
|
final_status = "completed"
|
|
else:
|
|
final_status = "pending_manual"
|
|
|
|
next_step = "等待人工确认" if final_status != "completed" else "继续自动执行后续任务"
|
|
return {
|
|
"status": final_status,
|
|
"registry_id": signal_source.get("registry_id"),
|
|
"registry_status": registry_status,
|
|
"onboarding_status": onboarding_status,
|
|
"execution_status": execution_status,
|
|
"typed_actions": typed_actions,
|
|
"material_action_count": len(material_actions),
|
|
"worker_executions": worker_executions,
|
|
"pending_workers": payload.get("pending_workers") if isinstance(payload.get("pending_workers"), list) else [],
|
|
"next_step": next_step,
|
|
"fallback_used": fallback_used,
|
|
}
|
|
|
|
@staticmethod
|
|
def _extract_typed_actions(openclaw_payload: Any) -> list[dict[str, Any]]:
|
|
if not isinstance(openclaw_payload, dict):
|
|
return []
|
|
result = openclaw_payload.get("result") if isinstance(openclaw_payload.get("result"), dict) else openclaw_payload
|
|
execution = result.get("execution") if isinstance(result, dict) and isinstance(result.get("execution"), dict) else {}
|
|
rows = execution.get("results") if isinstance(execution.get("results"), list) else []
|
|
actions: list[dict[str, Any]] = []
|
|
for row in rows:
|
|
if not isinstance(row, dict):
|
|
continue
|
|
actions.append(
|
|
{
|
|
"operation_code": str(row.get("operation_code") or "").strip(),
|
|
"status": str(row.get("status") or "").strip(),
|
|
"entity_id": row.get("entity_id"),
|
|
"payload": row.get("payload") if isinstance(row.get("payload"), dict) else {},
|
|
}
|
|
)
|
|
return actions
|