Skip to content

fix(gemini): handle thinking model response parts correctly#1140

Merged
chumyin merged 1 commit intozeroclaw-labs:mainfrom
mackenzieclark:fix/gemini-thinking-response
Feb 21, 2026
Merged

fix(gemini): handle thinking model response parts correctly#1140
chumyin merged 1 commit intozeroclaw-labs:mainfrom
mackenzieclark:fix/gemini-thinking-response

Conversation

@mackenzieclark
Copy link
Copy Markdown
Contributor

Summary

  • Gemini thinking models (e.g. gemini-3-pro-preview) return response parts with thought: true (internal reasoning) and thoughtSignature (opaque signature blob). The current extraction logic takes the first part, which is the thinking part, returning reasoning text instead of the actual answer — or failing with "error decoding response body" when the response structure doesn't match expectations.
  • Adds thought field to ResponsePart, implements effective_text() on CandidateContent to skip thinking/signature parts, updates extraction to use it.
  • Makes Candidate.content optional to guard against candidates with no content (e.g. safety-filtered responses).
  • 7 new inline tests covering thinking, non-thinking, fallback, empty, multi-part, signature-only, and internal API (cloudcode-pa) responses.
  • Non-thinking models are completely unaffected.

Problem

When using gemini-3-pro-preview (or other thinking-enabled Gemini models), the API returns parts like:

{
  "candidates": [{
    "content": {
      "parts": [
        {"thought": true, "text": "Let me reason about this..."},
        {"text": "The actual answer."},
        {"thoughtSignature": "base64blob..."}
      ]
    }
  }]
}

The previous code blindly took .parts.into_iter().next(), which returned the thinking part instead of the answer. Parts with only thoughtSignature (no text) were noise that could cause empty responses.

Changes

src/providers/gemini.rs

  • ResponsePart: adds thought: bool field (#[serde(default)]) to detect reasoning parts
  • Candidate: makes content optional (Option<CandidateContent>) for robustness
  • CandidateContent::effective_text(): new method following the effective_content() pattern from OpenAI provider — skips thought: true parts, skips signature-only parts, concatenates non-thinking text, falls back to thinking text if that's all that's available
  • send_generate_content: uses effective_text() instead of .parts.into_iter().next().and_then(|p| p.text)

CHANGELOG.md

  • Entry under [Unreleased] > Fixed

docs/providers-reference.md

  • Note about thinking model support in Gemini Notes section

Validation Evidence

  • cargo check --lib — compiles clean (no new warnings)
  • cargo clippy — no issues in gemini.rs
  • cargo fmt — no formatting issues in changed files
  • Note: cargo test --lib is blocked by a pre-existing compilation error in src/agent/loop_.rs:3603 (missing 8th arg to build_system_prompt_with_mode) — unrelated to this PR

Security Impact

  • Risk level: low
  • Change type: additive response parsing only
  • No new privileges, no policy changes, no secret handling changes
  • Non-thinking models remain on the existing code path

Rollback Plan

  • Revert this commit to restore previous extraction behavior
  • No schema or config changes, no stateful data migration required

🤖 Generated with Claude Code

Gemini thinking models (e.g. gemini-3-pro-preview) return response parts
with `thought: true` for internal reasoning and `thoughtSignature` for
opaque signatures. The previous extraction logic blindly took the first
part, which was the thinking part, returning reasoning text instead of the
actual answer.

- Add `thought` field to `ResponsePart` to detect reasoning parts
- Add `effective_text()` on `CandidateContent` to skip thinking/signature
  parts and extract only the real answer (falls back to thinking text if
  no non-thinking content is available)
- Make `Candidate.content` optional to guard against candidates with no
  content (e.g. safety-filtered responses)
- Add 7 focused tests covering thinking, non-thinking, fallback, empty,
  multi-part, signature-only, and internal API responses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

PR intake checks found warnings (non-blocking)

Fast safe checks found advisory issues. CI lint/test/build gates still enforce merge quality.

  • Missing required PR template sections: ## Validation Evidence (required), ## Security Impact (required), ## Privacy and Data Hygiene (required), ## Rollback Plan (required)
  • Incomplete required PR template fields: summary problem, summary why it matters, summary what changed, validation commands, security risk/mitigation, privacy status, rollback plan

Action items:

  1. Complete required PR template sections/fields.
  2. Remove tabs, trailing whitespace, and merge conflict markers from added lines.
  3. Re-run local checks before pushing:
    • ./scripts/ci/rust_quality_gate.sh
    • ./scripts/ci/rust_strict_delta_gate.sh
    • ./scripts/ci/docs_quality_gate.sh

Run logs: https://github.com/zeroclaw-labs/zeroclaw/actions/runs/22238834237

Detected blocking line issues (sample):

  • none

Detected advisory line issues (sample):

  • none

@github-actions
Copy link
Copy Markdown

Thanks for contributing to ZeroClaw.

For faster review, please ensure:

  • PR template sections are fully completed
  • cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test are included
  • If automation/agents were used heavily, add brief workflow notes
  • Scope is focused (prefer one concern per PR)

See CONTRIBUTING.md and docs/pr-workflow.md for full collaboration rules.

@github-actions github-actions Bot added docs Auto scope: docs/markdown/template files changed. provider Auto scope: src/providers/** changed. size: S Auto size: 81-250 non-doc changed lines. risk: medium Auto risk: src/** or dependency/config changes. labels Feb 20, 2026
@chumyin chumyin assigned chumyin and unassigned chumyin Feb 20, 2026
@chumyin chumyin merged commit 43f7bfa into zeroclaw-labs:main Feb 21, 2026
5 checks passed
@chumyin
Copy link
Copy Markdown
Contributor

chumyin commented Feb 21, 2026

Thanks for the contribution and collaboration.\n\nFinalization update:\n- Verified PR state, review status, and checks for merge eligibility.\n- Completed rebase merge.\n\nWe are currently conducting ZeroClaw automated testing. This is an automated comment from ZeroClaw. If you have any questions, please contact @chumyin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Auto scope: docs/markdown/template files changed. provider Auto scope: src/providers/** changed. risk: medium Auto risk: src/** or dependency/config changes. size: S Auto size: 81-250 non-doc changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants