From e071fbc140628d6f88944a5855677f967166a329 Mon Sep 17 00:00:00 2001 From: Hussainbeam <88007126+SYED-M-HUSSAIN@users.noreply.github.com> Date: Mon, 26 May 2025 12:20:09 +0500 Subject: [PATCH 1/2] fix(bedrock): return string content in ChatBedrockConverse streaming --- .../chat_models/bedrock_converse.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libs/aws/langchain_aws/chat_models/bedrock_converse.py b/libs/aws/langchain_aws/chat_models/bedrock_converse.py index 95e1beea..dde64bc0 100644 --- a/libs/aws/langchain_aws/chat_models/bedrock_converse.py +++ b/libs/aws/langchain_aws/chat_models/bedrock_converse.py @@ -1170,9 +1170,9 @@ def _parse_stream_event(event: Dict[str, Any]) -> Optional[BaseMessageChunk]: if "messageStart" in event: # TODO: needed? return ( - AIMessageChunk(content=[]) + AIMessageChunk(content="") if event["messageStart"]["role"] == "assistant" - else HumanMessageChunk(content=[]) + else HumanMessageChunk(content="") ) elif "contentBlockStart" in event: block = { @@ -1189,7 +1189,9 @@ def _parse_stream_event(event: Dict[str, Any]) -> Optional[BaseMessageChunk]: index=event["contentBlockStart"]["contentBlockIndex"], ) ) - return AIMessageChunk(content=[block], tool_call_chunks=tool_call_chunks) + # Keep content as list during streaming to preserve merging compatibility + content = [block] + return AIMessageChunk(content=content, tool_call_chunks=tool_call_chunks) elif "contentBlockDelta" in event: block = { **_bedrock_to_lc([event["contentBlockDelta"]["delta"]])[0], @@ -1205,19 +1207,19 @@ def _parse_stream_event(event: Dict[str, Any]) -> Optional[BaseMessageChunk]: index=event["contentBlockDelta"]["contentBlockIndex"], ) ) - return AIMessageChunk(content=[block], tool_call_chunks=tool_call_chunks) + # Keep content as list during streaming to preserve merging compatibility + content = [block] + return AIMessageChunk(content=content, tool_call_chunks=tool_call_chunks) elif "contentBlockStop" in event: # TODO: needed? - return AIMessageChunk( - content=[{"index": event["contentBlockStop"]["contentBlockIndex"]}] - ) + return AIMessageChunk(content="") elif "messageStop" in event: # TODO: snake case response metadata? - return AIMessageChunk(content=[], response_metadata=event["messageStop"]) + return AIMessageChunk(content="", response_metadata=event["messageStop"]) elif "metadata" in event: usage = _extract_usage_metadata(event["metadata"]) return AIMessageChunk( - content=[], response_metadata=event["metadata"], usage_metadata=usage + content="", response_metadata=event["metadata"], usage_metadata=usage ) elif "Exception" in list(event.keys())[0]: name, info = list(event.items())[0] From 740e24699bcbe18c6c562710eb1def78659408df Mon Sep 17 00:00:00 2001 From: Hussainbeam <88007126+SYED-M-HUSSAIN@users.noreply.github.com> Date: Tue, 27 May 2025 15:27:56 +0500 Subject: [PATCH 2/2] fixing the standard integration tests in PR-491 credit for the original fix goes to SYED-M-HUSSAIN --- .../langchain_aws/chat_models/bedrock_converse.py | 12 +++++++----- .../chat_models/test_bedrock_converse.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libs/aws/langchain_aws/chat_models/bedrock_converse.py b/libs/aws/langchain_aws/chat_models/bedrock_converse.py index dde64bc0..fff14d9e 100644 --- a/libs/aws/langchain_aws/chat_models/bedrock_converse.py +++ b/libs/aws/langchain_aws/chat_models/bedrock_converse.py @@ -1170,9 +1170,9 @@ def _parse_stream_event(event: Dict[str, Any]) -> Optional[BaseMessageChunk]: if "messageStart" in event: # TODO: needed? return ( - AIMessageChunk(content="") + AIMessageChunk(content=[]) if event["messageStart"]["role"] == "assistant" - else HumanMessageChunk(content="") + else HumanMessageChunk(content=[]) ) elif "contentBlockStart" in event: block = { @@ -1189,8 +1189,9 @@ def _parse_stream_event(event: Dict[str, Any]) -> Optional[BaseMessageChunk]: index=event["contentBlockStart"]["contentBlockIndex"], ) ) - # Keep content as list during streaming to preserve merging compatibility + # always keep block inside a list to preserve merging compatibility content = [block] + return AIMessageChunk(content=content, tool_call_chunks=tool_call_chunks) elif "contentBlockDelta" in event: block = { @@ -1207,12 +1208,13 @@ def _parse_stream_event(event: Dict[str, Any]) -> Optional[BaseMessageChunk]: index=event["contentBlockDelta"]["contentBlockIndex"], ) ) - # Keep content as list during streaming to preserve merging compatibility + # always keep block inside a list to preserve merging compatibility content = [block] + return AIMessageChunk(content=content, tool_call_chunks=tool_call_chunks) elif "contentBlockStop" in event: # TODO: needed? - return AIMessageChunk(content="") + return AIMessageChunk(content=[]) elif "messageStop" in event: # TODO: snake case response metadata? return AIMessageChunk(content="", response_metadata=event["messageStop"]) diff --git a/libs/aws/tests/integration_tests/chat_models/test_bedrock_converse.py b/libs/aws/tests/integration_tests/chat_models/test_bedrock_converse.py index f0a1c515..2977e629 100644 --- a/libs/aws/tests/integration_tests/chat_models/test_bedrock_converse.py +++ b/libs/aws/tests/integration_tests/chat_models/test_bedrock_converse.py @@ -455,7 +455,7 @@ def test_structured_output_thinking_force_tool_use() -> None: }, } with pytest.raises(llm.client.exceptions.ValidationException): - response = llm.client.converse(messages=messages, **params) + llm.client.converse(messages=messages, **params) def test_bedrock_pdf_inputs() -> None: