diff --git a/python/semantic_kernel/connectors/ai/bedrock/services/bedrock_chat_completion.py b/python/semantic_kernel/connectors/ai/bedrock/services/bedrock_chat_completion.py index 77a73fcc800a..45a4394eb995 100644 --- a/python/semantic_kernel/connectors/ai/bedrock/services/bedrock_chat_completion.py +++ b/python/semantic_kernel/connectors/ai/bedrock/services/bedrock_chat_completion.py @@ -253,6 +253,9 @@ def _create_chat_message_content(self, response: dict[str, Any]) -> ChatMessageC arguments=content["toolUse"]["input"], ) ) + elif "reasoningContent" in content: + # Reasoning blocks (sent by newer Claude models when they think) are not surfaced yet. + continue else: raise ServiceInvalidResponseError(f"Unsupported content type in the response: {content}") @@ -337,19 +340,24 @@ def _parse_content_block_delta_event( The content block delta event contains the completion. https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ContentBlockDeltaEvent.html """ - items: list[STREAMING_ITEM_TYPES] = [ - StreamingTextContent( - choice_index=0, - text=event["contentBlockDelta"]["delta"]["text"], - inner_content=event, + items: list[STREAMING_ITEM_TYPES] = [] + if "text" in event["contentBlockDelta"]["delta"]: + items.append( + StreamingTextContent( + choice_index=0, + text=event["contentBlockDelta"]["delta"]["text"], + inner_content=event, + ) ) - if "text" in event["contentBlockDelta"]["delta"] - else FunctionCallContent( - arguments=event["contentBlockDelta"]["delta"]["toolUse"]["input"], - inner_content=event, - index=event["contentBlockDelta"]["contentBlockIndex"], + elif "toolUse" in event["contentBlockDelta"]["delta"]: + items.append( + FunctionCallContent( + arguments=event["contentBlockDelta"]["delta"]["toolUse"]["input"], + inner_content=event, + index=event["contentBlockDelta"]["contentBlockIndex"], + ) ) - ] + # Other deltas (e.g. reasoningContent from newer Claude models) are not surfaced yet. return StreamingChatMessageContent( ai_model_id=self.ai_model_id, diff --git a/python/tests/unit/connectors/ai/bedrock/services/test_bedrock_chat_completion.py b/python/tests/unit/connectors/ai/bedrock/services/test_bedrock_chat_completion.py index efa702a43813..dde878797736 100644 --- a/python/tests/unit/connectors/ai/bedrock/services/test_bedrock_chat_completion.py +++ b/python/tests/unit/connectors/ai/bedrock/services/test_bedrock_chat_completion.py @@ -368,4 +368,64 @@ async def test_bedrock_streaming_chat_completion_invalid_event( pass +async def test_bedrock_chat_completion_skips_reasoning_content(chat_history: ChatHistory) -> None: + """Test that reasoning content blocks in the response are skipped.""" + response = { + "output": { + "message": { + "role": "assistant", + "content": [ + {"reasoningContent": {"reasoningText": {"text": "", "signature": "sig"}}}, + {"text": "Hi!"}, + ], + } + }, + "stopReason": "end_turn", + "usage": {"inputTokens": 10, "outputTokens": 5, "totalTokens": 15}, + } + with patch.object(MockBedrockRuntimeClient, "converse", return_value=response): + bedrock_chat_completion = BedrockChatCompletion( + model_id="us.anthropic.claude-opus-5-5", + runtime_client=MockBedrockRuntimeClient(), + client=MockBedrockClient(), + ) + + result = await bedrock_chat_completion.get_chat_message_content( + chat_history=chat_history, settings=BedrockChatPromptExecutionSettings() + ) + + assert len(result.items) == 1 + assert isinstance(result.items[0], TextContent) + assert result.content == "Hi!" + + +async def test_bedrock_streaming_chat_completion_skips_reasoning_delta(chat_history: ChatHistory) -> None: + """Test that reasoning content deltas in the stream are skipped.""" + events = [ + {"messageStart": {"role": "assistant"}}, + {"contentBlockDelta": {"contentBlockIndex": 0, "delta": {"reasoningContent": {"text": ""}}}}, + {"contentBlockDelta": {"contentBlockIndex": 0, "delta": {"reasoningContent": {"signature": "sig"}}}}, + {"contentBlockStop": {"contentBlockIndex": 0}}, + {"contentBlockDelta": {"contentBlockIndex": 1, "delta": {"text": "Hi!"}}}, + {"contentBlockStop": {"contentBlockIndex": 1}}, + {"messageStop": {"stopReason": "end_turn"}}, + ] + with patch.object(MockBedrockRuntimeClient, "converse_stream", return_value={"stream": iter(events)}): + bedrock_chat_completion = BedrockChatCompletion( + model_id="us.anthropic.claude-opus-5-5", + runtime_client=MockBedrockRuntimeClient(), + client=MockBedrockClient(), + ) + + chunks: list[StreamingChatMessageContent] = [] + async for streaming_messages in bedrock_chat_completion.get_streaming_chat_message_contents( + chat_history=chat_history, settings=BedrockChatPromptExecutionSettings() + ): + chunks.extend(streaming_messages) + response = reduce(lambda p, r: p + r, chunks) + + assert response.content == "Hi!" + assert response.finish_reason == FinishReason.STOP + + # endregion