Skip to content

GH-5806: Merge streaming tool call chunks in MessageAggregator#5854

Open
suryateja-g13 wants to merge 1 commit intospring-projects:mainfrom
suryateja-g13:GH-5806
Open

GH-5806: Merge streaming tool call chunks in MessageAggregator#5854
suryateja-g13 wants to merge 1 commit intospring-projects:mainfrom
suryateja-g13:GH-5806

Conversation

@suryateja-g13
Copy link
Copy Markdown

Summary

  • Streaming tool calls arrive in multiple chunks: first chunk carries id and name, subsequent chunks carry partial arguments with an empty id
  • MessageAggregator was calling addAll() on every chunk, producing a list of incomplete ToolCall entries with empty arguments, ultimately causing IllegalArgumentException: toolInput cannot be null or empty during execution
  • Added mergeToolCallChunk() helper: chunks with a non-empty id start a new entry; chunks with an empty id append their arguments to the most recently started call
  • Handles single tool calls split across N chunks and multiple sequential tool calls streamed one after another

Test plan

  • MessageAggregatorTests#should_merge_tool_call_chunks_into_single_complete_tool_call — replicates the exact streaming pattern from the issue report (4 chunks, first with id+name, rest with empty id and partial arguments)
  • MessageAggregatorTests#should_pass_through_complete_tool_call_unchanged — single complete tool call (no merging needed) still works
  • MessageAggregatorTests#should_merge_multiple_parallel_tool_calls — two sequential tool calls each split across two chunks produce two distinct merged entries
  • MessageAggregatorTests#should_handle_chunk_with_null_arguments — null arguments in first chunk handled without NPE

Fixes GH-5806 (#5806)

🤖 Generated with Claude Code

…gregator

When streaming, models emit tool calls across multiple chunks: the first
chunk carries the id and name while subsequent chunks carry partial
arguments with an empty id. The previous addAll() approach collected
every chunk as a separate ToolCall, producing incomplete entries with
empty arguments that caused IllegalArgumentException during execution.

mergeToolCallChunk() now inspects each incoming chunk: if it has a
non-empty id it starts a new entry; otherwise it appends its arguments
to the most recently started call. This handles single calls split
across N chunks as well as multiple sequential tool calls.

Fixes spring-projectsGH-5806 (spring-projects#5806)

Signed-off-by: Gorre Surya <sgorre92@gmail.com>
Copy link
Copy Markdown

@anuragg-saxenaa anuragg-saxenaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. The fix changes addAll to a mergeToolCallChunk() loop that correctly handles partial tool call chunks: chunks with a non-empty id start a new entry, while empty-id chunks append arguments to the last entry. The logic correctly reconstructs streamed tool calls across multiple chunks.

The new MessageAggregatorTests has good coverage: merging sequential chunks, passing through complete tool calls, handling multiple parallel tool calls, and null arguments. Edge case handling looks solid.

One minor observation: the code uses StringUtils.hasText(chunk.id()) for the empty-id check. Since id is a String record component, this is safe — empty string or null both return false from hasText. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Streaming tool calls are incorrectly merged, causing empty arguments and IllegalArgumentException

2 participants