Announcing AI SDK 5 Beta

AI SDK 5 is in beta — while more stable than alpha, APIs may still change. Pin to specific versions as breaking changes may occur in minor releases.

Beta Version Guidance

The AI SDK 5 Beta is intended for:

  • New projects where you can adopt the latest patterns from the start
  • Trying out new features and giving us feedback on the developer experience
  • Experimenting with migrations from v4 to understand the upgrade path
  • Development and testing environments where you can iterate quickly

Short on time? Wait for the stable release. We're focusing on polish and migration tooling improvements.

For production applications: Experiment with migrations in development, but avoid fully migrating production systems. Use this beta period to understand the changes and prepare your migration strategy.

What to Expect in Beta

  • No major breaking changes - the architecture is stable
  • Minor breaking changes possible - we may refine APIs for critical bugfixes
  • Bug fixes and DX improvements - active development continues

Your feedback during this beta phase directly shapes the final stable release. Share your experiences through GitHub issues.

While more stable than alpha, you may still encounter bugs in this Beta release. To help us improve the SDK, file bug reports on GitHub. Your reports directly contribute to making the final release more stable and reliable.

Installation

To install the AI SDK 5 Beta, run the following command:

# replace with your provider and framework
npm install ai@beta @ai-sdk/openai@beta @ai-sdk/react@beta

APIs may still change during beta. Pin to specific versions as breaking changes may occur in minor releases.

What's new in AI SDK 5?

AI SDK 5 is a redesign of the AI SDK's protocol and architecture based on everything we learned over the last two years of real-world usage. We also modernized the UI and protocols that have remained largely unchanged since AI SDK v2/3, to create a strong foundation for the future.

Why a new specification (LanguageModelV2)?

When we originally designed the v1 protocol over a year ago, the standard interaction pattern with language models was text in, text or tool call out. Today's LLMs go beyond text and tool calls, generating reasoning, sources, images and more. New use cases like computer-using agents introduce a fundamentally different approach to interacting with language models that made it impossible to support in a unified approach with our original architecture.

We needed a protocol designed for this new reality. While this is a breaking change that we take seriously, it provided an opportunity to rebuild the foundation and add new features.

New Features

LanguageModelV2

LanguageModelV2 represents a complete redesign of how the AI SDK communicates with language models, adapting to the increasingly complex outputs modern AI systems generate. The new LanguageModelV2 treats all LLM outputs as content parts, enabling consistent handling of text, images, reasoning, sources, and other response types. It has:

  • Content-First Design - Rather than separating text, reasoning, and tool calls, everything is represented as ordered content parts in a unified array
  • Improved Type Safety - The new LanguageModelV2 provides better TypeScript type guarantees, making it easier to work with different content types
  • Extensibility - Adding support for new model capabilities requires no changes to the core structure

Message Overhaul

AI SDK 5 introduces a completely redesigned message system with two message types that address the dual needs of what you render in your UI and what you send to the model. Context is crucial for effective language model generations, and these message types serve distinct purposes:

  • UIMessage represents the complete conversation history for your interface, preserving all message parts (text, images, data), metadata (creation timestamps, generation times), and UI state.

  • ModelMessage is optimized for sending to language models, considering token input constraints. It strips away UI-specific metadata and irrelevant content.

With this change, you must explicitly convert your UIMessages to ModelMessages before sending them to the model.

import { openai } from '@ai-sdk/openai';
import { convertToModelMessages, streamText, UIMessage } from 'ai';
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}

This separation is essential as you can't use a single message format for both purposes. Always save state in the UIMessage format to prevent information loss, with explicit conversion to ModelMessage when communicating with language models.

The new message system makes several highly requested features possible:

  • Type-safe Message Metadata - Add structured information per message
  • Type-safe Tool Calls - Improved type safety when defining and using tools in your messages
  • New Stream Writer - Stream any part type (reasoning, sources, etc.) retaining proper order
  • Data Parts - Stream type-safe arbitrary data parts for dynamic UI components

Type-safe Tool Calls

AI SDK 5 introduces type-safe tool calls in UI messages. Instead of generic tool-invocation types, tool parts use specific naming: tool-${toolName}. This provides better type safety and makes it easier to handle many tools in your UI.

AI SDK 4.0
// Generic tool-invocation type
{
message.parts.map(part => {
if (part.type === 'tool-invocation') {
return <div>{part.toolInvocation.toolName}</div>;
}
});
}
AI SDK 5.0
// Type-safe tool parts with specific names
{
message.parts.map(part => {
switch (part.type) {
case 'tool-getWeatherInformation':
return <div>Getting weather...</div>;
case 'tool-askForConfirmation':
return <div>Asking for confirmation...</div>;
}
});
}

Message metadata

