3030)
3131
3232
33+ def hello_world ():
34+ return "Hello, World!"
35+
36+
37+ @pytest .fixture
38+ def tool_with_no_parameters ():
39+ tool = Tool (
40+ name = "hello_world" ,
41+ description = "This prints hello world" ,
42+ parameters = {"properties" : {}, "type" : "object" },
43+ function = hello_world ,
44+ )
45+ return tool
46+
47+
3348@pytest .fixture
3449def tools ():
3550 tool_parameters = {"type" : "object" , "properties" : {"city" : {"type" : "string" }}, "required" : ["city" ]}
@@ -39,7 +54,6 @@ def tools():
3954 parameters = tool_parameters ,
4055 function = lambda x : x ,
4156 )
42-
4357 return [tool ]
4458
4559
@@ -533,6 +547,116 @@ def test_convert_streaming_chunks_to_chat_message_malformed_json(self, caplog):
533547 with caplog .at_level (logging .WARNING ):
534548 assert "Anthropic returned a malformed JSON string" in caplog .text
535549
550+ def test_convert_streaming_chunks_to_chat_message_tool_call_with_empty_arguments (self ):
551+ """
552+ Test converting streaming chunks with an empty tool call arguments
553+ """
554+ chunks = [
555+ StreamingChunk (
556+ content = "" ,
557+ meta = {
558+ "content_block" : {"citations" : None , "text" : "" , "type" : "text" },
559+ "index" : 0 ,
560+ "type" : "content_block_start" ,
561+ },
562+ ),
563+ StreamingChunk (
564+ content = "Certainly! I can" ,
565+ meta = {
566+ "delta" : {"text" : "Certainly! I can" , "type" : "text_delta" },
567+ "index" : 0 ,
568+ "type" : "content_block_delta" ,
569+ },
570+ ),
571+ StreamingChunk (
572+ content = ' help you print "Hello World" using the available' ,
573+ meta = {
574+ "delta" : {"text" : ' help you print "Hello World" using the available' , "type" : "text_delta" },
575+ "index" : 0 ,
576+ "type" : "content_block_delta" ,
577+ },
578+ ),
579+ StreamingChunk (
580+ content = " tool. Let's use the \" " ,
581+ meta = {
582+ "delta" : {"text" : " tool. Let's use the \" " , "type" : "text_delta" },
583+ "index" : 0 ,
584+ "type" : "content_block_delta" ,
585+ },
586+ ),
587+ StreamingChunk (
588+ content = 'hello_world" function to accomplish this task.' ,
589+ meta = {
590+ "delta" : {"text" : 'hello_world" function to accomplish this task.' , "type" : "text_delta" },
591+ "index" : 0 ,
592+ "type" : "content_block_delta" ,
593+ },
594+ ),
595+ StreamingChunk (
596+ content = "" ,
597+ meta = {
598+ "content_block" : {
599+ "id" : "toolu_014yzmmeNPAuTuiN92qV6LKr" ,
600+ "input" : {},
601+ "name" : "hello_world" ,
602+ "type" : "tool_use" ,
603+ },
604+ "index" : 1 ,
605+ "type" : "content_block_start" ,
606+ },
607+ ),
608+ StreamingChunk (
609+ content = "" ,
610+ meta = {
611+ "delta" : {"partial_json" : "" , "type" : "input_json_delta" },
612+ "index" : 1 ,
613+ "type" : "content_block_delta" ,
614+ },
615+ ),
616+ StreamingChunk (
617+ content = "" ,
618+ meta = {
619+ "delta" : {"stop_reason" : "tool_use" , "stop_sequence" : None },
620+ "type" : "message_delta" ,
621+ "usage" : {
622+ "cache_creation_input_tokens" : None ,
623+ "cache_read_input_tokens" : None ,
624+ "input_tokens" : None ,
625+ "output_tokens" : 69 ,
626+ "server_tool_use" : None ,
627+ },
628+ },
629+ ),
630+ ]
631+
632+ component = AnthropicChatGenerator (api_key = Secret .from_token ("test-api-key" ))
633+ message = component ._convert_streaming_chunks_to_chat_message (chunks , model = "claude-3-sonnet" )
634+
635+ # Verify the message content
636+ assert message .text == (
637+ 'Certainly! I can help you print "Hello World" using the available tool. Let\' s use the "hello_world" '
638+ "function to accomplish this task."
639+ )
640+
641+ # Verify tool calls
642+ assert len (message .tool_calls ) == 1
643+ tool_call = message .tool_calls [0 ]
644+ assert tool_call .id == "toolu_014yzmmeNPAuTuiN92qV6LKr"
645+ assert tool_call .tool_name == "hello_world"
646+ assert tool_call .arguments == {}
647+
648+ # Verify meta information
649+ assert message ._meta ["model" ] == "claude-3-sonnet"
650+ assert message ._meta ["index" ] == 0
651+ assert message ._meta ["finish_reason" ] == "tool_use"
652+ assert message ._meta ["usage" ] == {
653+ "cache_creation_input_tokens" : None ,
654+ "cache_read_input_tokens" : None ,
655+ "completion_tokens" : 69 ,
656+ "prompt_tokens" : None ,
657+ "server_tool_use" : None ,
658+ }
659+
536660 def test_serde_in_pipeline (self ):
537661 tool = Tool (name = "name" , description = "description" , parameters = {"x" : {"type" : "string" }}, function = print )
538662
@@ -970,6 +1094,47 @@ def test_live_run_with_tools_streaming(self, tools):
9701094 assert len (final_message .text ) > 0
9711095 assert "paris" in final_message .text .lower ()
9721096
1097+ @pytest .mark .skipif (
1098+ not os .environ .get ("ANTHROPIC_API_KEY" , None ),
1099+ reason = "Export an env var called ANTHROPIC_API_KEY containing the Anthropic API key to run this test." ,
1100+ )
1101+ @pytest .mark .integration
1102+ def test_live_run_with_tool_with_no_args_streaming (self , tool_with_no_parameters ):
1103+ """
1104+ Integration test that the AnthropicChatGenerator component can run with a tool that has no arguments and
1105+ streaming.
1106+ """
1107+ initial_messages = [ChatMessage .from_user ("Print Hello World using the print hello world tool." )]
1108+ component = AnthropicChatGenerator (tools = [tool_with_no_parameters ], streaming_callback = print_streaming_chunk )
1109+ results = component .run (messages = initial_messages )
1110+
1111+ assert len (results ["replies" ]) == 1
1112+ message = results ["replies" ][0 ]
1113+
1114+ # this is Anthropic thinking message prior to tool call
1115+ assert message .text is not None
1116+
1117+ # now we have the tool call
1118+ assert message .tool_calls
1119+ tool_call = message .tool_call
1120+ assert isinstance (tool_call , ToolCall )
1121+ assert tool_call .id is not None
1122+ assert tool_call .tool_name == "hello_world"
1123+ assert tool_call .arguments == {}
1124+ assert message .meta ["finish_reason" ] == "tool_use"
1125+
1126+ new_messages = [
1127+ * initial_messages ,
1128+ message ,
1129+ ChatMessage .from_tool (tool_result = "Hello World!" , origin = tool_call ),
1130+ ]
1131+ results = component .run (new_messages )
1132+ assert len (results ["replies" ]) == 1
1133+ final_message = results ["replies" ][0 ]
1134+ assert not final_message .tool_calls
1135+ assert len (final_message .text ) > 0
1136+ assert "hello" in final_message .text .lower ()
1137+
9731138 @pytest .mark .skipif (
9741139 not os .environ .get ("ANTHROPIC_API_KEY" , None ),
9751140 reason = "Export an env var called ANTHROPIC_API_KEY containing the Anthropic API key to run this test." ,
0 commit comments