""" AQI Intelligence Engine — Central Configuration Supports local development and HuggingFace Spaces deployment. """ import os import torch from pathlib import Path from dotenv import load_dotenv # Load local environment variables from .env file if it exists load_dotenv(dotenv_path=Path(__file__).parent / ".env") # ============================================================================= # Environment Detection # ============================================================================= IS_HF_SPACE = os.getenv("SPACE_ID") is not None ENV = os.getenv("ENV", "development") # ============================================================================= # ZeroGPU & Dynamic Device Configuration # ============================================================================= try: import spaces HAS_SPACES = True except ImportError: HAS_SPACES = False def get_device() -> str: """Dynamically return 'cuda' if CUDA/ZeroGPU is active, otherwise 'cpu'.""" return "cuda" if torch.cuda.is_available() else "cpu" def get_torch_dtype(): """Return torch.float16 for GPU or torch.float32 for CPU.""" return torch.float16 if get_device() == "cuda" else torch.float32 # Backward compatibility properties DEVICE = get_device() TORCH_DTYPE = get_torch_dtype() # ============================================================================= # Paths # ============================================================================= BASE_DIR = Path(__file__).parent MODELS_DIR = Path(os.getenv("MODELS_DIR", str(BASE_DIR / "models"))) MODELS_DIR.mkdir(parents=True, exist_ok=True) # HuggingFace cache — use /tmp on Spaces (writable), local dir otherwise HF_CACHE_DIR = Path("/tmp/hf_cache") if IS_HF_SPACE else MODELS_DIR / "hf_cache" HF_CACHE_DIR.mkdir(parents=True, exist_ok=True) os.environ["HF_HOME"] = str(HF_CACHE_DIR) os.environ["TRANSFORMERS_CACHE"] = str(HF_CACHE_DIR) # ============================================================================= # Model Identifiers (HuggingFace Hub) # ============================================================================= MODELS = { "timesfm": "google/timesfm-2.5-200m-pytorch", "florence2": "microsoft/Florence-2-base", "grounding_dino": "IDEA-Research/grounding-dino-tiny", "sam2": "facebook/sam2.1-hiera-small", } # ============================================================================= # Forecast Configuration # ============================================================================= FORECAST_CONFIG = { "max_context": 1024, # Max context length for TimesFM 2.5 "max_horizon": 128, # Max forecast horizon "horizon_24h": 24, # Steps for 24-hour forecast "horizon_48h": 48, # Steps for 48-hour forecast "horizon_72h": 72, # Steps for 72-hour forecast } # ============================================================================= # Vision Configuration # ============================================================================= VISION_CONFIG = { "florence2_max_tokens": 1024, "grounding_dino_box_threshold": 0.3, "grounding_dino_text_threshold": 0.25, "pollution_prompts": ( "smoke. fire. construction site. factory chimney. " "dust cloud. burning waste. heavy vehicles. industrial plant. " "brick kiln. open burning." ), } # ============================================================================= # SAM2 Configuration # ============================================================================= SAM2_CONFIG = { "points_per_batch": 32, "pred_iou_thresh": 0.7, "stability_score_thresh": 0.85, } # ============================================================================= # Data API Keys & URLs # ============================================================================= API_KEYS = { "openweather": os.getenv("OPENWEATHER_API_KEY", ""), "mappls": os.getenv("MAPPLS_API_KEY", ""), "sentinel_hub": os.getenv("SENTINEL_HUB_API_KEY", ""), "nasa_firms": os.getenv("NASA_FIRMS_API_KEY", ""), # CPCB (Central Pollution Control Board) — data.gov.in "cpcb_api_key": os.getenv("CPCB_API_KEY", "579b464db66ec23bdd000001cdd3946e44ce4aad7209ff7b23ac571b"), # Mappls (MapMyIndia) OAuth2 credentials "mappls_client_id": os.getenv("MAPPLS_CLIENT_ID", ""), "mappls_client_secret": os.getenv("MAPPLS_CLIENT_SECRET", ""), # Planet Insight Platform (replaces deprecated Sentinel Hub) "planet_api_key": os.getenv("SENTINEL_HUB_API_KEY", ""), # PLAK key "planet_client_id": os.getenv("PLANET_INSIGHT_CLIENT_ID", ""), "planet_client_secret": os.getenv("PLANET_INSIGHT_CLIENT_SECRET", ""), # TomTom "tomtom": os.getenv("TOMTOM_API_KEY", ""), # Provider selection "traffic_provider": os.getenv("TRAFFIC_PROVIDER", "mappls").lower(), } API_URLS = { "openweather_aqi": "http://api.openweathermap.org/data/2.5/air_pollution", "openweather_aqi_history": "http://api.openweathermap.org/data/2.5/air_pollution/history", "openweather_aqi_forecast": "http://api.openweathermap.org/data/2.5/air_pollution/forecast", "open_meteo": "https://api.open-meteo.com/v1/forecast", "open_meteo_historical": "https://archive-api.open-meteo.com/v1/archive", "overpass": "https://overpass-api.de/api/interpreter", "nasa_firms": "https://firms.modaps.eosdis.nasa.gov/api/area/csv", "worldpop": "https://www.worldpop.org/rest/data", # CPCB (data.gov.in) "cpcb_stations": "https://api.data.gov.in/resource/3b01bcb8-0b14-4abf-b6f2-c1bfd384ba69", # Planet Insight / Sentinel Hub APIs "sentinel_hub_auth": "https://services.sentinel-hub.com/auth/realms/main/protocol/openid-connect/token", "sentinel_hub_process": "https://services.sentinel-hub.com/api/v1/process", "sentinel_hub_catalog": "https://services.sentinel-hub.com/api/v1/catalog/1.0.0/search", "planet_data": "https://api.planet.com/data/v1", "planet_basemaps": "https://api.planet.com/basemaps/v1/mosaics", # TomTom "tomtom_traffic": "https://api.tomtom.com/traffic/services/4/flowSegmentData/absolute/10/json", } # ============================================================================= # Cache TTL Settings (in seconds) # ============================================================================= CACHE_TTL = { "aqi": 300, # 5 minutes "weather": 900, # 15 minutes "traffic": 120, # 2 minutes "satellite": 3600, # 1 hour "fire": 600, # 10 minutes "landuse": 86400, # 24 hours "population": 86400, # 24 hours "geospatial": 86400, # 24 hours "cpcb": 1800, # 30 minutes (CPCB stations update hourly) } # ============================================================================= # India AQI Breakpoints (NAQI Standard) # ============================================================================= AQI_BREAKPOINTS = { "good": (0, 50), "satisfactory": (51, 100), "moderate": (101, 200), "poor": (201, 300), "very_poor": (301, 400), "severe": (401, 500), } AQI_CATEGORIES = { "good": {"color": "#00B050", "risk": "minimal", "advisory": "Air quality is good. No precautions needed."}, "satisfactory": {"color": "#92D050", "risk": "low", "advisory": "Acceptable for most. Unusually sensitive may notice symptoms."}, "moderate": {"color": "#FFC000", "risk": "moderate", "advisory": "May cause breathing discomfort to sensitive groups."}, "poor": {"color": "#FF6600", "risk": "high", "advisory": "May cause breathing discomfort to people on prolonged exposure."}, "very_poor": {"color": "#FF0000", "risk": "very_high", "advisory": "May cause respiratory illness on prolonged exposure."}, "severe": {"color": "#800000", "risk": "critical", "advisory": "Serious health effects. Everyone may experience problems."}, } # ============================================================================= # Server Configuration # ============================================================================= SERVER_CONFIG = { "host": "0.0.0.0", "port": int(os.getenv("PORT", 7860)), "reload": ENV == "development", "workers": 1, # Single worker for model memory efficiency }