new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

AI SDK · Cookbook · all subjects

streaming/ui

96 notes in this subject, read out of this brain and free to use. This is page 2 of 2.

Use cases for custom useChat request body

Custom request bodies in useChat are useful for: only sending the last message to reduce data transferred, sending additional data alongside messages, and changing the structure of the request body to match specific server requirements.

Server-side handling of custom request body from useChat

When receiving a custom request body from useChat with prepareSendMessagesRequest, the server must parse the custom format, load any required message history from storage, combine it with the new message, and pass the complete message list to streamText. Use convertToModelMessages to prepare messages for the model, and wrap the result with createUIMessageStreamResponse and toUIMessageStream to send the response back to the client.

DefaultChatTransport with prepareSendMessagesRequest configuration

DefaultChatTransport accepts a configuration object with prepareSendMessagesRequest as a key. The prepareSendMessagesRequest function receives an object with 'id' and 'messages' properties. It should return an object with a 'body' property containing the custom request body to send to the server.

Use useChat hook for type-safe agent messages

Import useChat from '@ai-sdk/react' and pass AgentUIMessage as a generic to get type-safe access to messages including typed tool invocations and results. Call useChat() to get messages array and sendMessage function. Access message parts via m.parts array where each part has a type property.

Tool confirmation pattern with Yes/No buttons

For tools requiring user confirmation like askForConfirmation, render Yes and No buttons that call addToolOutput with the respective confirmation message. After confirmation is provided, state changes to 'output-available' and the confirmation result is displayed.

onToolCall handler for client-side tool execution

The onToolCall callback in useChat handles client-side tool execution. It receives a toolCall object containing toolName and toolCallId. Do not await operations in onToolCall to avoid potential deadlocks; instead, call addToolOutput asynchronously.

DefaultChatTransport for client-side chat API communication

The DefaultChatTransport class from the ai SDK is used to configure client-side chat communication with an API endpoint. It accepts an api parameter specifying the endpoint URL, such as '/api/chat'.

lastAssistantMessageIsCompleteWithToolCalls automatic send behavior

The sendAutomaticallyWhen parameter in useChat can be set to lastAssistantMessageIsCompleteWithToolCalls to automatically send tool outputs after all tool calls in an assistant message are complete.

Rendering tool output parts in chat messages

Messages in useChat have a parts array where each part can be of type 'text' or 'tool-{toolName}'. Tool parts have a type, toolCallId, state (either 'output-available' or pending), and an output field containing the tool's result.

Complete client-side weather chat implementation

This example shows a complete Chat component that uses useChat with DefaultChatTransport to communicate with an API, handles onToolCall for client-side tool execution (getLocation), and renders different UI components based on tool types: text parts, weather information with temperature and weekly forecast, location display, and confirmation buttons for askForConfirmation tool.

InferUITools and UIMessage type exports

The ai SDK exports InferUITools<typeof tools> to infer the tool types from a tool set, and UIMessage<never, UIDataTypes, ChatTools> to type chat messages that include UI tool rendering capabilities.

Rendering pending vs completed tool states

Tool parts have a state property: when state is 'output-available', the output field contains the result and should be rendered; otherwise, the tool is pending and a loading message like 'Calling {toolName}...' should be displayed.

Client-side Server-Sent Events parsing

On the client, use fetch to request the streaming endpoint, then use response.body.getReader() to read chunks. Decode chunks with TextDecoder, accumulate data in a buffer, split by newlines, remove the 'data: ' prefix from each line, and parse the remaining JSON to reconstruct StreamEvent objects.

Handle streaming buffer for incomplete events

When parsing Server-Sent Events on the client, maintain a buffer to handle incomplete lines that arrive across multiple read() calls. Split the accumulated text by newlines, keep the incomplete trailing portion in the buffer for the next iteration, and only process complete lines.

Custom streaming example: client component

