vercel-ai-sdk-expert · v4.7.0 · 2026-11-05 · sha256 570bd6851beacfc5

vercel-ai-sdk-expert v4.7.0A

Immutable. This exact content never changes and is served at /api/v1/blob/570bd6851beacfc5.

---
name: vercel-ai-sdk-expert
description: "Vercel AI SDK Core/UI, streaming structured data, and tool calling / Vercel AI SDK Core/UI, streaming data terstruktur, dan tool calling."
author: "Roedy Rustam"
version: "4.7.0"

---

# Vercel AI SDK Expert (Core, UI & Fullstack AI Engineering)

[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)

---

<a name="english"></a>
## 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 (
    <div className="flex flex-col h-[650px] w-full max-w-3xl mx-auto border border-border rounded-xl p-4 bg-background">
      <div className="flex-1 overflow-y-auto space-y-4 pr-2">
        {messages.map((m) => (
          <div key={m.id} className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
            <div className={`p-4 rounded-xl max-w-[85%] text-sm ${m.role === 'user' ? 'bg-primary text-primary-foreground' : 'bg-muted/80'}`}>
              {/* Reasoning Accordion */}
              {m.parts?.map((part, index) => {
                if (part.type === 'reasoning') {
                  return (
                    <details key={index} className="mb-2 p-2 bg-black/5 dark:bg-white/5 rounded text-xs">
                      <summary className="cursor-pointer font-medium text-muted-foreground">Thinking Process</summary>
                      <pre className="mt-1 whitespace-pre-wrap font-mono text-muted-foreground">{part.reasoning}</pre>
                    </details>
                  );
                }
                return null;
              })}

              <div className="whitespace-pre-wrap leading-relaxed">{m.content}</div>

              {/* Tool Invocations & Interactive Action Requests */}
              {m.toolInvocations?.map((inv) => (
                <div key={inv.toolCallId} className="mt-3 p-3 bg-background border border-border rounded-lg text-xs font-mono">
                  <div className="flex items-center justify-between font-semibold pb-1 border-b border-border">
                    <span>Tool: {inv.toolName}</span>
                    <span className="text-muted-foreground">{'result' in inv ? 'Done' : 'Running...'}</span>
                  </div>
                  {'result' in inv ? (
                    <pre className="mt-2 text-muted-foreground overflow-x-auto">{JSON.stringify(inv.result, null, 2)}</pre>
                  ) : (
                    <div className="mt-2 flex gap-2">
                      <button
                        onClick={() => addToolResult({ toolCallId: inv.toolCallId, result: { approved: true } })}
                        className="px-2 py-1 bg-primary text-primary-foreground rounded text-xs"
                      >
                        Approve Execution
                      </button>
                    </div>
                  )}
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>

      <form onSubmit={handleSubmit} className="flex gap-2 pt-3 border-t border-border">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask the performance auditor..."
          className="flex-1 px-3 py-2 text-sm border border-input rounded-md bg-background focus:outline-none focus:ring-1 focus:ring-ring"
        />
        <button
          type="submit"
          disabled={isLoading}
          className="px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md disabled:opacity-50"
        >
          Send
        </button>
      </form>
    </div>
  );
}
```

#### 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 <audit\|eval\|verify\|generate\|blueprint>` / 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` |

---


---

<a name="bahasa-indonesia"></a>
## 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 (
    <div className="flex flex-col h-[650px] w-full max-w-3xl mx-auto border border-slate-200 dark:border-slate-800 rounded-xl p-4 bg-white dark:bg-slate-900">
      <div className="flex-1 overflow-y-auto space-y-4 pr-2">
        {messages.map((m) => (
          <div key={m.id} className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
            <div className={`p-4 rounded-xl max-w-[85%] text-sm ${m.role === 'user' ? 'bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900' : 'bg-slate-100 text-slate-800 dark:bg-slate-800 dark:text-slate-200'}`}>
              {/* Bagian Penalaran Model / Thinking Process */}
              {m.parts?.map((bagian, index) => {
                if (bagian.type === 'reasoning') {
                  return (
                    <details key={index} className="mb-2 p-2 bg-black/5 dark:bg-white/5 rounded text-xs">
                      <summary className="cursor-pointer font-medium text-slate-500 dark:text-slate-400">Proses Penalaran (Thinking)</summary>
                      <pre className="mt-1 whitespace-pre-wrap font-mono text-slate-600 dark:text-slate-300">{bagian.reasoning}</pre>
                    </details>
                  );
                }
                return null;
              })}

              <div className="whitespace-pre-wrap leading-relaxed">{m.content}</div>

              {/* Status Pemanggilan Tool & Aksi Interaktif */}
              {m.toolInvocations?.map((inv) => (
                <div key={inv.toolCallId} className="mt-3 p-3 bg-white dark:bg-slate-950 border border-slate-200 dark:border-slate-800 rounded-lg text-xs font-mono">
                  <div className="flex items-center justify-between font-semibold pb-1 border-b border-slate-100 dark:border-slate-800">
                    <span>Tool: {inv.toolName}</span>
                    <span className="text-slate-400">{'result' in inv ? 'Selesai' : 'Sedang Berjalan...'}</span>
                  </div>
                  {'result' in inv ? (
                    <pre className="mt-2 text-slate-600 dark:text-slate-300 overflow-x-auto">{JSON.stringify(inv.result, null, 2)}</pre>
                  ) : (
                    <div className="mt-2 flex gap-2">
                      <button
                        onClick={() => addToolResult({ toolCallId: inv.toolCallId, result: { disetujui: true } })}
                        className="px-2 py-1 bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900 rounded text-xs"
                      >
                        Setujui Eksekusi Tool
                      </button>
                    </div>
                  )}
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>

      <form onSubmit={handleSubmit} className="flex gap-2 pt-3 border-t border-slate-200 dark:border-slate-800">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Tanyakan analisis pada agen..."
          className="flex-1 px-3 py-2 text-sm border border-slate-300 dark:border-slate-700 rounded-md bg-transparent focus:outline-none focus:ring-1 focus:ring-slate-900 dark:focus:ring-slate-100"
        />
        <button
          type="submit"
          disabled={isLoading}
          className="px-4 py-2 text-sm font-medium bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900 rounded-md disabled:opacity-50"
        >
          Kirim
        </button>
      </form>
    </div>
  );
}

#### 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 <audit\|eval\|verify\|generate\|blueprint>` / 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` |