@langchain/core 0.3.76 → 0.3.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -143,26 +143,45 @@ class AIMessageChunk extends base_js_1.BaseMessageChunk {
143
143
  };
144
144
  }
145
145
  else {
146
- const groupedToolCallChunk = fields.tool_call_chunks.reduce((acc, chunk) => {
147
- // Assign a fallback ID if the chunk doesn't have one
148
- // This can happen with tools that have empty schemas
149
- const chunkId = chunk.id || `fallback-${chunk.index || 0}`;
150
- acc[chunkId] = acc[chunkId] ?? [];
151
- acc[chunkId].push(chunk);
146
+ const groupedToolCallChunks = fields.tool_call_chunks.reduce((acc, chunk) => {
147
+ const matchedChunkIndex = acc.findIndex(([match]) => {
148
+ // If chunk has an id and index, match if both are present
149
+ if ("id" in chunk &&
150
+ chunk.id &&
151
+ "index" in chunk &&
152
+ chunk.index !== undefined) {
153
+ return chunk.id === match.id && chunk.index === match.index;
154
+ }
155
+ // If chunk has an id, we match on id
156
+ if ("id" in chunk && chunk.id) {
157
+ return chunk.id === match.id;
158
+ }
159
+ // If chunk has an index, we match on index
160
+ if ("index" in chunk && chunk.index !== undefined) {
161
+ return chunk.index === match.index;
162
+ }
163
+ return false;
164
+ });
165
+ if (matchedChunkIndex !== -1) {
166
+ acc[matchedChunkIndex].push(chunk);
167
+ }
168
+ else {
169
+ acc.push([chunk]);
170
+ }
152
171
  return acc;
153
- }, {});
172
+ }, []);
154
173
  const toolCalls = [];
155
174
  const invalidToolCalls = [];
156
- for (const [id, chunks] of Object.entries(groupedToolCallChunk)) {
175
+ for (const chunks of groupedToolCallChunks) {
157
176
  let parsedArgs = {};
158
177
  const name = chunks[0]?.name ?? "";
159
178
  const joinedArgs = chunks.map((c) => c.args || "").join("");
160
179
  const argsStr = joinedArgs.length ? joinedArgs : "{}";
161
- // Use the original ID from the first chunk if it exists, otherwise use the grouped ID
162
- const originalId = chunks[0]?.id || id;
180
+ const id = chunks[0]?.id;
163
181
  try {
164
182
  parsedArgs = (0, json_js_1.parsePartialJson)(argsStr);
165
- if (parsedArgs === null ||
183
+ if (!id ||
184
+ parsedArgs === null ||
166
185
  typeof parsedArgs !== "object" ||
167
186
  Array.isArray(parsedArgs)) {
168
187
  throw new Error("Malformed tool call chunk args.");
@@ -170,7 +189,7 @@ class AIMessageChunk extends base_js_1.BaseMessageChunk {
170
189
  toolCalls.push({
171
190
  name,
172
191
  args: parsedArgs,
173
- id: originalId,
192
+ id,
174
193
  type: "tool_call",
175
194
  });
176
195
  }
@@ -178,7 +197,7 @@ class AIMessageChunk extends base_js_1.BaseMessageChunk {
178
197
  invalidToolCalls.push({
179
198
  name,
180
199
  args: argsStr,
181
- id: originalId,
200
+ id,
182
201
  error: "Malformed args.",
183
202
  type: "invalid_tool_call",
184
203
  });
@@ -137,26 +137,45 @@ export class AIMessageChunk extends BaseMessageChunk {
137
137
  };
138
138
  }
139
139
  else {
140
- const groupedToolCallChunk = fields.tool_call_chunks.reduce((acc, chunk) => {
141
- // Assign a fallback ID if the chunk doesn't have one
142
- // This can happen with tools that have empty schemas
143
- const chunkId = chunk.id || `fallback-${chunk.index || 0}`;
144
- acc[chunkId] = acc[chunkId] ?? [];
145
- acc[chunkId].push(chunk);
140
+ const groupedToolCallChunks = fields.tool_call_chunks.reduce((acc, chunk) => {
141
+ const matchedChunkIndex = acc.findIndex(([match]) => {
142
+ // If chunk has an id and index, match if both are present
143
+ if ("id" in chunk &&
144
+ chunk.id &&
145
+ "index" in chunk &&
146
+ chunk.index !== undefined) {
147
+ return chunk.id === match.id && chunk.index === match.index;
148
+ }
149
+ // If chunk has an id, we match on id
150
+ if ("id" in chunk && chunk.id) {
151
+ return chunk.id === match.id;
152
+ }
153
+ // If chunk has an index, we match on index
154
+ if ("index" in chunk && chunk.index !== undefined) {
155
+ return chunk.index === match.index;
156
+ }
157
+ return false;
158
+ });
159
+ if (matchedChunkIndex !== -1) {
160
+ acc[matchedChunkIndex].push(chunk);
161
+ }
162
+ else {
163
+ acc.push([chunk]);
164
+ }
146
165
  return acc;
147
- }, {});
166
+ }, []);
148
167
  const toolCalls = [];
149
168
  const invalidToolCalls = [];
150
- for (const [id, chunks] of Object.entries(groupedToolCallChunk)) {
169
+ for (const chunks of groupedToolCallChunks) {
151
170
  let parsedArgs = {};
152
171
  const name = chunks[0]?.name ?? "";
153
172
  const joinedArgs = chunks.map((c) => c.args || "").join("");
154
173
  const argsStr = joinedArgs.length ? joinedArgs : "{}";
155
- // Use the original ID from the first chunk if it exists, otherwise use the grouped ID
156
- const originalId = chunks[0]?.id || id;
174
+ const id = chunks[0]?.id;
157
175
  try {
158
176
  parsedArgs = parsePartialJson(argsStr);
159
- if (parsedArgs === null ||
177
+ if (!id ||
178
+ parsedArgs === null ||
160
179
  typeof parsedArgs !== "object" ||
161
180
  Array.isArray(parsedArgs)) {
162
181
  throw new Error("Malformed tool call chunk args.");
@@ -164,7 +183,7 @@ export class AIMessageChunk extends BaseMessageChunk {
164
183
  toolCalls.push({
165
184
  name,
166
185
  args: parsedArgs,
167
- id: originalId,
186
+ id,
168
187
  type: "tool_call",
169
188
  });
170
189
  }
@@ -172,7 +191,7 @@ export class AIMessageChunk extends BaseMessageChunk {
172
191
  invalidToolCalls.push({
173
192
  name,
174
193
  args: argsStr,
175
- id: originalId,
194
+ id,
176
195
  error: "Malformed args.",
177
196
  type: "invalid_tool_call",
178
197
  });
@@ -285,7 +285,7 @@ right
285
285
  // Do not merge 'type' fields
286
286
  continue;
287
287
  }
288
- else if (["id", "output_version", "model_provider"].includes(key)) {
288
+ else if (["id", "name", "output_version", "model_provider"].includes(key)) {
289
289
  // Keep the incoming value for these fields
290
290
  merged[key] = value;
291
291
  }
@@ -322,14 +322,16 @@ function _mergeLists(left, right) {
322
322
  item !== null &&
323
323
  "index" in item &&
324
324
  typeof item.index === "number") {
325
- const toMerge = merged.findIndex((leftItem) => leftItem !== null &&
326
- typeof leftItem === "object" &&
327
- "index" in leftItem &&
328
- leftItem.index === item.index &&
329
- // Only merge if IDs match (or both are undefined)
330
- ("id" in leftItem && "id" in item
331
- ? leftItem.id === item.id
332
- : !("id" in leftItem) && !("id" in item)));
325
+ const toMerge = merged.findIndex((leftItem) => {
326
+ const isObject = typeof leftItem === "object";
327
+ const indiciesMatch = "index" in leftItem && leftItem.index === item.index;
328
+ const idsMatch = "id" in leftItem && "id" in item && leftItem?.id === item?.id;
329
+ const eitherItemMissingID = !("id" in leftItem) ||
330
+ !leftItem?.id ||
331
+ !("id" in item) ||
332
+ !item?.id;
333
+ return isObject && indiciesMatch && (idsMatch || eitherItemMissingID);
334
+ });
333
335
  if (toMerge !== -1 &&
334
336
  typeof merged[toMerge] === "object" &&
335
337
  merged[toMerge] !== null) {
@@ -272,7 +272,7 @@ right
272
272
  // Do not merge 'type' fields
273
273
  continue;
274
274
  }
275
- else if (["id", "output_version", "model_provider"].includes(key)) {
275
+ else if (["id", "name", "output_version", "model_provider"].includes(key)) {
276
276
  // Keep the incoming value for these fields
277
277
  merged[key] = value;
278
278
  }
@@ -309,14 +309,16 @@ export function _mergeLists(left, right) {
309
309
  item !== null &&
310
310
  "index" in item &&
311
311
  typeof item.index === "number") {
312
- const toMerge = merged.findIndex((leftItem) => leftItem !== null &&
313
- typeof leftItem === "object" &&
314
- "index" in leftItem &&
315
- leftItem.index === item.index &&
316
- // Only merge if IDs match (or both are undefined)
317
- ("id" in leftItem && "id" in item
318
- ? leftItem.id === item.id
319
- : !("id" in leftItem) && !("id" in item)));
312
+ const toMerge = merged.findIndex((leftItem) => {
313
+ const isObject = typeof leftItem === "object";
314
+ const indiciesMatch = "index" in leftItem && leftItem.index === item.index;
315
+ const idsMatch = "id" in leftItem && "id" in item && leftItem?.id === item?.id;
316
+ const eitherItemMissingID = !("id" in leftItem) ||
317
+ !leftItem?.id ||
318
+ !("id" in item) ||
319
+ !item?.id;
320
+ return isObject && indiciesMatch && (idsMatch || eitherItemMissingID);
321
+ });
320
322
  if (toMerge !== -1 &&
321
323
  typeof merged[toMerge] === "object" &&
322
324
  merged[toMerge] !== null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/core",
3
- "version": "0.3.76",
3
+ "version": "0.3.77",
4
4
  "description": "Core LangChain.js abstractions and schemas",
5
5
  "type": "module",
6
6
  "engines": {