"""Temporal workflow application service.""" from datetime import timedelta from fastapi import HTTPException, status from sqlalchemy import select from sqlalchemy.orm import selectinload from app.api.schemas.workflow import WorkflowStatusResponse, WorkflowStepRead from app.domain.enums import ServiceMode from app.infra.db.models.workflow_run import WorkflowRunORM from app.infra.temporal.client import get_temporal_client from app.infra.temporal.task_queues import IMAGE_PIPELINE_CONTROL_TASK_QUEUE from app.workers.workflows.low_end_pipeline import LowEndPipelineWorkflow from app.workers.workflows.mid_end_pipeline import MidEndPipelineWorkflow from app.workers.workflows.types import PipelineWorkflowInput, ReviewSignalPayload class WorkflowService: """Application service for Temporal workflow orchestration.""" @staticmethod def workflow_type_for_mode(service_mode: ServiceMode) -> str: """Return the workflow class name for a service mode.""" if service_mode == ServiceMode.AUTO_BASIC: return LowEndPipelineWorkflow.__name__ return MidEndPipelineWorkflow.__name__ async def start_workflow(self, workflow_input: PipelineWorkflowInput) -> None: """Start the appropriate Temporal workflow for an order.""" client = await get_temporal_client() workflow_id = f"order-{workflow_input.order_id}" workflow_callable = ( LowEndPipelineWorkflow.run if workflow_input.service_mode == ServiceMode.AUTO_BASIC else MidEndPipelineWorkflow.run ) await client.start_workflow( workflow_callable, workflow_input, id=workflow_id, task_queue=IMAGE_PIPELINE_CONTROL_TASK_QUEUE, run_timeout=timedelta(minutes=30), task_timeout=timedelta(seconds=30), ) async def signal_review(self, workflow_id: str, payload: ReviewSignalPayload) -> None: """Send a review signal to a running Temporal workflow.""" client = await get_temporal_client() handle = client.get_workflow_handle(workflow_id=workflow_id) await handle.signal("submit_review", payload) async def get_workflow_status(self, session, order_id: int) -> WorkflowStatusResponse: """Return persisted workflow execution state for an order.""" result = await session.execute( select(WorkflowRunORM) .where(WorkflowRunORM.order_id == order_id) .options(selectinload(WorkflowRunORM.steps)) ) workflow_run = result.scalar_one_or_none() if workflow_run is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Workflow not found") return WorkflowStatusResponse( order_id=workflow_run.order_id, workflow_id=workflow_run.workflow_id, workflow_type=workflow_run.workflow_type, workflow_status=workflow_run.status, current_step=workflow_run.current_step, steps=[WorkflowStepRead.model_validate(step) for step in workflow_run.steps], created_at=workflow_run.created_at, updated_at=workflow_run.updated_at, )