From c5ced033c92d8e663a8066a9fcf77049a2eddd7c Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 14 Jan 2026 13:37:01 +0900 Subject: [PATCH] fix: anthropic during call guardrail error --- litellm/proxy/common_request_processing.py | 17 +------ tests/proxy_unit_tests/test_proxy_server.py | 4 -- .../integrations/test_custom_guardrail.py | 45 ------------------- .../test_unified_guardrail.py | 44 ++++++++++++++++++ 4 files changed, 45 insertions(+), 65 deletions(-) diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index 61bffde3ac..3db1620107 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -649,9 +649,7 @@ class ProxyBaseLLMRequestProcessing: proxy_logging_obj.during_call_hook( data=self.data, user_api_key_dict=user_api_key_dict, - call_type=ProxyBaseLLMRequestProcessing._get_pre_call_type( - route_type=route_type # type: ignore - ), + call_type=route_type, # type: ignore ) ) @@ -1004,19 +1002,6 @@ class ProxyBaseLLMRequestProcessing: headers=headers, ) - @staticmethod - def _get_pre_call_type( - route_type: Literal["acompletion", "aembedding", "aresponses", "allm_passthrough_route"], - ) -> Literal["completion", "embedding", "responses", "allm_passthrough_route"]: - if route_type == "acompletion": - return "completion" - elif route_type == "aembedding": - return "embedding" - elif route_type == "aresponses": - return "responses" - elif route_type == "allm_passthrough_route": - return "allm_passthrough_route" - ######################################################### # Proxy Level Streaming Data Generator ######################################################### diff --git a/tests/proxy_unit_tests/test_proxy_server.py b/tests/proxy_unit_tests/test_proxy_server.py index 0db1240544..839d09b52d 100644 --- a/tests/proxy_unit_tests/test_proxy_server.py +++ b/tests/proxy_unit_tests/test_proxy_server.py @@ -640,10 +640,6 @@ def test_embedding(mock_aembedding, client_no_auth): pre_call_kwargs.get("call_type") == "aembedding" ), f"expected pre_call_hook to receive call_type='aembedding', got {pre_call_kwargs.get('call_type')}" - during_call_kwargs = mock_during_hook.await_args_list[0].kwargs - assert ( - during_call_kwargs.get("call_type") == "embedding" - ), f"expected during_call_hook to receive call_type='embedding', got {during_call_kwargs.get('call_type')}" except Exception as e: pytest.fail(f"LiteLLM Proxy test failed. Exception - {str(e)}") diff --git a/tests/test_litellm/integrations/test_custom_guardrail.py b/tests/test_litellm/integrations/test_custom_guardrail.py index d7d7720ff4..00557c10b5 100644 --- a/tests/test_litellm/integrations/test_custom_guardrail.py +++ b/tests/test_litellm/integrations/test_custom_guardrail.py @@ -492,51 +492,6 @@ class TestCustomGuardrailPassthroughSupport: assert result is True -class TestPassthroughCallTypeHandling: - """Tests for passthrough call type handling in common_request_processing.""" - - def test_get_pre_call_type_with_allm_passthrough_route(self): - """ - Test that _get_pre_call_type correctly maps allm_passthrough_route. - - This tests Fix #1: allm_passthrough_route was not being handled, causing call_type to be None. - """ - from litellm.proxy.common_request_processing import ( - ProxyBaseLLMRequestProcessing, - ) - - # Test the mapping - result = ProxyBaseLLMRequestProcessing._get_pre_call_type( - route_type="allm_passthrough_route" - ) - - # Should return allm_passthrough_route, not None - assert result == "allm_passthrough_route" - - def test_get_pre_call_type_preserves_standard_mappings(self): - """ - Test that _get_pre_call_type still correctly maps standard route types. - - Ensures Fix #1 didn't break existing functionality. - """ - from litellm.proxy.common_request_processing import ( - ProxyBaseLLMRequestProcessing, - ) - - # Test standard mappings are preserved - assert ( - ProxyBaseLLMRequestProcessing._get_pre_call_type(route_type="acompletion") - == "completion" - ) - assert ( - ProxyBaseLLMRequestProcessing._get_pre_call_type(route_type="aembedding") - == "embedding" - ) - assert ( - ProxyBaseLLMRequestProcessing._get_pre_call_type(route_type="aresponses") - == "responses" - ) - class TestEventTypeLogging: """Tests for event_type logging in guardrail information.""" diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py index f4aa28d98c..cdb23916bd 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_unified_guardrail.py @@ -4,6 +4,7 @@ import pytest from litellm.caching import DualCache from litellm.integrations.custom_guardrail import CustomGuardrail +from litellm.llms.base_llm.guardrail_translation.base_translation import BaseTranslation from litellm.proxy._experimental.mcp_server.guardrail_translation.handler import ( MCPGuardrailTranslationHandler, ) @@ -30,11 +31,28 @@ class RecordingGuardrail(CustomGuardrail): return {"texts": inputs.get("texts", [])} +class _NoopTranslation(BaseTranslation): + """Test translation handler that simply echoes input/output.""" + + async def process_input_messages(self, data, guardrail_to_apply, litellm_logging_obj=None): # type: ignore[override] + return data + + async def process_output_response( # type: ignore[override] + self, + response, + guardrail_to_apply, + litellm_logging_obj=None, + user_api_key_dict=None, + ): + return response + + @pytest.fixture(autouse=True) def _inject_mcp_handler_mapping(): """Inject MCP handler mapping so the unified guardrail can run inside tests.""" unified_module.endpoint_guardrail_translation_mappings = { CallTypes.call_mcp_tool: MCPGuardrailTranslationHandler, + CallTypes.anthropic_messages: _NoopTranslation, } yield unified_module.endpoint_guardrail_translation_mappings = None @@ -82,3 +100,29 @@ async def test_moderation_hook_uses_mcp_event_type(): ) assert guardrail.event_history == [GuardrailEventHooks.during_mcp_call] + + +@pytest.mark.asyncio +async def test_moderation_hook_runs_for_anthropic_messages(): + """Ensure anthropic_messages requests still trigger guardrail moderation.""" + handler = UnifiedLLMGuardrails() + guardrail = RecordingGuardrail() + + data = { + "guardrail_to_apply": guardrail, + "messages": [ + { + "role": "user", + "content": "Hello Anthropics", + } + ], + "model": "anthropic.claude-3", + } + + await handler.async_moderation_hook( + data=data, + user_api_key_dict=None, + call_type=CallTypes.anthropic_messages.value, + ) + + assert guardrail.event_history == [GuardrailEventHooks.during_call]