You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(sdk): stop chat.agent replay blocking the event loop on long turns
Recovering a long unfinished `chat.agent` response on a continuation run
could block the worker's event loop for minutes, long enough to miss
heartbeats and fail the run. Capturing the response at the end of a long
turn had the same cost.
Both paths rebuilt the message by draining `readUIMessageStream` and
keeping its last snapshot. That reducer emits a `structuredClone` of the
whole message after almost every chunk, and every chunk was enqueued
into one web stream up front, where each dequeue is linear in the queue
length on Node. Both costs are quadratic in turn length.
The reductions now go through one helper:
```ts
const message = await reduceUIMessageChunks(chunks, { message: original });
```
- It runs the same AI SDK reducer through `createUIMessageStream`'s
`onFinish`, which mutates one state and hands back only the final
message. That message is cloned once so it shares nothing with the
recorded chunks.
- Chunks are fed one at a time, each after the previous one comes out,
and the loop yields to the event loop every 10ms, so heartbeats keep
running.
- The result matches the last `readUIMessageStream` snapshot, including
`undefined` when no chunk would emit one. A malformed chunk is
identified exactly, and the chunks before it are reduced again on the
same path, so content before it is kept without a slow fallback.
A 320,000-chunk text response now reduces in under a second. Tests cover
equivalence with `readUIMessageStream` across finished, unfinished,
resumed and continued messages, a malformed chunk after a long prefix,
detachment from the input chunks, and bounded timer gaps during large
reductions.
Mono-RevId: 4ce17e79a4750a7c949bbc3448881ff0a98dc705
Recovering a long unfinished `chat.agent` response on a continuation run no longer blocks the worker. Rebuilding the response from its streamed chunks now takes time linear in its length and yields to the event loop as it goes, so heartbeats keep firing and the run is not killed mid-replay. Capturing the response at the end of a long turn gets the same speedup.
0 commit comments