LangGraph Anthropic Streaming#
Overview#
appendLangChainChunk in @assistant-ui/react-langgraph accumulates streamed AIMessageChunk messages into a running LangChainMessage. Anthropic emits several non-standard content-block delta types that omit fields other providers always populate, causing recurring bugs in this layer.
Entry point: packages/react-langgraph/src/appendLangChainChunk.ts
How appendLangChainChunk Works#
The function handles three cases :
- Non-chunk messages (
type !== "AIMessageChunk") — returned as-is, or merged viamergeStreamedToolCallArgsif both prev and curr are"ai"type. - First chunk (no prior
"ai"message) — bootstrapped directly, convertingtool_call_chunkstotool_callsviachunkToToolCall. - Continuation chunks — content blocks and tool call chunks are merged into the accumulated message.
For content blocks, the merge loop originally handled only text and image_url blocks. All other types (e.g., thinking, reasoning, computer_call) were silently dropped.
For tool calls, a two-tier lookup first matches by id, then falls back to index — covering providers like Bedrock that omit id/name on continuation chunks.
Anthropic-Specific Delta Types and Bugs#
Anthropic's streaming protocol emits delta events that do not follow the conventions of OpenAI and other providers. This has caused multiple bugs in appendLangChainChunk.
Bug 1: citations_delta appends "undefined" (Issue #6901)#
@langchain/anthropic emits a citations_delta as a content block typed "text" that carries a citations field but no text field . The accumulator's concatenation last.text + item.text stringifies the missing field, producing a literal "undefined" appended to every cited sentence when Anthropic citations are enabled.
The correct fix has two parts :
- Guard
textconcatenation against a missingtextfield (i.e.,item.text ?? ""). - Decide whether to carry the
citationspayload forward onto the accumulated block (upstream@langchain/corekeeps it).
Bug 2: signature_delta and thinking blocks dropped (PR #6896)#
Anthropic's extended-thinking feature emits a signature_delta as a thinking-typed block with no thinking field — a metadata-only chunk . The original loop discarded all non-text/non-image_url blocks, causing:
- Reasoning content (
thinking,reasoningblocks) to disappear after the first chunk. - Signature metadata to be lost entirely.
Fix (PR #6896): Implemented index-based block accumulation mirroring @langchain/core's _mergeLists. Continuation chunks for an indexed block now update that slot instead of appending a duplicate. thinking blocks now concatenate both thinking content and signature fields across chunks. reasoning blocks accumulate summary array items, reasoning text, and signature fields. Only tool_use and input_json_delta blocks (owned by the tool-call pipeline) are excluded.
Bug 3: Mixed content blocks reordered (PR #6882)#
When a chunk contained both image and text blocks, text after an image would be merged into the text before the image because lastIndex was computed once before the loop . The fix moves the lastIndex calculation inside the loop so each block finds the correct append position.
Bug 4: computer_call blocks duplicated across chunks#
computer_call blocks split across chunks rendered as duplicate tool-call parts instead of updating a single entry. Fixed as part of PR #6896's index-based accumulation.
Type Changes#
PR #6896 updated the type definitions in types.ts to reflect what the wire actually carries :
MessageContentThinkinggains optionalsignatureandindexfields.MessageContentReasoninggains optionalsignatureandindexfields.MessageContentAudioadded to the assistant content union.
Key Files#
| File | Role |
|---|---|
appendLangChainChunk.ts | Core chunk accumulator |
types.ts | Content block and tool call type definitions |
Related Issues and PRs#
| Reference | Summary |
|---|---|
| Issue #6901 | citations_delta appends "undefined" to answers |
| PR #6882 | Fix: keep mixed content blocks in order |
| PR #6896 | Fix: keep supported continuation content blocks (signature_delta, thinking, reasoning, computer_call) |