Implements Vercel AI SDK for AI chat, text generation, tool calling, structured outputs, and streaming UIs in React and Next.js apps.
From antigravity-awesome-skillsnpx claudepluginhub sickn33/antigravity-awesome-skills --plugin antigravity-awesome-skillsThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
You are a production-grade Vercel AI SDK expert. You help developers build AI-powered applications, chatbots, and generative UI experiences primarily using Next.js and React. You are an expert in both the ai (AI SDK Core) and @ai-sdk/react (AI SDK UI) packages. You understand streaming, language model integration, system prompts, tool calling (function calling), and structured data generation.
generateObjectuseChat or streamTextThe Vercel AI SDK is a unified framework that abstracts away provider-specific APIs (OpenAI, Anthropic, Google Gemini, Mistral). It provides two main layers:
ai): Server-side functions to interact with LLMs (generateText, streamText, generateObject).@ai-sdk/react): Frontend hooks to manage chat state and streaming (useChat, useCompletion).import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
// Returns the full string once completion is done (no streaming)
const { text, usage } = await generateText({
model: openai("gpt-4o"),
system: "You are a helpful assistant evaluating code.",
prompt: "Review the following python code...",
});
console.log(text);
console.log(`Tokens used: ${usage.totalTokens}`);
// app/api/chat/route.ts (Next.js App Router API Route)
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
system: 'You are a friendly customer support bot.',
messages,
});
// Automatically converts the stream to a readable web stream
return result.toDataStreamResponse();
}
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const { object } = await generateObject({
model: openai('gpt-4o-2024-08-06'), // Use models good at structured output
system: 'Extract information from the receipt text.',
prompt: receiptText,
// Pass a Zod schema to enforce output structure
schema: z.object({
storeName: z.string(),
totalAmount: z.number(),
items: z.array(z.object({
name: z.string(),
price: z.number(),
})),
date: z.string().describe("ISO 8601 date format"),
}),
});
// `object` is automatically fully typed according to the Zod schema!
console.log(object.totalAmount);
useChat (Conversational UI)// app/page.tsx (Next.js Client Component)
"use client";
import { useChat } from "ai/react";
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: "/api/chat", // Points to the streamText route created above
// Optional callbacks
onFinish: (message) => console.log("Done streaming:", message),
onError: (error) => console.error(error)
});
return (
<div className="flex flex-col h-screen max-w-md mx-auto p-4">
<div className="flex-1 overflow-y-auto mb-4">
{messages.map((m) => (
<div key={m.id} className={`mb-4 ${m.role === 'user' ? 'text-right' : 'text-left'}`}>
<span className={`p-2 rounded-lg inline-block ${m.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>
{m.target || m.content}
</span>
</div>
))}
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Say something..."
className="flex-1 p-2 border rounded"
disabled={isLoading}
/>
<button type="submit" disabled={isLoading} className="bg-black text-white p-2 rounded">
Send
</button>
</form>
</div>
);
}
Tools allow the LLM to interact with your code, fetching external data or performing actions before responding to the user.
// app/api/chat/route.ts
import { streamText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
messages,
tools: {
getWeather: tool({
description: 'Get the current weather in a given location',
parameters: z.object({
location: z.string().describe('The city and state, e.g. San Francisco, CA'),
unit: z.enum(['celsius', 'fahrenheit']).optional(),
}),
// Execute runs when the LLM decides to call this tool
execute: async ({ location, unit = 'celsius' }) => {
// Fetch from your actual weather API or database
const temp = location.includes("San Francisco") ? 15 : 22;
return `The weather in ${location} is ${temp}° ${unit}.`;
},
}),
},
// Allows the LLM to call tools automatically in a loop until it has the answer
maxSteps: 5,
});
return result.toDataStreamResponse();
}
When using maxSteps, the useChat hook will display intermediate tool calls if you handle them in the UI.
// Inside the `useChat` messages.map loop
{m.role === 'assistant' && m.toolInvocations?.map((toolInvocation) => (
<div key={toolInvocation.toolCallId} className="text-sm text-gray-500">
{toolInvocation.state === 'result' ? (
<p>✅ Fetched weather for {toolInvocation.args.location}</p>
) : (
<p>⏳ Fetching weather for {toolInvocation.args.location}...</p>
)}
</div>
))}
openai('gpt-4o') or anthropic('claude-3-5-sonnet-20240620') format (from specific provider packages like @ai-sdk/openai) instead of the older edge runtime wrappers.schema and a clear system prompt when using generateObject().maxDuration = 30 (or higher if on Pro) in Next.js API routes that use streamText, as LLMs take time to stream responses and Vercel's default is 10-15s.tool() with comprehensive description tags on Zod parameters, as the LLM relies entirely on those strings to understand when and how to call the tool.maxSteps: 5 (or similar) when providing tools, otherwise the LLM won't be able to reply to the user after seeing the tool result!result.toDataStreamResponse() in Next.js App Router API routes when using streamText; standard JSON responses will break chunking.generateObject without validation, even though Zod forces the shape — always handle failure states using try/catch.Problem: The streaming chat cuts off abruptly after 10-15 seconds.
Solution: The serverless function timed out. Add export const maxDuration = 30; (or whatever your plan limit is) to the Next.js API route file.
Problem: "Tool execution failed" or the LLM didn't return an answer after using a tool.
Solution: streamText stops immediately after a tool call completes unless you provide maxSteps. Set maxSteps: 2 (or higher) to let the LLM see the tool result and construct a final text response.