Skip to content

fix(providers): use native reasoning mode for Gemini instead of tagged#89379

Merged
steipete merged 5 commits into
openclaw:mainfrom
849261680:fix/gemini-reasoning-output-mode
Jun 2, 2026
Merged

fix(providers): use native reasoning mode for Gemini instead of tagged#89379
steipete merged 5 commits into
openclaw:mainfrom
849261680:fix/gemini-reasoning-output-mode

Conversation

@849261680

@849261680 849261680 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Gemini 2.5 Pro/Flash exposes reasoning via native thinkingParts through thinkingConfig.includeThoughts. The shared google-gemini family hook was selecting tagged reasoning output, which injected a <think>...</think>/<final>...</final> directive into the system prompt at the same time as the native thinking config was active.

The model then:

  1. Emits a native thought:true part
  2. Opens a <think> text block per the injected directive
  3. Pivots to a tool call before closing </think>
  4. Returns an empty post-tool assistant turn (content: [])

Gateway logs: incomplete turn detected: stopReason=stop payloads=0 - surfacing a spurious error to the user, while burning about 140K input tokens.

Changes

  • extensions/google/provider-registration.ts - override resolveReasoningOutputMode in the direct google provider to return "native", superseding the shared google-gemini family default of "tagged"
  • src/utils/provider-utils.ts - remove the now-dead BUILTIN_REASONING_OUTPUT_MODES entry keyed on "google-generative-ai": production callers pass the plugin id "google", not the transport API string, so the map entry was unreachable
  • docs/plugins/sdk-provider-plugins.md - document that direct Google uses native reasoning output while Gemini CLI keeps the tagged contract
  • Tests updated in both provider-utils.test.ts and extensions/google/index.test.ts

Real behavior proof

  • Behavior or issue addressed: Gemini 2.5 Pro/Flash with thinkingDefault: medium producing payloads=0 errors and spurious user-facing error messages on turns that include a tool call, as reported in Gemini text-tag reasoning conflicts with native thinking — produces unclosed <think>, empty post-tool turn, payloads=0 #69220.
  • Real environment tested: macOS arm64, Node v24.15.0, local source checkout on commit 2ed7ec9c3d; direct Google provider using auth profile google:default; model google/gemini-2.5-flash; embedded OpenClaw run with --thinking medium.
  • Exact steps or command run after this patch: pnpm openclaw agent --agent main --session-key pr-89379-gemini-proof --local --model google/gemini-2.5-flash --thinking medium --message 'Use an available local tool to run a harmless command that prints exactly OPENCLAW_GEMINI_PROOF, then reply with a one-sentence final answer that includes the command output. Do not search the web.' --json; node scripts/run-vitest.mjs src/utils/provider-utils.test.ts; node scripts/run-vitest.mjs extensions/google/index.test.ts; node ./node_modules/.bin/oxfmt --check docs/plugins/sdk-provider-plugins.md extensions/google/provider-registration.ts extensions/google/index.test.ts src/utils/provider-utils.ts src/utils/provider-utils.test.ts; pnpm docs:list; git diff --check.
  • Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output): copied live output and redacted runtime log from the patched direct Gemini run:
{
  "payloads": [
    {
      "text": "The command output is OPENCLAW_GEMINI_PROOF.",
      "mediaUrl": null
    }
  ],
  "meta": {
    "provider": "google",
    "model": "gemini-2.5-flash",
    "requestShaping": { "authMode": "auth-profile", "thinking": "medium" },
    "toolSummary": { "calls": 1, "tools": ["exec"], "failures": 0 },
    "completion": { "stopReason": "stop", "finishReason": "stop" },
    "executionTrace": {
      "winnerProvider": "google",
      "winnerModel": "gemini-2.5-flash",
      "result": "success",
      "runner": "embedded",
      "fallbackUsed": false
    }
  },
  "sessionScan": {
    "events": [
      { "type": "model_change", "provider": "google", "modelId": "gemini-2.5-flash" },
      { "type": "assistant", "contentTypes": ["thinking", "toolCall"], "toolNames": ["exec"], "stopReason": "toolUse" },
      { "type": "toolResult", "ok": true },
      { "type": "assistant", "contentTypes": ["text"], "stopReason": "stop" }
    ],
    "hasThinkTag": false,
    "hasFinalTag": false
  }
}
  • Observed result after fix: the live Gemini turn used one exec tool call, returned a post-tool assistant final text payload, and did not hit payloads=0; the session scan found no injected <think> or <final> tags. Focused tests passed: provider-utils.test.ts 25/25, extensions/google/index.test.ts 11/11. Formatting/docs/diff checks passed.
  • What was not tested: Gemini 2.5 Pro live API path was tested with gemini-2.5-flash; Gemini 2.5 Pro specifically and Gemini 3 were not tested in this local credential profile. Older Gemini models without native thinking are expected unaffected because native mode only prevents OpenClaw from injecting text-tag reasoning directives.
  • Proof limitations or environment constraints: proof uses local auth-profile credentials and a local embedded OpenClaw run, not Crabbox, so no secret-bearing remote run was needed.

