Files
content-factory/apps/backend/src/presentation/routes/agent_jobs.py
T

144 lines
4.5 KiB
Python

from __future__ import annotations
from typing import Any
from uuid import UUID
from fastapi import APIRouter, Body, Depends, HTTPException, Response, status
from pydantic import BaseModel, Field
from src.application.agent_jobs import (
cancel_agent_job,
claim_next_agent_job,
complete_agent_job,
create_test_codex_job,
get_agent_job,
list_agent_jobs,
retry_agent_job,
)
from src.domain.auth import ADMIN_ROLES
from src.domain.contracts import (
AgentJobListResponse,
AgentJobResponse,
AgentJobTestCodexRequest,
CurrentUser,
)
from src.infrastructure.repositories import BackendRepository
from src.presentation.dependencies import get_repository, require_roles
router = APIRouter(prefix="/api", tags=["agent-jobs"])
internal_router = APIRouter(prefix="/internal/agent-jobs", include_in_schema=False)
class AgentJobCompletionRequest(BaseModel):
workspace_path: str | None = None
stdout: str = ""
stderr: str = ""
exit_code: int | None = None
duration_ms: int | None = Field(default=None, ge=0)
output: dict[str, Any] = Field(default_factory=dict)
@router.post(
"/agent-jobs/test-codex",
response_model=AgentJobResponse,
status_code=status.HTTP_201_CREATED,
)
def post_test_codex_job(
request: AgentJobTestCodexRequest = Body(
default_factory=AgentJobTestCodexRequest
),
_: CurrentUser = Depends(require_roles(ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> AgentJobResponse:
return create_test_codex_job(repository, request)
@router.get("/agent-jobs", response_model=AgentJobListResponse)
def get_agent_jobs(
_: CurrentUser = Depends(require_roles(ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> AgentJobListResponse:
return list_agent_jobs(repository)
@router.get("/agent-jobs/{job_id}", response_model=AgentJobResponse)
def get_agent_job_route(
job_id: UUID,
_: CurrentUser = Depends(require_roles(ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> AgentJobResponse:
try:
return get_agent_job(repository, job_id)
except LookupError as error:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not Found") from error
@router.post(
"/agent-jobs/{job_id}/retry",
response_model=AgentJobResponse,
status_code=status.HTTP_201_CREATED,
)
def post_retry_agent_job(
job_id: UUID,
current_user: CurrentUser = Depends(require_roles(ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> AgentJobResponse:
try:
return retry_agent_job(
repository,
job_id,
actor_user_id=current_user.id,
)
except LookupError as error:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not Found") from error
except PermissionError as error:
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(error)) from error
@router.post("/agent-jobs/{job_id}/cancel", response_model=AgentJobResponse)
def post_cancel_agent_job(
job_id: UUID,
current_user: CurrentUser = Depends(require_roles(ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> AgentJobResponse:
try:
return cancel_agent_job(
repository,
job_id,
actor_user_id=current_user.id,
)
except LookupError as error:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not Found") from error
@internal_router.post("/claim", response_model=None)
def post_claim_agent_job(
repository: BackendRepository = Depends(get_repository),
):
claimed = claim_next_agent_job(repository)
if claimed is None:
return Response(status_code=status.HTTP_204_NO_CONTENT)
return claimed
@internal_router.post("/{job_id}/complete", response_model=AgentJobResponse)
def post_complete_agent_job(
job_id: UUID,
request: AgentJobCompletionRequest,
repository: BackendRepository = Depends(get_repository),
) -> AgentJobResponse:
try:
return complete_agent_job(
repository,
job_id=job_id,
workspace_path=request.workspace_path,
stdout=request.stdout,
stderr=request.stderr,
exit_code=request.exit_code,
duration_ms=request.duration_ms,
output=request.output,
)
except LookupError as error:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not Found") from error