Vercel AI Ecosystem \u2014 Complete Implementation Guide
This skill covers the full Vercel AI stack for building production-grade AI applications:
| Layer | Package | Purpose |
|-------|---------|---------|
| AI SDK Core | ai | Text generation, streaming, structured output, tools, agents, embeddings |
| AI SDK UI | @ai-sdk/react | React hooks: useChat, useCompletion, useObject |
| AI Gateway | @ai-sdk/gateway | Unified model access, routing, fallbacks, caching, observability |
| AI Elements | ai-elements | 48+ pre-built React components for AI interfaces (shadcn/ui based) |
| Chat SDK | chat | Cross-platform bots (Slack, Discord, Teams, GitHub, Telegram, Linear) |
| Workflow DevKit | workflow | Durable, resumable workflows with "use workflow" / "use step" directives |
| Security | botid, @vercel/firewall | Firewall/WAF, BotID bot detection, rate limiting, DDoS, abuse protection |
| Platform | @vercel/functions, @vercel/blob | Fluid Compute, streaming, storage, feature flags, caching, pricing |
| Sandbox | @vercel/sandbox | Isolated code execution in Firecracker microVMs, snapshots, network policies |
How to Use This Skill
- Start with the Quick Start Patterns below for common use cases
- Read the Critical v6 API Rules to avoid the most common mistakes
- Consult reference files (below) for detailed API docs on specific domains
Reference Files
Read reference files as needed for the specific domain you're working in:
| File | When to read |
|------|-------------|
| references/ai-sdk-core.md | generateText, streamText, Output helpers, generateImage, generateSpeech, providers, embeddings, error handling |
| references/ai-sdk-ui.md | useChat, useCompletion, useObject hooks, streaming patterns, message types |
| references/ai-elements.md | UI components: Message, Conversation, PromptInput, Reasoning, Tool, CodeBlock, etc. |
| references/agents-and-tools.md | ToolLoopAgent, multi-step agents, tool calling, MCP integration |
| references/chat-sdk.md | Cross-platform chat bots: adapters, cards, modals, streaming to platforms |
| references/workflow-sdk.md | Durable workflows, steps, sleep, webhooks, hooks, error handling, deployment |
| references/patterns.md | Architecture decisions, best practices, production patterns, middleware |
| references/ai-gateway.md | AI Gateway: routing, fallbacks, caching, BYOK, observability |
| references/data-stream-protocol.md | SSE protocol for custom backends and native mobile clients (SwiftUI) |
| references/streamdown.md | Streamdown markdown renderer: full API, plugins, remend, performance |
| references/security.md | Firewall/WAF rules, BotID, rate limiting, DDoS, prompt injection, cost protection |
| references/vercel-platform.md | Fluid Compute, function config, streaming, @vercel/functions, storage, feature flags, pricing, limits |
| references/vercel-sandbox.md | Sandbox SDK, AI agent integration, snapshots, network policies, credential brokering |
Quick Start Patterns
Pattern 1: AI Chat with Beautiful UI (Most Common)
Server route + useChat hook + AI Elements components:
// app/api/chat/route.ts
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: anthropic('claude-sonnet-4-5'),
messages,
});
return result.toDataStreamResponse();
}
// app/page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
import { Conversation, ConversationContent } from '@/components/ai-elements/conversation';
import { Message, MessageContent, MessageResponse } from '@/components/ai-elements/message';
import { PromptInput, PromptInputTextarea, PromptInputSubmit } from '@/components/ai-elements/prompt-input';
export default function Chat() {
const { messages, sendMessage, status, input, setInput } = useChat();
return (
<div className="flex flex-col h-screen">
<Conversation>
<ConversationContent>
{messages.map((message) => (
<Message from={message.role} key={message.id}>
<MessageContent>
{message.parts.map((part, i) => {
if (part.type === 'text') {
return <MessageResponse key={`${message.id}-${i}`}>{part.text}</MessageResponse>;
}
return null;
})}
</MessageContent>
</Message>
))}
</ConversationContent>
</Conversation>
<PromptInput onSubmit={(value) => sendMessage({ text: value })}>
<PromptInputTextarea value={input} onChange={(e) => setInput(e.target.value)} />
<PromptInputSubmit />
</PromptInput>
</div>
);
}
Pattern 2: Agent with Tool Display + Reasoning
// Render multi-part messages with reasoning, tools, and text
// v6: use if/else with startsWith for tool parts (dynamic type names)
{message.parts.map((part, i) => {
if (part.type === 'text') {
return <MessageResponse key={i}>{part.text}</MessageResponse>;
}
if (part.type === 'reasoning') {
return (
<Reasoning key={i}>
<ReasoningTrigger />
<ReasoningContent>{part.text}</ReasoningContent>
</Reasoning>
);
}
if (part.type.startsWith('tool-')) {
return <Tool key={i} part={part} />;
}
return null;
})}
Pattern 3: Structured Output with Tools
import { streamText, tool, Output, stepCountIs } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
const result = await streamText({
model: anthropic('claude-sonnet-4-5'),
tools: {
getWeather: tool({
description: 'Get weather for a city',
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ temp: 72, condition: 'sunny' }),
}),
},
output: Output.object({
schema: z.object({
summary: z.string(),
recommendations: z.array(z.string()),
}),
}),
stopWhen: stepCountIs(5), // Enable multi-step tool calling
messages,
});
Pattern 4: Cross-Platform Chat Bot
import { Chat } from 'chat';
import { createSlackAdapter } from '@chat-adapter/slack';
import { createRedisState } from '@chat-adapter/state-redis';
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
const bot = new Chat({
userName: 'mybot',
adapters: { slack: createSlackAdapter() },
state: createRedisState(),
});
bot.onNewMention(async (thread) => {
await thread.subscribe();
const result = streamText({
model: anthropic('claude-sonnet-4-5'),
prompt: thread.message.text,
});
await thread.post(result.toTextStream());
});
Pattern 5: Durable Workflow
import { sleep } from 'workflow';
export async function userOnboarding(email: string) {
'use workflow';
const user = await createUser(email);
await sendWelcomeEmail(user);
await sleep('3 days');
await sendFollowUpEmail(user);
}
Pattern 6: Secure AI Chat Endpoint (BotID + Rate Limiting + Auth)
// instrumentation-client.ts — client-side BotID setup
import { initBotId } from 'botid/client/core';
initBotId({
protect: [{ path: '/api/chat', method: 'POST' }],
});
// app/api/chat/route.ts — layered server-side protection
import { checkBotId } from 'botid/server';
import { checkRateLimit } from '@vercel/firewall';
import { streamText, stepCountIs } from 'ai';
import { openai } from '@ai-sdk/openai';
import { auth } from '@/auth';
export const maxDuration = 30;
export async function POST(req: Request) {
// Layer 1: Authentication
const session = await auth();
if (!session) return new Response('Unauthorized', { status: 401 });
// Layer 2: Bot detection
const { isBot } = await checkBotId();
if (isBot) return Response.json({ error: 'Access denied' }, { status: 403 });
// Layer 3: Rate limiting (per user)
const { rateLimited } = await checkRateLimit('ai-chat-limit', {
request: req,
rateLimitKey: session.user.id,
});
if (rateLimited) return Response.json({ error: 'Rate limit exceeded' }, { status: 429 });
// Layer 4: Input sanitization + resource limits
const { messages } = await req.json();
const sanitized = messages.filter((m: { role: string }) => m.role !== 'system');
const result = streamText({
model: openai('gpt-4o'),
system: 'You are a helpful assistant.',
messages: sanitized,
maxTokens: 4096,
stopWhen: stepCountIs(5),
abortSignal: req.signal,
});
return result.toDataStreamResponse();
}
Critical v6 API Rules
v6 renamed and restructured many APIs from v5. These are the most common mistakes \u2014 using old names causes runtime errors:
- Tool definition: Use
tool()helper withinputSchema(NOTparameters\u2014 renamed in v5/v6) - Model specification: Use provider functions \u2014
anthropic('claude-sonnet-4-5'),openai('gpt-4o') - Chat hook: Use
sendMessage({ text: '...' })(NOTappend(), NOT{ content }\u2014 v6 usestextproperty) - Message format: Access
message.partsarray, switch onpart.type. UIMessage no longer hascontent. - Reasoning parts: Use
part.text(NOTpart.reasoning\u2014 property renamed totextin v6) - Multi-step control: Use
stopWhen: stepCountIs(N)(NOTmaxSteps\u2014 removed in v6). For useChat, usesendAutomaticallyWhen. - Streaming response: Use
toDataStreamResponse()ortoUIMessageStreamResponse() - Agent response: Standalone
createAgentUIStreamResponse({ agent, uiMessages })(NOT a method on the agent) - ToolLoopAgent: Use
instructionsparameter (NOTsystem\u2014 renamed for agents) - Structured output:
Output.object(),Output.array(),Output.choice(),Output.json(),Output.text() - Tool approval: Use
needsApproval: trueon tool definition (NOTexperimental_toolCallApproval) - Tool part types: Parts use
tool-{toolName}pattern. States:input-streaming,input-available,approval-requested,output-available - Embeddings: Use
provider.embeddingModel('model-name')(NOTtextEmbeddingModel— renamed in v6) - MCP: Use
createMCPClient()from@ai-sdk/mcp - Package manager: Always detect from lockfile (pnpm-lock.yaml \u2192 pnpm, etc.)
- Message types:
CoreMessagerenamed toModelMessage. UseconvertToModelMessages()(NOTconvertToCoreMessages\u2014 renamed and now async) - generateObject/streamObject: Deprecated \u2014 use
generateText/streamTextwithOutput.*helpers instead - Embeddings:
textEmbeddingModel()renamed toembeddingModel(),textEmbedding()toembedding() - Token usage:
cachedInputTokens\u2192inputTokenDetails.cacheReadTokens,reasoningTokens\u2192outputTokenDetails.reasoningTokens - OpenAI strict mode:
strictJsonSchemadefaults totruein v6 \u2014 use.nullable()not.optional()in Zod schemas - Azure:
azure()uses Responses API by default; useazure.chat()for Chat Completions. Metadata key:azure(notopenai) - Migration tool: Run
npx @ai-sdk/codemod v6to automate most v5\u21926 changes - Tool results:
addToolResultrenamed toaddToolOutputin v6 (useChat hook)
Installing AI Elements
# Individual components (recommended)
npx ai-elements@latest add message conversation prompt-input reasoning tool code-block
# All components via shadcn registry
npx shadcn@latest add https://elements.ai-sdk.dev/api/registry/all.json
Components install as source code into your project (typically @/components/ai-elements/), giving you full control. Requires Tailwind CSS in CSS Variables mode.
Key Streaming Architecture
Client (useChat) <--SSE--> Server (streamText) <--Gateway--> LLM Provider
| | |
AI Elements Middleware AI Gateway
(rendering) (caching, logging, (routing, fallbacks,
guardrails, RAG) caching, observability)
The Data Stream Protocol uses Server-Sent Events with typed chunks: text deltas, tool calls, tool results, reasoning, and finish signals. MessageResponse component from AI Elements handles incremental markdown rendering via Streamdown without re-parsing on each chunk.
微信扫一扫