3
3
4
4
import pytest
5
5
from anthropic import AsyncAnthropic
6
+ from anthropic .lib .streaming import TextEvent
7
+ from anthropic .types import (
8
+ ContentBlockStopEvent ,
9
+ Message ,
10
+ MessageDeltaUsage ,
11
+ MessageStopEvent ,
12
+ RawContentBlockDeltaEvent ,
13
+ RawContentBlockStartEvent ,
14
+ RawMessageDeltaEvent ,
15
+ RawMessageStartEvent ,
16
+ TextBlock ,
17
+ TextDelta ,
18
+ Usage ,
19
+ )
20
+ from anthropic .types .raw_message_delta_event import Delta
6
21
7
22
from semantic_kernel .connectors .ai .anthropic .prompt_execution_settings .anthropic_prompt_execution_settings import (
8
23
AnthropicChatPromptExecutionSettings ,
12
27
from semantic_kernel .connectors .ai .open_ai .prompt_execution_settings .open_ai_prompt_execution_settings import (
13
28
OpenAIChatPromptExecutionSettings ,
14
29
)
30
+ from semantic_kernel .contents .chat_history import ChatHistory
15
31
from semantic_kernel .contents .chat_message_content import ChatMessageContent
16
32
from semantic_kernel .exceptions import ServiceInitializationError , ServiceResponseException
17
33
from semantic_kernel .functions .kernel_arguments import KernelArguments
@@ -26,43 +42,120 @@ def mock_settings() -> AnthropicChatPromptExecutionSettings:
26
42
@pytest .fixture
27
43
def mock_anthropic_client_completion () -> AsyncAnthropic :
28
44
client = MagicMock (spec = AsyncAnthropic )
45
+
29
46
chat_completion_response = AsyncMock ()
30
-
31
- content = [MagicMock (finish_reason = "stop" , message = MagicMock (role = "assistant" , content = "Test" ))]
32
- chat_completion_response .content = content
47
+ chat_completion_response .content = [TextBlock (text = "Hello! It's nice to meet you." , type = "text" )]
48
+ chat_completion_response .id = "test_id"
49
+ chat_completion_response .model = "claude-3-opus-20240229"
50
+ chat_completion_response .role = "assistant"
51
+ chat_completion_response .stop_reason = "end_turn"
52
+ chat_completion_response .stop_sequence = None
53
+ chat_completion_response .type = "message"
54
+ chat_completion_response .usage = Usage (input_tokens = 114 , output_tokens = 75 )
33
55
34
56
# Create a MagicMock for the messages attribute
35
57
messages_mock = MagicMock ()
36
- messages_mock .create = chat_completion_response
58
+ messages_mock .create = AsyncMock ( return_value = chat_completion_response )
37
59
38
60
# Assign the messages_mock to the client.messages attribute
39
61
client .messages = messages_mock
62
+
40
63
return client
41
64
42
65
43
66
@pytest .fixture
44
67
def mock_anthropic_client_completion_stream () -> AsyncAnthropic :
45
68
client = MagicMock (spec = AsyncAnthropic )
46
- chat_completion_response = MagicMock ()
47
69
48
- content = [
49
- MagicMock (finish_reason = "stop" , delta = MagicMock (role = "assistant" , content = "Test" )),
50
- MagicMock (finish_reason = "stop" , delta = MagicMock (role = "assistant" , content = "Test" , tool_calls = None )),
70
+ # Create MagicMock instances for each event with the spec set to the appropriate class
71
+ mock_raw_message_start_event = MagicMock (spec = RawMessageStartEvent )
72
+ mock_raw_message_start_event .message = MagicMock (spec = Message )
73
+ mock_raw_message_start_event .message .id = "test_message_id"
74
+ mock_raw_message_start_event .message .content = []
75
+ mock_raw_message_start_event .message .model = "claude-3-opus-20240229"
76
+ mock_raw_message_start_event .message .role = "assistant"
77
+ mock_raw_message_start_event .message .stop_reason = None
78
+ mock_raw_message_start_event .message .stop_sequence = None
79
+ mock_raw_message_start_event .message .type = "message"
80
+ mock_raw_message_start_event .message .usage = MagicMock (spec = Usage )
81
+ mock_raw_message_start_event .message .usage .input_tokens = 41
82
+ mock_raw_message_start_event .message .usage .output_tokens = 3
83
+ mock_raw_message_start_event .type = "message_start"
84
+
85
+ mock_raw_content_block_start_event = MagicMock (spec = RawContentBlockStartEvent )
86
+ mock_raw_content_block_start_event .content_block = MagicMock (spec = TextBlock )
87
+ mock_raw_content_block_start_event .content_block .text = ""
88
+ mock_raw_content_block_start_event .content_block .type = "text"
89
+ mock_raw_content_block_start_event .index = 0
90
+ mock_raw_content_block_start_event .type = "content_block_start"
91
+
92
+ mock_raw_content_block_delta_event = MagicMock (spec = RawContentBlockDeltaEvent )
93
+ mock_raw_content_block_delta_event .delta = MagicMock (spec = TextDelta )
94
+ mock_raw_content_block_delta_event .delta .text = "Hello! It"
95
+ mock_raw_content_block_delta_event .delta .type = "text_delta"
96
+ mock_raw_content_block_delta_event .index = 0
97
+ mock_raw_content_block_delta_event .type = "content_block_delta"
98
+
99
+ mock_text_event = MagicMock (spec = TextEvent )
100
+ mock_text_event .type = "text"
101
+ mock_text_event .text = "Hello! It"
102
+ mock_text_event .snapshot = "Hello! It"
103
+
104
+ mock_content_block_stop_event = MagicMock (spec = ContentBlockStopEvent )
105
+ mock_content_block_stop_event .index = 0
106
+ mock_content_block_stop_event .type = "content_block_stop"
107
+ mock_content_block_stop_event .content_block = MagicMock (spec = TextBlock )
108
+ mock_content_block_stop_event .content_block .text = "Hello! It's nice to meet you."
109
+ mock_content_block_stop_event .content_block .type = "text"
110
+
111
+ mock_raw_message_delta_event = MagicMock (spec = RawMessageDeltaEvent )
112
+ mock_raw_message_delta_event .delta = MagicMock (spec = Delta )
113
+ mock_raw_message_delta_event .delta .stop_reason = "end_turn"
114
+ mock_raw_message_delta_event .delta .stop_sequence = None
115
+ mock_raw_message_delta_event .type = "message_delta"
116
+ mock_raw_message_delta_event .usage = MagicMock (spec = MessageDeltaUsage )
117
+ mock_raw_message_delta_event .usage .output_tokens = 84
118
+
119
+ mock_message_stop_event = MagicMock (spec = MessageStopEvent )
120
+ mock_message_stop_event .type = "message_stop"
121
+ mock_message_stop_event .message = MagicMock (spec = Message )
122
+ mock_message_stop_event .message .id = "test_message_stop_id"
123
+ mock_message_stop_event .message .content = [MagicMock (spec = TextBlock )]
124
+ mock_message_stop_event .message .content [0 ].text = "Hello! It's nice to meet you."
125
+ mock_message_stop_event .message .content [0 ].type = "text"
126
+ mock_message_stop_event .message .model = "claude-3-opus-20240229"
127
+ mock_message_stop_event .message .role = "assistant"
128
+ mock_message_stop_event .message .stop_reason = "end_turn"
129
+ mock_message_stop_event .message .stop_sequence = None
130
+ mock_message_stop_event .message .type = "message"
131
+ mock_message_stop_event .message .usage = MagicMock (spec = Usage )
132
+ mock_message_stop_event .message .usage .input_tokens = 41
133
+ mock_message_stop_event .message .usage .output_tokens = 84
134
+
135
+ # Combine all mock events into a list
136
+ stream_events = [
137
+ mock_raw_message_start_event ,
138
+ mock_raw_content_block_start_event ,
139
+ mock_raw_content_block_delta_event ,
140
+ mock_text_event ,
141
+ mock_content_block_stop_event ,
142
+ mock_raw_message_delta_event ,
143
+ mock_message_stop_event ,
51
144
]
52
- chat_completion_response .content = content
53
145
54
- chat_completion_response_empty = MagicMock ()
55
- chat_completion_response_empty .content = []
146
+ async def async_generator ():
147
+ for event in stream_events :
148
+ yield event
149
+
150
+ # Create an AsyncMock for the stream
151
+ stream_mock = AsyncMock ()
152
+ stream_mock .__aenter__ .return_value = async_generator ()
56
153
57
- # Create a MagicMock for the messages attribute
58
154
messages_mock = MagicMock ()
59
- messages_mock .stream = chat_completion_response
60
-
61
- generator_mock = MagicMock ()
62
- generator_mock .__aiter__ .return_value = [chat_completion_response_empty , chat_completion_response ]
63
-
155
+ messages_mock .stream .return_value = stream_mock
156
+
64
157
client .messages = messages_mock
65
-
158
+
66
159
return client
67
160
68
161
@@ -72,7 +165,10 @@ async def test_complete_chat_contents(
72
165
mock_settings : AnthropicChatPromptExecutionSettings ,
73
166
mock_anthropic_client_completion : AsyncAnthropic ,
74
167
):
75
- chat_history = MagicMock ()
168
+ chat_history = ChatHistory ()
169
+ chat_history .add_user_message ("test_user_message" )
170
+ chat_history .add_assistant_message ("test_assistant_message" )
171
+
76
172
arguments = KernelArguments ()
77
173
chat_completion_base = AnthropicChatCompletion (
78
174
ai_model_id = "test_model_id" , service_id = "test" , api_key = "" , async_client = mock_anthropic_client_completion
@@ -81,6 +177,7 @@ async def test_complete_chat_contents(
81
177
content : list [ChatMessageContent ] = await chat_completion_base .get_chat_message_contents (
82
178
chat_history = chat_history , settings = mock_settings , kernel = kernel , arguments = arguments
83
179
)
180
+
84
181
assert content is not None
85
182
86
183
0 commit comments