Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
npm install ai @ai-sdk/openai @ai-sdk/anthropic// app/api/chat/route.ts
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages,
});
return result.toDataStreamResponse();
}// app/chat/page.tsx
"use client";
import { useChat } from "@ai-sdk/react";
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat();
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit">Enviar</button>
</form>
</div>
);
}Quando usar isso: Você precisa integrar chat com LLM, completions ou chamadas de ferramentas em um aplicativo React ou Next.js com suporte a streaming e o mínimo de boilerplate.
// app/components/ChatWithTools.tsx
"use client";
import { useChat } from "@ai-sdk/react";
export default function ChatWithTools() {
const { messages, input, handleInputChange, handleSubmit, isLoading } =
useChat({
api: "/api/chat-tools",
onError: (error) => console.error("Erro no chat:", error),
});
return (
<div className="mx-auto max-w-2xl p-4">
<div className="space-y-4">
{messages.map((message) => (
<div
key={message.id}
className={`rounded-lg p-3 ${
message.role === "user" ? "bg-blue-100 ml-auto" : "bg-gray-100"
}`}
>
<p className="text-sm font-medium">{message.role}</p>
<p>{message.content}</p>
{message.toolInvocations?.map((tool, i) => (
<pre key={i} className="mt-2 text-xs bg-gray-200 p-2 rounded">
{JSON.stringify(tool, null, 2)}
</pre>
))}
</div>
))}
</div>
<form onSubmit={handleSubmit} className="mt-4 flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Faça uma pergunta..."
className="flex-1 border rounded px-3 py-2"
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading}
className="bg-blue-600 text-white px-4 py-2 rounded disabled:opacity-50"
>
{isLoading ? "Pensando..." : "Enviar"}
</button>
</form>
</div>
);
}// app/api/chat-tools/route.ts
import { streamText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: anthropic("claude-sonnet-4-20250514"),
system: "Você é um assistente útil que pode consultar o clima.",
messages,
tools: {
getWeather: tool({
description: "Obtenha o clima atual para uma localização",
parameters: z.object({
location: z.string().describe("Nome da cidade"),
}),
execute: async ({ location }) => {
// Substitua pela chamada de API real
return { location, temperature: 72, condition: "sunny" };
},
}),
},
maxSteps: 5,
});
return result.toDataStreamResponse();
}O que isso demonstra:
useChatmaxStepsstreamText retorna um StreamTextResult que se converte em uma Response via toDataStreamResponse(), enviando Server-Sent Events para o clienteuseChat gerencia todo o estado da conversa: histórico de mensagens, estado de entrada, carregamento, erro e controlador de abortmaxSteps iteraçõesgenerateText é o equivalente não-streaming para processamento em lote ou geração no lado do servidorUsando OpenRouter para acesso a modelos:
npm install @openrouter/ai-sdk-providerimport { createOpenRouter } from "@openrouter/ai-sdk-provider";
const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
});
const result = streamText({
model: openrouter("anthropic/claude-sonnet-4-20250514"),
messages,
});Geração não-streaming:
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
const { text, usage } = await generateText({
model: openai("gpt-4o"),
prompt: "Resuma este artigo em 3 marcadores.",
});Saída estruturada com generateObject:
import { generateObject } from "ai";
import { z } from "zod";
const { object } = await generateObject({
model: openai("gpt-4o"),
schema: z.object({
recipe: z.string(),
ingredients: z.array(z.string()),
steps: z.array(z.string()),
}),
prompt: "Gere uma receita de cookies com gotas de chocolate.",
});useCompletion para prompts de turno único:
"use client";
import { useCompletion } from "@ai-sdk/react";
export default function Completion() {
const { completion, input, handleInputChange, handleSubmit } = useCompletion({
api: "/api/completion",
});
return (
<div>
<p>{completion}</p>
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
</form>
</div>
);
}Message de "ai" inclui id, role, content e toolInvocations opcionaisexecuteuseChat retorna messages: Message[] fortemente tipadoproviderOptionsimport type { Message } from "ai";
// Os tipos de resultado da ferramenta são inferidos do esquema Zod
const weatherTool = tool({
parameters: z.object({ city: z.string() }),
execute: async ({ city }) => {
// city é tipado como string
return { temp: 72 }; // o tipo de retorno é inferido
},
});Chaves de API ausentes - O SDK lança um erro em tempo de execução se a variável de ambiente da chave de API do provedor não estiver definida. Correção: Defina OPENAI_API_KEY, ANTHROPIC_API_KEY, etc. em .env.local. Cada provedor tem seu próprio nome de variável de ambiente esperado.
useChat não atualizando - Se as mensagens não estiverem sendo transmitidas, a rota da API pode não estar retornando uma resposta de fluxo de dados. Correção: Sempre retorne result.toDataStreamResponse(), não result.text ou uma resposta JSON simples.
Chamadas de ferramenta não executando - Ferramentas definidas, mas maxSteps não configurado, tem o valor padrão de 1, portanto, o uso de ferramentas em várias etapas não funcionará. Correção: Defina maxSteps: 5 (ou superior) para permitir que o modelo processe os resultados da ferramenta e continue.
Erros de CORS em desenvolvimento - As rotas da API devem estar no mesmo aplicativo Next.js. Correção: Use rotas de API do Next.js (app/api/) em vez de chamar um servidor externo diretamente de useChat.
Bundle grande de importações de provedor - Importar vários provedores aumenta o tamanho do bundle do cliente. Correção: As importações de provedor são apenas para servidor; mantenha-as em rotas de API ou ações de servidor, nunca em arquivos "use client".
| Biblioteca | Melhor para | Contraponto |
|---|---|---|
| Vercel AI SDK | Aplicativos de IA full-stack React/Next.js | Opinativo, vinculado às convenções do ecossistema Vercel |
| LangChain.js | Cadeias complexas, RAG, agentes | Abstração mais pesada, curva de aprendizado mais acentuada |
| SDK da OpenAI diretamente | Uso simples apenas com OpenAI | Sem hooks React de streaming, provedor único |
| SDK da Anthropic diretamente | Uso simples apenas com Claude | Sem hooks React de streaming, provedor único |
| LlamaIndex.ts | Indexação e recuperação de dados | Focado em RAG, menos em UI de chat |
streamText retorna um objeto StreamTextResult.toDataStreamResponse() para convertê-lo em uma Response com Server-Sent EventsuseChat no cliente consome este fluxo e atualiza as mensagens em tempo realresult.text ou JSON simples -- o hook espera o protocolo de fluxo de dadosimport { openai } from "@ai-sdk/openai";
import { anthropic } from "@ai-sdk/anthropic";
// Apenas mude a linha do modelo:
const result = streamText({
model: anthropic("claude-sonnet-4-20250514"),
messages,
});A interface unificada significa que apenas o parâmetro model muda.
maxSteps controla quantas rodadas de chamada de ferramenta e resultado o modelo pode realizarmaxSteps: 5 ou superior para uso de ferramentas em várias etapasimport { tool } from "ai";
import { z } from "zod";
const weatherTool = tool({
description: "Obtenha o clima para uma cidade",
parameters: z.object({
city: z.string().describe("Nome da cidade"),
}),
execute: async ({ city }) => {
return { temp: 72, condition: "sunny" };
},
});Os tipos de parâmetros da ferramenta são inferidos automaticamente do esquema Zod.
result.toDataStreamResponse(), não uma resposta JSONapi em useChat aponta para a rota corretastreamText envia tokens para o cliente à medida que são gerados (streaming)generateText espera a resposta completa e a retorna de uma vezstreamText para UIs de chat onde você deseja exibição de tokens em tempo realgenerateText para processamento em lote ou geração no lado do servidor onde o streaming não é necessárioimport { generateObject } from "ai";
import { z } from "zod";
const { object } = await generateObject({
model: openai("gpt-4o"),
schema: z.object({
title: z.string(),
tags: z.array(z.string()),
}),
prompt: "Gere metadados para um post de blog sobre React.",
});useCompletion gerencia um único ciclo de prompt/resposta, não uma conversacompletion (o texto da resposta), input e manipuladores de formulárioapi para uma rota que usa streamText com um prompt em vez de messages@ai-sdk/openai, @ai-sdk/anthropic) são apenas para servidor"use client" os agrupa no JavaScript do clienteuseChat e useCompletion são as únicas importações do AI SDK seguras para componentes clienteuseChat retorna messages: Message[] onde Message inclui id, role, content e toolInvocations opcionaisMessage de "ai" se precisar tipar props que aceitam mensagensimport type { Message } from "ai";import { createOpenRouter } from "@openrouter/ai-sdk-provider";
const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
});
const result = streamText({
model: openrouter("anthropic/claude-sonnet-4-20250514"),
messages,
});Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