From e60a72ee1de40e97b48354b29d8cca856e810289 Mon Sep 17 00:00:00 2001 From: user <70670632+stuxf@users.noreply.github.com> Date: Fri, 1 May 2026 08:06:10 +0000 Subject: [PATCH] fix(proxy): hardcode mock-testing strip list to avoid cyclic import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeQL flagged the previous ``from litellm.types.router import MockRouterTestingParams`` at module top-level — ``litellm.types.router`` indirectly imports back into proxy modules, so the dataclass may not exist yet when ``route_llm_request`` is being imported. Hardcode the three flag names instead, with a guard test (``test_mock_testing_kwarg_names_matches_dataclass``) that asserts the hardcoded list matches ``MockRouterTestingParams.fields`` so drift is caught at test time rather than missed in production. Co-Authored-By: Claude Opus 4.7 (1M context) --- litellm/proxy/route_llm_request.py | 16 ++++++++++------ .../test_litellm/proxy/test_route_llm_request.py | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/litellm/proxy/route_llm_request.py b/litellm/proxy/route_llm_request.py index f611384b7ae..bfe6b8484fa 100644 --- a/litellm/proxy/route_llm_request.py +++ b/litellm/proxy/route_llm_request.py @@ -1,17 +1,21 @@ import asyncio -from dataclasses import fields as _dc_fields from typing import TYPE_CHECKING, Any, Literal, Optional from fastapi import HTTPException, status import litellm from litellm.proxy._types import UserAPIKeyAuth -from litellm.types.router import MockRouterTestingParams -# Router-internal mock_testing_* flag names. Single source of truth so a -# new flag added to ``MockRouterTestingParams`` is automatically stripped. -_MOCK_TESTING_KWARG_NAMES: tuple = tuple( - f.name for f in _dc_fields(MockRouterTestingParams) +# Router-internal mock_testing_* flag names — kept in sync with +# ``litellm.types.router.MockRouterTestingParams`` by the test +# ``test_mock_testing_kwarg_names_matches_dataclass``. Hardcoding (rather +# than deriving via ``dataclasses.fields(MockRouterTestingParams)`` at +# import time) avoids a cyclic import: ``litellm.types.router`` imports +# back into proxy modules before this module finishes loading. +_MOCK_TESTING_KWARG_NAMES: tuple = ( + "mock_testing_fallbacks", + "mock_testing_context_fallbacks", + "mock_testing_content_policy_fallbacks", ) if TYPE_CHECKING: diff --git a/tests/test_litellm/proxy/test_route_llm_request.py b/tests/test_litellm/proxy/test_route_llm_request.py index 9ef90085e41..98b0b6be025 100644 --- a/tests/test_litellm/proxy/test_route_llm_request.py +++ b/tests/test_litellm/proxy/test_route_llm_request.py @@ -241,6 +241,21 @@ async def test_route_request_with_router_settings_override_preserves_existing(): assert call_kwargs["timeout"] == 30 +def test_mock_testing_kwarg_names_matches_dataclass(): + """``_MOCK_TESTING_KWARG_NAMES`` is hardcoded to avoid a cyclic import + against ``litellm.types.router``. This test guards against drift — + if a new ``mock_testing_*`` field is added to ``MockRouterTestingParams`` + the strip list must be updated to keep covering it.""" + from dataclasses import fields + + from litellm.proxy.route_llm_request import _MOCK_TESTING_KWARG_NAMES + from litellm.types.router import MockRouterTestingParams + + assert set(_MOCK_TESTING_KWARG_NAMES) == { + f.name for f in fields(MockRouterTestingParams) + } + + @pytest.mark.asyncio @pytest.mark.parametrize( "mock_flag",