```tsx 'use client'; import { useState } from 'react'; import { StreamEvent } from './api/stream/route'; export default function Home() { const [prompt, setPrompt] = useState(''); const [events, setEvents] = useState<StreamEvent[]>([]); const [isStreaming, setIsStreaming] = useState(false); const handleSubmit = async () => { setEvents([]); setIsStreaming(true); setPrompt(''); const response = await fetch('/api/stream', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt }), }); const reader = response.body?.getReader(); const decoder = new TextDecoder(); if (reader) { let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.trim()) { const dataStr = line.replace(/^data: /, ''); const event = JSON.parse(dataStr) as StreamEvent; setEvents(prev => [...prev, event]); } } } } setIsStreaming(false); }; return ( <div> <input value={prompt} onChange={e => setPrompt(e.target.value)} placeholder="Enter a prompt..." /> <button onClick={handleSubmit} disabled={isStreaming}> {isStreaming ? 'Streaming...' : 'Send'} </button> <pre>{JSON.stringify(events, null, 2)}</pre> </div> ); } ``` This example shows a Next.js client component that fetches and parses custom-formatted streaming events from the server.

readStreamableValue hook pattern in RSC client

Use `readStreamableValue` from `@ai-sdk/rsc` to consume streamed values on the client side. Iterate through the stream with a for-await loop to process each delta chunk. Example: `for await (const delta of readStreamableValue(output)) { setGeneration(current => current + delta); }`

readStreamableValue hook for streaming objects

The readStreamableValue function from @ai-sdk/rsc is used on the client to iterate over partial objects from a stream. It is used in a for-await loop to handle partial object updates as they arrive from the server.

Read streamable values on client with readStreamableValue

On the client, import `readStreamableValue` from '@ai-sdk/rsc' and iterate over the streamable value with `for await` to receive text deltas as they arrive. Build up the complete text by concatenating each delta.

Stream text with chat prompt example

Server action using streamText: ```typescript 'use server'; import { streamText } from 'ai'; import { createStreamableValue } from '@ai-sdk/rsc'; export interface Message { role: 'user' | 'assistant'; content: string; } export async function continueConversation(history: Message[]) { 'use server'; const stream = createStreamableValue(); (async () => { const { textStream } = streamText({ model: 'openai/gpt-5.4', system: "You are a dude that doesn't drop character until the DVD commentary.", messages: history, }); for await (const text of textStream) { stream.update(text); } stream.done(); })(); return { messages: history, newMessage: stream.value, }; } ``` Client component using readStreamableValue: ```tsx 'use client'; import { useState } from 'react'; import { Message, continueConversation } from './actions'; import { readStreamableValue } from '@ai-sdk/rsc'; export const maxDuration = 30; export default function Home() { const [conversation, setConversation] = useState<Message[]>([]); const [input, setInput] = useState<string>(''); return ( <div> <div> {conversation.map((message, index) => ( <div key={index}> {message.role}: {message.content} </div> ))} </div> <div> <input type="text" value={input} onChange={event => { setInput(event.target.value); }} /> <button onClick={async () => { const { messages, newMessage } = await continueConversation([ ...conversation, { role: 'user', content: input }, ]); let textContent = ''; for await (const delta of readStreamableValue(newMessage)) { textContent = `${textContent}${delta}`; setConversation([ ...messages, { role: 'assistant', content: textContent }, ]); } }} > Send Message </button> </div> </div> ); } ```

useChat import paths

Import useChat from '@ai-sdk/react' for React, Chat from '@ai-sdk/svelte' for Svelte, useChat from '@ai-sdk/vue' for Vue, and Chat from '@ai-sdk/angular' for Angular.

useChat major changes in AI SDK 5.0

AI SDK 5.0 significantly updated the useChat API. It now uses a transport-based architecture and no longer manages input state internally.

useChat parameters: chat and transport

useChat accepts optional 'chat' parameter of type Chat<UIMessage> for using an existing Chat instance (ignores other parameters if provided), and optional 'transport' parameter of type ChatTransport for specifying how messages are sent. DefaultChatTransport defaults to /api/chat endpoint.

DefaultChatTransport configuration options

