"""Administrator task projections and controls, backed by native Jobs.""" from fastapi import APIRouter, Depends, HTTPException from pydantic import Field from schemas import CamelModel from db.session import get_session from services.auth import require_admin from services.background_tasks.service import task_service from services.config_resolver import resolve_runtime_config router = APIRouter(prefix="/api/background-tasks", tags=["background-tasks"], dependencies=[Depends(require_admin)]) class SubmitTask(CamelModel): assistant_id: str instruction: str = Field(min_length=1, max_length=12000) request_id: str = Field(min_length=1, max_length=80) class TaskInput(CamelModel): reply: str | None = None answers: list[list[str]] | None = None @router.get("") async def list_tasks(assistant_id: str): return [record.public() for record in await task_service.repository.list(assistant_id)] @router.post("") async def submit_task(body: SubmitTask, db=Depends(get_session)): cfg = await resolve_runtime_config(db, body.assistant_id) if cfg.type != "prompt" or cfg.runtimeMode != "pipeline": raise HTTPException(400, "后台任务仅支持提示词 Pipeline") if cfg.task_backend is None: raise HTTPException(400, "助手未绑定任务后端") try: return await task_service.submit( assistant_id=body.assistant_id, conversation_id="web_" + body.assistant_id, backend=cfg.task_backend, instruction=body.instruction, invocation_id=body.request_id, variable_state={"values": cfg.dynamic_variables, "definitions": cfg.dynamic_variable_definitions, "secrets": cfg.secret_dynamic_variables}, ) except ValueError as exc: raise HTTPException(400, str(exc)) from exc @router.get("/{task_id}") async def get_task(task_id: str): try: return (await task_service.get(task_id)).public() except ValueError as exc: raise HTTPException(404, str(exc)) from exc @router.post("/{task_id}/cancel") async def cancel_task(task_id: str): try: return await task_service.cancel(task_id) except ValueError as exc: raise HTTPException(404, str(exc)) from exc @router.post("/{task_id}/input") async def send_input(task_id: str, body: TaskInput): try: return await task_service.send_input(task_id, body.model_dump(exclude_none=True)) except ValueError as exc: raise HTTPException(400, str(exc)) from exc except Exception as exc: raise HTTPException(502, "远端暂未接受回复,请稍后重试") from exc @router.post("/{task_id}/refresh") async def refresh_task(task_id: str): try: return await task_service.reconcile(task_id) except ValueError as exc: raise HTTPException(404, str(exc)) from exc except Exception as exc: raise HTTPException(502, "暂时无法核查远端状态") from exc