- Created skills/ directory - Moved 272 skills to skills/ subfolder - Kept agents/ at root level - Kept installation scripts and docs at root level Repository structure: - skills/ - All 272 skills from skills.sh - agents/ - Agent definitions - *.sh, *.ps1 - Installation scripts - README.md, etc. - Documentation Co-Authored-By: Claude <noreply@anthropic.com>
1357 lines
36 KiB
Markdown
1357 lines
36 KiB
Markdown
---
|
|
name: ai-sdk-core
|
|
description: |
|
|
Build backend AI with Vercel AI SDK v6 stable. Covers Output API (replaces generateObject/streamObject), speech synthesis, transcription, embeddings, MCP tools with security guidance. Includes v4→v5 migration and 15 error solutions with workarounds.
|
|
|
|
Use when: implementing AI SDK v5/v6, migrating versions, troubleshooting AI_APICallError, Workers startup issues, Output API errors, Gemini caching issues, Anthropic tool errors, MCP tools, or stream resumption failures.
|
|
user-invocable: true
|
|
---
|
|
|
|
# AI SDK Core
|
|
|
|
Backend AI with Vercel AI SDK v5 and v6.
|
|
|
|
**Installation:**
|
|
```bash
|
|
npm install ai @ai-sdk/openai @ai-sdk/anthropic @ai-sdk/google zod
|
|
```
|
|
|
|
---
|
|
|
|
## AI SDK 6 (Stable - January 2026)
|
|
|
|
**Status:** Stable
|
|
**Latest:** ai@6.0.26 (Jan 2026)
|
|
|
|
### BREAKING: Output API Replaces generateObject/streamObject
|
|
|
|
⚠️ **CRITICAL**: `generateObject()` and `streamObject()` are **DEPRECATED** and will be removed in a future version. Use the new Output API instead.
|
|
|
|
**Before (v5 - DEPRECATED):**
|
|
```typescript
|
|
// ❌ DEPRECATED - will be removed
|
|
import { generateObject } from 'ai';
|
|
|
|
const result = await generateObject({
|
|
model: openai('gpt-5'),
|
|
schema: z.object({ name: z.string(), age: z.number() }),
|
|
prompt: 'Generate a person',
|
|
});
|
|
```
|
|
|
|
**After (v6 - USE THIS):**
|
|
```typescript
|
|
// ✅ NEW OUTPUT API
|
|
import { generateText, Output } from 'ai';
|
|
|
|
const result = await generateText({
|
|
model: openai('gpt-5'),
|
|
output: Output.object({ schema: z.object({ name: z.string(), age: z.number() }) }),
|
|
prompt: 'Generate a person',
|
|
});
|
|
|
|
// Access the typed object
|
|
console.log(result.object); // { name: "Alice", age: 30 }
|
|
```
|
|
|
|
### Output Types
|
|
|
|
```typescript
|
|
import { generateText, Output } from 'ai';
|
|
|
|
// Object with Zod schema
|
|
output: Output.object({ schema: myZodSchema })
|
|
|
|
// Array of typed objects
|
|
output: Output.array({ schema: personSchema })
|
|
|
|
// Enum/choice from options
|
|
output: Output.choice({ choices: ['positive', 'negative', 'neutral'] })
|
|
|
|
// Plain text (explicit)
|
|
output: Output.text()
|
|
|
|
// Unstructured JSON (no schema validation)
|
|
output: Output.json()
|
|
```
|
|
|
|
### Streaming with Output API
|
|
|
|
```typescript
|
|
import { streamText, Output } from 'ai';
|
|
|
|
const result = streamText({
|
|
model: openai('gpt-5'),
|
|
output: Output.object({ schema: personSchema }),
|
|
prompt: 'Generate a person',
|
|
});
|
|
|
|
// Stream partial objects
|
|
for await (const partialObject of result.objectStream) {
|
|
console.log(partialObject); // { name: "Ali..." } -> { name: "Alice", age: ... }
|
|
}
|
|
|
|
// Get final object
|
|
const finalObject = await result.object;
|
|
```
|
|
|
|
### v6 New Features
|
|
|
|
**1. Agent Abstraction**
|
|
Unified interface for building agents with `ToolLoopAgent` class:
|
|
- Full control over execution flow, tool loops, and state management
|
|
- Replaces manual tool calling orchestration
|
|
|
|
**2. Tool Execution Approval (Human-in-the-Loop)**
|
|
|
|
Use selective approval for better UX. Not every tool call needs approval.
|
|
|
|
```typescript
|
|
tools: {
|
|
payment: tool({
|
|
// Dynamic approval based on input
|
|
needsApproval: async ({ amount }) => amount > 1000,
|
|
inputSchema: z.object({ amount: z.number() }),
|
|
execute: async ({ amount }) => { /* process payment */ },
|
|
}),
|
|
|
|
readFile: tool({
|
|
needsApproval: false, // Safe operations don't need approval
|
|
inputSchema: z.object({ path: z.string() }),
|
|
execute: async ({ path }) => fs.readFile(path),
|
|
}),
|
|
|
|
deleteFile: tool({
|
|
needsApproval: true, // Destructive operations always need approval
|
|
inputSchema: z.object({ path: z.string() }),
|
|
execute: async ({ path }) => fs.unlink(path),
|
|
}),
|
|
}
|
|
```
|
|
|
|
**Best Practices**:
|
|
- Use dynamic approval for operations where risk depends on parameters (e.g., payment amount)
|
|
- Always require approval for destructive operations (delete, modify, purchase)
|
|
- Don't require approval for safe read operations
|
|
- Add system instruction: "When a tool execution is not approved, do not retry it"
|
|
- Implement timeout for approval requests to prevent stuck states
|
|
- Store user preferences for repeat actions
|
|
|
|
**Sources**:
|
|
- [Next.js Human-in-the-Loop Guide](https://ai-sdk.dev/cookbook/next/human-in-the-loop)
|
|
- [Cloudflare Agents Human-in-the-Loop](https://developers.cloudflare.com/agents/guides/human-in-the-loop/)
|
|
- [Permit.io Best Practices](https://www.permit.io/blog/human-in-the-loop-for-ai-agents-best-practices-frameworks-use-cases-and-demo)
|
|
|
|
**3. Reranking for RAG**
|
|
```typescript
|
|
import { rerank } from 'ai';
|
|
|
|
const result = await rerank({
|
|
model: cohere.reranker('rerank-v3.5'),
|
|
query: 'user question',
|
|
documents: searchResults,
|
|
topK: 5,
|
|
});
|
|
```
|
|
|
|
**4. MCP Tools (Model Context Protocol)**
|
|
|
|
⚠️ **SECURITY WARNING**: MCP tools have significant production risks. See security section below.
|
|
|
|
```typescript
|
|
import { experimental_createMCPClient } from 'ai';
|
|
|
|
const mcpClient = await experimental_createMCPClient({
|
|
transport: { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem'] },
|
|
});
|
|
|
|
const tools = await mcpClient.tools();
|
|
|
|
const result = await generateText({
|
|
model: openai('gpt-5'),
|
|
tools,
|
|
prompt: 'List files in the current directory',
|
|
});
|
|
```
|
|
|
|
**Known Issue**: MCP tools may not execute in streaming mode ([Vercel Community Discussion](https://community.vercel.com/t/question-how-to-properly-pass-mcp-tools-to-backend-using-ai-sdk-uis-usechat/29714)). Use `generateText()` instead of `streamText()` for MCP tools.
|
|
|
|
**MCP Security Considerations**
|
|
|
|
⚠️ **CRITICAL**: Dynamic MCP tools in production have security risks:
|
|
|
|
**Risks**:
|
|
- Tool definitions become part of your agent's prompt
|
|
- Can change unexpectedly without warning
|
|
- Compromised MCP server can inject malicious prompts
|
|
- New tools can escalate user privileges (e.g., adding delete to read-only server)
|
|
|
|
**Solution - Use Static Tool Generation**:
|
|
|
|
```typescript
|
|
// ❌ RISKY: Dynamic tools change without your control
|
|
const mcpClient = await experimental_createMCPClient({ /* ... */ });
|
|
const tools = await mcpClient.tools(); // Can change anytime!
|
|
|
|
// ✅ SAFE: Generate static, versioned tool definitions
|
|
// Step 1: Install mcp-to-ai-sdk
|
|
npm install -g mcp-to-ai-sdk
|
|
|
|
// Step 2: Generate static tools (one-time, version controlled)
|
|
npx mcp-to-ai-sdk generate stdio 'npx -y @modelcontextprotocol/server-filesystem'
|
|
|
|
// Step 3: Import static tools
|
|
import { tools } from './generated-mcp-tools';
|
|
|
|
const result = await generateText({
|
|
model: openai('gpt-5'),
|
|
tools, // Static, reviewed, versioned
|
|
prompt: 'Use tools',
|
|
});
|
|
```
|
|
|
|
**Best Practice**: Generate static tools, review them, commit to version control, and only update intentionally.
|
|
|
|
**Source**: [Vercel Blog: MCP Security](https://vercel.com/blog/generate-static-ai-sdk-tools-from-mcp-servers-with-mcp-to-ai-sdk)
|
|
|
|
**5. Language Model Middleware**
|
|
```typescript
|
|
import { wrapLanguageModel, extractReasoningMiddleware } from 'ai';
|
|
|
|
const wrappedModel = wrapLanguageModel({
|
|
model: anthropic('claude-sonnet-4-5-20250929'),
|
|
middleware: extractReasoningMiddleware({ tagName: 'think' }),
|
|
});
|
|
|
|
// Reasoning extracted automatically from <think>...</think> tags
|
|
```
|
|
|
|
**6. Telemetry (OpenTelemetry)**
|
|
```typescript
|
|
const result = await generateText({
|
|
model: openai('gpt-5'),
|
|
prompt: 'Hello',
|
|
experimental_telemetry: {
|
|
isEnabled: true,
|
|
functionId: 'my-chat-function',
|
|
metadata: { userId: '123' },
|
|
recordInputs: true,
|
|
recordOutputs: true,
|
|
},
|
|
});
|
|
```
|
|
|
|
**Official Docs:** https://ai-sdk.dev/docs
|
|
|
|
---
|
|
|
|
## Latest AI Models (2025-2026)
|
|
|
|
### OpenAI
|
|
|
|
**GPT-5.2** (Dec 2025):
|
|
- 400k context window, 128k output tokens
|
|
- Enhanced reasoning capabilities
|
|
- Available in API platform
|
|
|
|
**GPT-5.1** (Nov 2025):
|
|
- Improved speed and efficiency over GPT-5
|
|
- "Warmer" and more intelligent responses
|
|
|
|
**GPT-5** (Aug 2025):
|
|
- 45% less hallucination than GPT-4o
|
|
- State-of-the-art in math, coding, visual perception
|
|
|
|
**o3 Reasoning Models** (Dec 2025):
|
|
- o3, o3-pro, o3-mini - Advanced reasoning
|
|
- o4-mini - Fast reasoning
|
|
|
|
```typescript
|
|
import { openai } from '@ai-sdk/openai';
|
|
const gpt52 = openai('gpt-5.2');
|
|
const gpt51 = openai('gpt-5.1');
|
|
const gpt5 = openai('gpt-5');
|
|
const o3 = openai('o3');
|
|
const o3mini = openai('o3-mini');
|
|
```
|
|
|
|
### Anthropic
|
|
|
|
**Claude 4 Family** (May-Oct 2025):
|
|
- **Opus 4** (May 22): Best for complex reasoning, $15/$75 per million tokens
|
|
- **Sonnet 4** (May 22): Balanced performance, $3/$15 per million tokens
|
|
- **Opus 4.1** (Aug 5): Enhanced agentic tasks, real-world coding
|
|
- **Sonnet 4.5** (Sept 29): Most capable for coding, agents, computer use
|
|
- **Haiku 4.5** (Oct 15): Small, fast, low-latency model
|
|
|
|
```typescript
|
|
import { anthropic } from '@ai-sdk/anthropic';
|
|
const sonnet45 = anthropic('claude-sonnet-4-5-20250929'); // Latest
|
|
const opus41 = anthropic('claude-opus-4-1-20250805');
|
|
const haiku45 = anthropic('claude-haiku-4-5-20251015');
|
|
```
|
|
|
|
### Google
|
|
|
|
**Gemini 2.5 Family** (Mar-Sept 2025):
|
|
- **Pro** (March 2025): Most intelligent, #1 on LMArena at launch
|
|
- **Pro Deep Think** (May 2025): Enhanced reasoning mode
|
|
- **Flash** (May 2025): Fast, cost-effective
|
|
- **Flash-Lite** (Sept 2025): Updated efficiency
|
|
|
|
```typescript
|
|
import { google } from '@ai-sdk/google';
|
|
const pro = google('gemini-2.5-pro');
|
|
const flash = google('gemini-2.5-flash');
|
|
const lite = google('gemini-2.5-flash-lite');
|
|
```
|
|
|
|
---
|
|
|
|
## Core Functions
|
|
|
|
### Text Generation
|
|
|
|
**generateText()** - Text completion with tools
|
|
**streamText()** - Real-time streaming
|
|
|
|
### Structured Output (v6 Output API)
|
|
|
|
**Output.object()** - Typed objects with Zod schema (replaces generateObject)
|
|
**Output.array()** - Typed arrays
|
|
**Output.choice()** - Enum selection
|
|
**Output.json()** - Unstructured JSON
|
|
|
|
See "AI SDK 6" section above for usage examples.
|
|
|
|
### Multi-Modal Capabilities
|
|
|
|
#### Speech Synthesis (Text-to-Speech)
|
|
|
|
```typescript
|
|
import { experimental_generateSpeech as generateSpeech } from 'ai';
|
|
import { openai } from '@ai-sdk/openai';
|
|
|
|
const result = await generateSpeech({
|
|
model: openai.speech('tts-1-hd'),
|
|
voice: 'alloy',
|
|
text: 'Hello, how can I help you today?',
|
|
});
|
|
|
|
// result.audio is an ArrayBuffer containing the audio
|
|
const audioBuffer = result.audio;
|
|
```
|
|
|
|
**Supported Providers:**
|
|
- OpenAI: tts-1, tts-1-hd, gpt-4o-mini-tts
|
|
- ElevenLabs: eleven_multilingual_v2, eleven_turbo_v2
|
|
- LMNT, Hume
|
|
|
|
#### Transcription (Speech-to-Text)
|
|
|
|
```typescript
|
|
import { experimental_transcribe as transcribe } from 'ai';
|
|
import { openai } from '@ai-sdk/openai';
|
|
|
|
const result = await transcribe({
|
|
model: openai.transcription('whisper-1'),
|
|
audio: audioFile, // File, Blob, ArrayBuffer, or URL
|
|
});
|
|
|
|
console.log(result.text); // Transcribed text
|
|
console.log(result.segments); // Timestamped segments
|
|
```
|
|
|
|
**Supported Providers:**
|
|
- OpenAI: whisper-1
|
|
- ElevenLabs, Deepgram, AssemblyAI, Groq, Rev.ai
|
|
|
|
#### Image Generation
|
|
|
|
```typescript
|
|
import { generateImage } from 'ai';
|
|
import { openai } from '@ai-sdk/openai';
|
|
|
|
const result = await generateImage({
|
|
model: openai.image('dall-e-3'),
|
|
prompt: 'A futuristic city at sunset',
|
|
size: '1024x1024',
|
|
n: 1,
|
|
});
|
|
|
|
// result.images is an array of generated images
|
|
const imageUrl = result.images[0].url;
|
|
const imageBase64 = result.images[0].base64;
|
|
```
|
|
|
|
**Supported Providers:**
|
|
- OpenAI: dall-e-2, dall-e-3
|
|
- Google: imagen-3.0
|
|
- Fal AI, Black Forest Labs (Flux), Luma AI, Replicate
|
|
|
|
#### Embeddings
|
|
|
|
```typescript
|
|
import { embed, embedMany, cosineSimilarity } from 'ai';
|
|
import { openai } from '@ai-sdk/openai';
|
|
|
|
// Single embedding
|
|
const result = await embed({
|
|
model: openai.embedding('text-embedding-3-small'),
|
|
value: 'Hello world',
|
|
});
|
|
console.log(result.embedding); // number[]
|
|
|
|
// Multiple embeddings (parallel processing)
|
|
const results = await embedMany({
|
|
model: openai.embedding('text-embedding-3-small'),
|
|
values: ['Hello', 'World', 'AI'],
|
|
maxParallelCalls: 5, // Parallel processing
|
|
});
|
|
|
|
// Compare similarity
|
|
const similarity = cosineSimilarity(
|
|
results.embeddings[0],
|
|
results.embeddings[1]
|
|
);
|
|
console.log(`Similarity: ${similarity}`); // 0.0 to 1.0
|
|
```
|
|
|
|
**Supported Providers:**
|
|
- OpenAI: text-embedding-3-small, text-embedding-3-large
|
|
- Google: text-embedding-004
|
|
- Cohere, Voyage AI, Mistral, Amazon Bedrock
|
|
|
|
#### Multi-Modal Prompts (Files, Images, PDFs)
|
|
|
|
```typescript
|
|
import { generateText } from 'ai';
|
|
import { google } from '@ai-sdk/google';
|
|
|
|
const result = await generateText({
|
|
model: google('gemini-2.5-pro'),
|
|
messages: [{
|
|
role: 'user',
|
|
content: [
|
|
{ type: 'text', text: 'Summarize this document' },
|
|
{ type: 'file', data: pdfBuffer, mimeType: 'application/pdf' },
|
|
],
|
|
}],
|
|
});
|
|
|
|
// Or with images
|
|
const result = await generateText({
|
|
model: openai('gpt-5'),
|
|
messages: [{
|
|
role: 'user',
|
|
content: [
|
|
{ type: 'text', text: 'What is in this image?' },
|
|
{ type: 'image', image: imageBuffer },
|
|
],
|
|
}],
|
|
});
|
|
```
|
|
|
|
See official docs for full API: https://ai-sdk.dev/docs/ai-sdk-core
|
|
|
|
---
|
|
|
|
## v5 Stream Response Methods
|
|
|
|
When returning streaming responses from an API, use the correct method:
|
|
|
|
| Method | Output Format | Use Case |
|
|
|--------|---------------|----------|
|
|
| `toTextStreamResponse()` | Plain text chunks | Simple text streaming |
|
|
| `toUIMessageStreamResponse()` | SSE with JSON events | **Chat UIs** (text-start, text-delta, text-end, finish) |
|
|
|
|
**For chat widgets and UIs, always use `toUIMessageStreamResponse()`:**
|
|
|
|
```typescript
|
|
const result = streamText({
|
|
model: workersai('@cf/qwen/qwen3-30b-a3b-fp8'),
|
|
messages,
|
|
system: 'You are helpful.',
|
|
});
|
|
|
|
// ✅ For chat UIs - returns SSE with JSON events
|
|
return result.toUIMessageStreamResponse({
|
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
|
});
|
|
|
|
// ❌ For simple text - returns plain text chunks only
|
|
return result.toTextStreamResponse();
|
|
```
|
|
|
|
**Note:** `toDataStreamResponse()` does NOT exist in AI SDK v5 (common misconception).
|
|
|
|
---
|
|
|
|
## workers-ai-provider Version Compatibility
|
|
|
|
**IMPORTANT:** `workers-ai-provider@2.x` requires AI SDK v5, NOT v4.
|
|
|
|
```bash
|
|
# ✅ Correct - AI SDK v5 with workers-ai-provider v2
|
|
npm install ai@^5.0.0 workers-ai-provider@^2.0.0 zod@^3.25.0
|
|
|
|
# ❌ Wrong - AI SDK v4 causes error
|
|
npm install ai@^4.0.0 workers-ai-provider@^2.0.0
|
|
# Error: "AI SDK 4 only supports models that implement specification version v1"
|
|
```
|
|
|
|
**Zod Version:** AI SDK v5 requires `zod@^3.25.0` or later for `zod/v3` and `zod/v4` exports. Older versions (3.22.x) cause build errors: "Could not resolve zod/v4".
|
|
|
|
---
|
|
|
|
## Cloudflare Workers Startup Fix
|
|
|
|
**Problem:** AI SDK v5 + Zod causes >270ms startup time (exceeds Workers 400ms limit).
|
|
|
|
**Solution:**
|
|
```typescript
|
|
// ❌ BAD: Top-level imports cause startup overhead
|
|
import { createWorkersAI } from 'workers-ai-provider';
|
|
const workersai = createWorkersAI({ binding: env.AI });
|
|
|
|
// ✅ GOOD: Lazy initialization inside handler
|
|
app.post('/chat', async (c) => {
|
|
const { createWorkersAI } = await import('workers-ai-provider');
|
|
const workersai = createWorkersAI({ binding: c.env.AI });
|
|
// ...
|
|
});
|
|
```
|
|
|
|
**Additional:**
|
|
- Minimize top-level Zod schemas
|
|
- Move complex schemas into route handlers
|
|
- Monitor startup time with Wrangler
|
|
|
|
---
|
|
|
|
## v5 Tool Calling Changes
|
|
|
|
**Breaking Changes:**
|
|
- `parameters` → `inputSchema` (Zod schema)
|
|
- Tool properties: `args` → `input`, `result` → `output`
|
|
- `ToolExecutionError` removed (now `tool-error` content parts)
|
|
- `maxSteps` parameter removed → Use `stopWhen(stepCountIs(n))`
|
|
|
|
**New in v5:**
|
|
- Dynamic tools (add tools at runtime based on context)
|
|
- Agent class (multi-step execution with tools)
|
|
|
|
---
|
|
|
|
## Critical v4→v5 Migration
|
|
|
|
AI SDK v5 introduced extensive breaking changes. If migrating from v4, follow this guide.
|
|
|
|
### Breaking Changes Overview
|
|
|
|
1. **Parameter Renames**
|
|
- `maxTokens` → `maxOutputTokens`
|
|
- `providerMetadata` → `providerOptions`
|
|
|
|
2. **Tool Definitions**
|
|
- `parameters` → `inputSchema`
|
|
- Tool properties: `args` → `input`, `result` → `output`
|
|
|
|
3. **Message Types**
|
|
- `CoreMessage` → `ModelMessage`
|
|
- `Message` → `UIMessage`
|
|
- `convertToCoreMessages` → `convertToModelMessages`
|
|
|
|
4. **Tool Error Handling**
|
|
- `ToolExecutionError` class removed
|
|
- Now `tool-error` content parts
|
|
- Enables automated retry
|
|
|
|
5. **Multi-Step Execution**
|
|
- `maxSteps` → `stopWhen`
|
|
- Use `stepCountIs()` or `hasToolCall()`
|
|
|
|
6. **Message Structure**
|
|
- Simple `content` string → `parts` array
|
|
- Parts: text, file, reasoning, tool-call, tool-result
|
|
|
|
7. **Streaming Architecture**
|
|
- Single chunk → start/delta/end lifecycle
|
|
- Unique IDs for concurrent streams
|
|
|
|
8. **Tool Streaming**
|
|
- Enabled by default
|
|
- `toolCallStreaming` option removed
|
|
|
|
9. **Package Reorganization**
|
|
- `ai/rsc` → `@ai-sdk/rsc`
|
|
- `ai/react` → `@ai-sdk/react`
|
|
- `LangChainAdapter` → `@ai-sdk/langchain`
|
|
|
|
### Migration Examples
|
|
|
|
**Before (v4):**
|
|
```typescript
|
|
import { generateText } from 'ai';
|
|
|
|
const result = await generateText({
|
|
model: openai.chat('gpt-4-turbo'),
|
|
maxTokens: 500,
|
|
providerMetadata: { openai: { user: 'user-123' } },
|
|
tools: {
|
|
weather: {
|
|
description: 'Get weather',
|
|
parameters: z.object({ location: z.string() }),
|
|
execute: async (args) => { /* args.location */ },
|
|
},
|
|
},
|
|
maxSteps: 5,
|
|
});
|
|
```
|
|
|
|
**After (v5):**
|
|
```typescript
|
|
import { generateText, tool, stopWhen, stepCountIs } from 'ai';
|
|
|
|
const result = await generateText({
|
|
model: openai('gpt-4-turbo'),
|
|
maxOutputTokens: 500,
|
|
providerOptions: { openai: { user: 'user-123' } },
|
|
tools: {
|
|
weather: tool({
|
|
description: 'Get weather',
|
|
inputSchema: z.object({ location: z.string() }),
|
|
execute: async ({ location }) => { /* input.location */ },
|
|
}),
|
|
},
|
|
stopWhen: stepCountIs(5),
|
|
});
|
|
```
|
|
|
|
### Migration Checklist
|
|
|
|
- [ ] Update all `maxTokens` to `maxOutputTokens`
|
|
- [ ] Update `providerMetadata` to `providerOptions`
|
|
- [ ] Convert tool `parameters` to `inputSchema`
|
|
- [ ] Update tool execute functions: `args` → `input`
|
|
- [ ] Replace `maxSteps` with `stopWhen(stepCountIs(n))`
|
|
- [ ] Update message types: `CoreMessage` → `ModelMessage`
|
|
- [ ] Remove `ToolExecutionError` handling
|
|
- [ ] Update package imports (`ai/rsc` → `@ai-sdk/rsc`)
|
|
- [ ] Test streaming behavior (architecture changed)
|
|
- [ ] Update TypeScript types
|
|
|
|
### Automated Migration
|
|
|
|
AI SDK provides a migration tool:
|
|
|
|
```bash
|
|
npx ai migrate
|
|
```
|
|
|
|
This will update most breaking changes automatically. Review changes carefully.
|
|
|
|
**Official Migration Guide:**
|
|
https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0
|
|
|
|
---
|
|
|
|
## Top 15 Errors & Solutions
|
|
|
|
### 1. AI_APICallError
|
|
|
|
**Cause:** API request failed (network, auth, rate limit).
|
|
|
|
**Solution:**
|
|
```typescript
|
|
import { AI_APICallError } from 'ai';
|
|
|
|
try {
|
|
const result = await generateText({
|
|
model: openai('gpt-4-turbo'),
|
|
prompt: 'Hello',
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof AI_APICallError) {
|
|
console.error('API call failed:', error.message);
|
|
console.error('Status code:', error.statusCode);
|
|
console.error('Response:', error.responseBody);
|
|
|
|
// Check common causes
|
|
if (error.statusCode === 401) {
|
|
// Invalid API key
|
|
} else if (error.statusCode === 429) {
|
|
// Rate limit - implement backoff
|
|
} else if (error.statusCode >= 500) {
|
|
// Provider issue - retry
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Validate API keys at startup
|
|
- Implement retry logic with exponential backoff
|
|
- Monitor rate limits
|
|
- Handle network errors gracefully
|
|
|
|
---
|
|
|
|
### 2. AI_NoObjectGeneratedError
|
|
|
|
**Cause:** Model didn't generate valid object matching schema.
|
|
|
|
**Solution:**
|
|
```typescript
|
|
import { AI_NoObjectGeneratedError } from 'ai';
|
|
|
|
try {
|
|
const result = await generateObject({
|
|
model: openai('gpt-4-turbo'),
|
|
schema: z.object({ /* complex schema */ }),
|
|
prompt: 'Generate data',
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof AI_NoObjectGeneratedError) {
|
|
console.error('No valid object generated');
|
|
|
|
// Solutions:
|
|
// 1. Simplify schema
|
|
// 2. Add more context to prompt
|
|
// 3. Provide examples in prompt
|
|
// 4. Try different model (gpt-5 or claude-sonnet-4-5 for complex objects)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Start with simple schemas, add complexity incrementally
|
|
- Include examples in prompt: "Generate a person like: { name: 'Alice', age: 30 }"
|
|
- Use GPT-4 for complex structured output
|
|
- Test schemas with sample data first
|
|
|
|
---
|
|
|
|
### 3. Worker Startup Limit (270ms+)
|
|
|
|
**Cause:** AI SDK v5 + Zod initialization overhead in Cloudflare Workers exceeds startup limits.
|
|
|
|
**Solution:**
|
|
```typescript
|
|
// BAD: Top-level imports cause startup overhead
|
|
import { createWorkersAI } from 'workers-ai-provider';
|
|
import { complexSchema } from './schemas';
|
|
|
|
const workersai = createWorkersAI({ binding: env.AI });
|
|
|
|
// GOOD: Lazy initialization inside handler
|
|
export default {
|
|
async fetch(request, env) {
|
|
const { createWorkersAI } = await import('workers-ai-provider');
|
|
const workersai = createWorkersAI({ binding: env.AI });
|
|
|
|
// Use workersai here
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Move AI SDK imports inside route handlers
|
|
- Minimize top-level Zod schemas
|
|
- Monitor Worker startup time (must be <400ms)
|
|
- Use Wrangler's startup time reporting
|
|
|
|
**GitHub Issue:** Search for "Workers startup limit" in Vercel AI SDK issues
|
|
|
|
---
|
|
|
|
### 4. streamText Fails Silently
|
|
|
|
**Cause:** Stream errors can be swallowed by `createDataStreamResponse`.
|
|
|
|
**Status:** ✅ **RESOLVED** - Fixed in ai@4.1.22 (February 2025)
|
|
|
|
**Solution (Recommended):**
|
|
```typescript
|
|
// Use the onError callback (added in v4.1.22)
|
|
const stream = streamText({
|
|
model: openai('gpt-4-turbo'),
|
|
prompt: 'Hello',
|
|
onError({ error }) {
|
|
console.error('Stream error:', error);
|
|
// Custom error logging and handling
|
|
},
|
|
});
|
|
|
|
// Stream safely
|
|
for await (const chunk of stream.textStream) {
|
|
process.stdout.write(chunk);
|
|
}
|
|
```
|
|
|
|
**Alternative (Manual try-catch):**
|
|
```typescript
|
|
// Fallback if not using onError callback
|
|
try {
|
|
const stream = streamText({
|
|
model: openai('gpt-4-turbo'),
|
|
prompt: 'Hello',
|
|
});
|
|
|
|
for await (const chunk of stream.textStream) {
|
|
process.stdout.write(chunk);
|
|
}
|
|
} catch (error) {
|
|
console.error('Stream error:', error);
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- **Use `onError` callback** for proper error capture (recommended)
|
|
- Implement server-side error monitoring
|
|
- Test stream error handling explicitly
|
|
- Always log on server side in production
|
|
|
|
**GitHub Issue:** #4726 (RESOLVED)
|
|
|
|
---
|
|
|
|
### 5. AI_LoadAPIKeyError
|
|
|
|
**Cause:** Missing or invalid API key.
|
|
|
|
**Solution:**
|
|
```typescript
|
|
import { AI_LoadAPIKeyError } from 'ai';
|
|
|
|
try {
|
|
const result = await generateText({
|
|
model: openai('gpt-4-turbo'),
|
|
prompt: 'Hello',
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof AI_LoadAPIKeyError) {
|
|
console.error('API key error:', error.message);
|
|
|
|
// Check:
|
|
// 1. .env file exists and loaded
|
|
// 2. Correct env variable name (OPENAI_API_KEY)
|
|
// 3. Key format is valid (starts with sk-)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Validate API keys at application startup
|
|
- Use environment variable validation (e.g., zod)
|
|
- Provide clear error messages in development
|
|
- Document required environment variables
|
|
|
|
---
|
|
|
|
### 6. AI_InvalidArgumentError
|
|
|
|
**Cause:** Invalid parameters passed to function.
|
|
|
|
**Solution:**
|
|
```typescript
|
|
import { AI_InvalidArgumentError } from 'ai';
|
|
|
|
try {
|
|
const result = await generateText({
|
|
model: openai('gpt-4-turbo'),
|
|
maxOutputTokens: -1, // Invalid!
|
|
prompt: 'Hello',
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof AI_InvalidArgumentError) {
|
|
console.error('Invalid argument:', error.message);
|
|
// Check parameter types and values
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Use TypeScript for type checking
|
|
- Validate inputs before calling AI SDK functions
|
|
- Read function signatures carefully
|
|
- Check official docs for parameter constraints
|
|
|
|
---
|
|
|
|
### 7. AI_NoContentGeneratedError
|
|
|
|
**Cause:** Model generated no content (safety filters, etc.).
|
|
|
|
**Solution:**
|
|
```typescript
|
|
import { AI_NoContentGeneratedError } from 'ai';
|
|
|
|
try {
|
|
const result = await generateText({
|
|
model: openai('gpt-4-turbo'),
|
|
prompt: 'Some prompt',
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof AI_NoContentGeneratedError) {
|
|
console.error('No content generated');
|
|
|
|
// Possible causes:
|
|
// 1. Safety filters blocked output
|
|
// 2. Prompt triggered content policy
|
|
// 3. Model configuration issue
|
|
|
|
// Handle gracefully:
|
|
return { text: 'Unable to generate response. Please try different input.' };
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Sanitize user inputs
|
|
- Avoid prompts that may trigger safety filters
|
|
- Have fallback messaging
|
|
- Log occurrences for analysis
|
|
|
|
---
|
|
|
|
### 8. AI_TypeValidationError
|
|
|
|
**Cause:** Zod schema validation failed on generated output.
|
|
|
|
**Solution:**
|
|
```typescript
|
|
import { AI_TypeValidationError } from 'ai';
|
|
|
|
try {
|
|
const result = await generateObject({
|
|
model: openai('gpt-4-turbo'),
|
|
schema: z.object({
|
|
age: z.number().min(0).max(120), // Strict validation
|
|
}),
|
|
prompt: 'Generate person',
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof AI_TypeValidationError) {
|
|
console.error('Validation failed:', error.message);
|
|
|
|
// Solutions:
|
|
// 1. Relax schema constraints
|
|
// 2. Add more guidance in prompt
|
|
// 3. Use .optional() for unreliable fields
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Start with lenient schemas, tighten gradually
|
|
- Use `.optional()` for fields that may not always be present
|
|
- Add validation hints in field descriptions
|
|
- Test with various prompts
|
|
|
|
---
|
|
|
|
### 9. AI_RetryError
|
|
|
|
**Cause:** All retry attempts failed.
|
|
|
|
**Solution:**
|
|
```typescript
|
|
import { AI_RetryError } from 'ai';
|
|
|
|
try {
|
|
const result = await generateText({
|
|
model: openai('gpt-4-turbo'),
|
|
prompt: 'Hello',
|
|
maxRetries: 3, // Default is 2
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof AI_RetryError) {
|
|
console.error('All retries failed');
|
|
console.error('Last error:', error.lastError);
|
|
|
|
// Check root cause:
|
|
// - Persistent network issue
|
|
// - Provider outage
|
|
// - Invalid configuration
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Investigate root cause of failures
|
|
- Adjust retry configuration if needed
|
|
- Implement circuit breaker pattern for provider outages
|
|
- Have fallback providers
|
|
|
|
---
|
|
|
|
### 10. Rate Limiting Errors
|
|
|
|
**Cause:** Exceeded provider rate limits (RPM/TPM).
|
|
|
|
**Solution:**
|
|
```typescript
|
|
// Implement exponential backoff
|
|
async function generateWithBackoff(prompt: string, retries = 3) {
|
|
for (let i = 0; i < retries; i++) {
|
|
try {
|
|
return await generateText({
|
|
model: openai('gpt-4-turbo'),
|
|
prompt,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof AI_APICallError && error.statusCode === 429) {
|
|
const delay = Math.pow(2, i) * 1000; // Exponential backoff
|
|
console.log(`Rate limited, waiting ${delay}ms`);
|
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
throw new Error('Rate limit retries exhausted');
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Monitor rate limit headers
|
|
- Queue requests to stay under limits
|
|
- Upgrade provider tier if needed
|
|
- Implement request throttling
|
|
|
|
---
|
|
|
|
### 11. TypeScript Performance with Zod
|
|
|
|
**Cause:** Complex Zod schemas slow down TypeScript type checking.
|
|
|
|
**Solution:**
|
|
```typescript
|
|
// Instead of deeply nested schemas at top level:
|
|
// const complexSchema = z.object({ /* 100+ fields */ });
|
|
|
|
// Define inside functions or use type assertions:
|
|
function generateData() {
|
|
const schema = z.object({ /* complex schema */ });
|
|
return generateObject({ model: openai('gpt-4-turbo'), schema, prompt: '...' });
|
|
}
|
|
|
|
// Or use z.lazy() for recursive schemas:
|
|
type Category = { name: string; subcategories?: Category[] };
|
|
const CategorySchema: z.ZodType<Category> = z.lazy(() =>
|
|
z.object({
|
|
name: z.string(),
|
|
subcategories: z.array(CategorySchema).optional(),
|
|
})
|
|
);
|
|
```
|
|
|
|
**Prevention:**
|
|
- Avoid top-level complex schemas
|
|
- Use `z.lazy()` for recursive types
|
|
- Split large schemas into smaller ones
|
|
- Use type assertions where appropriate
|
|
|
|
**Official Docs:**
|
|
https://ai-sdk.dev/docs/troubleshooting/common-issues/slow-type-checking
|
|
|
|
---
|
|
|
|
### 12. Invalid JSON Response (Provider-Specific)
|
|
|
|
**Cause:** Some models occasionally return invalid JSON.
|
|
|
|
**Solution:**
|
|
```typescript
|
|
// Use built-in retry and mode selection
|
|
const result = await generateObject({
|
|
model: openai('gpt-4-turbo'),
|
|
schema: mySchema,
|
|
prompt: 'Generate data',
|
|
mode: 'json', // Force JSON mode (supported by GPT-4)
|
|
maxRetries: 3, // Retry on invalid JSON
|
|
});
|
|
|
|
// Or catch and retry manually:
|
|
try {
|
|
const result = await generateObject({
|
|
model: openai('gpt-4-turbo'),
|
|
schema: mySchema,
|
|
prompt: 'Generate data',
|
|
});
|
|
} catch (error) {
|
|
// Retry with different model
|
|
const result = await generateObject({
|
|
model: openai('gpt-4-turbo'),
|
|
schema: mySchema,
|
|
prompt: 'Generate data',
|
|
});
|
|
}
|
|
```
|
|
|
|
**Prevention:**
|
|
- Use `mode: 'json'` when available
|
|
- Prefer GPT-4 for structured output
|
|
- Implement retry logic
|
|
- Validate responses
|
|
|
|
**GitHub Issue:** #4302 (Imagen 3.0 Invalid JSON)
|
|
|
|
---
|
|
|
|
### 13. Gemini Implicit Caching Fails with Tools
|
|
|
|
**Error**: No error, but higher API costs due to disabled caching
|
|
**Cause**: Google Gemini 3 Flash's cost-saving implicit caching doesn't work when any tools are defined, even if never used.
|
|
**Source**: [GitHub Issue #11513](https://github.com/vercel/ai/issues/11513)
|
|
|
|
**Why It Happens**: Gemini API disables caching when tools are present in the request, regardless of whether they're invoked.
|
|
|
|
**Prevention**:
|
|
```typescript
|
|
// Conditionally add tools only when needed
|
|
const needsTools = await analyzePrompt(userInput);
|
|
|
|
const result = await generateText({
|
|
model: google('gemini-3-flash'),
|
|
tools: needsTools ? { weather: weatherTool } : undefined,
|
|
prompt: userInput,
|
|
});
|
|
```
|
|
|
|
**Impact**: High - Can significantly increase API costs for repeated context
|
|
|
|
---
|
|
|
|
### 14. Anthropic Tool Error Results Cause JSON Parse Crash
|
|
|
|
**Error**: `SyntaxError: "[object Object]" is not valid JSON`
|
|
**Cause**: Anthropic provider built-in tools (web_fetch, etc.) return error objects that SDK tries to JSON.parse
|
|
**Source**: [GitHub Issue #11856](https://github.com/vercel/ai/issues/11856)
|
|
|
|
**Why It Happens**: When Anthropic built-in tools fail (e.g., url_not_allowed), they return error objects. AI SDK incorrectly tries to parse these as JSON strings.
|
|
|
|
**Prevention**:
|
|
```typescript
|
|
try {
|
|
const result = await generateText({
|
|
model: anthropic('claude-sonnet-4-5-20250929'),
|
|
tools: { web_fetch: { type: 'anthropic_defined', name: 'web_fetch' } },
|
|
prompt: userPrompt,
|
|
});
|
|
} catch (error) {
|
|
if (error.message.includes('is not valid JSON')) {
|
|
// Tool returned error result, handle gracefully
|
|
console.error('Tool execution failed - likely blocked URL or permission issue');
|
|
// Retry without tool or use custom tool
|
|
}
|
|
throw error;
|
|
}
|
|
```
|
|
|
|
**Impact**: High - Production crashes when using Anthropic built-in tools
|
|
|
|
---
|
|
|
|
### 15. Tool-Result in Assistant Message (Anthropic)
|
|
|
|
**Error**: Anthropic API error - `tool-result` in assistant message not allowed
|
|
**Cause**: Server-executed tools incorrectly place `tool-result` parts in assistant messages
|
|
**Source**: [GitHub Issue #11855](https://github.com/vercel/ai/issues/11855)
|
|
|
|
**Why It Happens**: When using server-executed tools (tools where `execute` runs on server, not sent to model), the AI SDK incorrectly includes `tool-result` parts in the assistant message. Anthropic expects tool-result only in user messages.
|
|
|
|
**Prevention**:
|
|
```typescript
|
|
// Workaround: Filter messages before sending
|
|
const filteredMessages = messages.map(msg => {
|
|
if (msg.role === 'assistant') {
|
|
return {
|
|
...msg,
|
|
content: msg.content.filter(part => part.type !== 'tool-result'),
|
|
};
|
|
}
|
|
return msg;
|
|
});
|
|
|
|
const result = await generateText({
|
|
model: anthropic('claude-sonnet-4-5-20250929'),
|
|
tools: { database: databaseTool },
|
|
messages: filteredMessages,
|
|
prompt: 'Get user data',
|
|
});
|
|
```
|
|
|
|
**Impact**: High - Breaks server-executed tool pattern with Anthropic provider
|
|
|
|
**Status**: Known issue, PR #11854 submitted
|
|
|
|
---
|
|
|
|
**More Errors:** https://ai-sdk.dev/docs/reference/ai-sdk-errors (31 total)
|
|
|
|
---
|
|
|
|
## Known Issues & Limitations
|
|
|
|
### useChat Stale Closures with Memoized Options
|
|
|
|
**Issue**: When using `useChat` with memoized options (common for performance), the `onData` and `onFinish` callbacks have stale closures and don't see updated state variables.
|
|
|
|
**Source**: [GitHub Issue #11686](https://github.com/vercel/ai/issues/11686)
|
|
|
|
**Reproduction**:
|
|
```typescript
|
|
const [count, setCount] = useState(0);
|
|
|
|
const chatOptions = useMemo(() => ({
|
|
onFinish: (message) => {
|
|
console.log('Count:', count); // ALWAYS 0, never updates!
|
|
},
|
|
}), []); // Empty deps = stale closure
|
|
|
|
const { messages, append } = useChat(chatOptions);
|
|
```
|
|
|
|
**Workaround 1 - Don't Memoize Callbacks**:
|
|
```typescript
|
|
const { messages, append } = useChat({
|
|
onFinish: (message) => {
|
|
console.log('Count:', count); // Now sees current count
|
|
},
|
|
});
|
|
```
|
|
|
|
**Workaround 2 - Use useRef**:
|
|
```typescript
|
|
const countRef = useRef(count);
|
|
useEffect(() => { countRef.current = count; }, [count]);
|
|
|
|
const chatOptions = useMemo(() => ({
|
|
onFinish: (message) => {
|
|
console.log('Count:', countRef.current); // Always current
|
|
},
|
|
}), []);
|
|
```
|
|
|
|
**Full Repro**: https://github.com/alechoey/ai-sdk-stale-ondata-repro
|
|
|
|
---
|
|
|
|
### Stream Resumption Fails on Tab Switch
|
|
|
|
**Issue**: When users switch browser tabs or background the app during an AI stream, the stream does not resume when they return. The connection is lost and does not automatically reconnect.
|
|
|
|
**Source**: [GitHub Issue #11865](https://github.com/vercel/ai/issues/11865)
|
|
|
|
**Impact**: High - Major UX issue for long-running streams
|
|
|
|
**Workaround 1 - Implement onError Handler**:
|
|
```typescript
|
|
const { messages, append, reload } = useChat({
|
|
api: '/api/chat',
|
|
onError: (error) => {
|
|
if (error.message.includes('stream') || error.message.includes('aborted')) {
|
|
// Attempt to reload last message
|
|
reload();
|
|
}
|
|
},
|
|
});
|
|
```
|
|
|
|
**Workaround 2 - Detect Visibility Change**:
|
|
```typescript
|
|
useEffect(() => {
|
|
const handleVisibilityChange = () => {
|
|
if (document.visibilityState === 'visible') {
|
|
// Check if stream was interrupted
|
|
const lastMessage = messages[messages.length - 1];
|
|
if (lastMessage?.role === 'assistant' && !lastMessage.content) {
|
|
reload();
|
|
}
|
|
}
|
|
};
|
|
|
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
}, [messages, reload]);
|
|
```
|
|
|
|
**Status**: Known limitation, no auto-reconnection built-in
|
|
|
|
---
|
|
|
|
## When to Use This Skill
|
|
|
|
### Use ai-sdk-core when:
|
|
|
|
- Building backend AI features (server-side text generation)
|
|
- Implementing server-side text generation (Node.js, Workers, Next.js)
|
|
- Creating structured AI outputs (JSON, forms, data extraction)
|
|
- Building AI agents with tools (multi-step workflows)
|
|
- Integrating multiple AI providers (OpenAI, Anthropic, Google, Cloudflare)
|
|
- Migrating from AI SDK v4 to v5
|
|
- Encountering AI SDK errors (AI_APICallError, AI_NoObjectGeneratedError, etc.)
|
|
- Using AI in Cloudflare Workers (with workers-ai-provider)
|
|
- Using AI in Next.js Server Components/Actions
|
|
- Need consistent API across different LLM providers
|
|
|
|
### Don't use this skill when:
|
|
|
|
- Building React chat UIs (use **ai-sdk-ui** skill instead)
|
|
- Need frontend hooks like useChat (use **ai-sdk-ui** skill instead)
|
|
- Need advanced topics like embeddings or image generation (check official docs)
|
|
- Building native Cloudflare Workers AI apps without multi-provider (use **cloudflare-workers-ai** skill instead)
|
|
- Need Generative UI / RSC (see https://ai-sdk.dev/docs/ai-sdk-rsc)
|
|
|
|
---
|
|
|
|
## Versions
|
|
|
|
**AI SDK:**
|
|
- Stable: ai@6.0.26 (Jan 2026)
|
|
- ⚠️ **Skip v6.0.40** - Breaking streaming change (reverted in v6.0.41)
|
|
- Legacy v5: ai@5.0.117 (ai-v5 tag)
|
|
- Zod 3.x/4.x both supported
|
|
|
|
**Latest Models (2026):**
|
|
- OpenAI: GPT-5.2, GPT-5.1, GPT-5, o3, o3-mini, o4-mini
|
|
- Anthropic: Claude Sonnet 4.5, Opus 4.1, Haiku 4.5
|
|
- Google: Gemini 2.5 Pro/Flash/Lite
|
|
|
|
**Check Latest:**
|
|
```bash
|
|
npm view ai version
|
|
npm view ai dist-tags
|
|
```
|
|
|
|
---
|
|
|
|
## Official Docs
|
|
|
|
**Core:**
|
|
- AI SDK v6: https://ai-sdk.dev/docs
|
|
- AI SDK Core: https://ai-sdk.dev/docs/ai-sdk-core/overview
|
|
- Output API: https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data
|
|
- v4→v5 Migration: https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0
|
|
- All Errors (31): https://ai-sdk.dev/docs/reference/ai-sdk-errors
|
|
- Providers (69+): https://ai-sdk.dev/providers/overview
|
|
|
|
**Multi-Modal:**
|
|
- Speech: https://ai-sdk.dev/docs/ai-sdk-core/speech
|
|
- Transcription: https://ai-sdk.dev/docs/ai-sdk-core/transcription
|
|
- Image Generation: https://ai-sdk.dev/docs/ai-sdk-core/image-generation
|
|
- Embeddings: https://ai-sdk.dev/docs/ai-sdk-core/embeddings
|
|
|
|
**GitHub:**
|
|
- Repository: https://github.com/vercel/ai
|
|
- Issues: https://github.com/vercel/ai/issues
|
|
|
|
---
|
|
|
|
**Last Updated:** 2026-01-20
|
|
**Skill Version:** 2.1.0
|
|
**Changes:** Added 3 new errors (Gemini caching, Anthropic tool errors, tool-result placement), MCP security guidance, tool approval best practices, React hooks edge cases, stream resumption workarounds
|
|
**AI SDK:** 6.0.26 stable (avoid v6.0.40)
|