routes.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. from core_domain import ServiceHealth
  2. from fastapi import APIRouter, Depends, HTTPException, Query
  3. from sqlalchemy import text
  4. from sqlalchemy.orm import Session
  5. from app.application.services import AgentApplicationService, build_agent_application_service
  6. from app.bootstrap.settings import AgentServiceSettings
  7. from app.db.session import get_db
  8. from app.schemas.agent import (
  9. AgentConfigCreateRequest,
  10. AgentConfigListRequest,
  11. AgentConfigResponse,
  12. AgentCreateRequest,
  13. AgentResponse,
  14. AgentRunCreateRequest,
  15. AgentRunDetailRequest,
  16. AgentRunExecuteRequest,
  17. AgentRunExecuteResponse,
  18. AgentRunResponse,
  19. AgentRunStatusUpdateRequest,
  20. AgentStatusUpdateRequest,
  21. AgentUpdateRequest,
  22. AgentToolInvocationResponse,
  23. AgentVersionCreateRequest,
  24. AgentVersionResponse,
  25. AgentWorkerExecuteNextRequest,
  26. AgentWorkerExecuteNextResponse,
  27. )
  28. router = APIRouter()
  29. def get_agent_service_settings() -> AgentServiceSettings:
  30. return AgentServiceSettings()
  31. def get_agent_application_service(
  32. db: Session = Depends(get_db),
  33. settings: AgentServiceSettings = Depends(get_agent_service_settings)) -> AgentApplicationService:
  34. return build_agent_application_service(db=db, settings=settings)
  35. @router.get("/health", response_model=ServiceHealth)
  36. def health_check(db: Session = Depends(get_db)) -> ServiceHealth:
  37. db.execute(text("SELECT 1"))
  38. return ServiceHealth(service="agent-service", status="ok", database="ok")
  39. @router.post("", response_model=AgentResponse)
  40. def create_agent(
  41. payload: AgentCreateRequest,
  42. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse:
  43. entity = service.create_agent(payload)
  44. return AgentResponse.from_entity(entity)
  45. @router.get("", response_model=list[AgentResponse])
  46. def list_agents(
  47. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentResponse]:
  48. return [AgentResponse.from_entity(item) for item in service.list_agents()]
  49. @router.post("/update", response_model=AgentResponse)
  50. def update_agent(
  51. payload: AgentUpdateRequest,
  52. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse:
  53. entity = service.update_agent(payload)
  54. if entity is None:
  55. raise HTTPException(status_code=404, detail=f"agent not found: {payload.agent_id}")
  56. return AgentResponse.from_entity(entity)
  57. @router.patch("/{agent_id}/status", response_model=AgentResponse)
  58. def update_agent_status(
  59. agent_id: str,
  60. payload: AgentStatusUpdateRequest,
  61. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse:
  62. entity = service.update_agent_status(agent_id=agent_id, payload=payload)
  63. if entity is None:
  64. raise HTTPException(status_code=404, detail=f"agent not found: {agent_id}")
  65. return AgentResponse.from_entity(entity)
  66. @router.post("/configs/create", response_model=AgentConfigResponse)
  67. def create_agent_config(
  68. payload: AgentConfigCreateRequest,
  69. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentConfigResponse:
  70. try:
  71. entity = service.create_agent_config(payload)
  72. except ValueError as exc:
  73. raise HTTPException(status_code=422, detail=str(exc)) from exc
  74. return AgentConfigResponse.from_entity(entity)
  75. @router.post("/configs/list", response_model=list[AgentConfigResponse])
  76. def list_agent_configs(
  77. payload: AgentConfigListRequest,
  78. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentConfigResponse]:
  79. return [
  80. AgentConfigResponse.from_entity(item)
  81. for item in service.list_agent_configs(payload)
  82. ]
  83. @router.post("/versions", response_model=AgentVersionResponse)
  84. def create_agent_version(
  85. payload: AgentVersionCreateRequest,
  86. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentVersionResponse:
  87. try:
  88. entity = service.create_agent_version(payload)
  89. except ValueError as exc:
  90. raise HTTPException(status_code=422, detail=str(exc)) from exc
  91. return AgentVersionResponse.from_entity(entity)
  92. @router.get("/versions", response_model=list[AgentVersionResponse])
  93. def list_agent_versions(
  94. agent_id: str = Query(...),
  95. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentVersionResponse]:
  96. return [
  97. AgentVersionResponse.from_entity(item)
  98. for item in service.list_agent_versions(agent_id=agent_id)
  99. ]
  100. @router.post("/runs", response_model=AgentRunResponse)
  101. def create_agent_run(
  102. payload: AgentRunCreateRequest,
  103. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse:
  104. try:
  105. entity = service.create_agent_run(payload)
  106. except ValueError as exc:
  107. raise HTTPException(status_code=422, detail=str(exc)) from exc
  108. return AgentRunResponse.from_entity(entity)
  109. @router.get("/runs", response_model=list[AgentRunResponse])
  110. def list_agent_runs(
  111. agent_id: str | None = Query(default=None),
  112. session_id: str | None = Query(default=None),
  113. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentRunResponse]:
  114. return [
  115. AgentRunResponse.from_entity(item)
  116. for item in service.list_agent_runs(
  117. agent_id=agent_id,
  118. session_id=session_id)
  119. ]
  120. @router.post("/runs/detail", response_model=AgentRunResponse)
  121. def get_agent_run(
  122. payload: AgentRunDetailRequest,
  123. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse:
  124. entity = service.get_agent_run(payload)
  125. if entity is None:
  126. raise HTTPException(status_code=404, detail=f"agent_run not found: {payload.agent_run_id}")
  127. return AgentRunResponse.from_entity(entity)
  128. @router.get(
  129. "/runs/{agent_run_id}/tool-invocations",
  130. response_model=list[AgentToolInvocationResponse])
  131. def list_agent_tool_invocations(
  132. agent_run_id: str,
  133. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentToolInvocationResponse]:
  134. return [
  135. AgentToolInvocationResponse.from_entity(item)
  136. for item in service.list_agent_tool_invocations(
  137. agent_run_id=agent_run_id)
  138. ]
  139. @router.post("/runs/{agent_run_id}/status", response_model=AgentRunResponse)
  140. def update_agent_run_status(
  141. agent_run_id: str,
  142. payload: AgentRunStatusUpdateRequest,
  143. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse:
  144. entity = service.update_agent_run_status(agent_run_id=agent_run_id, payload=payload)
  145. if entity is None:
  146. raise HTTPException(status_code=404, detail=f"agent_run not found: {agent_run_id}")
  147. return AgentRunResponse.from_entity(entity)
  148. @router.post("/runs/{agent_run_id}/execute", response_model=AgentRunExecuteResponse)
  149. def execute_agent_run(
  150. agent_run_id: str,
  151. payload: AgentRunExecuteRequest,
  152. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunExecuteResponse:
  153. entity = service.execute_agent_run(agent_run_id=agent_run_id, payload=payload)
  154. if entity is None:
  155. raise HTTPException(status_code=404, detail=f"agent_run not found: {agent_run_id}")
  156. output_json = entity.output_json or {}
  157. model_value = output_json.get("model")
  158. dry_run_value = output_json.get("dry_run")
  159. return AgentRunExecuteResponse(
  160. run=AgentRunResponse.from_entity(entity),
  161. model=model_value if isinstance(model_value, str) else None,
  162. dry_run=dry_run_value if isinstance(dry_run_value, bool) else False)
  163. @router.post("/workers/execute-next", response_model=AgentWorkerExecuteNextResponse)
  164. def execute_next_worker_task(
  165. payload: AgentWorkerExecuteNextRequest,
  166. settings: AgentServiceSettings = Depends(get_agent_service_settings),
  167. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentWorkerExecuteNextResponse:
  168. result = service.execute_next_claimed_agent_run(
  169. worker_key=payload.worker_key,
  170. lease_seconds=payload.lease_seconds or settings.worker_lease_seconds,
  171. dry_run=payload.dry_run if payload.dry_run is not None else settings.worker_dry_run)
  172. if result is None:
  173. raise HTTPException(status_code=404, detail="queued agent_run not found")
  174. entity, released_lease_count = result
  175. output_json = entity.output_json or {}
  176. model_value = output_json.get("model")
  177. dry_run_value = output_json.get("dry_run")
  178. return AgentWorkerExecuteNextResponse(
  179. run=AgentRunResponse.from_entity(entity),
  180. model=model_value if isinstance(model_value, str) else None,
  181. dry_run=dry_run_value if isinstance(dry_run_value, bool) else False,
  182. released_lease_count=released_lease_count)