# AI SDK by Vercel The AI SDK is a TypeScript toolkit designed to help developers build AI-powered applications and agents with React, Next.js, Vue, Svelte, Node.js, and more. It standardizes integrating artificial intelligence models across supported providers, enabling developers to focus on building great AI applications without worrying about provider-specific implementation details. The SDK consists of two main libraries: **AI SDK Core** provides a unified API for generating text, structured objects, tool calls, and building agents with LLMs; **AI SDK UI** offers framework-agnostic hooks for quickly building chat and generative user interfaces. The SDK supports multiple model providers including OpenAI, Anthropic, Google, Amazon Bedrock, and many others through a consistent interface. --- ## AI SDK Core ### generateText Generates text for a given prompt and model. Ideal for non-interactive use cases like drafting emails, summarizing documents, or agents using tools. ```typescript import { generateText } from 'ai'; import { openai } from '@ai-sdk/openai'; const { text, usage, finishReason } = await generateText({ model: openai('gpt-4o'), system: 'You are a professional writer. You write simple, clear, and concise content.', prompt: 'Write a vegetarian lasagna recipe for 4 people.', }); console.log(text); console.log('Token usage:', usage); console.log('Finish reason:', finishReason); ``` ### streamText Streams text from a given prompt and model in real-time. Essential for interactive use cases like chatbots where users expect immediate responses. ```typescript import { streamText } from 'ai'; import { openai } from '@ai-sdk/openai'; const result = streamText({ model: openai('gpt-4o'), prompt: 'Invent a new holiday and describe its traditions.', onChunk({ chunk }) { if (chunk.type === 'text') { process.stdout.write(chunk.text); } }, onFinish({ text, usage, finishReason }) { console.log('\nCompleted:', { usage, finishReason }); }, }); // Use as async iterable for await (const textPart of result.textStream) { process.stdout.write(textPart); } // Or get final text const finalText = await result.text; ``` ### generateText with Structured Output Generates structured data conforming to a Zod schema. The AI SDK validates the output to ensure type safety and correctness. ```typescript import { generateText, Output } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; const { output } = await generateText({ model: openai('gpt-4o'), output: Output.object({ schema: z.object({ recipe: z.object({ name: z.string(), ingredients: z.array( z.object({ name: z.string(), amount: z.string() }) ), steps: z.array(z.string()), }), }), }), prompt: 'Generate a lasagna recipe.', }); console.log('Recipe:', output.recipe.name); console.log('Ingredients:', output.recipe.ingredients); console.log('Steps:', output.recipe.steps); ``` ### streamText with Structured Output Streams structured data generation, allowing partial objects to be displayed as they are received. ```typescript import { streamText, Output } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; const { partialOutputStream } = streamText({ model: openai('gpt-4o'), output: Output.object({ schema: z.object({ recipe: z.object({ name: z.string(), ingredients: z.array( z.object({ name: z.string(), amount: z.string() }) ), steps: z.array(z.string()), }), }), }), prompt: 'Generate a lasagna recipe.', }); for await (const partialObject of partialOutputStream) { console.clear(); console.log('Partial:', partialObject); } ``` ### Tool Calling Enables models to call external functions/tools to perform specific tasks. Tools have descriptions, input schemas, and execute functions. ```typescript import { generateText, tool, stepCountIs } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; const { text, steps, toolCalls, toolResults } = await generateText({ model: openai('gpt-4o'), 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, unit: 'fahrenheit', }), }), convertTemperature: tool({ description: 'Convert temperature from Fahrenheit to Celsius', inputSchema: z.object({ temperature: z.number().describe('Temperature in Fahrenheit'), }), execute: async ({ temperature }) => ({ celsius: Math.round((temperature - 32) * (5 / 9)), }), }), }, stopWhen: stepCountIs(5), prompt: 'What is the weather in San Francisco in celsius?', }); console.log('Final answer:', text); console.log('Tool calls made:', toolCalls); console.log('Tool results:', toolResults); console.log('Steps taken:', steps.length); ``` ### ToolLoopAgent A class that handles LLM tool loops automatically, managing context and stopping conditions for agent workflows. ```typescript import { ToolLoopAgent, stepCountIs, tool } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; const weatherAgent = new ToolLoopAgent({ model: openai('gpt-4o'), tools: { weather: tool({ description: 'Get the weather in a location (in Fahrenheit)', 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, }), }), convertFahrenheitToCelsius: tool({ description: 'Convert temperature from Fahrenheit to Celsius', inputSchema: z.object({ temperature: z.number().describe('Temperature in Fahrenheit'), }), execute: async ({ temperature }) => { const celsius = Math.round((temperature - 32) * (5 / 9)); return { celsius }; }, }), }, stopWhen: stepCountIs(20), }); const result = await weatherAgent.generate({ prompt: 'What is the weather in San Francisco in celsius?', }); console.log(result.text); console.log('Steps:', result.steps); ``` ### embed Generates embeddings for a single value. Useful for semantic search, similarity comparisons, and clustering. ```typescript import { embed } from 'ai'; import { openai } from '@ai-sdk/openai'; const { embedding, usage } = await embed({ model: openai.embedding('text-embedding-3-small'), value: 'sunny day at the beach', }); console.log('Embedding dimensions:', embedding.length); console.log('Token usage:', usage); ``` ### embedMany Generates embeddings for multiple values in batch. Ideal for preparing data stores for retrieval-augmented generation (RAG). ```typescript import { embedMany, cosineSimilarity } from 'ai'; import { openai } from '@ai-sdk/openai'; const { embeddings, usage } = await embedMany({ model: openai.embedding('text-embedding-3-small'), values: [ 'sunny day at the beach', 'rainy afternoon in the city', 'snowy night in the mountains', ], }); // Calculate similarity between first two embeddings const similarity = cosineSimilarity(embeddings[0], embeddings[1]); console.log('Similarity:', similarity); console.log('Total tokens:', usage.tokens); ``` --- ## AI SDK UI ### useChat Hook Creates a conversational UI for chatbot applications with real-time message streaming and state management. ```typescript // app/page.tsx (Client Component) 'use client'; import { useChat } from '@ai-sdk/react'; import { DefaultChatTransport } from 'ai'; import { useState } from 'react'; export default function ChatPage() { const { messages, sendMessage, status, stop, error, regenerate, setMessages } = useChat({ transport: new DefaultChatTransport({ api: '/api/chat', }), onFinish: ({ message, messages }) => { console.log('Chat completed:', message); }, onError: (error) => { console.error('Chat error:', error); }, }); const [input, setInput] = useState(''); return (
{messages.map((message) => (
{message.role === 'user' ? 'User: ' : 'AI: '} {message.parts.map((part, index) => part.type === 'text' ? {part.text} : null )}
))} {error && (

An error occurred.

)} {(status === 'submitted' || status === 'streaming') && ( )}
{ e.preventDefault(); if (input.trim()) { sendMessage({ text: input }); setInput(''); } }} > setInput(e.target.value)} disabled={status !== 'ready'} placeholder="Say something..." />
); } ``` ```typescript // app/api/chat/route.ts (Server Route) import { convertToModelMessages, streamText, UIMessage } from 'ai'; import { openai } from '@ai-sdk/openai'; export const maxDuration = 30; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: openai('gpt-4o'), system: 'You are a helpful assistant.', messages: await convertToModelMessages(messages), }); return result.toUIMessageStreamResponse(); } ``` ### useObject Hook Creates interfaces that represent structured JSON objects being streamed, ideal for generating dynamic UI components. ```typescript // app/api/notifications/schema.ts import { z } from 'zod'; export const notificationSchema = z.object({ notifications: z.array( z.object({ name: z.string().describe('Name of a fictional person.'), message: z.string().describe('Message. Do not use emojis or links.'), }) ), }); ``` ```typescript // app/page.tsx (Client Component) 'use client'; import { experimental_useObject as useObject } from '@ai-sdk/react'; import { notificationSchema } from './api/notifications/schema'; export default function NotificationsPage() { const { object, submit, isLoading, error, stop } = useObject({ api: '/api/notifications', schema: notificationSchema, onFinish({ object, error }) { console.log('Generation completed:', object); }, }); return (
{isLoading && } {error &&

An error occurred.

} {object?.notifications?.map((notification, index) => (
{notification?.name}

{notification?.message}

))}
); } ``` ```typescript // app/api/notifications/route.ts (Server Route) import { streamText, Output } from 'ai'; import { openai } from '@ai-sdk/openai'; import { notificationSchema } from './schema'; export const maxDuration = 30; export async function POST(req: Request) { const context = await req.json(); const result = streamText({ model: openai('gpt-4o'), output: Output.object({ schema: notificationSchema }), prompt: `Generate 3 notifications for a messages app in this context: ${context}`, }); return result.toTextStreamResponse(); } ``` --- ## Provider Configuration ### OpenAI Provider Supports language models, embeddings, image generation, transcription, and speech synthesis. ```typescript import { openai, createOpenAI } from '@ai-sdk/openai'; import { generateText, streamText } from 'ai'; // Default provider instance const { text } = await generateText({ model: openai('gpt-4o'), prompt: 'Write a haiku about programming.', }); // Custom provider instance const customOpenAI = createOpenAI({ apiKey: process.env.OPENAI_API_KEY, baseURL: 'https://custom-api.example.com/v1', headers: { 'X-Custom-Header': 'value' }, }); // Using reasoning models with extended thinking const result = await generateText({ model: openai('o1'), prompt: 'Explain quantum entanglement step by step.', providerOptions: { openai: { reasoningEffort: 'medium', }, }, }); console.log('Reasoning tokens:', result.providerMetadata?.openai?.reasoningTokens); // Web search tool const searchResult = await generateText({ model: openai('gpt-4o'), prompt: 'What happened in tech news today?', tools: { web_search: openai.tools.webSearch({ searchContextSize: 'high', }), }, }); ``` ### Anthropic Provider Supports Claude models with features like extended thinking, computer use, and prompt caching. ```typescript import { anthropic, createAnthropic } from '@ai-sdk/anthropic'; import { generateText, tool } from 'ai'; import { z } from 'zod'; // Default provider instance const { text } = await generateText({ model: anthropic('claude-3-5-sonnet-20241022'), prompt: 'Write a haiku about programming.', }); // Extended thinking with Claude 3.5 Sonnet const { text: reasonedText, reasoning } = await generateText({ model: anthropic('claude-3-7-sonnet-20250219'), prompt: 'How many people will live in the world in 2040?', providerOptions: { anthropic: { thinking: { type: 'enabled', budgetTokens: 12000 }, }, }, }); console.log('Reasoning:', reasoning); console.log('Answer:', reasonedText); // Prompt caching for long contexts const longDocument = '... very long document ...'; const cachedResult = await generateText({ model: anthropic('claude-3-5-sonnet-20241022'), messages: [ { role: 'user', content: [ { type: 'text', text: longDocument, providerOptions: { anthropic: { cacheControl: { type: 'ephemeral' } }, }, }, { type: 'text', text: 'Summarize this document.' }, ], }, ], }); console.log('Cache tokens:', cachedResult.providerMetadata?.anthropic?.cacheCreationInputTokens); ``` ### Google Provider Supports Gemini models with multimodal capabilities. ```typescript import { google } from '@ai-sdk/google'; import { generateText } from 'ai'; import { readFileSync } from 'fs'; // Text generation const { text } = await generateText({ model: google('gemini-1.5-pro'), prompt: 'Explain the theory of relativity.', }); // Multimodal with images const imageResult = await generateText({ model: google('gemini-1.5-pro'), messages: [ { role: 'user', content: [ { type: 'text', text: 'Describe this image in detail.' }, { type: 'image', image: readFileSync('./image.png'), }, ], }, ], }); ``` --- ## Next.js API Routes ### Route Handler with streamText Creates a streaming API endpoint for chat applications using Next.js App Router. ```typescript // app/api/chat/route.ts import { convertToModelMessages, streamText, UIMessage } from 'ai'; import { openai } from '@ai-sdk/openai'; export const maxDuration = 30; export async function POST(req: Request) { const { messages, customOption }: { messages: UIMessage[]; customOption?: string } = await req.json(); const result = streamText({ model: openai('gpt-4o'), system: 'You are a helpful assistant.', messages: await convertToModelMessages(messages), onFinish({ text, usage }) { // Log usage or save to database console.log('Completed:', { textLength: text.length, usage }); }, }); return result.toUIMessageStreamResponse({ sendReasoning: true, sendSources: true, messageMetadata: ({ part }) => { if (part.type === 'finish') { return { totalTokens: part.totalUsage.totalTokens }; } }, onError: (error) => { console.error('Stream error:', error); return 'An error occurred during generation.'; }, }); } ``` ### Route Handler with Tool Execution Creates an API endpoint that supports tool calling with multi-step execution. ```typescript // app/api/agent/route.ts import { convertToModelMessages, streamText, tool, stepCountIs, UIMessage } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; export const maxDuration = 60; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: openai('gpt-4o'), system: 'You are a helpful assistant with access to weather data.', messages: await convertToModelMessages(messages), tools: { getWeather: tool({ description: 'Get current weather for a location', inputSchema: z.object({ location: z.string().describe('City name'), unit: z.enum(['celsius', 'fahrenheit']).optional().default('celsius'), }), execute: async ({ location, unit }) => { // Simulate API call const temp = Math.floor(Math.random() * 30) + 10; return { location, temperature: unit === 'fahrenheit' ? temp * 1.8 + 32 : temp, unit, condition: ['sunny', 'cloudy', 'rainy'][Math.floor(Math.random() * 3)], }; }, }), }, stopWhen: stepCountIs(10), onStepFinish({ text, toolCalls, toolResults }) { console.log('Step completed:', { text, toolCalls: toolCalls.length }); }, }); return result.toUIMessageStreamResponse(); } ``` --- ## Summary The AI SDK provides a comprehensive toolkit for building AI-powered applications across multiple frameworks. Its core strength lies in abstracting away provider-specific complexities while offering a unified API for text generation, structured data output, tool calling, and embeddings. The streaming-first architecture ensures responsive user experiences, while the type-safe schema validation with Zod guarantees data integrity. Key integration patterns include using `generateText` for server-side batch processing and `streamText` for real-time interactive applications. The `useChat` and `useObject` hooks simplify building chat interfaces and dynamic UI components in React applications. For complex agent workflows, the `ToolLoopAgent` class handles multi-step tool execution automatically. Providers like OpenAI, Anthropic, and Google are supported through consistent interfaces, enabling easy switching between models while maintaining the same application code structure. The SDK's middleware system allows for customization of model behavior, caching strategies, and telemetry integration.