Skip to content

fix(cli): accept available_models in custom provider config - #52287

Open
harjothkhara wants to merge 1 commit into
NousResearch:mainfrom
harjothkhara:fix/model-command-custom-provider-models
Open

harjothkhara wants to merge 1 commit into
NousResearch:mainfrom
harjothkhara:fix/model-command-custom-provider-models

Conversation

@harjothkhara

@harjothkhara harjothkhara commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Makes available_models a recognized model-declaration key for custom providers, so a provider that declares its models under that key is actually routable by /model.

This PR has been re-scoped against current main — see "What changed" below.

Related Issue

Fixes #52266

The bug on current main

_normalize_custom_provider_entry() (hermes_cli/config.py) is the single chokepoint every custom-provider path runs through. It recognized only models, so a hand-written entry like the one in #52266:

custom_providers:
  - name: tokenplan
    api_mode: chat_completions
    base_url: https://token-plan.example/compatible-mode/v1
    model: qwen3.7-plus
    available_models: [qwen3.7-plus, qwen3.7-max, deepseek-v4-flash, ...]

normalizes to an entry with no models key at all, and emits:

providers.tokenplan: unknown config keys ignored: available_models

Every downstream consumer reads the normalized models key, so the provider lists (0) models in the picker and no /model <id> against it resolves.

What changed since this PR was opened

The original version of this PR also added configured-provider colon parsing (tokenplan:deepseek-v4-flash, custom:tokenplan:qwen3.7-max). That half is now fixed on main by the "single-owner /model request parsing" consolidation in model_switch.py — all three forms from the issue parse cleanly there today, including the reported ValueError: too many values to unpack (expected 3).

So this PR is re-scoped to the one thing still broken: the unrecognized declaration key. The diff is now one file plus tests, instead of the previous four-file change to model_switch.py and config.py.

Reviewers: the earlier approval predates this rework and no longer describes the current diff — fresh eyes welcome.

Changes Made

  • hermes_cli/config.py
    • Merge available_models and models in _normalize_custom_provider_entry(). Fixing it at the chokepoint means typed routing, the picker list, and validation fallback all pick it up without each learning the alias.
    • models metadata wins for ids declared under both keys.
    • Extract the list/mapping coercion into _declared_models_to_mapping() so the alias inherits the same [{id: ...}] row handling models already had, instead of duplicating it.
    • Add available_models to _KNOWN_KEYS so it no longer trips the unknown-key warning.
  • tests/hermes_cli/test_custom_provider_available_models.py (new)
    • The issue's provider config verbatim; [{id: ...}] rows via the alias; models-wins-on-conflict; models-only entries unchanged; neither key leaves models unset; and a no-mutation check (entries alias the shared read-only config cache).

How to Test

  1. Configure a custom provider declaring models under available_models with hyphenated ids, as above.
  2. Run /model tokenplan:deepseek-v4-flash (or custom:tokenplan:qwen3.7-max, or /model deepseek-v4-flash --provider tokenplan).
  3. The switch resolves to custom:tokenplan and the requested model; the picker lists all declared models rather than (0).

Verified on this branch:

  • tests/hermes_cli/test_custom_provider_available_models.py6 passed
  • test_config_validation.py, test_custom_provider_normalize_no_mutate.py, test_custom_provider_extra_headers.py, test_provider_config_validation.py, test_runtime_provider_resolution.py, test_config.py142 passed
  • tests/hermes_cli/ -k "model_switch or picker or custom_provider or inventory"218 passed
  • ruff check on both changed files → passed

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists labels Jun 25, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Approved

Well-tested fix for custom provider model switching. The fix correctly recognizes available_models as a supported declaration and parses provider:model / custom:provider:model colon forms. Good refactoring of _match into a reusable _match_declared_model function.

Changes:

  • hermes_cli/config.py: Normalizes available_models into custom provider model view
  • hermes_cli/model_switch.py: New _DECLARED_MODEL_KEYS and _match_declared_model function

LGTM.

Reviewed by Hermes Agent

@harjothkhara
harjothkhara marked this pull request as ready for review June 25, 2026 05:24
@harjothkhara
harjothkhara force-pushed the fix/model-command-custom-provider-models branch 2 times, most recently from e1224a5 to 8e25689 Compare July 8, 2026 00:59
@harjothkhara

Copy link
Copy Markdown
Contributor Author

Rebased onto current main — the branch had gone CONFLICTING because hermes_cli/model_switch.py was refactored upstream (_declared_model_ids / local _match). Re-derived the fix on that structure: _DECLARED_MODEL_KEYS now routes matching through the canonical _declared_model_ids extractor rather than a duplicate helper, so available_models is threaded through every call site without diverging from main's model-shape parsing.

While rebasing I ran a cross-model review and hardened one real issue it surfaced: _normalize_custom_provider_entry treated available_models as a fallback to models, so a config declaring both (models: for per-model context-length metadata + available_models: for a plain switchable id list) would silently lose the available_models ids on every CLI/gateway/TUI surface that reads get_compatible_custom_providers(). It now merges the two — declared models metadata wins on id collision. Added:

  • an on-disk coexistence test (models + available_models both populated, through the real normalize/fold → switch_model chain);
  • on-disk coverage for the two remaining issue syntaxes (custom:<name>:model and <model> --provider <name>), alongside the existing provider:model colon-form test;
  • refreshed the stale "no colon provider:model syntax" contract text in the module header + /model docstrings (CLI and gateway), and documented available_models in providers.md.

Note on scope: the literal ValueError: too many values to unpack (expected 3) from the issue no longer reproduces on current main; the remaining defect this closes is the available_models routing/config gap for the reporter's config. All affected tests pass; clean rebase on current main.