Tests and validation

  • pnpm openclaw agent --agent main --session-key pr-89379-gemini-proof --local --model google/gemini-2.5-flash --thinking medium --message 'Use an available local tool to run a harmless command that prints exactly OPENCLAW_GEMINI_PROOF, then reply with a one-sentence final answer that includes the command output. Do not search the web.' --json
  • node scripts/run-vitest.mjs src/utils/provider-utils.test.ts - 25/25 passed
  • node scripts/run-vitest.mjs extensions/google/index.test.ts - 11/11 passed
  • node ./node_modules/.bin/oxfmt --check docs/plugins/sdk-provider-plugins.md extensions/google/provider-registration.ts extensions/google/index.test.ts src/utils/provider-utils.ts src/utils/provider-utils.test.ts - clean
  • pnpm docs:list - completed
  • git diff --check - clean

Fixes #69220

@openclaw-barnacle openclaw-barnacle Bot added extensions: google size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 2, 2026
@clawsweeper

clawsweeper Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 2, 2026, 5:55 AM ET / 09:55 UTC.

Summary
The PR changes direct Google Gemini reasoning output to native mode, removes the core google-generative-ai tagged fallback, updates Google/CLI regression tests, and documents the direct-vs-CLI reasoning contract.

PR surface: Source -10, Tests +16, Docs +7. Total +13 across 5 files.

Reproducibility: yes. from source and the linked live proof: current main can combine direct Gemini thinkingConfig.includeThoughts with the tagged <think>/<final> prompt directive. I did not run a live current-main repro in this read-only review.

Review metrics: 1 noteworthy metric.

  • Reasoning output modes: 1 direct provider changed to native; 1 CLI provider preserved as tagged. The quantified behavior split is the compatibility-sensitive part of the PR: direct Google changes output contract while Gemini CLI keeps the old tagged contract.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Optional: add Gemini 2.5 Pro or Gemini 3 live tool-call proof if maintainers want coverage beyond Flash before merge.

Risk before merge

  • [P1] Merging intentionally changes shipped direct Google Gemini output behavior from tagged <think>/<final> prompting to native thought parts, so maintainers should accept that older direct-Google final-tag enforcement is no longer the default.
  • [P1] The supplied live proof covers google/gemini-2.5-flash; Gemini 2.5 Pro and Gemini 3 are source-supported by the same path but not separately live-tested in the PR body.

Maintainer options:

  1. Accept the native-mode tradeoff (recommended)
    Merge with the current provider-local override, CLI regression test, docs update, and live Gemini 2.5 Flash proof if maintainers accept the direct-Google output-mode change.
  2. Broaden live provider proof first
    Ask for one additional Gemini 2.5 Pro or Gemini 3 tool-call run with thinking enabled if maintainers want live coverage beyond the Flash path before merge.

Next step before merge

  • No ClawSweeper repair lane is needed because review found no concrete patch defect; maintainers can handle merge/risk acceptance through the normal PR flow.

Security
Cleared: No concrete security or supply-chain concern found; the diff changes provider output-mode logic, tests, and docs without adding dependencies, secrets handling, CI, or code-download behavior.

Review details

Best possible solution:

Land the direct-Google native override with the CLI tagged invariant and docs/tests intact, after maintainers accept the provider-output compatibility tradeoff.

Do we have a high-confidence way to reproduce the issue?

Yes, from source and the linked live proof: current main can combine direct Gemini thinkingConfig.includeThoughts with the tagged <think>/<final> prompt directive. I did not run a live current-main repro in this read-only review.

Is this the best way to solve the issue?

