From 3dd41e94d2a3baa132a287e2968befc19a45c251 Mon Sep 17 00:00:00 2001 From: Youlian Simidjiyski Date: Sat, 22 Aug 2026 18:29:36 -0400 Subject: [PATCH] test(guardrails): pin anthropic response-scan scoping to the request scan The anthropic handler routes both scans through _scoped_request_view so skip_system_message drops only the hoisted top-level prompt while in-sequence system rows stay in scope. Only each scan was asserted in isolation, so the two could silently diverge; this compares them directly on one request --- .../test_anthropic_guardrail_handler.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py index a691a037ee2..426ad6fd472 100644 --- a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py +++ b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py @@ -1906,6 +1906,41 @@ class TestAnthropicResponseScanConversation: assert conversation[0] == {"role": "user", "content": "what's the weather?"} assert conversation[-1] == {"role": "assistant", "content": "Sunny today."} + @pytest.mark.asyncio + async def test_skip_system_scoping_matches_request_scan(self): + handler = AnthropicMessagesHandler() + guardrail = MockInputsRecordingGuardrail() + guardrail.skip_system_message_in_guardrail = True + request_data = self._request_data( + system="trusted top-level system prompt", + messages=[ + {"role": "user", "content": "safe text"}, + {"role": "system", "content": "prohibited correction"}, + {"role": "user", "content": "continue"}, + ], + ) + response = { + "id": "msg_1", + "model": "claude-sonnet-4-5", + "content": [{"type": "text", "text": "Done."}], + } + + await handler.process_input_messages(data=dict(request_data), guardrail_to_apply=guardrail) + await handler.process_output_response( + response=response, + guardrail_to_apply=guardrail, + request_data=dict(request_data), + ) + + (request_type, request_inputs), (response_type, response_inputs) = guardrail.calls + assert (request_type, response_type) == ("request", "response") + request_conversation = request_inputs["structured_messages"] + response_conversation = response_inputs["structured_messages"] + assert [message["role"] for message in request_conversation] == ["user", "system", "user"] + assert response_conversation[: len(request_conversation)] == request_conversation + assert response_conversation[len(request_conversation) :] == [{"role": "assistant", "content": "Done."}] + assert "trusted top-level system prompt" not in str(response_conversation) + @pytest.mark.asyncio async def test_response_scan_includes_translated_tools_and_tool_call_turn(self): handler = AnthropicMessagesHandler()