Files
odoo-ai/agent_service/routers/registry.py
ActiveBlue Build 430ab966b2 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>
2026-04-12 17:54:28 -04:00

99 lines
3.4 KiB
Python

from __future__ import annotations
import logging
from typing import Optional
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel
logger = logging.getLogger(__name__)
router = APIRouter(prefix='/registry', tags=['registry'])
class AgentInfo(BaseModel):
name: str
domain: str
active: bool
backend: str = 'ollama'
last_seen: Optional[str] = None
class BackendOverride(BaseModel):
agent_name: str
backend: str # ollama | claude
set_by: str
note: Optional[str] = None
@router.get('/agents', response_model=list[AgentInfo])
async def list_agents():
from ..app_state import get_agent_registry, get_llm_router
registry = get_agent_registry()
llm_router = get_llm_router()
if registry is None:
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail='Registry not ready')
agents = registry.get_all()
result = []
for agent in agents:
backend = 'ollama'
if llm_router:
try:
backend = await llm_router.get_backend(agent['name'])
except Exception:
pass
result.append(AgentInfo(
name=agent['name'],
domain=agent.get('domain', ''),
active=agent.get('active', True),
backend=backend,
last_seen=agent.get('last_seen'),
))
return result
@router.post('/sync', status_code=status.HTTP_200_OK)
async def sync_registry():
from ..app_state import get_agent_registry
registry = get_agent_registry()
if registry is None:
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail='Registry not ready')
try:
count = await registry.sync()
return {'synced': count}
except Exception as exc:
logger.error('registry sync failed: %s', exc)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc))
@router.post('/backend', status_code=status.HTTP_200_OK)
async def set_backend_override(req: BackendOverride):
from ..app_state import get_llm_router
llm_router = get_llm_router()
if llm_router is None:
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail='LLM router not ready')
if req.backend not in ('ollama', 'claude'):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='backend must be ollama or claude')
try:
await llm_router.set_backend_override(
caller=req.agent_name,
backend=req.backend,
set_by=req.set_by,
note=req.note,
)
return {'agent': req.agent_name, 'backend': req.backend}
except Exception as exc:
logger.error('set_backend_override failed: %s', exc)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc))
@router.delete('/backend/{agent_name}', status_code=status.HTTP_200_OK)
async def reset_backend_override(agent_name: str):
from ..app_state import get_llm_router
llm_router = get_llm_router()
if llm_router is None:
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail='LLM router not ready')
try:
await llm_router.reset_backend_override(caller=agent_name)
return {'agent': agent_name, 'reset': True}
except Exception as exc:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc))