Spaces:
Sleeping
Sleeping
| """Settings loader for the micro-trend Gradio app. | |
| Loads `settings.json` (same shape as `sample_code/settings.json`) with env | |
| overrides, and exposes a typed Settings object. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Any, Dict, Optional | |
| DEFAULT_SETTINGS_PATH = Path("settings.json") | |
| # Keys mirrored from sample_code/settings.json | |
| SETTING_KEYS = { | |
| "OPENAI_API_KEY", | |
| "GEMINI_API_KEY", | |
| "OPENAI_MODEL", | |
| "OPENAI_REASONING_EFFORT", | |
| "GOOGLE_GENAI_USE_VERTEXAI", | |
| "GOOGLE_CLOUD_PROJECT", | |
| "GOOGLE_CLOUD_LOCATION", | |
| } | |
| DEFAULT_MODEL = "gpt-5-mini" | |
| DEFAULT_REASONING = "medium" | |
| class Settings: | |
| openai_api_key: Optional[str] = None | |
| gemini_api_key: Optional[str] = None | |
| openai_model: str = DEFAULT_MODEL | |
| openai_reasoning_effort: Optional[str] = DEFAULT_REASONING | |
| google_genai_use_vertexai: bool = True | |
| google_cloud_project: Optional[str] = None | |
| google_cloud_location: Optional[str] = None | |
| def require_api_keys(self) -> None: | |
| """Raise if both providers are missing keys.""" | |
| if not self.openai_api_key and not self.gemini_api_key: | |
| raise RuntimeError("No API keys set: provide OPENAI_API_KEY and/or GEMINI_API_KEY via env or settings.json") | |
| def to_payload(self) -> Dict[str, Any]: | |
| """Return a dict useful for client construction/logging.""" | |
| return { | |
| "openai_model": self.openai_model, | |
| "openai_reasoning_effort": self.openai_reasoning_effort, | |
| "google_genai_use_vertexai": self.google_genai_use_vertexai, | |
| "google_cloud_project": self.google_cloud_project, | |
| "google_cloud_location": self.google_cloud_location, | |
| } | |
| def _coerce_bool(value: Any) -> bool: | |
| if isinstance(value, bool): | |
| return value | |
| if isinstance(value, str): | |
| return value.strip().lower() in {"1", "true", "yes", "on"} | |
| return bool(value) | |
| def _load_json(path: Path) -> Dict[str, Any]: | |
| if not path.exists(): | |
| return {} | |
| return json.loads(path.read_text(encoding="utf-8")) | |
| def load_settings(path: Path | None = None) -> Settings: | |
| """ | |
| Load settings with env overrides. | |
| Precedence: env > settings.json > defaults. | |
| """ | |
| settings_path = path or DEFAULT_SETTINGS_PATH | |
| raw = _load_json(settings_path) | |
| # Keep only recognized keys | |
| raw = {k: v for k, v in raw.items() if k in SETTING_KEYS} | |
| def pick(key: str, default: Any = None) -> Any: | |
| env_val = os.environ.get(key) | |
| return env_val if env_val is not None else raw.get(key, default) | |
| return Settings( | |
| openai_api_key=pick("OPENAI_API_KEY"), | |
| gemini_api_key=pick("GEMINI_API_KEY"), | |
| openai_model=pick("OPENAI_MODEL", DEFAULT_MODEL), | |
| openai_reasoning_effort=pick("OPENAI_REASONING_EFFORT", DEFAULT_REASONING), | |
| google_genai_use_vertexai=_coerce_bool(pick("GOOGLE_GENAI_USE_VERTEXAI", True)), | |
| google_cloud_project=pick("GOOGLE_CLOUD_PROJECT"), | |
| google_cloud_location=pick("GOOGLE_CLOUD_LOCATION"), | |
| ) | |