DefaultChatTransport accepts: api (string = '/api/chat', optional), credentials (RequestCredentials, optional), headers (Record<string, string> | Headers, optional), body (object, optional for extra body properties), fetch (FetchFunction, optional for custom implementation or middleware), prepareSendMessagesRequest (PrepareSendMessagesRequest, optional to customize requests before chat API calls), and prepareReconnectToStreamRequest (PrepareReconnectToStreamRequest, optional to customize reconnect requests).

PrepareSendMessageRequestOptions structure

PrepareSendMessageRequestOptions includes: id (string, the chat ID), messages (UIMessage[], current messages), requestMetadata (unknown), body (Record<string, any> | undefined), credentials (RequestCredentials | undefined), headers (HeadersInit | undefined), api (string, defaults to transport's /api/chat), trigger ('submit-message' | 'regenerate-message'), and messageId (string | undefined).

PrepareReconnectToStreamRequestOptions structure

PrepareReconnectToStreamRequestOptions includes: id (string, the chat ID), requestMetadata (unknown), body (Record<string, any> | undefined), credentials (RequestCredentials | undefined), headers (HeadersInit | undefined), and api (string, defaults to /api/chat/{chatId}/stream).

useChat additional parameters

useChat accepts: id (string, optional, auto-generated if not provided), messages (UIMessage[], optional initial messages), messageMetadataSchema (FlexibleSchema, optional for validating message metadata), dataPartSchemas (UIDataTypesToSchemas, optional for validating data parts), generateId (IdGenerator, optional function for generating unique IDs, defaults to AI SDK generateId), onToolCall ({toolCall: ToolCall} => void | Promise<void>, optional callback when tool call received), sendAutomaticallyWhen ((options: {messages: UIMessage[]}) => boolean | PromiseLike<boolean>, optional to resubmit messages when stream finishes or tool call added), onFinish (OnFinishOptions => void, optional callback when assistant response finishes), onError (Error => void, optional error callback), onData (DataUIPart => void, optional callback when data part received), throttle (number, optional ms for throttling updates, undefined disables throttling), and resume (boolean, optional to resume ongoing generation stream, defaults to false).

OnFinishOptions callback structure

OnFinishOptions includes: message (UIMessage, the response message), messages (UIMessage[], all messages including response), isAbort (boolean, true if aborted by client), isDisconnect (boolean, true if server disconnected), isError (boolean, true if errors stopped response), and finishReason (optional 'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other', undefined if not provided by model).

useChat returns: id and messages

useChat returns id (string, the chat ID) and messages (UIMessage[] array of current chat messages).

UIMessage structure

UIMessage includes: id (string, unique identifier), role ('system' | 'user' | 'assistant'), parts (UIMessagePart[], use for rendering in UI), and metadata (unknown, optional).

useChat returns: status

useChat returns status of type 'submitted' | 'streaming' | 'ready' | 'error'. Ready means idle, submitted means request sent, streaming means receiving response, error means request failed.

useChat returns: error

useChat returns error (Error | undefined, the error object if an error occurred).

sendMessage function signature

sendMessage(message?: { text: string; files?: FileList | FileUIPart[]; metadata?; messageId?: string } | CreateUIMessage, options?: ChatRequestOptions) => Promise<void>. Sends a new message or, if messageId provided, replaces the message (for editing). If no message provided, resubmits current messages (useful after adding tool outputs).

ChatRequestOptions structure

ChatRequestOptions includes: headers (Record<string, string> | Headers, optional additional headers), body (object, optional additional body JSON properties), and metadata (unknown, optional additional data for API endpoint).

regenerate function

regenerate(options?: { messageId?: string } & ChatRequestOptions) => Promise<void>. Regenerates the last assistant message or a specific message if messageId provided. Accepts ChatRequestOptions for headers, body, and metadata.

useChat streaming control methods

useChat provides: stop() to abort current streaming response, clearError() to clear error state, and resumeStream() to resume interrupted streaming (useful for network errors).

setMessages function

setMessages(messages: UIMessage[] | ((messages: UIMessage[]) => UIMessage[])) => void. Updates messages state locally without triggering API call. Useful for optimistic updates.

Give your agent this brain