From f3b23034280f217fa66b949151d1121ce1016a5a Mon Sep 17 00:00:00 2001 From: Test Date: Fri, 20 Mar 2026 12:47:23 -0700 Subject: [PATCH] fix(gateway): skip model auto-detection for custom/local providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the CLI fix for the gateway /model handler. When the user is on a custom provider (provider=custom, localhost, or 127.0.0.1 endpoint), /model no longer tries to auto-detect a provider switch. Previously, typing /model openrouter/nvidia/nemotron:free on Telegram while on a localhost endpoint would silently accept the model name on the local server — auto-detection failed to match the free model, so the provider stayed as custom with the localhost base_url. The user saw 'Model changed' but requests still went to localhost, which doesn't serve that model. Now shows the endpoint URL and provider:model syntax tip, matching the CLI behavior. --- gateway/run.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/gateway/run.py b/gateway/run.py index 95473874..3f47dafb 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -2498,8 +2498,22 @@ class GatewayRunner: # Parse provider:model syntax target_provider, new_model = parse_model_input(args, current_provider) + + # Detect custom/local provider — skip auto-detection to prevent + # silently accepting an OpenRouter model name on a localhost endpoint. + # Users must use explicit provider:model syntax to switch away. + _resolved_base = "" + try: + from hermes_cli.runtime_provider import resolve_runtime_provider as _rtp + _resolved_base = _rtp(requested=current_provider).get("base_url", "") + except Exception: + pass + is_custom = current_provider == "custom" or ( + "localhost" in _resolved_base or "127.0.0.1" in _resolved_base + ) + # Auto-detect provider when no explicit provider:model syntax was used - if target_provider == current_provider: + if target_provider == current_provider and not is_custom: from hermes_cli.models import detect_provider_for_model detected = detect_provider_for_model(new_model, current_provider) if detected: @@ -2580,7 +2594,18 @@ class GatewayRunner: # Clear fallback state since user explicitly chose a model self._effective_model = None self._effective_provider = None - return f"🤖 Model changed to `{new_model}` ({persist_note}){provider_note}{warning}\n_(takes effect on next message)_" + + # Helpful hint when staying on a custom/local endpoint + custom_hint = "" + if is_custom and not provider_changed: + endpoint = _resolved_base or "custom endpoint" + custom_hint = ( + f"\n**Endpoint:** `{endpoint}`" + "\n_To switch providers, use_ `/model provider:model`" + "\n_e.g._ `/model openrouter:anthropic/claude-sonnet-4`" + ) + + return f"🤖 Model changed to `{new_model}` ({persist_note}){provider_note}{warning}{custom_hint}\n_(takes effect on next message)_" async def _handle_provider_command(self, event: MessageEvent) -> str: """Handle /provider command - show available providers."""