chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,400 @@
# Copyright (c) Microsoft. All rights reserved.
from collections.abc import AsyncGenerator
from unittest.mock import AsyncMock, MagicMock
import pytest
from anthropic import AsyncAnthropic
from anthropic.lib.streaming import TextEvent
from anthropic.lib.streaming._types import InputJsonEvent
from anthropic.types import (
ContentBlockStopEvent,
InputJSONDelta,
Message,
MessageDeltaUsage,
MessageStopEvent,
RawContentBlockDeltaEvent,
RawContentBlockStartEvent,
RawMessageDeltaEvent,
RawMessageStartEvent,
TextBlock,
TextDelta,
ToolUseBlock,
Usage,
)
from anthropic.types.raw_message_delta_event import Delta
from semantic_kernel.connectors.ai.anthropic.prompt_execution_settings.anthropic_prompt_execution_settings import (
AnthropicChatPromptExecutionSettings,
)
from semantic_kernel.contents.chat_message_content import (
ChatMessageContent,
FunctionCallContent,
FunctionResultContent,
TextContent,
)
from semantic_kernel.contents.const import ContentTypes
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent, StreamingTextContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.contents.utils.finish_reason import FinishReason
@pytest.fixture
def mock_tool_calls_message() -> ChatMessageContent:
return ChatMessageContent(
ai_model_id="claude-3-opus-20240229",
metadata={},
content_type="message",
role=AuthorRole.ASSISTANT,
name=None,
items=[
TextContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type="text",
text="<thinking></thinking>",
encoding=None,
),
FunctionCallContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type=ContentTypes.FUNCTION_CALL_CONTENT,
id="test_function_call_content",
index=1,
name="math-Add",
function_name="Add",
plugin_name="math",
arguments={"input": 3, "amount": 3},
),
],
encoding=None,
finish_reason=FinishReason.TOOL_CALLS,
)
@pytest.fixture
def mock_parallel_tool_calls_message() -> ChatMessageContent:
return ChatMessageContent(
ai_model_id="claude-3-opus-20240229",
metadata={},
content_type="message",
role=AuthorRole.ASSISTANT,
name=None,
items=[
TextContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type="text",
text="<thinking></thinking>",
encoding=None,
),
FunctionCallContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type=ContentTypes.FUNCTION_CALL_CONTENT,
id="test_function_call_content_1",
index=1,
name="math-Add",
function_name="Add",
plugin_name="math",
arguments={"input": 3, "amount": 3},
),
FunctionCallContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type=ContentTypes.FUNCTION_CALL_CONTENT,
id="test_function_call_content_2",
index=1,
name="math-Subtract",
function_name="Subtract",
plugin_name="math",
arguments={"input": 6, "amount": 3},
),
],
encoding=None,
finish_reason=FinishReason.TOOL_CALLS,
)
@pytest.fixture
def mock_streaming_tool_calls_message() -> list:
stream_events = [
RawMessageStartEvent(
message=Message(
id="test_message_id",
content=[],
model="claude-3-opus-20240229",
role="assistant",
stop_reason=None,
stop_sequence=None,
type="message",
usage=Usage(input_tokens=1720, output_tokens=2),
),
type="message_start",
),
RawContentBlockStartEvent(content_block=TextBlock(text="", type="text"), index=0, type="content_block_start"),
RawContentBlockDeltaEvent(
delta=TextDelta(text="<thinking>", type="text_delta"), index=0, type="content_block_delta"
),
TextEvent(type="text", text="<thinking>", snapshot="<thinking>"),
RawContentBlockDeltaEvent(
delta=TextDelta(text="</thinking>", type="text_delta"), index=0, type="content_block_delta"
),
TextEvent(type="text", text="</thinking>", snapshot="<thinking></thinking>"),
ContentBlockStopEvent(
index=0, type="content_block_stop", content_block=TextBlock(text="<thinking></thinking>", type="text")
),
RawContentBlockStartEvent(
content_block=ToolUseBlock(id="test_tool_use_message_id", input={}, name="math-Add", type="tool_use"),
index=1,
type="content_block_start",
),
RawContentBlockDeltaEvent(
delta=InputJSONDelta(partial_json='{"input": 3, "amount": 3}', type="input_json_delta"),
index=1,
type="content_block_delta",
),
InputJsonEvent(type="input_json", partial_json='{"input": 3, "amount": 3}', snapshot={"input": 3, "amount": 3}),
ContentBlockStopEvent(
index=1,
type="content_block_stop",
content_block=ToolUseBlock(
id="test_tool_use_block_id", input={"input": 3, "amount": 3}, name="math-Add", type="tool_use"
),
),
RawMessageDeltaEvent(
delta=Delta(stop_reason="tool_use", stop_sequence=None),
type="message_delta",
usage=MessageDeltaUsage(output_tokens=159),
),
MessageStopEvent(
type="message_stop",
message=Message(
id="test_message_id",
content=[
TextBlock(text="<thinking></thinking>", type="text"),
ToolUseBlock(
id="test_tool_use_block_id", input={"input": 3, "amount": 3}, name="math-Add", type="tool_use"
),
],
model="claude-3-opus-20240229",
role="assistant",
stop_reason="tool_use",
stop_sequence=None,
type="message",
usage=Usage(input_tokens=100, output_tokens=100),
),
),
]
async def async_generator():
for event in stream_events:
yield event
stream_mock = AsyncMock()
stream_mock.__aenter__.return_value = async_generator()
return stream_mock
@pytest.fixture
def mock_tool_call_result_message() -> ChatMessageContent:
return ChatMessageContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type="message",
role=AuthorRole.TOOL,
name=None,
items=[
FunctionResultContent(
id="test_function_call_content",
result=6,
)
],
encoding=None,
finish_reason=FinishReason.TOOL_CALLS,
)
@pytest.fixture
def mock_parallel_tool_call_result_message() -> ChatMessageContent:
return ChatMessageContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type="message",
role=AuthorRole.TOOL,
name=None,
items=[
FunctionResultContent(
id="test_function_call_content_1",
result=6,
),
FunctionResultContent(
id="test_function_call_content_2",
result=3,
),
],
encoding=None,
finish_reason=FinishReason.TOOL_CALLS,
)
@pytest.fixture
def mock_streaming_chat_message_content() -> StreamingChatMessageContent:
return StreamingChatMessageContent(
choice_index=0,
ai_model_id="claude-3-opus-20240229",
metadata={},
role=AuthorRole.ASSISTANT,
name=None,
items=[
StreamingTextContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type="text",
text="<thinking></thinking>",
encoding=None,
choice_index=0,
),
FunctionCallContent(
inner_content=None,
ai_model_id=None,
metadata={},
content_type=ContentTypes.FUNCTION_CALL_CONTENT,
id="tool_id",
index=0,
name="math-Add",
function_name="Add",
plugin_name="math",
arguments='{"input": 3, "amount": 3}',
),
],
encoding=None,
finish_reason=FinishReason.TOOL_CALLS,
)
@pytest.fixture
def mock_settings() -> AnthropicChatPromptExecutionSettings:
return AnthropicChatPromptExecutionSettings()
@pytest.fixture
def mock_chat_message_response() -> Message:
return Message(
id="test_message_id",
content=[TextBlock(text="Hello, how are you?", type="text")],
model="claude-3-opus-20240229",
role="assistant",
stop_reason="end_turn",
stop_sequence=None,
type="message",
usage=Usage(input_tokens=10, output_tokens=10),
)
@pytest.fixture
def mock_streaming_message_response() -> AsyncGenerator:
raw_message_start_event = RawMessageStartEvent(
message=Message(
id="test_message_id",
content=[],
model="claude-3-opus-20240229",
role="assistant",
stop_reason=None,
stop_sequence=None,
type="message",
usage=Usage(input_tokens=41, output_tokens=3),
),
type="message_start",
)
raw_content_block_start_event = RawContentBlockStartEvent(
content_block=TextBlock(text="", type="text"),
index=0,
type="content_block_start",
)
raw_content_block_delta_event = RawContentBlockDeltaEvent(
delta=TextDelta(text="Hello! It", type="text_delta"),
index=0,
type="content_block_delta",
)
text_event = TextEvent(
type="text",
text="Hello! It",
snapshot="Hello! It",
)
content_block_stop_event = ContentBlockStopEvent(
index=0,
type="content_block_stop",
content_block=TextBlock(text="Hello! It's nice to meet you.", type="text"),
)
raw_message_delta_event = RawMessageDeltaEvent(
delta=Delta(stop_reason="end_turn", stop_sequence=None),
type="message_delta",
usage=MessageDeltaUsage(output_tokens=84),
)
message_stop_event = MessageStopEvent(
type="message_stop",
message=Message(
id="test_message_stop_id",
content=[TextBlock(text="Hello! It's nice to meet you.", type="text")],
model="claude-3-opus-20240229",
role="assistant",
stop_reason="end_turn",
stop_sequence=None,
type="message",
usage=Usage(input_tokens=41, output_tokens=84),
),
)
# Combine all mock events into a list
stream_events = [
raw_message_start_event,
raw_content_block_start_event,
raw_content_block_delta_event,
text_event,
content_block_stop_event,
raw_message_delta_event,
message_stop_event,
]
async def async_generator():
for event in stream_events:
yield event
# Create an AsyncMock for the stream
stream_mock = AsyncMock()
stream_mock.__aenter__.return_value = async_generator()
return stream_mock
@pytest.fixture
def mock_anthropic_client_completion(mock_chat_message_response: Message) -> AsyncAnthropic:
client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.create = AsyncMock(return_value=mock_chat_message_response)
client.messages = messages_mock
return client
@pytest.fixture
def mock_anthropic_client_completion_stream(mock_streaming_message_response: AsyncGenerator) -> AsyncAnthropic:
client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.stream.return_value = mock_streaming_message_response
client.messages = messages_mock
return client
@@ -0,0 +1,549 @@
# Copyright (c) Microsoft. All rights reserved.
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from anthropic import AsyncAnthropic
from anthropic.types import Message
from semantic_kernel.connectors.ai.anthropic.prompt_execution_settings.anthropic_prompt_execution_settings import (
AnthropicChatPromptExecutionSettings,
)
from semantic_kernel.connectors.ai.anthropic.services.anthropic_chat_completion import AnthropicChatCompletion
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (
OpenAIChatPromptExecutionSettings,
)
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.chat_message_content import ChatMessageContent, FunctionCallContent, TextContent
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.exceptions import (
ServiceInitializationError,
ServiceInvalidExecutionSettingsError,
ServiceResponseException,
)
from semantic_kernel.exceptions.service_exceptions import ServiceInvalidRequestError
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel import Kernel
async def test_complete_chat_contents(
kernel: Kernel,
mock_settings: AnthropicChatPromptExecutionSettings,
mock_chat_message_response: Message,
):
client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.create = AsyncMock(return_value=mock_chat_message_response)
client.messages = messages_mock
chat_history = ChatHistory()
chat_history.add_user_message("test_user_message")
arguments = KernelArguments()
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=client
)
content: list[ChatMessageContent] = await chat_completion_base.get_chat_message_contents(
chat_history=chat_history, settings=mock_settings, kernel=kernel, arguments=arguments
)
assert len(content) > 0
assert content[0].content != ""
assert content[0].role == AuthorRole.ASSISTANT
mock_message_text_content = ChatMessageContent(role=AuthorRole.ASSISTANT, items=[TextContent(text="test")])
mock_message_function_call = ChatMessageContent(
role=AuthorRole.ASSISTANT,
items=[
FunctionCallContent(
name="test",
arguments={"key": "test"},
)
],
)
@pytest.mark.parametrize(
"function_choice_behavior,model_responses,expected_result",
[
pytest.param(
FunctionChoiceBehavior.Auto(),
[[mock_message_function_call], [mock_message_text_content]],
TextContent,
id="auto",
),
pytest.param(
FunctionChoiceBehavior.Auto(auto_invoke=False),
[[mock_message_function_call]],
FunctionCallContent,
id="auto_none_invoke",
),
pytest.param(
FunctionChoiceBehavior.Required(auto_invoke=False),
[[mock_message_function_call]],
FunctionCallContent,
id="required_none_invoke",
),
],
)
async def test_complete_chat_contents_function_call_behavior_tool_call(
kernel: Kernel,
mock_settings: AnthropicChatPromptExecutionSettings,
function_choice_behavior: FunctionChoiceBehavior,
model_responses,
expected_result,
):
kernel.add_function("test", kernel_function(lambda key: "test", name="test"))
mock_settings.function_choice_behavior = function_choice_behavior
arguments = KernelArguments()
chat_completion_base = AnthropicChatCompletion(ai_model_id="test_model_id", service_id="test", api_key="")
with (
patch.object(chat_completion_base, "_inner_get_chat_message_contents", side_effect=model_responses),
):
response: list[ChatMessageContent] = await chat_completion_base.get_chat_message_contents(
chat_history=ChatHistory(system_message="Test"), settings=mock_settings, kernel=kernel, arguments=arguments
)
assert all(isinstance(content, expected_result) for content in response[0].items)
async def test_complete_chat_contents_function_call_behavior_without_kernel(
mock_settings: AnthropicChatPromptExecutionSettings,
mock_anthropic_client_completion: AsyncAnthropic,
):
chat_history = MagicMock()
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=mock_anthropic_client_completion
)
mock_settings.function_choice_behavior = FunctionChoiceBehavior.Auto()
with pytest.raises(ServiceInvalidExecutionSettingsError):
await chat_completion_base.get_chat_message_contents(chat_history=chat_history, settings=mock_settings)
async def test_complete_chat_stream_contents(
kernel: Kernel,
mock_settings: AnthropicChatPromptExecutionSettings,
mock_streaming_message_response,
):
client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.stream.return_value = mock_streaming_message_response
client.messages = messages_mock
chat_history = ChatHistory()
chat_history.add_user_message("test_user_message")
arguments = KernelArguments()
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id",
service_id="test",
api_key="",
async_client=client,
)
async for content in chat_completion_base.get_streaming_chat_message_contents(
chat_history, mock_settings, kernel=kernel, arguments=arguments
):
assert content is not None
mock_message_function_call = StreamingChatMessageContent(
role=AuthorRole.ASSISTANT, items=[FunctionCallContent(name="test")], choice_index="0"
)
mock_message_text_content = StreamingChatMessageContent(
role=AuthorRole.ASSISTANT, items=[TextContent(text="test")], choice_index="0"
)
@pytest.mark.parametrize(
"function_choice_behavior,model_responses,expected_result",
[
pytest.param(
FunctionChoiceBehavior.Auto(),
[[mock_message_function_call], [mock_message_text_content]],
TextContent,
id="auto",
),
pytest.param(
FunctionChoiceBehavior.Auto(auto_invoke=False),
[[mock_message_function_call]],
FunctionCallContent,
id="auto_none_invoke",
),
pytest.param(
FunctionChoiceBehavior.Required(auto_invoke=False),
[[mock_message_function_call]],
FunctionCallContent,
id="required_none_invoke",
),
pytest.param(FunctionChoiceBehavior.NoneInvoke(), [[mock_message_text_content]], TextContent, id="none"),
],
)
async def test_complete_chat_contents_streaming_function_call_behavior_tool_call(
kernel: Kernel,
mock_settings: AnthropicChatPromptExecutionSettings,
function_choice_behavior: FunctionChoiceBehavior,
model_responses,
expected_result,
):
mock_settings.function_choice_behavior = function_choice_behavior
# Mock sequence of model responses
generator_mocks = []
for mock_message in model_responses:
generator_mock = MagicMock()
generator_mock.__aiter__.return_value = [mock_message]
generator_mocks.append(generator_mock)
arguments = KernelArguments()
chat_completion_base = AnthropicChatCompletion(ai_model_id="test_model_id", service_id="test", api_key="")
with patch.object(chat_completion_base, "_inner_get_streaming_chat_message_contents", side_effect=generator_mocks):
messages = []
async for chunk in chat_completion_base.get_streaming_chat_message_contents(
chat_history=ChatHistory(system_message="Test"), settings=mock_settings, kernel=kernel, arguments=arguments
):
messages.append(chunk)
response = messages[-1]
assert all(isinstance(content, expected_result) for content in response[0].items)
async def test_anthropic_sdk_exception(kernel: Kernel, mock_settings: AnthropicChatPromptExecutionSettings):
client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.create.side_effect = Exception("Test Exception")
client.messages = messages_mock
chat_history = MagicMock()
arguments = KernelArguments()
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=client
)
with pytest.raises(ServiceResponseException):
await chat_completion_base.get_chat_message_contents(
chat_history=chat_history, settings=mock_settings, kernel=kernel, arguments=arguments
)
async def test_anthropic_sdk_exception_streaming(kernel: Kernel, mock_settings: AnthropicChatPromptExecutionSettings):
client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.stream.side_effect = Exception("Test Exception")
client.messages = messages_mock
chat_history = MagicMock()
arguments = KernelArguments()
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=client
)
with pytest.raises(ServiceResponseException):
async for content in chat_completion_base.get_streaming_chat_message_contents(
chat_history, mock_settings, kernel=kernel, arguments=arguments
):
assert content is not None
def test_anthropic_chat_completion_init(anthropic_unit_test_env) -> None:
# Test successful initialization
anthropic_chat_completion = AnthropicChatCompletion()
assert anthropic_chat_completion.ai_model_id == anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL_ID"]
assert isinstance(anthropic_chat_completion, ChatCompletionClientBase)
@pytest.mark.parametrize("exclude_list", [["ANTHROPIC_API_KEY"]], indirect=True)
def test_anthropic_chat_completion_init_with_empty_api_key(anthropic_unit_test_env) -> None:
ai_model_id = "test_model_id"
with pytest.raises(ServiceInitializationError):
AnthropicChatCompletion(
ai_model_id=ai_model_id,
env_file_path="test.env",
)
@pytest.mark.parametrize("exclude_list", [["ANTHROPIC_CHAT_MODEL_ID"]], indirect=True)
def test_anthropic_chat_completion_init_with_empty_model_id(anthropic_unit_test_env) -> None:
with pytest.raises(ServiceInitializationError):
AnthropicChatCompletion(
env_file_path="test.env",
)
def test_prompt_execution_settings_class(anthropic_unit_test_env):
anthropic_chat_completion = AnthropicChatCompletion()
prompt_execution_settings = anthropic_chat_completion.get_prompt_execution_settings_class()
assert prompt_execution_settings == AnthropicChatPromptExecutionSettings
async def test_with_different_execution_settings(kernel: Kernel, mock_anthropic_client_completion: MagicMock):
chat_history = MagicMock()
settings = OpenAIChatPromptExecutionSettings(temperature=0.2)
arguments = KernelArguments()
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=mock_anthropic_client_completion
)
await chat_completion_base.get_chat_message_contents(
chat_history=chat_history, settings=settings, kernel=kernel, arguments=arguments
)
assert mock_anthropic_client_completion.messages.create.call_args.kwargs["temperature"] == 0.2
async def test_with_different_execution_settings_stream(
kernel: Kernel, mock_anthropic_client_completion_stream: MagicMock
):
chat_history = MagicMock()
settings = OpenAIChatPromptExecutionSettings(temperature=0.2, seed=2)
arguments = KernelArguments()
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id",
service_id="test",
api_key="",
async_client=mock_anthropic_client_completion_stream,
)
async for chunk in chat_completion_base.get_streaming_chat_message_contents(
chat_history, settings, kernel=kernel, arguments=arguments
):
assert chunk is not None
assert mock_anthropic_client_completion_stream.messages.stream.call_args.kwargs["temperature"] == 0.2
async def test_prepare_chat_history_for_request_with_system_message(mock_anthropic_client_completion_stream: MagicMock):
chat_history = ChatHistory()
chat_history.add_system_message("System message")
chat_history.add_user_message("User message")
chat_history.add_assistant_message("Assistant message")
chat_history.add_system_message("Another system message")
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id",
service_id="test",
api_key="",
async_client=mock_anthropic_client_completion_stream,
)
remaining_messages, system_message_content = chat_completion_base._prepare_chat_history_for_request(
chat_history, role_key="role", content_key="content"
)
assert system_message_content == "System message"
assert remaining_messages == [
{"role": AuthorRole.USER, "content": "User message"},
{"role": AuthorRole.ASSISTANT, "content": [{"type": "text", "text": "Assistant message"}]},
]
assert not any(msg["role"] == AuthorRole.SYSTEM for msg in remaining_messages)
async def test_prepare_chat_history_for_request_with_tool_message(
mock_anthropic_client_completion_stream: MagicMock,
mock_tool_calls_message: ChatMessageContent,
mock_tool_call_result_message: ChatMessageContent,
):
chat_history = ChatHistory()
chat_history.add_user_message("What is 3+3?")
chat_history.add_message(mock_tool_calls_message)
chat_history.add_message(mock_tool_call_result_message)
chat_completion_client = AnthropicChatCompletion(
ai_model_id="test_model_id",
service_id="test",
api_key="",
async_client=mock_anthropic_client_completion_stream,
)
remaining_messages, system_message_content = chat_completion_client._prepare_chat_history_for_request(
chat_history, role_key="role", content_key="content"
)
assert system_message_content is None
assert remaining_messages == [
{"role": AuthorRole.USER, "content": "What is 3+3?"},
{
"role": AuthorRole.ASSISTANT,
"content": [
{"type": "text", "text": mock_tool_calls_message.items[0].text},
{
"type": "tool_use",
"id": mock_tool_calls_message.items[1].id,
"name": mock_tool_calls_message.items[1].name,
"input": mock_tool_calls_message.items[1].arguments,
},
],
},
{
"role": AuthorRole.USER,
"content": [
{
"type": "tool_result",
"tool_use_id": mock_tool_call_result_message.items[0].id,
"content": str(mock_tool_call_result_message.items[0].result),
}
],
},
]
async def test_prepare_chat_history_for_request_with_parallel_tool_message(
mock_anthropic_client_completion_stream: MagicMock,
mock_parallel_tool_calls_message: ChatMessageContent,
mock_parallel_tool_call_result_message: ChatMessageContent,
):
chat_history = ChatHistory()
chat_history.add_user_message("What is 3+3?")
chat_history.add_message(mock_parallel_tool_calls_message)
chat_history.add_message(mock_parallel_tool_call_result_message)
chat_completion_client = AnthropicChatCompletion(
ai_model_id="test_model_id",
service_id="test",
api_key="",
async_client=mock_anthropic_client_completion_stream,
)
remaining_messages, system_message_content = chat_completion_client._prepare_chat_history_for_request(
chat_history, role_key="role", content_key="content"
)
assert system_message_content is None
assert remaining_messages == [
{"role": AuthorRole.USER, "content": "What is 3+3?"},
{
"role": AuthorRole.ASSISTANT,
"content": [
{"type": "text", "text": mock_parallel_tool_calls_message.items[0].text},
*[
{
"type": "tool_use",
"id": function_call_content.id,
"name": function_call_content.name,
"input": function_call_content.arguments,
}
for function_call_content in mock_parallel_tool_calls_message.items[1:]
],
],
},
{
"role": AuthorRole.USER,
"content": [
{
"type": "tool_result",
"tool_use_id": function_result_content.id,
"content": str(function_result_content.result),
}
for function_result_content in mock_parallel_tool_call_result_message.items
],
},
]
async def test_prepare_chat_history_for_request_with_tool_message_right_after_user_message(
mock_anthropic_client_completion_stream: MagicMock,
mock_tool_call_result_message: ChatMessageContent,
):
chat_history = ChatHistory()
chat_history.add_user_message("What is 3+3?")
chat_history.add_message(mock_tool_call_result_message)
chat_completion_client = AnthropicChatCompletion(
ai_model_id="test_model_id",
service_id="test",
api_key="",
async_client=mock_anthropic_client_completion_stream,
)
with pytest.raises(ServiceInvalidRequestError, match="Tool message found after a user or system message."):
chat_completion_client._prepare_chat_history_for_request(chat_history, role_key="role", content_key="content")
async def test_prepare_chat_history_for_request_with_tool_message_as_the_first_message(
mock_anthropic_client_completion_stream: MagicMock,
mock_tool_call_result_message: ChatMessageContent,
):
chat_history = ChatHistory()
chat_history.add_message(mock_tool_call_result_message)
chat_completion_client = AnthropicChatCompletion(
ai_model_id="test_model_id",
service_id="test",
api_key="",
async_client=mock_anthropic_client_completion_stream,
)
with pytest.raises(ServiceInvalidRequestError, match="Tool message found without a preceding message."):
chat_completion_client._prepare_chat_history_for_request(chat_history, role_key="role", content_key="content")
async def test_send_chat_stream_request_tool_calls(
mock_streaming_tool_calls_message: MagicMock,
mock_streaming_chat_message_content: StreamingChatMessageContent,
):
chat_history = ChatHistory()
chat_history.add_user_message("What is 3+3?")
chat_history.add_message(mock_streaming_chat_message_content)
settings = AnthropicChatPromptExecutionSettings(
temperature=0.2,
max_tokens=100,
top_p=1.0,
frequency_penalty=0.0,
presence_penalty=0.0,
chat_history=chat_history,
)
client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.stream.return_value = mock_streaming_tool_calls_message
client.messages = messages_mock
chat_completion = AnthropicChatCompletion(
ai_model_id="test_model_id",
service_id="test",
api_key="",
async_client=client,
)
response = chat_completion._send_chat_stream_request(settings)
async for message in response:
assert message is not None
def test_client_base_url(mock_anthropic_client_completion: MagicMock):
chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=mock_anthropic_client_completion
)
assert chat_completion_base.service_url() is not None
def test_chat_completion_reset_settings(
mock_anthropic_client_completion: MagicMock,
):
chat_completion = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=mock_anthropic_client_completion
)
settings = AnthropicChatPromptExecutionSettings(tools=[{"name": "test"}], tool_choice={"type": "any"})
chat_completion._reset_function_choice_settings(settings)
assert settings.tools is None
assert settings.tool_choice is None
@@ -0,0 +1,129 @@
# Copyright (c) Microsoft. All rights reserved.
import pytest
from semantic_kernel.connectors.ai.anthropic.prompt_execution_settings.anthropic_prompt_execution_settings import (
AnthropicChatPromptExecutionSettings,
)
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.exceptions import ServiceInvalidExecutionSettingsError
def test_default_anthropic_chat_prompt_execution_settings():
settings = AnthropicChatPromptExecutionSettings()
assert settings.temperature is None
assert settings.top_p is None
assert settings.max_tokens == 1024
assert settings.messages is None
def test_custom_anthropic_chat_prompt_execution_settings():
settings = AnthropicChatPromptExecutionSettings(
temperature=0.5,
top_p=0.5,
max_tokens=128,
messages=[{"role": "system", "content": "Hello"}],
)
assert settings.temperature == 0.5
assert settings.top_p == 0.5
assert settings.max_tokens == 128
assert settings.messages == [{"role": "system", "content": "Hello"}]
def test_anthropic_chat_prompt_execution_settings_from_default_completion_config():
settings = PromptExecutionSettings(service_id="test_service")
chat_settings = AnthropicChatPromptExecutionSettings.from_prompt_execution_settings(settings)
assert chat_settings.service_id == "test_service"
assert chat_settings.temperature is None
assert chat_settings.top_p is None
assert chat_settings.max_tokens == 1024
def test_anthropic_chat_prompt_execution_settings_from_openai_prompt_execution_settings():
chat_settings = AnthropicChatPromptExecutionSettings(service_id="test_service", temperature=1.0)
new_settings = AnthropicChatPromptExecutionSettings(service_id="test_2", temperature=0.0)
chat_settings.update_from_prompt_execution_settings(new_settings)
assert chat_settings.service_id == "test_2"
assert chat_settings.temperature == 0.0
def test_anthropic_chat_prompt_execution_settings_from_custom_completion_config():
settings = PromptExecutionSettings(
service_id="test_service",
extension_data={
"temperature": 0.5,
"top_p": 0.5,
"max_tokens": 128,
"messages": [{"role": "system", "content": "Hello"}],
},
)
chat_settings = AnthropicChatPromptExecutionSettings.from_prompt_execution_settings(settings)
assert chat_settings.temperature == 0.5
assert chat_settings.top_p == 0.5
assert chat_settings.max_tokens == 128
def test_openai_chat_prompt_execution_settings_from_custom_completion_config_with_none():
settings = PromptExecutionSettings(
service_id="test_service",
extension_data={
"temperature": 0.5,
"top_p": 0.5,
"max_tokens": 128,
"messages": [{"role": "system", "content": "Hello"}],
},
)
chat_settings = AnthropicChatPromptExecutionSettings.from_prompt_execution_settings(settings)
assert chat_settings.temperature == 0.5
assert chat_settings.top_p == 0.5
assert chat_settings.max_tokens == 128
def test_openai_chat_prompt_execution_settings_from_custom_completion_config_with_functions():
settings = PromptExecutionSettings(
service_id="test_service",
extension_data={
"temperature": 0.5,
"top_p": 0.5,
"max_tokens": 128,
"tools": [{}],
"messages": [{"role": "system", "content": "Hello"}],
},
)
chat_settings = AnthropicChatPromptExecutionSettings.from_prompt_execution_settings(settings)
assert chat_settings.temperature == 0.5
assert chat_settings.top_p == 0.5
assert chat_settings.max_tokens == 128
def test_create_options():
settings = AnthropicChatPromptExecutionSettings(
service_id="test_service",
extension_data={
"temperature": 0.5,
"top_p": 0.5,
"max_tokens": 128,
"tools": [{}],
"messages": [{"role": "system", "content": "Hello"}],
},
)
options = settings.prepare_settings_dict()
assert options["temperature"] == 0.5
assert options["top_p"] == 0.5
assert options["max_tokens"] == 128
def test_tool_choice_none():
with pytest.raises(ServiceInvalidExecutionSettingsError, match="Tool choice 'none' is not supported by Anthropic."):
AnthropicChatPromptExecutionSettings(
service_id="test_service",
extension_data={
"temperature": 0.5,
"top_p": 0.5,
"max_tokens": 128,
"tool_choice": {"type": "none"},
"messages": [{"role": "system", "content": "Hello"}],
},
function_choice_behavior=FunctionChoiceBehavior.NoneInvoke(),
)