routes.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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. AgentDeleteRequest,
  14. AgentDetailRequest,
  15. AgentListRequest,
  16. AgentResponse,
  17. AgentRunCreateRequest,
  18. AgentRunDetailRequest,
  19. AgentRunExecutePostRequest,
  20. AgentRunExecuteRequest,
  21. AgentRunExecuteResponse,
  22. AgentRunListRequest,
  23. AgentRunResponse,
  24. AgentRunStatusPostRequest,
  25. AgentRunStatusUpdateRequest,
  26. AgentStatusUpdateRequest,
  27. AgentStatusPostRequest,
  28. AgentToolInvocationListRequest,
  29. AgentUpdateRequest,
  30. AgentToolInvocationResponse,
  31. AgentWorkerExecuteNextRequest,
  32. AgentWorkerExecuteNextResponse,
  33. DeleteData,
  34. )
  35. router = APIRouter()
  36. def get_agent_service_settings() -> AgentServiceSettings:
  37. return AgentServiceSettings()
  38. def get_agent_application_service(
  39. db: Session = Depends(get_db),
  40. settings: AgentServiceSettings = Depends(get_agent_service_settings)) -> AgentApplicationService:
  41. return build_agent_application_service(db=db, settings=settings)
  42. @router.get("/health", response_model=ServiceHealth)
  43. def health_check(db: Session = Depends(get_db)) -> ServiceHealth:
  44. db.execute(text("SELECT 1"))
  45. return ServiceHealth(service="agent-service", status="ok", database="ok")
  46. @router.post("", response_model=AgentResponse)
  47. def create_agent(
  48. payload: AgentCreateRequest,
  49. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse:
  50. entity = service.create_agent(payload)
  51. return AgentResponse.from_entity(entity)
  52. @router.get("", response_model=list[AgentResponse])
  53. def list_agents(
  54. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentResponse]:
  55. return [AgentResponse.from_entity(item) for item in service.list_agents()]
  56. @router.post("/list", response_model=list[AgentResponse])
  57. def list_agents_post(
  58. payload: AgentListRequest,
  59. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentResponse]:
  60. return [AgentResponse.from_entity(item) for item in service.list_agents()]
  61. @router.post("/detail", response_model=AgentResponse)
  62. def detail_agent(
  63. payload: AgentDetailRequest,
  64. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse:
  65. entity = service.get_agent(agent_id=payload.agent_id)
  66. if entity is None:
  67. raise HTTPException(status_code=404, detail=f"agent not found: {payload.agent_id}")
  68. return AgentResponse.from_entity(entity)
  69. @router.post("/update", response_model=AgentResponse)
  70. def update_agent(
  71. payload: AgentUpdateRequest,
  72. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse:
  73. entity = service.update_agent(payload)
  74. if entity is None:
  75. raise HTTPException(status_code=404, detail=f"agent not found: {payload.agent_id}")
  76. return AgentResponse.from_entity(entity)
  77. @router.patch("/{agent_id}/status", response_model=AgentResponse)
  78. def update_agent_status(
  79. agent_id: str,
  80. payload: AgentStatusUpdateRequest,
  81. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse:
  82. entity = service.update_agent_status(agent_id=agent_id, payload=payload)
  83. if entity is None:
  84. raise HTTPException(status_code=404, detail=f"agent not found: {agent_id}")
  85. return AgentResponse.from_entity(entity)
  86. @router.post("/status", response_model=AgentResponse)
  87. def update_agent_status_post(
  88. payload: AgentStatusPostRequest,
  89. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentResponse:
  90. entity = service.update_agent_status(
  91. agent_id=payload.agent_id,
  92. payload=AgentStatusUpdateRequest(status=payload.status))
  93. if entity is None:
  94. raise HTTPException(status_code=404, detail=f"agent not found: {payload.agent_id}")
  95. return AgentResponse.from_entity(entity)
  96. @router.post("/delete", response_model=DeleteData)
  97. def delete_agent_post(
  98. payload: AgentDeleteRequest,
  99. service: AgentApplicationService = Depends(get_agent_application_service)) -> DeleteData:
  100. return DeleteData(
  101. deleted=service.delete_agent(agent_id=payload.agent_id),
  102. agent_id=payload.agent_id)
  103. @router.post("/configs/create", response_model=AgentConfigResponse)
  104. def create_agent_config(
  105. payload: AgentConfigCreateRequest,
  106. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentConfigResponse:
  107. try:
  108. entity = service.create_agent_config(payload)
  109. except ValueError as exc:
  110. raise HTTPException(status_code=422, detail=str(exc)) from exc
  111. return AgentConfigResponse.from_entity(entity)
  112. @router.post("/configs/list", response_model=list[AgentConfigResponse])
  113. def list_agent_configs(
  114. payload: AgentConfigListRequest,
  115. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentConfigResponse]:
  116. return [
  117. AgentConfigResponse.from_entity(item)
  118. for item in service.list_agent_configs(agent_id=payload.agent_id)
  119. ]
  120. @router.post("/runs", response_model=AgentRunResponse)
  121. def create_agent_run(
  122. payload: AgentRunCreateRequest,
  123. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse:
  124. try:
  125. entity = service.create_agent_run(payload)
  126. except ValueError as exc:
  127. raise HTTPException(status_code=422, detail=str(exc)) from exc
  128. return AgentRunResponse.from_entity(entity)
  129. @router.get("/runs", response_model=list[AgentRunResponse])
  130. def list_agent_runs(
  131. agent_id: str | None = Query(default=None),
  132. session_id: str | None = Query(default=None),
  133. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentRunResponse]:
  134. return [
  135. AgentRunResponse.from_entity(item)
  136. for item in service.list_agent_runs(
  137. agent_id=agent_id,
  138. session_id=session_id)
  139. ]
  140. @router.post("/runs/list", response_model=list[AgentRunResponse])
  141. def list_agent_runs_post(
  142. payload: AgentRunListRequest,
  143. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentRunResponse]:
  144. return [
  145. AgentRunResponse.from_entity(item)
  146. for item in service.list_agent_runs(
  147. agent_id=payload.agent_id,
  148. session_id=payload.session_id)
  149. ]
  150. @router.post("/runs/detail", response_model=AgentRunResponse)
  151. def get_agent_run(
  152. payload: AgentRunDetailRequest,
  153. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse:
  154. entity = service.get_agent_run(payload)
  155. if entity is None:
  156. raise HTTPException(status_code=404, detail=f"agent_run not found: {payload.agent_run_id}")
  157. return AgentRunResponse.from_entity(entity)
  158. @router.get(
  159. "/runs/{agent_run_id}/tool-invocations",
  160. response_model=list[AgentToolInvocationResponse])
  161. def list_agent_tool_invocations(
  162. agent_run_id: str,
  163. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentToolInvocationResponse]:
  164. return [
  165. AgentToolInvocationResponse.from_entity(item)
  166. for item in service.list_agent_tool_invocations(
  167. agent_run_id=agent_run_id)
  168. ]
  169. @router.post(
  170. "/runs/tool-invocations/list",
  171. response_model=list[AgentToolInvocationResponse])
  172. def list_agent_tool_invocations_post(
  173. payload: AgentToolInvocationListRequest,
  174. service: AgentApplicationService = Depends(get_agent_application_service)) -> list[AgentToolInvocationResponse]:
  175. return [
  176. AgentToolInvocationResponse.from_entity(item)
  177. for item in service.list_agent_tool_invocations(
  178. agent_run_id=payload.agent_run_id)
  179. ]
  180. @router.post("/runs/{agent_run_id}/status", response_model=AgentRunResponse)
  181. def update_agent_run_status(
  182. agent_run_id: str,
  183. payload: AgentRunStatusUpdateRequest,
  184. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse:
  185. entity = service.update_agent_run_status(agent_run_id=agent_run_id, payload=payload)
  186. if entity is None:
  187. raise HTTPException(status_code=404, detail=f"agent_run not found: {agent_run_id}")
  188. return AgentRunResponse.from_entity(entity)
  189. @router.post("/runs/status", response_model=AgentRunResponse)
  190. def update_agent_run_status_post(
  191. payload: AgentRunStatusPostRequest,
  192. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunResponse:
  193. entity = service.update_agent_run_status(
  194. agent_run_id=payload.agent_run_id,
  195. payload=AgentRunStatusUpdateRequest(
  196. status=payload.status,
  197. worker_key=payload.worker_key,
  198. output_text=payload.output_text,
  199. output_json=payload.output_json,
  200. error_code=payload.error_code,
  201. error_message=payload.error_message))
  202. if entity is None:
  203. raise HTTPException(status_code=404, detail=f"agent_run not found: {payload.agent_run_id}")
  204. return AgentRunResponse.from_entity(entity)
  205. @router.post("/runs/{agent_run_id}/execute", response_model=AgentRunExecuteResponse)
  206. def execute_agent_run(
  207. agent_run_id: str,
  208. payload: AgentRunExecuteRequest,
  209. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunExecuteResponse:
  210. entity = service.execute_agent_run(agent_run_id=agent_run_id, payload=payload)
  211. if entity is None:
  212. raise HTTPException(status_code=404, detail=f"agent_run not found: {agent_run_id}")
  213. output_json = entity.output_json or {}
  214. model_value = output_json.get("model")
  215. dry_run_value = output_json.get("dry_run")
  216. return AgentRunExecuteResponse(
  217. run=AgentRunResponse.from_entity(entity),
  218. model=model_value if isinstance(model_value, str) else None,
  219. dry_run=dry_run_value if isinstance(dry_run_value, bool) else False)
  220. @router.post("/runs/execute", response_model=AgentRunExecuteResponse)
  221. def execute_agent_run_post(
  222. payload: AgentRunExecutePostRequest,
  223. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentRunExecuteResponse:
  224. entity = service.execute_agent_run(
  225. agent_run_id=payload.agent_run_id,
  226. payload=AgentRunExecuteRequest(
  227. worker_key=payload.worker_key,
  228. dry_run=payload.dry_run))
  229. if entity is None:
  230. raise HTTPException(status_code=404, detail=f"agent_run not found: {payload.agent_run_id}")
  231. output_json = entity.output_json or {}
  232. model_value = output_json.get("model")
  233. dry_run_value = output_json.get("dry_run")
  234. return AgentRunExecuteResponse(
  235. run=AgentRunResponse.from_entity(entity),
  236. model=model_value if isinstance(model_value, str) else None,
  237. dry_run=dry_run_value if isinstance(dry_run_value, bool) else False)
  238. @router.post("/workers/execute-next", response_model=AgentWorkerExecuteNextResponse)
  239. def execute_next_worker_task(
  240. payload: AgentWorkerExecuteNextRequest,
  241. settings: AgentServiceSettings = Depends(get_agent_service_settings),
  242. service: AgentApplicationService = Depends(get_agent_application_service)) -> AgentWorkerExecuteNextResponse:
  243. result = service.execute_next_claimed_agent_run(
  244. worker_key=payload.worker_key,
  245. lease_seconds=payload.lease_seconds or settings.worker_lease_seconds,
  246. dry_run=payload.dry_run if payload.dry_run is not None else settings.worker_dry_run)
  247. if result is None:
  248. raise HTTPException(status_code=404, detail="queued agent_run not found")
  249. entity, released_lease_count = result
  250. output_json = entity.output_json or {}
  251. model_value = output_json.get("model")
  252. dry_run_value = output_json.get("dry_run")
  253. return AgentWorkerExecuteNextResponse(
  254. run=AgentRunResponse.from_entity(entity),
  255. model=model_value if isinstance(model_value, str) else None,
  256. dry_run=dry_run_value if isinstance(dry_run_value, bool) else False,
  257. released_lease_count=released_lease_count)