"""AI PMO workflow handlers and registry.""" from __future__ import annotations from typing import Optional, TYPE_CHECKING from ..models import ConversationSession, IngestEvent, Project, WorkItem from ..schemas.world_model import WorldModel from .ai_pmo_intent_routing import ( INTENT_FOLLOWUP_CONTINUE, ROUTE_ACTION_DISCOVER, ROUTE_FAMILY_FOLLOWUP, ROUTE_FAMILY_PROJECT_CREATE, ROUTE_FAMILY_PROJECT_SIGNAL, ROUTE_FAMILY_QUERY, ROUTE_FAMILY_SIGNAL_SOURCE, CanonicalIntent, WorkflowRoute, _safe_int, ) if TYPE_CHECKING: from .ai_pmo_kernel_service import AIPMOKernelService class ContextAssembler: """拼装 world model 和工作流上下文,不承担执行逻辑。""" def __init__(self, kernel: "AIPMOKernelService") -> None: self.kernel = kernel async def assemble_world_model( self, *, sender: str, message_text: str, project_hint_id: Optional[int], session: Optional[ConversationSession], ) -> tuple[Optional[Project], WorldModel]: query_tables: set[str] = set() project = await self.kernel._resolve_project( message_text=message_text, project_hint_id=project_hint_id, query_tables=query_tables, session=session, ) world_model = await self.kernel.build_world_model( tenant_id=self.kernel.tenant_id, project_id=_safe_int(getattr(project, "id", None)), sender=sender, project=project, pre_queried_tables=query_tables, ) return project, world_model class WorkflowHandler: """工作流处理器基类。""" def __init__(self, kernel: "AIPMOKernelService") -> None: self.kernel = kernel async def start( self, *, route: WorkflowRoute, intent: CanonicalIntent, message_text: str, sender: str, operator_user_id: Optional[int], source_system: str, source_message_id: str, channel_ref: Optional[str], ) -> dict[str, object]: raise NotImplementedError async def resume( self, *, route: WorkflowRoute, target_work_item: WorkItem, target_ingest: IngestEvent, followup_sender: str, operator_user_id: Optional[int], source_system: str, source_message_id: str, message_text: str, world_model: dict[str, object], ) -> tuple[dict[str, object], dict[str, object]]: return await self.kernel._resume_followup_target( target_work_item=target_work_item, target_ingest=target_ingest, followup_intent=CanonicalIntent( name=INTENT_FOLLOWUP_CONTINUE, confidence=0.9, project_id=_safe_int(target_work_item.project_id), signal_title="", signal_summary="", metadata={ "workflow_route": route.key, "workflow_family": route.family, "workflow_action": route.action, }, ), followup_sender=followup_sender, operator_user_id=operator_user_id, source_system=source_system, source_message_id=source_message_id, world_model=world_model, message_text=message_text, workflow_route=route, ) class QueryWorkflowHandler(WorkflowHandler): async def start( self, *, route: WorkflowRoute, intent: CanonicalIntent, message_text: str, sender: str, operator_user_id: Optional[int], source_system: str, source_message_id: str, channel_ref: Optional[str], ) -> dict[str, object]: _ = route return await self.kernel.executor.execute( intent=intent, message_text=message_text, sender=sender, operator_user_id=operator_user_id, source_system=source_system, source_message_id=source_message_id, channel_ref=channel_ref, ) class ProjectCreateWorkflowHandler(WorkflowHandler): async def start( self, *, route: WorkflowRoute, intent: CanonicalIntent, message_text: str, sender: str, operator_user_id: Optional[int], source_system: str, source_message_id: str, channel_ref: Optional[str], ) -> dict[str, object]: _ = route return await self.kernel.executor.execute( intent=intent, message_text=message_text, sender=sender, operator_user_id=operator_user_id, source_system=source_system, source_message_id=source_message_id, channel_ref=channel_ref, ) class SignalSourceWorkflowHandler(WorkflowHandler): async def start( self, *, route: WorkflowRoute, intent: CanonicalIntent, message_text: str, sender: str, operator_user_id: Optional[int], source_system: str, source_message_id: str, channel_ref: Optional[str], ) -> dict[str, object]: _ = route return await self.kernel.executor.execute( intent=intent, message_text=message_text, sender=sender, operator_user_id=operator_user_id, source_system=source_system, source_message_id=source_message_id, channel_ref=channel_ref, ) class ProjectSignalWorkflowHandler(WorkflowHandler): async def start( self, *, route: WorkflowRoute, intent: CanonicalIntent, message_text: str, sender: str, operator_user_id: Optional[int], source_system: str, source_message_id: str, channel_ref: Optional[str], ) -> dict[str, object]: _ = sender _ = operator_user_id if route.action == ROUTE_ACTION_DISCOVER: return await self.kernel.executor.discover_project_signal_candidates( intent=intent, message_text=message_text, source_system=source_system, source_message_id=source_message_id, channel_ref=channel_ref, ) return await self.kernel.executor.execute( intent=intent, message_text=message_text, sender=sender, operator_user_id=operator_user_id, source_system=source_system, source_message_id=source_message_id, channel_ref=channel_ref, ) class WorkflowRegistry: """按 family.action 路由处理器。""" def __init__(self, kernel: "AIPMOKernelService") -> None: self.kernel = kernel self._handlers: dict[str, WorkflowHandler] = { ROUTE_FAMILY_QUERY: QueryWorkflowHandler(kernel), ROUTE_FAMILY_PROJECT_CREATE: ProjectCreateWorkflowHandler(kernel), ROUTE_FAMILY_PROJECT_SIGNAL: ProjectSignalWorkflowHandler(kernel), ROUTE_FAMILY_SIGNAL_SOURCE: SignalSourceWorkflowHandler(kernel), ROUTE_FAMILY_FOLLOWUP: WorkflowHandler(kernel), } def get_handler(self, *, route: WorkflowRoute) -> WorkflowHandler: handler = self._handlers.get(route.family) if handler is None: return self._handlers[ROUTE_FAMILY_QUERY] return handler