Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
ANTHROPIC_TO_SEMANTIC_KERNEL_FINISH_REASON_MAP = {
"end_turn": SemanticKernelFinishReason.STOP,
"max_tokens": SemanticKernelFinishReason.LENGTH,
"stop_sequence": SemanticKernelFinishReason.STOP,
"tool_use": SemanticKernelFinishReason.TOOL_CALLS,
Comment thread
Shxiao101 marked this conversation as resolved.
}

Expand Down Expand Up @@ -270,7 +271,7 @@ def _create_chat_message_content(

finish_reason = None
if response.stop_reason:
finish_reason = ANTHROPIC_TO_SEMANTIC_KERNEL_FINISH_REASON_MAP[response.stop_reason]
finish_reason = ANTHROPIC_TO_SEMANTIC_KERNEL_FINISH_REASON_MAP.get(response.stop_reason)

return ChatMessageContent(
inner_content=response,
Expand Down Expand Up @@ -308,7 +309,7 @@ def _create_streaming_chat_message_content(
)
)
elif isinstance(stream_event, RawMessageDeltaEvent):
finish_reason = ANTHROPIC_TO_SEMANTIC_KERNEL_FINISH_REASON_MAP[str(stream_event.delta.stop_reason)]
finish_reason = ANTHROPIC_TO_SEMANTIC_KERNEL_FINISH_REASON_MAP.get(str(stream_event.delta.stop_reason))
output_tokens = stream_event.usage.output_tokens
if metadata is None:
metadata = {"usage": {"output_tokens": output_tokens}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

import pytest
from anthropic import AsyncAnthropic
from anthropic.types import Message
from anthropic.types import Message, MessageDeltaUsage, RawMessageDeltaEvent, RawMessageStartEvent, TextBlock, 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,
Expand All @@ -18,6 +19,7 @@
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.contents.utils.finish_reason import FinishReason
from semantic_kernel.exceptions import (
ServiceInitializationError,
ServiceInvalidExecutionSettingsError,
Expand Down Expand Up @@ -547,3 +549,110 @@ def test_chat_completion_reset_settings(

assert settings.tools is None
assert settings.tool_choice is None


@pytest.mark.parametrize(
"stop_reason,expected_finish_reason",
[
pytest.param("end_turn", FinishReason.STOP, id="end_turn"),
pytest.param("max_tokens", FinishReason.LENGTH, id="max_tokens"),
pytest.param("tool_use", FinishReason.TOOL_CALLS, id="tool_use"),
pytest.param("stop_sequence", FinishReason.STOP, id="stop_sequence"),
pytest.param("refusal", None, id="unknown_stop_reason_returns_none"),
],
)
async def test_finish_reason_from_stop_reason(
kernel: Kernel,
mock_settings: AnthropicChatPromptExecutionSettings,
stop_reason: str,
expected_finish_reason: FinishReason | None,
):
response = Message(
id="test_message_id",
content=[TextBlock(text="test", type="text")],
model="test_model_id",
role="assistant",
stop_reason=stop_reason,
stop_sequence=None,
type="message",
usage=Usage(input_tokens=10, output_tokens=10),
)
client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.create = AsyncMock(return_value=response)
client.messages = messages_mock

chat_history = ChatHistory()
chat_history.add_user_message("test_user_message")

chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=client
)
contents: list[ChatMessageContent] = await chat_completion_base.get_chat_message_contents(
chat_history=chat_history, settings=mock_settings, kernel=kernel, arguments=KernelArguments()
)

assert contents[0].finish_reason is expected_finish_reason


@pytest.mark.parametrize(
"stop_reason,expected_finish_reason",
[
pytest.param("stop_sequence", FinishReason.STOP, id="stop_sequence"),
pytest.param("refusal", None, id="unknown_stop_reason_returns_none"),
],
)
async def test_finish_reason_from_stop_reason_stream(
kernel: Kernel,
mock_settings: AnthropicChatPromptExecutionSettings,
stop_reason: str,
expected_finish_reason: FinishReason | None,
):
stream_events = [
RawMessageStartEvent(
message=Message(
id="test_message_id",
content=[],
model="test_model_id",
role="assistant",
stop_reason=None,
stop_sequence=None,
type="message",
usage=Usage(input_tokens=10, output_tokens=2),
),
type="message_start",
),
RawMessageDeltaEvent(
delta=Delta(stop_reason=stop_reason, stop_sequence=None),
type="message_delta",
usage=MessageDeltaUsage(output_tokens=10),
),
]

async def async_generator():
for event in stream_events:
yield event

stream_mock = AsyncMock()
stream_mock.__aenter__.return_value = async_generator()

client = MagicMock(spec=AsyncAnthropic)
messages_mock = MagicMock()
messages_mock.stream.return_value = stream_mock
client.messages = messages_mock

chat_history = ChatHistory()
chat_history.add_user_message("test_user_message")

chat_completion_base = AnthropicChatCompletion(
ai_model_id="test_model_id", service_id="test", api_key="", async_client=client
)

last_content: StreamingChatMessageContent | None = None
async for content in chat_completion_base.get_streaming_chat_message_contents(
chat_history, mock_settings, kernel=kernel, arguments=KernelArguments()
):
last_content = content[0]

assert last_content is not None
assert last_content.finish_reason is expected_finish_reason
Loading