Skip to content

Commit 55318b2

Browse files
tombiiclaude
andcommitted
fix(openrouter): pattern-based fix for native OpenRouter model double-stripping
Replace the hardcoded NATIVE_OPENROUTER_MODELS set approach with a pattern-based check in _get_openai_compatible_provider_info: after stripping the outer "openrouter/" provider prefix, if the remaining model name still starts with "openrouter/", return immediately without further stripping. This fixes openrouter/openrouter/aurora-alpha, openrouter/openrouter/polaris-alpha, and any future native OpenRouter models — not just the three hard-coded ones (auto, free, bodybuilder) from the previous approach. Fixes #16353 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f694823 commit 55318b2

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

litellm/litellm_core_utils/get_llm_provider_logic.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,15 @@ def _get_openai_compatible_provider_info( # noqa: PLR0915
504504
custom_llm_provider = model.split("/", 1)[0]
505505
model = model.split("/", 1)[1]
506506

507+
# If the provider is openrouter and the remaining model name still starts
508+
# with "openrouter/", that inner prefix is part of the actual model ID on
509+
# the OpenRouter API (e.g. openrouter/openrouter/aurora-alpha →
510+
# model="openrouter/aurora-alpha"). Return immediately so the prefix is
511+
# not stripped a second time.
512+
if custom_llm_provider == "openrouter" and model.startswith("openrouter/"):
513+
dynamic_api_key = api_key or get_secret_str("OPENROUTER_API_KEY")
514+
return model, custom_llm_provider, dynamic_api_key, api_base
515+
507516
# Check JSON providers FIRST (before hardcoded ones)
508517
from litellm.llms.openai_like.dynamic_config import create_config_class
509518
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""
2+
Tests for OpenRouter model name routing in get_llm_provider.
3+
4+
OpenRouter-native models have IDs that start with "openrouter/" (e.g.
5+
openrouter/auto, openrouter/free, openrouter/aurora-alpha). When a user
6+
configures such a model in LiteLLM they use the double-prefixed form
7+
"openrouter/openrouter/aurora-alpha". get_llm_provider must strip only
8+
the outer "openrouter/" provider prefix and leave the inner one intact,
9+
so the correct model ID is sent to the OpenRouter API.
10+
11+
See: https://github.com/BerriAI/litellm/issues/16353
12+
"""
13+
14+
import os
15+
import sys
16+
17+
import pytest
18+
19+
sys.path.insert(0, os.path.abspath("../../../../.."))
20+
21+
import litellm
22+
23+
24+
class TestOpenRouterNativeModelRouting:
25+
"""get_llm_provider must not double-strip native OpenRouter model names."""
26+
27+
@pytest.mark.parametrize(
28+
"input_model,expected_model",
29+
[
30+
# Well-known native models
31+
("openrouter/openrouter/auto", "openrouter/auto"),
32+
("openrouter/openrouter/free", "openrouter/free"),
33+
("openrouter/openrouter/bodybuilder", "openrouter/bodybuilder"),
34+
# Arbitrary native models — the fix must be pattern-based, not a hardcoded list
35+
("openrouter/openrouter/aurora-alpha", "openrouter/aurora-alpha"),
36+
("openrouter/openrouter/polaris-alpha", "openrouter/polaris-alpha"),
37+
("openrouter/openrouter/some-future-model", "openrouter/some-future-model"),
38+
],
39+
)
40+
def test_double_prefixed_strips_once(self, input_model, expected_model):
41+
"""openrouter/openrouter/<model> should yield model=openrouter/<model>."""
42+
result_model, provider, _, _ = litellm.get_llm_provider(model=input_model)
43+
assert provider == "openrouter"
44+
assert result_model == expected_model
45+
46+
@pytest.mark.parametrize(
47+
"input_model,expected_model",
48+
[
49+
("openrouter/openrouter/aurora-alpha", "openrouter/aurora-alpha"),
50+
("openrouter/openrouter/auto", "openrouter/auto"),
51+
],
52+
)
53+
def test_no_double_strip_on_second_call(self, input_model, expected_model):
54+
"""Simulates two consecutive get_llm_provider calls (bridge → completion)."""
55+
model_first, provider, _, _ = litellm.get_llm_provider(model=input_model)
56+
assert model_first == expected_model
57+
58+
model_second, provider2, _, _ = litellm.get_llm_provider(model=model_first)
59+
assert provider2 == "openrouter"
60+
assert model_second == expected_model
61+
62+
@pytest.mark.parametrize(
63+
"input_model,expected_model",
64+
[
65+
("openrouter/anthropic/claude-3-haiku", "anthropic/claude-3-haiku"),
66+
("openrouter/meta-llama/llama-3-70b-instruct", "meta-llama/llama-3-70b-instruct"),
67+
],
68+
)
69+
def test_regular_models_still_strip_normally(self, input_model, expected_model):
70+
"""Non-native OpenRouter models should still have their prefix stripped."""
71+
result_model, provider, _, _ = litellm.get_llm_provider(model=input_model)
72+
assert provider == "openrouter"
73+
assert result_model == expected_model

0 commit comments

Comments
 (0)