From ba67844343ca6eeacb0846e9f6ea54ec7a16ead5 Mon Sep 17 00:00:00 2001 From: user <70670632+stuxf@users.noreply.github.com> Date: Fri, 22 May 2026 04:05:13 +0000 Subject: [PATCH] chore(proxy): make get_request_route imports lazy at call sites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the ``from litellm.proxy.auth.auth_utils import get_request_route`` imports added in the prior commit back to the function bodies that use them. The module-level form participates in a long-standing import cycle through ``auth_utils -> _types -> ...`` and was flagged by CodeQL on the PR; the lazy form matches the pattern the proxy already uses for ``user_api_key_auth`` and related helpers elsewhere in these files. Also drop the ``RouteChecks._is_assistants_api_request`` delegation in ``_get_metadata_variable_name`` introduced in the prior commit — the delegation pulled ``RouteChecks`` into the same cycle, and the call site reuses the resolved route for its other branches, so inlining the substring check is both cycle-free and avoids a redundant second ``get_request_route`` call. Comment in test_proxy_routes.py acknowledges that the two MCP table entries exercise ``get_request_route`` directly rather than the full production handler (which needs ASGI scope + MCP state to invoke). --- .../mcp_server/auth/user_api_key_auth_mcp.py | 6 +++++- litellm/proxy/auth/route_checks.py | 4 +++- litellm/proxy/common_utils/http_parsing_utils.py | 4 +++- litellm/proxy/health_endpoints/_health_endpoints.py | 4 +++- litellm/proxy/litellm_pre_call_utils.py | 9 +++++---- .../management_endpoints/mcp_management_endpoints.py | 4 +++- litellm/proxy/management_helpers/utils.py | 11 ++++++++++- .../pass_through_endpoints/pass_through_endpoints.py | 4 +++- .../spend_tracking/spend_management_endpoints.py | 4 +++- litellm/proxy/vector_store_endpoints/utils.py | 7 ++++++- tests/proxy_unit_tests/test_proxy_routes.py | 7 +++++++ 11 files changed, 51 insertions(+), 13 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py index ddf97b1986c..70fc2c233e7 100644 --- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py +++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py @@ -13,7 +13,6 @@ from litellm.proxy._types import ( SpecialHeaders, UserAPIKeyAuth, ) -from litellm.proxy.auth.auth_utils import get_request_route from litellm.proxy.auth.user_api_key_auth import user_api_key_auth @@ -119,6 +118,11 @@ class MCPRequestHandler: return b"{}" request.body = mock_body # type: ignore + # Inline import — auth_utils participates in a proxy import cycle. + from litellm.proxy.auth.auth_utils import ( # noqa: PLC0415 + get_request_route, + ) + request_route = get_request_route(request) # Only OAuth metadata routes registered under /.well-known/ are public. if request_route.startswith("/.well-known/"): diff --git a/litellm/proxy/auth/route_checks.py b/litellm/proxy/auth/route_checks.py index c4f1af1f267..3144c5cd25b 100644 --- a/litellm/proxy/auth/route_checks.py +++ b/litellm/proxy/auth/route_checks.py @@ -14,7 +14,6 @@ from litellm.proxy._types import ( ) from .auth_checks_organization import _user_is_org_admin -from .auth_utils import get_request_route # Management write routes denied to PROXY_ADMIN_VIEW_ONLY. Adding a new write # endpoint to a management router REQUIRES adding it here too — the surrounding @@ -628,6 +627,9 @@ class RouteChecks: Returns: bool: True if `thread` or `assistant` is in the request path, False otherwise """ + # Inline import — auth_utils participates in a proxy import cycle. + from .auth_utils import get_request_route # noqa: PLC0415 + route = get_request_route(request) if "thread" in route or "assistant" in route: return True diff --git a/litellm/proxy/common_utils/http_parsing_utils.py b/litellm/proxy/common_utils/http_parsing_utils.py index e7a3e85de4e..7abf1ef6760 100644 --- a/litellm/proxy/common_utils/http_parsing_utils.py +++ b/litellm/proxy/common_utils/http_parsing_utils.py @@ -6,7 +6,6 @@ import orjson from fastapi import Request, UploadFile, status from litellm._logging import verbose_proxy_logger -from litellm.proxy.auth.auth_utils import get_request_route from litellm.proxy._types import ProxyException from litellm.proxy.common_utils.callback_utils import ( get_metadata_variable_name_from_kwargs, @@ -509,6 +508,9 @@ def _add_vector_store_id_from_path(request_data: dict, request: Request) -> None request_data: The request data dictionary to populate request: The FastAPI Request object """ + # Inline import — auth_utils participates in a proxy import cycle. + from litellm.proxy.auth.auth_utils import get_request_route # noqa: PLC0415 + path = get_request_route(request) vector_store_match = re.search(r"/vector_stores/([^/]+)/", path) if vector_store_match: diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index ea680fc71bb..ba3aee75047 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -26,7 +26,6 @@ from litellm.proxy._types import ( UserAPIKeyAuth, WebhookEvent, ) -from litellm.proxy.auth.auth_utils import get_request_route from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.db.exception_handler import PrismaDBExceptionHandler from litellm.proxy.health_check import ( @@ -152,6 +151,9 @@ async def test_endpoint(request: Request): dict: A dictionary containing the route of the request URL. """ # ping the proxy server to check if its healthy + # Inline import — auth_utils participates in a proxy import cycle. + from litellm.proxy.auth.auth_utils import get_request_route # noqa: PLC0415 + return {"route": get_request_route(request)} diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index fb45339c2d2..8283c26ba5a 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -25,8 +25,6 @@ from litellm.proxy._types import ( TeamCallbackMetadata, UserAPIKeyAuth, ) -from litellm.proxy.auth.auth_utils import get_request_route -from litellm.proxy.auth.route_checks import RouteChecks from litellm.proxy.common_utils.callback_utils import ( get_metadata_variable_name_from_kwargs, ) @@ -334,10 +332,13 @@ def _get_metadata_variable_name(request: Request) -> str: For ALL other endpoints we call this "metadata" """ - if RouteChecks._is_assistants_api_request(request): - return "litellm_metadata" + # Inline imports — auth_utils/route_checks participate in a proxy import cycle. + from litellm.proxy.auth.auth_utils import get_request_route # noqa: PLC0415 path = get_request_route(request) + if "thread" in path or "assistant" in path: + return "litellm_metadata" + if any(route in path for route in LITELLM_METADATA_ROUTES): return "litellm_metadata" diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 71a2f166750..431ff49c7ce 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -52,7 +52,6 @@ from litellm.proxy._experimental.mcp_server.utils import ( from litellm.proxy._experimental.mcp_server.utils import ( validate_and_normalize_mcp_server_payload as _base_validate_and_normalize_mcp_server_payload, ) -from litellm.proxy.auth.auth_utils import get_request_route from litellm.proxy.common_utils.encrypt_decrypt_utils import ( decrypt_value_helper, encrypt_value_helper, @@ -1569,6 +1568,9 @@ if MCP_AVAILABLE: from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( # noqa: PLC0415 global_mcp_server_manager, ) + from litellm.proxy.auth.auth_utils import ( # noqa: PLC0415 + get_request_route, + ) server_id = request.path_params.get("server_id", "") if server_id: diff --git a/litellm/proxy/management_helpers/utils.py b/litellm/proxy/management_helpers/utils.py index 5a85ce32db4..b05c12be5f4 100644 --- a/litellm/proxy/management_helpers/utils.py +++ b/litellm/proxy/management_helpers/utils.py @@ -29,7 +29,6 @@ from litellm.proxy._types import ( # key request types; user request types; tea UserAPIKeyAuth, VirtualKeyEvent, ) -from litellm.proxy.auth.auth_utils import get_request_route from litellm.proxy.common_utils.http_parsing_utils import _read_request_body from litellm.proxy.utils import PrismaClient @@ -470,6 +469,11 @@ def management_endpoint_wrapper(func): if open_telemetry_logger is not None: if _http_request: + # Inline import — auth_utils participates in a proxy import cycle. + from litellm.proxy.auth.auth_utils import ( # noqa: PLC0415 + get_request_route, + ) + _route = get_request_route(_http_request) _request_body: dict = await _read_request_body( request=_http_request @@ -515,6 +519,11 @@ def management_endpoint_wrapper(func): if open_telemetry_logger is not None: _http_request = kwargs.get("http_request") if _http_request: + # Inline import — auth_utils participates in a proxy import cycle. + from litellm.proxy.auth.auth_utils import ( # noqa: PLC0415 + get_request_route, + ) + _route = get_request_route(_http_request) _request_body: dict = await _read_request_body( request=_http_request diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index d90e1407b0c..26f89e6b315 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -49,7 +49,6 @@ from litellm.proxy._types import ( ProxyException, UserAPIKeyAuth, ) -from litellm.proxy.auth.auth_utils import get_request_route from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_request_processing import ProxyBaseLLMRequestProcessing from litellm.proxy.common_utils.http_parsing_utils import ( @@ -1273,6 +1272,9 @@ def create_pass_through_route( user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), subpath: str = "", # captures sub-paths when include_subpath=True ): + from litellm.proxy.auth.auth_utils import ( # noqa: PLC0415 + get_request_route, + ) from litellm.proxy.pass_through_endpoints.pass_through_endpoints import ( InitPassThroughEndpointHelpers, ) diff --git a/litellm/proxy/spend_tracking/spend_management_endpoints.py b/litellm/proxy/spend_tracking/spend_management_endpoints.py index c68071360c3..335f917aa1b 100644 --- a/litellm/proxy/spend_tracking/spend_management_endpoints.py +++ b/litellm/proxy/spend_tracking/spend_management_endpoints.py @@ -12,7 +12,6 @@ import litellm from litellm._logging import verbose_proxy_logger from litellm.proxy._types import * from litellm.proxy._types import ProviderBudgetResponse, ProviderBudgetResponseObject -from litellm.proxy.auth.auth_utils import get_request_route from litellm.proxy.auth.user_api_key_auth import user_api_key_auth # NOTE: Avoid module-level import from common_utils: proxy_server imports this @@ -1818,6 +1817,9 @@ async def ui_view_spend_logs( # noqa: PLR0915 ) try: + # Inline import — auth_utils participates in a proxy import cycle. + from litellm.proxy.auth.auth_utils import get_request_route # noqa: PLC0415 + is_v2 = "/spend/logs/v2" in get_request_route(request) formats = ["%Y-%m-%d %H:%M:%S", "%Y-%m-%d"] if is_v2 else ["%Y-%m-%d %H:%M:%S"] diff --git a/litellm/proxy/vector_store_endpoints/utils.py b/litellm/proxy/vector_store_endpoints/utils.py index 7a57caee262..1221ccf119f 100644 --- a/litellm/proxy/vector_store_endpoints/utils.py +++ b/litellm/proxy/vector_store_endpoints/utils.py @@ -5,7 +5,6 @@ from fastapi import HTTPException, Request import litellm from litellm._logging import verbose_proxy_logger -from litellm.proxy.auth.auth_utils import get_request_route from litellm.proxy._types import ( LiteLLM_ObjectPermissionTable, LitellmUserRoles, @@ -331,6 +330,9 @@ def is_allowed_to_call_vector_store_endpoint( provider_config.get_vector_store_endpoints_by_type() ) + # Inline import — auth_utils participates in a proxy import cycle. + from litellm.proxy.auth.auth_utils import get_request_route # noqa: PLC0415 + request_route = get_request_route(request) # Determine the permission type based on the request @@ -395,6 +397,9 @@ def is_allowed_to_call_vector_store_files_endpoint( provider_config.get_vector_store_file_endpoints_by_type() ) + # Inline import — auth_utils participates in a proxy import cycle. + from litellm.proxy.auth.auth_utils import get_request_route # noqa: PLC0415 + request_route = get_request_route(request) permission_type: Optional[str] = None diff --git a/tests/proxy_unit_tests/test_proxy_routes.py b/tests/proxy_unit_tests/test_proxy_routes.py index 31488a6e360..db41bd65409 100644 --- a/tests/proxy_unit_tests/test_proxy_routes.py +++ b/tests/proxy_unit_tests/test_proxy_routes.py @@ -270,6 +270,13 @@ def _vector_store_id_in_path(req): # (label, scope_path, host_suffix_template, predicate, expected) — host_suffix_template # receives the host_header via %s substitution. The predicate is invoked on a Request # whose scope["path"] is scope_path and whose Host header is the formatted suffix. +# +# The MCP entries (well_known_mcp_bypass, pkce_token_suffix) call +# get_request_route directly rather than the surrounding production handler +# (MCPRequestHandler.process_mcp_request / _mcp_oauth_user_api_key_auth) — +# those handlers require an ASGI scope plus MCP state to invoke, and the call +# sites do nothing with the path except feed it to this helper. The helper- +# level assertion is the relevant signal. _CALL_SITES = [ ("assistants_classification", "/key/generate", "%s/thread", _is_assistants, False), (