spring-projects / spring-projects/spring-ai
Allow access to raw `ChatCompletionChunk` stream before tool call merge in `OpenAiApi`
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 9.5k
- Forks
- 2.9k
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 6
Description
Spring AI version: 1.1.5
Background
In OpenAiApi.chatCompletionStream(), the OpenAiStreamFunctionCallingHelper merges consecutive tool call chunks into a complete one using windowUntil + Flux.reduce(merge) before emitting them downstream.
.windowUntil(chunk -> helper.isStreamingToolFunctionCallFinish(chunk))
.concatMapIterable(window -> window.reduce(new ChatCompletionChunk(...), helper::merge))
This means that tool call parameters are always delivered in a single, fully-merged chunk. Downstream consumers (e.g. custom ChatModel wrappers, or direct OpenAiApi users) never see incremental argument updates.
Problem
When the model decides to call a tool (e.g. bash, read_file, etc.), the arguments can be long (hundreds of characters of JSON). With the current design, frontend UIs receive the complete parameters all at once — there is no way to show a "loading" state or incremental argument preview while the model is still streaming the tool call arguments.
This is especially noticeable with reasoning/thinking models where tool call parameters are emitted token by token by the API, but Spring AI buffers everything before forwarding.
Feature Request
Please provide a way to access the raw Flux<ChatCompletionChunk> before tool call merging. Possible approaches:
-
New method in
OpenAiApi: a variant ofchatCompletionStream()that returns un-merged chunks (only JSON parsing, withoutwindowUntil/reducemerging), letting callers handle tool call aggregation themselves. -
Configuration flag: Add an option on
ChatCompletionRequest(e.g.mergeToolCallChunks: true/false) to control whetherOpenAiApimerges tool call chunks internally. -
Callback/Listener: Provide a hook to intercept each raw chunk before merge, so that incremental arguments can be forwarded to custom handlers (e.g. SSE emitters for real-time frontend updates).
Use Case
Real-time frontend update during tool call streaming:
- When tool name is received → show "🧰 Calling tool: bash" with loading indicator
- When each argument fragment arrives → update parameter display progressively
- When execution completes → show result
This significantly improves UX for AI agent chat interfaces that need to show tool call progress to end users.
Workaround
Currently, the only way to get raw streaming data is to bypass OpenAiApi entirely and directly consume the SSE response from the upstream API using a raw WebClient, which duplicates Spring AI's request construction logic.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in OpenAiApi.chatCompletionStream() and trace the OpenAiStreamFunctionCallingHelper windowUntil and Flux.reduce(merge) flow. Compare the proposed method, request flag, and callback approaches, then verify that JSON parsing remains available while callers can receive incremental ChatCompletionChunk values before tool-call merging.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring
- Domain
- ai, api, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 42/100