Skip to content

fix(ai-gemini): dedupe functionResponse parts by id, not name - #1198

Open
citizen204 wants to merge 3 commits into
TanStack:mainfrom
citizen204:fix-894-gemini-parallel-tool-dedup
Open

fix(ai-gemini): dedupe functionResponse parts by id, not name#1198
citizen204 wants to merge 3 commits into
TanStack:mainfrom
citizen204:fix-894-gemini-parallel-tool-dedup

Conversation

@citizen204

@citizen204 citizen204 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Summary

When Gemini fires two or more parallel calls to the same tool in one turn, mergeConsecutiveSameRoleMessages deduped functionResponse parts by name, so the second (and any further) response to a repeated same-tool call was silently dropped. Gemini requires exactly one response part per call part in a turn, so the next request 400s:

INVALID_ARGUMENT: Please ensure that the number of function response parts is equal to the number of function call parts of the function call turn.

Every functionResponse already carries a unique id (msg.toolCallId, set at both construction sites) — keying the dedup on id instead keeps both parallel responses while still collapsing a genuine duplicate tool result (same id sent twice).

Fixes #894

Changes

  • packages/ai-gemini/src/adapters/text.ts: dedupe functionResponse parts by id instead of name in mergeConsecutiveSameRoleMessages.
  • packages/ai-gemini/tests/gemini-adapter.test.ts: regression test with two parallel calls to the same tool (distinct ids) — asserts both functionResponse parts survive.
  • .changeset/gemini-parallel-tool-dedup.md: patch changeset.

Ran the full ai-gemini suite (vitest run in packages/ai-gemini, after building its @tanstack/ai, @tanstack/ai-event-client, @tanstack/ai-utils workspace deps) — 325 passed, 18 files.

Note: an earlier PR (#960) attempted this exact fix and was self-closed by its author for unrelated reasons ("keeping open PRs lean") after CodeRabbit review raised no substantive objections — the bug is still present on main, so re-submitting with the same diagnosis plus a parallel-call regression test.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed an issue where responses from parallel calls to the same tool could be dropped.
    • Prevented request errors when multiple tool calls occur in the same turn.
    • Preserved distinct responses while still removing genuine duplicates.
  • Tests

    • Added coverage for parallel tool calls with separate call identifiers.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2be3e0a8-860b-4252-92a5-e471c430e8b0

📥 Commits

Reviewing files that changed from the base of the PR and between 2544a39 and d9c99cd.

📒 Files selected for processing (1)
  • packages/ai-gemini/tests/gemini-adapter.test.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The Gemini adapter now deduplicates functionResponse parts by tool-call ID instead of tool name. A regression test verifies that parallel calls to the same tool preserve both responses. A patch changeset records the fix.

Changes

Gemini tool response fix

Layer / File(s) Summary
Deduplicate responses by tool-call ID
.changeset/gemini-parallel-tool-dedup.md, packages/ai-gemini/src/adapters/text.ts
functionResponse deduplication now uses id, so parallel calls with the same tool name retain separate responses.
Validate parallel same-tool responses
packages/ai-gemini/tests/gemini-adapter.test.ts
The regression test verifies that responses with IDs call_1 and call_2 both remain in the Gemini payload.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to d9c99

This is a localized Gemini response-deduplication fix with a regression test for parallel same-tool calls; no actionable merge-blocking risk remains beyond normal checks and review.

Possibly related PRs

  • TanStack/ai#960: Implements the same Gemini response deduplication fix and regression coverage.

Suggested reviewers: alemtuzlak, tombeckenham

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: deduplicating Gemini functionResponse parts by ID instead of name.
Description check ✅ Passed The description clearly explains the bug, fix, affected files, regression test, changeset, and test results, although it omits the template checklist and release-impact sections.
Linked Issues check ✅ Passed The changes satisfy issue #894 by deduplicating functionResponse parts by unique tool-call ID and preserving parallel same-tool responses.
Out of Scope Changes check ✅ Passed The implementation, regression test, and patch changeset are directly related to the linked issue and PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2 files.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the PR, @citizen204! 🙌 @AlemTuzlak will take a look.

Automated pre-review checks

  • ✅ CI passing
  • ✅ No merge conflicts
  • ✅ Changeset present
  • ⚠️ No E2E test changes detected — behavior changes need coverage under testing/e2e/ (see CONTRIBUTING)

Automated triage — a human review follows.

@github-actions github-actions Bot added the waiting-on: maintainer The ball is in the maintainers’ court label Aug 22, 2026
Two parallel calls to the same tool share a functionResponse.name but
have distinct ids (msg.toolCallId, already set at both construction
sites). mergeConsecutiveSameRoleMessages deduped by name, so the
second response to a repeated same-tool call was dropped, leaving
Gemini with fewer response parts than call parts on the next request:

  400 INVALID_ARGUMENT: Please ensure that the number of function
  response parts is equal to the number of function call parts of the
  function call turn.

Key the dedup on functionResponse.id instead — it still collapses a
genuine duplicate tool result (same id twice), and now also preserves
both responses when the model fires the same tool twice in one turn.

Fixes TanStack#894
@tombeckenham
tombeckenham force-pushed the fix-894-gemini-parallel-tool-dedup branch from 2544a39 to 3426899 Compare August 22, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on: maintainer The ball is in the maintainers’ court

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ai-gemini: parallel calls to the same tool drop a functionResponse part, causing Gemini 400 (function response/call part count mismatch)

2 participants