Skip to content

fix(adapters): set AG-UI parentMessageId on tool-first tool calls#739

Closed
tombeckenham wants to merge 5 commits into
mainfrom
fix/tool-first-parent-message-id-all-adapters
Closed

fix(adapters): set AG-UI parentMessageId on tool-first tool calls#739
tombeckenham wants to merge 5 commits into
mainfrom
fix/tool-first-parent-message-id-all-adapters

Conversation

@tombeckenham

@tombeckenham tombeckenham commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Set AG-UI parentMessageId on TOOL_CALL_START in every text adapter so tool-first streams keep a stable assistant message id — a tool call that streams before any text no longer forces UIMessage.id to change mid-stream (which can remount the message subtree in useChat).
  • Extends fix: preserve anthropic assistant ids in tool-first streams #480 (Anthropic-only) to all adapters that emit TOOL_CALL_START: OpenAI Responses + Chat Completions (@tanstack/openai-base), OpenRouter (both), Gemini (text + experimental text-interactions), and Ollama.
  • Adds a tool-first regression per adapter asserting TOOL_CALL_START.parentMessageId === TEXT_MESSAGE_START.messageId.

The processor already consumes the field (chunk.parentMessageId ?? activeAssistantId), so the message is created with the correct id immediately and the later TEXT_MESSAGE_START matches — no rename, no remount.

fixes #477

Verification

  • pnpm --filter @tanstack/openai-base --filter @tanstack/ai-gemini --filter @tanstack/ai-ollama --filter @tanstack/ai-openrouter --filter @tanstack/ai-anthropic exec vitest run496 passed (incl. 6 new tool-first regressions)
  • nx run-many --target=test:types across the 5 packages — pass
  • nx run-many --target=test:eslint across the 5 packages — 0 errors

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed message ID stability when tool calls are emitted before text content, preventing unnecessary UI remounting and key instability in tool-first streaming scenarios.
  • Tests

    • Added comprehensive test coverage across all AI adapters to verify correct message linkage in tool-first streaming flows.

shaked-frame and others added 5 commits April 20, 2026 18:56
Bind TOOL_CALL_START to the stream's stable assistant message id via
AG-UI `parentMessageId` in every text adapter, so a tool call that
streams before any text no longer forces the assistant message id to
change mid-stream (which destabilises UIMessage.id and can remount the
message subtree in useChat). Fixes #477.

Extends #480 (Anthropic only) to:
- @tanstack/openai-base (Responses + Chat Completions)
- @tanstack/ai-openrouter (Responses + Chat Completions)
- @tanstack/ai-gemini (text + experimental text-interactions)
- @tanstack/ai-ollama

Adds a tool-first regression per adapter asserting the TOOL_CALL_START
parentMessageId equals the TEXT_MESSAGE_START messageId.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 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

Run ID: f9598237-84d4-4802-8062-b2e5c80c5189

📥 Commits

Reviewing files that changed from the base of the PR and between 570c08a and d24a601.

📒 Files selected for processing (18)
  • .changeset/tool-first-parent-message-id.md
  • packages/ai-anthropic/src/adapters/text.ts
  • packages/ai-anthropic/tests/anthropic-adapter.test.ts
  • packages/ai-gemini/src/adapters/text.ts
  • packages/ai-gemini/src/experimental/text-interactions/adapter.ts
  • packages/ai-gemini/tests/gemini-adapter.test.ts
  • packages/ai-gemini/tests/text-interactions-adapter.test.ts
  • packages/ai-ollama/src/adapters/text.ts
  • packages/ai-ollama/tests/text-adapter.test.ts
  • packages/ai-openrouter/src/adapters/responses-text.ts
  • packages/ai-openrouter/src/adapters/text.ts
  • packages/ai-openrouter/tests/openrouter-adapter.test.ts
  • packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts
  • packages/ai/tests/stream-processor.test.ts
  • packages/openai-base/src/adapters/chat-completions-text.ts
  • packages/openai-base/src/adapters/responses-text.ts
  • packages/openai-base/tests/chat-completions-text.test.ts
  • packages/openai-base/tests/responses-text.test.ts

📝 Walkthrough

Walkthrough

This PR adds a parentMessageId field to TOOL_CALL_START events across all AI provider adapters (Anthropic, Gemini, Ollama, OpenRouter, OpenAI Base). When tool calls arrive before text in streaming responses, this field binds them to the stable assistant message id, preventing mid-stream message id reassignment in UI components.

Changes

Tool-first streaming parentMessageId fix

