dist/plugins/api-ai-vercel-ai-sdk/skills/api-ai-vercel-ai-sdk/SKILL.md
Vercel AI SDK patterns - providers, text generation, streaming, structured output, tool calling, chat UI hooks, embeddings, and RAG
npx skillsauth add agents-inc/skills api-ai-vercel-ai-sdkInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: Use Vercel AI SDK (v6) to build AI-powered applications with a unified provider API. Use
generateText/streamTextfor text generation and streaming,Output.object()/Output.array()for structured data with Zod,tool()for function calling, anduseChat/useCompletionhooks for React chat UIs. Supports OpenAI, Anthropic, Google, and 20+ providers through a single API.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use the ai package (v6) with Output.object() / Output.array() for structured output -- NOT the deprecated generateObject / streamObject functions)
(You MUST define tool input schemas with z.object() and use .describe() on each property to help the model understand expected inputs)
(You MUST use streamText for user-facing responses to enable progressive rendering -- use generateText only for background/non-interactive tasks)
(You MUST handle streaming errors via onError callback -- streamText errors become part of the stream and are NOT thrown)
(You MUST use inputSchema (not parameters) when defining tools -- parameters was renamed in SDK v5+)
</critical_requirements>
Auto-detection: AI SDK, Vercel AI, generateText, streamText, generateObject, streamObject, Output.object, Output.array, useChat, useCompletion, @ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google, tool(), toolChoice, embedMany, embed, cosineSimilarity, ToolLoopAgent, smoothStream
When to use:
Key patterns covered:
generateText) and streaming (streamText)Output.object, Output.array, Output.choice)tool(), multi-step execution, and approval flowsuseChat for chat UIs, useCompletion for text completionembed, embedMany) and RAG patterns with cosineSimilarityWhen NOT to use:
openai npm package) is simplerDetailed Resources:
The Vercel AI SDK provides a unified TypeScript API for building AI-powered applications across providers. Instead of learning each provider's unique SDK, you write one set of code that works with OpenAI, Anthropic, Google, and 20+ other providers.
Core principles:
generateText({ model: 'openai/gpt-4o' }) and generateText({ model: 'anthropic/claude-sonnet-4.5' }) use the same API.streamText starts delivering tokens immediately. Use it for all user-facing responses. generateText blocks until completion and is better for background tasks and agent loops..describe() on schema properties to guide the model.useChat and useCompletion work with React, Svelte, Vue, and Angular. They manage streaming state, message history, and input handling.When to use Vercel AI SDK:
When NOT to use:
Configure providers via direct imports (auto-reads env vars), custom instances, or AI Gateway. See examples/core.md for full examples.
import { gateway } from "ai";
import { openai } from "@ai-sdk/openai";
// Gateway: provider/model string routing
const model = gateway("anthropic/claude-sonnet-4.5");
// Direct: auto-reads OPENAI_API_KEY from env
const openaiModel = openai("gpt-4o");
Use customProvider for semantic model aliases (models('fast'), models('smart')). Never hardcode API keys.
Use generateText for non-interactive tasks. Returns a promise that resolves when complete. See examples/core.md.
import { generateText } from "ai";
const { text, usage } = await generateText({
model: "openai/gpt-4o",
system: "You are a professional technical writer.",
prompt: `Summarize: ${article}`,
});
Use ModelMessage[] for multi-turn conversations. Append response.messages for continued dialogue. Do NOT use generateText for user-facing responses -- use streamText instead.
Use streamText for all user-facing responses. Errors are part of the stream (not thrown) -- use onError. See examples/core.md.
import { streamText, smoothStream } from "ai";
const result = streamText({
model: "anthropic/claude-sonnet-4.5",
prompt: "Explain TypeScript.",
experimental_transform: smoothStream(),
onError({ error }) {
console.error("Stream error:", error);
},
});
for await (const part of result.textStream) {
process.stdout.write(part);
}
Use result.toTextStreamResponse() in route handlers. Use result.fullStream for granular event types (text-delta, tool-call, error, finish).
Use Output.object() with generateText/streamText for type-safe structured data. See examples/structured-output.md.
import { generateText, Output } from "ai";
import { z } from "zod";
const schema = z.object({
name: z.string().describe("Recipe name"),
steps: z.array(z.string()).describe("Cooking instructions"),
});
const { output } = await generateText({
model: "openai/gpt-4o",
output: Output.object({ schema }),
prompt: "Generate a vegetarian lasagna recipe.",
});
Key variants: Output.array({ element }) with elementStream for streaming arrays, Output.choice() for classification, partialOutputStream for streaming partial objects. Do NOT use deprecated generateObject/streamObject.
Define tools with tool(), Zod inputSchema, and execute. The SDK handles multi-step loops. See examples/tools.md.
import { generateText, tool, stepCountIs } from "ai";
import { z } from "zod";
const weatherTool = tool({
description: "Get weather in a location",
inputSchema: z.object({
location: z.string().describe("City name"),
}),
execute: async ({ location }) => fetchWeather(location),
});
const MAX_STEPS = 5;
const { text } = await generateText({
model: "openai/gpt-4o",
tools: { weather: weatherTool },
stopWhen: stepCountIs(MAX_STEPS),
prompt: "Weather in SF and Tokyo?",
});
Key features: needsApproval for human-in-the-loop, ToolLoopAgent for reusable agents (use instructions not system), toolChoice to force/prevent tool usage, activeTools/prepareStep for per-step control. Always use stepCountIs() to prevent infinite loops.
useChat manages streaming chat state. v6 uses transport-based architecture and external input state. See examples/chat.md.
import { useChat } from "@ai-sdk/react";
import { useState } from "react";
export function Chat() {
const [input, setInput] = useState("");
const { messages, sendMessage, status, stop, error } = useChat();
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!input.trim()) return;
sendMessage({ text: input }); // NOT { role, content }
setInput("");
}
// ... render messages.parts, status-based UI
}
v6 breaking changes: sendMessage({ text }) replaces handleSubmit/append({ role, content }). External useState for input (hook no longer manages it). status replaces isLoading. Import from @ai-sdk/react not ai/react.
useCompletion handles single-turn text completions. Unlike useChat, it still manages input state internally. See examples/core.md.
import { useCompletion } from "@ai-sdk/react";
const { completion, input, handleInputChange, handleSubmit, isLoading } =
useCompletion({
api: "/api/completion",
});
Good for autocomplete, summarization, and one-shot generation where multi-turn chat is not needed.
</patterns><decision_framework>
Do you need AI-generated content?
├─ YES -> Is it user-facing (needs progressive display)?
│ ├─ YES -> Is it a multi-turn conversation?
│ │ ├─ YES -> useChat hook (React) or streamText (server)
│ │ └─ NO -> Is it a single completion/generation?
│ │ ├─ YES -> useCompletion hook (React) or streamText (server)
│ │ └─ NO -> streamText with custom UI
│ └─ NO -> Is it a background task (agent, batch)?
│ ├─ YES -> generateText (blocks until complete)
│ └─ NO -> generateText for simple one-shots
├─ Do you need structured data (JSON/objects)?
│ ├─ YES -> Output.object() with Zod schema
│ │ ├─ Need streaming partial object? -> streamText + partialOutputStream
│ │ ├─ Need array of items? -> Output.array() + elementStream
│ │ └─ Need one of N options? -> Output.choice()
│ └─ NO -> Plain text generation
├─ Do you need the model to call functions?
│ ├─ YES -> Define tools with tool() + Zod inputSchema
│ │ ├─ Multi-step reasoning? -> stopWhen: stepCountIs(N)
│ │ ├─ Need human approval? -> needsApproval on tool
│ │ └─ Single tool call? -> Default (stops after first response)
│ └─ NO -> No tools needed
└─ Do you need vector embeddings?
├─ Single text -> embed()
├─ Batch of texts -> embedMany()
└─ Similarity search -> cosineSimilarity()
What is your primary concern?
├─ Best reasoning / complex tasks -> anthropic/claude-sonnet-4.5 or openai/o3
├─ Fast + cheap for simple tasks -> openai/gpt-4o-mini or anthropic/claude-haiku-4.5
├─ Structured output reliability -> openai/gpt-4o (best schema adherence)
├─ Multi-modal (images + text) -> openai/gpt-4o or anthropic/claude-sonnet-4.5
├─ Google ecosystem / grounding -> google/gemini-2.5-flash
└─ Provider agnostic -> Use AI Gateway with model aliases
</decision_framework>
Framework support:
streamText (any framework with standard Request/Response)useChat, useCompletion) from @ai-sdk/react with framework-specific variants for Svelte, Vue, and AngularProvider architecture:
ai package provides generateText, streamText, embed, Output, tool, gateway@ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google) auto-read environment variables@ai-sdk/openai-compatible supports any OpenAI-compatible API (Ollama, Together AI, etc.)gateway) routes to any provider with a provider/model stringSchema integration:
Output.object()) and tool input schemas use Zod for validation and type inference<red_flags>
High Priority Issues:
generateObject / streamObject instead of generateText + Output.object() (removed in v6)parameters instead of inputSchema in tool definitions (renamed in v5+)generateText for user-facing chat responses (blocks until complete, no streaming)import { useChat } from 'ai/react' instead of import { useChat } from '@ai-sdk/react'CoreMessage type instead of ModelMessage (renamed in v6)sendMessage({ role: 'user', content: text }) instead of sendMessage({ text }) (v6 API change)Medium Priority Issues:
.describe() on Zod schema properties for structured output (model gets less guidance)stopWhen with stepCountIs() for multi-step tool calling (risks infinite loops)onError callback (errors silently disappear)system instead of instructions in ToolLoopAgent (renamed in v6)Common Mistakes:
streamText does NOT throw errors -- they appear in the stream as error eventsstreamText -- the function returns immediately, you must iterate the streamobject destructure from deprecated generateObject instead of output from generateText with Output.object()model parameter without a provider prefix (e.g., 'gpt-4o' instead of 'openai/gpt-4o')Gotchas & Edge Cases:
smoothStream() transform adds slight delay but makes output feel more natural -- always use for chat UIsOutput.array() with elementStream yields each element only when fully validated -- partial elements are not emittedembed() and embedMany() require embedding model strings (e.g., 'openai/text-embedding-3-small'), not chat model stringsuseChat v6 no longer manages input state -- you must use external useState for the input field and call sendMessage({ text }) (not { role, content })convertToModelMessages() is async in v6 (was sync as convertToCoreMessages() in v5)fullStream gives you all event types including tool-call, tool-result, source, and error -- textStream only gives text deltasusage property on results, including cache hit details in usage.inputTokenDetails</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use the ai package (v6) with Output.object() / Output.array() for structured output -- NOT the deprecated generateObject / streamObject functions)
(You MUST define tool input schemas with z.object() and use .describe() on each property to help the model understand expected inputs)
(You MUST use streamText for user-facing responses to enable progressive rendering -- use generateText only for background/non-interactive tasks)
(You MUST handle streaming errors via onError callback -- streamText errors become part of the stream and are NOT thrown)
(You MUST use inputSchema (not parameters) when defining tools -- parameters was renamed in SDK v5+)
Failure to follow these rules will produce broken AI integrations, deprecated API usage, or poor user experiences with blocked responses.
</critical_reminders>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety