Skip to content

fix: custom provider falls through to OpenRouter — three missing resolution paths#4174

Closed
SHL0MS wants to merge 1 commit into
NousResearch:mainfrom
SHL0MS:fix/provider-resolution-priority
Closed

fix: custom provider falls through to OpenRouter — three missing resolution paths#4174
SHL0MS wants to merge 1 commit into
NousResearch:mainfrom
SHL0MS:fix/provider-resolution-priority

Conversation

@SHL0MS

@SHL0MS SHL0MS commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator

Problem

Users who configure a custom endpoint (local Ollama, vLLM, llama.cpp) via hermes setup find their traffic silently routed through OpenRouter. /provider shows ollama/qwen3.5 via OpenRouter despite 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 for provider == "custom" (runtime_provider.py)

This is the primary bug. The runtime resolver handles nous, openai-codex, copilot-acp, anthropic, and API-key providers — but when resolve_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 returns None when requested_provider == "custom" — it's treated as a generic label, not a resolvable provider.

Fix: Added a provider == "custom" handler that reads model.base_url from config.yaml and OPENAI_BASE_URL from env. (+23 lines)

2. resolve_provider() ignores model.provider in config.yaml (auth.py)

The auto-detection chain goes: OAuth store → env var scan → provider registry. It never checks model.provider from config.yaml. A stale OPENROUTER_API_KEY in ~/.hermes/.env from a previous setup wins over the user's explicit model.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 leaves OPENROUTER_API_KEY in .env. This key then wins the auto-detect on next startup.

Fix: Clear OPENROUTER_API_KEY from .env when switching to custom. (+4 lines)

Changes

44 insertions across hermes_cli/runtime_provider.py, hermes_cli/auth.py, hermes_cli/main.py.

Closes #4172

@SHL0MS SHL0MS added the type/bug Something isn't working label Mar 31, 2026
@SHL0MS SHL0MS force-pushed the fix/provider-resolution-priority branch 2 times, most recently from 24c8948 to 0bf3e55 Compare March 31, 2026 04:51
@SHL0MS

SHL0MS commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator Author

Updated: found a second (deeper) root cause.

The auth.py fix alone was insufficient. Even after resolve_provider() correctly returns "custom", the runtime resolver in runtime_provider.py:304-428 has no handler for provider == "custom". It handles nous, openai-codex, copilot-acp, anthropic, and API-key providers — but "custom" falls through to _resolve_openrouter_runtime() at line 422, which returns an OpenRouter runtime.

Additionally, _get_named_custom_provider() at line 133 explicitly returns None when requested_provider == "custom" — it's treated as a generic label, not a resolvable provider.

Added fix: A provider == "custom" handler in resolve_runtime_provider() that reads model.base_url from config.yaml and OPENAI_BASE_URL from env. This is why the user saw "endpoint is localhost but provider is OpenRouter" — the base_url was correctly resolved but the provider label came from the OpenRouter fallback.

@SHL0MS SHL0MS closed this Mar 31, 2026
@SHL0MS SHL0MS changed the title fix: respect model.provider from config.yaml before env var auto-detection fix: custom provider falls through to OpenRouter — three missing resolution paths Mar 31, 2026
@SHL0MS SHL0MS reopened this Mar 31, 2026
…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
@SHL0MS SHL0MS force-pushed the fix/provider-resolution-priority branch from 0bf3e55 to ddfaf0e Compare March 31, 2026 04:55
@SHL0MS

SHL0MS commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator Author

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 .env dual-source confusion.

Our three fixes are now redundant:

  1. Runtime resolver custom handler — already handled in _resolve_openrouter_runtime (lines 242, 283)
  2. auth.py config check — resolve_provider("custom") short-circuits at line 709; auto-detect never runs
  3. Stale key clearing — refactor: make config.yaml the single source of truth for endpoint URLs #4165 removed OPENAI_BASE_URL from .env writes entirely

Thanks to the reviewer for catching this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Provider auto-detection ignores model.provider in config.yaml — stale API keys override explicit custom endpoint

1 participant