ZhuLinsen/daily_stock_analysis · error · ValueError

Hermes only supports PROTOCOL=openai

Error message

Hermes only supports PROTOCOL=openai

What it means

Hermes LLM gateway config guard (src/llm/hermes.py): canonicalize_hermes_protocol normalizes PROTOCOL (strip+lower, defaulting HERMES_DEFAULT_PROTOCOL='openai') and requires exactly 'openai'. The Hermes bridge only implements the OpenAI-compatible wire protocol; any other value (anthropic, gemini, etc.) is a misconfiguration and fails fast.

Source

Thrown at src/llm/hermes.py:155

    for secret in sorted(values, key=len, reverse=True):
        if secret:
            sanitized = sanitized.replace(secret, "[REDACTED]")
    patterns = [
        (r"(?i)(authorization\s*[:=]\s*)(bearer\s+)?([^\s,;]+)", r"\1[REDACTED]"),
        (r"(?i)(api[_-]?key\s*[:=]\s*)([^\s,;]+)", r"\1[REDACTED]"),
        (r"(?i)(cookie\s*[:=]\s*)([^\s,;]+)", r"\1[REDACTED]"),
        (r"(?i)bearer\s+[a-z0-9._\-]+", "Bearer [REDACTED]"),
        (r"(?i)sk-[a-z0-9_\-]+", "[REDACTED]"),
    ]
    for pattern, replacement in patterns:
        sanitized = re.sub(pattern, replacement, sanitized)
    return " ".join(sanitized.split())[:300]


def canonicalize_hermes_protocol(protocol: str) -> str:
    normalized = (protocol or HERMES_DEFAULT_PROTOCOL).strip().lower() or HERMES_DEFAULT_PROTOCOL
    if normalized != HERMES_DEFAULT_PROTOCOL:
        raise ValueError("Hermes only supports PROTOCOL=openai")
    return HERMES_DEFAULT_PROTOCOL


def canonicalize_hermes_base_url(base_url: str) -> str:
    """Return canonical Hermes base URL or raise ValueError.

    Allowed forms are loopback HTTP(S) URLs whose path is exactly /v1 or /v1/.
    localhost is canonicalized to 127.0.0.1 to avoid DNS/hosts ambiguity.
    """

    raw = (base_url or HERMES_DEFAULT_BASE_URL).strip() or HERMES_DEFAULT_BASE_URL
    parsed = urlparse(raw)
    if parsed.scheme.lower() not in {"http", "https"}:
        raise ValueError("Hermes BASE_URL must use http or https")
    if not parsed.netloc or not parsed.hostname:
        raise ValueError("Hermes BASE_URL must include a loopback host")
    if parsed.username or parsed.password:
        raise ValueError("Hermes BASE_URL must not include userinfo")

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Set PROTOCOL=openai (or omit it — 'openai' is the default when empty).
  2. Route non-OpenAI-protocol providers through the regular LLM channel config, not the Hermes bridge.

Example fix

# before
PROTOCOL=anthropic

# after
PROTOCOL=openai
Defensive patterns

Strategy: validation

Validate before calling

from src.llm.hermes import canonicalize_hermes_protocol

canonicalize_hermes_protocol(protocol)  # raises on anything non-openai

Type guard

def is_openai_protocol(value: str) -> bool:
    return (value or "openai").strip().lower() in {"", "openai"}

Try / catch

try:
    proto = canonicalize_hermes_protocol(cfg_protocol)
except ValueError:
    proto = "openai"  # or fail config load

Prevention

When it happens

Trigger: Setting PROTOCOL to anything other than 'openai' (case-insensitive) in Hermes-related config — 'anthropic', 'openai-compatible', 'OpenAI ' trailing space is fine (normalized) but 'oai' is not. Raised during Hermes settings canonicalization at startup.

Common situations: Copy-pasting a multi-provider LLM config template into the Hermes section; assuming Hermes proxies arbitrary protocols like the main LiteLLM channels do.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/3f4f857c56f573bd. Report an issue: GitHub.