from core_domain import ServiceHealth from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import text from sqlalchemy.orm import Session from app.application.services import AgentApplicationService, build_agent_application_service from app.bootstrap.settings import AgentServiceSettings from app.db.session import get_db from app.schemas.agent import ( AgentConfigCreateRequest, AgentConfigListRequest, AgentConfigResponse, AgentCreateRequest, AgentDeleteRequest, AgentDetailRequest, AgentListRequest, AgentResponse, AgentRunCreateRequest, AgentRunDetailRequest, AgentRunExecutePostRequest, AgentRunExecuteRequest, AgentRunExecuteResponse, AgentRunListRequest, AgentRunResponse, AgentRunStatusPostRequest, AgentRunStatusUpdateRequest, AgentStatusUpdateRequest, AgentStatusPostRequest, AgentToolInvocationListRequest, AgentUpdateRequest, AgentToolInvocationResponse, AgentWorkerExecuteNextRequest, AgentWorkerExecuteNextResponse, DeleteData, ) router = APIRouter() def get_agent_service_settings() -> AgentServiceSettings: return AgentServiceSettings() def get_agent_application_service( db: Session = Depends(get_db), settings: AgentServiceSettings = Depends(get_agent_service_settings)) -> AgentApplicationService: return build_agent_application_service(db=db, settings=settings) @router.get("/health", response_model=ServiceHealth) def health_check(db: Session = Depends(get_db)) -> ServiceHealth: db.execute(text("SELECT 1")) return ServiceHealth(service="agent-service", status="ok", database="ok") @router.post("", response_model=AgentResponse) def create_agent( payload: AgentCreateRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse: entity = service.create_agent(payload) return AgentResponse.from_entity(entity) @router.get("", response_model=list[AgentResponse]) def list_agents( service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentResponse]: return [AgentResponse.from_entity(item) for item in service.list_agents()] @router.post("/list", response_model=list[AgentResponse]) def list_agents_post( payload: AgentListRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentResponse]: return [AgentResponse.from_entity(item) for item in service.list_agents()] @router.post("/detail", response_model=AgentResponse) def detail_agent( payload: AgentDetailRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse: entity = service.get_agent(agent_id=payload.agent_id) if entity is None: raise HTTPException(status_code=404, detail=f"agent not found: {payload.agent_id}") return AgentResponse.from_entity(entity) @router.post("/update", response_model=AgentResponse) def update_agent( payload: AgentUpdateRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse: entity = service.update_agent(payload) if entity is None: raise HTTPException(status_code=404, detail=f"agent not found: {payload.agent_id}") return AgentResponse.from_entity(entity) @router.patch("/{agent_id}/status", response_model=AgentResponse) def update_agent_status( agent_id: str, payload: AgentStatusUpdateRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse: entity = service.update_agent_status(agent_id=agent_id, payload=payload) if entity is None: raise HTTPException(status_code=404, detail=f"agent not found: {agent_id}") return AgentResponse.from_entity(entity) @router.post("/status", response_model=AgentResponse) def update_agent_status_post( payload: AgentStatusPostRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse: entity = service.update_agent_status( agent_id=payload.agent_id, payload=AgentStatusUpdateRequest(status=payload.status)) if entity is None: raise HTTPException(status_code=404, detail=f"agent not found: {payload.agent_id}") return AgentResponse.from_entity(entity) @router.post("/delete", response_model=DeleteData) def delete_agent_post( payload: AgentDeleteRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> DeleteData: return DeleteData( deleted=service.delete_agent(agent_id=payload.agent_id), agent_id=payload.agent_id) @router.post("/configs/create", response_model=AgentConfigResponse) def create_agent_config( payload: AgentConfigCreateRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentConfigResponse: try: entity = service.create_agent_config(payload) except ValueError as exc: raise HTTPException(status_code=422, detail=str(exc)) from exc return AgentConfigResponse.from_entity(entity) @router.post("/configs/list", response_model=list[AgentConfigResponse]) def list_agent_configs( payload: AgentConfigListRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentConfigResponse]: return [ AgentConfigResponse.from_entity(item) for item in service.list_agent_configs(agent_id=payload.agent_id) ] @router.post("/runs", response_model=AgentRunResponse) def create_agent_run( payload: AgentRunCreateRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse: try: entity = service.create_agent_run(payload) except ValueError as exc: raise HTTPException(status_code=422, detail=str(exc)) from exc return AgentRunResponse.from_entity(entity) @router.get("/runs", response_model=list[AgentRunResponse]) def list_agent_runs( agent_id: str | None = Query(default=None), session_id: str | None = Query(default=None), service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentRunResponse]: return [ AgentRunResponse.from_entity(item) for item in service.list_agent_runs( agent_id=agent_id, session_id=session_id) ] @router.post("/runs/list", response_model=list[AgentRunResponse]) def list_agent_runs_post( payload: AgentRunListRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentRunResponse]: return [ AgentRunResponse.from_entity(item) for item in service.list_agent_runs( agent_id=payload.agent_id, session_id=payload.session_id) ] @router.post("/runs/detail", response_model=AgentRunResponse) def get_agent_run( payload: AgentRunDetailRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse: entity = service.get_agent_run(payload) if entity is None: raise HTTPException(status_code=404, detail=f"agent_run not found: {payload.agent_run_id}") return AgentRunResponse.from_entity(entity) @router.get( "/runs/{agent_run_id}/tool-invocations", response_model=list[AgentToolInvocationResponse]) def list_agent_tool_invocations( agent_run_id: str, service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentToolInvocationResponse]: return [ AgentToolInvocationResponse.from_entity(item) for item in service.list_agent_tool_invocations( agent_run_id=agent_run_id) ] @router.post( "/runs/tool-invocations/list", response_model=list[AgentToolInvocationResponse]) def list_agent_tool_invocations_post( payload: AgentToolInvocationListRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentToolInvocationResponse]: return [ AgentToolInvocationResponse.from_entity(item) for item in service.list_agent_tool_invocations( agent_run_id=payload.agent_run_id) ] @router.post("/runs/{agent_run_id}/status", response_model=AgentRunResponse) def update_agent_run_status( agent_run_id: str, payload: AgentRunStatusUpdateRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse: entity = service.update_agent_run_status(agent_run_id=agent_run_id, payload=payload) if entity is None: raise HTTPException(status_code=404, detail=f"agent_run not found: {agent_run_id}") return AgentRunResponse.from_entity(entity) @router.post("/runs/status", response_model=AgentRunResponse) def update_agent_run_status_post( payload: AgentRunStatusPostRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse: entity = service.update_agent_run_status( agent_run_id=payload.agent_run_id, payload=AgentRunStatusUpdateRequest( status=payload.status, worker_key=payload.worker_key, output_text=payload.output_text, output_json=payload.output_json, error_code=payload.error_code, error_message=payload.error_message)) if entity is None: raise HTTPException(status_code=404, detail=f"agent_run not found: {payload.agent_run_id}") return AgentRunResponse.from_entity(entity) @router.post("/runs/{agent_run_id}/execute", response_model=AgentRunExecuteResponse) def execute_agent_run( agent_run_id: str, payload: AgentRunExecuteRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunExecuteResponse: entity = service.execute_agent_run(agent_run_id=agent_run_id, payload=payload) if entity is None: raise HTTPException(status_code=404, detail=f"agent_run not found: {agent_run_id}") output_json = entity.output_json or {} model_value = output_json.get("model") dry_run_value = output_json.get("dry_run") return AgentRunExecuteResponse( run=AgentRunResponse.from_entity(entity), model=model_value if isinstance(model_value, str) else None, dry_run=dry_run_value if isinstance(dry_run_value, bool) else False) @router.post("/runs/execute", response_model=AgentRunExecuteResponse) def execute_agent_run_post( payload: AgentRunExecutePostRequest, service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunExecuteResponse: entity = service.execute_agent_run( agent_run_id=payload.agent_run_id, payload=AgentRunExecuteRequest( worker_key=payload.worker_key, dry_run=payload.dry_run)) if entity is None: raise HTTPException(status_code=404, detail=f"agent_run not found: {payload.agent_run_id}") output_json = entity.output_json or {} model_value = output_json.get("model") dry_run_value = output_json.get("dry_run") return AgentRunExecuteResponse( run=AgentRunResponse.from_entity(entity), model=model_value if isinstance(model_value, str) else None, dry_run=dry_run_value if isinstance(dry_run_value, bool) else False) @router.post("/workers/execute-next", response_model=AgentWorkerExecuteNextResponse) def execute_next_worker_task( payload: AgentWorkerExecuteNextRequest, settings: AgentServiceSettings = Depends(get_agent_service_settings), service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentWorkerExecuteNextResponse: result = service.execute_next_claimed_agent_run( worker_key=payload.worker_key, lease_seconds=payload.lease_seconds or settings.worker_lease_seconds, dry_run=payload.dry_run if payload.dry_run is not None else settings.worker_dry_run) if result is None: raise HTTPException(status_code=404, detail="queued agent_run not found") entity, released_lease_count = result output_json = entity.output_json or {} model_value = output_json.get("model") dry_run_value = output_json.get("dry_run") return AgentWorkerExecuteNextResponse( run=AgentRunResponse.from_entity(entity), model=model_value if isinstance(model_value, str) else None, dry_run=dry_run_value if isinstance(dry_run_value, bool) else False, released_lease_count=released_lease_count)