Class StreamingPartIndexer
StreamingParts index for providers that stream message content
without a content block index, so that
MessageAggregator can
rebuild the complete parts from the chunks.
The aggregator merges streamed parts by index: a partial part is appended to what was received earlier at the same index, and a complete part replaces it. Providers that stream content blocks, such as Anthropic, send that index with every delta. Others, such as Chat Completions, Gemini or Ollama, send reasoning deltas, text deltas and complete tool calls without an index, possibly several of them in one chunk. This class derives the index from the order in which the parts arrive:
- A
ReasoningPartorTextPartis a delta. It continues the current index when the previous delta was of the same kind, and starts the next index otherwise. So a run of reasoning deltas becomes oneReasoningPart, and a run of text deltas oneTextPart. - Any other part, such as a tool call, is complete when it arrives and gets the next index for itself. It also ends the current run, so a text delta that follows it starts a new text part.
- A tool-call argument delta, stamped through
stampToolCallDelta(org.springframework.ai.chat.messages.part.ToolCallPart), gets the next index on the first delta of its call. The later deltas of the call and the complete call keep that index, matched by tool call id. As the aggregator skips the deltas, they do not end the current run: a reasoning delta that arrives next to them still continues the reasoning part. The complete call ends it. - An empty
TextPartwithout a payload, such as the placeholder of a chunk that only carries usage, carries nothing. It is returned unchanged and does not end the current run, so that such a chunk in the middle of the reasoning does not split it in two.
reasoning("Think "), reasoning("more."),
text("Hel") and text("lo"), followed by one tool call, are stamped as
partial 0, partial 0, partial 1, partial 1 and complete 2. They aggregate to a
reasoning part "Think more.", a text part "Hello" and the tool call, in
that order.
A caller that must not index a part, for instance to keep streamed media out of the
aggregated message, keeps that part on the chunk unstamped rather than passing it to
stamp(MessagePart).
The aggregator groups indexed parts by response id, so one instance must serve all the parts that share a response id, in the order they appear in the stream. It is stateful and not thread-safe; the chunks of a stream are processed one at a time.
- Since:
- 2.1.0
- Author:
- Christian Tzolov, Dimitar Proynov
- See Also:
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstamp(MessagePart part) Returns a copy of the part with its stream index set, and advances this indexer.stampToolCallDelta(ToolCallPart delta) Returns a copy of a tool-call argument delta stamped as partial, seeStreamingParts.
-
Constructor Details
-
StreamingPartIndexer
public StreamingPartIndexer()
-
-
Method Details
-
stamp
Returns a copy of the part with its stream index set, and advances this indexer. Call it once per part, in the order the parts appear in the stream.- Parameters:
part- the part of a streamed chunk, without an index- Returns:
- an empty
TextPartwithout a payload unchanged; any otherTextPartor aReasoningPartstamped as partial, with the index of the current run of deltas of its kind, or the next index when it starts a new run; aToolCallPartstreamed as argument deltas stamped as complete, with the index of its deltas; any other part stamped as complete, with the next index
-
stampToolCallDelta
Returns a copy of a tool-call argument delta stamped as partial, seeStreamingParts. The first delta of a call gets the next index; its later deltas, and its complete part once passed tostamp(MessagePart), get the same index. The current run of deltas is not ended.- Parameters:
delta- the delta, carrying the id of its tool call- Returns:
- the delta stamped as partial
-