Skip to content

fix(ai): keep the first text delta when the text starts before the tool call - #1271

Merged
AlemTuzlak merged 3 commits into
TanStack:mainfrom
iRanadheer:fix/first-text-delta-after-tool-call
Aug 31, 2026
Merged

fix(ai): keep the first text delta when the text starts before the tool call#1271
AlemTuzlak merged 3 commits into
TanStack:mainfrom
iRanadheer:fix/first-text-delta-after-tool-call

Conversation

@iRanadheer

@iRanadheer iRanadheer commented Aug 30, 2026

Copy link
Copy Markdown

🎯 Changes

Follow-up to #1247. #1248 fixed the order where the tool call precedes the message's TEXT_MESSAGE_START. The first delta is still dropped in the other order, where the text starts first, because hasToolCallsSinceTextStart is cleared on the start paths and never once a segment has begun.

The first delta after a tool call cannot enter the isNewSegment branch, since previousSegment is empty at that point. The flag survives into the second delta, which then matches all three conditions: the accumulation resets and updateTextPart writes the remainder over the text part the first delta had created. Multi-segment behaviour is unaffected, since a later tool call sets the flag again in handleToolCallStartEvent.

Reproduces on 0.52.0 and on main.

What is in it:

Verified locally:

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with pnpm run test:pr, or these tests do not apply to this pull request.
  • I fully understand the code in this pull request, including any code generated with AI assistance.
  • Docs: I updated docs/ for this change, or this change is not user-facing.
  • Changeset: I added a changeset (pnpm changeset), or this PR does not change a published package.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes

    • Fixed streamed assistant responses after tool calls so the first text segment is preserved when content arrives in multiple chunks.
    • Prevented the beginning of a response from being dropped or overwritten.
  • Tests

    • Added regression and end-to-end coverage for text-first tool-call streaming scenarios.

…ol call

The first delta after a tool call cannot enter the isNewSegment branch,
since previousSegment is empty at that point. The flag survived into the
second delta, which then matched all three conditions: the accumulation
reset and updateTextPart wrote the remainder over the text part the first
delta had created.

TEXT_MESSAGE_START. This covers the other order, where the text starts
first.
The harness added with TanStack#1248 streams the tool call before the message's
TEXT_MESSAGE_START. This one streams them the other way round, which is
what providers that open the assistant message first emit.
@coderabbitai

coderabbitai Bot commented Aug 30, 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8a475fa8-4930-47a6-bbc3-b3df8d3348e5

📥 Commits

Reviewing files that changed from the base of the PR and between b899fe5 and 0ae844d.

📒 Files selected for processing (7)
  • .changeset/text-first-tool-drop.md
  • packages/ai/src/activities/chat/stream/processor.ts
  • packages/ai/tests/stream-processor.test.ts
  • testing/e2e/src/routeTree.gen.ts
  • testing/e2e/src/routes/api.text-first-tool-wire.ts
  • testing/e2e/src/routes/text-first-tool.tsx
  • testing/e2e/tests/text-first-tool.spec.ts

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


📝 Walkthrough

Walkthrough

The stream processor now preserves the first text delta after a tool call. Unit and end-to-end tests cover the event sequence with multiple text chunks.

Changes

Text-first tool stream handling

Layer / File(s) Summary
Stream processor state fix
packages/ai/src/activities/chat/stream/processor.ts, packages/ai/tests/stream-processor.test.ts, .changeset/text-first-tool-drop.md
The processor clears tool-call state before each text delta. A regression test verifies that It is and sunny. remain as It is sunny.. The changeset declares a patch release.
SSE regression stream
testing/e2e/src/routes/api.text-first-tool-wire.ts
A POST route emits the text-start, tool-call, tool-result, and two text-content events through Server-Sent Events.
Harness route and browser assertion
testing/e2e/src/routes/text-first-tool.tsx, testing/e2e/src/routes/routeTree.gen.ts, testing/e2e/tests/text-first-tool.spec.ts
The test page sends one message and renders assistant text. Generated routing registers the page and API route. Playwright asserts Hello, world..

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 0ae84

This change preserves the first streamed text delta when text begins before a tool call, preventing incomplete messages while retaining existing multi-segment behavior. No actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant TextFirstToolPage
  participant ApiTextFirstToolWire
  participant StreamProcessor
  Browser->>TextFirstToolPage: Open /text-first-tool
  TextFirstToolPage->>ApiTextFirstToolWire: POST chat request
  ApiTextFirstToolWire-->>TextFirstToolPage: Return SSE event stream
  TextFirstToolPage->>StreamProcessor: Process text and tool chunks
  StreamProcessor-->>TextFirstToolPage: Accumulate assistant text
  TextFirstToolPage-->>Browser: Render Hello, world.
Loading

Possibly related PRs

  • TanStack/ai#1248: Both changes modify StreamProcessor and add regression coverage for text deltas around tool calls.

Suggested reviewers: alemtuzlak, tombeckenham, season179

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary fix: preserving the first text delta when text starts before a tool call.
Description check ✅ Passed The description explains the issue, implementation, tests, release impact, and checklist status. It includes the required sections and a changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 6 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 6 files. (1 skipped: 1 unsupported.)

✨ 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.

@nx-cloud

nx-cloud Bot commented Aug 30, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 0ae844d


☁️ Nx Cloud last updated this comment at 2026-08-30 21:09:39 UTC

@nx-cloud

nx-cloud Bot commented Aug 30, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 0ae844d

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

