Summary
When using the Hermes Dashboard to switch the default model to a custom provider (e.g. SiliconFlow), the selection automatically reverts to openrouter. The CLI method (hermes config set) works correctly.
Environment
- Hermes Agent version: 0.18.0 (2026.7.1)
- OS: Linux (Ubuntu)
Steps to Reproduce
- Add a custom provider in
~/.hermes/config.yaml:
custom_providers:
- name: siliconflow
base_url: https://api.siliconflow.cn/v1
api_key: sk-xxx
api_mode: chat_completions
model: deepseek-ai/DeepSeek-V4-Flash
models:
deepseek-ai/DeepSeek-V4-Flash:
name: deepseek-ai/DeepSeek-V4-Flash
- Open Hermes Dashboard → Models page
- Select the custom provider (
siliconflow) and model (deepseek-ai/DeepSeek-V4-Flash)
- Click "Set as Main Model" or use the model switcher
Expected: Model switches to siliconflow / deepseek-ai/DeepSeek-V4-Flash
Actual: Model reverts to openrouter
Root Cause Analysis
The issue is in hermes_cli/web_server.py, function _normalize_main_model_assignment (around line 964-1028):
def _normalize_main_model_assignment(provider: str, model: str) -> tuple[str, str]:
canonical = normalize_provider(prov_in)
if canonical not in _KNOWN_PROVIDER_NAMES and "/" in model_in:
# Vendor prefix posing as a provider (analytics fallback).
# ... otherwise default to openrouter.
canonical = "openrouter"
prov_in = "openrouter"
_KNOWN_PROVIDER_NAMES only includes built-in providers. Custom providers defined in config.yaml are not recognized, so when the dashboard sends provider="siliconflow" with model="deepseek-ai/DeepSeek-V4-Flash", the function treats it as an unknown vendor prefix and forces fallback to openrouter.
Suggested Fix
Check custom_providers from config before falling back to openrouter:
def _normalize_main_model_assignment(provider: str, model: str) -> tuple[str, str]:
from hermes_cli.models import _KNOWN_PROVIDER_NAMES, normalize_provider
prov_in = (provider or "").strip()
model_in = (model or "").strip()
canonical = normalize_provider(prov_in)
# Check if provider exists in custom_providers config
try:
cfg = load_config()
custom_providers = cfg.get("custom_providers", [])
custom_provider_names = {cp.get("name") for cp in custom_providers if cp.get("name")}
except Exception:
custom_provider_names = set()
# If provider is a known custom provider, keep it verbatim
if canonical in custom_provider_names:
return prov_in, model_in
if canonical not in _KNOWN_PROVIDER_NAMES and "/" in model_in:
# ... existing fallback logic
Workaround
Use CLI instead of Dashboard:
hermes config set model.provider siliconflow
hermes config set model.default deepseek-ai/DeepSeek-V4-Flash
Additional Context
This affects all custom providers, not just SiliconFlow. Any user who defines a custom OpenAI-compatible endpoint in custom_providers will encounter this issue when trying to switch models via the Dashboard UI.
Summary
When using the Hermes Dashboard to switch the default model to a custom provider (e.g. SiliconFlow), the selection automatically reverts to
openrouter. The CLI method (hermes config set) works correctly.Environment
Steps to Reproduce
~/.hermes/config.yaml:siliconflow) and model (deepseek-ai/DeepSeek-V4-Flash)Expected: Model switches to
siliconflow/deepseek-ai/DeepSeek-V4-FlashActual: Model reverts to
openrouterRoot Cause Analysis
The issue is in
hermes_cli/web_server.py, function_normalize_main_model_assignment(around line 964-1028):_KNOWN_PROVIDER_NAMESonly includes built-in providers. Custom providers defined inconfig.yamlare not recognized, so when the dashboard sendsprovider="siliconflow"withmodel="deepseek-ai/DeepSeek-V4-Flash", the function treats it as an unknown vendor prefix and forces fallback to openrouter.Suggested Fix
Check
custom_providersfrom config before falling back to openrouter:Workaround
Use CLI instead of Dashboard:
Additional Context
This affects all custom providers, not just SiliconFlow. Any user who defines a custom OpenAI-compatible endpoint in
custom_providerswill encounter this issue when trying to switch models via the Dashboard UI.