from dataclasses import dataclass import json from pathlib import Path import os from uuid import UUID from dotenv import load_dotenv @dataclass(frozen=True, slots=True) class Settings: port: int api_key: str | None base_url: str model: str default_system_prompt: str data_dir: Path api_key_whitelist: dict[str, str] max_tool_rounds: int = 5 max_tool_calls_per_turn: int = 10 tool_timeout_seconds: float = 5.0 @classmethod def from_env(cls) -> "Settings": load_dotenv(dotenv_path=Path.cwd() / ".env", override=False) return cls( port=int(os.getenv("SERVICE_PORT", "8000")), api_key=os.getenv("DEEPSEEK_API_KEY"), base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"), model=os.getenv("DEEPSEEK_MODEL", "deepseek-v4-flash"), default_system_prompt=os.getenv( "DEFAULT_SYSTEM_PROMPT", "You are a helpful assistant." ), data_dir=Path(os.getenv("CHAT_DATA_DIR", "data")), api_key_whitelist=_parse_api_key_whitelist( os.getenv("API_KEY_WHITELIST", "{}") ), max_tool_rounds=int(os.getenv("MAX_TOOL_ROUNDS", "5")), max_tool_calls_per_turn=int( os.getenv("MAX_TOOL_CALLS_PER_TURN", "10") ), tool_timeout_seconds=float(os.getenv("TOOL_TIMEOUT_SECONDS", "5")), ) def _parse_api_key_whitelist(value: str) -> dict[str, str]: try: parsed = json.loads(value) except json.JSONDecodeError as exc: raise ValueError("API_KEY_WHITELIST must be valid JSON") from exc if not isinstance(parsed, dict): raise ValueError("API_KEY_WHITELIST must map user UUIDs to API keys") normalized: dict[str, str] = {} for user_id, api_key in parsed.items(): if not isinstance(user_id, str) or not isinstance(api_key, str) or not api_key: raise ValueError("API_KEY_WHITELIST must map user UUIDs to API keys") try: normalized_user_id = str(UUID(user_id)) except ValueError as exc: raise ValueError("API_KEY_WHITELIST user IDs must be valid UUIDs") from exc if normalized_user_id in normalized: raise ValueError("API_KEY_WHITELIST contains duplicate user UUIDs") normalized[normalized_user_id] = api_key if len(set(normalized.values())) != len(normalized): raise ValueError("API keys in API_KEY_WHITELIST must be unique") return normalized