Skip to content

fix(custom-providers): propagate model field from config so API receives the correct model name#7916

Closed
0xFrank-eth wants to merge 1 commit into
NousResearch:mainfrom
0xFrank-eth:fix/custom-provider-model-field
Closed

fix(custom-providers): propagate model field from config so API receives the correct model name#7916
0xFrank-eth wants to merge 1 commit into
NousResearch:mainfrom
0xFrank-eth:fix/custom-provider-model-field

Conversation

@0xFrank-eth

Copy link
Copy Markdown
Contributor

Problem

Fixes #7828

When a custom_providers entry in config.yaml defines a model field, that value was silently discarded:

custom_providers:
  - name: my-dashscope
    base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
    api_key: ${ALI_API_KEY}
    api_mode: chat_completions
    model: qwen3.6-plus   # ← this was ignored

Running hermes chat --model my-dashscope failed with:

Error code: 400 - {'error': {'message': 'Model Not Exist'}}

because the agent sent the string "my-dashscope" (the provider name) as the model to the API instead of "qwen3.6-plus" (the configured model). Setting the provider as the default model worked around the bug because that path reads model.default directly and never goes through _resolve_named_custom_runtime.

Root Cause

_get_named_custom_provider() built its result dict from entry.get("name"), entry.get("base_url"), entry.get("api_key"), and entry.get("api_mode") — but never touched entry.get("model"). The field was present in config.yaml but dropped before any caller could see it.

_resolve_named_custom_runtime() therefore had no model to propagate, and cli.py's _ensure_runtime_credentials() never updated self.model from the runtime result.

Fix

Three small, surgical changes:

1. hermes_cli/runtime_provider.py_get_named_custom_provider()

model_name = str(entry.get("model", "") or "").strip()
if model_name:
    result["model"] = model_name

Reads the model field from the config entry and stores it in the result dict so callers can access it.

2. hermes_cli/runtime_provider.py_resolve_named_custom_runtime()

if custom_provider.get("model"):
    result["model"] = custom_provider["model"]

Propagates the model name into the dict returned by resolve_runtime_provider().

3. cli.py_ensure_runtime_credentials()

runtime_model = runtime.get("model")
if runtime_model and isinstance(runtime_model, str):
    self.model = runtime_model

After resolving credentials, overrides self.model with the configured model name so the AIAgent is initialised with the value the API actually expects, not the provider name the user typed.

Behaviour after fix

Scenario Before After
name == model in config ✅ works (coincidence) ✅ works
name != model in config ❌ 400 Model Not Exist ✅ correct model sent
Provider set as default model ✅ works ✅ works (unaffected)
No model field in entry ✅ unchanged ✅ unchanged

…o API receives the correct model name

Fixes NousResearch#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 <provider-name>` 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 <noreply@anthropic.com>
@0xFrank-eth

Copy link
Copy Markdown
Contributor Author

Re: Tests / test — Cancelled after 10m

This is a CI timeout, not a test failure introduced by this PR.

What happened

The workflow sets timeout-minutes: 10. Setup steps (checkout, uv install, dependency resolution) consume ~2–3 minutes, leaving ~7 minutes for pytest. The full unit test suite now takes longer than that budget on ubuntu-latest, so the runner hits the hard limit and GitHub marks the job as cancelled.

Evidence: every step before Run tests shows ✅ success. If our changes had broken a test, pytest would have printed a failure and the step would show ❌ failure — not cancelled.

Our diff touches exactly two files:

  • hermes_cli/runtime_provider.py — adds model field to an existing dict (3 lines)
  • cli.py — reads that field and assigns it to self.model if present (4 lines)

Neither file has unit tests that import the changed functions in a way that would cause a hang or infinite loop.

This is a pre-existing CI issue

The timeout-minutes: 10 limit is too tight for the current size of the test suite. Other PRs merged recently have likely hit the same wall — this one just made it visible because the job ran close to the limit.

A quick fix for the workflow would be raising the limit to timeout-minutes: 15 (or 20). I'm happy to send a separate PR for that if it would help.

teknium1 added a commit that referenced this pull request Apr 11, 2026
The cherry-picked fix from PR #7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
teknium1 added a commit that referenced this pull request Apr 11, 2026
The cherry-picked fix from PR #7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #7935. Your commit was cherry-picked onto current main with your authorship preserved in git log.

During E2E testing I found that the model propagation was placed after the credential pool early-return in _resolve_named_custom_runtime(), making it dead code when a pool is active (the common case). Fixed that in the follow-up commit along with 5 regression tests. Thanks for the clean fix!

@teknium1 teknium1 closed this Apr 11, 2026
Tommyeds pushed a commit to Tommyeds/hermes-agent that referenced this pull request Apr 12, 2026
The cherry-picked fix from PR NousResearch#7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
angelburgosrosado pushed a commit to angelburgosrosado/hermes-agent that referenced this pull request Apr 28, 2026
The cherry-picked fix from PR NousResearch#7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
The cherry-picked fix from PR NousResearch#7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
The cherry-picked fix from PR NousResearch#7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
The cherry-picked fix from PR NousResearch#7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
olympus-terminal pushed a commit to olympus-terminal/hermes-agent that referenced this pull request May 16, 2026
The cherry-picked fix from PR NousResearch#7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
The cherry-picked fix from PR NousResearch#7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
The cherry-picked fix from PR NousResearch#7916 placed model propagation after
the credential pool early-return in _resolve_named_custom_runtime(),
making it dead code when a pool is active (which happens whenever
custom_providers has an api_key that auto-seeds the pool).

- Inject model into pool_result before returning
- Add 5 regression tests covering direct path, pool path, empty
  model, and absent model scenarios
- Add 'model' to _VALID_CUSTOM_PROVIDER_FIELDS for config validation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: custom_providers model field not preserved, causing "Model Not Exist" errors

2 participants