From 9d7fc307b80fcb8efeb46fdde408a184c68851e1 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Thu, 26 Mar 2026 09:28:53 +0530 Subject: [PATCH] fix(openrouter): strip LiteLLM prefix when proxy sets custom_llm_provider Wildcard openrouter/* deployments pass custom_llm_provider=openrouter with the full openrouter/provider/model id; OpenRouter expects provider/model. Strip the outer openrouter/ only when the remainder contains a slash so native ids like openrouter/auto stay intact. Adds regression test for proxy wildcard path. Made-with: Cursor --- .../litellm_core_utils/get_llm_provider_logic.py | 13 ++++++++----- .../openrouter/test_openrouter_provider_routing.py | 14 +++++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py index 3621841737..c26759eaf0 100644 --- a/litellm/litellm_core_utils/get_llm_provider_logic.py +++ b/litellm/litellm_core_utils/get_llm_provider_logic.py @@ -158,12 +158,15 @@ def get_llm_provider( # noqa: PLR0915 ): # handle scenario where model="azure/*" and custom_llm_provider="azure" model = custom_llm_provider + "/" + model - # Native OpenRouter models have IDs like "openrouter/free" where the - # "openrouter/" prefix is part of the actual model name on the API. - # When called from a bridge (e.g. anthropic_messages adapter), - # custom_llm_provider is already resolved, so return early to prevent - # the provider-list stripping below from removing the prefix. + # OpenRouter: when the router/proxy already set custom_llm_provider, + # the model may still carry LiteLLM's "openrouter/" routing prefix. + # Native IDs like "openrouter/auto" must stay intact for the API; IDs + # like "openrouter/anthropic/claude-3.5-sonnet" must become + # "anthropic/claude-3.5-sonnet" (OpenRouter expects provider/model). if custom_llm_provider == "openrouter" and model.startswith("openrouter/"): + remainder = model[len("openrouter/") :] + if "/" in remainder: + return remainder, custom_llm_provider, dynamic_api_key, api_base return model, custom_llm_provider, dynamic_api_key, api_base if api_key and api_key.startswith("os.environ/"): diff --git a/tests/test_litellm/llms/openrouter/test_openrouter_provider_routing.py b/tests/test_litellm/llms/openrouter/test_openrouter_provider_routing.py index 72cf2eec37..0815b15c87 100644 --- a/tests/test_litellm/llms/openrouter/test_openrouter_provider_routing.py +++ b/tests/test_litellm/llms/openrouter/test_openrouter_provider_routing.py @@ -80,7 +80,10 @@ class TestOpenRouterNativeModelRouting: "input_model,expected_model", [ ("openrouter/anthropic/claude-3-haiku", "anthropic/claude-3-haiku"), - ("openrouter/meta-llama/llama-3-70b-instruct", "meta-llama/llama-3-70b-instruct"), + ( + "openrouter/meta-llama/llama-3-70b-instruct", + "meta-llama/llama-3-70b-instruct", + ), ], ) def test_regular_models_still_strip_normally(self, input_model, expected_model): @@ -88,3 +91,12 @@ class TestOpenRouterNativeModelRouting: result_model, provider, _, _ = litellm.get_llm_provider(model=input_model) assert provider == "openrouter" assert result_model == expected_model + + def test_wildcard_deployment_strips_routing_prefix(self): + """openrouter/* proxy deployments pass custom_llm_provider; strip LiteLLM prefix.""" + result_model, provider, _, _ = litellm.get_llm_provider( + model="openrouter/anthropic/claude-3.5-sonnet", + custom_llm_provider="openrouter", + ) + assert provider == "openrouter" + assert result_model == "anthropic/claude-3.5-sonnet"