image-understanding / settings.py
shahkushan1's picture
Add Gradio micro-trend app with LLM integrations and prompt loading
2948ced
"""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"
@dataclass
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"),
)