From be58ae730ef080e144786040afb8bc4a190dbab5 Mon Sep 17 00:00:00 2001 From: daleselaji-dev <265319989+daleselaji-dev@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:41:05 +0800 Subject: [PATCH 1/2] fix(router): preserve requested model for wildcard deployments --- litellm/router.py | 10 +++++- .../test_router_wildcard_model.py | 36 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/router_unit_tests/test_router_wildcard_model.py diff --git a/litellm/router.py b/litellm/router.py index 9cde292657c..8f2397173d3 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -4490,7 +4490,15 @@ class Router: self._update_kwargs_with_deployment(deployment=deployment, kwargs=kwargs, function_name=function_name) data: Final = deployment["litellm_params"].copy() - model_name: Final = data["model"] + deployment_model_name: Final = data["model"] + # Wildcard deployments provide provider credentials but must keep + # the concrete model requested by the caller. Passing the wildcard + # through prevents model-cost lookups and Responses API bridging. + model_name = ( + model + if "*" in deployment_model_name and "*" not in model + else deployment_model_name + ) self.total_calls[model_name] += 1 self._add_deployment_model_to_endpoint_for_llm_passthrough_route( diff --git a/tests/router_unit_tests/test_router_wildcard_model.py b/tests/router_unit_tests/test_router_wildcard_model.py new file mode 100644 index 00000000000..c643d0f8237 --- /dev/null +++ b/tests/router_unit_tests/test_router_wildcard_model.py @@ -0,0 +1,36 @@ +from unittest.mock import AsyncMock +from unittest.mock import patch + +import pytest + +from litellm import Router + + +@pytest.mark.asyncio +async def test_wildcard_deployment_preserves_requested_model() -> None: + """Provider wildcard credentials must not replace the concrete request model.""" + router = Router(model_list=[]) + deployment = { + "model_name": "openai/*", + "litellm_params": {"model": "openai/*", "api_key": "sk-test"}, + "model_info": {"id": "wildcard-deployment"}, + } + original_function = AsyncMock(return_value="response") + + with ( + patch.object( + router, + "async_get_available_deployment", + new=AsyncMock(return_value=deployment), + ), + patch.object(router, "async_routing_strategy_pre_call_checks", new=AsyncMock()), + patch.object(router, "_get_client", return_value=None), + ): + result = await router._ageneric_api_call_with_fallbacks_helper( + model="openai/gpt-5.3-codex", + original_generic_function=original_function, + messages=[{"role": "user", "content": "ping"}], + ) + + assert result == "response" + assert original_function.await_args.kwargs["model"] == "openai/gpt-5.3-codex" From daf849c433753713374bf4163818f8988c97a0c3 Mon Sep 17 00:00:00 2001 From: daleselaji-dev <265319989+daleselaji-dev@users.noreply.github.com> Date: Wed, 5 Aug 2026 11:57:00 +0800 Subject: [PATCH 2/2] test(router): exercise wildcard deployment selection --- litellm/router.py | 3 -- .../test_router_wildcard_model.py | 35 ++++++++----------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index 8f2397173d3..87a81fa9034 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -4491,9 +4491,6 @@ class Router: data: Final = deployment["litellm_params"].copy() deployment_model_name: Final = data["model"] - # Wildcard deployments provide provider credentials but must keep - # the concrete model requested by the caller. Passing the wildcard - # through prevents model-cost lookups and Responses API bridging. model_name = ( model if "*" in deployment_model_name and "*" not in model diff --git a/tests/router_unit_tests/test_router_wildcard_model.py b/tests/router_unit_tests/test_router_wildcard_model.py index c643d0f8237..da9731ef2f7 100644 --- a/tests/router_unit_tests/test_router_wildcard_model.py +++ b/tests/router_unit_tests/test_router_wildcard_model.py @@ -1,5 +1,4 @@ from unittest.mock import AsyncMock -from unittest.mock import patch import pytest @@ -9,28 +8,22 @@ from litellm import Router @pytest.mark.asyncio async def test_wildcard_deployment_preserves_requested_model() -> None: """Provider wildcard credentials must not replace the concrete request model.""" - router = Router(model_list=[]) - deployment = { - "model_name": "openai/*", - "litellm_params": {"model": "openai/*", "api_key": "sk-test"}, - "model_info": {"id": "wildcard-deployment"}, - } + router = Router( + model_list=[ + { + "model_name": "openai/*", + "litellm_params": {"model": "openai/*", "api_key": "sk-test"}, + "model_info": {"id": "wildcard-deployment"}, + } + ] + ) original_function = AsyncMock(return_value="response") - with ( - patch.object( - router, - "async_get_available_deployment", - new=AsyncMock(return_value=deployment), - ), - patch.object(router, "async_routing_strategy_pre_call_checks", new=AsyncMock()), - patch.object(router, "_get_client", return_value=None), - ): - result = await router._ageneric_api_call_with_fallbacks_helper( - model="openai/gpt-5.3-codex", - original_generic_function=original_function, - messages=[{"role": "user", "content": "ping"}], - ) + result = await router._ageneric_api_call_with_fallbacks_helper( + model="openai/gpt-5.3-codex", + original_generic_function=original_function, + messages=[{"role": "user", "content": "ping"}], + ) assert result == "response" assert original_function.await_args.kwargs["model"] == "openai/gpt-5.3-codex"