fix(models): a vendor's own id on its first-party provider never re-routes
215fd0ecb9 made the current provider's live catalog outrank static
guesses, but a live catalog that could not be fetched (Codex outage, cold
cache) looked identical to "not served", so `/model gpt-6-astra` on
openai-codex still walked to OpenRouter (which relists every vendor)
whenever an OpenRouter key existed. Same shape for grok-* on xai-oauth
and claude-* on anthropic.
detect_provider_for_model now treats a vendor id typed on that vendor's
single-vendor first-party provider as a selection: stay, let the vendor
accept or reject it (the existing "not found in listing" note still
fires). Cross-vendor remaps (claude id on Codex -> keyed OpenRouter) and
aggregator / custom / multi-vendor reseller sessions are unchanged.
This commit is contained in:
@@ -938,7 +938,8 @@ def detect_provider_for_model(
|
||||
skipped and the ladder continues (``None`` = stay on the current provider). Exceptions: the user
|
||||
NAMED the provider (``/model nous``), or there is no current provider yet (``auto``) — then the
|
||||
first guess is returned so the credential step fails loudly instead of silently ignoring input."""
|
||||
from hermes_cli.models_detect import current_provider_catalog_match, provider_has_credentials
|
||||
from hermes_cli.models_detect import (
|
||||
current_provider_catalog_match, current_provider_owns_vendor, provider_has_credentials)
|
||||
|
||||
name = (model_name or "").strip()
|
||||
if not name:
|
||||
@@ -950,6 +951,10 @@ def detect_provider_for_model(
|
||||
served = current_provider_catalog_match(name, current_provider)
|
||||
if served is not None:
|
||||
return (current_provider, served) if served != name else None
|
||||
# Live catalog unavailable or lagging: the vendor's own id on the vendor's first-party provider
|
||||
# is still a selection — an aggregator relisting it is not grounds to switch.
|
||||
if current_provider_owns_vendor(name, current_provider):
|
||||
return None
|
||||
|
||||
no_selection = (current_provider or "").strip().lower() in {"", "auto"}
|
||||
for candidate in _detection_candidates(name, current_provider):
|
||||
|
||||
@@ -44,6 +44,31 @@ def current_provider_catalog_match(model_name: str, current_provider: str) -> Op
|
||||
(mid for mid in catalog if "/" in mid and mid.split("/", 1)[1].lower() == wanted), None)
|
||||
|
||||
|
||||
def current_provider_owns_vendor(model_name: str, current_provider: str) -> bool:
|
||||
"""True when *model_name* belongs to the vendor a single-vendor first-party provider natively
|
||||
serves (``gpt-6-astra`` on ``openai-codex``, ``grok-4.6`` on ``xai-oauth``).
|
||||
|
||||
A first-party session plus that vendor's own id is a selection, not a guess: when the live
|
||||
catalog could not confirm the id (fetch failed, static fallback lags an early-access rollout)
|
||||
the answer is "stay and let the vendor accept or reject it", never "a reseller lists it, so
|
||||
switch there". Aggregators, custom endpoints and multi-vendor resellers (nvidia, alibaba, ...)
|
||||
have no single native vendor and are skipped."""
|
||||
from hermes_cli.model_normalize import detect_vendor
|
||||
from hermes_cli.models import _AGGREGATOR_PROVIDERS, _PROVIDER_MODELS, normalize_provider
|
||||
|
||||
provider = (current_provider or "").strip().lower()
|
||||
if provider in _SKIP or provider.startswith("custom:"):
|
||||
return False
|
||||
normalized = normalize_provider(provider)
|
||||
if normalized in _SKIP or normalized in _AGGREGATOR_PROVIDERS:
|
||||
return False
|
||||
vendor = detect_vendor(model_name or "")
|
||||
if not vendor:
|
||||
return False
|
||||
native = {detect_vendor(mid) for mid in _PROVIDER_MODELS.get(normalized, ())} - {None}
|
||||
return native == {vendor}
|
||||
|
||||
|
||||
def provider_has_credentials(provider: str) -> bool:
|
||||
"""Whether *provider* can be switched to without the user typing a key: env/.env key, auth
|
||||
store login, or a usable credential-pool entry. ``custom``/``custom:*`` targets only come out
|
||||
|
||||
Reference in New Issue
Block a user