Layer / File(s) Summary
Changeset documentation
.changeset/tool-first-parent-message-id.md
Documents the patch release and fix for tool-first streaming: TOOL_CALL_START events now carry parentMessageId to bind them to the stable TEXT_MESSAGE_START message id.
Adapter implementations: parentMessageId addition
packages/ai-anthropic/src/adapters/text.ts, packages/ai-gemini/src/adapters/text.ts, packages/ai-gemini/src/experimental/text-interactions/adapter.ts, packages/ai-ollama/src/adapters/text.ts, packages/ai-openrouter/src/adapters/text.ts, packages/ai-openrouter/src/adapters/responses-text.ts, packages/openai-base/src/adapters/chat-completions-text.ts, packages/openai-base/src/adapters/responses-text.ts
All streaming adapters now emit parentMessageId when issuing TOOL_CALL_START events, linking each tool call to the current assistant message id. Anthropic and Gemini adapters add the field at both content-delta and block-stop points; OpenRouter and OpenAI Base add it in three lifecycle locations (item-added, item-done backfill, and completed backstop).
Adapter test coverage: tool-first streaming verification
packages/ai-anthropic/tests/anthropic-adapter.test.ts, packages/ai-gemini/tests/gemini-adapter.test.ts, packages/ai-gemini/tests/text-interactions-adapter.test.ts, packages/ai-ollama/tests/text-adapter.test.ts, packages/ai-openrouter/tests/openrouter-adapter.test.ts, packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts, packages/openai-base/tests/chat-completions-text.test.ts, packages/openai-base/tests/responses-text.test.ts
Each adapter test suite includes a new tool-first streaming case verifying that TOOL_CALL_START.parentMessageId matches the subsequent TEXT_MESSAGE_START.messageId, ensuring message id stability mid-stream across all providers.
Stream processor integration test: message consolidation with parentMessageId
packages/ai/tests/stream-processor.test.ts
Validates end-to-end that the processor preserves the server-provided message id (via parentMessageId) when tool calls arrive first, correctly merging tool-call and text parts into a single assistant message with stable identity.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • TanStack/ai#606: Updates Anthropic streaming adapter's server_tool_use handling to prevent client tool-args corruption, modifying adjacent tool-event processing logic in the same adapter file.

Suggested reviewers

  • AlemTuzlak

Poem

🐰 Tool calls rushing in, before the text takes flight,
Now they find their anchor—a message id so tight,
No more mid-stream remount, no flickering display,
The parent link keeps them stable, come what may!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: setting parentMessageId on TOOL_CALL_START events to fix tool-first streaming stability.
Description check ✅ Passed The PR description provides comprehensive context including the problem summary, affected adapters, test coverage, and verification results matching the template's key requirements.
Linked Issues check ✅ Passed The PR fully addresses issue #477 by setting parentMessageId on TOOL_CALL_START across all adapters, ensuring stable assistant message IDs during tool-first streaming to prevent mid-stream remounts.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to the stated objective: adding parentMessageId to TOOL_CALL_START events in adapters and comprehensive regression tests for tool-first scenarios.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tool-first-parent-message-id-all-adapters

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 and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

10 package(s) bumped directly, 21 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-anthropic 0.15.1 → 1.0.0 Changeset
@tanstack/ai-event-client 0.5.4 → 1.0.0 Changeset
@tanstack/ai-fal 0.7.23 → 1.0.0 Changeset
@tanstack/ai-gemini 0.15.1 → 1.0.0 Changeset
@tanstack/ai-ollama 0.8.1 → 1.0.0 Changeset
@tanstack/ai-openai 0.14.1 → 1.0.0 Changeset
@tanstack/ai-openrouter 0.13.1 → 1.0.0 Changeset
@tanstack/openai-base 0.8.1 → 1.0.0 Changeset
@tanstack/ai-code-mode 0.2.5 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.2.5 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.2.20 → 1.0.0 Dependent
@tanstack/ai-grok 0.11.2 → 1.0.0 Dependent
@tanstack/ai-groq 0.4.2 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.30 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.30 → 1.0.0 Dependent
@tanstack/ai-preact 0.9.4 → 1.0.0 Dependent
@tanstack/ai-react 0.15.4 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.6 → 1.0.0 Dependent
@tanstack/ai-solid 0.13.4 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.6 → 1.0.0 Dependent
@tanstack/ai-svelte 0.13.4 → 1.0.0 Dependent
@tanstack/ai-vue 0.13.4 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.28.0 → 0.29.0 Changeset
@tanstack/ai-client 0.16.3 → 0.17.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-devtools-core 0.4.8 → 0.4.9 Dependent
@tanstack/ai-isolate-cloudflare 0.2.21 → 0.2.22 Dependent
@tanstack/ai-mcp 0.1.0 → 0.1.1 Dependent
@tanstack/ai-vue-ui 0.2.16 → 0.2.17 Dependent
@tanstack/preact-ai-devtools 0.1.51 → 0.1.52 Dependent
@tanstack/react-ai-devtools 0.2.51 → 0.2.52 Dependent
@tanstack/solid-ai-devtools 0.2.51 → 0.2.52 Dependent

@nx-cloud

nx-cloud Bot commented Jun 10, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit d24a601

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 23s View ↗

☁️ Nx Cloud last updated this comment at 2026-06-10 07:04:23 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jun 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@739

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@739

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@739

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@739

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@739

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@739

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@739

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@739

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@739

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@739

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@739

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@739

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@739

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@739

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@739

@tanstack/ai-mcp

npm i https://pkg.pr.new/@tanstack/ai-mcp@739

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@739

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@739

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@739

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@739

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@739

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@739

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@739

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@739

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@739

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@739

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@739

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@739

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@739

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@739

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@739

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@739

commit: d24a601

@tombeckenham

Copy link
Copy Markdown
Contributor Author

Superseded — folded these changes directly into #480 (shaked-frame/fix/anthropic-tool-parent-message-id) via maintainer edit. Same commit, no new PR needed.

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.

useChat assistant message.id changes mid-stream for Anthropic tool-first responses

2 participants