From 7e6d9e89ed3f16189e42e480de26ffe6281441b3 Mon Sep 17 00:00:00 2001 From: mateo Date: Wed, 23 Sep 2026 01:22:41 +0000 Subject: [PATCH] fix: satisfy stable/1.101.x lint and api-sync gates for the jev backport --- litellm/proxy/auth/auth_checks.py | 4 +- litellm/proxy/auth/team_grants.py | 2 +- .../callback_config_validation.py | 2 +- .../guardrail_hooks/typesafe/__init__.py | 4 +- .../guardrail_hooks/typesafe/typesafe.py | 41 ++-- .../team_callback_endpoints.py | 3 +- .../typesafe_passthrough_logging_handler.py | 2 +- litellm/router_strategy/budget_limiter.py | 6 +- .../complexity_router/complexity_router.py | 16 +- tests/litellm_utils_tests/test_utils.py | 12 +- .../test_model_management_endpoints.py | 211 ++++++++---------- .../test_llm_pass_through_endpoints.py | 52 +++-- .../router_strategy/test_complexity_router.py | 2 +- .../add_model/ClassificationMethodConfig.tsx | 1 - .../edit_auto_router_modal.tsx | 7 +- 15 files changed, 193 insertions(+), 172 deletions(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index d02d37fe3a5..f601e94ed11 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -5229,7 +5229,9 @@ async def _check_team_member_model_access( ): return # no per-member restriction — inherit team-level check - member_allowed_models: Final[list[str]] = loaded_membership.litellm_budget_table.allowed_models + member_allowed_models: Final[list[str]] = ( + loaded_membership.litellm_budget_table.allowed_models + ) # mutable-ok: allowed_models is a prisma model list consumed read-only try: _can_object_call_model( model=model, diff --git a/litellm/proxy/auth/team_grants.py b/litellm/proxy/auth/team_grants.py index 2029ee342ae..f05d7484a27 100644 --- a/litellm/proxy/auth/team_grants.py +++ b/litellm/proxy/auth/team_grants.py @@ -59,7 +59,7 @@ class TeamGrants(TypedDict, total=False): team_tpd_limit: ReadOnly[int | None] team_max_budget: ReadOnly[float | None] team_soft_budget: ReadOnly[float | None] - team_model_max_budget: ReadOnly[dict[str, object] | None] + team_model_max_budget: ReadOnly[dict[str, object] | None] # mutable-ok: prisma JSON column deserialized per-request team_spend: ReadOnly[float | None] team_models: ReadOnly[Sequence[str]] team_blocked: ReadOnly[bool] diff --git a/litellm/proxy/common_utils/callback_config_validation.py b/litellm/proxy/common_utils/callback_config_validation.py index c9d97068313..a2c4fe49339 100644 --- a/litellm/proxy/common_utils/callback_config_validation.py +++ b/litellm/proxy/common_utils/callback_config_validation.py @@ -107,7 +107,7 @@ def cross_entry_family_error( """ if not callback_vars: return None - stored_by_var: Final = { + stored_by_var: Final = { # mutable-ok: config dict is updated in place per callback entry var: value for entry in stored_vars_by_entry for var, value in entry.items() if _family_of(var) is not None } family_values: Final = frozenset( diff --git a/litellm/proxy/guardrails/guardrail_hooks/typesafe/__init__.py b/litellm/proxy/guardrails/guardrail_hooks/typesafe/__init__.py index dcea75d3a98..405edaa7b80 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/typesafe/__init__.py +++ b/litellm/proxy/guardrails/guardrail_hooks/typesafe/__init__.py @@ -20,8 +20,8 @@ if TYPE_CHECKING: def _coerce_event_hook( - mode: str | list[str] | Mode, -) -> GuardrailEventHooks | list[GuardrailEventHooks] | Mode: + mode: str | list[str] | Mode, # mutable-ok: event hook unions accept an ordered list +) -> GuardrailEventHooks | list[GuardrailEventHooks] | Mode: # mutable-ok: event hook unions accept an ordered list if isinstance(mode, Mode): return mode if isinstance(mode, list): diff --git a/litellm/proxy/guardrails/guardrail_hooks/typesafe/typesafe.py b/litellm/proxy/guardrails/guardrail_hooks/typesafe/typesafe.py index 9df5c204a77..7246f6c2d06 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/typesafe/typesafe.py +++ b/litellm/proxy/guardrails/guardrail_hooks/typesafe/typesafe.py @@ -60,14 +60,14 @@ _STR_OBJECT_DICT_ADAPTER: Final = TypeAdapter(dict[str, object]) _OBJECT_LIST_ADAPTER: Final = TypeAdapter(list[object]) -def _as_str_object_dict(value: object) -> dict[str, object] | None: +def _as_str_object_dict(value: object) -> dict[str, object] | None: # mutable-ok: parsed dict feeds the JSON log record try: return _STR_OBJECT_DICT_ADAPTER.validate_python(value) except ValidationError: return None -def _as_object_list(value: object) -> list[object] | None: +def _as_object_list(value: object) -> list[object] | None: # mutable-ok: tool call list is serialized to JSON try: return _OBJECT_LIST_ADAPTER.validate_python(value) except ValidationError: @@ -120,7 +120,7 @@ def _question_instructions(question_id: str) -> str: ) -def _tool_call_entry(tool_call: object) -> dict[str, object] | None: +def _tool_call_entry(tool_call: object) -> dict[str, object] | None: # mutable-ok: entry dict is serialized to JSON parsed_call = _as_str_object_dict(tool_call) if parsed_call is None: return None @@ -129,7 +129,9 @@ def _tool_call_entry(tool_call: object) -> dict[str, object] | None: return {"name": fn.get("name"), "arguments": fn.get("arguments")} # mutable-ok: serialized to JSON -def _tool_call_entries(assistant_message: Mapping[str, object]) -> tuple[dict[str, object], ...]: +def _tool_call_entries( + assistant_message: Mapping[str, object], +) -> tuple[dict[str, object], ...]: # mutable-ok: entry dicts are serialized to JSON tool_calls: Final = _as_object_list(assistant_message.get("tool_calls")) if tool_calls is None: return () @@ -158,7 +160,10 @@ class TypeSafeGuardrail(CustomGuardrail): max_result_chars_in_state: int | None = None, unreachable_fallback: str | None = None, guardrail_name: str | None = None, - event_hook: GuardrailEventHooks | list[GuardrailEventHooks] | Mode | None = None, + event_hook: GuardrailEventHooks + | list[GuardrailEventHooks] + | Mode + | None = None, # mutable-ok: event hook unions accept an ordered list default_on: bool = False, async_handler: AsyncHTTPHandler | None = None, ) -> None: @@ -190,7 +195,7 @@ class TypeSafeGuardrail(CustomGuardrail): default_on=default_on, ) - def _handle_failure(self, error: str, log_detail: dict[str, object]) -> None: + def _handle_failure(self, error: str, log_detail: dict[str, object]) -> None: # mutable-ok: log detail record """fail_open logs and returns; fail_closed raises a generic 502 (upstream bodies stay in server logs).""" if self.unreachable_fallback == "fail_open": verbose_proxy_logger.warning( @@ -202,7 +207,9 @@ class TypeSafeGuardrail(CustomGuardrail): verbose_proxy_logger.error("TypeSafe: %s. detail=%s", error, log_detail) raise HTTPException(status_code=502, detail={"error": error}) # mutable-ok: FastAPI wants a dict detail - def _candidate_exchanges(self, messages: Sequence[dict[str, object]]) -> tuple[tuple[int, ...], ...]: + def _candidate_exchanges( + self, messages: Sequence[dict[str, object]] + ) -> tuple[tuple[int, ...], ...]: # mutable-ok: message dicts come from the request payload """Completed tool exchanges eligible for evaluation: unprotected, and long enough to be worth a call.""" protected: Final = _protected_indices(messages) candidates: Final = tuple( @@ -216,7 +223,9 @@ class TypeSafeGuardrail(CustomGuardrail): return candidates[-_MAX_EXCHANGES_EVALUATED:] @staticmethod - def _exchange_tool_text(messages: Sequence[dict[str, object]], group: tuple[int, ...]) -> str: + def _exchange_tool_text( + messages: Sequence[dict[str, object]], group: tuple[int, ...] + ) -> str: # mutable-ok: message dicts come from the request payload return "".join( content_to_text(messages[index].get("content")) for index in group[1:] @@ -224,8 +233,10 @@ class TypeSafeGuardrail(CustomGuardrail): ) def _build_state( - self, messages: Sequence[dict[str, object]], candidates: tuple[tuple[int, ...], ...] - ) -> dict[str, object]: + self, + messages: Sequence[dict[str, object]], + candidates: tuple[tuple[int, ...], ...], # mutable-ok: candidate groups index request message dicts + ) -> dict[str, object]: # mutable-ok: result dict feeds the JSON log record task: Final = next( ( content_to_text(messages[index].get("content")) @@ -249,7 +260,9 @@ class TypeSafeGuardrail(CustomGuardrail): return {"task": task, "system": system, "tool_exchanges": tool_exchanges} # mutable-ok: serialized to JSON async def _call_systemone( - self, state: dict[str, object], question_ids: Sequence[str] + self, + state: dict[str, object], + question_ids: Sequence[str], # mutable-ok: state dict is the parsed log record ) -> _JevSystemOneResponse | None: """Returns the response, or None when the service failed and fail_open applies.""" payload: Final[dict[str, object]] = { # mutable-ok: serialized to JSON by httpx @@ -275,8 +288,8 @@ class TypeSafeGuardrail(CustomGuardrail): ) except asyncio.CancelledError: raise - except Exception as e: - detail: Final[dict[str, object]] = ( + except Exception as e: # noqa: BLE001 # a logging-path failure must never break the guardrail call + detail: Final[dict[str, object]] = ( # mutable-ok: log detail record { # mutable-ok: log detail record "error_type": type(e).__name__, "detail": str(e), @@ -318,7 +331,7 @@ class TypeSafeGuardrail(CustomGuardrail): async def apply_guardrail( self, inputs: GenericGuardrailAPIInputs, - request_data: dict[str, object], + request_data: dict[str, object], # mutable-ok: request-shaped dict input_type: Literal["request", "response"], logging_obj: LiteLLMLoggingObj | None = None, ) -> GenericGuardrailAPIInputs: diff --git a/litellm/proxy/management_endpoints/team_callback_endpoints.py b/litellm/proxy/management_endpoints/team_callback_endpoints.py index 1932e89717b..64b032d2f18 100644 --- a/litellm/proxy/management_endpoints/team_callback_endpoints.py +++ b/litellm/proxy/management_endpoints/team_callback_endpoints.py @@ -356,7 +356,8 @@ async def add_team_callbacks( decrypted_logging: Final = decrypt_callback_vars(team_metadata).get("logging") stored_entries: Final = decrypted_logging if isinstance(decrypted_logging, list) else () stored_entry_vars: Final = [ # mutable-ok: read-only input to the check, never stored - entry.get("callback_vars") or {} for entry in stored_entries + entry.get("callback_vars") or {} + for entry in stored_entries # mutable-ok: callback settings dict is assembled from the request body ] family_error: Final = cross_entry_family_error(data.callback_vars, stored_entry_vars) if family_error is not None: diff --git a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/typesafe_passthrough_logging_handler.py b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/typesafe_passthrough_logging_handler.py index 887d17a7a20..a9cb765e1ce 100644 --- a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/typesafe_passthrough_logging_handler.py +++ b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/typesafe_passthrough_logging_handler.py @@ -66,7 +66,7 @@ class TypeSafePassthroughLoggingHandler: cache_hit: bool, request_body: Mapping[str, object], custom_llm_provider: str, - **kwargs: object, + **kwargs: object, # kwargs-ok: logging handler receives the standard logging kwargs ) -> PassThroughEndpointLoggingTypedDict: response: Final = _parse_typesafe_response(response_body) response_model: Final = response.model diff --git a/litellm/router_strategy/budget_limiter.py b/litellm/router_strategy/budget_limiter.py index 3e094df7ac8..57e8ddc1c33 100644 --- a/litellm/router_strategy/budget_limiter.py +++ b/litellm/router_strategy/budget_limiter.py @@ -93,10 +93,12 @@ class _LiteLLMParamsDictView: return dict(self._params) -async def _push_increments_to_redis(redis_cache: RedisCache, queued: list[RedisPipelineIncrementOperation]) -> None: +async def _push_increments_to_redis( + redis_cache: RedisCache, queued: list[RedisPipelineIncrementOperation] +) -> None: # mutable-ok: pipeline ops list is built once and consumed by redis try: await redis_cache.async_increment_pipeline(increment_list=queued) - except Exception as e: + except Exception as e: # noqa: BLE001 # a budget push failure must not abort the pipeline log_redis_failure(verbose_router_logger, logging.ERROR, "Error syncing in-memory cache with Redis", e) diff --git a/litellm/router_strategy/complexity_router/complexity_router.py b/litellm/router_strategy/complexity_router/complexity_router.py index 4091cd0feb5..65ee3067664 100644 --- a/litellm/router_strategy/complexity_router/complexity_router.py +++ b/litellm/router_strategy/complexity_router/complexity_router.py @@ -497,7 +497,7 @@ def _human_text(content: object, marker_pairs: tuple[tuple[str, str], ...] = _DE def _encrypted_classifier_task( request_kwargs: Mapping[str, object] | None, marker_pairs: tuple[tuple[str, str], ...], -) -> dict[str, object] | None: +) -> dict[str, object] | None: # mutable-ok: task dict mirrors the request payload shape from litellm.litellm_core_utils.prompt_templates.factory import resolve_structured_messages raw_input: Final = (request_kwargs or EMPTY_MAPPING).get("input") @@ -511,7 +511,9 @@ def _encrypted_classifier_task( ( item for item in reversed(items) - if (messages := resolve_structured_messages(messages=None, request_kwargs={"input": [item]})) + if ( + messages := resolve_structured_messages(messages=None, request_kwargs={"input": [item]}) + ) # mutable-ok: request-shaped literal and any(_iter_human_asks_newest_first(messages, marker_pairs)) ), None, @@ -524,9 +526,11 @@ def _encrypted_classifier_task( return None if not any(part.get("type") == "encrypted_content" and part.get("encrypted_content") for part in parts): return None - return { + return { # mutable-ok: request-shaped dict **current, - "content": [part for part in parts if part.get("type") in ("input_text", "encrypted_content")], + "content": [ + part for part in parts if part.get("type") in ("input_text", "encrypted_content") + ], # mutable-ok: request-shaped dict } @@ -2133,7 +2137,9 @@ class ComplexityRouter(CustomLogger): if llm_config is None or classifier_system_prompt is None or classifier_response_format is None: raise ValueError("classifier_llm_config is not set") - marker_pairs: Final = self._reminder_markers_for_request(request_kwargs or {}) + marker_pairs: Final = self._reminder_markers_for_request( + request_kwargs or {} + ) # mutable-ok: empty kwargs fallback encrypted_task: Final = _encrypted_classifier_task(request_kwargs, marker_pairs) user_payload: Final = self._classifier_context_payload( prompt, system_prompt, request_kwargs, messages, encrypted_task=encrypted_task is not None diff --git a/tests/litellm_utils_tests/test_utils.py b/tests/litellm_utils_tests/test_utils.py index 1c5e399e601..a446d955ecb 100644 --- a/tests/litellm_utils_tests/test_utils.py +++ b/tests/litellm_utils_tests/test_utils.py @@ -1102,7 +1102,9 @@ def test_async_http_handler(mock_async_client): concurrent_limit = 2 # Mock the transport creation to return a specific transport - with mock.patch.object(AsyncHTTPHandler, "_create_async_transport") as mock_create_transport: + with mock.patch.object( + AsyncHTTPHandler, "_create_async_transport" + ) as mock_create_transport: # test-quality-ok: the patched global is the collaborator's only injection seam mock_transport = mock.MagicMock() mock_create_transport.return_value = mock_transport @@ -1548,7 +1550,9 @@ def test_get_valid_models_openai_proxy(monkeypatch): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data - with patch.object(litellm.module_level_client, "get", return_value=mock_response) as mock_post: + with patch.object( + litellm.module_level_client, "get", return_value=mock_response + ) as mock_post: # test-quality-ok: the patched global is the collaborator's only injection seam valid_models = get_valid_models(check_provider_endpoint=True) assert "litellm_proxy/gpt-5.5" in valid_models @@ -1625,7 +1629,9 @@ def test_get_valid_models_fireworks_ai(monkeypatch): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data - with patch.object(litellm.module_level_client, "get", return_value=mock_response) as mock_post: + with patch.object( + litellm.module_level_client, "get", return_value=mock_response + ) as mock_post: # test-quality-ok: the patched global is the collaborator's only injection seam valid_models = get_valid_models(check_provider_endpoint=True) print("valid_models", valid_models) mock_post.assert_called_once() diff --git a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py index bc8994ef210..6b4a4813540 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py @@ -308,22 +308,21 @@ class TestModelManagementAuthChecks: @pytest.mark.asyncio async def test_add_new_model_rejects_credential_attach_for_non_admin(self): - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( add_new_model, ) mock_prisma = MagicMock() with ( - patch( + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.prisma_client", mock_prisma - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam + ), patch( # test-quality-ok: prior auth check needs a live DB; only the credential check is under test "litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call", new=AsyncMock(return_value=None), @@ -345,7 +344,6 @@ class TestModelManagementAuthChecks: @pytest.mark.asyncio async def test_patch_model_rejects_credential_attach_for_non_admin(self): - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( patch_model, ) @@ -358,18 +356,18 @@ class TestModelManagementAuthChecks: model_info={"id": model_id}, ) with ( - patch( + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.prisma_client", MagicMock() - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.llm_router", MagicMock() - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam + ), patch( # test-quality-ok: stubs the DB row fetch; only the credential check is under test "litellm.proxy.management_endpoints.model_management_endpoints.get_db_model", new=AsyncMock(return_value=db_model), @@ -990,9 +988,6 @@ class TestUpdateModel: model-level guardrails (and any other litellm_params change) silently no-op until the APScheduler reload tick fires ~30 s later. """ - from litellm.proxy.management_endpoints.model_management_endpoints import ( - update_model, - ) from litellm.types.router import ( ModelInfo, updateDeployment, @@ -1354,7 +1349,9 @@ class TestTeamModelUpdate: patch( "litellm.proxy.management_endpoints.model_management_endpoints.team_model_add" ) as mock_team_model_add, - patch("litellm.proxy.management_endpoints.model_management_endpoints.update_team") as mock_update_team, + patch( + "litellm.proxy.management_endpoints.model_management_endpoints.update_team" + ) as mock_update_team, # test-quality-ok: the patched global is the collaborator's only injection seam ): result = await _update_team_model_in_db( db_model=db_model, @@ -1409,7 +1406,9 @@ class TestTeamModelUpdate: ) with ( - patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete") as mock_delete, + patch( + "litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete" + ) as mock_delete, # test-quality-ok: the patched global is the collaborator's only injection seam patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_add") as mock_add, ): await _update_existing_team_model_assignment( @@ -1450,7 +1449,9 @@ class TestTeamModelUpdate: ) with ( - patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete") as mock_delete, + patch( + "litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete" + ) as mock_delete, # test-quality-ok: the patched global is the collaborator's only injection seam patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_add") as mock_add, ): await _update_existing_team_model_assignment( @@ -1505,9 +1506,9 @@ class TestTeamModelUpdate: "litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.allow_team_model_action", AsyncMock(return_value=True), ), - patch( + patch( # test-quality-ok: team models are premium-gated through a proxy global with no injection seam "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: team models are premium-gated through a proxy global with no injection seam + ), patch( # test-quality-ok: the team list write is the collaborator whose ordering is asserted "litellm.proxy.management_endpoints.model_management_endpoints.team_model_add", side_effect=team_add, @@ -1567,7 +1568,9 @@ class TestTeamModelUpdate: ) with ( - patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete") as mock_delete, + patch( + "litellm.proxy.management_endpoints.model_management_endpoints.team_model_delete" + ) as mock_delete, # test-quality-ok: the patched global is the collaborator's only injection seam patch("litellm.proxy.management_endpoints.model_management_endpoints.team_model_add") as mock_add, ): await _update_existing_team_model_assignment( @@ -3248,9 +3251,6 @@ class TestPatchModelBlockedAuthGate: @pytest.mark.asyncio async def test_team_admin_cannot_toggle_blocked(self): - from litellm.proxy.management_endpoints.model_management_endpoints import ( - patch_model, - ) non_admin = UserAPIKeyAuth( user_id="team_admin", @@ -3290,9 +3290,6 @@ class TestPatchModelBlockedAuthGate: @pytest.mark.asyncio async def test_proxy_admin_can_toggle_blocked(self): - from litellm.proxy.management_endpoints.model_management_endpoints import ( - patch_model, - ) admin = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) existing_row = MagicMock() @@ -3340,9 +3337,6 @@ class TestPatchModelRowDeletedBeforeWrite: @pytest.mark.asyncio async def test_patch_model_404s_when_update_returns_none(self): - from litellm.proxy.management_endpoints.model_management_endpoints import ( - patch_model, - ) from litellm.proxy.proxy_server import ProxyException admin = UserAPIKeyAuth(user_id="admin", user_role=LitellmUserRoles.PROXY_ADMIN) @@ -3360,18 +3354,18 @@ class TestPatchModelRowDeletedBeforeWrite: mock_prisma.db.litellm_proxymodeltable.update = AsyncMock(return_value=None) with ( - patch( + patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.prisma_client", mock_prisma - ), # test-quality-ok: proxy_server module global is the endpoint's only injection point - patch( + ), + patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.llm_router", MagicMock(**{"get_model_ids.return_value": ["m1"]}) - ), # test-quality-ok: proxy_server module global is the endpoint's only injection point - patch( + ), + patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: proxy_server module global is the endpoint's only injection point - patch( + ), + patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: proxy_server module global is the endpoint's only injection point + ), patch( # test-quality-ok: stubs the auth gate so the test exercises the not-found branch under test "litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call", new=AsyncMock(return_value=None), @@ -3438,7 +3432,6 @@ class TestWriteSurfacesReloadDrop: def test_raise_if_reload_degraded_serving_contract(self, monkeypatch): import litellm - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( raise_if_reload_degraded_serving, ) @@ -3484,7 +3477,6 @@ class TestWriteSurfacesReloadDrop: all the desired set is unknown, so every drop is reported. """ import litellm - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( raise_if_reload_degraded_serving, reload_serving_verdict, @@ -3630,7 +3622,6 @@ class TestConcurrentModelWritesDoNotEvictEachOther: request's reload for it is the 500 that made concurrent model creates fail. """ import litellm - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( raise_if_reload_degraded_serving, reload_serving_verdict, @@ -4121,7 +4112,6 @@ class TestStrategyRouterWriteValidation: @pytest.mark.asyncio async def test_patch_model_rejects_double_prefix(self): - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( patch_model, ) @@ -4161,7 +4151,6 @@ class TestStrategyRouterWriteValidation: @pytest.mark.asyncio async def test_add_new_model_rejects_prefixed_model_without_config(self): - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( add_new_model, ) @@ -4526,12 +4515,12 @@ class TestStrategyRouterWriteValidation: self._live_router_holding_one_capability(limit, config_config) if config_config is not None else None ) with ( - patch( + patch( # test-quality-ok: the guard reads the proxy license singleton with no injection seam "litellm.proxy.proxy_server._license_check.auto_router_capability_limit", lambda: limit - ), # test-quality-ok: the guard reads the proxy license singleton with no injection seam - patch( + ), + patch( # test-quality-ok: the guard reads the proxy router global with no injection seam "litellm.proxy.proxy_server.llm_router", live_router - ), # test-quality-ok: the guard reads the proxy router global with no injection seam + ), patch( # test-quality-ok: the cross-pod publish is the side effect under test; redis is not configured here "litellm.proxy.management_endpoints.model_management_endpoints.publish_config_change", new=AsyncMock(), @@ -4615,7 +4604,6 @@ class TestStrategyRouterWriteValidation: @pytest.mark.asyncio async def test_add_new_model_refuses_a_second_heuristic_v2_router_before_the_db_write(self) -> None: - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( add_new_model, ) @@ -4624,21 +4612,21 @@ class TestStrategyRouterWriteValidation: fake = self._FakeDb(["auto_router/complexity_router"]) with ( - patch( + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.prisma_client", fake - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.llm_router", None - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server._license_check.auto_router_capability_limit", lambda: 1 - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam + ), patch( # test-quality-ok: prior auth check needs a live DB; only the license limit is under test "litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call", new=AsyncMock(return_value=None), @@ -4666,7 +4654,6 @@ class TestStrategyRouterWriteValidation: @pytest.mark.asyncio async def test_model_less_patch_rejects_router_config_on_a_regular_model(self) -> None: """PATCH rejects the poison before its row write or the capability slot.""" - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import patch_model from litellm.types.router import updateLiteLLMParams @@ -4679,18 +4666,18 @@ class TestStrategyRouterWriteValidation: ) fake = self._FakeDb([]) with ( - patch( + patch( # test-quality-ok: endpoint reads proxy globals with no injection seam "litellm.proxy.proxy_server.prisma_client", fake - ), # test-quality-ok: endpoint reads proxy globals with no injection seam - patch( + ), + patch( # test-quality-ok: config-based lookup must be absent to drive the stored-row branch "litellm.proxy.proxy_server.llm_router", None - ), # test-quality-ok: config-based lookup must be absent to drive the stored-row branch - patch( + ), + patch( # test-quality-ok: endpoint reaches its DB-write branch only with this process setting "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: endpoint reaches its DB-write branch only with this process setting - patch( + ), + patch( # test-quality-ok: authorization branch reads the proxy-wide premium flag "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: authorization branch reads the proxy-wide premium flag + ), patch( # test-quality-ok: inject stored regular row without a database "litellm.proxy.management_endpoints.model_management_endpoints.get_db_model", new=AsyncMock(return_value=regular), @@ -4718,7 +4705,6 @@ class TestStrategyRouterWriteValidation: @pytest.mark.asyncio async def test_model_less_legacy_update_rejects_router_config_on_a_regular_model(self) -> None: """The legacy update endpoint enforces the same boundary before its row write or slot.""" - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import update_model from litellm.types.router import ModelInfo, updateLiteLLMParams @@ -4734,18 +4720,18 @@ class TestStrategyRouterWriteValidation: existing_row.litellm_params = regular.litellm_params.model_dump() fake = self._FakeDb([], existing_row=existing_row) with ( - patch( + patch( # test-quality-ok: endpoint reads proxy globals with no injection seam "litellm.proxy.proxy_server.prisma_client", fake - ), # test-quality-ok: endpoint reads proxy globals with no injection seam - patch( + ), + patch( # test-quality-ok: config-based lookup must be absent to drive the stored-row branch "litellm.proxy.proxy_server.llm_router", None - ), # test-quality-ok: config-based lookup must be absent to drive the stored-row branch - patch( + ), + patch( # test-quality-ok: endpoint reaches its DB-write branch only with this process setting "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: endpoint reaches its DB-write branch only with this process setting - patch( + ), + patch( # test-quality-ok: authorization branch reads the proxy-wide premium flag "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: authorization branch reads the proxy-wide premium flag + ), patch( # test-quality-ok: endpoint must reject before database authorization needs a live store "litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call", new=AsyncMock(return_value=None), @@ -4771,9 +4757,6 @@ class TestStrategyRouterWriteValidation: """patch_model relays HTTPException as-is, so the license refusal reaches the client as a plain 403.""" from fastapi import HTTPException - from litellm.proxy.management_endpoints.model_management_endpoints import ( - patch_model, - ) from litellm.types.router import updateLiteLLMParams model_id = "other-id" @@ -4781,21 +4764,21 @@ class TestStrategyRouterWriteValidation: fake = self._FakeDb(["auto_router/complexity_router"]) with ( - patch( + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.prisma_client", fake - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.llm_router", None - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server._license_check.auto_router_capability_limit", lambda: 1 - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam + ), patch( # test-quality-ok: the write must be refused before this DB step runs "litellm.proxy.management_endpoints.model_management_endpoints.get_db_model", new=AsyncMock(return_value=self._db_complexity_router(model_id)), @@ -4821,7 +4804,6 @@ class TestStrategyRouterWriteValidation: @pytest.mark.asyncio async def test_update_model_refuses_switching_another_router_to_heuristic_v2(self) -> None: - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( update_model, ) @@ -4843,21 +4825,21 @@ class TestStrategyRouterWriteValidation: fake = self._FakeDb(["auto_router/complexity_router"], existing_row=existing_row) with ( - patch( + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.prisma_client", fake - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.llm_router", None - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server.premium_user", True - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam - patch( + ), + patch( # test-quality-ok: endpoint reads proxy server globals with no injection seam "litellm.proxy.proxy_server._license_check.auto_router_capability_limit", lambda: 1 - ), # test-quality-ok: endpoint reads proxy server globals with no injection seam + ), patch( # test-quality-ok: prior auth check needs a live DB; only the license limit is under test "litellm.proxy.management_endpoints.model_management_endpoints.ModelManagementAuthChecks.can_user_make_model_call", new=AsyncMock(return_value=None), @@ -4877,7 +4859,6 @@ class TestStrategyRouterWriteValidation: @pytest.mark.asyncio async def test_update_model_rejects_prefix_strip(self): - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( update_model, ) @@ -4980,7 +4961,6 @@ class TestAutoRouterClassifierDefaultPrompt: @pytest.mark.asyncio async def test_negative_context_window_size_is_rejected(self): - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( get_auto_router_classifier_default_prompt, ) @@ -5124,7 +5104,6 @@ class TestAutoRouterClassifierDefaultPrompt: ], ) def test_built_in_preview_rejects_the_same_invalid_labels_as_get(self, tier_labels): - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( AutoRouterClassifierPromptPreviewRequest, preview_auto_router_classifier_prompt, @@ -5230,7 +5209,6 @@ class TestAutoRouterClassifierDefaultPrompt: async def test_malformed_tier_labels_are_rejected_rather_than_silently_ignored(self): """An unparseable or invalid rename must not fall back to the canonical classification_rubric: that would prefill tier names the router does not accept while looking like it worked.""" - from litellm.proxy._types import ProxyException from litellm.proxy.management_endpoints.model_management_endpoints import ( get_auto_router_classifier_default_prompt, ) @@ -5282,7 +5260,6 @@ class TestEnforceRpmTpmOnModelAdd: ], ) def test_raises_when_enabled_and_missing(self, params, expected_missing): - from litellm.proxy._types import ProxyException with pytest.raises(ProxyException) as exc_info: _raise_if_rate_limits_required_but_missing(litellm_params=params, enforced=True) @@ -5322,19 +5299,19 @@ class TestBlockModelResponseSerialization: app.dependency_overrides[ps.user_api_key_auth] = lambda: admin try: with ( - patch( + patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.prisma_client", mock_prisma - ), # test-quality-ok: proxy_server module global is the endpoint's only injection point - patch( + ), + patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.store_model_in_db", True - ), # test-quality-ok: proxy_server module global is the endpoint's only injection point + ), patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.llm_router", MagicMock(**{"get_model_ids.return_value": ["m-block-1"]}), ), - patch( + patch( # test-quality-ok: proxy_server module global is the endpoint's only injection point "litellm.proxy.proxy_server.redis_usage_cache", None - ), # test-quality-ok: proxy_server module global is the endpoint's only injection point + ), patch( # test-quality-ok: stubs the cache write so the test observes only response serialization "litellm.proxy.management_endpoints.model_management_endpoints.clear_cache", new=AsyncMock(return_value=ReconcileOutcome(still_desired=None, live_after=None)), @@ -5424,7 +5401,6 @@ class TestAccessGroupModelSync: @pytest.mark.asyncio async def test_patch_model_rename_rewrites_the_groups_that_named_the_model(self): - from litellm.proxy.management_endpoints.model_management_endpoints import patch_model mock_prisma = self._prisma_with_row("m-rename", "gpt-5.6", deployment_count=0) router = MagicMock() @@ -5446,7 +5422,6 @@ class TestAccessGroupModelSync: @pytest.mark.asyncio async def test_patch_model_rename_appends_when_a_sibling_deployment_keeps_the_old_name(self): - from litellm.proxy.management_endpoints.model_management_endpoints import patch_model mock_prisma = self._prisma_with_row("m-rename", "gpt-5.6", deployment_count=1) router = MagicMock() @@ -5465,7 +5440,6 @@ class TestAccessGroupModelSync: @pytest.mark.asyncio async def test_patch_model_without_a_rename_leaves_access_groups_alone(self): - from litellm.proxy.management_endpoints.model_management_endpoints import patch_model mock_prisma = self._prisma_with_row("m-same", "gpt-5.6", deployment_count=0) router = MagicMock() @@ -5511,7 +5485,6 @@ class TestAccessGroupModelSync: @pytest.mark.asyncio async def test_update_model_persists_a_new_model_name_and_rewrites_the_groups(self): - from litellm.proxy.management_endpoints.model_management_endpoints import update_model from litellm.types.router import ModelInfo, updateLiteLLMParams mock_prisma = self._prisma_with_row("m-terraform", "gpt-5.6", deployment_count=0) diff --git a/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py b/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py index be798adfcd3..5b0e0649cdd 100644 --- a/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py +++ b/tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py @@ -274,7 +274,9 @@ class TestVertexAIPassThroughHandler: test_token = vertex_credentials with ( - mock.patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth") as mock_load_auth, + mock.patch( + "litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth" + ) as mock_load_auth, # test-quality-ok: the patched global is the collaborator's only injection seam mock.patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route" ) as mock_create_route, @@ -375,7 +377,9 @@ class TestVertexAIPassThroughHandler: test_token = vertex_credentials with ( - mock.patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth") as mock_load_auth, + mock.patch( + "litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth" + ) as mock_load_auth, # test-quality-ok: the patched global is the collaborator's only injection seam mock.patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route" ) as mock_create_route, @@ -473,7 +477,9 @@ class TestVertexAIPassThroughHandler: mock_response = Response() with ( - mock.patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth") as mock_load_auth, + mock.patch( + "litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth" + ) as mock_load_auth, # test-quality-ok: the patched global is the collaborator's only injection seam mock.patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route" ) as mock_create_route, @@ -615,7 +621,9 @@ class TestVertexAIPassThroughHandler: mock_request.method = "POST" mock_response = Mock() - with patch("litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.user_api_key_auth") as mock_auth: + with patch( + "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.user_api_key_auth" + ) as mock_auth: # test-quality-ok: the patched global is the collaborator's only injection seam mock_auth.return_value = {"api_key": "test-key-123"} with patch( @@ -1196,7 +1204,9 @@ class TestVertexAIDiscoveryPassThroughHandler: test_token = "test-auth-token" with ( - mock.patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth") as mock_load_auth, + mock.patch( + "litellm.llms.vertex_ai.vertex_llm_base.VertexBase.load_auth" + ) as mock_load_auth, # test-quality-ok: the patched global is the collaborator's only injection seam mock.patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route" ) as mock_create_route, @@ -1257,7 +1267,9 @@ class TestVertexAIDiscoveryPassThroughHandler: mock_request.method = "POST" mock_response = Mock() - with patch("litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.user_api_key_auth") as mock_auth: + with patch( + "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.user_api_key_auth" + ) as mock_auth: # test-quality-ok: the patched global is the collaborator's only injection seam mock_auth.return_value = {"api_key": "test-key-123"} with patch( @@ -1851,7 +1863,9 @@ class TestLLMPassthroughFactoryProxyRoute: mock_user_api_key_dict = MagicMock() with ( - patch("litellm.utils.ProviderConfigManager.get_provider_model_info") as mock_get_provider, + patch( + "litellm.utils.ProviderConfigManager.get_provider_model_info" + ) as mock_get_provider, # test-quality-ok: the patched global is the collaborator's only injection seam patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.passthrough_endpoint_router.get_credentials" ) as mock_get_creds, @@ -1898,9 +1912,9 @@ class TestVLLMProxyRoute: "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.is_passthrough_request_using_router_model", return_value=True, ) - @patch( + @patch( # test-quality-ok: patching litellm internal for unit test isolation "litellm.proxy.proxy_server.llm_router" - ) # test-quality-ok: patching litellm internal for unit test isolation + ) async def test_vllm_proxy_route_with_router_model(self, mock_llm_router, mock_is_router, mock_get_body): mock_request = MagicMock(spec=Request) mock_request.method = "POST" @@ -1961,9 +1975,9 @@ class TestGigachatProxyRoute: "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.is_passthrough_request_using_router_model", return_value=True, ) - @patch( + @patch( # test-quality-ok: patching litellm internal for unit test isolation "litellm.proxy.proxy_server.llm_router" - ) # test-quality-ok: patching litellm internal for unit test isolation + ) async def test_gigachat_proxy_route_with_router_model(self, mock_llm_router, mock_is_router, mock_get_body): mock_request = MagicMock(spec=Request) mock_request.method = "POST" @@ -2498,7 +2512,9 @@ class TestForwardHeaders: mock_httpx_response.aread = AsyncMock(return_value=b'{"result": "success"}') with ( - patch("litellm.utils.ProviderConfigManager.get_provider_model_info") as mock_get_provider, + patch( + "litellm.utils.ProviderConfigManager.get_provider_model_info" + ) as mock_get_provider, # test-quality-ok: the patched global is the collaborator's only injection seam patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.passthrough_endpoint_router.get_credentials" ) as mock_get_creds, @@ -2821,7 +2837,9 @@ class TestMilvusProxyRoute: patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.is_allowed_to_call_vector_store_endpoint" ), - patch("litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints._safe_set_request_parsed_body"), + patch( + "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints._safe_set_request_parsed_body" + ), # test-quality-ok: the patched global is the collaborator's only injection seam patch.object(litellm, "vector_store_index_registry") as mock_index_registry, patch.object(litellm, "vector_store_registry") as mock_vector_registry, ): @@ -2876,7 +2894,9 @@ class TestMilvusProxyRoute: patch.object(litellm, "vector_store_registry") as mock_vector_registry, ): mock_provider_config = MagicMock() - mock_provider_config.get_auth_credentials.return_value = {"headers": {}} + mock_provider_config.get_auth_credentials.return_value = { + "headers": {} + } # test-quality-ok: the patched global is the collaborator's only injection seam mock_provider_config.get_complete_url.return_value = None mock_get_config.return_value = mock_provider_config @@ -2926,7 +2946,9 @@ class TestMilvusProxyRoute: patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.is_allowed_to_call_vector_store_endpoint" ), - patch("litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints._safe_set_request_parsed_body"), + patch( + "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints._safe_set_request_parsed_body" + ), # test-quality-ok: the patched global is the collaborator's only injection seam patch( "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.create_pass_through_route" ) as mock_create_route, diff --git a/tests/test_litellm/router_strategy/test_complexity_router.py b/tests/test_litellm/router_strategy/test_complexity_router.py index 20bae8cc830..bbff78e6e2c 100644 --- a/tests/test_litellm/router_strategy/test_complexity_router.py +++ b/tests/test_litellm/router_strategy/test_complexity_router.py @@ -8,7 +8,7 @@ import asyncio import logging import sys from collections.abc import Mapping -from typing import Dict, List +from typing import Dict, Final, List from unittest.mock import AsyncMock, MagicMock, patch import pytest diff --git a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx index 7484e647b33..9fefc627494 100644 --- a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx @@ -269,7 +269,6 @@ const ClassificationMethodConfig: React.FC = ({ const explicitlySupportedClassifierEfforts = effortOptionsByModel[classifierModel]; const handleClassifierTypeChange = (classifierType: ClassifierType) => { - onChange(transitionClassifierType(value, classifierType)); }; diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx index d31a9f4c51c..ba681491a9a 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx +++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx @@ -140,8 +140,7 @@ export interface StoredComplexityRouterConfig { */ const asNumber = (v: unknown): number | undefined => (typeof v === "number" ? v : undefined); const asBoolean = (v: unknown): boolean | undefined => (typeof v === "boolean" ? v : undefined); -const asNonEmptyString = (v: unknown): string | undefined => - typeof v === "string" && v.trim() !== "" ? v : undefined; +const asNonEmptyString = (v: unknown): string | undefined => (typeof v === "string" && v.trim() !== "" ? v : undefined); export const hydrateComplexityRouterConfig = ( parsedConfig: StoredComplexityRouterConfig, @@ -173,9 +172,7 @@ export const hydrateComplexityRouterConfig = ( classifier_context_window_size: asNumber(parsedConfig.classifier_context_window_size), classifier_context_budget_chars: asNumber(parsedConfig.classifier_context_budget_chars), classifier_context_per_turn_chars: asNumber(parsedConfig.classifier_context_per_turn_chars), - classifier_context_include_assistant_turns: asBoolean( - parsedConfig.classifier_context_include_assistant_turns, - ), + classifier_context_include_assistant_turns: asBoolean(parsedConfig.classifier_context_include_assistant_turns), classifier_fallback: parsedConfig.classifier_fallback === "default_model" || parsedConfig.classifier_fallback === "heuristic" ? parsedConfig.classifier_fallback