- config.py: pydantic-settings with all env vars, privacy mode, per-agent overrides - app_state.py: global singletons (pool, master agent, registry, llm_router, sweep) - main.py: FastAPI lifespan startup — DB pool, LLM router, Odoo client, agents, master - routers/dispatch.py: POST /dispatch with rate limiting and webhook secret auth - routers/approval.py: GET /approval/pending, POST /approval/respond - routers/registry.py: GET/POST /registry/agents, POST /registry/backend overrides - routers/sweep.py: POST /sweep trigger, GET /sweep/status - routers/health.py: GET /health, GET /health/detailed (DB/Odoo/Ollama checks) - requirements.txt: pinned deps (fastapi, uvicorn, asyncpg, anthropic, alembic) - Dockerfile: python:3.11-slim, single uvicorn worker - docker-compose.yml: agent-service + postgres:15, bound to 192.168.2.47:8001 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
56 lines
1.7 KiB
Python
56 lines
1.7 KiB
Python
from __future__ import annotations
|
|
import asyncio
|
|
import logging
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, HTTPException, status
|
|
from pydantic import BaseModel
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix='/sweep', tags=['sweep'])
|
|
|
|
# Track running sweep task
|
|
_sweep_task: Optional[asyncio.Task] = None
|
|
_last_sweep_result: Optional[dict] = None
|
|
|
|
|
|
class SweepRequest(BaseModel):
|
|
agents: list[str] = [] # empty = all active agents
|
|
|
|
|
|
class SweepStatusResponse(BaseModel):
|
|
running: bool
|
|
last_result: Optional[dict] = None
|
|
|
|
|
|
@router.post('', status_code=status.HTTP_202_ACCEPTED)
|
|
async def trigger_sweep(req: SweepRequest):
|
|
global _sweep_task
|
|
if _sweep_task and not _sweep_task.done():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_409_CONFLICT,
|
|
detail='Sweep already running',
|
|
)
|
|
from ..app_state import get_sweep_coordinator
|
|
coordinator = get_sweep_coordinator()
|
|
if coordinator is None:
|
|
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail='Sweep coordinator not ready')
|
|
|
|
async def _run():
|
|
global _last_sweep_result
|
|
try:
|
|
result = await coordinator.run_sweep(agents=req.agents or None)
|
|
_last_sweep_result = result
|
|
except Exception as exc:
|
|
logger.error('sweep run error: %s', exc)
|
|
_last_sweep_result = {'error': str(exc)}
|
|
|
|
_sweep_task = asyncio.create_task(_run())
|
|
return {'status': 'accepted', 'agents': req.agents or 'all'}
|
|
|
|
|
|
@router.get('/status', response_model=SweepStatusResponse)
|
|
async def sweep_status():
|
|
running = _sweep_task is not None and not _sweep_task.done()
|
|
return SweepStatusResponse(running=running, last_result=_last_sweep_result)
|