from contextlib import asynccontextmanager import logging from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s", ) logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING) logging.getLogger("sqlalchemy.pool").setLevel(logging.WARNING) from app.api.middleware.rate_limit import TierRateLimitMiddleware from app.api.middleware.sanitizer import SanitizerMiddleware from app.config.settings import settings @asynccontextmanager async def lifespan(app: FastAPI): # Startup: ensure agent tool modules are loaded. import app.agents # noqa: F401 yield # Shutdown: dispose SQLAlchemy connection pool from app.db import engine await engine.dispose() def create_app() -> FastAPI: app = FastAPI( title="AdiuvAI Cloud API", version="0.1.0", docs_url="/docs" if settings.ENV == "dev" else None, redoc_url=None, lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Middleware stack (Starlette inserts at position 0, so last-added = outermost). # Request flow: TierRateLimit → Sanitizer → CORS → Router # Response flow: Router → CORS → Sanitizer → TierRateLimit app.add_middleware(SanitizerMiddleware) app.add_middleware(TierRateLimitMiddleware) from app.api.routes import agents, auth, billing, chat, device_ws, memory app.include_router(auth.router, prefix="/api/v1") app.include_router(chat.router, prefix="/api/v1") app.include_router(billing.router, prefix="/api/v1") app.include_router(agents.router, prefix="/api/v1") app.include_router(device_ws.router, prefix="/api/v1") app.include_router(memory.router, prefix="/api/v1") @app.get("/api/v1/health", tags=["health"]) async def health() -> dict: return {"status": "ok", "version": app.version} return app app = create_app()