Skip to main content
wrapVercelSdk instruments the Vercel AI SDK module. Wrapped functions behave exactly like the originals; each call is captured as an LLM event, and tools executed during the call are captured as tool events on the same trace.

Quick setup

import * as ai from "ai";
import { Carbon } from "@carbon-js/sdk";
import { wrapVercelSdk } from "@carbon-js/sdk/ai";

const carbon = new Carbon();
const { generateText } = wrapVercelSdk(ai, carbon);

const result = await generateText({
  model: "google/gemini-3-flash",
  prompt: "What is the capital of France?",
});

// Flush : Only needed in serverless and other short-lived environments
await carbon.flushPendingEvents();
Unlike the OpenAI and Anthropic wrappers, wrapVercelSdk does not mutate the module you pass in — it returns a new wrapped object. Import functions from the wrapped object, not from ai directly.

What gets captured

FunctionCaptured as
generateTextOne LLM event per step, plus one tool event per tool execution
streamTextThe same, recorded after the stream completes
ToolLoopAgent (generate and stream)LLM and tool events for every step of the loop
The wrapper forces AI SDK telemetry on for wrapped calls and uses its lifecycle callbacks to build events. Existing telemetry configuration is preserved.

Attach metadata

Add a carbon field to the call arguments to set a trace ID, context identifiers, or custom properties:
import { tool } from "ai";
import { z } from "zod";

const result = await generateText({
  model: "google/gemini-3-flash",
  prompt: "What is the weather in Tokyo?",
  tools: {
    get_weather: tool({
      description: "Get the current weather for a city",
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ city, tempC: 22 }),
    }),
  },
  carbon: {
    context: { agentId: "travel-agent", threadId: "thread-92", userId: "user-481" },
  },
});
Tool executions inside the call are recorded automatically — no extra wrapping needed. See Traces and context for what each field powers in the dashboard.

Streaming

streamText events are recorded after the stream finishes, so consume the stream to completion:
const { streamText } = wrapVercelSdk(ai, carbon);

const result = streamText({
  model: "google/gemini-3-flash",
  prompt: "Write a haiku about ledgers.",
});

for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}
A stream that is abandoned before it completes produces no event.

Flushing

In serverless and other short-lived environments, flush buffered events before exiting:
await carbon.flushPendingEvents();
Buffering and flush behavior are configurable — see Configuration.