From ab156ab4dfb75961a7e48532ba922e9f2d8a2f25 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 28 Aug 2025 15:14:09 -0700 Subject: [PATCH] [Bug Fix] Virtual keys with llm_api type cause Internal Server Error when using /anthropic/* and other llm passthrough routes (#14046) * fix route checks passthrough_routes_wildcard * refactor check_route_access * test_virtual_key_llm_api_route_includes_passthrough_prefix --- litellm/proxy/_types.py | 7 ++++ litellm/proxy/auth/route_checks.py | 35 +++++++++++++++++-- .../proxy/auth/test_route_checks.py | 23 ++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 2239526a316..5a1952686bc 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -326,6 +326,12 @@ class LiteLLMRoutes(enum.Enum): "/mistral", ] + ######################################################### + # e.g /vllm/*, anthropic/*, etc. + # allows using /anthropic/v1/messages, /vllm/v1/chat/completions, etc. + ######################################################### + passthrough_routes_wildcard = [f"{route}/*" for route in mapped_pass_through_routes] + anthropic_routes = [ "/v1/messages", ] @@ -356,6 +362,7 @@ class LiteLLMRoutes(enum.Enum): openai_routes + anthropic_routes + mapped_pass_through_routes + + passthrough_routes_wildcard + apply_guardrail_routes + mcp_routes ) diff --git a/litellm/proxy/auth/route_checks.py b/litellm/proxy/auth/route_checks.py index 8883f7d5429..3774cdfb810 100644 --- a/litellm/proxy/auth/route_checks.py +++ b/litellm/proxy/auth/route_checks.py @@ -300,6 +300,13 @@ class RouteChecks: if re.match(pattern, route): return True return False + + @staticmethod + def _is_wildcard_pattern(pattern: str) -> bool: + """ + Check if pattern is a wildcard pattern + """ + return pattern.endswith("*") @staticmethod def _route_matches_wildcard_pattern(route: str, pattern: str) -> bool: @@ -342,10 +349,34 @@ class RouteChecks: Returns: bool: True if route is allowed, False otherwise """ - return route in allowed_routes or any( # Check exact match + ######################################################### + # exact match route is in allowed_routes + ######################################################### + if route in allowed_routes: + return True + + ######################################################### + # wildcard match route is in allowed_routes + # e.g calling /anthropic/v1/messages is allowed if allowed_routes has /anthropic/* + ######################################################### + wildcard_allowed_routes = [route for route in allowed_routes if RouteChecks._is_wildcard_pattern(pattern=route)] + for allowed_route in wildcard_allowed_routes: + if RouteChecks._route_matches_wildcard_pattern(route=route, pattern=allowed_route): + return True + + ######################################################### + # pattern match route is in allowed_routes + # pattern: "/threads/{thread_id}" + # route: "/threads/thread_49EIN5QF32s4mH20M7GFKdlZ" + # returns: True + ######################################################### + if any( # Check pattern match RouteChecks._route_matches_pattern(route=route, pattern=allowed_route) for allowed_route in allowed_routes - ) # Check pattern match + ): + return True + + return False @staticmethod def _is_assistants_api_request(request: Request) -> bool: diff --git a/tests/test_litellm/proxy/auth/test_route_checks.py b/tests/test_litellm/proxy/auth/test_route_checks.py index b42b362b41d..ac09917e4cd 100644 --- a/tests/test_litellm/proxy/auth/test_route_checks.py +++ b/tests/test_litellm/proxy/auth/test_route_checks.py @@ -130,6 +130,29 @@ def test_virtual_key_allowed_routes_with_litellm_routes_member_name_denied(): assert "Only allowed to call routes: ['info_routes']" in str(exc_info.value) assert "Tried to call route: /chat/completions" in str(exc_info.value) +@pytest.mark.parametrize("route", [ + "/anthropic/v1/messages", + "/anthropic/v1/count_tokens", + "/gemini/v1/models", + "/gemini/countTokens", +]) +def test_virtual_key_llm_api_route_includes_passthrough_prefix(route): + """ + Virtual key with llm_api_routes should allow passthrough routes like /anthropic/v1/messages + + Relevant issue: https://github.com/BerriAI/litellm/issues/14017 + """ + + valid_token = UserAPIKeyAuth( + user_id="test_user", allowed_routes=["llm_api_routes"] + ) + + result = RouteChecks.is_virtual_key_allowed_to_call_route( + route=route, valid_token=valid_token + ) + + assert result is True + def test_virtual_key_allowed_routes_with_multiple_litellm_routes_member_names(): """Test that virtual key works with multiple LiteLLMRoutes member names in allowed_routes"""