<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Streaming on RockB</title><link>https://baeseokjae.github.io/tags/streaming/</link><description>Recent content in Streaming on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Wed, 22 Apr 2026 05:37:21 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/streaming/index.xml" rel="self" type="application/rss+xml"/><item><title>Vercel AI SDK Guide 2026: Build Streaming AI Apps in TypeScript With One SDK</title><link>https://baeseokjae.github.io/posts/vercel-ai-sdk-guide-2026/</link><pubDate>Wed, 22 Apr 2026 05:37:21 +0000</pubDate><guid>https://baeseokjae.github.io/posts/vercel-ai-sdk-guide-2026/</guid><description>Complete guide to Vercel AI SDK in 2026 — streaming, tool calling, structured output, agents, and production deployment with code examples.</description><content:encoded><![CDATA[<p>The Vercel AI SDK is a unified TypeScript library that lets you build streaming AI applications across OpenAI, Anthropic, Google, and 13+ other providers without rewriting your core logic when you switch models. Install it once, pick your provider, and ship production-ready AI features in hours instead of days.</p>
<h2 id="what-is-the-vercel-ai-sdk-and-why-it-matters-in-2026">What Is the Vercel AI SDK and Why It Matters in 2026</h2>
<p>The Vercel AI SDK is an open-source TypeScript toolkit for building AI-powered web applications with a provider-agnostic API, first-class streaming support, and framework-native UI hooks. As of April 2026, it has 11.5 million weekly npm downloads, 23.7K GitHub stars, and 614+ contributors — making it the most widely adopted TypeScript AI library for web developers. The SDK is organized into three layers: AI SDK Core handles server-side text generation, object generation, and tool calling; AI SDK UI provides React/Vue/Svelte hooks like <code>useChat</code> and <code>useCompletion</code> for building chat interfaces without managing stream state; and AI SDK RSC integrates with React Server Components for edge-compatible generative UI. The SDK supports 100+ LLM models across 16+ providers via the Vercel AI Gateway, including OpenAI GPT-4o, Anthropic Claude, Google Gemini, and open models on Together/Groq. In 2026 Vercel added three major features on top: Workflows (long-running durable agents), Sandbox (secure agent code execution), and AI Elements (prebuilt UI components). OpenCode — one of the most popular open-source coding agents — is built entirely on AI SDK, which validates its production-grade viability.</p>
<h3 id="the-three-layer-architecture">The Three-Layer Architecture</h3>
<p>The SDK cleanly separates concerns: Core runs on the server or edge, UI runs on the client, and RSC bridges the two with streaming server components. This separation means you can adopt incrementally — start with Core for a simple API route, add UI hooks when you need chat state management, and layer in RSC if you need server-driven generative UI.</p>
<h3 id="how-it-fits-the-vercel-ecosystem">How It Fits the Vercel Ecosystem</h3>
<p>AI Gateway gives you one API key to access 100+ models with automatic fallbacks and rate limit management. Sandbox provides a secure Node.js environment for agents that need to execute code. Workflows lets agents suspend and resume across function invocations, solving the serverless timeout problem for long-running tasks.</p>
<h2 id="getting-started-installing-and-configuring-ai-sdk">Getting Started: Installing and Configuring AI SDK</h2>
<p>Getting started with the Vercel AI SDK requires installing the <code>ai</code> core package plus one or more provider adapters. The setup takes under five minutes for a Next.js project and works equally well in any Node.js or edge runtime environment. The provider adapter pattern is the key architectural decision: you import a model from its provider package and pass it to AI SDK functions, meaning you can swap from OpenAI to Anthropic by changing a single import and model string — your business logic stays untouched. This design was explicitly chosen to prevent vendor lock-in, and in practice it means you can A/B test models in production, build fallback chains, or migrate providers without refactoring your entire codebase. The package size is small — <code>ai</code> is under 200KB minified — and it is designed to run on Vercel Edge Functions, Cloudflare Workers, and standard Node.js without adaptation. For new projects, the recommended starting point is a Next.js App Router app with the <code>edge</code> runtime on API routes, which gives you global distribution and sub-100ms cold starts.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install ai @ai-sdk/openai @ai-sdk/anthropic @ai-sdk/google
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// .env.local
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">OPENAI_API_KEY</span><span style="color:#f92672">=</span><span style="color:#a6e22e">sk</span><span style="color:#f92672">-</span>...
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ANTHROPIC_API_KEY</span><span style="color:#f92672">=</span><span style="color:#a6e22e">sk</span><span style="color:#f92672">-</span><span style="color:#a6e22e">ant</span><span style="color:#f92672">-</span>...
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">GOOGLE_GENERATIVE_AI_API_KEY</span><span style="color:#f92672">=</span>...
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// app/api/chat/route.ts
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">streamText</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;ai&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@ai-sdk/openai&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">runtime</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;edge&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">POST</span>(<span style="color:#a6e22e">req</span>: <span style="color:#66d9ef">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">messages</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">json</span>()
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">streamText</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#39;gpt-4o&#39;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">messages</span>,
</span></span><span style="display:flex;"><span>  })
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">toDataStreamResponse</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="ai-gateway-one-key-for-100-models">AI Gateway: One Key for 100+ Models</h3>
<p>Vercel AI Gateway lets you use a single <code>VERCEL_API_KEY</code> to access models from OpenAI, Anthropic, Google, Mistral, and more. It handles rate limit rotation, cost tracking, and automatic retry logic. For teams that need to experiment with multiple providers without managing individual API key billing, Gateway is the fastest path to a multi-model setup.</p>
<h2 id="ai-sdk-core-text-generation-and-streaming">AI SDK Core: Text Generation and Streaming</h2>
<p>AI SDK Core is the server-side engine that converts provider-specific APIs into a consistent interface for generating text, streaming responses, and calling tools. The two primary functions are <code>generateText</code> and <code>streamText</code>. <code>generateText</code> is for synchronous operations — you send a prompt and wait for the full response, which is ideal for batch jobs, summarization pipelines, and any context where the user is not watching a UI render in real time. <code>streamText</code> is the streaming counterpart: it returns a <code>ReadableStream</code> that you can pipe directly to a <code>Response</code> object, and it integrates with UI hooks via the <code>toDataStreamResponse()</code> method. Both functions accept the same options object — <code>model</code>, <code>messages</code>, <code>system</code>, <code>tools</code>, <code>maxSteps</code>, <code>temperature</code>, and more — so switching between them is a one-word change. Provider switching is similarly simple: swapping <code>openai('gpt-4o')</code> for <code>anthropic('claude-opus-4-7')</code> is the only change needed. Retry logic and fallbacks are handled with the <code>wrapLanguageModel</code> utility and the <code>fallback</code> provider, which tries a list of models in order if the primary returns an error. The consistency across providers is the single biggest productivity gain AI SDK offers compared to using provider SDKs directly.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">generateText</span>, <span style="color:#a6e22e">streamText</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;ai&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">anthropic</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@ai-sdk/anthropic&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// One-shot generation
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">text</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">generateText</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">anthropic</span>(<span style="color:#e6db74">&#39;claude-sonnet-4-6&#39;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">prompt</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Summarize the key features of React 19 in 3 bullet points.&#39;</span>,
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Streaming
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">streamText</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">anthropic</span>(<span style="color:#e6db74">&#39;claude-sonnet-4-6&#39;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">prompt</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Write a guide on async/await in TypeScript.&#39;</span>,
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#66d9ef">await</span> (<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">chunk</span> <span style="color:#66d9ef">of</span> <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">textStream</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">stdout</span>.<span style="color:#a6e22e">write</span>(<span style="color:#a6e22e">chunk</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="built-in-fallbacks-and-retry-logic">Built-In Fallbacks and Retry Logic</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">createFallback</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@ai-sdk/provider-utils&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@ai-sdk/openai&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">anthropic</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@ai-sdk/anthropic&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">resilientModel</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createFallback</span>([
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">openai</span>(<span style="color:#e6db74">&#39;gpt-4o&#39;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">anthropic</span>(<span style="color:#e6db74">&#39;claude-sonnet-4-6&#39;</span>),
</span></span><span style="display:flex;"><span>])
</span></span></code></pre></div><h2 id="structured-output-with-zod-schemas">Structured Output with Zod Schemas</h2>
<p><code>generateObject</code> and <code>streamObject</code> are AI SDK&rsquo;s solution to one of the biggest pain points in production AI: getting reliable, type-safe structured data from LLMs instead of freeform text that you then parse with fragile regexes. These functions accept a Zod schema and use the model&rsquo;s native structured output mode — JSON mode for OpenAI, tool-use-based extraction for Anthropic — to guarantee the response matches the schema shape. If the model returns malformed output, AI SDK retries automatically. This is not just a developer convenience: structured output is essential for any AI pipeline where the response feeds into downstream logic, databases, or APIs. Teams using <code>generateObject</code> in production report near-zero JSON parsing errors compared to prompt-based extraction, and the Zod types flow through the entire TypeScript type system so you get autocomplete on the AI response object. The <code>streamObject</code> variant lets you stream partial structured objects, enabling progressive UI rendering as the AI fills in fields — useful for forms, dashboards, or any interface where showing partial data is better than a blank loading state. For data extraction tasks — pulling product specs from HTML, extracting entities from documents, or parsing unstructured API responses — structured output with Zod is the recommended production approach.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">generateObject</span>, <span style="color:#a6e22e">streamObject</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;ai&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@ai-sdk/openai&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;zod&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">BlogPostSchema</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">z</span>.<span style="color:#66d9ef">object</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">title</span>: <span style="color:#66d9ef">z.string</span>(),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">summary</span>: <span style="color:#66d9ef">z.string</span>().<span style="color:#a6e22e">max</span>(<span style="color:#ae81ff">200</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">tags</span>: <span style="color:#66d9ef">z.array</span>(<span style="color:#a6e22e">z</span>.<span style="color:#66d9ef">string</span>()).<span style="color:#a6e22e">max</span>(<span style="color:#ae81ff">5</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">seoScore</span>: <span style="color:#66d9ef">z.number</span>().<span style="color:#a6e22e">min</span>(<span style="color:#ae81ff">0</span>).<span style="color:#a6e22e">max</span>(<span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">sections</span>: <span style="color:#66d9ef">z.array</span>(<span style="color:#a6e22e">z</span>.<span style="color:#66d9ef">object</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">heading</span>: <span style="color:#66d9ef">z.string</span>(),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">keyPoints</span>: <span style="color:#66d9ef">z.array</span>(<span style="color:#a6e22e">z</span>.<span style="color:#66d9ef">string</span>()),
</span></span><span style="display:flex;"><span>  })),
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">object</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">generateObject</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#39;gpt-4o&#39;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">schema</span>: <span style="color:#66d9ef">BlogPostSchema</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">prompt</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Analyze this article and return structured metadata: ...&#39;</span>,
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#66d9ef">object</span>.<span style="color:#a6e22e">title</span>) <span style="color:#75715e">// TypeScript knows the full type
</span></span></span></code></pre></div><h3 id="streaming-structured-objects">Streaming Structured Objects</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">partialObjectStream</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">streamObject</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#39;gpt-4o&#39;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">schema</span>: <span style="color:#66d9ef">BlogPostSchema</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">prompt</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Generate a blog post outline for: &#34;AI agents in 2026&#34;&#39;</span>,
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#66d9ef">await</span> (<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">partial</span> <span style="color:#66d9ef">of</span> <span style="color:#a6e22e">partialObjectStream</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// partial.title appears as soon as the model generates it
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#a6e22e">updateUI</span>(<span style="color:#a6e22e">partial</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="building-chat-uis-with-ai-sdk-ui-hooks">Building Chat UIs with AI SDK UI Hooks</h2>
<p>AI SDK UI is the client-side complement to Core, providing React hooks that manage chat state, streaming responses, and optimistic updates without requiring a single <code>useState</code> or <code>useEffect</code> for stream handling. The primary hook is <code>useChat</code>, which gives you <code>messages</code>, <code>input</code>, <code>handleInputChange</code>, <code>handleSubmit</code>, and <code>isLoading</code> — everything needed to build a ChatGPT-like interface in under 50 lines of React. Under the hood it connects to your AI route, handles stream parsing, and appends message chunks to state as they arrive. The <code>useCompletion</code> hook handles text completion use cases — autocomplete, writing suggestions, or any single-prompt UX. <code>useObject</code> streams structured objects from a <code>streamObject</code> route and exposes the partial object as it builds, enabling progressive form filling or AI-driven dashboard updates. All three hooks work with React, Vue, Svelte, and SolidJS — the framework-agnostic design means you can share backend patterns between projects built on different frontend stacks. The hooks integrate with React Suspense and Error Boundaries for graceful loading and error states without extra wiring.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// app/components/Chat.tsx
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#e6db74">&#39;use client&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">useChat</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;ai/react&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">Chat() {</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">input</span>, <span style="color:#a6e22e">handleInputChange</span>, <span style="color:#a6e22e">handleSubmit</span>, <span style="color:#a6e22e">isLoading</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">useChat</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">api</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;/api/chat&#39;</span>,
</span></span><span style="display:flex;"><span>  })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> (
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>      {<span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">map</span>(<span style="color:#a6e22e">m</span> <span style="color:#f92672">=&gt;</span> (
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">key</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">id</span>} <span style="color:#a6e22e">className</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">role</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#39;user&#39;</span> <span style="color:#f92672">?</span> <span style="color:#e6db74">&#39;user&#39;</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;assistant&#39;</span>}&gt;
</span></span><span style="display:flex;"><span>          {<span style="color:#a6e22e">m</span>.<span style="color:#a6e22e">content</span>}
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>      ))}
</span></span><span style="display:flex;"><span>      &lt;<span style="color:#f92672">form</span> <span style="color:#a6e22e">onSubmit</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">handleSubmit</span>}&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">input</span> <span style="color:#a6e22e">value</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">input</span>} <span style="color:#a6e22e">onChange</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">handleInputChange</span>} <span style="color:#a6e22e">disabled</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">isLoading</span>} /&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">button</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;submit&#34;</span> <span style="color:#a6e22e">disabled</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">isLoading</span>}&gt;<span style="color:#a6e22e">Send</span>&lt;/<span style="color:#f92672">button</span>&gt;
</span></span><span style="display:flex;"><span>      &lt;/<span style="color:#f92672">form</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  )
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="framework-support-comparison">Framework Support Comparison</h3>
<table>
  <thead>
      <tr>
          <th>Hook</th>
          <th>React</th>
          <th>Vue</th>
          <th>Svelte</th>
          <th>SolidJS</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>useChat</code></td>
          <td>✅</td>
          <td>✅</td>
          <td>✅</td>
          <td>✅</td>
      </tr>
      <tr>
          <td><code>useCompletion</code></td>
          <td>✅</td>
          <td>✅</td>
          <td>✅</td>
          <td>✅</td>
      </tr>
      <tr>
          <td><code>useObject</code></td>
          <td>✅</td>
          <td>✅</td>
          <td>✅</td>
          <td>✅</td>
      </tr>
      <tr>
          <td><code>useAssistant</code></td>
          <td>✅</td>
          <td>❌</td>
          <td>❌</td>
          <td>❌</td>
      </tr>
  </tbody>
