feat(service): add FastAPI agent service with 5 routers and Docker setup
- 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>
This commit is contained in:
55
agent_service/routers/sweep.py
Normal file
55
agent_service/routers/sweep.py
Normal file
@@ -0,0 +1,55 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user