Skip to content

fix(ai): disable thinking when clamped to off in buildGoogleSimpleThinking (#101785)#101825

Closed
liuhao1024 wants to merge 2 commits into
openclaw:mainfrom
liuhao1024:fix/fix-gemini-thinking-clamp-to-off-bug
Closed

fix(ai): disable thinking when clamped to off in buildGoogleSimpleThinking (#101785)#101825
liuhao1024 wants to merge 2 commits into
openclaw:mainfrom
liuhao1024:fix/fix-gemini-thinking-clamp-to-off-bug

Conversation

@liuhao1024

@liuhao1024 liuhao1024 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

When a user requests a reasoning level (e.g., low or medium) on a Google model that does not support thinking, the clampThinkingLevel function correctly clamps the requested level to "off" (indicating the model cannot support the requested reasoning capability). However, the buildGoogleSimpleThinking function in packages/ai/src/providers/google-shared.ts had a logic bug: it treated "off" as a valid reasoning level and mapped it to "high" effort, then incorrectly enabled thinking for models that should have it disabled.

This caused unexpected API failures or unnecessary token overhead when using non-thinking-capable models with reasoning settings that get clamped to "off".

Why This Change Was Made

The bug was in line 560-562 of packages/ai/src/providers/google-shared.ts:

// BEFORE (buggy):
const effort = (
  clampedReasoning === "off" || clampedReasoning === "max" ? "high" : clampedReasoning
) as ClampedGoogleThinkingLevel;

The problem: when clampThinkingLevel returns "off" (meaning "model doesn't support this level"), the ternary operator mapped "off" to "high" effort, and the code then enabled thinking with that high effort.

The fix adds an explicit check after clamping: if the reasoning level was clamped to "off", disable thinking entirely. This preserves the intent that only models supporting thinking should have it enabled, and only at supported levels.

User Impact

  • Users requesting reasoning levels on non-thinking-capable Google models (e.g., Gemini 1.5 Pro) will no longer encounter unexpected API failures from incorrectly enabled high-level thinking
  • Reduced unnecessary token overhead for deployments that accidentally use thinking settings on incompatible models
  • Fixes a P2 bug that could cause crashes or unexpected behavior in agent workflows using Google models with clamped reasoning settings

Evidence

  • Behavior addressed: buildGoogleSimpleThinking() incorrectly enabled HIGH-level thinking when clampThinkingLevel() clamped a requested reasoning level to "off" for non-reasoning-capable Google models.

  • Environment tested: OpenClaw source at commit 827f2c442 (upstream main), Node 22.19+, TypeScript strict mode.

  • Steps run after the patch:

    1. Added a focused regression case in packages/ai/src/providers/google-shared.test.ts that calls buildGoogleSimpleThinking() with a non-reasoning model (reasoning: false, id "gemini-1.5-pro") and reasoning: "low" — exercising the exact clamp-to-off path.
    2. Ran the verification on current main (without fix) to confirm the bug reproduces: the function returned enabled: true instead of enabled: false.
    3. Ran the same verification on the fix branch: the function correctly returns enabled: false.
  • Evidence after fix:
    Focused behavior verification — before fix (main, no patch):

    × buildGoogleSimpleThinking > disables thinking when clampThinkingLevel returns off for non-reasoning models
      AssertionError: expected true to be false
      - Expected: false
      + Received: true
    

    After fix (patch applied):

    ✓ buildGoogleSimpleThinking > disables thinking when clampThinkingLevel returns off for non-reasoning models
    

    The non-reasoning model with reasoning: "low" now returns { enabled: false } with no budgetTokens and no level, confirming thinking is fully disabled when clamped to off.

  • Observed result after fix: The clamp-to-off path in buildGoogleSimpleThinking() now correctly returns { enabled: false }. Before the fix, it returned { enabled: true } with high-effort thinking, causing API errors on non-thinking-capable Google models. The regression case fails on main and passes with the patch.

  • What was not tested: Google API network round-trips (the fix is in provider request-shaping logic before any network call; the function output is deterministic given model + options).

Real behavior proof

  • Behavior addressed: buildGoogleSimpleThinking() incorrectly enabled HIGH-level thinking when clampThinkingLevel() clamped a requested reasoning level to "off" for non-reasoning-capable Google models.

  • Environment tested: OpenClaw source at commit 827f2c442 (upstream main), Node 22.19+, TypeScript strict mode.

  • Steps run after the patch:

    1. Added a focused regression case in packages/ai/src/providers/google-shared.test.ts that calls buildGoogleSimpleThinking() with a non-reasoning model (reasoning: false, id "gemini-1.5-pro") and reasoning: "low" — exercising the exact clamp-to-off path.
    2. Ran the verification on current main (without fix) to confirm the bug reproduces: the function returned enabled: true instead of enabled: false.
    3. Ran the same verification on the fix branch: the function correctly returns enabled: false.
  • Evidence after fix:
    Focused behavior verification — before fix (main, no patch):

    × buildGoogleSimpleThinking > disables thinking when clampThinkingLevel returns off for non-reasoning models
      AssertionError: expected true to be false
      - Expected: false
      + Received: true
    

    After fix (patch applied):

    ✓ buildGoogleSimpleThinking > disables thinking when clampThinkingLevel returns off for non-reasoning models
    

    The non-reasoning model with reasoning: "low" now returns { enabled: false } with no budgetTokens and no level, confirming thinking is fully disabled when clamped to off.

  • Observed result after fix: The clamp-to-off path in buildGoogleSimpleThinking() now correctly returns { enabled: false }. Before the fix, it returned { enabled: true } with high-effort thinking, causing API errors on non-thinking-capable Google models. The regression case fails on main and passes with the patch.

  • What was not tested: Google API network round-trips (the fix is in provider request-shaping logic before any network call; the function output is deterministic given model + options).

  • AI-assisted (Hermes Agent)

…nking

When clampThinkingLevel returns 'off' for a model that doesn't support the
requested reasoning level, buildGoogleSimpleThinking now correctly disables
thinking entirely instead of mapping 'off' to 'high' effort and incorrectly
enabling it.

Fixes openclaw#101785
@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close as superseded: this PR contains a useful helper-level guard, but #101832 is the stronger active landing path because it fixes both the shared helper and the final Google simple-completion sanitizer path with positive runtime proof.

Root-cause cluster
Relationship: superseded
Canonical: #101832
Summary: This PR is a partial fix for the same Gemini clamp-to-off bug, while the canonical active PR covers both the shared helper and final sanitizer path with sufficient proof.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Canonical path: Close this branch and land or review #101832, or an equivalent two-surface fix, so the helper and final sanitizer behavior change together.

So I’m closing this here and keeping the remaining discussion on #101832.

Review details

Best possible solution:

Close this branch and land or review #101832, or an equivalent two-surface fix, so the helper and final sanitizer behavior change together.

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

Yes. Source inspection shows clampThinkingLevel() can return off, current main maps that helper result to enabled high thinking, and the wrapper can later re-map original xhigh/max into Gemini 3 HIGH.

Is this the best way to solve the issue?

No. The helper guard is useful but too narrow as a standalone fix; the maintainable solution also preserves the clamped effective level through the final simple-completion sanitizer.

Security review:

Security review cleared: The diff only changes Google provider request-shaping code and tests; no dependency, workflow, lockfile, script, permission, artifact, or secret-handling surface changed.

AGENTS.md: found and applied where relevant.

What I checked:

  • Root policy read: Root AGENTS.md was read fully and applied; no packages/ai scoped AGENTS.md exists, and src/agents/AGENTS.md was read because the superseding path touches an agent wrapper. (AGENTS.md:1, 22376d80e163)
  • Current main helper bug: Current main still maps clampedReasoning === "off" to high before returning enabled Google thinking, so the helper-level bug is real. (packages/ai/src/providers/google-shared.ts:559, 22376d80e163)
  • Current main final sanitizer gap: The Google simple-completion wrapper passes the original options.reasoning into sanitizeGoogleThinkingPayload(), so a requested xhigh/max can be restored to Gemini 3 HIGH after helper clamping unless the wrapper is also fixed. (src/agents/google-simple-completion-stream.ts:49, 22376d80e163)
  • Current PR diff: This PR only adds the shared-helper clamped-off return and helper tests; it does not change the simple-completion wrapper sanitizer input. (packages/ai/src/providers/google-shared.ts:557, bab9ade236e9)
  • Superseding PR coverage: The active superseding PR fixes the same helper guard and also clamps the reasoning level before passing it to the Google simple-completion sanitizer, with proof: sufficient and ready-for-maintainer-look labels. (src/agents/google-simple-completion-stream.ts:22, d4f31fb354fb)
  • Canonical issue context: The linked issue remains open and describes the same Gemini clamp-to-off bug; the superseding PR uses closing syntax for it and ClawSweeper already identified this PR as only a partial overlap.

Likely related people:

  • vincentkoc: Recent merged refactors touched packages/ai/src/providers/google-shared.ts, google.ts, and google-vertex.ts, narrowing provider declarations around the affected helper. (role: recent area contributor; confidence: medium; commits: 380bc24d25ca, 1b6f3e43d1e5; files: packages/ai/src/providers/google-shared.ts, packages/ai/src/providers/google.ts, packages/ai/src/providers/google-vertex.ts)
  • steipete: Earlier history introduced the Google thinking helper SDK-boundary work that underlies the sanitizer path involved in the complete fix. (role: feature history contributor; confidence: medium; commits: ec86d0f64a8f; files: src/plugin-sdk/provider-stream-shared.ts, src/llm/providers/stream-wrappers/google.ts)
  • giodl73-repo: An active maintainer-labeled PR by this author is working on the Google thinking payload sanitizer surface, which is the same final sanitizer path missing from this branch. (role: adjacent owner; confidence: medium; commits: 3df494680c6d; files: src/plugin-sdk/provider-stream-shared.ts)
  • joshavant: The compact checkout blames the affected helper and wrapper lines to a broad grafted snapshot commit; the commit title is unrelated, so this is only a weak routing clue. (role: current blame carrier; confidence: low; commits: 39a5bb89eb3f; files: packages/ai/src/providers/google-shared.ts, src/agents/google-simple-completion-stream.ts, src/plugin-sdk/provider-stream-shared.ts)

Codex review notes: model internal, reasoning high; reviewed against 22376d80e163.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 7, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. label Jul 8, 2026
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Superseded by #101832, now merged as f984209f54d5170efe29595a1308b7912d4fe321.

The landed fix includes this shared-builder correction and also clamps the final simple-completion payload sanitizer input, which is necessary to keep unsupported reasoning from being re-enabled later in the request path. It adds provider-builder, generic non-reasoning, and final Gemini 3 payload regressions. Exact-head CI passed: https://github.com/openclaw/openclaw/actions/runs/29005626431

Thank you for identifying the original clamp bug.

@steipete steipete closed this Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants