mrkeyoor.com_
Wed 05 Aug 05:03 UTC
npmAI / MLupdated 05 Aug 2026

ai

The AI SDK is Vercel's provider-agnostic TypeScript toolkit for building LLM applications and agents. One API (generateText, streamText, structured output via zod schemas, tool calls) works across OpenAI, Anthropic, Google, and other providers: either through the Vercel AI Gateway with plain model strings like 'anthropic/claude-opus-4.6', or through per-provider packages such as @ai-sdk/openai. Companion UI packages add useChat-style hooks for React, Svelte, Vue, and Angular, and the ToolLoopAgent class runs multi-step tool loops server side with a typed message protocol down to the browser.

Verdict

The default for TypeScript LLM apps, especially anything with streaming chat UI in Next.js. You pay for its pace: pin versions and read the migration notes on every major.

API stability3/5Seven majors in roughly three years; the generateText/streamText core endures but message types, tool definitions, and the UI protocol have all moved between majors
Docs4/5ai-sdk.dev has thorough guides, a full API reference, and starter templates; the version churn means older blog posts and examples on the web go stale fast
Maintenance5/5Vercel-staffed with pushes landing daily and a rapid patch cadence (7.0.52 current); the roughly 1,750 open issues and PRs track heavy usage, not neglect
Ecosystem4/520M weekly downloads, first-party provider packages for every major model vendor plus community providers and framework hooks; younger and thinner than the raw provider SDK ecosystems

Use it if

  • You want to swap model providers without rewriting call sites: the model is a string or provider function and the surrounding code stays identical
  • You are building chat UI in Next.js, React, Svelte, or Vue and want streaming, message state, and tool rendering handled by useChat instead of hand-rolled SSE
  • You need typed structured output: zod schema in, validated object out
  • You are building tool-using agents and want the tool loop, streaming, and UI message protocol as one coherent stack
Skip it if

Setup reality

npm install ai requires Node 22+. The quick path routes calls through the Vercel AI Gateway using bare model strings, which needs a gateway credential; direct provider access means installing @ai-sdk/openai, @ai-sdk/anthropic, and so on separately, each reading its own API key env var. UI hooks live in yet another package per framework (@ai-sdk/react and friends). The recurring cost is churn: majors arrive fast, and message formats, tool definitions, and helper names have all been renamed across versions, so pin your version and budget upgrade time.

Patterns

Generate text with a gateway model stringgenerate-text

import { generateText } from 'ai';

const { text } = await generateText({
  model: 'openai/gpt-5.4', // Vercel AI Gateway model string
  prompt: 'What is an agent?',
});

Gateway model strings need no provider package installed, but they do need an AI Gateway credential in the environment.

Stream a completion to stdoutstream-text

import { streamText } from 'ai';

const result = streamText({
  model: 'anthropic/claude-opus-4.6',
  prompt: 'Explain backpressure in one paragraph.',
});

for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

streamText starts immediately and does not reject on model errors; errors arrive as part of the stream, so try/catch alone will not catch them.

Get validated structured output with zodstructured-output

import { generateText, Output } from 'ai';
import { z } from 'zod';

const { output } = await generateText({
  model: 'openai/gpt-5.4',
  output: Output.object({
    schema: z.object({
      recipe: z.object({
        name: z.string(),
        steps: z.array(z.string()),
      }),
    }),
  }),
  prompt: 'Generate a lasagna recipe.',
});

Read the result from output, not text; schema validation failures throw, so wrap strict schemas in try/catch.

Define a typed tooldefine-tool

import { tool } from 'ai';
import { z } from 'zod';

const getWeather = tool({
  description: 'Get current weather for a city',
  inputSchema: z.object({ city: z.string() }),
  execute: async ({ city }) => ({ city, tempC: 21 }),
});

The schema field is inputSchema; older examples on the web still show parameters, which no longer type-checks.

Run a multi-step agent with ToolLoopAgentagent-tool-loop

import { ToolLoopAgent } from 'ai';

const agent = new ToolLoopAgent({
  model: 'openai/gpt-5.4',
  system: 'You are an agent with access to a shell environment.',
  tools: { shell: myShellTool },
});

The agent keeps calling tools until the model stops requesting them; guard expensive tools with your own limits.

Serve an agent from a Next.js routeagent-route-handler

// app/api/chat/route.ts
import { createAgentUIStreamResponse } from 'ai';
import { agent } from '@/agent';

export async function POST(req: Request) {
  const { messages } = await req.json();
  return createAgentUIStreamResponse({ agent, messages });
}

useChat posts to /api/chat by default; if you change the route path, configure the hook to match.

Build a chat UI with useChatchat-ui-react

'use client';
import { useChat } from '@ai-sdk/react';
import { useState } from 'react';

export default function Chat() {
  const { messages, status, sendMessage } = useChat();
  const [input, setInput] = useState('');

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        sendMessage({ text: input });
        setInput('');
      }}
    >
      {messages.map((m) => (
        <div key={m.id}>
          {m.parts.map((part, i) =>
            part.type === 'text' ? <span key={i}>{part.text}</span> : null,
          )}
        </div>
      ))}
      <input
        value={input}
        onChange={(e) => setInput(e.target.value)}
        disabled={status !== 'ready'}
      />
    </form>
  );
}

Messages are arrays of typed parts, not plain strings; render by switching on part.type or tool parts silently disappear.

Render a tool call in the UI by staterender-tool-invocation

import { UIToolInvocation } from 'ai';

function ToolView({ invocation }: { invocation: UIToolInvocation<any> }) {
  switch (invocation.state) {
    case 'input-available':
      return <div>Running tool...</div>;
    case 'output-available':
      return <pre>{JSON.stringify(invocation.output, null, 2)}</pre>;
  }
  return null;
}

Tool parts move through states (input-available, then output-available); render every state or users see nothing while a tool runs.

Call a provider package directlydirect-provider

import { anthropic } from '@ai-sdk/anthropic';
import { generateText } from 'ai';

const result = await generateText({
  model: anthropic('claude-opus-4-6'),
  prompt: 'Hello!',
});

Provider packages are separate installs and read their own env vars (ANTHROPIC_API_KEY here); the gateway is bypassed entirely.

Use a system prompt with a message historysystem-prompt-messages

import { generateText } from 'ai';

const { text } = await generateText({
  model: 'openai/gpt-5.4',
  system: 'You answer in one sentence.',
  messages: [{ role: 'user', content: 'What is RAG?' }],
});

Pass prompt or messages, never both; the call rejects if the two are combined.

Alternatives

PackageRegistryPick it when
openainpmYou only call OpenAI models and want the official client with newest features first
@anthropic-ai/sdknpmAnthropic-only apps that want the raw Messages API without an abstraction layer
langchainnpmYou care more about chains, retrievers, and a large integration catalog than typed UI streaming