""" ISRO Climate Twin — FastAPI Application Entry Point Routes: /api/predict, /api/scenario, /api/chat, /api/data, /api/metrics, /api/health All LLM calls routed via LiteLLM proxy (SKILL.md Rule 5). """ from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles import os import logging from .routes import predict, scenario, chat logger = logging.getLogger(__name__) # ─── Lifespan: startup / shutdown events ────────────────────────────── @asynccontextmanager async def lifespan(app: FastAPI): """Initialize DB, Redis, and model on startup.""" logger.info("🛰️ ISRO Climate Twin API starting up...") # Import here to avoid circular imports from ..db.session import init_db from ..agents.orchestrator import warm_up_graph await init_db() await warm_up_graph() logger.info("✅ All services initialized. Ready to serve.") yield logger.info("🔌 Shutting down gracefully...") # ─── FastAPI App ─────────────────────────────────────────────────────── app = FastAPI( title="ISRO Climate Twin API", description="AI-Powered Digital Twin of India's Climate — Maharashtra Pilot", version="0.1.0", docs_url="/api/docs", redoc_url="/api/redoc", lifespan=lifespan, ) # ─── CORS ───────────────────────────────────────────────────────────── cors_origins = os.getenv( "CORS_ORIGINS", "http://localhost:5173,http://localhost:7860" ).split(",") app.add_middleware( CORSMiddleware, allow_origins=cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ─── Routers ────────────────────────────────────────────────────────── app.include_router(predict.router, prefix="/api", tags=["Forecast"]) app.include_router(scenario.router, prefix="/api", tags=["Scenario"]) app.include_router(chat.router, prefix="/api", tags=["AI Chat"]) @app.get("/api/health", tags=["System"]) async def health(): """Health check endpoint for container readiness probes.""" return {"status": "ok", "service": "isro-climate-twin-api", "version": "0.1.0"} # ─── Serve React static build in production ─────────────────────────── static_dir = os.path.join(os.path.dirname(__file__), "..", "..", "static") if os.path.isdir(static_dir): app.mount("/", StaticFiles(directory=static_dir, html=True), name="static")