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/backend

184 notes in this subject, read out of this brain and free to use. This is page 1 of 4.

API route for streaming chat with streamText

Example Next.js API route that handles chat requests and streams responses: import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream, UIMessage, } from 'ai'; export const maxDuration = 30; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: 'openai/gpt-4o', messages: await convertToModelMessages(messages), }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }), }); }

toUIMessageStream converts stream format

The toUIMessageStream function from the AI SDK converts the result stream from streamText into UIMessageStream format that can be rendered in the UI. Usage: toUIMessageStream({ stream: result.stream })

Build a chat interface with Gemini 3 Pro, Next.js and useChat hook

To build a chat interface with Gemini 3 Pro and Next.js: First, install ai and @ai-sdk/google packages. Then create a route handler at app/api/chat/route.ts that uses streamText with google('gemini-3-pro-preview'), convertToModelMessages to convert UI messages to model messages, and createUIMessageStreamResponse with toUIMessageStream to return the streamed response. Finally, use the useChat hook from @ai-sdk/react in app/page.tsx to handle user messages and display the chat conversation.

Chat route handler with Gemini 3 Pro and streaming example

The following example shows a Next.js route handler for streaming chat with Gemini 3 Pro: ```tsx filename="app/api/chat/route.ts" import { google } from '@ai-sdk/google'; import { streamText, UIMessage, convertToModelMessages, createUIMessageStreamResponse, toUIMessageStream, } from 'ai'; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: google('gemini-3-pro-preview'), messages: await convertToModelMessages(messages), }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }), }); } ```

AI SDK role in modern AI application development

The AI SDK is a TypeScript toolkit for building AI applications with large language models like Claude 4 alongside popular frameworks including React, Next.js, Vue, Svelte, and Node.js. It abstracts away differences between model providers, eliminates boilerplate code for building chatbots, and allows generation of rich interactive components. The SDK consists of AI SDK Core for unified LLM API calls and AI SDK UI for building interactive interfaces with frameworks like Next.js, Nuxt, SvelteKit, and SolidStart.

AI SDK Core basic text generation with Claude 4

This example shows how to call Claude 4 Sonnet with AI SDK Core using generateText. The generateText function returns an object with text, reasoningText, and reasoning properties. Import anthropic from '@ai-sdk/anthropic' and generateText from 'ai', then pass a model and prompt to generateText.

Next.js route handler for streaming chat with Claude 4

This example shows how to create a POST route handler at app/api/chat/route.ts for streaming chat messages with Claude 4. It imports anthropic and AnthropicLanguageModelOptions from '@ai-sdk/anthropic', and streamText, convertToModelMessages, createUIMessageStreamResponse, toUIMessageStream, and UIMessage type from 'ai'. The route handler receives messages of type UIMessage[] from the request, calls streamText with the model, converts messages to model messages, enables extended thinking with budgetTokens: 15000, and enables the interleaved-thinking beta via headers. It returns createUIMessageStreamResponse with the toUIMessageStream helper configured with sendReasoning: true to forward reasoning tokens to the client. ```tsx import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic'; import { streamText, convertToModelMessages, createUIMessageStreamResponse, toUIMessageStream, type UIMessage, } from 'ai'; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: anthropic('claude-sonnet-4-20250514'), messages: await convertToModelMessages(messages), headers: { 'anthropic-beta': 'interleaved-thinking-2025-05-14', }, providerOptions: { anthropic: { thinking: { type: 'enabled', budgetTokens: 15000 }, } satisfies AnthropicLanguageModelOptions, }, }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream, sendReasoning: true, }), }); } ```

generateText with Claude 4 extended thinking example

This example shows how to enable extended thinking with Claude 4 using generateText. It imports anthropic and AnthropicLanguageModelOptions from '@ai-sdk/anthropic', and generateText from 'ai'. The model is called with providerOptions containing anthropic.thinking set to {type: 'enabled', budgetTokens: 15000} and headers containing 'anthropic-beta': 'interleaved-thinking-2025-05-14'. The response includes text, reasoningText, and reasoning properties which can be logged separately. ```ts import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic'; import { generateText } from 'ai'; const { text, reasoningText, reasoning } = await generateText({ model: anthropic('claude-sonnet-4-20250514'), prompt: 'How will quantum computing impact cryptography by 2050?', providerOptions: { anthropic: { thinking: { type: 'enabled', budgetTokens: 15000 }, } satisfies AnthropicLanguageModelOptions, }, headers: { 'anthropic-beta': 'interleaved-thinking-2025-05-14', }, }); console.log(text); // text response console.log(reasoningText); // reasoning text console.log(reasoning); // reasoning details including redacted reasoning ```

