"""Configuration management with validation.""" import os from dataclasses import dataclass, field from pathlib import Path from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() class ConfigurationError(Exception): """Raised when configuration is invalid.""" pass @dataclass class Settings: """Application settings loaded from environment variables.""" # LLM Configuration deepseek_api_key: str = field( default_factory=lambda: os.getenv("DEEPSEEK_API_KEY", "") ) deepseek_base_url: str = field( default_factory=lambda: os.getenv( "DEEPSEEK_BASE_URL", "https://api.deepseek.com" ) ) model: str = field( default_factory=lambda: os.getenv("DEEPSEEK_MODEL", "deepseek-chat") ) temperature: float = field( default_factory=lambda: float(os.getenv("TEMPERATURE", "0.2")) ) # TMDB Configuration tmdb_api_key: str = field(default_factory=lambda: os.getenv("TMDB_API_KEY", "")) tmdb_base_url: str = field( default_factory=lambda: os.getenv( "TMDB_BASE_URL", "https://api.themoviedb.org/3" ) ) # Storage Configuration memory_file: str = field( default_factory=lambda: os.getenv("MEMORY_FILE", "memory.json") ) # Security Configuration max_tool_iterations: int = field( default_factory=lambda: int(os.getenv("MAX_TOOL_ITERATIONS", "5")) ) request_timeout: int = field( default_factory=lambda: int(os.getenv("REQUEST_TIMEOUT", "30")) ) # Memory Configuration max_history_messages: int = field( default_factory=lambda: int(os.getenv("MAX_HISTORY_MESSAGES", "10")) ) def __post_init__(self): """Validate settings after initialization.""" self._validate() def _validate(self) -> None: """Validate configuration values.""" # Validate temperature if not 0.0 <= self.temperature <= 2.0: raise ConfigurationError( f"Temperature must be between 0.0 and 2.0, got {self.temperature}" ) # Validate max_tool_iterations if self.max_tool_iterations < 1 or self.max_tool_iterations > 20: raise ConfigurationError( f"max_tool_iterations must be between 1 and 20, got {self.max_tool_iterations}" ) # Validate request_timeout if self.request_timeout < 1 or self.request_timeout > 300: raise ConfigurationError( f"request_timeout must be between 1 and 300 seconds, got {self.request_timeout}" ) # Validate URLs if not self.deepseek_base_url.startswith(("http://", "https://")): raise ConfigurationError( f"Invalid deepseek_base_url: {self.deepseek_base_url}" ) if not self.tmdb_base_url.startswith(("http://", "https://")): raise ConfigurationError(f"Invalid tmdb_base_url: {self.tmdb_base_url}") # Validate memory file path memory_path = Path(self.memory_file) if memory_path.exists() and not memory_path.is_file(): raise ConfigurationError( f"memory_file exists but is not a file: {self.memory_file}" ) def is_deepseek_configured(self) -> bool: """Check if DeepSeek API is properly configured.""" return bool(self.deepseek_api_key and self.deepseek_base_url) def is_tmdb_configured(self) -> bool: """Check if TMDB API is properly configured.""" return bool(self.tmdb_api_key and self.tmdb_base_url) # Global settings instance settings = Settings()