diff --git a/libs/core/langchain_core/utils/_merge.py b/libs/core/langchain_core/utils/_merge.py index 6a0cb38f078..e5982f009fb 100644 --- a/libs/core/langchain_core/utils/_merge.py +++ b/libs/core/langchain_core/utils/_merge.py @@ -120,6 +120,16 @@ def merge_lists(left: list | None, *others: list | None) -> list | None: if ( "index" in e_left and e_left["index"] == e["index"] # index matches + # Prevent text blocks from merging with non-text + # blocks that happen to share the same index (e.g., + # streaming text at index 0 vs tool_call_chunk at + # index 0 from the API). This is a text-vs- + # everything-else partition; non-text blocks at the + # same index may still merge with each other. + and ( + (e_left.get("type") == "text") + == (e.get("type") == "text") + ) and ( # IDs not inconsistent e_left.get("id") in (None, "") or e.get("id") in (None, "") diff --git a/libs/core/tests/unit_tests/utils/test_utils.py b/libs/core/tests/unit_tests/utils/test_utils.py index 815296f8889..f1a48369ce9 100644 --- a/libs/core/tests/unit_tests/utils/test_utils.py +++ b/libs/core/tests/unit_tests/utils/test_utils.py @@ -433,6 +433,27 @@ def test_generation_chunk_addition_type_error() -> None: [{"no_index": "b"}], [{"no_index": "a"}, {"no_index": "b"}], ), + # Text and non-text blocks with same index should NOT merge + ( + [{"index": 0, "type": "text", "text": "hello"}], + [{"index": 0, "type": "tool_call_chunk", "name": "foo", "args": "{}"}], + [ + {"index": 0, "type": "text", "text": "hello"}, + {"index": 0, "type": "tool_call_chunk", "name": "foo", "args": "{}"}, + ], + ), + # Two text blocks with same index should still merge + ( + [{"index": 0, "type": "text", "text": "hel"}], + [{"index": 0, "type": "text", "text": "lo"}], + [{"index": 0, "type": "text", "text": "hello"}], + ), + # Two non-text blocks with same index should still merge + ( + [{"index": 0, "type": "tool_call_chunk", "args": '{"a'}], + [{"index": 0, "type": "tool_call_chunk", "args": '": 1}'}], + [{"index": 0, "type": "tool_call_chunk", "args": '{"a": 1}'}], + ), ], ) def test_merge_lists(