fix(core): fix merge_lists incorrectly merging parallel tool calls (#35281)

This commit is contained in:
yaowubarbara
2026-02-19 09:33:17 +08:00
committed by GitHub
parent 3686bcbd96
commit 5053436dcf
2 changed files with 50 additions and 1 deletions

View File

@@ -117,7 +117,15 @@ def merge_lists(left: list | None, *others: list | None) -> list | None:
to_merge = [
i
for i, e_left in enumerate(merged)
if "index" in e_left and e_left["index"] == e["index"]
if (
"index" in e_left
and e_left["index"] == e["index"] # index matches
and ( # IDs not inconsistent
e_left.get("id") is None
or e.get("id") is None
or e_left["id"] == e["id"]
)
)
]
if to_merge:
# TODO: Remove this once merge_dict is updated with special

View File

@@ -916,6 +916,47 @@ def test_merge_tool_calls() -> None:
assert len(merged) == 2
def test_merge_tool_calls_parallel_same_index() -> None:
"""Test parallel tool calls with same index but different IDs."""
# Two parallel tool calls with the same index but different IDs
left = create_tool_call_chunk(
name="read_file", args='{"path": "foo.txt"}', id="tooluse_ABC", index=0
)
right = create_tool_call_chunk(
name="search_text", args='{"query": "bar"}', id="tooluse_DEF", index=0
)
merged = merge_lists([left], [right])
assert merged is not None
assert len(merged) == 2
assert merged[0]["name"] == "read_file"
assert merged[0]["id"] == "tooluse_ABC"
assert merged[1]["name"] == "search_text"
assert merged[1]["id"] == "tooluse_DEF"
# Streaming continuation: same index, id=None on continuation chunk
# should still merge correctly with the original chunk
first = create_tool_call_chunk(name="tool1", args="", id="id1", index=0)
continuation = create_tool_call_chunk(
name=None, args='{"key": "value"}', id=None, index=0
)
merged = merge_lists([first], [continuation])
assert merged is not None
assert len(merged) == 1
assert merged[0]["name"] == "tool1"
assert merged[0]["args"] == '{"key": "value"}'
assert merged[0]["id"] == "id1"
# Three parallel tool calls all with the same index
tc1 = create_tool_call_chunk(name="tool_a", args="{}", id="id_a", index=0)
tc2 = create_tool_call_chunk(name="tool_b", args="{}", id="id_b", index=0)
tc3 = create_tool_call_chunk(name="tool_c", args="{}", id="id_c", index=0)
merged = merge_lists([tc1], [tc2], [tc3])
assert merged is not None
assert len(merged) == 3
assert [m["name"] for m in merged] == ["tool_a", "tool_b", "tool_c"]
assert [m["id"] for m in merged] == ["id_a", "id_b", "id_c"]
def test_tool_message_serdes() -> None:
message = ToolMessage(
"foo", artifact={"bar": {"baz": 123}}, tool_call_id="1", status="error"