Initial commit
This commit is contained in:
522
references/v5-breaking-changes.md
Normal file
522
references/v5-breaking-changes.md
Normal file
@@ -0,0 +1,522 @@
|
||||
# AI SDK v4 → v5 Migration Guide
|
||||
|
||||
Complete guide to breaking changes from AI SDK v4 to v5.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
AI SDK v5 introduced **extensive breaking changes** to improve consistency, type safety, and functionality. This guide covers all critical changes with before/after examples.
|
||||
|
||||
**Migration Effort:** Medium-High (2-8 hours depending on codebase size)
|
||||
|
||||
**Automated Migration Available:**
|
||||
```bash
|
||||
npx ai migrate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core API Changes
|
||||
|
||||
### 1. Parameter Renames
|
||||
|
||||
**Change:** `maxTokens` → `maxOutputTokens`, `providerMetadata` → `providerOptions`
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
const result = await generateText({
|
||||
model: openai.chat('gpt-4'),
|
||||
maxTokens: 500,
|
||||
providerMetadata: {
|
||||
openai: { user: 'user-123' }
|
||||
},
|
||||
prompt: 'Hello',
|
||||
});
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
maxOutputTokens: 500,
|
||||
providerOptions: {
|
||||
openai: { user: 'user-123' }
|
||||
},
|
||||
prompt: 'Hello',
|
||||
});
|
||||
```
|
||||
|
||||
**Why:** `maxOutputTokens` is clearer that it limits generated tokens, not prompt tokens. `providerOptions` better reflects that it's for provider-specific configuration.
|
||||
|
||||
---
|
||||
|
||||
### 2. Tool Definitions
|
||||
|
||||
**Change:** `parameters` → `inputSchema`, tool properties renamed
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
const tools = {
|
||||
weather: {
|
||||
description: 'Get weather',
|
||||
parameters: z.object({
|
||||
location: z.string(),
|
||||
}),
|
||||
execute: async (args) => {
|
||||
return { temp: 72, location: args.location };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// In tool call result:
|
||||
console.log(toolCall.args); // { location: "SF" }
|
||||
console.log(toolCall.result); // { temp: 72 }
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
import { tool } from 'ai';
|
||||
|
||||
const tools = {
|
||||
weather: tool({
|
||||
description: 'Get weather',
|
||||
inputSchema: z.object({
|
||||
location: z.string(),
|
||||
}),
|
||||
execute: async ({ location }) => {
|
||||
return { temp: 72, location };
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
// In tool call result:
|
||||
console.log(toolCall.input); // { location: "SF" }
|
||||
console.log(toolCall.output); // { temp: 72 }
|
||||
```
|
||||
|
||||
**Why:** `inputSchema` clarifies it's a Zod schema. `input`/`output` are clearer than `args`/`result`.
|
||||
|
||||
---
|
||||
|
||||
### 3. Message Types
|
||||
|
||||
**Change:** `CoreMessage` → `ModelMessage`, `Message` → `UIMessage`
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
import { CoreMessage, convertToCoreMessages } from 'ai';
|
||||
|
||||
const messages: CoreMessage[] = [
|
||||
{ role: 'user', content: 'Hello' },
|
||||
];
|
||||
|
||||
const converted = convertToCoreMessages(uiMessages);
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
import { ModelMessage, convertToModelMessages } from 'ai';
|
||||
|
||||
const messages: ModelMessage[] = [
|
||||
{ role: 'user', content: 'Hello' },
|
||||
];
|
||||
|
||||
const converted = convertToModelMessages(uiMessages);
|
||||
```
|
||||
|
||||
**Why:** `ModelMessage` better reflects that these are messages for the model. `UIMessage` is for UI hooks.
|
||||
|
||||
---
|
||||
|
||||
### 4. Tool Error Handling
|
||||
|
||||
**Change:** `ToolExecutionError` removed, errors now appear as content parts
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
import { ToolExecutionError } from 'ai';
|
||||
|
||||
const tools = {
|
||||
risky: {
|
||||
execute: async (args) => {
|
||||
throw new ToolExecutionError({
|
||||
message: 'API failed',
|
||||
cause: originalError,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Error would stop execution
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
const tools = {
|
||||
risky: tool({
|
||||
execute: async (input) => {
|
||||
// Just throw regular errors
|
||||
throw new Error('API failed');
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
// Error appears as tool-error content part
|
||||
// Model can see the error and retry or handle it
|
||||
```
|
||||
|
||||
**Why:** Enables automated retry in multi-step scenarios. Model can see and respond to errors.
|
||||
|
||||
---
|
||||
|
||||
### 5. Multi-Step Execution
|
||||
|
||||
**Change:** `maxSteps` → `stopWhen` with conditions
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
const result = await generateText({
|
||||
model: openai.chat('gpt-4'),
|
||||
tools: { /* ... */ },
|
||||
maxSteps: 5,
|
||||
experimental_continueSteps: true,
|
||||
prompt: 'Complex task',
|
||||
});
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
import { stopWhen, stepCountIs } from 'ai';
|
||||
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
tools: { /* ... */ },
|
||||
stopWhen: stepCountIs(5),
|
||||
prompt: 'Complex task',
|
||||
});
|
||||
|
||||
// Or stop on specific tool:
|
||||
stopWhen: hasToolCall('finalize')
|
||||
|
||||
// Or custom condition:
|
||||
stopWhen: (step) => step.stepCount > 5 || step.hasToolCall('finish')
|
||||
```
|
||||
|
||||
**Why:** More flexible control over when multi-step execution stops. `experimental_continueSteps` no longer needed.
|
||||
|
||||
---
|
||||
|
||||
### 6. Message Structure
|
||||
|
||||
**Change:** Simple `content` string → `parts` array
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
const message = {
|
||||
role: 'user',
|
||||
content: 'Hello',
|
||||
};
|
||||
|
||||
// Tool calls embedded in message differently
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
const message = {
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'text', text: 'Hello' },
|
||||
],
|
||||
};
|
||||
|
||||
// Tool calls as parts:
|
||||
const messageWithTool = {
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{ type: 'text', text: 'Let me check...' },
|
||||
{
|
||||
type: 'tool-call',
|
||||
toolCallId: '123',
|
||||
toolName: 'weather',
|
||||
args: { location: 'SF' },
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
**Part Types:**
|
||||
- `text`: Text content
|
||||
- `file`: File attachments
|
||||
- `reasoning`: Extended thinking (Claude)
|
||||
- `tool-call`: Tool invocation
|
||||
- `tool-result`: Tool result
|
||||
- `tool-error`: Tool error (new in v5)
|
||||
|
||||
**Why:** Unified structure for all content types. Enables richer message formats.
|
||||
|
||||
---
|
||||
|
||||
### 7. Streaming Architecture
|
||||
|
||||
**Change:** Single chunk format → start/delta/end lifecycle
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
stream.on('chunk', (chunk) => {
|
||||
console.log(chunk.text);
|
||||
});
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
for await (const part of stream.fullStream) {
|
||||
if (part.type === 'text-delta') {
|
||||
console.log(part.textDelta);
|
||||
} else if (part.type === 'finish') {
|
||||
console.log('Stream finished:', part.finishReason);
|
||||
}
|
||||
}
|
||||
|
||||
// Or use simplified textStream:
|
||||
for await (const text of stream.textStream) {
|
||||
console.log(text);
|
||||
}
|
||||
```
|
||||
|
||||
**Stream Event Types:**
|
||||
- `text-delta`: Text chunk
|
||||
- `tool-call-delta`: Tool call chunk
|
||||
- `tool-result`: Tool result
|
||||
- `finish`: Stream complete
|
||||
- `error`: Stream error
|
||||
|
||||
**Why:** Better structure for concurrent streaming and metadata.
|
||||
|
||||
---
|
||||
|
||||
### 8. Tool Streaming
|
||||
|
||||
**Change:** Enabled by default
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
const result = await generateText({
|
||||
model: openai.chat('gpt-4'),
|
||||
tools: { /* ... */ },
|
||||
toolCallStreaming: true, // Opt-in
|
||||
});
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
tools: { /* ... */ },
|
||||
// Tool streaming enabled by default
|
||||
});
|
||||
```
|
||||
|
||||
**Why:** Better UX. Tools stream by default for real-time feedback.
|
||||
|
||||
---
|
||||
|
||||
### 9. Package Reorganization
|
||||
|
||||
**Change:** Separate packages for RSC and React
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
import { streamUI } from 'ai/rsc';
|
||||
import { useChat } from 'ai/react';
|
||||
import { LangChainAdapter } from 'ai';
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
import { streamUI } from '@ai-sdk/rsc';
|
||||
import { useChat } from '@ai-sdk/react';
|
||||
import { LangChainAdapter } from '@ai-sdk/langchain';
|
||||
```
|
||||
|
||||
**Install:**
|
||||
```bash
|
||||
npm install @ai-sdk/rsc @ai-sdk/react @ai-sdk/langchain
|
||||
```
|
||||
|
||||
**Why:** Cleaner package structure. Easier to tree-shake unused functionality.
|
||||
|
||||
---
|
||||
|
||||
## UI Hook Changes (See ai-sdk-ui Skill)
|
||||
|
||||
Brief summary (detailed in `ai-sdk-ui` skill):
|
||||
|
||||
1. **useChat Input Management:** No longer managed by hook
|
||||
2. **useChat Actions:** `append()` → `sendMessage()`
|
||||
3. **useChat Props:** `initialMessages` → `messages` (controlled)
|
||||
4. **StreamData Removed:** Replaced by message streams
|
||||
|
||||
See: `ai-sdk-ui` skill for complete UI migration guide
|
||||
|
||||
---
|
||||
|
||||
## Provider-Specific Changes
|
||||
|
||||
### OpenAI
|
||||
|
||||
**Change:** Default API changed
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
const model = openai.chat('gpt-4'); // Uses Chat Completions API
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
const model = openai('gpt-4'); // Uses Responses API
|
||||
// strictSchemas: true → strictJsonSchema: true
|
||||
```
|
||||
|
||||
**Why:** Responses API is newer and has better features.
|
||||
|
||||
---
|
||||
|
||||
### Google
|
||||
|
||||
**Change:** Search grounding moved to tool
|
||||
|
||||
**Before (v4):**
|
||||
```typescript
|
||||
const model = google.generativeAI('gemini-pro', {
|
||||
googleSearchRetrieval: true,
|
||||
});
|
||||
```
|
||||
|
||||
**After (v5):**
|
||||
```typescript
|
||||
import { google, googleSearchRetrieval } from '@ai-sdk/google';
|
||||
|
||||
const result = await generateText({
|
||||
model: google('gemini-pro'),
|
||||
tools: {
|
||||
search: googleSearchRetrieval(),
|
||||
},
|
||||
prompt: 'Search for...',
|
||||
});
|
||||
```
|
||||
|
||||
**Why:** More flexible. Search is now a tool like others.
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] Update package versions (`ai@^5.0.76`, `@ai-sdk/openai@^2.0.53`, etc.)
|
||||
- [ ] Run automated migration: `npx ai migrate`
|
||||
- [ ] Review automated changes
|
||||
- [ ] Update all `maxTokens` → `maxOutputTokens`
|
||||
- [ ] Update `providerMetadata` → `providerOptions`
|
||||
- [ ] Convert tool `parameters` → `inputSchema`
|
||||
- [ ] Update tool properties: `args` → `input`, `result` → `output`
|
||||
- [ ] Replace `maxSteps` with `stopWhen(stepCountIs(n))`
|
||||
- [ ] Update message types: `CoreMessage` → `ModelMessage`
|
||||
- [ ] Remove `ToolExecutionError` handling (just throw errors)
|
||||
- [ ] Update package imports (`ai/rsc` → `@ai-sdk/rsc`)
|
||||
- [ ] Test streaming behavior
|
||||
- [ ] Update TypeScript types
|
||||
- [ ] Test tool calling
|
||||
- [ ] Test multi-step execution
|
||||
- [ ] Check for message structure changes in your code
|
||||
- [ ] Update any custom error handling
|
||||
- [ ] Test with real API calls
|
||||
|
||||
---
|
||||
|
||||
## Common Migration Errors
|
||||
|
||||
### Error: "maxTokens is not a valid parameter"
|
||||
|
||||
**Solution:** Change to `maxOutputTokens`
|
||||
|
||||
### Error: "ToolExecutionError is not exported from 'ai'"
|
||||
|
||||
**Solution:** Remove ToolExecutionError, just throw regular errors
|
||||
|
||||
### Error: "Cannot find module 'ai/rsc'"
|
||||
|
||||
**Solution:** Install and import from `@ai-sdk/rsc`
|
||||
```bash
|
||||
npm install @ai-sdk/rsc
|
||||
import { streamUI } from '@ai-sdk/rsc';
|
||||
```
|
||||
|
||||
### Error: "model.chat is not a function"
|
||||
|
||||
**Solution:** Remove `.chat()` call
|
||||
```typescript
|
||||
// Before: openai.chat('gpt-4')
|
||||
// After: openai('gpt-4')
|
||||
```
|
||||
|
||||
### Error: "maxSteps is not a valid parameter"
|
||||
|
||||
**Solution:** Use `stopWhen(stepCountIs(n))`
|
||||
|
||||
---
|
||||
|
||||
## Testing After Migration
|
||||
|
||||
```typescript
|
||||
// Test basic generation
|
||||
const test1 = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
console.log('✅ Basic generation:', test1.text);
|
||||
|
||||
// Test streaming
|
||||
const test2 = streamText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
for await (const chunk of test2.textStream) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
console.log('\n✅ Streaming works');
|
||||
|
||||
// Test structured output
|
||||
const test3 = await generateObject({
|
||||
model: openai('gpt-4'),
|
||||
schema: z.object({ name: z.string() }),
|
||||
prompt: 'Generate a person',
|
||||
});
|
||||
console.log('✅ Structured output:', test3.object);
|
||||
|
||||
// Test tools
|
||||
const test4 = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
tools: {
|
||||
test: tool({
|
||||
description: 'Test tool',
|
||||
inputSchema: z.object({ value: z.string() }),
|
||||
execute: async ({ value }) => ({ result: value }),
|
||||
}),
|
||||
},
|
||||
prompt: 'Use the test tool with value "hello"',
|
||||
});
|
||||
console.log('✅ Tools work');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- **Official Migration Guide:** https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0
|
||||
- **Automated Migration:** `npx ai migrate`
|
||||
- **GitHub Discussions:** https://github.com/vercel/ai/discussions
|
||||
- **v5 Release Blog:** https://vercel.com/blog/ai-sdk-5
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-10-21
|
||||
Reference in New Issue
Block a user