fix: custom provider falls through to OpenRouter — three missing resolution paths#4174
fix: custom provider falls through to OpenRouter — three missing resolution paths#4174SHL0MS wants to merge 1 commit into
Conversation
24c8948 to
0bf3e55
Compare
|
Updated: found a second (deeper) root cause. The auth.py fix alone was insufficient. Even after Additionally, Added fix: A |
…ction resolve_provider() ignored config.yaml's model.provider field entirely. Users who selected a custom endpoint (e.g. local Ollama) via hermes setup found their traffic routed through OpenRouter if a stale OPENROUTER_API_KEY or OPENAI_API_KEY existed in ~/.hermes/.env. Two fixes: 1. In resolve_provider(), check config.yaml model.provider BEFORE falling through to env var detection. Explicit user choice now takes priority over stale API keys. 2. In _model_flow_custom(), clear OPENROUTER_API_KEY and OPENAI_API_KEY from .env when switching to a custom endpoint, preventing them from overriding the custom endpoint in future auto-detection. Closes NousResearch#4172
0bf3e55 to
ddfaf0e
Compare
|
Closing — the underlying bug was real (two users confirmed it on v2026.3.30) but #4165 (merged hours after this PR was opened) fixed the root cause by making config.yaml the single source of truth for endpoint URLs and removing the Our three fixes are now redundant:
Thanks to the reviewer for catching this. |
Problem
Users who configure a custom endpoint (local Ollama, vLLM, llama.cpp) via
hermes setupfind their traffic silently routed through OpenRouter./providershowsollama/qwen3.5 via OpenRouterdespite the user explicitly selecting their local endpoint and having no OpenRouter configuration.Reported by multiple users.
Three root causes
1.
resolve_runtime_provider()has no handler forprovider == "custom"(runtime_provider.py)This is the primary bug. The runtime resolver handles
nous,openai-codex,copilot-acp,anthropic, and API-key providers — but whenresolve_provider()returns"custom", there is no matching branch. It falls through to_resolve_openrouter_runtime()at line 422, which returns an OpenRouter runtime with the user's localhost base_url. Result: correct endpoint, wrong provider label, wrong billing.Additionally,
_get_named_custom_provider()at line 133 explicitly returnsNonewhenrequested_provider == "custom"— it's treated as a generic label, not a resolvable provider.Fix: Added a
provider == "custom"handler that readsmodel.base_urlfrom config.yaml andOPENAI_BASE_URLfrom env. (+23 lines)2.
resolve_provider()ignoresmodel.providerin config.yaml (auth.py)The auto-detection chain goes: OAuth store → env var scan → provider registry. It never checks
model.providerfrom config.yaml. A staleOPENROUTER_API_KEYin~/.hermes/.envfrom a previous setup wins over the user's explicitmodel.provider: "custom".Fix: Added config.yaml check as priority #2, before env var scanning. (+15 lines)
3.
_model_flow_custom()doesn't clear stale provider keys (main.py)When switching to a custom endpoint,
deactivate_provider()clears the OAuth store but leavesOPENROUTER_API_KEYin.env. This key then wins the auto-detect on next startup.Fix: Clear
OPENROUTER_API_KEYfrom.envwhen switching to custom. (+4 lines)Changes
44 insertions across
hermes_cli/runtime_provider.py,hermes_cli/auth.py,hermes_cli/main.py.Closes #4172