From 4400a6c189b643849925cb5f0a01bd6a5c71db2d Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Sat, 4 Oct 2025 09:18:26 -0700 Subject: [PATCH] test bedrock guardrails --- .../guardrail_hooks/bedrock_guardrails.py | 23 +++++--- .../test_bedrock_guardrails.py | 57 +++++++++++-------- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py b/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py index c88ebe16d9..7479c9dfcf 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py +++ b/litellm/proxy/guardrails/guardrail_hooks/bedrock_guardrails.py @@ -727,14 +727,21 @@ class BedrockGuardrail(CustomGuardrail, BaseAWSLLM): ) return - outputs: List[BedrockGuardrailOutput] = ( - response.get("outputs", []) or [] - ) - if not any(output.get("text") for output in outputs): - verbose_proxy_logger.warning( - "Bedrock AI: not running guardrail. No output text in response" - ) - return + # Check if the ModelResponse has text content in its choices + # to avoid sending empty content to Bedrock (e.g., during tool calls) + if isinstance(response, litellm.ModelResponse): + has_text_content = False + for choice in response.choices: + if isinstance(choice, litellm.Choices): + if choice.message.content and isinstance(choice.message.content, str): + has_text_content = True + break + + if not has_text_content: + verbose_proxy_logger.warning( + "Bedrock AI: not running guardrail. No output text in response" + ) + return ######################################################### ########## 1. Make parallel Bedrock API requests ########## diff --git a/tests/guardrails_tests/test_bedrock_guardrails.py b/tests/guardrails_tests/test_bedrock_guardrails.py index c4d1655594..7ca78d83ae 100644 --- a/tests/guardrails_tests/test_bedrock_guardrails.py +++ b/tests/guardrails_tests/test_bedrock_guardrails.py @@ -1384,28 +1384,34 @@ async def test_bedrock_guardrail_post_call_success_hook_no_output_text(): guardrailVersion="DRAFT" ) - # Mock Bedrock API with no output text - mock_bedrock_response = MagicMock() - mock_bedrock_response.status_code = 200 - mock_bedrock_response.json.return_value = { - "output": { - "message": { - "role": "assistant", - "content": [ - { - "toolUse": { - "toolUseId": "tooluse_kZJMlvQmRJ6eAyJE5GIl7Q", - "name": "top_song", - "input": { - "sign": "WZPZ" - } - } - } - ] - } - }, - "stopReason": "tool_use" - } + # Create a ModelResponse with tool calls (no text content) + # This simulates a response where the LLM is making a tool call + mock_response = litellm.ModelResponse( + id="test-id", + choices=[ + litellm.Choices( + index=0, + message=litellm.Message( + role="assistant", + content=None, # No text content + tool_calls=[ + litellm.utils.ChatCompletionMessageToolCall( + id="tooluse_kZJMlvQmRJ6eAyJE5GIl7Q", + function=litellm.utils.Function( + name="top_song", + arguments='{"sign": "WZPZ"}' + ), + type="function" + ) + ] + ), + finish_reason="tool_calls" + ) + ], + created=1234567890, + model="gpt-4o", + object="chat.completion" + ) data = { "model": "gpt-4o", @@ -1415,10 +1421,11 @@ async def test_bedrock_guardrail_post_call_success_hook_no_output_text(): } mock_user_api_key_dict = UserAPIKeyAuth() - return await guardrail.async_post_call_success_hook( + result = await guardrail.async_post_call_success_hook( data=data, - response=mock_bedrock_response, + response=mock_response, user_api_key_dict=mock_user_api_key_dict, ) - # If no error is raised, then the test passes + # If no error is raised and result is None, then the test passes + assert result is None print("✅ No output text in response test passed") \ No newline at end of file