Initial commit
This commit is contained in:
209
references/links-to-official-docs.md
Normal file
209
references/links-to-official-docs.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# AI SDK Core - Official Documentation Links
|
||||
|
||||
Organized links to official AI SDK and provider documentation.
|
||||
|
||||
---
|
||||
|
||||
## AI SDK Core Documentation
|
||||
|
||||
### Getting Started
|
||||
|
||||
- **Introduction:** https://ai-sdk.dev/docs/introduction
|
||||
- **AI SDK Core Overview:** https://ai-sdk.dev/docs/ai-sdk-core/overview
|
||||
- **Foundations:** https://ai-sdk.dev/docs/foundations/overview
|
||||
|
||||
### Core Functions
|
||||
|
||||
- **Generating Text:** https://ai-sdk.dev/docs/ai-sdk-core/generating-text
|
||||
- **Streaming Text:** https://ai-sdk.dev/docs/ai-sdk-core/streaming-text
|
||||
- **Generating Structured Data:** https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data
|
||||
- **Streaming Structured Data:** https://ai-sdk.dev/docs/ai-sdk-core/streaming-structured-data
|
||||
|
||||
### Tool Calling & Agents
|
||||
|
||||
- **Tools and Tool Calling:** https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling
|
||||
- **Agents Overview:** https://ai-sdk.dev/docs/agents/overview
|
||||
- **Building Agents:** https://ai-sdk.dev/docs/agents/building-agents
|
||||
|
||||
### Advanced Topics (Not Replicated in This Skill)
|
||||
|
||||
- **Embeddings:** https://ai-sdk.dev/docs/ai-sdk-core/embeddings
|
||||
- **Image Generation:** https://ai-sdk.dev/docs/ai-sdk-core/generating-images
|
||||
- **Transcription (Audio to Text):** https://ai-sdk.dev/docs/ai-sdk-core/generating-transcriptions
|
||||
- **Speech (Text to Audio):** https://ai-sdk.dev/docs/ai-sdk-core/generating-speech
|
||||
- **MCP Tools:** https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools
|
||||
- **Telemetry:** https://ai-sdk.dev/docs/ai-sdk-core/telemetry
|
||||
- **Generative UI (RSC):** https://ai-sdk.dev/docs/ai-sdk-rsc
|
||||
|
||||
---
|
||||
|
||||
## Migration & Troubleshooting
|
||||
|
||||
- **v4 → v5 Migration Guide:** https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0
|
||||
- **All Error Types (28 total):** https://ai-sdk.dev/docs/reference/ai-sdk-errors
|
||||
- **Troubleshooting Guide:** https://ai-sdk.dev/docs/troubleshooting
|
||||
- **Common Issues:** https://ai-sdk.dev/docs/troubleshooting/common-issues
|
||||
- **Slow Type Checking:** https://ai-sdk.dev/docs/troubleshooting/common-issues/slow-type-checking
|
||||
|
||||
---
|
||||
|
||||
## Provider Documentation
|
||||
|
||||
### Provider Overview
|
||||
|
||||
- **All Providers:** https://ai-sdk.dev/providers/overview
|
||||
- **Provider Selection Guide:** https://ai-sdk.dev/providers/overview#provider-selection
|
||||
|
||||
### OpenAI
|
||||
|
||||
- **OpenAI Provider Docs:** https://ai-sdk.dev/providers/ai-sdk-providers/openai
|
||||
- **OpenAI Platform:** https://platform.openai.com/
|
||||
- **API Keys:** https://platform.openai.com/api-keys
|
||||
- **Rate Limits:** https://platform.openai.com/account/rate-limits
|
||||
- **Pricing:** https://openai.com/api/pricing/
|
||||
|
||||
### Anthropic
|
||||
|
||||
- **Anthropic Provider Docs:** https://ai-sdk.dev/providers/ai-sdk-providers/anthropic
|
||||
- **Anthropic Console:** https://console.anthropic.com/
|
||||
- **Claude Models:** https://docs.anthropic.com/en/docs/models-overview
|
||||
- **Rate Limits:** https://docs.anthropic.com/en/api/rate-limits
|
||||
- **Pricing:** https://www.anthropic.com/pricing
|
||||
|
||||
### Google
|
||||
|
||||
- **Google Provider Docs:** https://ai-sdk.dev/providers/ai-sdk-providers/google
|
||||
- **Google AI Studio:** https://aistudio.google.com/
|
||||
- **API Keys:** https://aistudio.google.com/app/apikey
|
||||
- **Gemini Models:** https://ai.google.dev/models/gemini
|
||||
- **Pricing:** https://ai.google.dev/pricing
|
||||
|
||||
### Cloudflare Workers AI
|
||||
|
||||
- **Workers AI Provider (Community):** https://ai-sdk.dev/providers/community-providers/cloudflare-workers-ai
|
||||
- **Cloudflare Workers AI Docs:** https://developers.cloudflare.com/workers-ai/
|
||||
- **AI SDK Configuration:** https://developers.cloudflare.com/workers-ai/configuration/ai-sdk/
|
||||
- **Available Models:** https://developers.cloudflare.com/workers-ai/models/
|
||||
- **GitHub (workers-ai-provider):** https://github.com/cloudflare/ai/tree/main/packages/workers-ai-provider
|
||||
- **Pricing:** https://developers.cloudflare.com/workers-ai/platform/pricing/
|
||||
|
||||
### Community Providers
|
||||
|
||||
- **Community Providers List:** https://ai-sdk.dev/providers/community-providers
|
||||
- **Ollama:** https://ai-sdk.dev/providers/community-providers/ollama
|
||||
- **FriendliAI:** https://ai-sdk.dev/providers/community-providers/friendliai
|
||||
- **LM Studio:** https://ai-sdk.dev/providers/community-providers/lmstudio
|
||||
|
||||
---
|
||||
|
||||
## Framework Integration
|
||||
|
||||
### Next.js
|
||||
|
||||
- **Next.js App Router Integration:** https://ai-sdk.dev/docs/getting-started/nextjs-app-router
|
||||
- **Next.js Pages Router Integration:** https://ai-sdk.dev/docs/getting-started/nextjs-pages-router
|
||||
- **Next.js Documentation:** https://nextjs.org/docs
|
||||
|
||||
### Node.js
|
||||
|
||||
- **Node.js Integration:** https://ai-sdk.dev/docs/getting-started/nodejs
|
||||
|
||||
### Vercel Deployment
|
||||
|
||||
- **Vercel Functions:** https://vercel.com/docs/functions
|
||||
- **Vercel Streaming:** https://vercel.com/docs/functions/streaming
|
||||
- **Vercel Environment Variables:** https://vercel.com/docs/projects/environment-variables
|
||||
|
||||
### Cloudflare Workers
|
||||
|
||||
- **Cloudflare Workers Docs:** https://developers.cloudflare.com/workers/
|
||||
- **Wrangler CLI:** https://developers.cloudflare.com/workers/wrangler/
|
||||
- **Workers Configuration:** https://developers.cloudflare.com/workers/wrangler/configuration/
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
- **generateText:** https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text
|
||||
- **streamText:** https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text
|
||||
- **generateObject:** https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-object
|
||||
- **streamObject:** https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-object
|
||||
- **Tool:** https://ai-sdk.dev/docs/reference/ai-sdk-core/tool
|
||||
- **Agent:** https://ai-sdk.dev/docs/reference/ai-sdk-core/agent
|
||||
|
||||
---
|
||||
|
||||
## GitHub & Community
|
||||
|
||||
- **GitHub Repository:** https://github.com/vercel/ai
|
||||
- **GitHub Issues:** https://github.com/vercel/ai/issues
|
||||
- **GitHub Discussions:** https://github.com/vercel/ai/discussions
|
||||
- **Discord Community:** https://discord.gg/vercel
|
||||
|
||||
---
|
||||
|
||||
## Blog Posts & Announcements
|
||||
|
||||
- **AI SDK 5.0 Release:** https://vercel.com/blog/ai-sdk-5
|
||||
- **Vercel AI Blog:** https://vercel.com/blog/category/ai
|
||||
- **Engineering Blog (Agents):** https://www.anthropic.com/engineering
|
||||
|
||||
---
|
||||
|
||||
## TypeScript & Zod
|
||||
|
||||
- **Zod Documentation:** https://zod.dev/
|
||||
- **TypeScript Handbook:** https://www.typescriptlang.org/docs/
|
||||
|
||||
---
|
||||
|
||||
## Complementary Skills
|
||||
|
||||
For complete AI SDK coverage, also see:
|
||||
|
||||
- **ai-sdk-ui skill:** Frontend React hooks (useChat, useCompletion, useObject)
|
||||
- **cloudflare-workers-ai skill:** Native Cloudflare Workers AI binding (no multi-provider)
|
||||
|
||||
---
|
||||
|
||||
## Quick Navigation
|
||||
|
||||
### I want to...
|
||||
|
||||
**Generate text:**
|
||||
- Docs: https://ai-sdk.dev/docs/ai-sdk-core/generating-text
|
||||
- Template: `templates/generate-text-basic.ts`
|
||||
|
||||
**Stream text:**
|
||||
- Docs: https://ai-sdk.dev/docs/ai-sdk-core/streaming-text
|
||||
- Template: `templates/stream-text-chat.ts`
|
||||
|
||||
**Generate structured output:**
|
||||
- Docs: https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data
|
||||
- Template: `templates/generate-object-zod.ts`
|
||||
|
||||
**Use tools:**
|
||||
- Docs: https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling
|
||||
- Template: `templates/tools-basic.ts`
|
||||
|
||||
**Build an agent:**
|
||||
- Docs: https://ai-sdk.dev/docs/agents/overview
|
||||
- Template: `templates/agent-with-tools.ts`
|
||||
|
||||
**Migrate from v4:**
|
||||
- Docs: https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0
|
||||
- Reference: `references/v5-breaking-changes.md`
|
||||
|
||||
**Fix an error:**
|
||||
- Docs: https://ai-sdk.dev/docs/reference/ai-sdk-errors
|
||||
- Reference: `references/top-errors.md`
|
||||
|
||||
**Set up a provider:**
|
||||
- Reference: `references/providers-quickstart.md`
|
||||
|
||||
**Deploy to production:**
|
||||
- Reference: `references/production-patterns.md`
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-10-21
|
||||
621
references/production-patterns.md
Normal file
621
references/production-patterns.md
Normal file
@@ -0,0 +1,621 @@
|
||||
# AI SDK Core - Production Patterns
|
||||
|
||||
Best practices for deploying AI SDK Core in production environments.
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### 1. Streaming for Long-Form Content
|
||||
|
||||
**Always use streaming for user-facing long-form content:**
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: User-facing (better perceived performance)
|
||||
app.post('/chat', async (req, res) => {
|
||||
const stream = streamText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: req.body.message,
|
||||
});
|
||||
|
||||
return stream.toDataStreamResponse();
|
||||
});
|
||||
|
||||
// ❌ BAD: User waits for entire response
|
||||
app.post('/chat', async (req, res) => {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: req.body.message,
|
||||
});
|
||||
|
||||
return res.json({ response: result.text });
|
||||
});
|
||||
|
||||
// ✅ GOOD: Background tasks (no user waiting)
|
||||
async function processDocument(doc: string) {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: `Analyze: ${doc}`,
|
||||
});
|
||||
|
||||
await saveToDatabase(result.text);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Set Appropriate maxOutputTokens
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Limit token usage based on use case
|
||||
const shortSummary = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Summarize in 2 sentences',
|
||||
maxOutputTokens: 100, // Prevents over-generation
|
||||
});
|
||||
|
||||
const article = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Write article',
|
||||
maxOutputTokens: 2000, // Appropriate for long-form
|
||||
});
|
||||
|
||||
// ❌ BAD: No limit (can waste tokens/money)
|
||||
const unlimited = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Write something',
|
||||
// No maxOutputTokens
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Cache Provider Instances
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Reuse provider instances
|
||||
const gpt4 = openai('gpt-4-turbo');
|
||||
const claude = anthropic('claude-3-5-sonnet-20241022');
|
||||
|
||||
app.post('/chat', async (req, res) => {
|
||||
const result = await generateText({
|
||||
model: gpt4, // Reuse
|
||||
prompt: req.body.message,
|
||||
});
|
||||
return res.json({ response: result.text });
|
||||
});
|
||||
|
||||
// ❌ BAD: Create new instance every time
|
||||
app.post('/chat', async (req, res) => {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4-turbo'), // New instance each call
|
||||
prompt: req.body.message,
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Optimize Zod Schemas (Especially in Workers)
|
||||
|
||||
```typescript
|
||||
// ❌ BAD: Complex schema at top level (slow startup)
|
||||
const ComplexSchema = z.object({
|
||||
// 50+ fields with deep nesting
|
||||
});
|
||||
|
||||
// ✅ GOOD: Define schemas inside functions
|
||||
function generateStructuredData() {
|
||||
const schema = z.object({
|
||||
// Schema definition here
|
||||
});
|
||||
|
||||
return generateObject({ model: openai('gpt-4'), schema, prompt: '...' });
|
||||
}
|
||||
|
||||
// ✅ GOOD: Split into smaller reusable schemas
|
||||
const AddressSchema = z.object({ street: z.string(), city: z.string() });
|
||||
const PersonSchema = z.object({ name: z.string(), address: AddressSchema });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### 1. Wrap All AI Calls in Try-Catch
|
||||
|
||||
```typescript
|
||||
async function generateSafely(prompt: string) {
|
||||
try {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt,
|
||||
});
|
||||
|
||||
return { success: true, data: result.text };
|
||||
} catch (error) {
|
||||
if (error instanceof AI_APICallError) {
|
||||
console.error('API call failed:', error.statusCode, error.message);
|
||||
return { success: false, error: 'AI service temporarily unavailable' };
|
||||
} else if (error instanceof AI_NoContentGeneratedError) {
|
||||
console.error('No content generated');
|
||||
return { success: false, error: 'Unable to generate response' };
|
||||
} else {
|
||||
console.error('Unknown error:', error);
|
||||
return { success: false, error: 'An error occurred' };
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Handle Specific Error Types
|
||||
|
||||
```typescript
|
||||
import {
|
||||
AI_APICallError,
|
||||
AI_NoObjectGeneratedError,
|
||||
AI_TypeValidationError,
|
||||
AI_RetryError,
|
||||
} from 'ai';
|
||||
|
||||
async function robustGeneration(prompt: string) {
|
||||
try {
|
||||
return await generateText({ model: openai('gpt-4'), prompt });
|
||||
} catch (error) {
|
||||
switch (error.constructor) {
|
||||
case AI_APICallError:
|
||||
if (error.statusCode === 429) {
|
||||
// Rate limit - wait and retry
|
||||
await wait(5000);
|
||||
return retry();
|
||||
} else if (error.statusCode >= 500) {
|
||||
// Provider issue - try fallback
|
||||
return generateText({ model: anthropic('claude-3-5-sonnet-20241022'), prompt });
|
||||
}
|
||||
break;
|
||||
|
||||
case AI_RetryError:
|
||||
// All retries failed - use fallback provider
|
||||
return generateText({ model: google('gemini-2.5-pro'), prompt });
|
||||
|
||||
case AI_NoContentGeneratedError:
|
||||
// Content filtered - return safe message
|
||||
return { text: 'Unable to generate response for this input.' };
|
||||
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Implement Retry Logic
|
||||
|
||||
```typescript
|
||||
async function generateWithRetry(prompt: string, maxRetries = 3) {
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
return await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt,
|
||||
maxRetries: 2, // Built-in retry
|
||||
});
|
||||
} catch (error) {
|
||||
if (i === maxRetries - 1) throw error; // Last attempt failed
|
||||
|
||||
// Exponential backoff
|
||||
const delay = Math.pow(2, i) * 1000;
|
||||
console.log(`Retry ${i + 1}/${maxRetries} after ${delay}ms`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Log Errors Properly
|
||||
|
||||
```typescript
|
||||
function logAIError(error: any, context: Record<string, any>) {
|
||||
const errorLog = {
|
||||
timestamp: new Date().toISOString(),
|
||||
type: error.constructor.name,
|
||||
message: error.message,
|
||||
statusCode: error.statusCode,
|
||||
responseBody: error.responseBody,
|
||||
context,
|
||||
stack: error.stack,
|
||||
};
|
||||
|
||||
// Send to monitoring service (e.g., Sentry, Datadog)
|
||||
console.error('AI SDK Error:', JSON.stringify(errorLog));
|
||||
|
||||
// Track metrics
|
||||
metrics.increment('ai.error', {
|
||||
type: error.constructor.name,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await generateText({ model: openai('gpt-4'), prompt });
|
||||
} catch (error) {
|
||||
logAIError(error, { prompt, model: 'gpt-4' });
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cost Optimization
|
||||
|
||||
### 1. Choose Appropriate Models
|
||||
|
||||
```typescript
|
||||
// Model selection based on task complexity
|
||||
async function generateWithCostOptimization(prompt: string, complexity: 'simple' | 'medium' | 'complex') {
|
||||
const models = {
|
||||
simple: openai('gpt-3.5-turbo'), // $0.50 / 1M tokens
|
||||
medium: openai('gpt-4-turbo'), // $10 / 1M tokens
|
||||
complex: openai('gpt-4'), // $30 / 1M tokens
|
||||
};
|
||||
|
||||
return generateText({
|
||||
model: models[complexity],
|
||||
prompt,
|
||||
});
|
||||
}
|
||||
|
||||
// Usage
|
||||
await generateWithCostOptimization('Translate to Spanish', 'simple');
|
||||
await generateWithCostOptimization('Analyze sentiment', 'medium');
|
||||
await generateWithCostOptimization('Complex reasoning task', 'complex');
|
||||
```
|
||||
|
||||
### 2. Set Token Limits
|
||||
|
||||
```typescript
|
||||
// Prevent runaway costs
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Write essay',
|
||||
maxOutputTokens: 500, // Hard limit
|
||||
});
|
||||
|
||||
// Adjust limits per use case
|
||||
const limits = {
|
||||
chatMessage: 200,
|
||||
summary: 300,
|
||||
article: 2000,
|
||||
analysis: 1000,
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Cache Results
|
||||
|
||||
```typescript
|
||||
import { LRUCache } from 'lru-cache';
|
||||
|
||||
const cache = new LRUCache<string, string>({
|
||||
max: 1000, // Max 1000 items
|
||||
ttl: 1000 * 60 * 60, // 1 hour TTL
|
||||
});
|
||||
|
||||
async function generateWithCache(prompt: string) {
|
||||
const cacheKey = `ai:${hash(prompt)}`;
|
||||
|
||||
// Check cache
|
||||
const cached = cache.get(cacheKey);
|
||||
if (cached) {
|
||||
console.log('Cache hit');
|
||||
return { text: cached, cached: true };
|
||||
}
|
||||
|
||||
// Generate
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt,
|
||||
});
|
||||
|
||||
// Store in cache
|
||||
cache.set(cacheKey, result.text);
|
||||
|
||||
return { text: result.text, cached: false };
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Monitor Usage
|
||||
|
||||
```typescript
|
||||
// Track token usage
|
||||
let totalTokensUsed = 0;
|
||||
let totalCost = 0;
|
||||
|
||||
async function generateWithTracking(prompt: string) {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt,
|
||||
});
|
||||
|
||||
// Track tokens
|
||||
totalTokensUsed += result.usage.totalTokens;
|
||||
|
||||
// Estimate cost (GPT-4: $30/1M tokens)
|
||||
const cost = (result.usage.totalTokens / 1_000_000) * 30;
|
||||
totalCost += cost;
|
||||
|
||||
console.log(`Tokens: ${result.usage.totalTokens}, Cost: $${cost.toFixed(4)}`);
|
||||
console.log(`Total tokens: ${totalTokensUsed}, Total cost: $${totalCost.toFixed(2)}`);
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cloudflare Workers Best Practices
|
||||
|
||||
### 1. Lazy Initialization
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Import inside handler
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const { generateText } = await import('ai');
|
||||
const { createWorkersAI } = await import('workers-ai-provider');
|
||||
|
||||
const workersai = createWorkersAI({ binding: env.AI });
|
||||
|
||||
const result = await generateText({
|
||||
model: workersai('@cf/meta/llama-3.1-8b-instruct'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
|
||||
return new Response(result.text);
|
||||
}
|
||||
};
|
||||
|
||||
// ❌ BAD: Top-level imports (startup overhead)
|
||||
import { generateText } from 'ai';
|
||||
const workersai = createWorkersAI({ binding: env.AI }); // Runs at startup!
|
||||
```
|
||||
|
||||
### 2. Monitor Startup Time
|
||||
|
||||
```bash
|
||||
# Wrangler reports startup time
|
||||
npx wrangler deploy
|
||||
|
||||
# Output shows:
|
||||
# Startup Time: 287ms (must be <400ms)
|
||||
```
|
||||
|
||||
### 3. Handle Streaming Properly
|
||||
|
||||
```typescript
|
||||
app.post('/chat/stream', async (c) => {
|
||||
const workersai = createWorkersAI({ binding: c.env.AI });
|
||||
|
||||
const stream = streamText({
|
||||
model: workersai('@cf/meta/llama-3.1-8b-instruct'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
|
||||
// Return ReadableStream for Workers
|
||||
return new Response(stream.toTextStream(), {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
},
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next.js / Vercel Best Practices
|
||||
|
||||
### 1. Server Actions for Mutations
|
||||
|
||||
```typescript
|
||||
// app/actions.ts
|
||||
'use server';
|
||||
|
||||
export async function generateContent(input: string) {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: input,
|
||||
maxOutputTokens: 500,
|
||||
});
|
||||
|
||||
return result.text;
|
||||
}
|
||||
|
||||
// app/page.tsx (Client Component)
|
||||
'use client';
|
||||
|
||||
import { generateContent } from './actions';
|
||||
|
||||
export default function Page() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
async function handleSubmit(formData: FormData) {
|
||||
setLoading(true);
|
||||
const result = await generateContent(formData.get('input') as string);
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
return <form action={handleSubmit}>...</form>;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Server Components for Initial Loads
|
||||
|
||||
```typescript
|
||||
// app/page.tsx (Server Component)
|
||||
export default async function Page() {
|
||||
// Generate on server
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Welcome message',
|
||||
});
|
||||
|
||||
// No loading state needed
|
||||
return <div>{result.text}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. API Routes for Streaming
|
||||
|
||||
```typescript
|
||||
// app/api/chat/route.ts
|
||||
import { streamText } from 'ai';
|
||||
import { openai } from '@ai-sdk/openai';
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const { messages } = await request.json();
|
||||
|
||||
const stream = streamText({
|
||||
model: openai('gpt-4-turbo'),
|
||||
messages,
|
||||
});
|
||||
|
||||
return stream.toDataStreamResponse();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoring and Logging
|
||||
|
||||
### 1. Track Key Metrics
|
||||
|
||||
```typescript
|
||||
// Token usage
|
||||
metrics.gauge('ai.tokens.total', result.usage.totalTokens);
|
||||
metrics.gauge('ai.tokens.prompt', result.usage.promptTokens);
|
||||
metrics.gauge('ai.tokens.completion', result.usage.completionTokens);
|
||||
|
||||
// Response time
|
||||
const startTime = Date.now();
|
||||
const result = await generateText({ model: openai('gpt-4'), prompt });
|
||||
metrics.timing('ai.response_time', Date.now() - startTime);
|
||||
|
||||
// Error rate
|
||||
metrics.increment('ai.errors', { type: error.constructor.name });
|
||||
```
|
||||
|
||||
### 2. Structured Logging
|
||||
|
||||
```typescript
|
||||
import winston from 'winston';
|
||||
|
||||
const logger = winston.createLogger({
|
||||
format: winston.format.json(),
|
||||
transports: [new winston.transports.Console()],
|
||||
});
|
||||
|
||||
logger.info('AI generation started', {
|
||||
model: 'gpt-4',
|
||||
promptLength: prompt.length,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
const result = await generateText({ model: openai('gpt-4'), prompt });
|
||||
|
||||
logger.info('AI generation completed', {
|
||||
model: 'gpt-4',
|
||||
tokensUsed: result.usage.totalTokens,
|
||||
responseLength: result.text.length,
|
||||
duration: Date.now() - startTime,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
### 1. Queue Requests
|
||||
|
||||
```typescript
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
// Limit: 50 requests per minute
|
||||
const queue = new PQueue({
|
||||
concurrency: 5,
|
||||
interval: 60000,
|
||||
intervalCap: 50,
|
||||
});
|
||||
|
||||
async function generateQueued(prompt: string) {
|
||||
return queue.add(() =>
|
||||
generateText({ model: openai('gpt-4'), prompt })
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Monitor Rate Limits
|
||||
|
||||
```typescript
|
||||
async function generateWithRateCheck(prompt: string) {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt,
|
||||
});
|
||||
|
||||
// Check rate limit headers (provider-specific)
|
||||
console.log('Remaining requests:', response.headers['x-ratelimit-remaining']);
|
||||
console.log('Resets at:', response.headers['x-ratelimit-reset']);
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### 1. Sanitize User Inputs
|
||||
|
||||
```typescript
|
||||
function sanitizePrompt(userInput: string): string {
|
||||
// Remove potential prompt injections
|
||||
return userInput
|
||||
.replace(/system:/gi, '')
|
||||
.replace(/ignore previous/gi, '')
|
||||
.slice(0, 1000); // Limit length
|
||||
}
|
||||
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: sanitizePrompt(req.body.message),
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Validate API Keys
|
||||
|
||||
```typescript
|
||||
// Startup validation
|
||||
function validateEnv() {
|
||||
const required = ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY'];
|
||||
|
||||
for (const key of required) {
|
||||
if (!process.env[key]) {
|
||||
throw new Error(`Missing: ${key}`);
|
||||
}
|
||||
|
||||
if (!process.env[key].match(/^sk-/)) {
|
||||
throw new Error(`Invalid format: ${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateEnv();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
See Vercel's official deployment documentation:
|
||||
https://vercel.com/docs/functions
|
||||
|
||||
For Cloudflare Workers:
|
||||
https://developers.cloudflare.com/workers/
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-10-21
|
||||
329
references/providers-quickstart.md
Normal file
329
references/providers-quickstart.md
Normal file
@@ -0,0 +1,329 @@
|
||||
# AI SDK Core - Providers Quick Start
|
||||
|
||||
Quick reference for setting up the top 4 AI providers with AI SDK v5.
|
||||
|
||||
---
|
||||
|
||||
## OpenAI
|
||||
|
||||
**Package:** `@ai-sdk/openai`
|
||||
**Version:** 2.0.53+
|
||||
**Maturity:** Excellent
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
npm install @ai-sdk/openai
|
||||
```
|
||||
|
||||
```bash
|
||||
# .env
|
||||
OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { openai } from '@ai-sdk/openai';
|
||||
import { generateText } from 'ai';
|
||||
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4-turbo'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
```
|
||||
|
||||
### Available Models
|
||||
|
||||
| Model | Use Case | Cost | Speed |
|
||||
|-------|----------|------|-------|
|
||||
| gpt-5 | Latest (if available) | High | Medium |
|
||||
| gpt-4-turbo | Complex reasoning | High | Medium |
|
||||
| gpt-4 | High quality | High | Slow |
|
||||
| gpt-3.5-turbo | Simple tasks | Low | Fast |
|
||||
|
||||
### Common Errors
|
||||
|
||||
- **401 Unauthorized**: Invalid API key
|
||||
- **429 Rate Limit**: Exceeded RPM/TPM limits
|
||||
- **500 Server Error**: OpenAI service issue
|
||||
|
||||
### Links
|
||||
|
||||
- Docs: https://ai-sdk.dev/providers/ai-sdk-providers/openai
|
||||
- API Keys: https://platform.openai.com/api-keys
|
||||
- Rate Limits: https://platform.openai.com/account/rate-limits
|
||||
|
||||
---
|
||||
|
||||
## Anthropic
|
||||
|
||||
**Package:** `@ai-sdk/anthropic`
|
||||
**Version:** 2.0.0+
|
||||
**Maturity:** Excellent
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
npm install @ai-sdk/anthropic
|
||||
```
|
||||
|
||||
```bash
|
||||
# .env
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { anthropic } from '@ai-sdk/anthropic';
|
||||
import { generateText } from 'ai';
|
||||
|
||||
const result = await generateText({
|
||||
model: anthropic('claude-3-5-sonnet-20241022'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
```
|
||||
|
||||
### Available Models
|
||||
|
||||
| Model | Use Case | Context | Speed |
|
||||
|-------|----------|---------|-------|
|
||||
| claude-3-5-sonnet-20241022 | Best balance | 200K | Medium |
|
||||
| claude-3-opus-20240229 | Highest intelligence | 200K | Slow |
|
||||
| claude-3-haiku-20240307 | Fast and cheap | 200K | Fast |
|
||||
|
||||
### Common Errors
|
||||
|
||||
- **authentication_error**: Invalid API key
|
||||
- **rate_limit_error**: Rate limit exceeded
|
||||
- **overloaded_error**: Service overloaded, retry
|
||||
|
||||
### Links
|
||||
|
||||
- Docs: https://ai-sdk.dev/providers/ai-sdk-providers/anthropic
|
||||
- API Keys: https://console.anthropic.com/
|
||||
- Model Details: https://docs.anthropic.com/en/docs/models-overview
|
||||
|
||||
---
|
||||
|
||||
## Google
|
||||
|
||||
**Package:** `@ai-sdk/google`
|
||||
**Version:** 2.0.0+
|
||||
**Maturity:** Excellent
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
npm install @ai-sdk/google
|
||||
```
|
||||
|
||||
```bash
|
||||
# .env
|
||||
GOOGLE_GENERATIVE_AI_API_KEY=...
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { google } from '@ai-sdk/google';
|
||||
import { generateText } from 'ai';
|
||||
|
||||
const result = await generateText({
|
||||
model: google('gemini-2.5-pro'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
```
|
||||
|
||||
### Available Models
|
||||
|
||||
| Model | Use Case | Context | Free Tier |
|
||||
|-------|----------|---------|-----------|
|
||||
| gemini-2.5-pro | Complex reasoning | 1M | Generous |
|
||||
| gemini-2.5-flash | Fast & efficient | 1M | Generous |
|
||||
| gemini-2.5-flash-lite | Ultra-fast | 1M | Generous |
|
||||
|
||||
### Common Errors
|
||||
|
||||
- **SAFETY**: Content filtered by safety settings
|
||||
- **QUOTA_EXCEEDED**: Rate limit exceeded
|
||||
- **INVALID_ARGUMENT**: Invalid parameters
|
||||
|
||||
### Links
|
||||
|
||||
- Docs: https://ai-sdk.dev/providers/ai-sdk-providers/google
|
||||
- API Keys: https://aistudio.google.com/app/apikey
|
||||
- Model Details: https://ai.google.dev/models/gemini
|
||||
|
||||
---
|
||||
|
||||
## Cloudflare Workers AI
|
||||
|
||||
**Package:** `workers-ai-provider`
|
||||
**Version:** 2.0.0+
|
||||
**Type:** Community Provider
|
||||
**Maturity:** Good
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
npm install workers-ai-provider
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// wrangler.jsonc
|
||||
{
|
||||
"ai": {
|
||||
"binding": "AI"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { createWorkersAI } from 'workers-ai-provider';
|
||||
import { generateText } from 'ai';
|
||||
|
||||
// In Cloudflare Worker handler
|
||||
const workersai = createWorkersAI({ binding: env.AI });
|
||||
|
||||
const result = await generateText({
|
||||
model: workersai('@cf/meta/llama-3.1-8b-instruct'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
```
|
||||
|
||||
### Available Models
|
||||
|
||||
| Model | Use Case | Notes |
|
||||
|-------|----------|-------|
|
||||
| @cf/meta/llama-3.1-8b-instruct | General purpose | Recommended |
|
||||
| @cf/meta/llama-3.1-70b-instruct | Complex tasks | Slower |
|
||||
| @cf/mistral/mistral-7b-instruct-v0.1 | Alternative | Good quality |
|
||||
|
||||
### Common Issues
|
||||
|
||||
- **Startup Limit**: Move imports inside handlers
|
||||
- **270ms+**: Lazy-load AI SDK to avoid startup overhead
|
||||
|
||||
### Important Notes
|
||||
|
||||
1. **Startup Optimization Required:**
|
||||
```typescript
|
||||
// BAD: Top-level import causes startup overhead
|
||||
const workersai = createWorkersAI({ binding: env.AI });
|
||||
|
||||
// GOOD: Lazy initialization
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const workersai = createWorkersAI({ binding: env.AI });
|
||||
// Use here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **When to Use:**
|
||||
- Multi-provider scenarios (OpenAI + Workers AI)
|
||||
- Using AI SDK UI hooks
|
||||
- Need consistent API across providers
|
||||
|
||||
3. **When to Use Native Binding:**
|
||||
- Cloudflare-only deployment
|
||||
- Maximum performance
|
||||
- See: `cloudflare-workers-ai` skill
|
||||
|
||||
### Links
|
||||
|
||||
- Docs: https://ai-sdk.dev/providers/community-providers/cloudflare-workers-ai
|
||||
- Models: https://developers.cloudflare.com/workers-ai/models/
|
||||
- GitHub: https://github.com/cloudflare/ai/tree/main/packages/workers-ai-provider
|
||||
|
||||
---
|
||||
|
||||
## Provider Comparison
|
||||
|
||||
| Feature | OpenAI | Anthropic | Google | Cloudflare |
|
||||
|---------|--------|-----------|--------|------------|
|
||||
| **Quality** | Excellent | Excellent | Excellent | Good |
|
||||
| **Speed** | Medium | Medium | Fast | Fast |
|
||||
| **Cost** | Medium | Medium | Low | Lowest |
|
||||
| **Context** | 128K | 200K | 1M | 128K |
|
||||
| **Structured Output** | ✅ | ✅ | ✅ | ⚠️ |
|
||||
| **Tool Calling** | ✅ | ✅ | ✅ | ⚠️ |
|
||||
| **Streaming** | ✅ | ✅ | ✅ | ✅ |
|
||||
| **Free Tier** | ❌ | ❌ | ✅ | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## Multi-Provider Setup
|
||||
|
||||
```typescript
|
||||
import { openai } from '@ai-sdk/openai';
|
||||
import { anthropic } from '@ai-sdk/anthropic';
|
||||
import { google } from '@ai-sdk/google';
|
||||
import { generateText } from 'ai';
|
||||
|
||||
// Use different providers for different tasks
|
||||
const complexTask = await generateText({
|
||||
model: openai('gpt-4'), // Best reasoning
|
||||
prompt: 'Complex analysis...',
|
||||
});
|
||||
|
||||
const longContext = await generateText({
|
||||
model: anthropic('claude-3-5-sonnet-20241022'), // Long context
|
||||
prompt: 'Document: ' + longDocument,
|
||||
});
|
||||
|
||||
const fastTask = await generateText({
|
||||
model: google('gemini-2.5-flash'), // Fast and cheap
|
||||
prompt: 'Quick summary...',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fallback Pattern
|
||||
|
||||
```typescript
|
||||
async function generateWithFallback(prompt: string) {
|
||||
try {
|
||||
return await generateText({ model: openai('gpt-4'), prompt });
|
||||
} catch (error) {
|
||||
console.error('OpenAI failed, trying Anthropic...');
|
||||
try {
|
||||
return await generateText({ model: anthropic('claude-3-5-sonnet-20241022'), prompt });
|
||||
} catch (error2) {
|
||||
console.error('Anthropic failed, trying Google...');
|
||||
return await generateText({ model: google('gemini-2.5-pro'), prompt });
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## All Providers
|
||||
|
||||
AI SDK supports 25+ providers. See full list:
|
||||
https://ai-sdk.dev/providers/overview
|
||||
|
||||
**Other Official Providers:**
|
||||
- xAI (Grok)
|
||||
- Mistral
|
||||
- Azure OpenAI
|
||||
- Amazon Bedrock
|
||||
- DeepSeek
|
||||
- Groq
|
||||
|
||||
**Community Providers:**
|
||||
- Ollama (local models)
|
||||
- FriendliAI
|
||||
- Portkey
|
||||
- LM Studio
|
||||
- Baseten
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-10-21
|
||||
739
references/top-errors.md
Normal file
739
references/top-errors.md
Normal file
@@ -0,0 +1,739 @@
|
||||
# AI SDK Core - Top 12 Errors & Solutions
|
||||
|
||||
Comprehensive guide to the most common AI SDK Core errors with actionable solutions.
|
||||
|
||||
---
|
||||
|
||||
## 1. AI_APICallError
|
||||
|
||||
**Type:** Network/API Error
|
||||
**Frequency:** Very Common
|
||||
**Severity:** High
|
||||
|
||||
### Cause
|
||||
|
||||
API request to provider failed due to:
|
||||
- Invalid API key
|
||||
- Network connectivity issues
|
||||
- Rate limit exceeded
|
||||
- Provider service outage
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
import { AI_APICallError } from 'ai';
|
||||
|
||||
try {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
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);
|
||||
|
||||
// Handle specific status codes
|
||||
if (error.statusCode === 401) {
|
||||
// Invalid API key
|
||||
console.error('Check OPENAI_API_KEY environment variable');
|
||||
} else if (error.statusCode === 429) {
|
||||
// Rate limit - implement exponential backoff
|
||||
await wait(Math.pow(2, retryCount) * 1000);
|
||||
// retry...
|
||||
} else if (error.statusCode >= 500) {
|
||||
// Provider issue - retry later
|
||||
console.error('Provider service issue, retry in 1 minute');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Validate API keys at application startup
|
||||
- Implement retry logic with exponential backoff
|
||||
- Monitor rate limits via response headers
|
||||
- Handle network errors gracefully
|
||||
- Set reasonable timeouts
|
||||
|
||||
### Resources
|
||||
|
||||
- Docs: https://ai-sdk.dev/docs/reference/ai-sdk-errors/ai-api-call-error
|
||||
|
||||
---
|
||||
|
||||
## 2. AI_NoObjectGeneratedError
|
||||
|
||||
**Type:** Generation Error
|
||||
**Frequency:** Common
|
||||
**Severity:** Medium
|
||||
|
||||
### Cause
|
||||
|
||||
Model didn't generate a valid object matching the Zod schema:
|
||||
- Schema too complex for model
|
||||
- Prompt doesn't provide enough context
|
||||
- Model capabilities exceeded
|
||||
- Safety filters triggered
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
import { AI_NoObjectGeneratedError } from 'ai';
|
||||
|
||||
try {
|
||||
const result = await generateObject({
|
||||
model: openai('gpt-4'),
|
||||
schema: z.object({
|
||||
// Complex schema
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
nested: z.object({ /* ... */ }),
|
||||
}),
|
||||
prompt: 'Generate a person',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AI_NoObjectGeneratedError) {
|
||||
console.error('No valid object generated');
|
||||
|
||||
// Solutions:
|
||||
// 1. Simplify schema
|
||||
const simpler = z.object({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
});
|
||||
|
||||
// 2. Add more context
|
||||
const betterPrompt = 'Generate a person profile with name (string) and age (number, 18-80)';
|
||||
|
||||
// 3. Try different model
|
||||
const result2 = await generateObject({
|
||||
model: openai('gpt-4'), // GPT-4 better than 3.5 for complex objects
|
||||
schema: simpler,
|
||||
prompt: betterPrompt,
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Start with simple schemas, add complexity gradually
|
||||
- Include examples in prompt: `"Generate like: { name: 'Alice', age: 30 }"`
|
||||
- Use GPT-4 or Claude for complex structured output
|
||||
- Test schemas with sample data first
|
||||
- Add descriptions to schema fields using `.describe()`
|
||||
|
||||
### Resources
|
||||
|
||||
- Docs: https://ai-sdk.dev/docs/reference/ai-sdk-errors/ai-no-object-generated-error
|
||||
|
||||
---
|
||||
|
||||
## 3. Worker Startup Limit (270ms+)
|
||||
|
||||
**Type:** Cloudflare Workers Issue
|
||||
**Frequency:** Common (Workers only)
|
||||
**Severity:** High (blocks deployment)
|
||||
|
||||
### Cause
|
||||
|
||||
AI SDK v5 + Zod initialization overhead exceeds Cloudflare Workers startup limit (must be <400ms):
|
||||
- Top-level imports of AI SDK packages
|
||||
- Complex Zod schemas at module level
|
||||
- Provider initialization at startup
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
// ❌ BAD: Top-level imports cause startup overhead
|
||||
import { createWorkersAI } from 'workers-ai-provider';
|
||||
import { generateText } from 'ai';
|
||||
import { complexSchema } from './schemas'; // Heavy Zod schemas
|
||||
|
||||
const workersai = createWorkersAI({ binding: env.AI }); // Runs at startup!
|
||||
|
||||
// ✅ GOOD: Lazy initialization inside handler
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
// Import inside handler
|
||||
const { createWorkersAI } = await import('workers-ai-provider');
|
||||
const { generateText } = await import('ai');
|
||||
|
||||
const workersai = createWorkersAI({ binding: env.AI });
|
||||
|
||||
const result = await generateText({
|
||||
model: workersai('@cf/meta/llama-3.1-8b-instruct'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
|
||||
return new Response(result.text);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Alternative Solution (Move Schemas Inside Routes)
|
||||
|
||||
```typescript
|
||||
// ❌ BAD: Top-level schema
|
||||
import { z } from 'zod';
|
||||
const PersonSchema = z.object({ /* complex schema */ });
|
||||
|
||||
// ✅ GOOD: Schema inside handler
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const { z } = await import('zod');
|
||||
const PersonSchema = z.object({ /* complex schema */ });
|
||||
|
||||
// Use schema here
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Never initialize AI SDK at module top-level in Workers
|
||||
- Move all imports inside route handlers
|
||||
- Minimize top-level Zod schemas
|
||||
- Monitor Worker startup time: `wrangler deploy` shows startup duration
|
||||
- Target < 270ms startup time to be safe (limit is 400ms)
|
||||
|
||||
### Resources
|
||||
|
||||
- Cloudflare Workers AI Docs: https://developers.cloudflare.com/workers-ai/configuration/ai-sdk/
|
||||
- GitHub: Search "Workers startup limit" in Vercel AI SDK issues
|
||||
|
||||
---
|
||||
|
||||
## 4. streamText Fails Silently
|
||||
|
||||
**Type:** Streaming Error
|
||||
**Frequency:** Occasional
|
||||
**Severity:** Medium (hard to debug)
|
||||
|
||||
### Cause
|
||||
|
||||
Stream errors are swallowed by `createDataStreamResponse()` or framework response handling:
|
||||
- Error occurs during streaming
|
||||
- Error handler not set up
|
||||
- Response already committed
|
||||
- Client disconnects
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Add explicit error handling
|
||||
const stream = streamText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
|
||||
try {
|
||||
for await (const chunk of stream.textStream) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
} catch (error) {
|
||||
// Error may not reach here if stream swallows it
|
||||
console.error('Stream error:', error);
|
||||
}
|
||||
|
||||
// ✅ BETTER: Always log on server side
|
||||
console.log('Starting stream...');
|
||||
const stream = streamText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
|
||||
stream.result.then(
|
||||
(result) => console.log('Stream success:', result.usage),
|
||||
(error) => console.error('Stream failed:', error) // This will catch errors!
|
||||
);
|
||||
|
||||
return stream.toDataStreamResponse();
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Always check server logs for stream errors
|
||||
- Implement server-side error monitoring (e.g., Sentry)
|
||||
- Test stream error handling explicitly
|
||||
- Use `try-catch` around stream consumption
|
||||
- Monitor for unexpected stream terminations
|
||||
|
||||
### Resources
|
||||
|
||||
- GitHub Issue: #4726
|
||||
|
||||
---
|
||||
|
||||
## 5. AI_LoadAPIKeyError
|
||||
|
||||
**Type:** Configuration Error
|
||||
**Frequency:** Very Common (setup)
|
||||
**Severity:** High (blocks usage)
|
||||
|
||||
### Cause
|
||||
|
||||
API key missing or invalid:
|
||||
- `.env` file not loaded
|
||||
- Wrong environment variable name
|
||||
- API key format invalid
|
||||
- Environment variable not set in deployment
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
import { AI_LoadAPIKeyError } from 'ai';
|
||||
|
||||
try {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Hello',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AI_LoadAPIKeyError) {
|
||||
console.error('API key error:', error.message);
|
||||
|
||||
// Debugging steps:
|
||||
console.log('OPENAI_API_KEY exists:', !!process.env.OPENAI_API_KEY);
|
||||
console.log('Key starts with sk-:', process.env.OPENAI_API_KEY?.startsWith('sk-'));
|
||||
|
||||
// Common issues:
|
||||
// 1. .env not loaded → use dotenv or similar
|
||||
// 2. Wrong variable name → check provider docs
|
||||
// 3. Key format wrong → verify in provider dashboard
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
```typescript
|
||||
// Validate at startup
|
||||
function validateEnv() {
|
||||
const required = ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY'];
|
||||
|
||||
for (const key of required) {
|
||||
if (!process.env[key]) {
|
||||
throw new Error(`Missing required environment variable: ${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateEnv();
|
||||
```
|
||||
|
||||
### Environment Variable Names
|
||||
|
||||
| Provider | Variable Name |
|
||||
|----------|---------------|
|
||||
| OpenAI | `OPENAI_API_KEY` |
|
||||
| Anthropic | `ANTHROPIC_API_KEY` |
|
||||
| Google | `GOOGLE_GENERATIVE_AI_API_KEY` |
|
||||
|
||||
### Resources
|
||||
|
||||
- Docs: https://ai-sdk.dev/docs/reference/ai-sdk-errors/ai-load-api-key-error
|
||||
|
||||
---
|
||||
|
||||
## 6. AI_InvalidArgumentError
|
||||
|
||||
**Type:** Validation Error
|
||||
**Frequency:** Common (development)
|
||||
**Severity:** Low (easy to fix)
|
||||
|
||||
### Cause
|
||||
|
||||
Invalid parameters passed to AI SDK function:
|
||||
- Negative `maxOutputTokens`
|
||||
- Invalid temperature (must be 0-2)
|
||||
- Wrong parameter types
|
||||
- Missing required parameters
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
import { AI_InvalidArgumentError } from 'ai';
|
||||
|
||||
try {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
maxOutputTokens: -1, // ❌ Invalid!
|
||||
temperature: 3.0, // ❌ Must be 0-2
|
||||
prompt: 'Hello',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AI_InvalidArgumentError) {
|
||||
console.error('Invalid argument:', error.message);
|
||||
// Fix: Check parameter types and values
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Use TypeScript for compile-time type checking
|
||||
- Validate inputs before calling AI SDK functions
|
||||
- Read function signatures carefully
|
||||
- Check official docs for parameter constraints
|
||||
- Use IDE autocomplete
|
||||
|
||||
### Resources
|
||||
|
||||
- Docs: https://ai-sdk.dev/docs/reference/ai-sdk-errors/ai-invalid-argument-error
|
||||
|
||||
---
|
||||
|
||||
## 7. AI_NoContentGeneratedError
|
||||
|
||||
**Type:** Generation Error
|
||||
**Frequency:** Occasional
|
||||
**Severity:** Medium
|
||||
|
||||
### Cause
|
||||
|
||||
Model generated no content:
|
||||
- Safety filters blocked output
|
||||
- Prompt triggered content policy
|
||||
- Model configuration issue
|
||||
- Empty prompt
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
import { AI_NoContentGeneratedError } from 'ai';
|
||||
|
||||
try {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
prompt: 'Some potentially problematic prompt',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AI_NoContentGeneratedError) {
|
||||
console.error('No content generated');
|
||||
|
||||
// Return user-friendly message
|
||||
return {
|
||||
text: 'Unable to generate response. Please try different input.',
|
||||
error: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Sanitize user inputs
|
||||
- Avoid prompts that may trigger safety filters
|
||||
- Have fallback messaging
|
||||
- Log occurrences for analysis
|
||||
- Test with edge cases
|
||||
|
||||
### Resources
|
||||
|
||||
- Docs: https://ai-sdk.dev/docs/reference/ai-sdk-errors/ai-no-content-generated-error
|
||||
|
||||
---
|
||||
|
||||
## 8. AI_TypeValidationError
|
||||
|
||||
**Type:** Validation Error
|
||||
**Frequency:** Common (with generateObject)
|
||||
**Severity:** Medium
|
||||
|
||||
### Cause
|
||||
|
||||
Zod schema validation failed on generated output:
|
||||
- Model output doesn't match schema
|
||||
- Schema too strict
|
||||
- Model misunderstood schema
|
||||
- Invalid JSON generated
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
import { AI_TypeValidationError } from 'ai';
|
||||
|
||||
try {
|
||||
const result = await generateObject({
|
||||
model: openai('gpt-4'),
|
||||
schema: z.object({
|
||||
age: z.number().min(0).max(120), // Strict validation
|
||||
email: z.string().email(), // Strict format
|
||||
}),
|
||||
prompt: 'Generate person',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AI_TypeValidationError) {
|
||||
console.error('Validation failed:', error.message);
|
||||
|
||||
// Solutions:
|
||||
// 1. Relax schema
|
||||
const relaxed = z.object({
|
||||
age: z.number(), // Remove min/max
|
||||
email: z.string().optional(), // Make optional
|
||||
});
|
||||
|
||||
// 2. Add guidance in prompt
|
||||
const better = await generateObject({
|
||||
model: openai('gpt-4'),
|
||||
schema: relaxed,
|
||||
prompt: 'Generate person with age 18-80 and valid email',
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Start with lenient schemas, tighten gradually
|
||||
- Use `.optional()` for unreliable fields
|
||||
- Add validation hints in field descriptions
|
||||
- Test with various prompts
|
||||
- Use mode: 'json' when available
|
||||
|
||||
### Resources
|
||||
|
||||
- Docs: https://ai-sdk.dev/docs/reference/ai-sdk-errors/ai-type-validation-error
|
||||
|
||||
---
|
||||
|
||||
## 9. AI_RetryError
|
||||
|
||||
**Type:** Network Error
|
||||
**Frequency:** Occasional
|
||||
**Severity:** High
|
||||
|
||||
### Cause
|
||||
|
||||
All retry attempts failed:
|
||||
- Persistent network issue
|
||||
- Provider outage
|
||||
- Invalid configuration
|
||||
- Unreachable API endpoint
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
import { AI_RetryError } from 'ai';
|
||||
|
||||
try {
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4'),
|
||||
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);
|
||||
console.error('Retry count:', error.retryCount);
|
||||
|
||||
// Implement circuit breaker
|
||||
if (isProviderDown()) {
|
||||
switchToFallbackProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Investigate root cause of failures
|
||||
- Adjust retry configuration if needed
|
||||
- Implement circuit breaker pattern
|
||||
- Have fallback providers
|
||||
- Monitor provider status pages
|
||||
|
||||
### Resources
|
||||
|
||||
- Docs: https://ai-sdk.dev/docs/reference/ai-sdk-errors/ai-retry-error
|
||||
|
||||
---
|
||||
|
||||
## 10. Rate Limiting Errors
|
||||
|
||||
**Type:** API Limit Error
|
||||
**Frequency:** Common (production)
|
||||
**Severity:** High
|
||||
|
||||
### Cause
|
||||
|
||||
Exceeded provider rate limits:
|
||||
- RPM (Requests Per Minute) exceeded
|
||||
- TPM (Tokens Per Minute) exceeded
|
||||
- Concurrent request limit hit
|
||||
- Free tier limits reached
|
||||
|
||||
### 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'),
|
||||
prompt,
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.statusCode === 429) {
|
||||
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s, 8s...
|
||||
console.log(`Rate limited, waiting ${delay}ms`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error('Rate limit retries exhausted');
|
||||
}
|
||||
|
||||
// Or use queue
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
const queue = new PQueue({ concurrency: 5, interval: 60000, intervalCap: 50 });
|
||||
|
||||
async function generateQueued(prompt: string) {
|
||||
return queue.add(() => generateText({ model: openai('gpt-4'), prompt }));
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Monitor rate limit headers in responses
|
||||
- Queue requests to stay under limits
|
||||
- Upgrade provider tier if needed
|
||||
- Implement request throttling
|
||||
- Cache results when possible
|
||||
|
||||
### Resources
|
||||
|
||||
- OpenAI Rate Limits: https://platform.openai.com/account/rate-limits
|
||||
- Anthropic Rate Limits: https://docs.anthropic.com/en/api/rate-limits
|
||||
|
||||
---
|
||||
|
||||
## 11. TypeScript Performance with Zod
|
||||
|
||||
**Type:** Development Issue
|
||||
**Frequency:** Occasional
|
||||
**Severity:** Low (annoying)
|
||||
|
||||
### Cause
|
||||
|
||||
Complex Zod schemas slow down TypeScript type checking:
|
||||
- Deeply nested schemas
|
||||
- Many union types
|
||||
- Recursive types
|
||||
- Top-level complex schemas
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
// ❌ BAD: Complex schema at top level
|
||||
const ComplexSchema = z.object({
|
||||
// 100+ fields with nested objects...
|
||||
});
|
||||
|
||||
// ✅ GOOD: Define inside function
|
||||
function generateData() {
|
||||
const schema = z.object({
|
||||
// Complex schema here
|
||||
});
|
||||
|
||||
return generateObject({ model: openai('gpt-4'), schema, prompt: '...' });
|
||||
}
|
||||
|
||||
// ✅ GOOD: Use z.lazy() for recursive
|
||||
type Category = { name: string; subcategories?: Category[] };
|
||||
|
||||
const CategorySchema: z.ZodType<Category> = z.lazy(() =>
|
||||
z.object({
|
||||
name: z.string(),
|
||||
subcategories: z.array(CategorySchema).optional(),
|
||||
})
|
||||
);
|
||||
|
||||
// ✅ GOOD: Split large schemas
|
||||
const AddressSchema = z.object({ /* ... */ });
|
||||
const PersonSchema = z.object({
|
||||
address: AddressSchema, // Reuse smaller schema
|
||||
});
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Avoid top-level complex schemas
|
||||
- Use `z.lazy()` for recursive types
|
||||
- Split large schemas into smaller ones
|
||||
- Use type assertions where appropriate
|
||||
- Enable `skipLibCheck` in tsconfig.json if desperate
|
||||
|
||||
### Resources
|
||||
|
||||
- Troubleshooting: https://ai-sdk.dev/docs/troubleshooting/common-issues/slow-type-checking
|
||||
|
||||
---
|
||||
|
||||
## 12. Invalid JSON Response (Provider-Specific)
|
||||
|
||||
**Type:** Provider Issue
|
||||
**Frequency:** Rare
|
||||
**Severity:** Medium
|
||||
|
||||
### Cause
|
||||
|
||||
Some models occasionally return invalid JSON:
|
||||
- Model error
|
||||
- Provider API issue
|
||||
- Specific model version bug (e.g., Imagen 3.0)
|
||||
|
||||
### Solution
|
||||
|
||||
```typescript
|
||||
// Use built-in retry and mode selection
|
||||
try {
|
||||
const result = await generateObject({
|
||||
model: openai('gpt-4'),
|
||||
schema: mySchema,
|
||||
prompt: 'Generate data',
|
||||
mode: 'json', // Force JSON mode (GPT-4 supports this)
|
||||
maxRetries: 3, // Retry on invalid JSON
|
||||
});
|
||||
} catch (error) {
|
||||
// Fallback to different model
|
||||
console.error('GPT-4 failed, trying Claude...');
|
||||
const result2 = await generateObject({
|
||||
model: anthropic('claude-3-5-sonnet-20241022'),
|
||||
schema: mySchema,
|
||||
prompt: 'Generate data',
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Prevention
|
||||
|
||||
- Use `mode: 'json'` when available
|
||||
- Prefer GPT-4/Claude for structured output
|
||||
- Implement retry logic
|
||||
- Validate responses
|
||||
- Have fallback models
|
||||
|
||||
### Resources
|
||||
|
||||
- GitHub Issue: #4302 (Imagen 3.0 Invalid JSON)
|
||||
|
||||
---
|
||||
|
||||
## For More Errors
|
||||
|
||||
See complete error reference (28 total error types):
|
||||
https://ai-sdk.dev/docs/reference/ai-sdk-errors
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-10-21
|
||||
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