67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
"""API для запуска задач на worker-профилях"""
|
|
from __future__ import annotations
|
|
import subprocess, uuid, json, os
|
|
from datetime import datetime, timezone
|
|
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel, Field
|
|
|
|
router = APIRouter(prefix="/api/v1/runs", tags=["runs"])
|
|
|
|
# In-memory storage (до БД в следующих частях)
|
|
_runs: dict[str, dict] = {}
|
|
|
|
HERMES = os.environ.get("HERMES_BIN", "hermes")
|
|
|
|
class RunRequest(BaseModel):
|
|
agent: str = Field(description="worker-code | worker-fast | worker-research | worker-review")
|
|
goal: str = Field(min_length=3, max_length=2000)
|
|
model: str | None = None
|
|
|
|
class RunResponse(BaseModel):
|
|
run_id: str
|
|
agent: str
|
|
goal: str
|
|
status: str
|
|
created_at: str
|
|
|
|
@router.post("", response_model=RunResponse)
|
|
def create_run(req: RunRequest):
|
|
if req.agent not in ("worker-code", "worker-fast", "worker-research", "worker-review"):
|
|
raise HTTPException(400, f"Неизвестный агент: {req.agent}")
|
|
|
|
run_id = uuid.uuid4().hex[:12]
|
|
created = datetime.now(timezone.utc).isoformat()
|
|
cmd = [HERMES, "-p", req.agent, "chat", "-q", req.goal, "-Q"]
|
|
if req.model:
|
|
cmd.extend(["-m", req.model])
|
|
|
|
# Запуск в фоне
|
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
|
|
_runs[run_id] = {
|
|
"run_id": run_id, "agent": req.agent, "goal": req.goal,
|
|
"status": "running", "created_at": created, "pid": process.pid,
|
|
}
|
|
return RunResponse(run_id=run_id, agent=req.agent, goal=req.goal, status="running", created_at=created)
|
|
|
|
@router.get("")
|
|
def list_runs(agent: str | None = None, limit: int = 20):
|
|
result = list(_runs.values())
|
|
if agent:
|
|
result = [r for r in result if r["agent"] == agent]
|
|
# Check status for running processes
|
|
for r in result:
|
|
if r["status"] == "running":
|
|
try:
|
|
pid = r.get("pid")
|
|
if pid:
|
|
os.kill(pid, 0) # check if alive
|
|
except (OSError, ProcessLookupError):
|
|
r["status"] = "completed"
|
|
return {"runs": sorted(result, key=lambda r: r["created_at"], reverse=True)[:limit]}
|
|
|
|
@router.get("/{run_id}")
|
|
def get_run(run_id: str):
|
|
if run_id not in _runs:
|
|
raise HTTPException(404, "Задача не найдена")
|
|
return _runs[run_id]
|