@@ -79,9 +79,9 @@ def convert_to_bedrock_format(
79
79
80
80
if messages :
81
81
for message in messages :
82
- message_text_content : Optional [
83
- List [ str ]
84
- ] = self . get_content_for_message ( message = message )
82
+ message_text_content : Optional [List [ str ]] = (
83
+ self . get_content_for_message ( message = message )
84
+ )
85
85
if message_text_content is None :
86
86
continue
87
87
for text_content in message_text_content :
@@ -241,7 +241,7 @@ def _should_raise_guardrail_blocked_exception(
241
241
self , response : BedrockGuardrailResponse
242
242
) -> bool :
243
243
"""
244
- By default always raise an exception when a guardrail intervention is detected .
244
+ Only raise exception for "BLOCKED" actions, not for "ANONYMIZED" actions .
245
245
246
246
If `self.mask_request_content` or `self.mask_response_content` is set to `True`, then use the output from the guardrail to mask the request or response content.
247
247
"""
@@ -250,11 +250,68 @@ def _should_raise_guardrail_blocked_exception(
250
250
if self .mask_request_content or self .mask_response_content :
251
251
return False
252
252
253
- # if intervention, return True
254
- if response .get ("action" ) == "GUARDRAIL_INTERVENED" :
255
- return True
256
-
257
253
# if no intervention, return False
254
+ if response .get ("action" ) != "GUARDRAIL_INTERVENED" :
255
+ return False
256
+
257
+ # Check assessments to determine if any actions were BLOCKED (vs ANONYMIZED)
258
+ assessments = response .get ("assessments" , [])
259
+ if not assessments :
260
+ return False
261
+
262
+ for assessment in assessments :
263
+ # Check topic policy
264
+ topic_policy = assessment .get ("topicPolicy" )
265
+ if topic_policy :
266
+ topics = topic_policy .get ("topics" , [])
267
+ for topic in topics :
268
+ if topic .get ("action" ) == "BLOCKED" :
269
+ return True
270
+
271
+ # Check content policy
272
+ content_policy = assessment .get ("contentPolicy" )
273
+ if content_policy :
274
+ filters = content_policy .get ("filters" , [])
275
+ for filter_item in filters :
276
+ if filter_item .get ("action" ) == "BLOCKED" :
277
+ return True
278
+
279
+ # Check word policy
280
+ word_policy = assessment .get ("wordPolicy" )
281
+ if word_policy :
282
+ custom_words = word_policy .get ("customWords" , [])
283
+ for custom_word in custom_words :
284
+ if custom_word .get ("action" ) == "BLOCKED" :
285
+ return True
286
+ managed_words = word_policy .get ("managedWordLists" , [])
287
+ for managed_word in managed_words :
288
+ if managed_word .get ("action" ) == "BLOCKED" :
289
+ return True
290
+
291
+ # Check sensitive information policy
292
+ sensitive_info_policy = assessment .get ("sensitiveInformationPolicy" )
293
+ if sensitive_info_policy :
294
+ pii_entities = sensitive_info_policy .get ("piiEntities" , [])
295
+ if pii_entities :
296
+ for pii_entity in pii_entities :
297
+ if pii_entity .get ("action" ) == "BLOCKED" :
298
+ return True
299
+ regexes = sensitive_info_policy .get ("regexes" , [])
300
+ if regexes :
301
+ for regex in regexes :
302
+ if regex .get ("action" ) == "BLOCKED" :
303
+ return True
304
+
305
+ # Check contextual grounding policy
306
+ contextual_grounding_policy = assessment .get ("contextualGroundingPolicy" )
307
+ if contextual_grounding_policy :
308
+ grounding_filters = contextual_grounding_policy .get ("filters" , [])
309
+ for filter_item in grounding_filters :
310
+ if filter_item .get ("action" ) == "BLOCKED" :
311
+ return True
312
+
313
+ # If we got here, intervention occurred but no BLOCKED actions found
314
+ # This means all actions were ANONYMIZED or NONE, so don't raise exception
258
315
return False
259
316
260
317
@log_guardrail_information
@@ -300,11 +357,11 @@ async def async_pre_call_hook(
300
357
#########################################################
301
358
########## 2. Update the messages with the guardrail response ##########
302
359
#########################################################
303
- data [
304
- "messages"
305
- ] = self . _update_messages_with_updated_bedrock_guardrail_response (
306
- messages = new_messages ,
307
- bedrock_guardrail_response = bedrock_guardrail_response ,
360
+ data ["messages" ] = (
361
+ self . _update_messages_with_updated_bedrock_guardrail_response (
362
+ messages = new_messages ,
363
+ bedrock_guardrail_response = bedrock_guardrail_response ,
364
+ )
308
365
)
309
366
310
367
#########################################################
@@ -354,11 +411,11 @@ async def async_moderation_hook(
354
411
#########################################################
355
412
########## 2. Update the messages with the guardrail response ##########
356
413
#########################################################
357
- data [
358
- "messages"
359
- ] = self . _update_messages_with_updated_bedrock_guardrail_response (
360
- messages = new_messages ,
361
- bedrock_guardrail_response = bedrock_guardrail_response ,
414
+ data ["messages" ] = (
415
+ self . _update_messages_with_updated_bedrock_guardrail_response (
416
+ messages = new_messages ,
417
+ bedrock_guardrail_response = bedrock_guardrail_response ,
418
+ )
362
419
)
363
420
364
421
#########################################################
@@ -408,11 +465,11 @@ async def async_post_call_success_hook(
408
465
#########################################################
409
466
########## 2. Update the messages with the guardrail response ##########
410
467
#########################################################
411
- data [
412
- "messages"
413
- ] = self . _update_messages_with_updated_bedrock_guardrail_response (
414
- messages = new_messages ,
415
- bedrock_guardrail_response = bedrock_guardrail_response ,
468
+ data ["messages" ] = (
469
+ self . _update_messages_with_updated_bedrock_guardrail_response (
470
+ messages = new_messages ,
471
+ bedrock_guardrail_response = bedrock_guardrail_response ,
472
+ )
416
473
)
417
474
418
475
#########################################################
@@ -440,21 +497,29 @@ def _update_messages_with_updated_bedrock_guardrail_response(
440
497
Returns:
441
498
List of messages with content masked according to guardrail response
442
499
"""
443
- # Skip processing if masking is not enabled
444
- if not (self .mask_request_content or self .mask_response_content ):
445
- return messages
446
-
447
500
# Get masked texts from guardrail response
448
501
masked_texts = self ._extract_masked_texts_from_response (
449
502
bedrock_guardrail_response
450
503
)
451
- if not masked_texts :
452
- return messages
453
504
454
- # Apply masking to messages using index tracking
455
- return self ._apply_masking_to_messages (
456
- messages = messages , masked_texts = masked_texts
457
- )
505
+ # If guardrail provided masked output, use it regardless of masking flags
506
+ # because the guardrail has already determined this content needs anonymization
507
+ if masked_texts :
508
+ verbose_proxy_logger .debug (
509
+ "Bedrock guardrail provided masked output, applying to messages"
510
+ )
511
+ return self ._apply_masking_to_messages (
512
+ messages = messages , masked_texts = masked_texts
513
+ )
514
+
515
+ # If masking is enabled but no masked texts available, still try to apply
516
+ # (this maintains backward compatibility for edge cases)
517
+ if self .mask_request_content or self .mask_response_content :
518
+ verbose_proxy_logger .debug (
519
+ "Masking enabled but no masked output from guardrail, returning original messages"
520
+ )
521
+
522
+ return messages
458
523
459
524
async def async_post_call_streaming_iterator_hook (
460
525
self ,
0 commit comments