| """Configuration management.""" |
| import json |
| import os |
| from typing import Any, Callable |
|
|
| DEFAULT_CONFIG = { |
| "port": 7860, |
| "host": "0.0.0.0", |
| "retry_attempts": 3, |
| "retry_delay_sec": 2, |
| "request_timeout_sec": 180, |
| "gemini_bl": "boq_assistant-bard-web-server_20260525.09_p0", |
| "auth_user": None, |
| "xsrf_token": None, |
| "default_model": "gemini-3.5-flash", |
| "log_requests": True, |
| "cookie_file": None, |
| "proxy": None, |
| "api_keys": [], |
| } |
|
|
| CONFIG = dict(DEFAULT_CONFIG) |
|
|
|
|
| def _env_value(*names: str) -> str | None: |
| """Return the first non-empty environment variable value.""" |
| for name in names: |
| value = os.environ.get(name) |
| if value is not None and value != "": |
| return value |
| return None |
|
|
|
|
| def _to_bool(value: str) -> bool: |
| return value.strip().lower() in {"1", "true", "yes", "y", "on"} |
|
|
|
|
| def _to_int(value: str) -> int: |
| return int(value.strip()) |
|
|
|
|
| def _to_nullable_str(value: str) -> str | None: |
| value = value.strip() |
| if value.lower() in {"", "none", "null"}: |
| return None |
| return value |
|
|
|
|
| def _to_list(value: str) -> list[str]: |
| return [item.strip() for item in value.split(",") if item.strip()] |
|
|
|
|
| def load_config(path: str = None): |
| """Load config from JSON file.""" |
| if path and os.path.exists(path): |
| with open(path, encoding="utf-8") as f: |
| CONFIG.update(json.load(f)) |
| return CONFIG |
|
|
|
|
| def apply_env_overrides(): |
| """Apply environment variables with highest priority. |
| |
| Supported variables are documented in .env.example. Short names such as PORT |
| are accepted first, and GEMINI_WEB2API_* aliases are also supported. |
| """ |
| scalar_mappings: list[tuple[str, Callable[[str], Any], tuple[str, ...]]] = [ |
| ("port", _to_int, ("PORT", "GEMINI_WEB2API_PORT")), |
| ("host", str, ("HOST", "GEMINI_WEB2API_HOST")), |
| ("retry_attempts", _to_int, ("RETRY_ATTEMPTS", "GEMINI_WEB2API_RETRY_ATTEMPTS")), |
| ("retry_delay_sec", _to_int, ("RETRY_DELAY_SEC", "GEMINI_WEB2API_RETRY_DELAY_SEC")), |
| ("request_timeout_sec", _to_int, ("REQUEST_TIMEOUT_SEC", "GEMINI_WEB2API_REQUEST_TIMEOUT_SEC")), |
| ("gemini_bl", str, ("GEMINI_BL", "GEMINI_WEB2API_GEMINI_BL")), |
| ("auth_user", _to_nullable_str, ("AUTH_USER", "GEMINI_AUTH_USER", "GEMINI_WEB2API_AUTH_USER")), |
| ("xsrf_token", _to_nullable_str, ("XSRF_TOKEN", "GEMINI_XSRF_TOKEN", "GEMINI_WEB2API_XSRF_TOKEN")), |
| ("default_model", str, ("DEFAULT_MODEL", "GEMINI_DEFAULT_MODEL", "GEMINI_WEB2API_DEFAULT_MODEL")), |
| ("log_requests", _to_bool, ("LOG_REQUESTS", "GEMINI_WEB2API_LOG_REQUESTS")), |
| ("cookie_file", _to_nullable_str, ("COOKIE_FILE", "GEMINI_COOKIE_FILE", "GEMINI_WEB2API_COOKIE_FILE")), |
| ("proxy", _to_nullable_str, ("PROXY", "HTTP_PROXY_URL", "GEMINI_WEB2API_PROXY")), |
| ] |
|
|
| for key, caster, names in scalar_mappings: |
| value = _env_value(*names) |
| if value is None: |
| continue |
| CONFIG[key] = caster(value) |
|
|
| |
| api_key = _env_value("API_KEY", "GEMINI_API_KEY", "GEMINI_WEB2API_API_KEY") |
| if api_key is not None: |
| CONFIG["api_keys"] = [api_key.strip()] if api_key.strip() else [] |
| return CONFIG |
|
|
| api_keys = _env_value("API_KEYS", "GEMINI_API_KEYS", "GEMINI_WEB2API_API_KEYS") |
| if api_keys is not None: |
| CONFIG["api_keys"] = _to_list(api_keys) |
|
|
| return CONFIG |
|
|
|
|
| def find_config(): |
| """Search for config file in standard locations.""" |
| for p in ["./config.json", os.path.expanduser("~/.config/gemini-web2api/config.json")]: |
| if os.path.exists(p): |
| return p |
| return None |
|
|