--- name: vercel-ai-sdk-expert description: "Expert guide for Vercel AI SDK (Core, UI, RSC), streaming structured data, multi-provider model switching, tool calling loops, and React 19/Next.js 15 AI engineering / Panduan ahli Vercel AI SDK, streaming data terstruktur, dan integrasi AI pada React 19/Next.js 15." author: "Roedy Rustam" version: "4.4.2" --- # Vercel AI SDK Expert (Core, UI & Fullstack AI Engineering) [English](#english) | [Bahasa Indonesia](#bahasa-indonesia) --- ## English ### Orchestration & Integration - **Upstream Models & Transports**: `ai-llm-integration-expert`, `frontier-ai-models-expert`, `gemini-agent-booster` - **Frontend Presentation & Generative UI**: `senior-frontend`, `nextjs-app-router-expert`, `ephemeral-generative-ui-architect` - **Tooling & Swarms**: `mcp-server-architect`, `multi-agent-orchestration`, `parallel-tool-orchestrator` - **Contracts & Schemas**: `structured-output-contracts`, `form-validation-expert`, `typescript-expert` - **Lifecycle & Governance**: `zero-to-prod-orchestrator`, `session-memory-manager`, `anti-slop` ### Description Production-grade guide for building AI applications using the **Vercel AI SDK (Core & UI)**. Covers unified model provider abstraction (`@ai-sdk/anthropic`, `@ai-sdk/openai`, `@ai-sdk/google`), streaming text and structured objects (`streamText`, `streamObject`), dynamic multi-step tool execution loops with `maxSteps`, client-side React 19 hooks (`useChat`, `useCompletion`), streaming data attachments (`createDataStreamResponse`), and generative UI rendering. **Swarm Synergy:** Within the **Frontend & UI Swarm**, this skill serves as the AI UI Presentation Lead. It translates complex backend multi-agent outputs and streaming tokens into accessible, beautiful web components in Phase 4 & Phase 5. ### Trigger Conditions - Integrating conversational chat, streaming completions, or generative UI in React 19 / Next.js 15. - Implementing structured data extraction using `generateObject` or `streamObject` with Zod schemas. - Building autonomous multi-step tool-calling loops on Next.js Route Handlers or Server Actions. - Switching seamlessly across frontier providers (Claude Sonnet 5.5 / Fable 5.1, Gemini 4 Argon / Pro, OpenAI GPT-5.6 Sol, Grok 4.7, Ollama). - Building streaming data channels with custom metadata, tool status indicators, and citations. ### Vercel AI SDK Architecture (Core vs UI) ``` ┌─────────────────────────────────────────────────────────────┐ │ CLIENT LAYER │ │ useChat / useCompletion / Generative UI React Components │ │ • Optimistic updates • Stream reader • Tool invocation │ └──────────────────────────────▲──────────────────────────────┘ │ HTTP SSE / Data Stream Protocol ┌──────────────────────────────▼──────────────────────────────┐ │ SERVER ROUTE / ACTION │ │ streamText({ │ │ model: anthropic('claude-sonnet-5.5-20261022'), │ │ tools: { weatherTool, dbQueryTool }, │ │ maxSteps: 5, │ │ }).toDataStreamResponse() │ └─────────────────────────────────────────────────────────────┘ ``` ### Core Implementation Guidelines #### 1. Next.js 15 Route Handler with Multi-Step Tool Calling & Step Hooks Use `streamText` with `maxSteps`, `onStepFinish`, and native prompt caching for iterative tool calling: ```typescript // app/api/chat/route.ts import { anthropic } from '@ai-sdk/anthropic'; import { streamText, tool } from 'ai'; import { z } from 'zod'; export const maxDuration = 60; // Allow long-running agentic reasoning export async function POST(req: Request) { const { messages } = await req.json(); const result = streamText({ model: anthropic('claude-sonnet-5.5-20261022'), messages, maxSteps: 10, // Iterative tool-calling loop up to 10 steps providerOptions: { anthropic: { cacheControl: { type: 'ephemeral' }, // Prompt caching for 90% TTFT acceleration }, }, tools: { calculateMetrics: tool({ description: 'Computes analytical metrics from raw time series data', parameters: z.object({ datasetId: z.string().min(1), metricType: z.enum(['p95_latency', 'error_rate', 'throughput']), }), execute: async ({ datasetId, metricType }) => { const metrics = await fetchDatasetMetrics(datasetId, metricType); return { datasetId, metricType, value: metrics.result, timestamp: new Date().toISOString() }; }, }), }, system: 'You are an elite software performance auditor. Always ground conclusions with tool metrics.', onStepFinish: ({ stepType, text, toolCalls, toolResults, finishReason, usage }) => { console.log(`[Step ${stepType}] Finish: ${finishReason}, Prompt Tokens: ${usage.promptTokens}, Completion: ${usage.completionTokens}`); if (toolCalls && toolCalls.length > 0) { console.log(`Dispatched tools: ${toolCalls.map(t => t.toolName).join(', ')}`); } }, }); return result.toDataStreamResponse(); } async function fetchDatasetMetrics(datasetId: string, metricType: string) { return { result: metricType === 'p95_latency' ? '12.4ms' : '0.01%' }; } ``` #### 2. Streaming Type-Safe Structured Objects (`streamObject`) Stream validated structured objects to client components with partial object completion: ```typescript import { google } from '@ai-sdk/google'; import { streamObject } from 'ai'; import { z } from 'zod'; export const SecurityReportSchema = z.object({ securityVulnerabilities: z.array(z.object({ severity: z.enum(['low', 'medium', 'high', 'critical']), cwe: z.string(), explanation: z.string(), suggestedFix: z.string(), })), overallRiskScore: z.number().min(0).max(100), passesReview: z.boolean(), mitigationSummary: z.string(), }); export async function POST(req: Request) { const { codeDiff } = await req.json(); // Invariant: Gracefully fall back to Gemini 3.8 Flash if Gemini 4 Pro is unprovisioned let modelInstance; try { modelInstance = google('gemini-4-pro'); } catch (err) { console.warn('[VercelAISDK] Gemini 4 Pro unavailable. Falling back to Gemini 3.8 Flash:', err); modelInstance = google('gemini-3.8-flash'); } const result = streamObject({ model: modelInstance, schema: SecurityReportSchema, prompt: `Audit the following git diff for security regressions:\n${codeDiff}`, }); return result.toTextStreamResponse(); } ``` #### 3. Client Hook Integration (`useChat` with Interactive Tool Invocations) Render streaming markdown, reasoning tokens, and interactive human-in-the-loop tool approvals: ```tsx 'use client'; import { useChat } from '@ai-sdk/react'; export function AgenticChat() { const { messages, input, handleInputChange, handleSubmit, isLoading, addToolResult } = useChat({ maxSteps: 10, }); return (
{messages.map((m) => (
{/* Reasoning Accordion */} {m.parts?.map((part, index) => { if (part.type === 'reasoning') { return (
Thinking Process
{part.reasoning}
); } return null; })}
{m.content}
{/* Tool Invocations & Interactive Action Requests */} {m.toolInvocations?.map((inv) => (
Tool: {inv.toolName} {'result' in inv ? 'Done' : 'Running...'}
{'result' in inv ? (
{JSON.stringify(inv.result, null, 2)}
) : (
)}
))}
))}
); } ``` #### 4. Multimodal Attachments (Images, PDFs & Audio) Pass multimodal file buffers or URLs directly into `streamText` message arrays: ```typescript import { google } from '@ai-sdk/google'; import { streamText } from 'ai'; export async function POST(req: Request) { const { prompt, pdfBase64, imageBase64 } = await req.json(); // Invariant: Gracefully fall back to Gemini 3.8 Flash if Gemini 4 Argon is unprovisioned let modelInstance; try { modelInstance = google('gemini-4-argon'); } catch (err) { console.warn('[VercelAISDK] Gemini 4 Argon unavailable. Falling back to Gemini 3.8 Flash:', err); modelInstance = google('gemini-3.8-flash'); } const result = streamText({ model: modelInstance, messages: [ { role: 'user', content: [ { type: 'text', text: prompt }, { type: 'file', mimeType: 'application/pdf', data: pdfBase64 }, { type: 'image', image: imageBase64 }, ], }, ], }); return result.toDataStreamResponse(); } ``` #### 5. Custom LanguageModelV1 Middleware (`wrapLanguageModel`) Implement security screening, prompt caching telemetry, or rate limiting via unified middleware: ```typescript import { anthropic } from '@ai-sdk/anthropic'; import { experimental_wrapLanguageModel as wrapLanguageModel } from 'ai'; export const auditedModel = wrapLanguageModel({ model: anthropic('claude-sonnet-5.5-20261022'), middleware: { wrapGenerate: async ({ doGenerate, params }) => { console.log(`[Audit Middleware] Pre-generation prompt tokens: ${params.prompt.length}`); const result = await doGenerate(); console.log(`[Audit Middleware] Generated tokens: ${result.text?.length ?? 0}`); return result; }, wrapStream: async ({ doStream, params }) => { console.log(`[Audit Middleware] Initiating stream for model: ${params.modelId}`); return await doStream(); }, }, }); ``` #### 6. Direct Data Stream Control with `createDataStreamResponse` Send custom progress updates, telemetry events, and citations alongside model tokens: ```typescript import { anthropic } from '@ai-sdk/anthropic'; import { createDataStreamResponse, streamText } from 'ai'; export async function POST(req: Request) { const { messages } = await req.json(); return createDataStreamResponse({ execute: async (dataStream) => { // 1. Send initial telemetry annotation dataStream.writeMessageAnnotation({ step: 'INITIALIZING_AUDIT', timestamp: Date.now() }); // 2. Stream LLM tokens (with Claude Opus 5.5 -> Claude Opus 4.6 fallback support) let modelInstance; try { modelInstance = anthropic('claude-opus-5.5-20261022'); } catch (err) { console.warn('[VercelAISDK] Claude Opus 5.5 unavailable. Falling back to Claude Opus 4.6:', err); modelInstance = anthropic('claude-opus-4.6'); } const result = streamText({ model: modelInstance, messages, }); result.mergeIntoDataStream(dataStream); }, }); } ``` ### Sovereign Execution, Token Efficiency & Invariants | Dimension | Sovereign Mandate & Deterministic Contract | | :--- | :--- | | **Frontier Model Cascade** | Medium / High (`GPT-5.6 Sol Medium`, `Gemini 4 Argon`, `Claude Sonnet 5.5 High`, `Grok 4.7 High Fast`) | | **Availability Fallback** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Zero unhandled missing-model errors) | | **KV-Cache & Token Protocol** | Pin static instructions & schemas at prompt prefix (`kv-cache-prefix-optimizer`); pass surgical `CONTEXT_MAP.md` slices (<250 tokens/call) during Swarm Fan-Out | | **SaaT CLI & MCP Dispatch** | CLI: `vibes skill exec vercel-ai-sdk-expert ` / MCP: `vibes_skill_dispatch`, `vibes_ast_query`, `vibes_compile_cache`, `vibes_optimize_budget`, `vibes_verify_gate` | | **NFR & Anti-Slop Invariants** | P99 API Gateway Latency < 85ms, OpenAPI 3.1 / TypeSpec strict schema parity, RFC 9457 Problem Details error contracts, Idempotency-Key enforcement; 100% complete zero-placeholder output verified via `node scripts/check-anti-slop.js --strict` | --- --- ## Bahasa Indonesia ### Integrasi Orkestrasi - **Model Hulu & Transport**: `ai-llm-integration-expert`, `frontier-ai-models-expert`, `gemini-agent-booster` - **Presentasi Frontend & Generative UI**: `senior-frontend`, `nextjs-app-router-expert`, `ephemeral-generative-ui-architect` - **Tooling & Swarm**: `mcp-server-architect`, `multi-agent-orchestration`, `parallel-tool-orchestrator` - **Kontrak & Skema**: `structured-output-contracts`, `form-validation-expert`, `typescript-expert` - **Siklus Hidup & Tata Kelola**: `zero-to-prod-orchestrator`, `session-memory-manager`, `anti-slop` ### Deskripsi Panduan produksi untuk membangun aplikasi AI menggunakan **Vercel AI SDK (Core & UI)**. Mencakup abstraksi penyedia model terpadu (`@ai-sdk/anthropic`, `@ai-sdk/openai`, `@ai-sdk/google`), streaming teks dan objek terstruktur (`streamText`, `streamObject`), siklus eksekusi tool multi-langkah otonom dengan `maxSteps`, hook klien React 19 (`useChat`, `useCompletion`), streaming respons saluran data (`createDataStreamResponse`), dan rendering Generative UI. **Sinergi Swarm:** Di dalam **Frontend & UI Swarm**, skill ini berperan sebagai Pemimpin Presentasi UI AI. Skill ini bertugas mentransformasikan keluaran multi-agen backend dan token streaming menjadi komponen web yang interaktif, aksesibel, dan elegan pada Fase 4 & Fase 5. ### Kondisi Pemicu - Mengintegrasikan chat percakapan, streaming respons, atau generative UI di React 19 / Next.js 15. - Menerapkan ekstraksi data terstruktur dengan validasi skema Zod via `generateObject` atau `streamObject`. - Membangun loop pemanggilan tool (*tool-calling loops*) multi-langkah di Route Handler atau Server Actions. - Beralih fleksibel antar penyedia model frontier (Claude Sonnet 5.5 / Fable 5.1, Gemini 4 Argon / Pro, OpenAI GPT-5.6 Sol, Grok 4.7, Ollama). - Mengelola status eksekusi tool, indikator loading, dan rendering komponen UI secara dinamis saat streaming berlangsung. --- ### Implementasi Pola Kunci (TypeScript & React 19) #### 1. Next.js 15 Route Handler dengan Multi-Step Tool Calling & Hooks Langkah Gunakan `streamText` dengan `maxSteps: 10`, `onStepFinish`, dan prompt caching native untuk siklus pemanggilan tool iteratif otonom: ```typescript // app/api/chat/route.ts import { anthropic } from '@ai-sdk/anthropic'; import { streamText, tool } from 'ai'; import { z } from 'zod'; export const maxDuration = 60; // Izinkan penalaran agen hingga 60 detik export async function POST(req: Request) { const { messages } = await req.json(); const hasil = streamText({ model: anthropic('claude-sonnet-5.5-20261022'), messages, maxSteps: 10, // Siklus iteratif pemanggilan tool hingga 10 langkah providerOptions: { anthropic: { cacheControl: { type: 'ephemeral' }, // Prompt caching untuk akselerasi TTFT hingga 90% }, }, tools: { hitungMetrikPerforma: tool({ description: 'Menghitung metrik analitik aplikasi dari data deret waktu mentah', parameters: z.object({ datasetId: z.string().min(1), metrik: z.enum(['p95_latensi', 'tingkat_error', 'throughput']), }), execute: async ({ datasetId, metrik }) => { const data = await ambilMetrikSistem(datasetId, metrik); return { datasetId, metrik, nilai: data.hasil, timestamp: new Date().toISOString() }; }, }), }, system: 'Anda adalah auditor performa perangkat lunak elit. Selalu sertakan bukti valid dari pemanggilan tool.', onStepFinish: ({ stepType, text, toolCalls, toolResults, finishReason, usage }) => { console.log(`[Langkah ${stepType}] Status: ${finishReason}, Prompt Tokens: ${usage.promptTokens}, Completion: ${usage.completionTokens}`); if (toolCalls && toolCalls.length > 0) { console.log(`Tool dipanggil: ${toolCalls.map(t => t.toolName).join(', ')}`); } }, }); return hasil.toDataStreamResponse(); } async function ambilMetrikSistem(datasetId: string, metrik: string) { return { hasil: metrik === 'p95_latensi' ? '12.4ms' : '0.01%' }; } ``` #### 2. Streaming Objek Terstruktur Type-Safe (`streamObject`) Streaming objek terstruktur tervalidasi skema Zod secara instan ke antarmuka klien: ```typescript import { google } from '@ai-sdk/google'; import { streamObject } from 'ai'; import { z } from 'zod'; export const SkemaLaporanKeamanan = z.object({ kerentananKeamanan: z.array(z.object({ tingkatKeparahan: z.enum(['rendah', 'sedang', 'tinggi', 'kritis']), cwe: z.string(), penjelasan: z.string(), saranPerbaikan: z.string(), })), skorRisikoGlobal: z.number().min(0).max(100), lolosReview: z.boolean(), ringkasanMitigasi: z.string(), }); export async function POST(req: Request) { const { diffKode } = await req.json(); // Invarian: Pengalihan otomatis ke Gemini 3.8 Flash jika Gemini 4 Pro belum aktif let modelTarget; try { modelTarget = google('gemini-4-pro'); } catch (galat) { console.warn('[VercelAISDK] Gemini 4 Pro belum tersedia. Mengalihkan ke Gemini 3.8 Flash:', galat); modelTarget = google('gemini-3.8-flash'); } const hasil = streamObject({ model: modelTarget, schema: SkemaLaporanKeamanan, prompt: `Audit perubahan kode berikut untuk potensi regresi keamanan:\n${diffKode}`, }); return hasil.toTextStreamResponse(); } ``` #### 3. Integrasi Hook Klien React 19 (`useChat` dengan Invokasi Tool & Persetujuan) Render token streaming, akordion penalaran (*thinking process*), dan persetujuan eksekusi tool interaktif: ```tsx 'use client'; import { useChat } from '@ai-sdk/react'; export function AntarmukaChatAgen() { const { messages, input, handleInputChange, handleSubmit, isLoading, addToolResult } = useChat({ maxSteps: 10, }); return (
{messages.map((m) => (
{/* Bagian Penalaran Model / Thinking Process */} {m.parts?.map((bagian, index) => { if (bagian.type === 'reasoning') { return (
Proses Penalaran (Thinking)
{bagian.reasoning}
); } return null; })}
{m.content}
{/* Status Pemanggilan Tool & Aksi Interaktif */} {m.toolInvocations?.map((inv) => (
Tool: {inv.toolName} {'result' in inv ? 'Selesai' : 'Sedang Berjalan...'}
{'result' in inv ? (
{JSON.stringify(inv.result, null, 2)}
) : (
)}
))}
))}
); } #### 4. Pemrosesan Lampiran Multimodal (Gambar, PDF & Dokumen) Kirim buffer file multimodal atau URL langsung ke dalam array pesan `streamText`: ```typescript import { google } from '@ai-sdk/google'; import { streamText } from 'ai'; export async function POST(req: Request) { const { prompt, pdfBase64, imageBase64 } = await req.json(); // Invarian: Pengalihan otomatis ke Gemini 3.8 Flash jika Gemini 4 Argon belum aktif let modelTarget; try { modelTarget = google('gemini-4-argon'); } catch (galat) { console.warn('[VercelAISDK] Gemini 4 Argon belum tersedia. Mengalihkan ke Gemini 3.8 Flash:', galat); modelTarget = google('gemini-3.8-flash'); } const hasil = streamText({ model: modelTarget, messages: [ { role: 'user', content: [ { type: 'text', text: prompt }, { type: 'file', mimeType: 'application/pdf', data: pdfBase64 }, { type: 'image', image: imageBase64 }, ], }, ], }); return hasil.toDataStreamResponse(); } ``` #### 5. Middleware Kustom LanguageModelV1 (`wrapLanguageModel`) Terapkan penyaringan keamanan, logging token, dan guardrail melalui middleware terpadu: ```typescript import { anthropic } from '@ai-sdk/anthropic'; import { experimental_wrapLanguageModel as wrapLanguageModel } from 'ai'; export const modelAudit = wrapLanguageModel({ model: anthropic('claude-sonnet-5.5-20261022'), middleware: { wrapGenerate: async ({ doGenerate, params }) => { console.log(`[Middleware Audit] Panjang prompt sebelum generasi: ${params.prompt.length}`); const hasil = await doGenerate(); console.log(`[Middleware Audit] Token selesai di-generate: ${hasil.text?.length ?? 0}`); return hasil; }, wrapStream: async ({ doStream, params }) => { console.log(`[Middleware Audit] Memulai streaming untuk model: ${params.modelId}`); return await doStream(); }, }, }); ``` #### 6. Kendali Aliran Data Langsung dengan `createDataStreamResponse` Kirim pembaruan progres kustom, metadata status, dan anotasi telemetri bersamaan dengan token LLM: ```typescript import { anthropic } from '@ai-sdk/anthropic'; import { createDataStreamResponse, streamText } from 'ai'; export async function POST(req: Request) { const { messages } = await req.json(); return createDataStreamResponse({ execute: async (dataStream) => { // 1. Tulis anotasi status awal dataStream.writeMessageAnnotation({ status: 'MEMULAI_AUDIT', timestamp: Date.now() }); // 2. Stream token LLM (dengan fallback Claude Opus 5.5 -> Claude Opus 4.6) let modelTarget; try { modelTarget = anthropic('claude-opus-5.5-20261022'); } catch (galat) { console.warn('[VercelAISDK] Claude Opus 5.5 tidak tersedia. Mengalihkan ke Claude Opus 4.6:', galat); modelTarget = anthropic('claude-opus-4.6'); } const hasil = streamText({ model: modelTarget, messages, }); hasil.mergeIntoDataStream(dataStream); }, }); } ``` ### Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian | Dimensi | Mandat Berdaulat & Kontrak Deterministik | | :--- | :--- | | **Kaskade Model Frontier** | Medium / High (`GPT-5.6 Sol Medium`, `Gemini 4 Argon`, `Claude Sonnet 5.5 High`, `Grok 4.7 High Fast`) | | **Invarian Fallback Ketersediaan** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Bebas gagal saat model belum tersedia/kuota penuh) | | **Protokol Hemat Token & KV-Cache** | Kunci instruksi & skema statis di prefix awal (`kv-cache-prefix-optimizer`); kirim potongan `CONTEXT_MAP.md` (<250 token/panggilan) saat Fan-Out | | **Eksekusi SaaT CLI & MCP** | CLI: `vibes skill exec vercel-ai-sdk-expert ` / MCP: `vibes_skill_dispatch`, `vibes_ast_query`, `vibes_compile_cache`, `vibes_optimize_budget`, `vibes_verify_gate` | | **Invarian NFR & Anti-Slop** | Latensi P99 API < 85ms, paritas skema ketat OpenAPI 3.1 / TypeSpec, kontrak error RFC 9457 Problem Details, penegakan Idempotency-Key; kode 100% tuntas tanpa placeholder diverifikasi melalui `node scripts/check-anti-slop.js --strict` |