</table>
<h2 id="tool-calling-giving-your-ai-agent-superpowers">Tool Calling: Giving Your AI Agent Superpowers</h2>
<p>Tool calling in AI SDK is the mechanism that transforms a passive text generator into an active agent — the model describes which tools it wants to invoke, AI SDK executes them server-side, and the results feed back into the next model turn automatically. Tools are defined with the <code>tool</code> helper, which takes a <code>description</code> (natural language explanation for the model), <code>parameters</code> (a Zod schema for typed inputs), and an <code>execute</code> function (the actual implementation). The SDK handles the full tool-call cycle: formatting the tool description for the provider, parsing the model&rsquo;s structured tool-call output, executing the function with validated arguments, and appending the result to the conversation context. <code>maxSteps</code> controls how many tool-call cycles the agent can run before stopping, preventing infinite loops while allowing multi-step reasoning chains of 5–10 steps. Tool results stream to the client via <code>toDataStreamResponse()</code>, so users see intermediate tool outputs in real time rather than waiting for the final answer. In production applications, tool sets commonly include database query tools, web search, calculator functions, external API calls, and file operations — anything your server-side code can do, the agent can orchestrate. The Zod parameter schemas provide input validation at zero extra cost, catching malformed tool calls before they reach your database or external services.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">streamText</span>, <span style="color:#a6e22e">tool</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;ai&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">openai</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@ai-sdk/openai&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">z</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;zod&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">streamText</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#39;gpt-4o&#39;</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">maxSteps</span>: <span style="color:#66d9ef">5</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">tools</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">getWeather</span>: <span style="color:#66d9ef">tool</span>({
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Get current weather for a city&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">parameters</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">city</span>: <span style="color:#66d9ef">z.string</span>() }),
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">city</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">res</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#e6db74">`https://api.weather.com/</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">city</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">json</span>()
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>    }),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">searchDatabase</span>: <span style="color:#66d9ef">tool</span>({
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">description</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Search the product database&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">parameters</span>: <span style="color:#66d9ef">z.object</span>({ <span style="color:#a6e22e">query</span>: <span style="color:#66d9ef">z.string</span>(), <span style="color:#a6e22e">limit</span>: <span style="color:#66d9ef">z.number</span>().<span style="color:#66d9ef">default</span>(<span style="color:#ae81ff">5</span>) }),
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">execute</span>: <span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">query</span>, <span style="color:#a6e22e">limit</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">products</span>.<span style="color:#a6e22e">search</span>(<span style="color:#a6e22e">query</span>, { <span style="color:#a6e22e">limit</span> })
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>    }),
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [{ <span style="color:#a6e22e">role</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#a6e22e">content</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;What is the weather in Tokyo and do we sell umbrellas?&#39;</span> }],
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">toDataStreamResponse</span>()
</span></span></code></pre></div><h3 id="multi-step-agent-loop">Multi-Step Agent Loop</h3>
<p>With <code>maxSteps: 5</code>, the model can: call <code>getWeather</code> → get Tokyo weather → call <code>searchDatabase</code> for umbrellas → combine results → return final answer. Each step is visible to the user via streaming tool call indicators rendered automatically by <code>useChat</code>.</p>
<h2 id="building-ai-agents-with-multi-step-reasoning">Building AI Agents with Multi-Step Reasoning</h2>
<p>An AI agent in the Vercel AI SDK context is a <code>streamText</code> or <code>generateText</code> call with tools enabled and <code>maxSteps</code> set above 1 — the model reasons, calls tools, observes results, and reasons again until it reaches a conclusion or exhausts its step budget. This loop pattern is what separates agents from chatbots: rather than answering from static training knowledge, the agent actively queries databases, fetches URLs, or calls APIs to gather real-time information before formulating a response. The key to a production-grade agent is memory and context management: you control what goes in <code>messages</code>, so you can implement sliding window context, summarization, or retrieval-augmented generation by fetching relevant documents before calling the model. For RAG integration, the standard pattern is to embed the user query, retrieve top-k chunks from a vector store (Pinecone, Supabase pgvector, or Upstash), and prepend them as a system message. The agent then has access to both retrieved context and its tool-calling ability, so it can fetch additional information if the retrieved chunks are insufficient. OpenCode&rsquo;s architecture demonstrates this at scale: it uses AI SDK with file system tools, runs multi-step reasoning loops to understand a codebase, and streams results back to a terminal UI — all without custom streaming infrastructure because AI SDK handles it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Research agent with RAG
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">researchAgent</span>(<span style="color:#a6e22e">query</span>: <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">relevantDocs</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">vectorStore</span>.<span style="color:#a6e22e">search</span>(<span style="color:#a6e22e">query</span>, { <span style="color:#a6e22e">topK</span>: <span style="color:#66d9ef">5</span> })
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">streamText</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#39;gpt-4o&#39;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">maxSteps</span>: <span style="color:#66d9ef">8</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">system</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`You are a research agent. Use context and tools to answer thoroughly.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Context from knowledge base:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"></span><span style="color:#e6db74">${</span><span style="color:#a6e22e">relevantDocs</span>.<span style="color:#a6e22e">map</span>(<span style="color:#a6e22e">d</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">content</span>).<span style="color:#a6e22e">join</span>(<span style="color:#e6db74">&#39;\n\n&#39;</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">messages</span><span style="color:#f92672">:</span> [{ <span style="color:#a6e22e">role</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#a6e22e">content</span>: <span style="color:#66d9ef">query</span> }],
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tools</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">searchWeb</span>: <span style="color:#66d9ef">tool</span>({ <span style="color:#75715e">/* web search implementation */</span> }),
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">fetchUrl</span>: <span style="color:#66d9ef">tool</span>({ <span style="color:#75715e">/* URL fetching implementation */</span> }),
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">saveNote</span>: <span style="color:#66d9ef">tool</span>({ <span style="color:#75715e">/* note saving implementation */</span> }),
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  })
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">toDataStreamResponse</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="vercel-workflows-long-running-agents-that-survive">Vercel Workflows: Long-Running Agents That Survive</h2>
<p>Vercel Workflows is a 2026 addition to the AI SDK ecosystem that solves the most critical limitation of serverless AI agents: function timeout. Standard serverless functions on Vercel time out after 30 seconds (Pro plan) or 5 minutes (Enterprise), which is insufficient for agents that need to search the web, process large documents, run multi-stage pipelines, or wait for human approval. Workflows introduces durable execution — agent tasks are broken into named steps that can suspend (persist state to managed storage), wait for external events, and resume exactly where they left off across multiple function invocations without losing context. This makes genuinely complex agentic pipelines feasible on serverless infrastructure: a content generation pipeline can run for 20+ minutes as it researches, drafts, and revises content, with the agent suspending between phases. The <code>@vercel/workflows</code> package integrates directly with AI SDK&rsquo;s <code>generateText</code> and <code>streamText</code> — you wrap agent logic in a <code>workflow</code> function and use <code>step.run()</code> to define resumable checkpoints. Human-in-the-loop approval is supported via <code>step.waitForEvent()</code>, which suspends the workflow until a webhook fires. In 2026, Workflows is the recommended architecture for any AI task that may exceed 30 seconds or requires coordination between multiple agents.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">workflow</span>, <span style="color:#a6e22e">step</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@vercel/workflows&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">generateText</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;ai&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">anthropic</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@ai-sdk/anthropic&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">contentPipeline</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">workflow</span>(<span style="color:#66d9ef">async</span> ({ <span style="color:#a6e22e">input</span> }<span style="color:#f92672">:</span> { <span style="color:#a6e22e">input</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">topic</span>: <span style="color:#66d9ef">string</span> } }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Each step is resumable — survives function timeout
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">research</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">step</span>.<span style="color:#a6e22e">run</span>(<span style="color:#e6db74">&#39;research&#39;</span>, <span style="color:#66d9ef">async</span> () <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">text</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">generateText</span>({
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">anthropic</span>(<span style="color:#e6db74">&#39;claude-opus-4-7&#39;</span>),
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">prompt</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`Research: </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">input</span>.<span style="color:#a6e22e">topic</span><span style="color:#e6db74">}</span><span style="color:#e6db74">. Return key facts and sources.`</span>,
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">text</span>
</span></span><span style="display:flex;"><span>  })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">draft</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">step</span>.<span style="color:#a6e22e">run</span>(<span style="color:#e6db74">&#39;draft&#39;</span>, <span style="color:#66d9ef">async</span> () <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">text</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">generateText</span>({
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">anthropic</span>(<span style="color:#e6db74">&#39;claude-sonnet-4-6&#39;</span>),
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">prompt</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`Using this research: </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">research</span><span style="color:#e6db74">}</span><span style="color:#960050;background-color:#1e0010">\</span><span style="color:#e6db74">n</span><span style="color:#960050;background-color:#1e0010">\</span><span style="color:#e6db74">nWrite a 1000-word article about </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">input</span>.<span style="color:#a6e22e">topic</span><span style="color:#e6db74">}</span><span style="color:#e6db74">.`</span>,
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">text</span>
</span></span><span style="display:flex;"><span>  })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> { <span style="color:#a6e22e">research</span>, <span style="color:#a6e22e">draft</span> }
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><h2 id="production-deployment-and-scaling">Production Deployment and Scaling</h2>
<p>Deploying a Vercel AI SDK application to production requires careful attention to runtime selection, cost management, and observability. For runtime selection, Edge Functions are the right choice for streaming chat routes because they have lower cold-start latency and are globally distributed across 30+ regions — users in Tokyo get a fast response without routing to a US datacenter. Node.js runtime is better for heavy tool execution, large file processing, or anything requiring Node-specific APIs. Cost management starts with the <code>maxTokens</code> parameter to cap spending per request, and AI Gateway adds team-level spend limits and per-model cost tracking with dashboards. For rate limiting on API routes, <code>@vercel/kv</code> with a sliding window counter is the standard pattern: each user or IP gets N requests per minute, excess requests return 429 with a <code>retry-after</code> header. Observability is critical for catching silent model failures: the <code>onFinish</code> callback in <code>streamText</code> and <code>generateText</code> lets you log token usage, model name, latency, and finish reason to your analytics pipeline, enabling cost attribution per feature and alerting on abnormal token consumption. Vercel&rsquo;s built-in function logs surface AI SDK error events automatically for debugging.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">POST</span>(<span style="color:#a6e22e">req</span>: <span style="color:#66d9ef">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">messages</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">json</span>()
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Rate limiting check
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ip</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">headers</span>.<span style="color:#66d9ef">get</span>(<span style="color:#e6db74">&#39;x-forwarded-for&#39;</span>) <span style="color:#f92672">??</span> <span style="color:#e6db74">&#39;anonymous&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">success</span> } <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">limit</span>(<span style="color:#a6e22e">ip</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#a6e22e">success</span>) <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Response</span>(<span style="color:#e6db74">&#39;Rate limit exceeded&#39;</span>, { <span style="color:#a6e22e">status</span>: <span style="color:#66d9ef">429</span> })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">streamText</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">openai</span>(<span style="color:#e6db74">&#39;gpt-4o&#39;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">messages</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">maxTokens</span>: <span style="color:#66d9ef">2000</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">temperature</span>: <span style="color:#66d9ef">0.7</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">onFinish</span><span style="color:#f92672">:</span> ({ <span style="color:#a6e22e">usage</span>, <span style="color:#a6e22e">finishReason</span> }) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">analytics</span>.<span style="color:#a6e22e">track</span>(<span style="color:#e6db74">&#39;ai_completion&#39;</span>, {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">tokens</span>: <span style="color:#66d9ef">usage.totalTokens</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">finishReason</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">model</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;gpt-4o&#39;</span>,
</span></span><span style="display:flex;"><span>      })
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  })
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">toDataStreamResponse</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="ai-sdk-vs-langchain-vs-mastra-framework-comparison">AI SDK vs LangChain vs Mastra: Framework Comparison</h2>
<p>Choosing between Vercel AI SDK, LangChain.js, and Mastra in 2026 depends primarily on your stack, agent complexity, and how important streaming and bundle size are to your application. Vercel AI SDK is the right choice for TypeScript web developers building streaming-first applications — it is the lightest of the three (under 200KB), has the best Next.js and Edge Function integration, and provides the most seamless streaming API with minimal boilerplate. LangChain.js has the broadest ecosystem: pre-built chains, 50+ vector store integrations, document loaders, memory modules, and a large community cookbook — making it better for teams needing to quickly assemble complex RAG pipelines from components rather than writing integration code themselves. Mastra, which emerged in late 2025, sits between the two: TypeScript-native like AI SDK but with an opinionated agent framework including built-in memory, durable workflow primitives, and multi-agent coordination, targeting developers who need more structure than AI SDK provides without LangChain&rsquo;s abstraction overhead. The bundle size difference is meaningful for edge and browser deployments where LangChain.js&rsquo;s 2MB+ footprint can impact cold start times. For most Next.js applications in 2026, AI SDK is the practical default and LangChain or Mastra are reached for only when specific missing features justify the additional complexity.</p>
<table>
  <thead>
      <tr>
          <th>Feature</th>
          <th>Vercel AI SDK</th>
          <th>LangChain.js</th>
          <th>Mastra</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Bundle size</td>
          <td>~200KB</td>
          <td>~2MB+</td>
          <td>~500KB</td>
      </tr>
      <tr>
          <td>Streaming</td>
          <td>First-class</td>
          <td>Good</td>
          <td>Good</td>
      </tr>
      <tr>
          <td>Tool calling</td>
          <td>Native</td>
          <td>Via chains</td>
          <td>Native</td>
      </tr>
      <tr>
          <td>Structured output</td>
          <td>Zod-native</td>
          <td>Manual</td>
          <td>Zod-native</td>
      </tr>
      <tr>
          <td>Long-running agents</td>
          <td>Via Workflows</td>
          <td>Partial</td>
          <td>Built-in</td>
      </tr>
      <tr>
          <td>Next.js/Edge</td>
          <td>Excellent</td>
          <td>Moderate</td>
          <td>Good</td>
      </tr>
      <tr>
          <td>Pre-built integrations</td>
          <td>16+ providers</td>
          <td>50+</td>
          <td>20+</td>
      </tr>
      <tr>
          <td>TypeScript types</td>
          <td>Excellent</td>
          <td>Good</td>
          <td>Excellent</td>
      </tr>
      <tr>
          <td>Learning curve</td>
          <td>Low</td>
          <td>High</td>
          <td>Medium</td>
      </tr>
  </tbody>