Stream text with Claude 3.7 Sonnet in Next.js

Create a route handler using streamText from the AI SDK. Import streamText, convertToModelMessages, createUIMessageStreamResponse, and toUIMessageStream. The handler receives UIMessage[] from the request, converts them to model messages, and returns a streamed response. Use providerOptions to enable thinking if needed. Set sendReasoning: true in toUIMessageStream to forward reasoning tokens to the client.

Streaming chat route handler with extended thinking

Example route handler for Next.js (app/api/chat/route.ts) that streams text with extended thinking: export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: anthropic('claude-3-7-sonnet-20250219'), messages: await convertToModelMessages(messages), providerOptions: { anthropic: { thinking: { type: 'enabled', budgetTokens: 12000 } } } }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream, sendReasoning: true }) }); }

Chat route handler setup for streaming

Set maxDuration = 30 in the route handler to allow responses up to 30 seconds. Extract messages from request.json() with type UIMessage[]. Use streamText with converted model messages and return createUIMessageStreamResponse with toUIMessageStream.

Chat interface with Next.js and AI SDK

Create a chat route handler at app/api/chat/route.ts that uses streamText with convertToModelMessages, createUIMessageStreamResponse, and toUIMessageStream. Use the useChat hook from '@ai-sdk/react' on the frontend to manage messages and send user input.

Streaming Llama 3.1 responses

To stream Llama 3.1 model responses, use the streamText function instead of generateText. Import streamText from 'ai' and deepInfra from '@ai-sdk/deepinfra'. Example: const { textStream } = streamText({ model: deepInfra('meta-llama/Meta-Llama-3.1-405B-Instruct'), prompt: 'What is love?' });

Chat endpoint with Llama 3.1 in Next.js

Create a POST route handler at app/api/chat/route.ts. Import deepInfra, convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream, UIMessage from 'ai'. Set maxDuration to 30 seconds. Extract messages from request JSON, call streamText with deepInfra model and converted messages, return createUIMessageStreamResponse wrapping toUIMessageStream output. This creates a streaming chat endpoint.

Server Action for streamUI with Llama 3.1

Create a Server Action using 'use server' directive. Import streamUI from '@ai-sdk/rsc' and deepInfra from '@ai-sdk/deepinfra'. Call streamUI with model, prompt, text parameter for rendering content, and tools object defining available actions. Tools use generate async generator functions. Return result.value to send the streamed component to the client.

Next.js chat application with o1 backend

This example shows a Next.js chat application backend using o1 with streamText: ```tsx filename="app/api/chat/route.ts" import { openai } from '@ai-sdk/openai'; import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream, UIMessage, } from 'ai'; // Allow responses up to 5 minutes export const maxDuration = 300; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: openai('o1'), messages: await convertToModelMessages(messages), }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }), }); } ``` Note: maxDuration is set to 300 seconds (5 minutes) to allow for o1's extended reasoning time.

Next.js chat endpoint for o3-mini with streaming

This example shows a Next.js route handler for streaming chat messages with o3-mini: ```tsx import { openai } from '@ai-sdk/openai'; import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream, UIMessage, } from 'ai'; export const maxDuration = 300; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: openai('o3-mini'), messages: await convertToModelMessages(messages), }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }), }); } ``` The maxDuration is set to 300 seconds (5 minutes) to allow responses to complete.

Build streaming chat with DeepSeek R1 and Next.js

Create a route handler for the chat endpoint at `app/api/chat/route.ts`: ```tsx import { deepSeek } from '@ai-sdk/deepseek'; import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream, UIMessage, } from 'ai'; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: deepSeek('deepseek-reasoner'), messages: await convertToModelMessages(messages), }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream, sendReasoning: true, }), }); } ``` Set `sendReasoning: true` to forward the model's reasoning tokens to the client.

DeepSeek V3.2 streaming chat route handler

```tsx import { deepSeek } from '@ai-sdk/deepseek'; import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream, UIMessage, } from 'ai'; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: deepSeek('deepseek-reasoner'), messages: await convertToModelMessages(messages), }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream, sendReasoning: true }), }); } ``` This route handler at app/api/chat/route.ts streams text responses from DeepSeek V3.2 in reasoning mode, converts UI messages to model messages, and returns a UI message stream response with reasoning enabled.

generateText server endpoint example