Metadata allows you to attach structured information to individual messages, making it easier to track details like response time, token usage, or model specifications. This information can enhance your UI with contextual data without embedding it in the message content itself.

To add metadata to a message, first define the metadata schema:

app/api/chat/example-metadata-schema.ts
export const exampleMetadataSchema = z.object({
duration: z.number().optional(),
model: z.string().optional(),
totalTokens: z.number().optional(),
});
export type ExampleMetadata = z.infer<typeof exampleMetadataSchema>;

Then add the metadata using the message.metadata property on the toUIMessageStreamResponse() utility:

app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { convertToModelMessages, streamText, UIMessage } from 'ai';
import { ExampleMetadata } from './example-metadata-schema';
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const startTime = Date.now();
const result = streamText({
model: openai('gpt-4o'),
prompt: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse({
messageMetadata: ({ part }): ExampleMetadata | undefined => {
// send custom information to the client on start:
if (part.type === 'start') {
return {
model: 'gpt-4o', // initial model id
};
}
// send additional model information on finish-step:
if (part.type === 'finish-step') {
return {
model: part.response.modelId, // update with the actual model id
duration: Date.now() - startTime,
};
}
// when the message is finished, send additional information:
if (part.type === 'finish') {
return {
totalTokens: part.totalUsage.totalTokens,
};
}
},
});
}

Finally, use the metadata type with useChat and render the (type-safe) metadata in your UI:

app/page.tsx
import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport, UIMessage } from 'ai';
import { ExampleMetadata } from './api/chat/example-metadata-schema';
type MyMessage = UIMessage<ExampleMetadata>;
export default function Chat() {
const { messages } = useChat<MyMessage>({
transport: new DefaultChatTransport({
api: '/api/chat',
}),
});
return (
<div>
{messages.map(message => (
<div key={message.id} className="whitespace-pre-wrap">
{message.role === 'user' ? 'User: ' : 'AI: '}
{message.metadata?.duration && (
<div>Duration: {message.metadata.duration}ms</div>
)}
{message.metadata?.model && (
<div>Model: {message.metadata.model}</div>
)}
{message.metadata?.totalTokens && (
<div>Total tokens: {message.metadata.totalTokens}</div>
)}
</div>
))}
</div>
);
}

UIMessageStream

The UI Message Stream enables streaming any content parts from the server to the client. With this stream, you can send structured data like custom sources from your RAG pipeline directly to your UI. The stream writer is a utility that makes it easy to write to this message stream.

const stream = createUIMessageStream({
execute: writer => {
// stream custom sources
writer.write({
type: 'source',
value: {
type: 'source',
sourceType: 'url',
id: 'source-1',
url: 'https://example.com',
title: 'Example Source',
},
});
},
});

On the client, these will be added to the ordered message.parts array.

Data Parts

The new stream writer enables a type-safe way to stream arbitrary data from the server to the client and display it in your UI.

You can create and stream custom data parts on the server:

// On the server
const stream = createUIMessageStream({
execute: writer => {
// Initial update
writer.write({
type: 'data-weather', // Custom type
id: toolCallId, // ID for updates
data: { city, status: 'loading' }, // Your data
});
// Later, update the same part
writer.write({
type: 'data-weather',
id: toolCallId,
data: { city, weather, status: 'success' },
});
},
});

On the client, you can render these parts with full type safety:

{
message.parts
.filter(part => part.type === 'data-weather') // type-safe
.map((part, index) => (
<Weather
key={index}
city={part.data.city} // type-safe
weather={part.data.weather} // type-safe
status={part.data.status} // type-safe
/>
));
}

Data parts appear in the message.parts array along with other content, maintaining the proper ordering of the conversation. You can update parts by referencing the same ID, enabling dynamic experiences like collaborative artifacts.

Enhanced useChat Architecture

AI SDK 5 introduces a new useChat architecture with transport-based configuration. This design makes state management and API integration flexible, allowing you to configure backend protocols without rewriting application logic.

The new useChat hook uses a transport system for better modularity:

  • Transport Configuration – configure API endpoints and request handling through transport objects
  • Enhanced State Management – improved message handling with the new UIMessage format
  • Type Safety – stronger TypeScript support throughout the chat lifecycle

Configure useChat with the transport system:

import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: '/api/chat', // your chat endpoint
headers: { 'Custom-Header': 'value' },
}),
maxSteps: 5,
});

Server-Sent Events (SSE)

AI SDK 5 uses Server-Sent Events (SSE) instead of a custom streaming protocol. SSE is a common web standard for sending data from servers to browsers. This switch has several advantages:

  • Works everywhere - Uses technology that works in all major browsers and platforms
  • Easier to troubleshoot - See the data stream in browser developer tools
  • Simpler to build upon - Adding new features is more straightforward
  • More stable - Built on proven technology that many developers already use

Agentic Control

AI SDK 5 introduces new features for building agents that help you control model behavior more precisely.

prepareStep

The prepareStep function gives you fine-grained control over each step in a multi-step agent. It's called before a step starts and allows you to:

  • Dynamically change the model used for specific steps
  • Force specific tool selections for particular steps
  • Limit which tools are available during specific steps
  • Examine the context of previous steps before proceeding
const result = await generateText({
// ...
experimental_prepareStep: async ({ model, stepNumber, maxSteps, steps }) => {
if (stepNumber === 0) {
return {
// use a different model for this step:
model: modelForThisParticularStep,
// force a tool choice for this step:
toolChoice: { type: 'tool', toolName: 'tool1' },
// limit the tools that are available for this step:
experimental_activeTools: ['tool1'],
};
}
// when nothing is returned, the default settings are used
},
});

This makes it easier to build AI systems that adapt their capabilities based on context and task requirements.

stopWhen

The stopWhen parameter lets you define stopping conditions for your agent. Instead of running indefinitely, you can specify exactly when the agent should terminate based on various conditions:

  • Reaching a maximum number of steps
  • Calling a specific tool
  • Satisfying any custom condition you define
const result = generateText({
// ...
// stop loop at 5 steps
stopWhen: stepCountIs(5),
});
const result = generateText({
// ...
// stop loop when weather tool called
stopWhen: hasToolCall('weather'),
});
const result = generateText({
// ...
// stop loop at your own custom condition
stopWhen: maxTotalTokens(20000),
});

These agentic controls form the foundation for building reliable, controllable AI systems that tackle complex problems while remaining within well-defined constraints.

Additional New Features

Tool Output Schema

Tools can now optionally specify an output schema for better type inference and validation:

AI SDK 5.0
import { tool } from 'ai';
import { z } from 'zod';
const weatherTool = tool({
description: 'Get weather information',
inputSchema: z.object({
city: z.string(),
}),
outputSchema: z.object({
temperature: z.number(),
conditions: z.string(),
}),
execute: async ({ city }) => ({
temperature: 72,
conditions: 'sunny',
}),
});

Tool Type Inference Helpers

New utility types simplify working with tool types:

AI SDK 5.0
import { InferToolInput, InferToolOutput, InferUITool } from 'ai';
import { weatherTool } from './weatherTool';
// Infer input and output types from tool definitions
type WeatherInput = InferToolInput<typeof weatherTool>;
type WeatherOutput = InferToolOutput<typeof weatherTool>;
type WeatherUITool = InferUITool<typeof weatherTool>;
// Use in UI message type definitions
type MyUIMessage = UIMessage<
never, // metadata type
UIDataTypes, // data parts type
{
weather: WeatherUITool;
}
>;

OpenAI Provider-Executed Tools

New built-in tools for OpenAI:

AI SDK 5.0
import { openai } from '@ai-sdk/openai';
const result = await generateText({
model: openai('gpt-4.1'),
tools: {
file_search: openai.tools.fileSearch(),
web_search_preview: openai.tools.webSearchPreview({
searchContextSize: 'high',
}),
},
messages,
});

Available tools:

  • fileSearch: Search through uploaded documents using OpenAI's file search
  • webSearchPreview: Web search capabilities (preview feature)

When using provider-defined tools like fileSearch and webSearchPreview, the tool execution results are automatically added to the message history, providing context for subsequent interactions.

This automatic message history inclusion ensures that:

  • Tool execution context is preserved across conversation turns
  • Follow-up questions can reference previously searched information
  • The full conversation flow is maintained for debugging and logging

Enhanced Tool Streaming

Tools now support fine-grained streaming callbacks:

AI SDK 5.0
const weatherTool = tool({
inputSchema: z.object({ city: z.string() }),
onInputStart: ({ toolCallId }) => {
console.log('Tool input streaming started:', toolCallId);
},
onInputDelta: ({ inputTextDelta, toolCallId }) => {
console.log('Tool input delta:', inputTextDelta);
},
onInputAvailable: ({ input, toolCallId }) => {
console.log('Tool input ready:', input);
},
execute: async ({ city }) => {
return `Weather in ${city}: sunny, 72°F`;
},
});

Migration from AI SDK 4.x

Ready to upgrade from AI SDK 4.x to 5.0 Beta? We created a comprehensive migration guide to help you through the process.

The migration involves several key changes:

  • Updated message format with UIMessage and ModelMessage types
  • New useChat architecture with transport system
  • New streaming protocol with Server-Sent Events
  • Improved type safety and developer experience

View the complete Migration Guide →

The migration guide includes:

  • Step-by-step upgrade instructions
  • Detailed examples for each breaking change
  • Best practices for adopting new features