Skip to content

Plugin providers resolve in /model, the model picker and hermes model, aliases land on the canonical name (#69576, salvage #52549 #54705 #65968 #34368) - #116853

Merged
teknium1 merged 3 commits into
mainfrom
fix/pp116408-switch-resolution
Sep 20, 2026
Merged

teknium1 merged 3 commits into
mainfrom
fix/pp116408-switch-resolution

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

Out-of-tree model-provider plugins now resolve everywhere a provider NAME is resolved at switch time — /model --provider <plugin|alias>, the Desktop/TUI model picker (tui_gatewayswitch_model) and hermes model — including profiles whose endpoint is minted at runtime, and an alias switch lands on the profile's canonical name.

Part of #116408. Closes #69576.

What changed

  • hermes_cli/providers.py::resolve_provider_full gains a last rung: any registered ProviderProfile becomes a ProviderDef after every user-configured rung (providers:, custom_providers:, managed llamacpp, models.dev), so config keeps precedence. Only the bare custom placeholder (aliases ollama/local/vllm) is excluded — model-switch completes it from the current endpoint, as before.
  • get_provider() and the new rung share one builder (_plugin_profile_pdef): the ProviderDef.id is profile.name (not the requested alias), URL-shaped env vars (*_BASE_URL/*_URL) are the endpoint override rather than credentials, and a profile with an env-configured endpoint resolves at the get_provider() rung (@WolframRavenwolf, fix(providers): resolve ProviderProfile identity consistently #52549).
  • hermes_cli/runtime_provider.py: a persisted model.provider written under a profile alias is the same registered provider as its canonical name, so its base_url/api_mode overrides apply (@WolframRavenwolf, fix(providers): resolve ProviderProfile identity consistently #52549 — compares the two registry rows' shared ProviderConfig.id; does not touch _detect_api_mode_for_url / profile api_mode propagation, which [Bug]: provider plugins' ProviderProfile.api_mode dropped at runtime — anthropic_messages plugins with non-/anthropic base_url 404 #53054's lane owns).
  • Docs: website/docs/developer-guide/model-provider-plugin.md integration table row for /model --provider + model picker switch.
  • Tests: tests/hermes_cli/test_provider_profile_identity.py — three invariants over a real plugin dir under the test HERMES_HOME (discovery + auth mirror real, only the remote model probe stubbed), all red on origin/main:
    1. alias switch carries the canonical identity + user providers: block wins,
    2. runtime-endpoint profile is a known provider while custom/ollama/custom:<x> placeholders stay None,
    3. persisted alias config applies to the canonical runtime provider.

Root cause

get_provider()'s plugin rung (7072fc4) admitted only profiles with a literal base_url and returned id=<requested alias>; a profile with an empty base_url (endpoint computed at runtime, vertex-style) never reached any rung of resolve_provider_full, so switch_model() rejected it as Unknown provider, while the picker listed it and the aux client already accepted it through the PROVIDER_REGISTRY mirror.

Live probe (temp HERMES_HOME, real plugin dir, real imports, providers*/hermes_cli*/agent* purged)

Profiles: example-key (base_url set, alias example-alias, anthropic_messages), example-runtime (empty base_url, api_key auth).

Surface Name base (76fe8f7) head
resolve_provider_full example-key id=example-key ✅ identical ✅
resolve_provider_full example-alias id=example-alias (alias leaks as identity) id=example-key ✅
resolve_provider_full example-runtime None id=example-runtime, source=plugin-profile ✅
switch_model(--provider) example-key target=example-key ✅ identical ✅
switch_model(--provider) example-alias target=example-alias target=example-key ✅
switch_model(--provider) example-runtime Unknown provider 'example-runtime' target=example-runtime, api_key resolved ✅
tui_gateway.server._apply_model_switch (Desktop/TUI picker RPC) example-runtime / rt-alias Unknown provider model_override.provider=example-runtime for both ✅
aux.resolve_provider_client example-key / alias / runtime ✅ (already via PROVIDER_REGISTRY mirror) identical ✅
negative: custom, ollama, vllm, custom:nope (no custom_providers) None None ✅
negative: no-such-provider None None ✅
control: nous, openai-codex, anthropic, openrouter, custom:foo, kimi-coding-cn, azure-foundry, vertex resolve_provider_full dict byte-identical to base
vertex-ai (bundled profile alias) resolve_provider_full id=vertex-ai id=vertex (intended: the profile-only alias @teknium1 named on #65968)

Tests: scripts/run_tests.sh tests/hermes_cli/ tests/agent/test_auxiliary_client_resolve_dedup.py tests/providers/ — the only failures (test_dashboard_auth_gate ×4, test_cmd_update::test_venv_repair_path_refreshes_memory_provider [uv not on PATH], test_local_runtime_recovery::test_reparented_router_keeps_its_endpoint [live-guard]) reproduce identically on a pristine origin/main worktree.

Salvage / credit

Not covered

Infographic

Plugin providers resolve everywhere

WolframRavenwolf and others added 3 commits September 19, 2026 23:10
Keep canonical plugin profile identity through CLI aliases, separate URL
variables from credentials, and honor matching persisted configuration
across registered aliases. Retain native custom and built-in routing.

Retain real discovery/runtime test provenance from the contributor while
omitting the already absorbed bridge and separately owned transport fix.

Co-authored-by: david-bowiegxw <834563048@qq.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…he model picker and hermes model

resolve_provider_full() gained a plugin-profile rung in get_provider() (7072fc4) but only for
profiles with a concrete base_url; a profile whose endpoint is minted at runtime (empty base_url,
no URL env var — vertex-style token exchange) was still "Unknown provider" in `/model --provider`,
the Desktop/TUI model picker (tui_gateway -> switch_model) and `hermes model`, while the picker
listed it and the aux client already accepted it through PROVIDER_REGISTRY.

Add one last rung in resolve_provider_full() that turns any registered ProviderProfile into a
ProviderDef, AFTER every user-configured rung (providers:, custom_providers:, llamacpp,
models.dev) so config keeps precedence, excluding only the bare ``custom`` placeholder that
model-switch completes from the current endpoint. The ProviderDef builder is shared with
get_provider() and keys the id on profile.name (alias switches persist under the canonical name).

Tests are trimmed to three invariants proven red on origin/main: alias identity through
switch_model + user-config precedence, the runtime-endpoint profile + placeholder negatives, and
the persisted-alias runtime config from the salvaged commit.

Salvage of #69576's proposed fix; #54705, #65968 and #34368 implemented the same tail rung.
Co-authored-by: Kyzcreig <kyzcreig@users.noreply.github.com>
Co-authored-by: 雾塔 <wutayunfan@gmail.com>
Co-authored-by: Bosco Barros <bosco.barros@ifood.com.br>
@github-actions

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on c81d909 — docs: plugin profiles and their aliases resolve in /model --

debug info

CI timings

CI timings · View report · View job

Wall time 8m39s vs 8m14s (+5.1%). 8 job(s) slower, 3 faster, 2 unchanged.

  • OS-specific tests / Windows-only tests: +41.0s
  • Docs Site / docs-site-checks: -30.0s
  • Python lints / Windows footguns (blocking): +25.0s
  • Python tests / Run tests: +11.0s
  • Check contributors / check-attribution: +7.0s

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins labels Sep 20, 2026
@teknium1
teknium1 merged commit 9f0cd9d into main Sep 20, 2026
37 checks passed
@teknium1
teknium1 deleted the fix/pp116408-switch-resolution branch September 20, 2026 17:02
teknium1 added a commit that referenced this pull request Sep 20, 2026
… onto #116853

The rebase kept the cherry-picked _registry_knows/sys.modules variant that the
refactor commit had already replaced with one registered_api_modes() read per
gate; this puts the branch's intended final providers.py back on top of the
#116853 builder.
teknium1 added a commit that referenced this pull request Sep 20, 2026
… onto #116853

The rebase kept the cherry-picked _registry_knows/sys.modules variant that the
refactor commit had already replaced with one registered_api_modes() read per
gate; this puts the branch's intended final providers.py back on top of the
#116853 builder.
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 comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

3 participants