The following is a complete example of a Next.js API route for chat completion: ```typescript import { generateText, type ModelMessage } from 'ai'; export async function POST(req: Request) { const { messages }: { messages: ModelMessage[] } = await req.json(); const { responseMessages } = await generateText({ model: 'openai/gpt-4o', system: 'You are a helpful assistant.', messages, }); return Response.json({ messages: responseMessages }); } ``` This endpoint receives messages from the client, calls generateText with the model, system prompt, and messages, and returns the response messages.

Chat endpoint with image generation tool

Create a POST endpoint at /api/chat that receives messages as UIMessage[]. Call streamText() with the model (e.g., 'openai/gpt-4o'), convert messages using convertToModelMessages(), optionally set stopWhen with isStepCount(), and pass the tools object. Wrap the response with createUIMessageStreamResponse() and toUIMessageStream() to handle tool invocations in the stream.

Chat completion streaming pattern overview

Streaming chat completion allows long-running model responses to be displayed to users in real-time as they are generated, rather than waiting for the complete response. This improves user experience for lengthy outputs.

Convert and stream chat messages to client

Use toUIMessageStream to convert the streamText result stream into a UI-compatible message stream, then wrap it with createUIMessageStreamResponse to return a streaming response from the Next.js API route.

Server-side streamText with chat prompt

The streamText function from ai package accepts a model identifier string (e.g., 'openai/gpt-4o'), a system prompt, and messages converted using convertToModelMessages. It returns a result object with a stream property containing the text generation stream.

convertToModelMessages function usage

The convertToModelMessages function is an async function from the 'ai' package that transforms UIMessage array format into the model-specific message format required by the streamText function.

UIMessage type for chat communication

UIMessage is the type used for chat messages in the AI SDK. Messages are imported from the 'ai' package and contain the structured message data passed between client and server.

createUIMessageStreamResponse and toUIMessageStream for streaming responses

createUIMessageStreamResponse and toUIMessageStream are functions from the 'ai' module used to handle streaming responses in Next.js route handlers. toUIMessageStream takes an object with a 'stream' property from streamText and converts it to a UI message stream format. createUIMessageStreamResponse wraps this stream and returns a proper HTTP response for the client.

streamText function for server-side text generation

The streamText function from the 'ai' module is used on the server to generate text based on a prompt. It accepts a configuration object with properties: 'model' (e.g., 'openai/gpt-4o'), 'system' (system prompt), and 'prompt' (user prompt). It returns an object with a 'stream' property containing the streamed text data.

Complete server-side streaming text generation example

import { createUIMessageStreamResponse, streamText, toUIMessageStream, } from 'ai'; export async function POST(req: Request) { const { prompt }: { prompt: string } = await req.json(); const result = streamText({ model: 'openai/gpt-4o', system: 'You are a helpful assistant.', prompt, }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }), }); } This example shows a Next.js route handler at /api/completion that receives a prompt, generates text using streamText with GPT-4o, and returns the streamed response using createUIMessageStreamResponse.

toUIMessageStream converts model stream to UI-compatible format

toUIMessageStream takes the stream from streamText result and converts it to a UI-friendly format. It is chained as toUIMessageStream({ stream: result.stream }).

convertToModelMessages transforms UIMessages to model messages

Use convertToModelMessages to convert UIMessages to model messages. This function automatically handles multimodal content including images. It is used on the server side before passing messages to streamText.

createUIMessageStreamResponse wraps stream for UI consumption

createUIMessageStreamResponse is called with a stream object (typically from toUIMessageStream applied to the result.stream) to format the response for consumption by the client UI.

Cache wrapStream responses with simulateReadableStream

In wrapStream middleware, cache an array of stream parts rather than the response directly. When a cached result exists, use the simulateReadableStream function to create a simulated ReadableStream that returns the cached response chunk-by-chunk as if it were being generated by the model. The simulateReadableStream function accepts initialDelayInMs and chunkDelayInMs parameters to control timing.

Cache wrapGenerate responses directly

In wrapGenerate middleware, you can cache the response directly. Create a cache key from the params, check if a cached result exists, and if so return it with any necessary timestamp reformatting. If not cached, call doGenerate, store the result in the cache, and return it.

Reformat timestamps when returning cached responses

When returning cached responses that contain timestamp fields stored as strings, reformat them back to Date objects. For LanguageModelV4StreamPart chunks with type 'response-metadata', convert the timestamp string to a new Date object before returning.

LanguageModelMiddleware has wrapGenerate and wrapStream methods

LanguageModelMiddleware provides two methods for intercepting model calls. The wrapGenerate method is called when using generateText, and wrapStream is called when using streamText. Both methods receive doGenerate/doStream and params as parameters.

Example: API route with wrapLanguageModel and caching middleware

