Skip to content

Fix/model switch custom providers - #10737

Closed
firexrwt wants to merge 4 commits into
NousResearch:mainfrom
firexrwt:fix/model-switch-custom-providers
Closed

Fix/model switch custom providers#10737
firexrwt wants to merge 4 commits into
NousResearch:mainfrom
firexrwt:fix/model-switch-custom-providers

Conversation

@firexrwt

Copy link
Copy Markdown

What does this PR do?

The /model picker was broken for users with custom_providers entries that use a models dict - only the single model field was read, so the picker always showed just one option and switching models was effectively impossible.

This PR fixes model detection in the picker and resolves two additional bugs discovered during testing:

  1. Picker didn't detect models from models dict - list_authenticated_providers() only read the model field from each custom_providers entry. The models dict (used for multi-model provider configs) was ignored entirely.
  2. Switched model reverted on next message - _ensure_runtime_credentials() re-read model.default from config.yaml on every message and overwrote the session switch.
  3. Auxiliary tasks used wrong model - title generation and other auxiliary tasks always called _read_main_model() which reads from config, not the active session model. On single-GPU local LLM setups this caused a second model to be requested into VRAM, resulting in OOM errors.

Related Issue

N/A

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)

Changes Made

  • hermes_cli/model_switch.py: include keys from models dict when building the picker's model list for custom_providers entries
  • cli.py (send_message): save self.model before _ensure_runtime_credentials() and restore it if the resolver overwrote it with the config default
  • cli.py (_apply_model_switch_result): call set_runtime_model() to propagate the switch to auxiliary tasks
  • agent/auxiliary_client.py: add set_runtime_model() and _runtime_model_override; _read_main_model() checks the override before falling back to config.yaml

How to Test

  1. Add a custom_providers entry with a models dict containing multiple model IDs, run /model - all models should appear in the picker
  2. Switch model via /model, send a message - verify the switched model is used, not the config default
  3. On a local LLM setup (e.g. LM Studio): switch model, send a message, check server logs - only one model should be requested per exchange

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q - 6 new tests pass; 26 pre-existing failures on main are unrelated to these changes
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Fedora Linux 43 x86_64, AMD Ryzen 9 7900X, NVIDIA RTX 5070 Ti, LM Studio 0.4.11 (Build 1)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) - N/A
  • I've updated cli-config.yaml.example if I added/changed config keys - N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows - N/A
  • I've considered cross-platform impact (Windows, macOS) - Python-only changes, no platform-specific code
  • I've updated tool descriptions/schemas if I changed tool behavior - N/A

Screenshots / Logs

LM Studio server logs before fix - two different models requested per exchange:

[INFO] [mistralai/devstral-small-2-2512] Running chat completion ...
[INFO] [google/gemma-4-e4b] Running chat completion ...   ← title generation, triggers OOM on single GPU

After fix - single model throughout:

[INFO] [mistralai/devstral-small-2-2512] Running chat completion ...
[INFO] [mistralai/devstral-small-2-2512] Running chat completion ...

Co-developed with Claude Code

@drousselbot drousselbot 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.

QA review summary

Thanks for the detailed repro and the focused tests. I found a blocking gap before this is ready for QA pass:

  1. The PR now records provider and base_url in set_runtime_model(...), but agent/auxiliary_client.py only consumes _runtime_model_override. Auxiliary routing still reads provider/runtime from config/env (_read_main_provider(), _resolve_custom_runtime(), get_available_vision_backends(), _resolve_auto()), so a runtime provider/base-URL switch is not actually propagated despite the new comments and test names implying it is.
  2. In cli.py, the new _ensure_runtime_credentials() guard restores only self.model. That still allows self.provider / self.base_url / related routing state to be overwritten on the next message if the active runtime switch changed provider, which is the same class of regression in a broader form.
  3. The new tests verify the model override and that provider/base URL values are stored, but they do not verify the behavioral claim that auxiliary tasks actually resolve through the switched provider/base URL.

Validation run on this PR head:

  • python3 -m pytest -q tests/test_model_switch_fixes.py ✅ (6 passed)
  • gh pr checks 10737 → no checks reported yet
  • local code review found the routing gap above

Because of the unresolved runtime-routing mismatch, I’m requesting changes rather than marking this QA-passed.

@drousselbot

Copy link
Copy Markdown

Deterministic workflow-state update could not be persisted after QA review.

workflow_dispatcher.py transition --event qa-fail failed because this repository does not currently have the dispatcher label vocabulary (qa-failed is missing; needs-qa, qa-passed, po-approved, blocked, waiting-on-human, etc. are also absent from gh label list).

QA verdict for this PR is still changes requested / hand back to dev, but the lane state could not be recorded mechanically.

Remediation: create the dispatcher workflow labels for this repo, then re-apply the intended QA transition.

@firexrwt
firexrwt requested a review from drousselbot April 16, 2026 22:15
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Apr 25, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for identifying the custom-provider model-switch failure modes. Current main now provides the requested guarantees, so this PR is redundant.

Automated hermes-sweeper review evidence:

  • hermes_cli/model_switch.py:2234-2240 merges all declared custom_providers[].models IDs into the picker; tests/hermes_cli/test_model_switch_custom_providers.py:332-365 verifies dict-form enumeration. This was added by bca03eab2.
  • cli.py:8173-8180 persists the live route in requested_provider and replaces explicit credentials/base URL, preventing the next credential-resolution pass from restoring the config route. This behavior landed in 70f798043.
  • agent/turn_context.py:154-163 passes the live provider/model/endpoint credentials per turn, while agent/auxiliary_client.py:2225-2250 records them for auxiliary routing; 40fcb9658 added the custom-provider endpoint coverage.

No release tag was determined from the inspected history.

@teknium1 teknium1 closed this Jul 12, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants