From bdc63d590ecf3e3a396c09a7e400b6ff59fed607 Mon Sep 17 00:00:00 2001 From: yassin Date: Tue, 15 Sep 2026 00:42:18 +0000 Subject: [PATCH 1/2] fix(router): keep weighted routing when a deployment id equals a model_name Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/router.py | 2 +- tests/test_litellm/test_router.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/litellm/router.py b/litellm/router.py index 0657c1e05ba..ca0f685b6df 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -12502,7 +12502,7 @@ class Router: # check if aliases set on litellm model alias map if specific_deployment is True: return model, self._get_deployment_by_litellm_model(model=model) - elif self.has_model_id(model): + elif model not in self.model_names and self.has_model_id(model): deployment: Final = self.get_deployment(model_id=model) if deployment is not None: deployment_model: Final = deployment.litellm_params.model diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index f1b445fb1bd..1977fe715cd 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -15806,3 +15806,29 @@ async def test_an_open_circuit_breaker_skips_the_session_binding_without_a_warni assert binding is None assert [record.getMessage() for record in caplog.records if record.levelno >= logging.WARNING] == [] assert any("circuit breaker is open" in record.getMessage() for record in caplog.records) + + +@pytest.mark.asyncio +async def test_model_name_colliding_with_a_deployment_id_still_load_balances_the_group(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-5-nano", + "litellm_params": {"model": "openai/gpt-5-nano", "api_key": "k", "weight": 0, "mock_response": "A"}, + "model_info": {"id": "gpt-5-nano"}, + }, + { + "model_name": "gpt-5-nano", + "litellm_params": {"model": "openai/gpt-5-mini", "api_key": "k", "weight": 1, "mock_response": "B"}, + "model_info": {"id": "gpt-5-mini-dep"}, + }, + ], + routing_strategy="simple-shuffle", + ) + + by_group = await router.acompletion(model="gpt-5-nano", messages=[{"role": "user", "content": "hi"}]) + by_id = await router.acompletion(model="gpt-5-mini-dep", messages=[{"role": "user", "content": "hi"}]) + + assert by_group._hidden_params["model_id"] == "gpt-5-mini-dep" + assert by_group.choices[0].message.content == "B" + assert by_id._hidden_params["model_id"] == "gpt-5-mini-dep" From 1943667fef6036437acab574d5a024ac10d563db Mon Sep 17 00:00:00 2001 From: yassin Date: Tue, 15 Sep 2026 00:51:13 +0000 Subject: [PATCH 2/2] fix(router): run sync pre-call checks when a model_name collides with a deployment id Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/router.py | 2 +- tests/test_litellm/test_router.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/litellm/router.py b/litellm/router.py index ca0f685b6df..e56530a19e3 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -2461,7 +2461,7 @@ class Router: ### DEPLOYMENT-SPECIFIC PRE-CALL CHECKS ### (e.g. update rpm pre-call. Raise error, if deployment over limit) ## only run if model group given, not model id - if not self.has_model_id(model): + if model in self.model_names or not self.has_model_id(model): self.routing_strategy_pre_call_checks(deployment=deployment) input_kwargs: Final = { diff --git a/tests/test_litellm/test_router.py b/tests/test_litellm/test_router.py index 1977fe715cd..f412af4564b 100644 --- a/tests/test_litellm/test_router.py +++ b/tests/test_litellm/test_router.py @@ -15832,3 +15832,31 @@ async def test_model_name_colliding_with_a_deployment_id_still_load_balances_the assert by_group._hidden_params["model_id"] == "gpt-5-mini-dep" assert by_group.choices[0].message.content == "B" assert by_id._hidden_params["model_id"] == "gpt-5-mini-dep" + + +def test_sync_completion_runs_pre_call_checks_for_a_model_name_colliding_with_a_deployment_id(): + router = litellm.Router( + model_list=[ + { + "model_name": "gpt-5-nano", + "litellm_params": {"model": "openai/gpt-5-nano", "api_key": "k", "weight": 0, "mock_response": "A"}, + "model_info": {"id": "gpt-5-nano"}, + }, + { + "model_name": "gpt-5-nano", + "litellm_params": {"model": "openai/gpt-5-mini", "api_key": "k", "weight": 1, "mock_response": "B"}, + "model_info": {"id": "gpt-5-mini-dep"}, + }, + ], + routing_strategy="simple-shuffle", + ) + + with patch.object(router, "routing_strategy_pre_call_checks") as pre_call_checks: + by_group = router.completion(model="gpt-5-nano", messages=[{"role": "user", "content": "hi"}]) + assert by_group._hidden_params["model_id"] == "gpt-5-mini-dep" + pre_call_checks.assert_called_once() + assert pre_call_checks.call_args.kwargs["deployment"]["model_info"]["id"] == "gpt-5-mini-dep" + + by_id = router.completion(model="gpt-5-mini-dep", messages=[{"role": "user", "content": "hi"}]) + assert by_id._hidden_params["model_id"] == "gpt-5-mini-dep" + pre_call_checks.assert_called_once()