File size: 5,663 Bytes
eda316b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6af95df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eda316b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6af95df
 
 
 
 
 
 
 
 
 
eda316b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"""Environment bootstrap (``.env``) and cache path helpers."""

from __future__ import annotations

import os
from pathlib import Path
from typing import Literal

_BOOTSTRAPPED = False
LLMProvider = Literal["google", "openrouter"]
OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"


def bootstrap_env() -> None:
    """Load ``.env`` from the process cwd (non-fatal if missing). Safe to call twice."""
    global _BOOTSTRAPPED
    if _BOOTSTRAPPED:
        return
    try:
        from dotenv import load_dotenv

        load_dotenv()
    except ImportError:
        pass
    _BOOTSTRAPPED = True


def default_humeo_cache_root() -> Path:
    """Default cache root: ``~/.cache/humeo`` on Unix; ``%LOCALAPPDATA%/humeo`` on Windows."""
    override = (os.environ.get("HUMEO_CACHE_ROOT") or "").strip()
    if override:
        return Path(override)
    if os.name == "nt":
        base = Path(os.environ.get("LOCALAPPDATA", str(Path.home() / "AppData" / "Local")))
        return base / "humeo"
    return Path.home() / ".cache" / "humeo"


def resolve_gemini_api_key() -> str:
    """Return an API key for Gemini, or raise if none is configured.



    Prefer ``GOOGLE_API_KEY``; fall back to ``GEMINI_API_KEY``. Values are read from

    the environment after ``bootstrap_env()`` (``.env`` in cwd).



    We require an explicit key so we do not fall back to Application Default

    Credentials (e.g. ``gcloud auth application-default login``), which often

    lack the Generative Language API scope and produce

    ``403 ACCESS_TOKEN_SCOPE_INSUFFICIENT``.

    """
    bootstrap_env()
    for env_name in ("GOOGLE_API_KEY", "GEMINI_API_KEY"):
        val = (os.environ.get(env_name) or "").strip()
        if val:
            return val
    raise ValueError(
        "Set GOOGLE_API_KEY or GEMINI_API_KEY for Gemini clip selection. "
        "See docs/ENVIRONMENT.md. Without an API key the client may use ADC and fail "
        "with insufficient scopes (403)."
    )


def resolve_openrouter_api_key() -> str:
    """Return the OpenRouter API key, or raise if missing."""
    return resolve_openrouter_api_keys()[0]


def resolve_openrouter_api_keys() -> list[str]:
    """Return OpenRouter API keys in failover order, or raise if missing.

    ``OPENROUTER_API_KEY`` remains the primary key. Optional backups can be
    supplied with ``OPENROUTER_API_KEY_BACKUP``, ``OPENROUTER_API_KEY_2``, or
    a comma/newline-separated ``OPENROUTER_API_KEYS`` value.
    """
    bootstrap_env()
    keys: list[str] = []
    for env_name in (
        "OPENROUTER_API_KEY",
        "OPENROUTER_API_KEY_BACKUP",
        "OPENROUTER_API_KEY_2",
    ):
        val = (os.environ.get(env_name) or "").strip()
        if val:
            keys.append(val)

    extra = (os.environ.get("OPENROUTER_API_KEYS") or "").strip()
    if extra:
        for item in extra.replace("\n", ",").split(","):
            val = item.strip()
            if val:
                keys.append(val)

    deduped = list(dict.fromkeys(keys))
    if deduped:
        return deduped
    raise ValueError(
        "Set OPENROUTER_API_KEY to use OpenRouter as the backend for the Gemini stages. "
        "See docs/ENVIRONMENT.md."
    )


def current_llm_provider() -> LLMProvider | None:
    """Best-effort active backend detection from the environment.



    ``HUMEO_LLM_PROVIDER`` overrides key-based auto-detection when set.

    """
    bootstrap_env()
    forced = (os.environ.get("HUMEO_LLM_PROVIDER") or "auto").strip().lower()
    if forced in ("google", "openrouter"):
        return forced  # type: ignore[return-value]
    if (os.environ.get("GOOGLE_API_KEY") or "").strip():
        return "google"
    if (os.environ.get("GEMINI_API_KEY") or "").strip():
        return "google"
    if any(
        (os.environ.get(name) or "").strip()
        for name in (
            "OPENROUTER_API_KEY",
            "OPENROUTER_API_KEY_BACKUP",
            "OPENROUTER_API_KEY_2",
            "OPENROUTER_API_KEYS",
        )
    ):
        return "openrouter"
    return None


def resolve_llm_provider() -> LLMProvider:
    """Return the active backend for Gemini-like stages, or raise if none is configured."""
    provider = current_llm_provider()
    if provider is not None:
        if provider == "google":
            resolve_gemini_api_key()
        else:
            resolve_openrouter_api_key()
        return provider
    raise ValueError(
        "Set GOOGLE_API_KEY or GEMINI_API_KEY for the Google Gemini SDK, "
        "or set OPENROUTER_API_KEY to route these stages through OpenRouter. "
        "You can also force the backend with HUMEO_LLM_PROVIDER=google|openrouter."
    )


def model_name_for_provider(model_name: str, provider: LLMProvider) -> str:
    """Normalize model identifiers between Google Gemini SDK and OpenRouter.



    - Google SDK expects bare Gemini ids like ``gemini-3.1-flash-lite-preview``.

    - OpenRouter expects provider-qualified ids like

      ``google/gemini-3.1-flash-lite-preview``.

    """
    name = model_name.strip()
    if provider == "openrouter":
        if "/" not in name and name.startswith(("gemini-", "gemma-")):
            return f"google/{name}"
        return name
    if provider == "google" and name.startswith("google/"):
        return name.split("/", 1)[1]
    return name


def openrouter_default_headers() -> dict[str, str]:
    """Headers that help identify Humeo traffic to OpenRouter."""
    return {
        "HTTP-Referer": "https://github.com/frenzy2004/shortform",
        "X-OpenRouter-Title": "Humeo",
    }