hackathon / main.py
MohdUmar0223's picture
Update main.py
223c705 verified
Raw
History Blame Contribute Delete
6.34 kB
"""
AQI Intelligence Engine — FastAPI Main Application
The central entry point that:
1. Loads all AI models on startup
2. Registers all API routers
3. Serves both locally and on HuggingFace Spaces (Docker)
"""
import logging
import sys
import io
# Fix Windows console encoding for emoji rendering
if sys.platform.startswith("win"):
try:
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
except AttributeError:
pass
import time
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from config import SERVER_CONFIG, DEVICE
class HealthResponse(BaseModel):
status: str = "ok"
models_loaded: bool = False
device: str = "cpu"
timestamp: datetime = Field(default_factory=datetime.utcnow)
version: str = "2.0.0"
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[logging.StreamHandler(sys.stdout)],
)
logger = logging.getLogger("aqi_engine")
# Track model loading state
models_loaded = False
startup_time = None
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Load AI models on startup, clean up on shutdown."""
global models_loaded, startup_time
logger.info("=" * 60)
logger.info("🚀 AQI Intelligence Engine — Starting Up")
logger.info(f" Device: {DEVICE}")
logger.info("=" * 60)
import asyncio
import anyio
async def load_models_bg():
global models_loaded, startup_time
bg_start = time.time()
try:
from services.forecast.service import load_model as load_forecast
await anyio.to_thread.run_sync(load_forecast)
logger.info("✅ Forecast model loaded successfully")
except Exception as e:
logger.warning(f"Forecast model loading deferred: {e}")
# Heavy vision/segmentation models are loaded on-demand to prevent OOM crashes in container
logger.info("💤 Vision & Segmentation models deferred to on-demand loading")
elapsed = round(time.time() - bg_start, 2)
models_loaded = True
startup_time = elapsed
logger.info(f"✅ Startup models loaded in {elapsed}s")
# Start loading in background without blocking port binding
asyncio.create_task(load_models_bg())
logger.info(f"📡 API docs: http://localhost:{SERVER_CONFIG['port']}/docs")
logger.info("=" * 60)
yield # Application runs here
logger.info("Shutting down AQI Intelligence Engine...")
# =============================================================================
# FastAPI Application
# =============================================================================
app = FastAPI(
title="AQI Intelligence Engine",
description=(
"AI-powered Urban Air Quality Intelligence Platform for Smart City Intervention. "
"Provides AQI forecasting (TimesFM 2.5), satellite vision analysis (Florence-2 + "
"Grounding DINO), segmentation (SAM2), geospatial analysis, hotspot detection, "
"atmospheric dispersion modeling, source attribution, health risk assessment, "
"and inspector route optimization."
),
version="2.0.0",
lifespan=lifespan,
)
# =============================================================================
# CORS Middleware (allow Next.js frontend to call this API)
# =============================================================================
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production: restrict to your Next.js domain
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# =============================================================================
# Register Routers
# =============================================================================
from routers.services import services_router
app.include_router(services_router, prefix="/api/v1")
app.include_router(services_router, prefix="/api")
app.include_router(services_router)
# =============================================================================
# Mount Frontend Static Files & Serve index.html at Root
# =============================================================================
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import os
# Create frontend directory if it doesn't exist yet
os.makedirs("frontend", exist_ok=True)
# Mount the static directory
app.mount("/frontend", StaticFiles(directory="frontend"), name="frontend")
# =============================================================================
# Health Check
# =============================================================================
@app.get("/health", response_model=HealthResponse, tags=["System"])
async def health_check():
"""Health check endpoint — verifies the server and models are running."""
return HealthResponse(
status="ok",
models_loaded=models_loaded,
device=DEVICE,
timestamp=datetime.now(timezone.utc),
version="2.0.0",
)
@app.get("/", tags=["System"])
async def root():
"""Root endpoint serving the interactive control center dashboard."""
index_path = os.path.join("frontend", "index.html")
if os.path.exists(index_path):
return FileResponse(index_path)
return {
"name": "AQI Intelligence Engine",
"version": "2.0.0",
"message": "Frontend dashboard not built yet. Visit /docs for APIs.",
"docs": "/docs",
"health": "/health"
}
# =============================================================================
# Run with: uvicorn main:app --reload --port 7860
# =============================================================================
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host=SERVER_CONFIG["host"],
port=SERVER_CONFIG["port"],
reload=SERVER_CONFIG["reload"],
workers=SERVER_CONFIG["workers"],
reload_excludes=["models/*", "**/models/*", "*.json", "frontend/*"],
)