From 4eae6c98f98ae528745083058d8f9f248cc8e114 Mon Sep 17 00:00:00 2001 From: Nathan Esquenazi Date: Fri, 3 Apr 2026 20:23:25 +0000 Subject: [PATCH] fix: cross-provider model pick causes Connection lost on non-OpenRouter profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: resolve_model_provider() had a branch: if config_provider and config_provider != 'openrouter' and prefix in _PROVIDER_MODELS: return bare, prefix, None When Camanji profile (config_provider='anthropic') picked openai/gpt-5.4-mini from the OpenRouter dropdown, prefix='openai' matched _PROVIDER_MODELS and config_provider was not 'openrouter', so it returned ('gpt-5.4-mini', 'openai', None). The agent then demanded OPENAI_API_KEY directly -- not found -- RuntimeError -- stream crashed -- 'Connection lost'. Fix: if prefix != config_provider (cross-provider selection), always route through openrouter with the full provider/model string. Only strip the prefix and call a direct provider API when the config_provider EXACTLY matches the model prefix. Cases verified: openrouter + openai/gpt-5.4-mini -> (openai/gpt-5.4-mini, openrouter) ✓ anthropic + openai/gpt-5.4-mini -> (openai/gpt-5.4-mini, openrouter) ✓ FIXED anthropic + anthropic/claude-... -> (claude-..., anthropic) ✓ anthropic + claude-sonnet-4-6 bare -> (claude-sonnet-4-6, anthropic) ✓ openrouter + anthropic/claude-... -> (anthropic/claude-..., openrouter) ✓ Tests: 426 passed, 0 failed. --- api/config.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/api/config.py b/api/config.py index b961754..dc399b1 100644 --- a/api/config.py +++ b/api/config.py @@ -373,15 +373,16 @@ def resolve_model_provider(model_id: str): if '/' in model_id: prefix, bare = model_id.split('/', 1) - # If prefix matches config provider, strip it and use that provider directly + # If prefix matches config provider exactly, strip it and use that provider directly. + # e.g. config=anthropic, model=anthropic/claude-... → bare name to anthropic API if config_provider and prefix == config_provider: return bare, config_provider, config_base_url - # If the config provider is openrouter (or unset/None), pass the full - # provider/model string through -- OpenRouter uses this as its model ID. - # Only strip the prefix and switch to a direct-API provider when the - # config is explicitly set to that direct provider. - if config_provider and config_provider != 'openrouter' and prefix in _PROVIDER_MODELS: - return bare, prefix, None + # If prefix does NOT match config provider, the user picked a cross-provider model + # from the OpenRouter dropdown (e.g. config=anthropic but picked openai/gpt-5.4-mini). + # In this case always route through openrouter with the full provider/model string. + # Never strip the prefix and try a direct-API call to a provider whose key may not exist. + if prefix in _PROVIDER_MODELS and prefix != config_provider: + return model_id, 'openrouter', None return model_id, config_provider, config_base_url