diff --git a/libs/core/langchain_core/messages/base.py b/libs/core/langchain_core/messages/base.py index d73b4d526b3..37f48904ad5 100644 --- a/libs/core/langchain_core/messages/base.py +++ b/libs/core/langchain_core/messages/base.py @@ -105,6 +105,9 @@ def merge_content( # Add the second content to the last element if isinstance(first_content[-1], str): return first_content[:-1] + [first_content[-1] + second_content] + # If second content is an empty string, treat as a no-op + elif second_content == "": + return first_content else: # Otherwise, add the second content as a new element of the list return first_content + [second_content] diff --git a/libs/core/langchain_core/utils/_merge.py b/libs/core/langchain_core/utils/_merge.py index b6f3ab25d43..0ed5236e544 100644 --- a/libs/core/langchain_core/utils/_merge.py +++ b/libs/core/langchain_core/utils/_merge.py @@ -60,6 +60,10 @@ def merge_lists(left: Optional[List], right: Optional[List]) -> Optional[List]: if e_left["index"] == e["index"] ] if to_merge: + # If a top-level "type" has been set for a chunk, it should no + # longer be overridden by the "type" field in future chunks. + if "type" in merged[to_merge[0]] and "type" in e: + e.pop("type") merged[to_merge[0]] = merge_dicts(merged[to_merge[0]], e) else: merged = merged + [e] diff --git a/libs/core/tests/unit_tests/test_messages.py b/libs/core/tests/unit_tests/test_messages.py index c893f8ced57..8ab61278e70 100644 --- a/libs/core/tests/unit_tests/test_messages.py +++ b/libs/core/tests/unit_tests/test_messages.py @@ -203,6 +203,35 @@ def test_complex_ai_message_chunks() -> None: ) ), "Concatenating when both content arrays are dicts with separate indexes should not merge" # noqa: E501 + assert ( + AIMessageChunk(content=[{"index": 0, "text": "I am", "type": "text_block"}]) + + AIMessageChunk( + content=[{"index": 0, "text": " indeed.", "type": "text_block"}] + ) + == AIMessageChunk( + content=[{"index": 0, "text": "I am indeed.", "type": "text_block"}] + ) + ), "Concatenating when both content arrays are dicts with the same index and type should merge" # noqa: E501 + + assert ( + AIMessageChunk(content=[{"index": 0, "text": "I am", "type": "text_block"}]) + + AIMessageChunk( + content=[{"index": 0, "text": " indeed.", "type": "text_block_delta"}] + ) + == AIMessageChunk( + content=[{"index": 0, "text": "I am indeed.", "type": "text_block"}] + ) + ), "Concatenating when both content arrays are dicts with the same index and different types should merge without updating type" # noqa: E501 + + assert ( + AIMessageChunk(content=[{"index": 0, "text": "I am", "type": "text_block"}]) + + AIMessageChunk(content="", response_metadata={"extra": "value"}) + == AIMessageChunk( + content=[{"index": 0, "text": "I am", "type": "text_block"}], + response_metadata={"extra": "value"}, + ) + ), "Concatenating when one content is an array and one is an empty string should not add a new item, but should concat other fields" # noqa: E501 + def test_function_message_chunks() -> None: assert FunctionMessageChunk(