@harjothkhara
harjothkhara force-pushed the fix/model-command-custom-provider-models branch from 8e25689 to 460f0b0 Compare July 10, 2026 20:06
@harjothkhara

Copy link
Copy Markdown
Contributor Author

Ping — approved Jun 25 and green. Let me know if anything's blocking the merge.

@harjothkhara
harjothkhara force-pushed the fix/model-command-custom-provider-models branch 2 times, most recently from 528fe08 to 3e15e9a Compare July 14, 2026 04:06
@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for the focused fix. The current-main premise is verified: hermes_cli/config.py:4706-4819 neither recognizes nor folds available_models, and hermes_cli/model_switch.py:767-785 / 1282-1308 do not use it for configured-provider routing or validation.

The PR head 3e15e9ae addresses those paths consistently: it normalizes and merges the alias, reuses _declared_model_ids for exact matching, and guards colon parsing through configured-provider resolution. The added regression coverage includes the three reported syntaxes, on-disk normalization, aggregator precedence, and legacy custom-provider/built-in-name collision behavior. GitHub reports the head mergeable and required checks passing.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
@harjothkhara
harjothkhara force-pushed the fix/model-command-custom-provider-models branch from 3e15e9a to 0e33362 Compare July 24, 2026 05:40
@harjothkhara

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Rebased onto current main: the endpoint listing was restructured into grouped endpoints on main, so the available_models alias now feeds the group's entry_models collection loop instead of the old flat list. All four touched test suites pass locally (250 tests).

@harjothkhara
harjothkhara force-pushed the fix/model-command-custom-provider-models branch from 0e33362 to 5c62552 Compare July 30, 2026 20:15
@harjothkhara harjothkhara changed the title fix(cli): route custom provider model commands fix(cli): accept available_models in custom provider config Jul 30, 2026
@harjothkhara

Copy link
Copy Markdown
Contributor Author

Re-scoped against current main and force-pushed.

Two things changed since this was last touched:

  1. The reported crash is already fixed here. ValueError: too many values to unpack (expected 3) no longer reproduces — the single-owner /model request parsing in model_switch.py handles all three forms from /model slash command fails with "too many values to unpack (expected 3)" for models with hyphens in custom providers #52266 (tokenplan:deepseek-v4-flash, custom:tokenplan:qwen3.7-max, --provider tokenplan). I've dropped this PR's colon-parsing half entirely rather than re-litigate ground main already covers.

  2. What's still broken is narrower: available_models isn't a recognized declaration key, so _normalize_custom_provider_entry() drops it with unknown config keys ignored and the entry ends up with no models at all — provider shows (0) models, nothing resolves. That's the only thing this PR now fixes, at the normalization chokepoint so the routing/picker/validation paths don't each need to learn the alias.

The diff went from four files to one plus tests.

Worth flagging: the approval on this PR predates the rework and doesn't describe the current change. Happy to have it re-reviewed from scratch — or closed, if the narrower fix is better folded in elsewhere.

@harjothkhara

Copy link
Copy Markdown
Contributor Author

@kshitijk4poor — could I get a maintainer review here? You've been in hermes_cli/ a lot lately so this should be quick to judge.

Status: green (27 checks), one file plus tests, fixes #52266.

Flagging one thing so it isn't misread: the approval showing on this PR is from an external contributor, not a maintainer, and it predates a re-scope + force-push that replaced the diff. It shouldn't count toward merge — this needs a fresh look.

The change is narrow. _normalize_custom_provider_entry() recognized only models, so a config declaring its models under available_models had the key dropped with unknown config keys ignored and ended up with no models at all — the provider lists (0) models and no /model <id> against it resolves. Merging the two keys at that one chokepoint covers typed routing, the picker list, and validation together, instead of teaching each path the alias.

Worth noting the other half of the original issue (the too many values to unpack crash on colon forms) is already fixed on main by the single-owner /model parsing work, so that's dropped from this PR rather than re-litigated.

Equally happy to close this if you'd rather fold the fix in elsewhere — #52266 would just need to stay open.

@harjothkhara

Copy link
Copy Markdown
Contributor Author

@teknium1 — could you take a look, or point me at whoever owns hermes_cli/config.py?

Fixes #52266, still open and unassigned. I re-verified on current main: available_models is still unrecognised in hermes_cli/config.py, so the bug is live.

One file plus tests, green CI, no conflicts. The Jun 25 approval was dropped by a re-scope force-push, so it needs a fresh look rather than a re-approve.

@harjothkhara

Copy link
Copy Markdown
Contributor Author

Status check-in: still green, no conflicts, one file plus tests, on today's main. Nothing's changed here since the Aug 3 update — leaving this for whoever gets to hermes_cli/config.py next rather than re-pinging individually again so soon.

Hand-written custom-provider entries declare their model list as
available_models, but _normalize_custom_provider_entry only knew models.
The alias fell through to the unknown-key warning and was dropped, so the
normalized entry carried no models at all: the picker listed the provider
with (0) models and every /model <id> against it failed to resolve, since
all routing paths read the normalized models key.

Merge both keys at the normalization chokepoint so every downstream
consumer (typed routing, picker list, validation fallback) sees them
without each having to learn the alias. models metadata wins for ids
declared in both. The list/mapping coercion moves to a helper so the
alias gets the same [{id: ...}] row handling models already had.

Fixes NousResearch#52266
@harjothkhara
harjothkhara force-pushed the fix/model-command-custom-provider-models branch from 5c62552 to 167a7e1 Compare August 19, 2026 02:27

This branch has not been deployed

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

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/model slash command fails with "too many values to unpack (expected 3)" for models with hyphens in custom providers

4 participants