"""Application configuration from environment + runtime overrides.""" from functools import lru_cache from pathlib import Path from pydantic_settings import BaseSettings class Settings(BaseSettings): """App settings loaded from env.""" app_name: str = "Fund Tracer API" debug: bool = False # Database database_url: str = "sqlite+aiosqlite:///./fund_tracer.db" # Uploads upload_dir: Path = Path("./uploads") max_upload_size_mb: int = 20 allowed_extensions: set[str] = {"png", "jpg", "jpeg", "webp"} # LLM llm_provider: str = "openai" # openai | anthropic | deepseek | custom_openai openai_api_key: str | None = None anthropic_api_key: str | None = None deepseek_api_key: str | None = None custom_openai_api_key: str | None = None custom_openai_base_url: str | None = None openai_model: str = "gpt-4o" anthropic_model: str = "claude-3-5-sonnet-20241022" deepseek_model: str = "deepseek-chat" custom_openai_model: str = "gpt-4o-mini" class Config: env_file = ".env" env_file_encoding = "utf-8" extra = "ignore" _runtime_overrides: dict[str, str | None] = {} def _apply_overrides(settings: Settings) -> Settings: for key, value in _runtime_overrides.items(): if hasattr(settings, key): setattr(settings, key, value) return settings @lru_cache def get_settings() -> Settings: return _apply_overrides(Settings()) def update_runtime_settings(payload: dict[str, str | None]) -> Settings: """Update runtime settings and refresh cached Settings object.""" allowed = { "llm_provider", "openai_api_key", "anthropic_api_key", "deepseek_api_key", "custom_openai_api_key", "custom_openai_base_url", "custom_openai_model", } for key, value in payload.items(): if key in allowed: _runtime_overrides[key] = value get_settings.cache_clear() return get_settings() def public_settings() -> dict: s = get_settings() return { "llm_provider": s.llm_provider, "providers": ["openai", "anthropic", "deepseek", "custom_openai"], "models": { "openai": s.openai_model, "anthropic": s.anthropic_model, "deepseek": s.deepseek_model, "custom_openai": s.custom_openai_model, }, "base_urls": { "custom_openai": s.custom_openai_base_url or "", }, "has_keys": { "openai": bool(s.openai_api_key), "anthropic": bool(s.anthropic_api_key), "deepseek": bool(s.deepseek_api_key), "custom_openai": bool(s.custom_openai_api_key), }, }