30
30
)
31
31
32
32
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
+
33
48
@pytest .fixture
34
49
def tools ():
35
50
tool_parameters = {"type" : "object" , "properties" : {"city" : {"type" : "string" }}, "required" : ["city" ]}
@@ -39,7 +54,6 @@ def tools():
39
54
parameters = tool_parameters ,
40
55
function = lambda x : x ,
41
56
)
42
-
43
57
return [tool ]
44
58
45
59
@@ -533,6 +547,116 @@ def test_convert_streaming_chunks_to_chat_message_malformed_json(self, caplog):
533
547
with caplog .at_level (logging .WARNING ):
534
548
assert "Anthropic returned a malformed JSON string" in caplog .text
535
549
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
+
536
660
def test_serde_in_pipeline (self ):
537
661
tool = Tool (name = "name" , description = "description" , parameters = {"x" : {"type" : "string" }}, function = print )
538
662
@@ -970,6 +1094,47 @@ def test_live_run_with_tools_streaming(self, tools):
970
1094
assert len (final_message .text ) > 0
971
1095
assert "paris" in final_message .text .lower ()
972
1096
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
+
973
1138
@pytest .mark .skipif (
974
1139
not os .environ .get ("ANTHROPIC_API_KEY" , None ),
975
1140
reason = "Export an env var called ANTHROPIC_API_KEY containing the Anthropic API key to run this test." ,
0 commit comments