import os from typing import List, Optional, Union try: from pydantic_settings import BaseSettings, SettingsConfigDict except ImportError: from pydantic import BaseModel class BaseSettings(BaseModel): # type: ignore def __init__(self, **kwargs): env_kwargs = {} for field_name, field_info in self.model_fields.items(): alias = getattr(field_info, 'alias', None) keys_to_check = [k for k in [alias, field_name.upper(), field_name] if k] for k in keys_to_check: if k in os.environ: env_kwargs[field_name] = os.environ[k] break env_kwargs.update(kwargs) super().__init__(**env_kwargs) SettingsConfigDict = dict # type: ignore from pydantic import Field, field_validator class Settings(BaseSettings): # Environment app_env: str = Field(default="production", alias="APP_ENV") app_name: str = "CropAI Cloud Inference Backend" app_version: str = "1.2.0" debug: bool = Field(default=False, alias="DEBUG") # Server Network host: str = Field(default="0.0.0.0", alias="HOST") port: int = Field(default=8000, alias="PORT") allowed_origins: Union[List[str], str] = Field( default=["*"], alias="ALLOWED_ORIGINS" ) # Google Gemini Multimodal Vision API Key (Confidential Server-Side) gemini_api_key: Optional[str] = Field(default=None, alias="GEMINI_API_KEY") gemini_model_name: str = Field(default="gemini-3.8-flash", alias="GEMINI_MODEL_NAME") # ML Inference Settings primary_hf_model: str = Field( default="linkanjarad/mobilenet_v2_1.0_224-plant-disease-identification", alias="PRIMARY_HF_MODEL" ) rice_hf_model: str = Field( default="prithivMLmods/Rice-Leaf-Disease", alias="RICE_HF_MODEL" ) enable_warmup: bool = True max_image_size_mb: int = Field(default=15, alias="MAX_IMAGE_SIZE_MB") confidence_threshold_warning: float = 0.70 # Rate Limiting & Security rate_limit_per_minute: int = Field(default=120, alias="RATE_LIMIT_PER_MINUTE") secret_key: str = Field(default="cropai-secret-key-prod-2026", alias="SECRET_KEY") # Agronomic Hotlines kisan_helpline: str = "+918817814859" expert_whatsapp: str = "+918817814859" # Cloud Persistence (Google Cloud Firestore / GCP) gcp_project_id: Optional[str] = Field(default=None, alias="GCP_PROJECT_ID") firestore_collection_tickets: str = "cropai_expert_tickets" firestore_collection_diagnoses: str = "cropai_diagnoses" @property def cors_origins(self) -> List[str]: if isinstance(self.allowed_origins, str): return [o.strip() for o in self.allowed_origins.split(",") if o.strip()] return self.allowed_origins model_config = SettingsConfigDict( env_file=os.path.join(os.path.dirname(__file__), ".env"), env_file_encoding="utf-8", extra="ignore" ) settings = Settings()