Streaming
createChatHandler streams the model response through the Vercel AI SDK UI
message protocol. The widget currently commits partial updates at a fixed 50 ms
throttle.
Note
streamingThrottleMs remains in the public type, but <ChatWidget> does not
currently forward it to the internal interface. Treat 50 ms as fixed until a
release wires the prop through.
Bound tool loops
import { stepCountIs } from 'ai';
createChatHandler({
getUserId: getChatUserId,
model,
store,
stopWhen: stepCountIs(5),
});stopWhen accepts an AI SDK stop condition. The default is bounded; use
stepCountIs(1) to prevent chaining.
Error mapping
createChatHandler({
getUserId: getChatUserId,
model,
store,
onError: (error) => isAbortError(error)
? 'Response stopped.'
: 'Something went wrong. Please try again.',
});onError controls user-facing text. Server error logging remains enabled unless
logErrors: false is set. Disable it only when another sink captures the same
failures.
Post-turn telemetry
onChatFinish runs after persistence. It can inspect AI SDK usage and provider
metadata; its own failures are logged and swallowed.
createChatHandler({
getUserId: getChatUserId,
model,
store,
onChatFinish: async ({ ctx, usage, providerMetadata }) => {
await analytics.track({
userId: ctx.userId,
conversationId: ctx.conversationId,
usage,
providerMetadata,
});
},
});Do not save the turn again in this hook. See Streaming reliability for proxy buffering, timeouts, and deployment probes.