From 6f0ea1be5f3327b482b5f57f58c968886b345b32 Mon Sep 17 00:00:00 2001 From: Azmi Muwahid <92735014+azmi2409@users.noreply.github.com> Date: Tue, 20 Oct 2026 18:46:07 +0700 Subject: [PATCH] fix(router): satisfy ruff format check and cover deployments without model info --- .../complexity_router/complexity_router.py | 10 +++++--- .../router_strategy/test_complexity_router.py | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index 736bb6cc59c..e139206b874 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -3456,9 +3456,13 @@ class ComplexityRouter(CustomLogger): @staticmethod def _deployment_model_id(deployment: Mapping[str, object]) -> str | None: model_info: Final = deployment.get("model_info") - deployment_id: Final = cast( # cast-ok: the isinstance check in the condition narrows model_info to a Mapping with str keys - "Mapping[str, object]", model_info - ).get("id") if isinstance(model_info, Mapping) else None + deployment_id: Final = ( + cast( # cast-ok: the isinstance check in the condition narrows model_info to a Mapping with str keys + "Mapping[str, object]", model_info + ).get("id") + if isinstance(model_info, Mapping) + else None + ) return None if deployment_id is None else str(deployment_id) def _group_has_deployment_id(self, group: str, model_id: str) -> bool: diff --git a/tests/unit/router_strategy/test_complexity_router.py b/tests/unit/router_strategy/test_complexity_router.py index a5c39e616b5..fc54cb120c1 100644 --- a/tests/unit/router_strategy/test_complexity_router.py +++ b/tests/unit/router_strategy/test_complexity_router.py @@ -17426,6 +17426,31 @@ class TestPreviousResponseReuse: assert decision["cause"] == "previous_response_reuse", decision assert decision["conversation_continuing"] is True, decision + @pytest.mark.asyncio + async def test_tool_continuation_skips_deployments_without_model_info(self, mock_router_instance, basic_config): + router = self._router(mock_router_instance, basic_config, default_model="gpt-4o") + deployments = { + "gpt-4o-mini": [ + {"model_name": "gpt-4o-mini", "litellm_params": {"model": "gpt-4o-mini"}}, + { + "model_name": "gpt-4o-mini", + "litellm_params": {"model": "gpt-4o-mini"}, + "model_info": {"id": "deployment-mini"}, + }, + ] + } + mock_router_instance.get_model_list.side_effect = lambda model_name=None: deployments.get(model_name, []) + result = await router.async_pre_routing_hook( + model="test-complexity-router", + request_kwargs={"previous_response_id": self._encoded_response_id("deployment-mini")}, + messages=[{"role": "tool", "content": "search results", "tool_call_id": "call_1"}], + ) + assert result is not None + assert result.model == "gpt-4o-mini", result.routing_decision + decision = result.routing_decision + assert decision is not None + assert decision["cause"] == "previous_response_reuse", decision + @pytest.mark.asyncio async def test_unencoded_previous_response_id_still_falls_back_to_default( self, mock_router_instance, basic_config