From 0e6354df5077c7e020671e80c3c9f6e585f7e8b3 Mon Sep 17 00:00:00 2001 From: 0xFrank-eth <0xFrank-eth@users.noreply.github.com> Date: Sat, 11 Apr 2026 22:53:08 +0300 Subject: [PATCH] fix(custom-providers): propagate model field from config to runtime so API receives the correct model name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #7828 When a custom_providers entry carries a `model` field, that value was silently dropped by `_get_named_custom_provider` and `_resolve_named_custom_runtime`. Callers received a runtime dict with `base_url`, `api_key`, and `api_mode` — but no `model`. As a result, `hermes chat --model ` sent the *provider name* (e.g. "my-dashscope-provider") as the model string to the API instead of the configured model (e.g. "qwen3.6-plus"), producing: Error code: 400 - {'error': {'message': 'Model Not Exist'}} Setting the provider as the *default* model in config.yaml worked because that path writes `model.default` and the agent reads it back directly, bypassing the broken runtime resolution path. Changes: 1. hermes_cli/runtime_provider.py — _get_named_custom_provider() Reads `entry.get("model")` and includes it in the result dict so the value is available to callers. 2. hermes_cli/runtime_provider.py — _resolve_named_custom_runtime() Propagates `custom_provider["model"]` into the returned runtime dict. 3. cli.py — _ensure_runtime_credentials() After resolving runtime, if `runtime["model"]` is set, assign it to `self.model` so the AIAgent is initialised with the correct model name rather than the provider name the user typed on the CLI. Co-Authored-By: Claude Sonnet 4.6 --- cli.py | 9 +++++++++ hermes_cli/runtime_provider.py | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/cli.py b/cli.py index 0969a060b..ff80a49b8 100644 --- a/cli.py +++ b/cli.py @@ -2710,6 +2710,15 @@ class HermesCLI: self.api_key = api_key self.base_url = base_url + # When a custom_provider entry carries an explicit `model` field, + # use it as the effective model name. Without this, running + # `hermes chat --model ` sends the provider name + # (e.g. "my-provider") as the model string to the API instead of + # the configured model (e.g. "qwen3.6-plus"), causing 400 errors. + runtime_model = runtime.get("model") + if runtime_model and isinstance(runtime_model, str): + self.model = runtime_model + # Normalize model for the resolved provider (e.g. swap non-Codex # models when provider is openai-codex). Fixes #651. model_changed = self._normalize_model_for_provider(resolved_provider) diff --git a/hermes_cli/runtime_provider.py b/hermes_cli/runtime_provider.py index 3d1333c26..c3fcd3aae 100644 --- a/hermes_cli/runtime_provider.py +++ b/hermes_cli/runtime_provider.py @@ -304,6 +304,9 @@ def _get_named_custom_provider(requested_provider: str) -> Optional[Dict[str, An api_mode = _parse_api_mode(entry.get("api_mode")) if api_mode: result["api_mode"] = api_mode + model_name = str(entry.get("model", "") or "").strip() + if model_name: + result["model"] = model_name return result return None @@ -339,7 +342,7 @@ def _resolve_named_custom_runtime( ] api_key = next((candidate for candidate in api_key_candidates if has_usable_secret(candidate)), "") - return { + result = { "provider": "custom", "api_mode": custom_provider.get("api_mode") or _detect_api_mode_for_url(base_url) @@ -348,6 +351,11 @@ def _resolve_named_custom_runtime( "api_key": api_key or "no-key-required", "source": f"custom_provider:{custom_provider.get('name', requested_provider)}", } + # Propagate the model name so callers can override self.model when the + # provider name differs from the actual model string the API expects. + if custom_provider.get("model"): + result["model"] = custom_provider["model"] + return result def _resolve_openrouter_runtime(