Yes; the provider-local override is the narrowest maintainable fix because direct Google consumes native thought parts while Gemini CLI still needs the shared tagged text-output contract. Changing the shared GOOGLE_GEMINI_PROVIDER_HOOKS would be too broad.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against e24582d53cbf.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied live output and a redacted runtime/session scan from a patched Gemini 2.5 Flash --thinking medium tool-call run showing a final payload, no payloads=0, and no injected tags.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes copied live output and a redacted runtime/session scan from a patched Gemini 2.5 Flash --thinking medium tool-call run showing a final payload, no payloads=0, and no injected tags.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The linked bug affects real direct-Gemini agent/tool-call replies by producing empty post-tool payloads and user-facing errors.
  • merge-risk: 🚨 compatibility: The PR intentionally changes the shipped direct Google Gemini reasoning-output contract from tagged prompt directives to native thought parts.
  • merge-risk: 🚨 message-delivery: The changed output mode sits on the path that determines whether post-tool assistant text becomes a visible payload or an empty reply.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes copied live output and a redacted runtime/session scan from a patched Gemini 2.5 Flash --thinking medium tool-call run showing a final payload, no payloads=0, and no injected tags.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied live output and a redacted runtime/session scan from a patched Gemini 2.5 Flash --thinking medium tool-call run showing a final payload, no payloads=0, and no injected tags.
Evidence reviewed

PR surface:

Source -10, Tests +16, Docs +7. Total +13 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 2 6 16 -10
Tests 2 20 4 +16
Docs 1 8 1 +7
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 34 21 +13

What I checked:

  • Current main still injects tagged reasoning for Google-family providers: On current main, buildProviderReplayFamilyHooks({ family: "google-gemini" }) returns resolveReasoningOutputMode as tagged, and the direct Google provider spreads GOOGLE_GEMINI_PROVIDER_HOOKS without overriding it. (src/plugin-sdk/provider-model-shared.ts:238, e24582d53cbf)
  • Current main maps the transport API string to tagged as a fallback: BUILTIN_REASONING_OUTPUT_MODES still contains "google-generative-ai": "tagged" on current main, so the PR's deletion is not already implemented. (src/utils/provider-utils.ts:10, e24582d53cbf)
  • Runtime callers pass provider id and carry model API separately: The embedded runner calls isReasoningTagProvider(params.provider, ...) and passes modelApi: params.model.api only as context, supporting the PR's claim that provider ids own the output-mode decision. (src/agents/embedded-agent-runner/run/attempt.ts:1698, e24582d53cbf)
  • Direct Google already requests native thinking parts: The direct Google transport builds generationConfig.thinkingConfig with includeThoughts: true for thinking-enabled Gemini requests, so keeping tagged prompt directives at the same time creates overlapping reasoning contracts. (extensions/google/transport-stream.ts:441, e24582d53cbf)
  • Direct Google consumes native thought parts: The direct Google stream parser treats part.thought === true as a thinking block and persists thoughtSignature, matching native Gemini output rather than text-tag output. (extensions/google/transport-stream.ts:1251, e24582d53cbf)
  • Dependency contract supports native thought fields: The published @google/[email protected] types expose GenerateContentConfig.thinkingConfig, ThinkingConfig.includeThoughts, and Part.thought / Part.thoughtSignature, matching the PR's native-mode rationale. (extensions/google/package.json:8, e24582d53cbf)

