@@ -84,10 +84,6 @@ def _run_patch_behaviour_tests(self):
8484        self ._test_unpatched_botocore_propagator ()
8585        self ._test_unpatched_gevent_instrumentation ()
8686        self ._test_unpatched_starlette_instrumentation ()
87-         # TODO: remove these tests once we bump botocore instrumentation version to 0.56b0 
88-         # Bedrock Runtime tests 
89-         self ._test_unpatched_converse_stream_wrapper ()
90-         self ._test_unpatched_extract_tool_calls ()
9187
9288        # Apply patches 
9389        apply_instrumentation_patches ()
@@ -178,6 +174,16 @@ def _test_unpatched_botocore_instrumentation(self):
178174        # DynamoDB 
179175        self .assertTrue ("dynamodb"  in  _KNOWN_EXTENSIONS , "Upstream has removed a DynamoDB extension" )
180176
177+         # Bedrock Runtime tests 
178+         # TODO: remove these tests once we bump botocore instrumentation version to 0.56b0 
179+         self ._test_unpatched_converse_stream_wrapper ()
180+         self ._test_unpatched_extract_tool_calls ()
181+ 
182+         # TODO: remove these tests once we bump botocore instrumentation version to 0.60b0 
183+         self ._test_unpatched_process_anthropic_claude_chunk ({"location" : "Seattle" }, {"location" : "Seattle" })
184+         self ._test_unpatched_process_anthropic_claude_chunk (None , None )
185+         self ._test_unpatched_process_anthropic_claude_chunk ({}, {})
186+ 
181187    def  _test_unpatched_gevent_instrumentation (self ):
182188        self .assertFalse (gevent .monkey .is_module_patched ("os" ), "gevent os module has been patched" )
183189        self .assertFalse (gevent .monkey .is_module_patched ("thread" ), "gevent thread module has been patched" )
@@ -223,10 +229,14 @@ def _test_patched_botocore_instrumentation(self):
223229        # Bedrock Agent Operation 
224230        self ._test_patched_bedrock_agent_instrumentation ()
225231
226-         # TODO: remove these tests once we bump botocore instrumentation version to 0.56b0 
227232        # Bedrock Runtime 
233+         # TODO: remove these tests once we bump botocore instrumentation version to 0.56b0 
228234        self ._test_patched_converse_stream_wrapper ()
229235        self ._test_patched_extract_tool_calls ()
236+         # TODO: remove these tests once we bump botocore instrumentation version to 0.60b0 
237+         self ._test_patched_process_anthropic_claude_chunk ({"location" : "Seattle" }, {"location" : "Seattle" })
238+         self ._test_patched_process_anthropic_claude_chunk (None , None )
239+         self ._test_patched_process_anthropic_claude_chunk ({}, {})
230240
231241        # Bedrock Agent Runtime 
232242        self .assertTrue ("bedrock-agent-runtime"  in  _KNOWN_EXTENSIONS )
@@ -600,6 +610,95 @@ def _test_patched_extract_tool_calls(self):
600610        result  =  bedrock_utils .extract_tool_calls (message_with_string_content , True )
601611        self .assertIsNone (result )
602612
613+         # Test with toolUse format to exercise the for loop 
614+         message_with_tool_use  =  {"role" : "assistant" , "content" : [{"toolUse" : {"toolUseId" : "id1" , "name" : "func1" }}]}
615+         result  =  bedrock_utils .extract_tool_calls (message_with_tool_use , True )
616+         self .assertEqual (len (result ), 1 )
617+ 
618+         # Test with tool_use format to exercise the for loop 
619+         message_with_type_tool_use  =  {
620+             "role" : "assistant" ,
621+             "content" : [{"type" : "tool_use" , "id" : "id2" , "name" : "func2" }],
622+         }
623+         result  =  bedrock_utils .extract_tool_calls (message_with_type_tool_use , True )
624+         self .assertEqual (len (result ), 1 )
625+ 
626+     def  _test_patched_process_anthropic_claude_chunk (
627+         self , input_value : Dict [str , str ], expected_output : Dict [str , str ]
628+     ):
629+         self ._test_process_anthropic_claude_chunk (input_value , expected_output , False )
630+ 
631+     def  _test_unpatched_process_anthropic_claude_chunk (
632+         self , input_value : Dict [str , str ], expected_output : Dict [str , str ]
633+     ):
634+         self ._test_process_anthropic_claude_chunk (input_value , expected_output , True )
635+ 
636+     def  _test_process_anthropic_claude_chunk (
637+         self , input_value : Dict [str , str ], expected_output : Dict [str , str ], expect_exception : bool 
638+     ):
639+         """Test that _process_anthropic_claude_chunk handles various tool_use input formats.""" 
640+         wrapper  =  bedrock_utils .InvokeModelWithResponseStreamWrapper (
641+             stream = MagicMock (),
642+             stream_done_callback = MagicMock ,
643+             stream_error_callback = MagicMock ,
644+             model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0" ,
645+         )
646+ 
647+         # Simulate message_start 
648+         wrapper ._process_anthropic_claude_chunk (
649+             {
650+                 "type" : "message_start" ,
651+                 "message" : {
652+                     "role" : "assistant" ,
653+                     "content" : [],
654+                 },
655+             }
656+         )
657+ 
658+         # Simulate content_block_start with specified input 
659+         content_block  =  {
660+             "type" : "tool_use" ,
661+             "id" : "test_id" ,
662+             "name" : "test_tool" ,
663+         }
664+         if  input_value  is  not None :
665+             content_block ["input" ] =  input_value 
666+ 
667+         wrapper ._process_anthropic_claude_chunk (
668+             {
669+                 "type" : "content_block_start" ,
670+                 "index" : 0 ,
671+                 "content_block" : content_block ,
672+             }
673+         )
674+ 
675+         # Simulate content_block_stop 
676+         try :
677+             wrapper ._process_anthropic_claude_chunk ({"type" : "content_block_stop" , "index" : 0 })
678+         except  TypeError :
679+             if  expect_exception :
680+                 return 
681+             else :
682+                 raise 
683+ 
684+         # Verify the message content 
685+         self .assertEqual (len (wrapper ._message ["content" ]), 1 )
686+         tool_block  =  wrapper ._message ["content" ][0 ]
687+         self .assertEqual (tool_block ["type" ], "tool_use" )
688+         self .assertEqual (tool_block ["id" ], "test_id" )
689+         self .assertEqual (tool_block ["name" ], "test_tool" )
690+ 
691+         if  expected_output  is  not None :
692+             self .assertEqual (tool_block ["input" ], expected_output )
693+             self .assertIsInstance (tool_block ["input" ], dict )
694+         else :
695+             self .assertNotIn ("input" , tool_block )
696+ 
697+         # Just adding this to do basic sanity checks and increase code coverage 
698+         wrapper ._process_anthropic_claude_chunk ({"type" : "content_block_delta" , "index" : 0 })
699+         wrapper ._process_anthropic_claude_chunk ({"type" : "message_delta" })
700+         wrapper ._process_anthropic_claude_chunk ({"type" : "message_stop" })
701+ 
603702    def  _test_patched_bedrock_agent_instrumentation (self ):
604703        """For bedrock-agent service, both extract_attributes and on_success provides attributes, 
605704        the attributes depend on the API being invoked.""" 
0 commit comments