import { cacheMiddleware } from '@/ai/middleware'; import { wrapLanguageModel, streamText, tool, createUIMessageStreamResponse, toUIMessageStream, } from 'ai'; import { z } from 'zod'; const wrappedModel = wrapLanguageModel({ model: 'openai/gpt-4o-mini', middleware: cacheMiddleware, }); export async function POST(req: Request) { const { messages } = await req.json(); const result = streamText({ model: wrappedModel, messages, tools: { weather: tool({ description: 'Get the weather in a location', inputSchema: z.object({ location: z.string().describe('The location to get the weather for'), }), execute: async ({ location }) => ({ location, temperature: 72 + Math.floor(Math.random() * 21) - 10, }), }), }, }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }), }); } This example shows how to apply caching middleware to a model using wrapLanguageModel and use it with streamText and tools in an API route.

Example: LanguageModelMiddleware with Redis caching

import { Redis } from '@upstash/redis'; import { type LanguageModelV4, type LanguageModelV4Middleware, type LanguageModelV4StreamPart, simulateReadableStream, } from 'ai'; const redis = new Redis({ url: process.env.KV_URL, token: process.env.KV_TOKEN, }); export const cacheMiddleware: LanguageModelV4Middleware = { wrapGenerate: async ({ doGenerate, params }) => { const cacheKey = JSON.stringify(params); const cached = (await redis.get(cacheKey)) as Awaited< ReturnType<LanguageModelV4['doGenerate']> > | null; if (cached !== null) { return { ...cached, response: { ...cached.response, timestamp: cached?.response?.timestamp ? new Date(cached?.response?.timestamp) : undefined, }, }; } const result = await doGenerate(); redis.set(cacheKey, result); return result; }, wrapStream: async ({ doStream, params }) => { const cacheKey = JSON.stringify(params); const cached = await redis.get(cacheKey); if (cached !== null) { const formattedChunks = (cached as LanguageModelV4StreamPart[]).map(p => { if (p.type === 'response-metadata' && p.timestamp) { return { ...p, timestamp: new Date(p.timestamp) }; } else return p; }); return { stream: simulateReadableStream({ initialDelayInMs: 0, chunkDelayInMs: 10, chunks: formattedChunks, }), }; } const { stream, ...rest } = await doStream(); const fullResponse: LanguageModelV4StreamPart[] = []; const transformStream = new TransformStream< LanguageModelV4StreamPart, LanguageModelV4StreamPart >({ transform(chunk, controller) { fullResponse.push(chunk); controller.enqueue(chunk); }, flush() { redis.set(cacheKey, fullResponse); }, }); return { stream: stream.pipeThrough(transformStream), ...rest, }; }, }; This example shows a complete caching middleware implementation using LanguageModelV4Middleware to cache both generate and stream responses with Redis.

Collect stream chunks before caching in wrapStream

In wrapStream, if the result is not cached, use a TransformStream to collect all stream parts into an array as they are streamed. The transform function pushes each chunk to fullResponse array and enqueues it. In the flush method, after streaming completes, store the fullResponse array in the cache using redis.set.

Use wrapLanguageModel to apply middleware to a model

To apply a LanguageModelV4Middleware to a model, use the wrapLanguageModel function. Pass an object with model (the model identifier string) and middleware (the middleware instance) properties. The returned wrappedModel can be used with streamText, generateText, and other functions.

Cache key can be generated from params using JSON.stringify

In caching middleware, you can create a cache key by stringifying the params object using JSON.stringify. This creates a deterministic key that represents the input parameters.

Normal programming in multi-step workflows

Multi-step streaming with createUIMessageStream supports standard programming constructs including if-else statements and loops between steps. This enables conditional branching and iterative workflows alongside streaming.

Different models and settings per streaming step

Each streamText call in a multi-step workflow can use different models, system prompts, tools, and other settings independently. This allows composing workflows where step 1 extracts goals with gpt-4o-mini, and step 2 processes with gpt-4o and different instructions.

streamText multi-step server implementation

Example showing a two-step streaming server. Step 1 uses openai/gpt-4o-mini with toolChoice 'required' to extract a goal. Step 2 uses openai/gpt-4o with a different system prompt and no tools, continuing from step 1 results. Each step forwards via writer.merge with toUIMessageStream, step 1 with sendFinish false and step 2 with sendStart false.

Passing previous step results to next streamText call

To continue a workflow stream with results from a previous step, use convertToModelMessages on original messages and concatenate with (await result1.response).messages to build the messages array for the next streamText call. This preserves the conversation history and tool calls from earlier steps.

sendFinish and sendStart options in toUIMessageStream

