From 4a3b77527152e549f08a74ed941b424bd5f9e006 Mon Sep 17 00:00:00 2001 From: Zhibo Zhang Date: Sun, 1 Nov 2026 15:31:07 +0800 Subject: [PATCH 1/3] fix(router): enforce region prechecks without request content Signed-off-by: Zhibo Zhang <24061721@hdu.edu.cn> --- litellm/router_python_core.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/litellm/router_python_core.py b/litellm/router_python_core.py index 30b05456987..1368d3c06da 100644 --- a/litellm/router_python_core.py +++ b/litellm/router_python_core.py @@ -13713,10 +13713,10 @@ class PythonCore(DeprecatedRouterMembers): ) -> tuple[Mapping[str, Any], ...]: """ `deployments` without the ones `_pre_call_checks` keeps this request away from for being outside its allowed - region, typed for the tag routing filter + region, regardless of whether the request carries countable message or input content """ allowed_model_region: Final = None if request_kwargs is None else request_kwargs.get("allowed_model_region") - if allowed_model_region is None or not self._core_enable_pre_call_checks or not has_request_input: + if allowed_model_region is None or not self._core_enable_pre_call_checks: return tuple(deployments) if not isinstance(allowed_model_region, str): return () @@ -15757,7 +15757,7 @@ class PythonCore(DeprecatedRouterMembers): parent_otel_span=parent_otel_span, ) - if self._core_enable_pre_call_checks and (messages is not None or input is not None): + if self._core_enable_pre_call_checks: deployments_to_check: Final = healthy_deployments healthy_deployments = self._pre_call_checks( model=model, @@ -17028,7 +17028,7 @@ class PythonCore(DeprecatedRouterMembers): ) # filter pre-call checks - if self._core_enable_pre_call_checks and (messages is not None or input is not None): + if self._core_enable_pre_call_checks: healthy_deployments = self._pre_call_checks( model=model, healthy_deployments=healthy_deployments, @@ -17272,7 +17272,7 @@ class PythonCore(DeprecatedRouterMembers): pass_through_deployments = self._filter_blocked_deployments(pass_through_deployments) # 5. Apply pre-call checks (if enabled) - if self._core_enable_pre_call_checks and (messages is not None or input is not None): + if self._core_enable_pre_call_checks: pass_through_deployments = self._pre_call_checks( model=model, healthy_deployments=pass_through_deployments, From 87dbe0f4cea3963ba6a5838fe93e6942a4062e77 Mon Sep 17 00:00:00 2001 From: Zhibo Zhang Date: Sun, 1 Nov 2026 15:31:13 +0800 Subject: [PATCH 2/3] test(router): cover region filtering without message input Signed-off-by: Zhibo Zhang <24061721@hdu.edu.cn> --- .../test_router_region_no_input.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/unit/test_router/test_router_region_no_input.py diff --git a/tests/unit/test_router/test_router_region_no_input.py b/tests/unit/test_router/test_router_region_no_input.py new file mode 100644 index 00000000000..2ae769cea81 --- /dev/null +++ b/tests/unit/test_router/test_router_region_no_input.py @@ -0,0 +1,31 @@ +import pytest +from litellm import Router + + +@pytest.mark.asyncio +async def test_allowed_model_region_filters_requests_without_messages_or_input() -> None: + router = Router( + model_list=[ + { + "model_name": "region-test", + "litellm_params": {"model": "openai/gpt-4o", "region_name": "eu"}, + "model_info": {"id": "eu-deployment"}, + }, + { + "model_name": "region-test", + "litellm_params": {"model": "openai/gpt-4o-mini", "region_name": "us"}, + "model_info": {"id": "us-deployment"}, + }, + ], + enable_pre_call_checks=True, + ) + + deployments = await router.select( + model="region-test", + request_kwargs={"allowed_model_region": "eu"}, + kind="healthy", + messages=None, + input=None, + ) + + assert [deployment["model_info"]["id"] for deployment in deployments] == ["eu-deployment"] From 709e27feb2347c4345ea97d60440c8532bf119bd Mon Sep 17 00:00:00 2001 From: Zhibo Zhang Date: Mon, 2 Nov 2026 20:01:16 +0800 Subject: [PATCH 3/3] test(router): cover region filtering across async sync and passthrough selectors --- .../test_router_region_no_input.py | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/tests/unit/test_router/test_router_region_no_input.py b/tests/unit/test_router/test_router_region_no_input.py index 2ae769cea81..30503e1e849 100644 --- a/tests/unit/test_router/test_router_region_no_input.py +++ b/tests/unit/test_router/test_router_region_no_input.py @@ -2,24 +2,36 @@ import pytest from litellm import Router -@pytest.mark.asyncio -async def test_allowed_model_region_filters_requests_without_messages_or_input() -> None: - router = Router( +def _router_with_region_deployments(*, pass_through: bool = False) -> Router: + return Router( model_list=[ { "model_name": "region-test", - "litellm_params": {"model": "openai/gpt-4o", "region_name": "eu"}, + "litellm_params": { + "model": "openai/gpt-4o", + "region_name": "eu", + "use_in_pass_through": pass_through, + }, "model_info": {"id": "eu-deployment"}, }, { "model_name": "region-test", - "litellm_params": {"model": "openai/gpt-4o-mini", "region_name": "us"}, + "litellm_params": { + "model": "openai/gpt-4o-mini", + "region_name": "us", + "use_in_pass_through": pass_through, + }, "model_info": {"id": "us-deployment"}, }, ], enable_pre_call_checks=True, ) + +@pytest.mark.asyncio +async def test_allowed_model_region_filters_async_selection_without_messages_or_input() -> None: + router = _router_with_region_deployments() + deployments = await router.select( model="region-test", request_kwargs={"allowed_model_region": "eu"}, @@ -29,3 +41,29 @@ async def test_allowed_model_region_filters_requests_without_messages_or_input() ) assert [deployment["model_info"]["id"] for deployment in deployments] == ["eu-deployment"] + + +def test_allowed_model_region_filters_sync_selection_without_messages_or_input() -> None: + router = _router_with_region_deployments() + + deployment = router.get_available_deployment( + model="region-test", + request_kwargs={"allowed_model_region": "eu"}, + messages=None, + input=None, + ) + + assert deployment["model_info"]["id"] == "eu-deployment" + + +def test_allowed_model_region_filters_passthrough_without_messages_or_input() -> None: + router = _router_with_region_deployments(pass_through=True) + + deployment = router.get_available_deployment_for_pass_through( + model="region-test", + request_kwargs={"allowed_model_region": "eu"}, + messages=None, + input=None, + ) + + assert deployment["model_info"]["id"] == "eu-deployment"