Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[spring-ai] Streaming responses ending with CJK punctuation (。!?) are misclassified as partial and never persisted to the session

オープン 初心者向け
#1,608 コメント 1 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@caps-xia がすでに取り組んでいます。

2026年10月6日 から。

  • #1620 @caps-xia による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
84/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
java, spring
領域
backend

調査の方向性

contrib/spring-ai/src/main/java/com/google/adk/models/springai/MessageConverter.java のメソッド isPartialResponse から始めてください。これは ASCII の . ! ? と改行のみをチェックします。Issue の最小再現は、ChatResponse を 。 で終わらせて MessageConverter#toLlmResponse を直接呼び出すので、それを contrib/spring-ai/src/test 配下のユニットテストに変換し、現在 partial=true を返すことを確認してください。完了とは、。!? で終わる応答が partial=false を生成し、新しいテストとモジュールの既存テストの両方がパスすることを意味します。

索引モデルが issue の本文から書いたものです。

説明

Describe the Bug

In the Spring AI bridge (contrib/spring-ai), MessageConverter#isPartialResponse
decides whether a streaming chunk is partial by checking only ASCII sentence-ending
punctuation: ., !, ?, \n. CJK terminal punctuation (。, !, ?) is not
included.

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=true
and therefore never persisted to the session — the reply silently disappears from
conversation history, breaking multi-turn context and session replay.

Steps to Reproduce
  1. Use the official com.google.adk.models.springai.SpringAI model bridge with any
    OpenAI-compatible model, streaming mode (RunConfig SSE).
  2. Send a question that produces a Chinese answer, e.g. 根因是库存扣减为 0 未拦截。
  3. Observe the final LlmResponse: partial is true, and the session event store
    never 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 Chinese
and should be partial=false, persisted to the session like its ASCII-. counterpart.

Observed Behavior

Verified against google-adk-spring-ai:1.9.0 on our classpath:

[PROBE] chinese-ending partial = true    <-- bug: final reply treated as partial
[PROBE] ascii-ending   partial = false

Downstream effect: Runner-driven sessions lose the assistant's final answer for
CJK 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
  • ADK Library Version: verified on 1.9.0, and confirmed still present in
    1.11.0 (latest release) and on current main
    (contrib/spring-ai/src/main/java/com/google/adk/models/springai/MessageConverter.java#isPartialResponse)
  • OS: macOS (behavior is OS-independent — pure string logic)
  • TS Version: N/A (Java)
Model Information

Model-independent. Observed with GLM via an OpenAI-compatible gateway; the
classification happens in MessageConverter before 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:

  1. Add CJK terminal punctuation to the check: 。!? (plus …, )).
  2. Better: don't infer completeness from punctuation at all. Text legitimately ends
    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
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.adk.models.springai.MessageConverter;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;

import java.util.List;

MessageConverter converter = new MessageConverter(new ObjectMapper());

ChatResponse chineseEnding = new ChatResponse(List.of(new Generation(
        AssistantMessage.builder().content("根因是库存扣减为 0 未拦截。").build())));
ChatResponse asciiEnding = new ChatResponse(List.of(new Generation(
        AssistantMessage.builder().content("Root cause: zero-stock deduction is not blocked.").build())));

boolean chinesePartial = converter.toLlmResponse(chineseEnding, true).partial().orElse(false); // true  (bug)
boolean asciiPartial   = converter.toLlmResponse(asciiEnding, true).partial().orElse(false);   // false
How often has this issue occurred?

Always (100%) — every Chinese final response ending with 。, which is essentially
every conversation.

主要言語
Java
スター
1.7k
フォーク
431
平均マージ
2日 20時間
マージ済み PR(30日)
42

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

google/adk-java のほかの issue

google/adk-java の issue をすべて見る

似ている issue

Java の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。