The toUIMessageStream function accepts sendFinish (boolean, default true) and sendStart (boolean, default true) options. Set sendFinish to false on all steps except the last to avoid premature finish events. Set sendStart to false on all steps except the first to avoid duplicate start events. This enables seamless multi-step streaming in a single assistant message.

Multi-step streaming with createUIMessageStream and streamText

To create multiple streaming steps within a single assistant message, use createUIMessageStream with execute callback. Inside execute, call streamText multiple times for different steps. Forward each step's result using writer.merge with toUIMessageStream, controlling finish and start events via sendFinish and sendStart parameters to prevent duplicate events between steps.

Server-side streaming route with convertToModelMessages and createUIMessageStreamResponse

Use convertToModelMessages to transform UIMessage objects into model-compatible format, streamText to get the language model response stream, toUIMessageStream to convert it to UI format, and createUIMessageStreamResponse to wrap the final response. This pattern enables streaming responses from Next.js route handlers.

Converting and streaming messages with tool responses

Use convertToModelMessages() to convert UIMessage[] to model-compatible message format before passing to streamText. Wrap the result stream with toUIMessageStream({ stream: result.stream }) and pass it to createUIMessageStreamResponse() to return a properly formatted streaming response that includes tool call results.

streamText server endpoint for tool execution

The POST handler at /api/chat receives messages as a JSON array of ChatMessage objects. It calls streamText() with parameters: model (e.g., 'openai/gpt-4o'), system (system prompt string), messages (converted using convertToModelMessages()), stopWhen (e.g., isStepCount(5)), and tools (the ToolSet object). The response is created using createUIMessageStreamResponse() with a stream from toUIMessageStream() wrapping result.stream.

Schema definition for streamed objects

Define schemas in a separate file using Zod (z.object, z.array, z.string with .describe() for field descriptions). Import this schema on both client and server. Use .describe() to provide instructions to the model about each field, such as 'Name of a fictional person' or 'Message. Do not use emojis or links.'

Example: streaming unschemaed JSON with Output.json

Server code: import { streamText, Output, createTextStreamResponse, toTextStream } from 'ai'; export const maxDuration = 30; export async function POST(req: Request) { const context = await req.json(); const result = streamText({ model: 'openai/gpt-4o', output: Output.json(), prompt: `Generate 3 notifications (in JSON) for a messages app in this context:` + context, }); return createTextStreamResponse({ stream: toTextStream({ stream: result.stream }), }); } Client code: const { object, submit, isLoading, stop } = useObject({ api: '/api/use-object', schema: z.unknown(), }); {JSON.stringify(object, null, 2)}

Output.json mode for unschemaed JSON generation

Output.json() can be used when you don't want to specify a schema, such as when the data structure is defined by dynamic user requests. The model will still attempt to generate JSON data based on the prompt. Use z.unknown() as the schema on the client side when using Output.json() on the server.

Output.array mode for streaming array elements

Output.array mode allows streaming an array of objects one element at a time, which is useful for generating lists of items. Use Output.array({ element: notificationSchema }) on the server. The schema passed to Output.array should be for a single element, not an array. On the client, wrap the schema in z.array() for the useObject hook.

Server-side streamText with Object output

On the server, use streamText with Output.object({ schema: notificationSchema }). Wrap the result stream using toTextStream({ stream: result.stream }) and return it with createTextStreamResponse. Set maxDuration to 30 or higher to allow sufficient time for generation.

Stream Object with Output.object mode

The streamText function with Output.object allows you to stream object generation in real-time to the client. This is useful for large schemas where generation takes a long time. The client displays the generated object as it is being generated, rather than waiting for completion. Use Output.object({ schema: yourSchema }) on the server and useObject hook on the client to handle partial results.

Use MCP tools with streamText in Next.js

Call streamText with model, tools (from MCP clients), and prompt. Use onEnd callback to close all MCP clients after streaming completes. Optionally close clients in onError callback; not closing keeps connections open for retries. Return response.toDataStreamResponse() to stream to client.

Server-side chat API route with streamText

Create a POST route at app/api/chat/route.ts that receives { messages: UIMessage[] } in the request body. Use convertToModelMessages(messages) to convert UIMessages to model messages. Call streamText() with the model and converted messages. Return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }) }).

Server route maxDuration setting

Set export const maxDuration = 30 in the API route to allow streaming responses up to 30 seconds.

Stream agent response in route handler

In a Next.js API route, call agent.stream() with converted model messages, then return createUIMessageStreamResponse wrapping toUIMessageStream of the result stream. Use convertToModelMessages to transform incoming AgentUIMessage objects before passing to agent.stream().

Give your agent this brain