Files
ai-agent/backend/core/routes.py
Gabriel Vidal 0ff9e40242 refactor(backend): split main.py into domain packages with app.state injection
main.py (5146 lines, 106 routes) becomes an assembly only: one package per
domain — core, files, settings, dashboards, conversations, diff,
notifications, forms, runs, accounts, workers, models, cron, agents,
projects, services, goals, memories, plans, templates — each exposing an
APIRouter; the flat domain modules move into their package behind a barrel
that keeps the old `import conversations` / `import projects` spellings.

The shared singletons (store, meta_store, hub, indexer, …) are built once by
core.state.build_state() and attached to app.state.ai; routes take them as
the `deps: State` dependency and helpers as an explicit `deps: AppState`.
conversations/pricing.py carries the per-model rates out of the parser.

Verified: route table and OpenAPI byte-identical; 90 read endpoints
golden-diffed against the monolith on a copy of the live data (identical);
write routes smoke-tested; 66 backend tests pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 23:55:47 +02:00

52 lines
1.5 KiB
Python

"""App-level routes: the SSE event stream, deploy status and health."""
import queue
from fastapi import APIRouter
from fastapi.responses import StreamingResponse
import schemas
from core.http import _r
from core.state import State
from deploy_status import read_status as read_deploy_status
router = APIRouter()
@router.get("/api/events")
def events(deps: State):
"""Server-Sent Events: ``meta`` / ``transcript`` change pings for the UI."""
q = deps.hub.subscribe()
def gen():
try:
yield "retry: 3000\n\n"
while True:
try:
data = q.get(timeout=15)
yield f"data: {data}\n\n"
except queue.Empty:
yield ": ping\n\n" # keep proxies from closing the stream
finally:
deps.hub.unsubscribe(q)
return StreamingResponse(
gen(), media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no",
"Connection": "keep-alive"})
@router.get("/api/deploy-status", responses=_r(schemas.DeployStatus))
def deploy_status():
"""Current blue-green deploy snapshot (or ``null`` when none is in flight).
Written by ``services/ai-agent/deploy.sh`` into the data dir; polled by the
:class:`DeployWatcher`, which pings a ``deploy`` SSE event on any change.
"""
return read_deploy_status()
@router.get("/api/health")
def health():
return {"ok": True}