Skip to content

WIP: fix useChat tool calls#51

Draft
kaschula wants to merge 10 commits intomainfrom
ait-680-useChat-tool-calls
Draft

WIP: fix useChat tool calls#51
kaschula wants to merge 10 commits intomainfrom
ait-680-useChat-tool-calls

Conversation

@kaschula
Copy link
Copy Markdown
Contributor

No description provided.

owenpearson and others added 9 commits April 13, 2026 12:42
Fixes an issue where `sendMessages()` returned an empty `ReadableStream`
to `useChat`. The stream returned here is used to power important
features of useChat, such as:
- status changes (submitted -> streaming -> ready)
- turn lifecycle hooks (e.g. `onToolCall`, `onData`, `onFinish`)
- evaluating whether `sendAutomaticallyWhen` should send

With an empty stream, none of these features worked. This transport aims
to, as much as possible, be a drop-in replacement for the default
transport so we need to make these features work.

This commit returns the real stream from `turn.stream` to `useChat`,
which allows these features to work as expected. As a result of this
change, both `useChat` and `useMessageSync` now accumulate messages in
parallel but both produce identical messages from the same underlying
Ably events.

When the AI SDK doesn't set messageId on the start chunk (currently this
is logically equivalent to not being in 'persistence mode') useChat and
the transport will assign different IDs. This commit adds `messageId` to
`EncoderOptions` so that the server transport can pass its generated ID
to the vercel encoder which uses it as a fallback, which ensures that
both sides converge on the same ID.
useChat's internal write() compares message IDs against the last
message in state.messages. If useMessageSync replaces the array
mid-stream (e.g. when an observer turn arrives), the ID check can
fail and push a duplicate message.

ChatTransport now tracks whether an own-turn stream is active via
a `streaming` flag and `onStreamingChange` callback. The stream is
wrapped in a passthrough TransformStream that resolves a done
promise on close or error, clearing the flag automatically.

useMessageSync accepts an optional ChatTransport and suppresses
setMessages while streaming. When the gate opens, an immediate
sync fires to pick up any observer messages that arrived during
the stream.

Tests verify: streaming flag lifecycle (including error and
pre-stream failure), useMessageSync gating and ungating with
observer messages, and concurrent sendMessage behavior documenting
the AbstractChat activeResponse limitation.
Enable monthly dependency update PRs for the root package, both
demo apps, and GitHub Actions versions. Updates are grouped by
type (minor/patch, major, security) to reduce PR noise.
Replace the monolithic serial test.yml with dev.yml containing
parallel jobs: check (typecheck, format, lint, error-codes), unit
tests, integration tests and build.

Parallel jobs give faster wall-clock feedback, clearer failure
attribution (each check is a separate PR status), and allow
selective re-runs. Unit and integration tests run across
Node 20, 22, and 24. Concurrency groups cancel in-progress runs
when new commits are pushed.
Commit demo app lockfiles generated from a clean state (no
node_modules) so npm ci works cross-platform. Note: npm has a
known issue [1] where lockfiles can lose cross-platform
optional deps under certain conditions. If demo CI breaks with
"Missing: @next/swc-..." errors, delete node_modules and
package-lock.json then regenerate from scratch.

Provide a dummy ABLY_API_KEY env var for the build step. Next.js
evaluates all route modules during `next build`, including the
module-level Ably client initialization that requires an API key
— even though the routes are never called at build time. An
alternative (lazy-initializing the Ably client on first request)
was considered but rejected to keep demo app code simple and
representative of real usage patterns.

[1] npm/cli#4828
Add @vitest/coverage-v8 with coverage config (disabled by default,
enabled via --coverage flag in CI). On the base Node version, the
test job collects coverage and posts a summary to PRs via
vitest-coverage-report-action.
Deploy core UMD bundle to prod-cdn.ably.com on every GitHub release
using the shared ably/cdn-deploy-action. Uses AWS OIDC federation
for authentication. Versioned copies are uploaded (e.g. 0.0.2 ->
0, 0.0, 0.0.2) so consumers can pin to major/minor/patch.
Add typedoc with all 4 entry points configured. Generated docs are
deployed to the SDK docs CDN on pushes to main and tag creation.

"treatWarningsAsErrors" is set to false for now as the public API
surface needs more JSDoc coverage.
The lifecycle tracker's clearScope was called on finish and abort but
not on error. If error is the terminal event in a turn (no subsequent
finish), the scope entry leaks. clearScope is idempotent so adding it
defensively is harmless.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kaschula kaschula marked this pull request as draft April 13, 2026 14:32
@github-actions github-actions bot temporarily deployed to staging/pull/51/typedoc April 13, 2026 14:32 Inactive
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 13, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 92.83% 2022 / 2178
🔵 Statements 91.06% 2162 / 2374
🔵 Functions 94.65% 372 / 393
🔵 Branches 78.02% 902 / 1156
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/core/codec/types.ts 0% 0% 0% 0%
src/core/transport/server-transport.ts 93.71% 70.23% 90.47% 94.07% 90-97, 179, 216-234
src/vercel/codec/decoder.ts 95.3% 71.26% 100% 96.5% 102, 119, 124, 161, 178, 210, 479
src/vercel/codec/encoder.ts 96.49% 87.27% 100% 96.42% 190, 377-384
src/vercel/react/use-message-sync.ts 100% 100% 100% 100%
src/vercel/transport/chat-transport.ts 97.14% 90% 100% 96.92% 244-245
Generated in workflow #46 for commit 9eb72b5 by the Vitest Coverage Report Action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants