Spaces:
Sleeping
Sleeping
| """MealGraph configuration. | |
| This module exposes a Pydantic-Settings ``Settings`` singleton plus a small | |
| backward-compatibility shim so existing code can still read ``config.DEBUG_MODE``, | |
| ``config.LOG_DIR``, etc. New code should import :func:`get_settings` directly:: | |
| from config import get_settings | |
| settings = get_settings() | |
| if settings.debug_mode: | |
| ... | |
| Mutation must go through :func:`set_settings` (the legacy ``config.X = y`` write | |
| pattern would otherwise silently shadow the Pydantic value). | |
| """ | |
| from __future__ import annotations | |
| from typing import Any, Dict, List, Optional | |
| from pydantic import Field | |
| from pydantic_settings import BaseSettings, SettingsConfigDict | |
| class Settings(BaseSettings): | |
| """Process-wide configuration. | |
| Values are loaded from (in order of precedence): direct ``set_settings`` | |
| calls, environment variables prefixed with ``MEALGRAPH_``, the ``.env`` | |
| file in the project root, and the defaults declared here. | |
| """ | |
| model_config = SettingsConfigDict( | |
| env_prefix="MEALGRAPH_", | |
| env_file=".env", | |
| env_file_encoding="utf-8", | |
| extra="ignore", | |
| case_sensitive=False, | |
| validate_assignment=True, | |
| ) | |
| # --- Logging / persistence ------------------------------------------------- | |
| log_dir: Optional[str] = None | |
| persistence_dir: Optional[str] = None | |
| # --- Debug switches -------------------------------------------------------- | |
| debug_mode: bool = False | |
| debug_level: str = "full" # 'full' or 'output' | |
| debug_scopes: Dict[str, List[str]] = Field( | |
| default_factory=lambda: {"agents": ["all"], "tools": ["all"]} | |
| ) | |
| # --- LLM / rate limiting --------------------------------------------------- | |
| enable_rate_limiting: bool = True | |
| gemini_api_keys: List[str] = Field(default_factory=list) | |
| # Singleton holder. Instantiated lazily so tests can set env vars before first read. | |
| _settings: Optional[Settings] = None | |
| def get_settings() -> Settings: | |
| """Return the process-wide ``Settings`` instance, creating it on first call.""" | |
| global _settings | |
| if _settings is None: | |
| _settings = Settings() | |
| return _settings | |
| def reset_settings() -> None: | |
| """Drop the cached singleton so the next ``get_settings`` call re-reads env. | |
| Intended for use in tests. | |
| """ | |
| global _settings | |
| _settings = None | |
| def set_settings(**updates: Any) -> Settings: | |
| """Update fields on the singleton ``Settings``. | |
| Accepts both legacy upper-case names (``DEBUG_MODE``) and Pydantic field | |
| names (``debug_mode``). Returns the updated settings instance. | |
| """ | |
| s = get_settings() | |
| for raw_key, value in updates.items(): | |
| attr = _LEGACY_ATTR_MAP.get(raw_key, raw_key.lower()) | |
| if not hasattr(s, attr): | |
| raise AttributeError(f"Settings has no attribute {attr!r}") | |
| setattr(s, attr, value) | |
| return s | |
| # --- Legacy attribute proxy ---------------------------------------------------- | |
| # Existing code does ``import config`` then reads ``config.DEBUG_MODE`` etc. | |
| # PEP 562 ``__getattr__`` lets us forward those reads to the singleton. | |
| _LEGACY_ATTR_MAP: Dict[str, str] = { | |
| "DEBUG_MODE": "debug_mode", | |
| "DEBUG_LEVEL": "debug_level", | |
| "DEBUG_SCOPES": "debug_scopes", | |
| "LOG_DIR": "log_dir", | |
| "PERSISTENCE_DIR": "persistence_dir", | |
| "ENABLE_RATE_LIMITING": "enable_rate_limiting", | |
| } | |
| def __getattr__(name: str) -> Any: # noqa: D401 — module-level dunder | |
| """PEP 562: forward legacy CONST-style reads to the Settings singleton.""" | |
| if name in _LEGACY_ATTR_MAP: | |
| return getattr(get_settings(), _LEGACY_ATTR_MAP[name]) | |
| raise AttributeError(f"module {__name__!r} has no attribute {name!r}") | |
| __all__ = ["Settings", "get_settings", "reset_settings", "set_settings"] | |