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

code
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

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

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