129 lines
4.2 KiB
Python
129 lines
4.2 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from api.contracts.task_schemas import (
|
|
BrowserTaskAcceptedResponse,
|
|
BrowserTaskHistoryResponse,
|
|
BrowserTaskRequest,
|
|
BrowserTaskResultResponse,
|
|
BrowserTaskStatusResponse,
|
|
TaskHistoryEvent,
|
|
)
|
|
from api.domain.task_status import TaskStatus
|
|
from api.repositories.task_store import TaskRecord
|
|
from api.services.task_service import TaskService
|
|
|
|
router = APIRouter(prefix="/api/browser", tags=["browser-tasks"])
|
|
|
|
|
|
def get_task_service(request: Request) -> TaskService:
|
|
return request.app.state.task_service
|
|
|
|
|
|
@router.post("/tasks", response_model=BrowserTaskAcceptedResponse, status_code=202)
|
|
async def create_task(
|
|
payload: BrowserTaskRequest,
|
|
service: TaskService = Depends(get_task_service),
|
|
) -> BrowserTaskAcceptedResponse:
|
|
rec = await service.submit_task(task=payload.task.strip(), timeout=payload.timeout, metadata=payload.metadata)
|
|
return BrowserTaskAcceptedResponse(task_id=rec.task_id, status=rec.status)
|
|
|
|
|
|
@router.get("/tasks/{task_id}", response_model=BrowserTaskStatusResponse)
|
|
async def get_task_status(task_id: str, service: TaskService = Depends(get_task_service)) -> BrowserTaskStatusResponse:
|
|
rec = await service.get_task(task_id)
|
|
if rec is None:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
return _to_status_response(rec)
|
|
|
|
|
|
@router.get("/tasks/{task_id}/result", response_model=BrowserTaskResultResponse)
|
|
async def get_task_result(
|
|
task_id: str,
|
|
service: TaskService = Depends(get_task_service),
|
|
) -> JSONResponse | BrowserTaskResultResponse:
|
|
rec = await service.get_task(task_id)
|
|
if rec is None:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
if rec.status in (TaskStatus.queued, TaskStatus.running):
|
|
return JSONResponse(
|
|
status_code=202,
|
|
content={
|
|
"task_id": rec.task_id,
|
|
"status": rec.status.value,
|
|
"success": False,
|
|
"execution_time": rec.execution_time,
|
|
"result": None,
|
|
"error": None,
|
|
"raw_response": None,
|
|
},
|
|
)
|
|
|
|
return BrowserTaskResultResponse(
|
|
task_id=rec.task_id,
|
|
status=rec.status,
|
|
success=(rec.status == TaskStatus.succeeded),
|
|
execution_time=rec.execution_time,
|
|
result=rec.result,
|
|
error=rec.error,
|
|
raw_response=rec.raw_response,
|
|
)
|
|
|
|
|
|
@router.get("/tasks/{task_id}/history", response_model=BrowserTaskHistoryResponse)
|
|
async def get_task_history(
|
|
task_id: str,
|
|
service: TaskService = Depends(get_task_service),
|
|
) -> JSONResponse | BrowserTaskHistoryResponse:
|
|
rec = await service.get_task(task_id)
|
|
if rec is None:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
if rec.status in (TaskStatus.queued, TaskStatus.running):
|
|
return JSONResponse(
|
|
status_code=202,
|
|
content={
|
|
"task_id": rec.task_id,
|
|
"status": rec.status.value,
|
|
"history": rec.history,
|
|
},
|
|
)
|
|
|
|
return BrowserTaskHistoryResponse(
|
|
task_id=rec.task_id,
|
|
status=rec.status,
|
|
history=_to_history_events(rec),
|
|
)
|
|
|
|
|
|
def _to_status_response(rec: TaskRecord) -> BrowserTaskStatusResponse:
|
|
return BrowserTaskStatusResponse(
|
|
task_id=rec.task_id,
|
|
status=rec.status,
|
|
create_at=rec.create_at,
|
|
started_at=rec.started_at,
|
|
finished_at=rec.finished_at,
|
|
error=rec.error,
|
|
)
|
|
|
|
|
|
def _to_history_events(rec: TaskRecord) -> list[TaskHistoryEvent]:
|
|
events: list[TaskHistoryEvent] = []
|
|
for index, item in enumerate(rec.history, start=1):
|
|
kind = str(item.get("kind") or item.get("type") or "system")
|
|
content = item.get("content")
|
|
if content is not None:
|
|
content = str(content)
|
|
data = item.get("data")
|
|
if not isinstance(data, dict):
|
|
data = {}
|
|
|
|
step = item.get("step")
|
|
if not isinstance(step, int):
|
|
step = index
|
|
|
|
events.append(TaskHistoryEvent(step=step, kind=kind, content=content, data=data))
|
|
|
|
return events
|
|
|