Large language models (LLMs) understand and generate text from prompts. The Vercel AI SDK is a provider-agnostic layer over LLM APIs - core functions are generateText, streamText, and embed. This post uses the OpenAI provider and mirrors the patterns from the OpenAI Responses API post.
For the lower-level openai npm package, see the Chat Completions API and Responses API posts.
Prerequisites
- OpenAI account
- Generated API key
- Enabled billing
- Node.js version 26
-
ai,@ai-sdk/openai, andzodinstalled (npm i ai @ai-sdk/openai zod) - For Markdown output:
marked,dompurify, andjsdom(npm i marked dompurify jsdom)
Client setup
Create an OpenAI provider with your API key (read from the environment in production).
import { createOpenAI } from '@ai-sdk/openai';
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });
The same provider can target other hosts that implement a compatible API by setting baseURL and apiKey:
const openai = createOpenAI({
apiKey: process.env.LLM_API_KEY,
baseURL: 'https://your-gateway.example/v1',
});
Many third-party gateways support Chat Completions only. The examples below use openai(model) (Responses API path). If your provider does not support it, switch to openai.chat(model) and skip the web search example.
Basic integration
Pass a string as prompt and read text from the result.
import { generateText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });
const { text } = await generateText({
model: openai('gpt-5.5'),
prompt: 'Write a one-sentence bedtime story about a unicorn.',
});
console.log(text);
System prompt
Use the system parameter for stable behavior (tone, format, role). It takes precedence over casual wording in the user message.
const { text } = await generateText({
model: openai('gpt-5.5'),
system: 'Reply in one short sentence. Use plain language.',
prompt: 'Explain what an LLM is.',
});
console.log(text);
Few-shot prompting
Pass prior turns in a messages array with user and assistant roles, then the new user message. Keep task rules in system.
const { text } = await generateText({
model: openai('gpt-5.5'),
system:
'Classify sentiment as exactly one word: positive, negative, or neutral.',
messages: [
{ role: 'user', content: 'I love this!' },
{ role: 'assistant', content: 'positive' },
{ role: 'user', content: 'This is awful.' },
{ role: 'assistant', content: 'negative' },
{ role: 'user', content: 'It is fine I guess.' },
],
});
console.log(text);
Streaming
Use streamText and iterate over textStream for incremental text.
import { streamText } from 'ai';
const result = streamText({
model: openai('gpt-5.5'),
prompt: 'List three colors.',
});
for await (const part of result.textStream) {
process.stdout.write(part);
}
Structured output with JSON schema
Constrain the model to JSON matching your schema via Output.object() and a Zod schema. The SDK validates the result.
import { generateText, Output } from 'ai';
import { z } from 'zod';
const { output } = await generateText({
model: openai('gpt-5.5'),
prompt: 'The film Inception was directed by Christopher Nolan.',
output: Output.object({
schema: z.object({
title: z.string(),
director: z.string(),
}),
schemaName: 'movie_summary',
}),
});
console.log(output.title, output.director);
Markdown output to HTML
Ask for Markdown in system, then convert text to HTML and sanitize before rendering (for example with innerHTML in the browser or when storing HTML).
import { marked } from 'marked';
import { JSDOM } from 'jsdom';
import DOMPurify from 'dompurify';
const purify = DOMPurify(new JSDOM('').window);
const { text } = await generateText({
model: openai('gpt-5.5'),
system: 'Reply in Markdown only. Use a heading and a short bullet list.',
prompt: 'Explain what an LLM is in three bullet points.',
});
const markdown = text;
const html = marked.parse(markdown);
const safeHtml = purify.sanitize(html);
Always run DOMPurify.sanitize on model-generated HTML. The model can emit unsafe markup. Sanitization strips scripts and other dangerous content.
Web search tool
Enable the built-in web search tool when the answer should use current information from the web.
const result = await generateText({
model: openai('gpt-5.5'),
tools: { web_search: openai.tools.webSearch() },
prompt: 'What was a major tech headline this week? Cite sources briefly.',
});
console.log(result.text);
Web search adds latency and tool usage cost. Use a model that supports tools.
Embeddings
Embeddings are numeric vectors that represent the semantic meaning of text. Use them for semantic search, clustering, and RAG.
Pass a single string to embed and read the vector from embedding.
import { embed } from 'ai';
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: 'How do I connect pgvector to PostgreSQL?',
});
console.log(embedding.length);
Pass multiple strings in a values array with embedMany. Results are in the same order as the input.
import { embedMany } from 'ai';
const chunks = [
'pgvector adds vector similarity search to PostgreSQL.',
'LangChain helps split long documents into retrieval-friendly chunks.',
'RAG retrieves context first, then asks an LLM to answer.',
];
const { embeddings } = await embedMany({
model: openai.embedding('text-embedding-3-small'),
values: chunks,
});
console.log(embeddings.length); // 3
For a full RAG flow with pgvector, see the RAG with OpenAI embeddings post.
Demo
Runnable scripts for each section live in the vercel-ai-sdk-demo folder. Get access via code demos.
Top comments (0)