You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Users who configure a custom endpoint (e.g., local Ollama on DGX Spark) via `hermes setup` find that Hermes still routes through OpenRouter and burns paid API tokens. `/provider` shows `ollama/qwen3.5 via OpenRouter` despite the user explicitly selecting their local endpoint and having no OpenRouter configuration.
This affects both the main chat model and all auxiliary tasks (compression, vision, memory flush, etc.).
Three root causes
1. `resolve_runtime_provider()` has no handler for `provider == "custom"` (primary bug)
`runtime_provider.py:304-428` handles `nous`, `openai-codex`, `copilot-acp`, `anthropic`, and API-key providers. 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.
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.
This is why the user sees "endpoint is localhost but provider is OpenRouter" — the base_url resolves correctly, but the provider label comes from the OpenRouter fallback.
2. `resolve_provider()` ignores `model.provider` in config.yaml
`auth.py:667` runs an auto-detection chain:
```
Priority (broken):
Explicit CLI args
auth.json active_provider (OAuth)
OPENAI_API_KEY or OPENROUTER_API_KEY in env → "openrouter"
Provider-specific API keys → that provider
Fallback: raise error
Missing: model.provider from config.yaml is NEVER checked.
```
`hermes setup` correctly writes `model.provider: "custom"` to config.yaml, but `resolve_provider()` doesn't read it. A stale `OPENROUTER_API_KEY` in `.env` from a previous configuration wins.
3. `_model_flow_custom()` doesn't clear stale provider keys
`main.py:1292` calls `deactivate_provider()` which clears `active_provider` in auth.json, but does NOT clear `OPENROUTER_API_KEY` from `.env`. The stale key survives and wins the auto-detect on next startup.
Problem
Users who configure a custom endpoint (e.g., local Ollama on DGX Spark) via `hermes setup` find that Hermes still routes through OpenRouter and burns paid API tokens. `/provider` shows `ollama/qwen3.5 via OpenRouter` despite the user explicitly selecting their local endpoint and having no OpenRouter configuration.
This affects both the main chat model and all auxiliary tasks (compression, vision, memory flush, etc.).
Three root causes
1. `resolve_runtime_provider()` has no handler for `provider == "custom"` (primary bug)
`runtime_provider.py:304-428` handles `nous`, `openai-codex`, `copilot-acp`, `anthropic`, and API-key providers. 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.
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.
This is why the user sees "endpoint is localhost but provider is OpenRouter" — the base_url resolves correctly, but the provider label comes from the OpenRouter fallback.
2. `resolve_provider()` ignores `model.provider` in config.yaml
`auth.py:667` runs an auto-detection chain:
```
Priority (broken):
Missing: model.provider from config.yaml is NEVER checked.
```
`hermes setup` correctly writes `model.provider: "custom"` to config.yaml, but `resolve_provider()` doesn't read it. A stale `OPENROUTER_API_KEY` in `.env` from a previous configuration wins.
3. `_model_flow_custom()` doesn't clear stale provider keys
`main.py:1292` calls `deactivate_provider()` which clears `active_provider` in auth.json, but does NOT clear `OPENROUTER_API_KEY` from `.env`. The stale key survives and wins the auto-detect on next startup.
Reproduction
Even without stale keys, root cause #1 alone causes the bug — `provider == "custom"` always falls through to OpenRouter.
Impact