</table>
<h2 id="faq">FAQ</h2>
<p><strong>What is the difference between AI SDK Core and AI SDK UI?</strong>
AI SDK Core (<code>generateText</code>, <code>streamText</code>, <code>generateObject</code>) runs on the server and handles model calls. AI SDK UI (<code>useChat</code>, <code>useCompletion</code>, <code>useObject</code>) runs on the client and manages stream state, message history, and UI updates. In a Next.js app, Core lives in <code>app/api/</code> routes and UI hooks live in client components. You can use Core without UI (for backend pipelines) but UI requires a Core-powered API endpoint.</p>
<p><strong>Can I use Vercel AI SDK without Vercel hosting?</strong>
Yes. The AI SDK is a pure npm package with no dependency on Vercel&rsquo;s infrastructure. You can use it in any Node.js server, AWS Lambda, Cloudflare Workers, or on-premise environment. Vercel-specific features like Workflows and AI Gateway require Vercel hosting, but AI SDK Core and UI work on any JavaScript runtime.</p>
<p><strong>How do I switch between AI providers in Vercel AI SDK?</strong>
Change one line: swap the model import and the model string. Replace <code>openai('gpt-4o')</code> with <code>anthropic('claude-sonnet-4-6')</code>. The rest of your code — messages, tools, streaming — stays identical. This is the main design goal: provider portability without refactoring business logic.</p>
<p><strong>What is the recommended way to add memory to an AI SDK agent?</strong>
The SDK does not manage memory itself — you control <code>messages</code>. Store conversation history in a database (KV, Postgres, or Upstash), retrieve the last N turns before each request, and pass them as <code>messages</code>. For long-term memory across sessions, embed user facts and retrieve them via vector search, prepending relevant memories to the system prompt before each request.</p>
<p><strong>Does Vercel AI SDK support multi-modal inputs like images and PDFs?</strong>
Yes. Models that support vision (GPT-4o, Claude Opus 4, Gemini Pro Vision) accept <code>content</code> arrays with <code>{ type: 'image', image: url }</code> or <code>{ type: 'file', data: base64, mimeType: 'application/pdf' }</code> parts alongside text. AI SDK normalizes these into the provider&rsquo;s expected format automatically, so you write the same code regardless of which vision model you use.</p>
]]></content:encoded></item></channel></rss>