Skip to content

Commit 1519a97

Browse files
mcblumhntrl
andauthored
fix(core): update chunk concat logic to match on missing ID fields (#8987)
Co-authored-by: Hunter Lovell <[email protected]> Co-authored-by: Hunter Lovell <[email protected]>
1 parent fd4691f commit 1519a97

File tree

3 files changed

+86
-12
lines changed

3 files changed

+86
-12
lines changed

.changeset/afraid-paws-give.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@langchain/core": patch
3+
---
4+
5+
update chunk concat logic to match on missing ID fields

langchain-core/src/messages/base.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -456,17 +456,19 @@ export function _mergeLists<Content extends MessageContentComplex>(
456456
"index" in item &&
457457
typeof item.index === "number"
458458
) {
459-
const toMerge = merged.findIndex(
460-
(leftItem) =>
461-
leftItem !== null &&
462-
typeof leftItem === "object" &&
463-
"index" in leftItem &&
464-
leftItem.index === item.index &&
465-
// Only merge if IDs match (or both are undefined)
466-
("id" in leftItem && "id" in item
467-
? leftItem.id === item.id
468-
: !("id" in leftItem) && !("id" in item))
469-
);
459+
const toMerge = merged.findIndex((leftItem) => {
460+
const isObject = typeof leftItem === "object";
461+
const indiciesMatch =
462+
"index" in leftItem && leftItem.index === item.index;
463+
const idsMatch =
464+
"id" in leftItem && "id" in item && leftItem?.id === item?.id;
465+
const eitherItemMissingID =
466+
!("id" in leftItem) ||
467+
!leftItem?.id ||
468+
!("id" in item) ||
469+
!item?.id;
470+
return isObject && indiciesMatch && (idsMatch || eitherItemMissingID);
471+
});
470472
if (
471473
toMerge !== -1 &&
472474
typeof merged[toMerge] === "object" &&

langchain-core/src/messages/tests/ai.test.ts

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, it, expect } from "@jest/globals";
22
import { AIMessageChunk } from "../ai.js";
33

44
describe("AIMessageChunk", () => {
5-
it("should properly merge tool call chunks", () => {
5+
it("should properly merge tool call chunks that have matching indices and ids", () => {
66
const chunk1 = new AIMessageChunk({
77
content: "",
88
tool_call_chunks: [
@@ -45,4 +45,71 @@ describe("AIMessageChunk", () => {
4545
);
4646
expect(secondCall?.id).toBe("5abf542e-87f3-4899-87c6-8f7d9cb6a28d");
4747
});
48+
49+
it("should properly merge tool call chunks that have matching indices and at least one id is blank", () => {
50+
const chunk1 = new AIMessageChunk({
51+
content: "",
52+
tool_call_chunks: [
53+
{
54+
name: "add_new_task",
55+
type: "tool_call_chunk",
56+
index: 0,
57+
id: "9fb5c937-6944-4173-84be-ad1caee1cedd",
58+
},
59+
],
60+
});
61+
const chunk2 = new AIMessageChunk({
62+
content: "",
63+
tool_call_chunks: [
64+
{
65+
args: '{"tasks":["buy tomatoes","help child with math"]}',
66+
type: "tool_call_chunk",
67+
index: 0,
68+
},
69+
],
70+
});
71+
72+
const merged = chunk1.concat(chunk2);
73+
expect(merged.tool_call_chunks).toHaveLength(1);
74+
75+
const firstCall = merged.tool_call_chunks?.[0];
76+
expect(firstCall?.name).toBe("add_new_task");
77+
expect(firstCall?.args).toBe(
78+
'{"tasks":["buy tomatoes","help child with math"]}'
79+
);
80+
expect(firstCall?.id).toBe("9fb5c937-6944-4173-84be-ad1caee1cedd");
81+
});
82+
83+
it("should properly merge tool call chunks that have matching indices no IDs at all", () => {
84+
const chunk1 = new AIMessageChunk({
85+
content: "",
86+
tool_call_chunks: [
87+
{
88+
name: "add_new_task",
89+
type: "tool_call_chunk",
90+
index: 0,
91+
},
92+
],
93+
});
94+
const chunk2 = new AIMessageChunk({
95+
content: "",
96+
tool_call_chunks: [
97+
{
98+
args: '{"tasks":["buy tomatoes","help child with math"]}',
99+
type: "tool_call_chunk",
100+
index: 0,
101+
},
102+
],
103+
});
104+
105+
const merged = chunk1.concat(chunk2);
106+
expect(merged.tool_call_chunks).toHaveLength(1);
107+
108+
const firstCall = merged.tool_call_chunks?.[0];
109+
expect(firstCall?.name).toBe("add_new_task");
110+
expect(firstCall?.args).toBe(
111+
'{"tasks":["buy tomatoes","help child with math"]}'
112+
);
113+
expect(firstCall?.id).toBeUndefined();
114+
});
48115
});

0 commit comments

Comments
 (0)