144 lines
4.5 KiB
Python
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
|