mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-14 09:05:34 +00:00
refactor: move native OpenRouter check to get_llm_provider before strip
The previous check in _get_openai_compatible_provider_info() ran after
the model name was already split, so it never caught the second
get_llm_provider() call from the anthropic_messages bridge.
Moved the check to get_llm_provider() before the provider-list
stripping, using a pattern-based approach (custom_llm_provider ==
"openrouter" and model.startswith("openrouter/")) instead of a
hardcoded set. This covers all current and future native OpenRouter
models.
Updated tests to verify the bridge double-call scenario with
custom_llm_provider passed through.
This commit is contained in:
@@ -158,6 +158,14 @@ 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.
|
||||
if custom_llm_provider == "openrouter" and model.startswith("openrouter/"):
|
||||
return model, custom_llm_provider, dynamic_api_key, api_base
|
||||
|
||||
if api_key and api_key.startswith("os.environ/"):
|
||||
dynamic_api_key = get_secret_str(api_key)
|
||||
|
||||
@@ -504,15 +512,6 @@ def _get_openai_compatible_provider_info( # noqa: PLR0915
|
||||
custom_llm_provider = model.split("/", 1)[0]
|
||||
model = model.split("/", 1)[1]
|
||||
|
||||
# If the provider is openrouter and the remaining model name still starts
|
||||
# with "openrouter/", that inner prefix is part of the actual model ID on
|
||||
# the OpenRouter API (e.g. openrouter/openrouter/aurora-alpha →
|
||||
# model="openrouter/aurora-alpha"). Return immediately so the prefix is
|
||||
# not stripped a second time.
|
||||
if custom_llm_provider == "openrouter" and model.startswith("openrouter/"):
|
||||
dynamic_api_key = api_key or get_secret_str("OPENROUTER_API_KEY")
|
||||
return model, custom_llm_provider, dynamic_api_key, api_base
|
||||
|
||||
# Check JSON providers FIRST (before hardcoded ones)
|
||||
from litellm.llms.openai_like.dynamic_config import create_config_class
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
|
||||
@@ -44,31 +44,37 @@ class TestOpenRouterNativeModelRouting:
|
||||
assert result_model == expected_model
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_model,expected_first,expected_second",
|
||||
"input_model",
|
||||
[
|
||||
# After the first call strips outer prefix: openrouter/openrouter/aurora-alpha
|
||||
# → openrouter/aurora-alpha. A second call on that result splits at the
|
||||
# first "/" giving provider=openrouter, model=aurora-alpha — which is the
|
||||
# correct model ID to send to the OpenRouter API.
|
||||
("openrouter/openrouter/aurora-alpha", "openrouter/aurora-alpha", "aurora-alpha"),
|
||||
("openrouter/openrouter/auto", "openrouter/auto", "auto"),
|
||||
"openrouter/openrouter/aurora-alpha",
|
||||
"openrouter/openrouter/auto",
|
||||
"openrouter/openrouter/free",
|
||||
"openrouter/openrouter/some-future-model",
|
||||
],
|
||||
)
|
||||
def test_no_double_strip_on_second_call(self, input_model, expected_first, expected_second):
|
||||
def test_bridge_double_call_preserves_native_model(self, input_model):
|
||||
"""Simulates two consecutive get_llm_provider calls (bridge → completion).
|
||||
|
||||
The first call (bridge) converts openrouter/openrouter/<model> to
|
||||
openrouter/<model>. The second call (completion) further strips the
|
||||
remaining openrouter/ provider prefix and returns <model> — the bare
|
||||
model ID that should be sent to the OpenRouter API.
|
||||
The first call (bridge) strips the outer prefix:
|
||||
openrouter/openrouter/<model> → openrouter/<model>
|
||||
|
||||
The second call (completion) receives custom_llm_provider="openrouter"
|
||||
from the bridge, detects the native model, and preserves it:
|
||||
openrouter/<model> → openrouter/<model> (no further stripping)
|
||||
"""
|
||||
# First call: bridge resolves provider
|
||||
model_first, provider, _, _ = litellm.get_llm_provider(model=input_model)
|
||||
assert provider == "openrouter"
|
||||
assert model_first == expected_first
|
||||
expected_model = input_model.split("/", 1)[1] # openrouter/<model>
|
||||
assert model_first == expected_model
|
||||
|
||||
model_second, provider2, _, _ = litellm.get_llm_provider(model=model_first)
|
||||
# Second call: completion receives model + custom_llm_provider from bridge
|
||||
model_second, provider2, _, _ = litellm.get_llm_provider(
|
||||
model=model_first,
|
||||
custom_llm_provider="openrouter",
|
||||
)
|
||||
assert provider2 == "openrouter"
|
||||
assert model_second == expected_second
|
||||
assert model_second == expected_model # preserved, not stripped further
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_model,expected_model",
|
||||
|
||||
Reference in New Issue
Block a user