Merge pull request #22320 from tombii/fix/openrouter-native-model-double-stripping

fix(openrouter): pattern-based fix for native model double-stripping
This commit is contained in:
Cesar Garcia 2026-03-02 17:01:48 -03:00 • committed by GitHub
commit 5d512f64fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 98 additions and 0 deletions

View file

@ -159,6 +159,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)

View file

@ -0,0 +1,90 @@
"""
Tests for OpenRouter model name routing in get_llm_provider.
OpenRouter-native models have IDs that start with "openrouter/" (e.g.
openrouter/auto, openrouter/free, openrouter/aurora-alpha). When a user
configures such a model in LiteLLM they use the double-prefixed form
"openrouter/openrouter/aurora-alpha". get_llm_provider must strip only
the outer "openrouter/" provider prefix and leave the inner one intact,
so the correct model ID is sent to the OpenRouter API.
See: https://github.com/BerriAI/litellm/issues/16353
"""
import os
import sys
import pytest
sys.path.insert(0, os.path.abspath("../../../../.."))
import litellm
class TestOpenRouterNativeModelRouting:
"""get_llm_provider must not double-strip native OpenRouter model names."""
@pytest.mark.parametrize(
"input_model,expected_model",
[
# Well-known native models
("openrouter/openrouter/auto", "openrouter/auto"),
("openrouter/openrouter/free", "openrouter/free"),
("openrouter/openrouter/bodybuilder", "openrouter/bodybuilder"),
# Arbitrary native models — the fix must be pattern-based, not a hardcoded list
("openrouter/openrouter/aurora-alpha", "openrouter/aurora-alpha"),
("openrouter/openrouter/polaris-alpha", "openrouter/polaris-alpha"),
("openrouter/openrouter/some-future-model", "openrouter/some-future-model"),
],
)
def test_double_prefixed_strips_once(self, input_model, expected_model):
"""openrouter/openrouter/<model> should yield model=openrouter/<model>."""
result_model, provider, _, _ = litellm.get_llm_provider(model=input_model)
assert provider == "openrouter"
assert result_model == expected_model
@pytest.mark.parametrize(
"input_model",
[
"openrouter/openrouter/aurora-alpha",
"openrouter/openrouter/auto",
"openrouter/openrouter/free",
"openrouter/openrouter/some-future-model",
],
)
def test_bridge_double_call_preserves_native_model(self, input_model):
"""Simulates two consecutive get_llm_provider calls (bridge → completion).
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"
expected_model = input_model.split("/", 1)[1] # openrouter/<model>
assert model_first == expected_model
# 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_model # preserved, not stripped further
@pytest.mark.parametrize(
"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"),
],
)
def test_regular_models_still_strip_normally(self, input_model, expected_model):
"""Non-native OpenRouter models should still have their prefix stripped."""
result_model, provider, _, _ = litellm.get_llm_provider(model=input_model)
assert provider == "openrouter"
assert result_model == expected_model