Skip to content

feat(gemini): 支持 Gemini Thinking 并持久化 thought signature#2335

Open
wzl521 wants to merge 2 commits into
agentscope-ai:mainfrom
wzl521:feat/2240-gemini-thinking-enhancement
Open

feat(gemini): 支持 Gemini Thinking 并持久化 thought signature#2335
wzl521 wants to merge 2 commits into
agentscope-ai:mainfrom
wzl521:feat/2240-gemini-thinking-enhancement

Conversation

@wzl521

@wzl521 wzl521 commented Jul 21, 2026

Copy link
Copy Markdown

Background

#2240 — The Gemini model adapter lacked first-class support for Gemini's built-in thinking capability. There were several gaps:

  1. No includeThoughts / thinkingLevel options — GenerateOptions only had thinkingBudget, but Gemini's API requires includeThoughts (to independently control whether thoughts appear in responses) and thinkingLevel (e.g., "minimal",
    "low", "medium", "high") for the latest models.
  2. thoughtSignature was not persisted — Gemini returns an opaque thoughtSignature on each part. This signature must be round-tripped back to Gemini on subsequent turns, but the codebase only captured it on ToolUseBlock (via
    ToolUseBlock.METADATA_THOUGHT_SIGNATURE), not on TextBlock or ThinkingBlock. After a JSON serialization round-trip, the byte-array signature was lost or corrupted.
  3. Part boundaries were lost during accumulation — ThinkingAccumulator and TextAccumulator merged all chunks into a single block. When a Gemini response contained multiple parts each with its own thoughtSignature, the boundaries
    were destroyed, making it impossible to reconstruct distinct signed parts in the next request.

Changes

Core (agentscope-core)

  • GenerateOptions: Added includeThoughts(Boolean) and thinkingLevel(String) options alongside the existing thinkingBudget. These are now properly merged in merge(GenerateOptions, GenerateOptions).
  • ContentBlockMetadataKeys: New constant class with THOUGHT_SIGNATURE key, replacing the ad-hoc constant previously defined on ToolUseBlock. This is the single source of truth for the thought signature metadata key across all
    content block types.
  • TextBlock: Added metadata field (provider-specific Map<String, Object>) with getter and builder support. This enables text blocks to carry thoughtSignature.
  • ThinkingBlock: Already had metadata support; wired into the new signature persistence pipeline.
  • ToolUseBlock: Replaced the local METADATA_THOUGHT_SIGNATURE constant with ContentBlockMetadataKeys.THOUGHT_SIGNATURE reference. No behavioral change.
  • TextAccumulator / ThinkingAccumulator: Introduced buildAllTextBlocks() / buildAllThinkingBlocks() methods that preserve thought-signature Part boundaries. When a chunk carries a thoughtSignature, the current block is completed
    and a new one begins. The existing buildAggregated() method continues to return a single merged block for backward compatibility.
  • ReasoningContext: Switched from buildAggregated() to buildAllXXXBlocks() for both text and thinking, ensuring the final assistant message preserves distinct signed parts.

Gemini Extension (agentscope-extensions-model-gemini)

  • GeminiThoughtSignatureUtils (new): Utility class that:
    • Extracts thoughtSignature from a Gemini Part into content block metadata (as byte[] in memory).
    • Restores a persisted signature back onto a Gemini Part.Builder — supports both byte[] (in-memory) and Base64-encoded String (after JSON serialization).
  • GeminiChatFormatter: Updated applyOptions to build ThinkingConfig from all three fields (thinkingBudget, thinkingLevel, includeThoughts). When only thinkingBudget is set (legacy callers), includeThoughts defaults to true
    preserving existing behavior. When includeThoughts is explicitly set, it always takes precedence.
  • GeminiResponseParser: Refactored parsePartsToBlocks to:
    • Preserve original Part order (no longer reorders thinking → text → toolCall).
    • Attach thoughtSignature metadata to ALL block types (ThinkingBlock, TextBlock, ToolUseBlock) via GeminiThoughtSignatureUtils.extractMetadata.
    • Emit empty blocks when only metadata (signature) is present.
  • GeminiMessageConverter: Updated to restore thoughtSignature from content block metadata onto Gemini Part objects for all assistant-role blocks (ThinkingBlock → Part.thought(true), TextBlock → text Part, ToolUseBlock →
    functionCall Part) via GeminiThoughtSignatureUtils.applyMetadata.

