Vercel AI SDK reference notes

Just a guy who loves to write code and watch anime.
Search for a command to run...

Just a guy who loves to write code and watch anime.
No comments yet. Be the first to comment.
Qualities that make outstanding builders.

Bipedal vs quadrupedal: how many legs This is about the number of legs the creature walks on. Bipedal. Two legs. Humans, ostriches, T-rex, kangaroos, most fantasy humanoids. Quadrupedal. Four legs. Do

Intro You hear "lerp" and "smoothstep" everywhere in game dev. They sound like math jargon. They're not. Both are small tools that do the same job: smoothly move from one value to another. The problem

What is Kinematics Kinematics is the math field. It's the study of how things move without worrying about forces (which would be dynamics). FK and IK are the two branches: forward kinematics and inver

Intro Textures are usually the biggest cost in a 3D scene. Memory, bandwidth, and load time all get eaten by them. Resizing them is the obvious lever. There's more. This post is about the less obvious

The Vercel AI SDK is an open-source JavaScript library that provides a unified interface for working with different LLM (Large Language Model) providers. It handles the communication between your application and various AI models, making it easy to switch between providers without rewriting your code.
The SDK is completely free and open-source. You can use it in any JavaScript environment → you don't need to use Vercel's hosting or pay them anything.
The SDK has three main packages:
Core (package name: ai) → For backend code (Node.js, Bun, Deno)
AI SDK UI → Frontend components and hooks (framework-agnostic)
AI SDK RSC → React Server Components integration
npm install ai
For specific providers, install their packages:
npm install @ai-sdk/anthropic
npm install @ai-sdk/openai
The simplest way to get text from an LLM:
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
const model = anthropic("claude-3-5-haiku-latest");
const response = await generateText({
model,
prompt: "What is the chemical formula for water?",
});
console.log(response.text); // "The chemical formula for water is H2O."
For a better user experience, stream the response as it's generated:
import { streamText } from "ai";
const { textStream } = await streamText({
model,
prompt: "What is the color of the sun?",
});
// Stream to UI
for await (const text of textStream) {
// Add text to your UI as it arrives
process.stdout.write(text);
}
// Or get the final text when complete
const finalText = await textStream;
Guide the model's behavior with system prompts:
const { text } = await generateText({
model,
prompt: "Summarize this article...",
system: "You are a text summarizer. Be concise. Return only the summary."
});
You can also use messages format:
const { text } = await generateText({
model,
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello, who are you?" }
]
});
Easily switch between different models:
import { openai } from "@ai-sdk/openai";
import { anthropic } from "@ai-sdk/anthropic";
import { generateText, type LanguageModel } from "ai";
// Function that works with any model
async function askQuestion(prompt: string, model: LanguageModel) {
const { text } = await generateText({ model, prompt });
return text;
}
// Use with different providers
const openaiResponse = await askQuestion("Tell me a joke", openai("gpt-4o"));
const anthropicResponse = await askQuestion("Tell me a joke", anthropic("claude-3-opus"));
Manage conversations with message history:
// Messages array structure
const messages = [
{ role: "system", content: "You are a friendly assistant." },
{ role: "user", content: "Hello, who are you?" },
{ role: "assistant", content: "Hi there! I'm Claude, your AI assistant. How can I help you today?" },
{ role: "user", content: "What can you do?" }
];
// Pass to model
const { response } = await generateText({
model,
messages
});
// Add new messages from response
const updatedMessages = [...messages, ...response.messages];
Get structured data instead of free-form text:
import { generateObject } from "ai";
import { z } from "zod";
const schema = z.object({
recipe: z.object({
name: z.string().describe("The title of the recipe"),
ingredients: z.array(
z.object({
name: z.string(),
amount: z.string(),
})
).describe("The ingredients needed for the recipe"),
steps: z.array(z.string()).describe("The steps to make the recipe")
})
});
const { object } = await generateObject({
model,
schema,
prompt: "How to make chocolate cake?",
schemaName: "Recipe"
});
console.log(object.recipe.name); // "Chocolate Cake"
Stream objects as they're being generated:
import { streamObject } from "ai";
const result = await streamObject({
model,
schema,
prompt: "How to make pancakes?"
});
// Stream partial objects
for await (const obj of result.partialObjectStream) {
// Update UI with partial object
updateUI(obj);
}
// Get final object
const finalObject = await result.object;
Classify text into predefined categories:
import { generateObject } from "ai";
const sentiment = await generateObject({
model,
output: "enum",
enum: ["positive", "negative", "neutral"],
prompt: "I love this product!",
system: "Classify the sentiment of the text."
});
console.log(sentiment.object); // "positive"
Generate arrays of structured data:
import { generateObject } from "ai";
const { object: users } = await generateObject({
model,
output: "array",
schema: z.object({
name: z.string(),
age: z.number(),
email: z.string().email()
}),
prompt: "Generate 5 fake users."
});
console.log(users); // Array of 5 user objects
Process and describe images:
import { generateText } from "ai";
import { readFileSync } from "fs";
// From local file
const imageData = readFileSync("./image.jpg");
const description = await generateText({
model,
system: "Describe this image concisely.",
messages: [{
role: "user",
content: [{ type: "image", image: imageData }]
}]
});
// From URL
const urlDescription = await generateText({
model,
system: "Describe this image concisely.",
messages: [{
role: "user",
// new URL is required
content: [{ type: "image", image: new URL("https://example.com/image.jpg") }]
}]
});
Extract structured data from PDFs:
import { generateObject } from "ai";
import { readFileSync } from "fs";
const { object } = await generateObject({
model,
schema: z.object({
invoiceNumber: z.string(),
total: z.number(),
currency: z.string(),
// Other fields...
}),
messages: [{
role: "user",
content: [{
type: "file",
data: readFileSync("./invoice.pdf"),
mimeType: "application/pdf"
}]
}]
});
Create embeddings for semantic search:
import { embedMany, embed, cosineSimilarity } from "ai";
import { openai } from "@ai-sdk/openai";
const embeddingModel = openai.embedding("text-embedding-3-small");
// Create embeddings for multiple items
const { embeddings } = await embedMany({
model: embeddingModel,
values: ["Dog", "Cat", "Car", "Bike"]
});
// Create embedding for search term
const searchTerm = await embed({
model: embeddingModel,
value: "Canine"
});
// Find similar items
const similarities = embeddings.map((embedding, index) => ({
value: values[index],
similarity: cosineSimilarity(embedding, searchTerm.embedding)
}));
const sortedResults = similarities.sort((a, b) => b.similarity - a.similarity);
Allow the model to call functions:
import { generateText, tool } from "ai";
import { z } from "zod";
// Define a tool
const getWeatherTool = tool({
description: "Get the current weather in a city",
parameters: z.object({
city: z.string().describe("The city to get weather for")
}),
execute: async ({ city }) => {
// Call a weather API
return `The weather in ${city} is 25°C and sunny.`;
}
});
// Use the tool
const { text, steps } = await generateText({
model,
prompt: "What's the weather in London?",
tools: {
getWeather: getWeatherTool
}
});
// See tool calls
console.log(steps[0].toolCalls);
Create agents that can use tools in a reasoning loop:
import { streamText, tool } from "ai";
const { textStream } = await streamText({
model,
prompt: "What's the weather in London and Paris?",
tools: {
getWeather: getWeatherTool
},
maxSteps: 3 // Allow multiple tool-calling steps
});
// Display the streaming result
for await (const text of textStream) {
process.stdout.write(text);
}
The maxSteps parameter determines how many reasoning steps the model can take. The model may stop earlier if it decides it has completed the task.