Likely related people:

  • steipete: Recent GitHub commit history shows multiple Google provider and Gemini transport changes, including moving Gemini transport into the plugin and recovering Gemini tool-call thought signatures. (role: recent Google provider/runtime contributor; confidence: high; commits: 85826c83e4a1, 8dd91b14d3c0, 9ead0ae9219e; files: extensions/google/provider-registration.ts, extensions/google/transport-stream.ts)
  • vincentkoc: Commit 356110c52f79ff13e698cbae518a5c2ff8795b5d added GOOGLE_GEMINI_PROVIDER_HOOKS and shared the Gemini hook bundle between direct Google and Gemini CLI. (role: shared Google Gemini hook introducer; confidence: high; commits: 356110c52f79; files: extensions/google/provider-hooks.ts, extensions/google/provider-registration.ts, extensions/google/gemini-cli-provider.ts)
  • shakkernerd: Commit 590655472b4c2e20d273c746826b8c3fa904ff46 introduced the built-in reasoning provider fast path and the original google-generative-ai tagged fallback that this PR removes. (role: provider-utils reasoning fallback introducer; confidence: high; commits: 590655472b4c; files: src/utils/provider-utils.ts, src/utils/provider-utils.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 2, 2026
… CLI tagged

Gemini 2.5+ delivers reasoning via native thinkingParts (thinkingConfig.
includeThoughts). Having tagged mode active at the same time injects a
<think>…</think>/<final>…</final> directive into the system prompt; the
model opens a <think> block before a tool call, never closes it, and
returns an empty post-tool turn (content:[], payloads=0 error, openclaw#69220).

Fix: override resolveReasoningOutputMode in buildGoogleProvider() only —
not in the shared GOOGLE_GEMINI_PROVIDER_HOOKS. The Gemini CLI backend
(google-gemini-cli) runs gemini --output-format json and parses a text
response field, not native thought parts; it must stay on tagged mode.
A regression test confirms google-gemini-cli remains "tagged".

Also remove the dead BUILTIN_REASONING_OUTPUT_MODES entry keyed on
"google-generative-ai" from provider-utils.ts — that string is only
ever the transport model.api value, never the provider id passed to
resolveReasoningOutputMode, so the map was unreachable.

Fixes openclaw#69220
@849261680
849261680 force-pushed the fix/gemini-reasoning-output-mode branch from 58b1e75 to 76b1356 Compare June 2, 2026 08:07
@849261680

Copy link
Copy Markdown
Contributor Author

Addressed ClawSweeper's P1 finding: the resolveReasoningOutputMode: () => "native" override has been moved from the shared GOOGLE_GEMINI_PROVIDER_HOOKS to buildGoogleProvider() only.

google-gemini-cli continues to inherit "tagged" from the google-gemini family hook (no behavior change for CLI). A regression test (keeps google-gemini-cli on tagged reasoning mode) is now added to index.test.ts to lock this invariant.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@openclaw-barnacle openclaw-barnacle Bot added the docs Improvements or additions to documentation label Jun 2, 2026
@849261680

Copy link
Copy Markdown
Contributor Author

Updated the PR with the direct Google/Gemini docs alignment and a redacted live Gemini 2.5 Flash --thinking medium tool-call proof from the patched branch. The live run shows one exec tool call, a normal post-tool final payload, no payloads=0, and no injected <think> / <final> tags in the session scan.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 2, 2026
@849261680

Copy link
Copy Markdown
Contributor Author

The Real behavior proof section is now a proper ## Real behavior proof heading and the repo policy script evaluates the current PR body as passed. GitHub has also applied proof: supplied after the updated body.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 2, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 2, 2026
@steipete

steipete commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Maintainer verification for exact head 459dad98e2744be9ac2a81fbd829c8a57579b658:

  • Reviewed and kept the contributor's live direct-Gemini proof: google/gemini-2.5-flash with --thinking medium used an exec tool, returned final text, and did not produce the payloads=0 incomplete-turn failure from Gemini text-tag reasoning conflicts with native thinking — produces unclosed <think>, empty post-tool turn, payloads=0 #69220.
  • Added maintainer fixup e56ff80ffb so google-antigravity text-style hook aliases remain tagged while native Gemini transports stay native.
  • Added maintainer fixup 22025556a1 so provider-only direct google / google-vertex reasoning checks default to native when modelApi is not yet threaded, keeping final-tag filtering aligned with prompt mode.
  • Added maintainer fixup 459dad98e2 to import ProviderReasoningOutputModeContext from the plugin-entry SDK barrel, matching the exported type surface and fixing the type lanes.
  • Ran node scripts/run-vitest.mjs extensions/google/index.test.ts src/utils/provider-utils.test.ts: provider-utils.test.ts 25/25 and extensions/google/index.test.ts 11/11 passed.
  • Ran node ./node_modules/.bin/oxfmt --check docs/plugins/sdk-provider-plugins.md extensions/google/provider-registration.ts extensions/google/index.test.ts src/utils/provider-utils.ts src/utils/provider-utils.test.ts.
  • Ran git diff --check.
  • Ran pnpm docs:list.
  • Ran pnpm tsgo:extensions and pnpm check:test-types after the maintainer import fix.
  • Ran branch autoreview against origin/main on the final branch shape; no accepted/actionable findings remained.
  • Exact-head GitHub checks are green: 141 pass, 25 skipped.

Known proof gap: the live Gemini run was from the contributor proof before the maintainer alias/type fixups; those fixups were covered with source assertions, focused tests, type lanes, autoreview, and exact-head CI.

@steipete
steipete merged commit b1bdc29 into openclaw:main Jun 2, 2026
166 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 3, 2026
openclaw#89379)

* fix(providers): use native reasoning mode for direct Gemini API, keep CLI tagged

Gemini 2.5+ delivers reasoning via native thinkingParts (thinkingConfig.
includeThoughts). Having tagged mode active at the same time injects a
<think>…</think>/<final>…</final> directive into the system prompt; the
model opens a <think> block before a tool call, never closes it, and
returns an empty post-tool turn (content:[], payloads=0 error, openclaw#69220).

Fix: override resolveReasoningOutputMode in buildGoogleProvider() only —
not in the shared GOOGLE_GEMINI_PROVIDER_HOOKS. The Gemini CLI backend
(google-gemini-cli) runs gemini --output-format json and parses a text
response field, not native thought parts; it must stay on tagged mode.
A regression test confirms google-gemini-cli remains "tagged".

Also remove the dead BUILTIN_REASONING_OUTPUT_MODES entry keyed on
"google-generative-ai" from provider-utils.ts — that string is only
ever the transport model.api value, never the provider id passed to
resolveReasoningOutputMode, so the map was unreachable.

Fixes openclaw#69220

* docs: clarify Gemini reasoning output modes

* fix(google): keep Antigravity reasoning tagged

* fix(google): default direct reasoning checks to native

* fix(google): import reasoning context from plugin entry

---------

Co-authored-by: Peter Steinberger <[email protected]>
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
openclaw#89379)

* fix(providers): use native reasoning mode for direct Gemini API, keep CLI tagged

Gemini 2.5+ delivers reasoning via native thinkingParts (thinkingConfig.
includeThoughts). Having tagged mode active at the same time injects a
<think>…</think>/<final>…</final> directive into the system prompt; the
model opens a <think> block before a tool call, never closes it, and
returns an empty post-tool turn (content:[], payloads=0 error, openclaw#69220).

Fix: override resolveReasoningOutputMode in buildGoogleProvider() only —
not in the shared GOOGLE_GEMINI_PROVIDER_HOOKS. The Gemini CLI backend
(google-gemini-cli) runs gemini --output-format json and parses a text
response field, not native thought parts; it must stay on tagged mode.
A regression test confirms google-gemini-cli remains "tagged".

Also remove the dead BUILTIN_REASONING_OUTPUT_MODES entry keyed on
"google-generative-ai" from provider-utils.ts — that string is only
ever the transport model.api value, never the provider id passed to
resolveReasoningOutputMode, so the map was unreachable.

Fixes openclaw#69220

* docs: clarify Gemini reasoning output modes

* fix(google): keep Antigravity reasoning tagged

* fix(google): default direct reasoning checks to native

* fix(google): import reasoning context from plugin entry

---------

Co-authored-by: Peter Steinberger <[email protected]>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
openclaw#89379)

* fix(providers): use native reasoning mode for direct Gemini API, keep CLI tagged

Gemini 2.5+ delivers reasoning via native thinkingParts (thinkingConfig.
includeThoughts). Having tagged mode active at the same time injects a
<think>…</think>/<final>…</final> directive into the system prompt; the
model opens a <think> block before a tool call, never closes it, and
returns an empty post-tool turn (content:[], payloads=0 error, openclaw#69220).

Fix: override resolveReasoningOutputMode in buildGoogleProvider() only —
not in the shared GOOGLE_GEMINI_PROVIDER_HOOKS. The Gemini CLI backend
(google-gemini-cli) runs gemini --output-format json and parses a text
response field, not native thought parts; it must stay on tagged mode.
A regression test confirms google-gemini-cli remains "tagged".

Also remove the dead BUILTIN_REASONING_OUTPUT_MODES entry keyed on
"google-generative-ai" from provider-utils.ts — that string is only
ever the transport model.api value, never the provider id passed to
resolveReasoningOutputMode, so the map was unreachable.

Fixes openclaw#69220

* docs: clarify Gemini reasoning output modes

* fix(google): keep Antigravity reasoning tagged

* fix(google): default direct reasoning checks to native

* fix(google): import reasoning context from plugin entry

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation extensions: google merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gemini text-tag reasoning conflicts with native thinking — produces unclosed <think>, empty post-tool turn, payloads=0

2 participants