Documentation

  • docs/v2/en/integration/model/gemini.md: Added "Thinking" section documenting thinkingLevel, includeThoughts, and the signature round-trip behavior.
  • docs/v2/zh/integration/model/gemini.md: Chinese translation.
  • docs/v2/en/integration/model/gemini-request-flow.md (new): Full end-to-end Mermaid diagram of a Gemini request, covering message conversion, thinking config, signature extraction/persistence, and the three completion paths (plain
    answer / tool call / persistent history replay).
  • docs/v2/zh/integration/model/gemini-request-flow.md (new): Chinese translation.

- 新增 ContentBlockMetadataKeys 常量类统一管理 metadata 键
- ToolUseBlock 改用 THOUGHT_SIGNATURE 常量替代硬编码字符串
- 新增 GeminiThoughtSignatureUtils 工具类处理 byte[] 与 Base64 转换
- GeminiMessageConverter 支持 ThinkingBlock 转换及 thoughtSignature 恢复
- GeminiResponseParser 将 thoughtSignature 解析到 ContentBlock metadata
- TextBlock 新增 metadata 字段支持 JSON 序列化
- ThinkingAccumulator 和 TextAccumulator 新增 metadata 积累逻辑
- GenerateOptions 新增 includeThoughts 和 thinkingLevel 字段
- GeminiChatFormatter 按字段构建 ThinkingConfig(thinkingBudget / thinkingLevel / includeThoughts)
- 新增端到端测试验证 signature 在流式聚合、JSON 持久化、回放中的完整性
- 更新中英文文档
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


武志立 seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.38650% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...model/gemini/formatter/GeminiMessageConverter.java 93.33% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

- 修改 TextAccumulator 与 ThinkingAccumulator,在元数据包含 THOUGHT_SIGNATURE 时立即结束当前分区,从而维持签名边界独立
- 新增 buildAllTextBlocks() / buildAllThinkingBlocks() 方法,返回保留原始顺序的多分区列表
- 更新 ReasoningContext.buildFinalMessage(),使用 addAll 替代此前合并的单个块,使多个签名分区能够被正确序列化与重放
- 修正 GeminiResponseParser 对空文本但携带签名的处理,允许空字符串的 thinking/text 部分仅凭 signed metadata 保留
- 新增测试用例覆盖:空签名分区、非助手消息跳过 ThinkingBlock、签名边界保留、Base64 编解码及非法签名拒绝等场景
@wzl521

wzl521 commented Jul 21, 2026

Copy link
Copy Markdown
Author

@oss-maintainer

Copy link
Copy Markdown
Collaborator

CLA Not Signed

The Contributor License Agreement (CLA) check is currently pending on this PR (license/cla: Contributor License Agreement is not signed yet.). This PR cannot be merged until the CLA is signed.

@wzl521 please sign the CLA via the CLA assistant badge in the comment above, or visit https://cla-assistant.io/agentscope-ai/agentscope-java. Once signed, the license/cla status will turn green.


Automated check by github-manager-bot

@wzl521

wzl521 commented Jul 23, 2026

Copy link
Copy Markdown
Author

I have signed the CLA,please retry it!

@wzl521

wzl521 commented Jul 23, 2026

Copy link
Copy Markdown
Author

CLA Not Signed  尚未签署 CLA 文件

The Contributor License Agreement (CLA) check is currently pending on this PR (license/cla: Contributor License Agreement is not signed yet.). This PR cannot be merged until the CLA is signed.关于《贡献者许可协议》的确认工作目前仍处于待处理状态( license/cla :尚未签署《贡献者许可协议》)。在签署该协议之前,无法合并此拉取请求。

@wzl521 please sign the CLA via the CLA assistant badge in the comment above, or visit https://cla-assistant.io/agentscope-ai/agentscope-java. Once signed, the license/cla status will turn green.请通过上面评论中的 CLA 助手按钮签署 CLA 文件,或者访问 https://cla-assistant.io/agentscope-ai/agentscope-java。一旦签署完成, license/cla 的状态就会变为绿色。

Automated check by github-manager-bot由 github-manager-bot 进行的自动化检查

I have signed the CLA,please retry it!

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.

3 participants