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
| Function | Captured as |
|---|
generateText | One LLM event per step, plus one tool event per tool execution |
streamText | The 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.
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.