File size: 2,926 Bytes
a3b21d2
 
 
 
 
 
 
 
 
 
 
 
 
 
0c02b78
a3b21d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""
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")