routes.py 6.6 KB

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