"""Load immutable runtime settings from environment variables. Settings are created once by the application factory and passed to services. """ from functools import lru_cache from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict APP_VERSION = "2.0.0" class Settings(BaseSettings): """Environment-backed application configuration.""" model_config = SettingsConfigDict( env_prefix="", case_sensitive=False, extra="ignore", ) app_password: str | None = None app_secret_key: str = Field(min_length=1) data_root: str = "/data/loop_logger" env: str = "prod" app_name: str = "Habit Journal" openrouter_api_key: str | None = None openrouter_model: str = "openrouter/free" openrouter_base_url: str = "https://openrouter.ai/api/v1" coach_timeout_sec: float = 25 coach_temperature: float = 0.3 coach_max_tokens: int = 400 coach_history_k: int = 10 coach_trace_limit: int = 200 min_stats_n: int = 5 stats_shrink_k: float = 3 match_alpha: float = 0.5 session_max_age_sec: int = 604800 debug_include_full_brief: bool = False login_rate_limit: int = 10 login_rate_window_sec: int = 900 max_body_bytes: int = 65536 agent_token: str | None = None reschedule_min_gap_min: int = 90 schedule_day_start: str = "06:00" schedule_day_end: str = "23:00" schedule_timezone: str = "Africa/Dar_es_Salaam" schedule_max_blocks: int = 20 @property def is_prod(self) -> bool: """Return whether production-only security behavior is enabled.""" return self.env.lower() == "prod" @lru_cache def get_settings() -> Settings: """Read and cache environment settings.""" return Settings()