From ff655de4813a4a52862f3e2a3e6158c5ecf22a40 Mon Sep 17 00:00:00 2001 From: donrhmexe Date: Mon, 6 Apr 2026 15:20:06 +0200 Subject: [PATCH] fix: model alias fallback uses authenticated providers instead of hardcoded openrouter/nous MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an alias like 'claude' can't be resolved on the current provider, _resolve_alias_fallback() tries other providers. Previously it hardcoded ('openrouter', 'nous') — so '/model claude' on z.ai would resolve to openrouter even if the user doesn't have openrouter credentials but does have anthropic. Now the fallback uses the user's actual authenticated providers (detected via list_authenticated_providers which is backed by the models.dev in-memory cache). If no authenticated providers are found, falls back to the old ('openrouter', 'nous') for backwards compatibility. New helper: get_authenticated_provider_slugs() returns just the slug strings from list_authenticated_providers(). --- hermes_cli/model_switch.py | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index bff54eaef..b2f763c61 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -339,12 +339,37 @@ def resolve_alias( return None +def get_authenticated_provider_slugs( + current_provider: str = "", + user_providers: dict = None, +) -> list[str]: + """Return slugs of providers that have credentials. + + Uses ``list_authenticated_providers()`` which is backed by the models.dev + in-memory cache (1 hr TTL) — no extra network cost. + """ + try: + providers = list_authenticated_providers( + current_provider=current_provider, + user_providers=user_providers, + max_models=0, + ) + return [p["slug"] for p in providers] + except Exception: + return [] + + def _resolve_alias_fallback( raw_input: str, - fallback_providers: tuple[str, ...] = ("openrouter", "nous"), + authenticated_providers: list[str] = (), ) -> Optional[tuple[str, str, str]]: - """Try to resolve an alias on fallback providers.""" - for provider in fallback_providers: + """Try to resolve an alias on the user's authenticated providers. + + Falls back to ``("openrouter", "nous")`` only when no authenticated + providers are supplied (backwards compat for non-interactive callers). + """ + providers = authenticated_providers or ("openrouter", "nous") + for provider in providers: result = resolve_alias(raw_input, provider) if result is not None: return result @@ -494,7 +519,11 @@ def switch_model( # --- Step b: Alias exists but not on current provider -> fallback --- key = raw_input.strip().lower() if key in MODEL_ALIASES: - fallback_result = _resolve_alias_fallback(raw_input) + authed = get_authenticated_provider_slugs( + current_provider=current_provider, + user_providers=user_providers, + ) + fallback_result = _resolve_alias_fallback(raw_input, authed) if fallback_result is not None: target_provider, new_model, resolved_alias = fallback_result logger.debug(