[spring-ai] Streaming responses ending with CJK punctuation (。!?) are misclassified as partial and never persisted to the session #1608
Description
Activity
Thanks for triaging, @hirematha!
I hit this in production and currently work around it with a custom
BaseLlmbridge that aggregates the final response itself, so I have solid reproduction coverage. Happy to contribute a PR unless one is already in progress — I'd just like to align on direction first:-
Minimal fix — add CJK terminal punctuation (
。,!,?) to the check. Low risk, but it stays a locale-dependent heuristic: any script whose punctuation isn't in the list remains broken. -
Root fix — stop inferring "partial vs final" from punctuation altogether: stream chunks as partials, and emit the aggregated response as the single final event on stream completion. Locale-independent by construction, and mostly just wiring —
StreamingResponseAggregator#getFinalResponse()already builds exactly this event, but the streaming path never calls it (the completion callback only callsemitter.onComplete()). I'd avoid anchoring the end detection onfinishReasoninstead: it differs across providers (stop/end_turn/STOP), tool-call turns usetool_calls, and some OpenAI-compatible gateways drop it entirely.
My preference is 2, with 1 fine as a short-term patch. Would you be open to a PR for either? (Happy to cover persistence-timing and error-path details in the PR description.)
-
- addedwaiting on reporterWaiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.
on Oct 6, 2026 Hi @caps-xia, thank you for reporting this issue, I have been able to reproduce it using your inputs. It would be great if you could contribute a robust fix to address this issue.
- added 5 commits that reference this issue
on Oct 6, 2026 Hi @caps-xia, thank you for your contribution and quick fix, I have verified through local testing that your fix addresses the issue.
Currently, the issue and the associated PR are under review by our team and we will keep you updated if any additional information is required. Thank you.- added and removedwaiting on reporterWaiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.
on Oct 8, 2026
Describe the Bug
In the Spring AI bridge (
contrib/spring-ai),MessageConverter#isPartialResponsedecides whether a streaming chunk is partial by checking only ASCII sentence-ending
punctuation:
.,!,?,\n. CJK terminal punctuation (。,!,?) is notincluded.
As a result, when a model's final streaming response ends with a Chinese sentence
mark (which is the normal case for Chinese users), it is classified as
partial=trueand therefore never persisted to the session — the reply silently disappears from
conversation history, breaking multi-turn context and session replay.
Steps to Reproduce
com.google.adk.models.springai.SpringAImodel bridge with anyOpenAI-compatible model, streaming mode (
RunConfigSSE).根因是库存扣减为 0 未拦截。LlmResponse:partialistrue, and the session event storenever receives this response.
A minimal unit-level repro (calling the converter directly) is provided below.
No exception/stacktrace is produced — the failure is silent, which is what makes it
hard to notice in production.
Expected Behavior
A response ending with
。(or!/?) is a complete final response in Chineseand should be
partial=false, persisted to the session like its ASCII-.counterpart.Observed Behavior
Verified against
google-adk-spring-ai:1.9.0on our classpath:Downstream effect:
Runner-driven sessions lose the assistant's final answer forCJK conversations;
Session.events()replay shows the question without the reply.Live streaming looks fine (partials are forwarded to the client), so the loss only
shows up in history, multi-turn follow-ups and session replay.
Environment Details
1.11.0 (latest release) and on current
main(
contrib/spring-ai/src/main/java/com/google/adk/models/springai/MessageConverter.java#isPartialResponse)Model Information
Model-independent. Observed with GLM via an OpenAI-compatible gateway; the
classification happens in
MessageConverterbefore any model-specific handling.Regression
N/A — the heuristic has been present since we started using the bridge (1.9.0),
and is unchanged through 1.11.0.
Additional Context
Suggested fix, from minimal to better:
。!?(plus…,)).without sentence-ending characters (lists, code blocks, tables). Partial-ness
could be derived from chunk position / stream state instead of the last character.
This affects every CJK-language user of the official SpringAI bridge — and because
partials are still forwarded live, the missing persistence only surfaces later
(history / follow-up turns), which makes it easy to misdiagnose as a session-store
problem.
Minimal Reproduction Code
How often has this issue occurred?
Always (100%) — every Chinese final response ending with
。, which is essentiallyevery conversation.