| """
|
| Application configuration using Pydantic Settings.
|
|
|
| Loads configuration from environment variables and .env file.
|
| """
|
|
|
| from functools import lru_cache
|
| from typing import Literal
|
|
|
| import torch
|
| from pydantic import Field
|
| from pydantic import field_validator
|
| from pydantic_settings import BaseSettings
|
| from pydantic_settings import SettingsConfigDict
|
|
|
|
|
| class Settings(BaseSettings):
|
| """Application settings loaded from environment variables."""
|
|
|
| model_config = SettingsConfigDict(
|
| env_file=".env",
|
| env_file_encoding="utf-8",
|
| case_sensitive=False,
|
| extra="ignore",
|
| )
|
|
|
|
|
|
|
|
|
| APP_NAME: str = "VoiceAuth API"
|
| APP_VERSION: str = "1.0.0"
|
| DEBUG: bool = False
|
| HOST: str = "0.0.0.0"
|
| PORT: int = 8000
|
|
|
|
|
|
|
|
|
| API_KEYS: str = Field(
|
| default="",
|
| description="Comma-separated list of valid API keys",
|
| )
|
| CORS_ORIGINS: str = Field(
|
| default="http://localhost:3000,http://localhost:8000",
|
| description="Comma-separated list of allowed CORS origins",
|
| )
|
| RATE_LIMIT_REQUESTS: int = Field(default=100, ge=1)
|
| RATE_LIMIT_PERIOD: int = Field(default=60, ge=1, description="Period in seconds")
|
|
|
|
|
|
|
|
|
| MODEL_NAME: str = "facebook/wav2vec2-base"
|
| MODEL_PATH: str = ""
|
| DEVICE: str = "auto"
|
| MAX_AUDIO_DURATION: float = Field(default=30.0, ge=1.0)
|
| MIN_AUDIO_DURATION: float = Field(default=0.5, ge=0.1)
|
| SAMPLE_RATE: int = Field(default=16000, ge=8000, le=48000)
|
|
|
|
|
|
|
|
|
| REDIS_URL: str = "redis://localhost:6379"
|
| REDIS_DB: int = 0
|
|
|
|
|
|
|
|
|
| LOG_LEVEL: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO"
|
| LOG_FORMAT: Literal["json", "console"] = "json"
|
|
|
|
|
|
|
|
|
| @property
|
| def api_keys_list(self) -> list[str]:
|
| """Parse comma-separated API keys into a list."""
|
| if not self.API_KEYS:
|
| return []
|
| return [key.strip() for key in self.API_KEYS.split(",") if key.strip()]
|
|
|
| @property
|
| def cors_origins_list(self) -> list[str]:
|
| """Parse comma-separated CORS origins into a list."""
|
| if not self.CORS_ORIGINS:
|
| return []
|
| return [origin.strip() for origin in self.CORS_ORIGINS.split(",") if origin.strip()]
|
|
|
| @property
|
| def torch_device(self) -> str:
|
| """Determine the appropriate torch device."""
|
| if self.DEVICE == "auto":
|
| return "cuda" if torch.cuda.is_available() else "cpu"
|
| return self.DEVICE
|
|
|
| @property
|
| def model_identifier(self) -> str:
|
| """Get the model path or name to load."""
|
| return self.MODEL_PATH if self.MODEL_PATH else self.MODEL_NAME
|
|
|
|
|
|
|
|
|
| @field_validator("DEVICE")
|
| @classmethod
|
| def validate_device(cls, v: str) -> str:
|
| """Validate device configuration."""
|
| valid_devices = {"auto", "cpu", "cuda", "mps"}
|
|
|
| if v.startswith("cuda:"):
|
| return v
|
| if v not in valid_devices:
|
| raise ValueError(f"Device must be one of {valid_devices} or 'cuda:N' format")
|
| return v
|
|
|
|
|
| @lru_cache
|
| def get_settings() -> Settings:
|
| """
|
| Get cached settings instance.
|
|
|
| Uses lru_cache to ensure settings are only loaded once.
|
| """
|
| return Settings()
|
|
|