from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import text from sqlalchemy.orm import Session from core_domain import ServiceHealth from app.application.services import RuntimeApplicationService from app.bootstrap.settings import RuntimeServiceSettings from app.db.session import get_db from app.domain.repositories import NodeRunRepository, WorkflowRunRepository from app.infrastructure.workflow_client import WorkflowServiceClient, WorkflowServiceClientError from app.schemas.run import ( NodeRunResponse, NodeRunStatusUpdateRequest, RunBootstrapResponse, RunCreateRequest, WorkflowRunResponse, WorkflowRunStatusUpdateRequest, ) router = APIRouter() def get_runtime_settings() -> RuntimeServiceSettings: return RuntimeServiceSettings() def get_runtime_application_service( db: Session = Depends(get_db), settings: RuntimeServiceSettings = Depends(get_runtime_settings), ) -> RuntimeApplicationService: return RuntimeApplicationService( workflow_run_repository=WorkflowRunRepository(db), node_run_repository=NodeRunRepository(db), workflow_client=WorkflowServiceClient(base_url=settings.workflow_service_url), ) @router.get("/health", response_model=ServiceHealth) def health_check(db: Session = Depends(get_db)) -> ServiceHealth: db.execute(text("SELECT 1")) return ServiceHealth(service="runtime-service", status="ok", database="ok") @router.post("/runs", response_model=RunBootstrapResponse) def create_run( payload: RunCreateRequest, service: RuntimeApplicationService = Depends(get_runtime_application_service), ) -> RunBootstrapResponse: try: workflow_run, initial_node = service.create_run(payload) except WorkflowServiceClientError as exc: raise HTTPException(status_code=502, detail=str(exc)) from exc return RunBootstrapResponse( run=WorkflowRunResponse.from_entity(workflow_run), initial_node=NodeRunResponse.from_entity(initial_node) if initial_node else None, ) @router.get("/runs", response_model=list[WorkflowRunResponse]) def list_runs( tenant_id: str = Query(...), session_id: str | None = Query(default=None), service: RuntimeApplicationService = Depends(get_runtime_application_service), ) -> list[WorkflowRunResponse]: return [ WorkflowRunResponse.from_entity(item) for item in service.list_runs(tenant_id=tenant_id, session_id=session_id) ] @router.get("/node-runs", response_model=list[NodeRunResponse]) def list_node_runs( tenant_id: str = Query(...), run_id: str = Query(...), service: RuntimeApplicationService = Depends(get_runtime_application_service), ) -> list[NodeRunResponse]: return [ NodeRunResponse.from_entity(item) for item in service.list_node_runs(tenant_id=tenant_id, run_id=run_id) ] @router.post("/runs/{run_id}/status", response_model=WorkflowRunResponse) def update_run_status( run_id: str, payload: WorkflowRunStatusUpdateRequest, service: RuntimeApplicationService = Depends(get_runtime_application_service), ) -> WorkflowRunResponse: entity = service.update_run_status(run_id=run_id, payload=payload) if entity is None: raise HTTPException(status_code=404, detail=f"workflow_run not found: {run_id}") return WorkflowRunResponse.from_entity(entity) @router.post("/node-runs/{node_run_id}/status", response_model=NodeRunResponse) def update_node_run_status( node_run_id: str, payload: NodeRunStatusUpdateRequest, service: RuntimeApplicationService = Depends(get_runtime_application_service), ) -> NodeRunResponse: entity = service.update_node_run_status(node_run_id=node_run_id, payload=payload) if entity is None: raise HTTPException(status_code=404, detail=f"node_run not found: {node_run_id}") return NodeRunResponse.from_entity(entity)