☁️ Nx Cloud last updated this comment at 2026-08-30 21:09:40 UTC

@pkg-pr-new

pkg-pr-new Bot commented Aug 30, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

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

@tanstack/ai-acp

npm i https://pkg.pr.new/@tanstack/ai-acp@1271

@tanstack/ai-angular

npm i https://pkg.pr.new/@tanstack/ai-angular@1271

@tanstack/ai-anthropic

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

@tanstack/ai-bedrock

npm i https://pkg.pr.new/@tanstack/ai-bedrock@1271

@tanstack/ai-byteplus

npm i https://pkg.pr.new/@tanstack/ai-byteplus@1271

@tanstack/ai-claude-code

npm i https://pkg.pr.new/@tanstack/ai-claude-code@1271

@tanstack/ai-client

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

@tanstack/ai-code-mode

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

@tanstack/ai-code-mode-snippets

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

@tanstack/ai-codex

npm i https://pkg.pr.new/@tanstack/ai-codex@1271

@tanstack/ai-cohere

npm i https://pkg.pr.new/@tanstack/ai-cohere@1271

@tanstack/ai-compaction

npm i https://pkg.pr.new/@tanstack/ai-compaction@1271

@tanstack/ai-devtools-core

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

@tanstack/ai-durable-stream

npm i https://pkg.pr.new/@tanstack/ai-durable-stream@1271

@tanstack/ai-elevenlabs

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

@tanstack/ai-event-client

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

@tanstack/ai-fal

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

@tanstack/ai-gemini

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

@tanstack/ai-grok

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

@tanstack/ai-grok-build

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

@tanstack/ai-groq

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

@tanstack/ai-isolate-cloudflare

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

@tanstack/ai-isolate-daytona

npm i https://pkg.pr.new/@tanstack/ai-isolate-daytona@1271

@tanstack/ai-isolate-node

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

@tanstack/ai-isolate-quickjs

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

@tanstack/ai-isolate-quickjs-bun

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

@tanstack/ai-llmgateway

npm i https://pkg.pr.new/@tanstack/ai-llmgateway@1271

@tanstack/ai-lovable

npm i https://pkg.pr.new/@tanstack/ai-lovable@1271

@tanstack/ai-mcp

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

@tanstack/ai-memory

npm i https://pkg.pr.new/@tanstack/ai-memory@1271

@tanstack/ai-mistral

npm i https://pkg.pr.new/@tanstack/ai-mistral@1271

@tanstack/ai-octane

npm i https://pkg.pr.new/@tanstack/ai-octane@1271

@tanstack/ai-ollama

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

@tanstack/ai-openai

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

@tanstack/ai-opencode

npm i https://pkg.pr.new/@tanstack/ai-opencode@1271

@tanstack/ai-openrouter

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

@tanstack/ai-perplexity

npm i https://pkg.pr.new/@tanstack/ai-perplexity@1271

@tanstack/ai-persistence

npm i https://pkg.pr.new/@tanstack/ai-persistence@1271

@tanstack/ai-preact

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

@tanstack/ai-react

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

@tanstack/ai-react-ui

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

@tanstack/ai-sandbox

npm i https://pkg.pr.new/@tanstack/ai-sandbox@1271

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-sandbox-cloudflare@1271

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/@tanstack/ai-sandbox-daytona@1271

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/@tanstack/ai-sandbox-docker@1271

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/@tanstack/ai-sandbox-local-process@1271

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/@tanstack/ai-sandbox-sprites@1271

@tanstack/ai-sandbox-upstash-box

npm i https://pkg.pr.new/@tanstack/ai-sandbox-upstash-box@1271

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/@tanstack/ai-sandbox-vercel@1271

@tanstack/ai-skills

npm i https://pkg.pr.new/@tanstack/ai-skills@1271

@tanstack/ai-solid

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

@tanstack/ai-solid-ui

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

@tanstack/ai-svelte

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

@tanstack/ai-utils

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

@tanstack/ai-vercel-gateway

npm i https://pkg.pr.new/@tanstack/ai-vercel-gateway@1271

@tanstack/ai-vertex

npm i https://pkg.pr.new/@tanstack/ai-vertex@1271

@tanstack/ai-vue

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

@tanstack/ai-vue-ui

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

@tanstack/openai-base

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

@tanstack/preact-ai-devtools

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

@tanstack/react-ai-devtools

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

@tanstack/solid-ai-devtools

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

@tanstack/svelte-ai-devtools

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

commit: 0ae844d

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the PR, @iRanadheer! 🙌 @tombeckenham will take a look.

Automated pre-review checks

  • ✅ CI passing
  • ✅ No merge conflicts
  • ✅ Changeset present
  • ✅ E2E test changes included

Automated triage — a human review follows.

@AlemTuzlak

Copy link
Copy Markdown
Contributor

/ai-review

@AlemTuzlak

Copy link
Copy Markdown
Contributor

This comment is automated by a Grok agent. It is not a maintainer review.

Verdict: ready
Head SHA: 0ae844d
Label: ai-ready

Findings

  • None

Push
Did not push.

Maintainers still GitHub-approve.

@AlemTuzlak AlemTuzlak added the ai-ready Grok review bot: a maintainer can merge after they Approve label Aug 31, 2026
@AlemTuzlak
AlemTuzlak merged commit cfb8454 into TanStack:main Aug 31, 2026
9 checks passed
@github-actions github-actions Bot mentioned this pull request Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-ready Grok review bot: a maintainer can merge after they Approve waiting-on: maintainer The ball is in the maintainers’ court

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants