@@ -120,10 +120,6 @@ def _run_patch_behaviour_tests(self):
120120 self ._test_unpatched_botocore_propagator ()
121121 self ._test_unpatched_gevent_instrumentation ()
122122 self ._test_unpatched_starlette_instrumentation ()
123- # TODO: remove these tests once we bump botocore instrumentation version to 0.56b0
124- # Bedrock Runtime tests
125- self ._test_unpatched_converse_stream_wrapper ()
126- self ._test_unpatched_extract_tool_calls ()
127123
128124 # Apply patches
129125 apply_instrumentation_patches ()
@@ -222,6 +218,16 @@ def _test_unpatched_botocore_instrumentation(self):
222218 # DynamoDB
223219 self .assertTrue ("dynamodb" in _KNOWN_EXTENSIONS , "Upstream has removed a DynamoDB extension" )
224220
221+ # Bedrock Runtime tests
222+ # TODO: remove these tests once we bump botocore instrumentation version to 0.56b0
223+ self ._test_unpatched_converse_stream_wrapper ()
224+ self ._test_unpatched_extract_tool_calls ()
225+
226+ # TODO: remove these tests once we bump botocore instrumentation version to 0.60b0
227+ self ._test_unpatched_process_anthropic_claude_chunk ({"location" : "Seattle" }, {"location" : "Seattle" })
228+ self ._test_unpatched_process_anthropic_claude_chunk (None , None )
229+ self ._test_unpatched_process_anthropic_claude_chunk ({}, {})
230+
225231 def _test_unpatched_gevent_instrumentation (self ):
226232 self .assertFalse (gevent .monkey .is_module_patched ("os" ), "gevent os module has been patched" )
227233 self .assertFalse (gevent .monkey .is_module_patched ("thread" ), "gevent thread module has been patched" )
@@ -267,10 +273,14 @@ def _test_patched_botocore_instrumentation(self):
267273 # Bedrock Agent Operation
268274 self ._test_patched_bedrock_agent_instrumentation ()
269275
270- # TODO: remove these tests once we bump botocore instrumentation version to 0.56b0
271276 # Bedrock Runtime
277+ # TODO: remove these tests once we bump botocore instrumentation version to 0.56b0
272278 self ._test_patched_converse_stream_wrapper ()
273279 self ._test_patched_extract_tool_calls ()
280+ # TODO: remove these tests once we bump botocore instrumentation version to 0.60b0
281+ self ._test_patched_process_anthropic_claude_chunk ({"location" : "Seattle" }, {"location" : "Seattle" })
282+ self ._test_patched_process_anthropic_claude_chunk (None , None )
283+ self ._test_patched_process_anthropic_claude_chunk ({}, {})
274284
275285 # Bedrock Agent Runtime
276286 self .assertTrue ("bedrock-agent-runtime" in _KNOWN_EXTENSIONS )
@@ -679,6 +689,95 @@ def _test_patched_extract_tool_calls(self):
679689 result = bedrock_utils .extract_tool_calls (message_with_string_content , True )
680690 self .assertIsNone (result )
681691
692+ # Test with toolUse format to exercise the for loop
693+ message_with_tool_use = {"role" : "assistant" , "content" : [{"toolUse" : {"toolUseId" : "id1" , "name" : "func1" }}]}
694+ result = bedrock_utils .extract_tool_calls (message_with_tool_use , True )
695+ self .assertEqual (len (result ), 1 )
696+
697+ # Test with tool_use format to exercise the for loop
698+ message_with_type_tool_use = {
699+ "role" : "assistant" ,
700+ "content" : [{"type" : "tool_use" , "id" : "id2" , "name" : "func2" }],
701+ }
702+ result = bedrock_utils .extract_tool_calls (message_with_type_tool_use , True )
703+ self .assertEqual (len (result ), 1 )
704+
705+ def _test_patched_process_anthropic_claude_chunk (
706+ self , input_value : Dict [str , str ], expected_output : Dict [str , str ]
707+ ):
708+ self ._test_process_anthropic_claude_chunk (input_value , expected_output , False )
709+
710+ def _test_unpatched_process_anthropic_claude_chunk (
711+ self , input_value : Dict [str , str ], expected_output : Dict [str , str ]
712+ ):
713+ self ._test_process_anthropic_claude_chunk (input_value , expected_output , True )
714+
715+ def _test_process_anthropic_claude_chunk (
716+ self , input_value : Dict [str , str ], expected_output : Dict [str , str ], expect_exception : bool
717+ ):
718+ """Test that _process_anthropic_claude_chunk handles various tool_use input formats."""
719+ wrapper = bedrock_utils .InvokeModelWithResponseStreamWrapper (
720+ stream = MagicMock (),
721+ stream_done_callback = MagicMock ,
722+ stream_error_callback = MagicMock ,
723+ model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0" ,
724+ )
725+
726+ # Simulate message_start
727+ wrapper ._process_anthropic_claude_chunk (
728+ {
729+ "type" : "message_start" ,
730+ "message" : {
731+ "role" : "assistant" ,
732+ "content" : [],
733+ },
734+ }
735+ )
736+
737+ # Simulate content_block_start with specified input
738+ content_block = {
739+ "type" : "tool_use" ,
740+ "id" : "test_id" ,
741+ "name" : "test_tool" ,
742+ }
743+ if input_value is not None :
744+ content_block ["input" ] = input_value
745+
746+ wrapper ._process_anthropic_claude_chunk (
747+ {
748+ "type" : "content_block_start" ,
749+ "index" : 0 ,
750+ "content_block" : content_block ,
751+ }
752+ )
753+
754+ # Simulate content_block_stop
755+ try :
756+ wrapper ._process_anthropic_claude_chunk ({"type" : "content_block_stop" , "index" : 0 })
757+ except TypeError :
758+ if expect_exception :
759+ return
760+ else :
761+ raise
762+
763+ # Verify the message content
764+ self .assertEqual (len (wrapper ._message ["content" ]), 1 )
765+ tool_block = wrapper ._message ["content" ][0 ]
766+ self .assertEqual (tool_block ["type" ], "tool_use" )
767+ self .assertEqual (tool_block ["id" ], "test_id" )
768+ self .assertEqual (tool_block ["name" ], "test_tool" )
769+
770+ if expected_output is not None :
771+ self .assertEqual (tool_block ["input" ], expected_output )
772+ self .assertIsInstance (tool_block ["input" ], dict )
773+ else :
774+ self .assertNotIn ("input" , tool_block )
775+
776+ # Just adding this to do basic sanity checks and increase code coverage
777+ wrapper ._process_anthropic_claude_chunk ({"type" : "content_block_delta" , "index" : 0 })
778+ wrapper ._process_anthropic_claude_chunk ({"type" : "message_delta" })
779+ wrapper ._process_anthropic_claude_chunk ({"type" : "message_stop" })
780+
682781 def _test_patched_bedrock_agent_instrumentation (self ):
683782 """For bedrock-agent service, both extract_attributes and on_success provides attributes,
684783 the attributes depend on the API being invoked."""
0 commit comments