I am streaming message chunks, that I want to concatenate once I receive them. The AIMessageChunk.concat() method makes weird assumptions on the structure of my messages and subsequently merges them incorrectly.
Examples:
Strings
When I have a string typed field under additional_kwargs, e.g. “role": "assistant" in each chunk, the .concat() method merges two chunks to "role”: “assistantassistant”.
Timestamps
When I have a number typed field under additional_kwargs, e.g. “created": 1734524005 in each chunk, the .concat() method adds two chunks to "created”: 3469048010.
This is quite unexpected. Understandibly, it is difficult to make assumption on the fields, but I think there should be a way to influence this, right?
Hi,
concat() deep-merges content but shallow-concats/adds lc_kwargs/additional_kwargs scalars (strings/numbers) langchainjs/libs/langchain-core/src/messages/ai.ts at main · langchain-ai/langchainjs · GitHub (Line 393)
Here is an example of manually safe merging.
import { AIMessageChunk } from "@langchain/core/messages";
function safeConcat(chunks) {
if (!chunks.length) throw new Error("No chunks");
// Clone FIRST chunk (preserves ID, role, timestamp)
const merged = structuredClone(chunks[0]);
// Append content only
for (let i = 1; i < chunks.length; i++) {
merged.content += chunks[i].content;
}
// Preserve FIRST chunk metadata (no summing/concat)
merged.lc_kwargs = { ...chunks[0].lc_kwargs };
merged.responseMetadata = { ...chunks[0].responseMetadata };
// Optional: Override timestamp with LAST
if (chunks[chunks.length - 1].lc_kwargs.created) {
merged.lc_kwargs.created = chunks[chunks.length - 1].lc_kwargs.created;
}
return merged;
}
let chunks = [];
for await (const chunk of await model.stream(messages)) {
if (chunk instanceof AIMessageChunk) chunks.push(chunk);
}
const fullMessage = safeConcat(chunks);
Let me know if this solves your issue.