routes.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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 TeamApplicationService, build_team_application_service
  6. from app.bootstrap.settings import TeamServiceSettings
  7. from app.db.session import get_db
  8. from app.domain.repositories import (
  9. TeamDefinitionRepository,
  10. TeamRunRepository,
  11. TeamVersionRepository,
  12. )
  13. from app.schemas.team import (
  14. TeamCreateRequest,
  15. TeamResponse,
  16. TeamRunCreateRequest,
  17. TeamRunExecuteRequest,
  18. TeamRunExecuteResponse,
  19. TeamRunResponse,
  20. TeamRunStatusUpdateRequest,
  21. TeamWorkerExecuteNextRequest,
  22. TeamWorkerExecuteNextResponse,
  23. TeamStatusUpdateRequest,
  24. TeamVersionCreateRequest,
  25. TeamVersionResponse,
  26. )
  27. router = APIRouter()
  28. def get_team_settings() -> TeamServiceSettings:
  29. return TeamServiceSettings()
  30. def get_team_application_service(
  31. db: Session = Depends(get_db),
  32. settings: TeamServiceSettings = Depends(get_team_settings),
  33. ) -> TeamApplicationService:
  34. return build_team_application_service(
  35. team_repository=TeamDefinitionRepository(db),
  36. team_version_repository=TeamVersionRepository(db),
  37. team_run_repository=TeamRunRepository(db),
  38. settings=settings,
  39. )
  40. @router.get("/health", response_model=ServiceHealth)
  41. def health_check(db: Session = Depends(get_db)) -> ServiceHealth:
  42. db.execute(text("SELECT 1"))
  43. return ServiceHealth(service="team-service", status="ok", database="ok")
  44. @router.post("", response_model=TeamResponse)
  45. def create_team(
  46. payload: TeamCreateRequest,
  47. service: TeamApplicationService = Depends(get_team_application_service),
  48. ) -> TeamResponse:
  49. entity = service.create_team(payload)
  50. return TeamResponse.from_entity(entity)
  51. @router.get("", response_model=list[TeamResponse])
  52. def list_teams(
  53. tenant_id: str = Query(...),
  54. service: TeamApplicationService = Depends(get_team_application_service),
  55. ) -> list[TeamResponse]:
  56. return [TeamResponse.from_entity(item) for item in service.list_teams(tenant_id=tenant_id)]
  57. @router.patch("/{team_id}/status", response_model=TeamResponse)
  58. def update_team_status(
  59. team_id: str,
  60. payload: TeamStatusUpdateRequest,
  61. service: TeamApplicationService = Depends(get_team_application_service),
  62. ) -> TeamResponse:
  63. entity = service.update_team_status(team_id=team_id, payload=payload)
  64. if entity is None:
  65. raise HTTPException(status_code=404, detail=f"team not found: {team_id}")
  66. return TeamResponse.from_entity(entity)
  67. @router.post("/versions", response_model=TeamVersionResponse)
  68. def create_team_version(
  69. payload: TeamVersionCreateRequest,
  70. service: TeamApplicationService = Depends(get_team_application_service),
  71. ) -> TeamVersionResponse:
  72. try:
  73. entity = service.create_team_version(payload)
  74. except ValueError as exc:
  75. raise HTTPException(status_code=422, detail=str(exc)) from exc
  76. return TeamVersionResponse.from_entity(entity)
  77. @router.get("/versions", response_model=list[TeamVersionResponse])
  78. def list_team_versions(
  79. tenant_id: str = Query(...),
  80. team_id: str = Query(...),
  81. service: TeamApplicationService = Depends(get_team_application_service),
  82. ) -> list[TeamVersionResponse]:
  83. return [
  84. TeamVersionResponse.from_entity(item)
  85. for item in service.list_team_versions(tenant_id=tenant_id, team_id=team_id)
  86. ]
  87. @router.post("/runs", response_model=TeamRunResponse)
  88. def create_team_run(
  89. payload: TeamRunCreateRequest,
  90. service: TeamApplicationService = Depends(get_team_application_service),
  91. ) -> TeamRunResponse:
  92. try:
  93. entity = service.create_team_run(payload)
  94. except ValueError as exc:
  95. raise HTTPException(status_code=422, detail=str(exc)) from exc
  96. return TeamRunResponse.from_entity(entity)
  97. @router.get("/runs", response_model=list[TeamRunResponse])
  98. def list_team_runs(
  99. tenant_id: str = Query(...),
  100. team_id: str | None = Query(default=None),
  101. session_id: str | None = Query(default=None),
  102. service: TeamApplicationService = Depends(get_team_application_service),
  103. ) -> list[TeamRunResponse]:
  104. return [
  105. TeamRunResponse.from_entity(item)
  106. for item in service.list_team_runs(
  107. tenant_id=tenant_id,
  108. team_id=team_id,
  109. session_id=session_id,
  110. )
  111. ]
  112. @router.post("/runs/{team_run_id}/status", response_model=TeamRunResponse)
  113. def update_team_run_status(
  114. team_run_id: str,
  115. payload: TeamRunStatusUpdateRequest,
  116. service: TeamApplicationService = Depends(get_team_application_service),
  117. ) -> TeamRunResponse:
  118. entity = service.update_team_run_status(team_run_id=team_run_id, payload=payload)
  119. if entity is None:
  120. raise HTTPException(status_code=404, detail=f"team_run not found: {team_run_id}")
  121. return TeamRunResponse.from_entity(entity)
  122. @router.post("/runs/{team_run_id}/execute", response_model=TeamRunExecuteResponse)
  123. def execute_team_run(
  124. team_run_id: str,
  125. payload: TeamRunExecuteRequest,
  126. service: TeamApplicationService = Depends(get_team_application_service),
  127. ) -> TeamRunExecuteResponse:
  128. entity = service.execute_team_run(team_run_id=team_run_id, payload=payload)
  129. if entity is None:
  130. raise HTTPException(status_code=404, detail=f"team_run not found: {team_run_id}")
  131. output_json = entity.output_json or {}
  132. member_run_count = output_json.get("member_run_count")
  133. dry_run = output_json.get("dry_run")
  134. return TeamRunExecuteResponse(
  135. run=TeamRunResponse.from_entity(entity),
  136. member_run_count=member_run_count if isinstance(member_run_count, int) else 0,
  137. dry_run=dry_run if isinstance(dry_run, bool) else payload.dry_run,
  138. )
  139. @router.post("/workers/execute-next", response_model=TeamWorkerExecuteNextResponse)
  140. def execute_next_worker_task(
  141. payload: TeamWorkerExecuteNextRequest,
  142. settings: TeamServiceSettings = Depends(get_team_settings),
  143. service: TeamApplicationService = Depends(get_team_application_service),
  144. ) -> TeamWorkerExecuteNextResponse:
  145. result = service.execute_next_claimed_team_run(
  146. worker_key=payload.worker_key,
  147. lease_seconds=payload.lease_seconds or settings.worker_lease_seconds,
  148. dry_run=payload.dry_run if payload.dry_run is not None else settings.worker_dry_run,
  149. )
  150. if result is None:
  151. raise HTTPException(status_code=404, detail="queued team_run not found")
  152. entity, released_lease_count = result
  153. output_json = entity.output_json or {}
  154. member_run_count = output_json.get("member_run_count")
  155. dry_run = output_json.get("dry_run")
  156. return TeamWorkerExecuteNextResponse(
  157. run=TeamRunResponse.from_entity(entity),
  158. member_run_count=member_run_count if isinstance(member_run_count, int) else 0,
  159. dry_run=dry_run if isinstance(dry_run, bool) else settings.worker_dry_run,
  160. released_lease_count=released_lease_count,
  161. )