Vercel AI SDK
Trace Vercel AI SDK calls in Muster via OpenTelemetry, capturing generateText, streamText, generateObject, and tool calls.
This guide explains how to integrate Muster with the Vercel AI SDK to monitor and debug LLM-powered applications.
The integration leverages OpenTelemetry, an observability standard. When you enable telemetry on the AI SDK and add the LangfuseSpanProcessor, your AI calls automatically flow into Muster for analysis.
Integration Steps
1. Install Dependencies
npm install ai
npm install @ai-sdk/openai
npm install @langfuse/tracing @langfuse/otel @opentelemetry/sdk-nodeAny Vercel AI SDK provider (Anthropic, Google, Mistral, etc.) works with this integration.
2. Configure Environment Variables
LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_PUBLIC_KEY="pk-lf-..."
LANGFUSE_BASE_URL="https://app.getmuster.io" # or your self-hosted Muster URL
OPENAI_API_KEY="sk-proj-..."API keys come from your Muster project's Settings → API Keys page.
3. Initialize OpenTelemetry with LangfuseSpanProcessor
import { NodeSDK } from "@opentelemetry/sdk-node";
import { LangfuseSpanProcessor } from "@langfuse/otel";
const sdk = new NodeSDK({
spanProcessors: [new LangfuseSpanProcessor()],
});
sdk.start();4. Enable Telemetry on AI SDK Calls
import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
const { text } = await generateText({
model: openai("gpt-5.1"),
prompt: "What is the weather like today in San Francisco?",
tools: {
getWeather: tool({
description: "Get the weather in a location",
inputSchema: z.object({
location: z.string().describe("The location to get the weather for"),
}),
execute: async ({ location }) => ({
location,
temperature: 72 + Math.floor(Math.random() * 21) - 10,
}),
}),
},
experimental_telemetry: { isEnabled: true },
});Works with generateText, streamText, generateObject, and tool calls.
5. View Traces in Muster
After execution, traces appear in your Muster dashboard showing complete model calls, tool usage, and latency metrics.
Combining with Prompt Management
Link prompts to traces by passing prompt metadata:
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { LangfuseClient } from "@langfuse/client";
const langfuse = new LangfuseClient();
const prompt = await langfuse.prompt.get("movie-critic");
const compiledPrompt = prompt.compile({
someVariable: "example-variable",
});
const { text } = await generateText({
model: openai("gpt-5"),
prompt: compiledPrompt,
experimental_telemetry: {
isEnabled: true,
metadata: {
langfusePrompt: prompt.toJSON(),
},
},
});Next.js Setup
For production Next.js applications, create an instrumentation.ts file:
// instrumentation.ts
import { LangfuseSpanProcessor } from "@langfuse/otel";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
export const langfuseSpanProcessor = new LangfuseSpanProcessor();
const tracerProvider = new NodeTracerProvider({
spanProcessors: [langfuseSpanProcessor],
});
tracerProvider.register();Important: Use manual OpenTelemetry setup via
NodeTracerProviderrather than@vercel/otel— the latter does not yet support OpenTelemetry JS SDK v2.
Example Chat API Route
// app/api/chat/route.ts
import { streamText } from "ai";
import { after } from "next/server";
import { openai } from "@ai-sdk/openai";
import {
observe,
propagateAttributes,
setActiveTraceIO,
} from "@langfuse/tracing";
import { trace } from "@opentelemetry/api";
import { langfuseSpanProcessor } from "@/src/instrumentation";
const handler = async (req: Request) => {
const { messages, chatId, userId } = await req.json();
setActiveTraceIO({
input: messages[messages.length - 1].parts.find(
(part) => part.type === "text"
)?.text,
});
await propagateAttributes(
{
traceName: "chat-message",
sessionId: chatId,
userId,
},
async () => {
const result = streamText({
model: openai("gpt-5.1"),
messages,
experimental_telemetry: {
isEnabled: true,
},
onFinish: async (result) => {
setActiveTraceIO({
output: result.content,
});
trace.getActiveSpan().end();
},
onError: async (error) => {
setActiveTraceIO({
output: error,
});
trace.getActiveSpan()?.end();
},
});
after(async () => await langfuseSpanProcessor.forceFlush());
return result.toUIMessageStreamResponse();
}
);
};
export const POST = observe(handler, {
name: "handle-chat-message",
endOnExit: false,
});Key points:
observe()creates a Muster trace around the handler.propagateAttributes()adds session and user metadata.forceFlush()ensures traces are sent before serverless termination.endOnExit: falsekeeps the observation open until streaming completes.
Using With Other Observability Tools
The Vercel AI SDK uses OpenTelemetry under the instrumentation scope ai.
When using Sentry, Datadog, or other OTel-based tools, additional
configuration may be needed. See Using Langfuse with an Existing OpenTelemetry Setup upstream.
Interoperability with the JS/TS SDK
Context Manager Approach
import { startActiveObservation, propagateAttributes } from "@langfuse/tracing";
await startActiveObservation("context-manager", async (span) => {
span.update({
input: { query: "What is the capital of France?" },
});
await propagateAttributes(
{
userId: "user-123",
sessionId: "session-123",
metadata: {
source: "api",
region: "us-east-1",
},
tags: ["api", "user"],
version: "1.0.0",
},
async () => {
const { text } = await generateText({
model: openai("gpt-5"),
prompt: "What is the capital of France?",
experimental_telemetry: { isEnabled: true },
});
}
);
span.update({ output: "Paris" });
});observe Wrapper Approach
import { observe, propagateAttributes } from "@langfuse/tracing";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
const processUserRequest = observe(
async (userQuery: string) => {
return await propagateAttributes(
{
userId: "user-123",
sessionId: "session-123",
metadata: {
source: "api",
region: "us-east-1",
},
tags: ["api", "user"],
version: "1.0.0",
},
async () => {
const { text } = await generateText({
model: openai("gpt-5"),
prompt: userQuery,
experimental_telemetry: { isEnabled: true },
});
return text;
}
);
},
{ name: "process-user-request" }
);
const result = await processUserRequest("some query");Troubleshooting
No Traces Appearing
Enable debug mode:
export LANGFUSE_LOG_LEVEL="DEBUG"If OTel spans appear in the logs but traces do not reach Muster:
- Call
forceFlush()at application end (critical for serverless). - Verify API keys and base URL.
If no OTel spans appear at all, instrumentation is likely not running before your application code.
Unwanted Observations
Other libraries may emit irrelevant OTel spans. Filter them out to avoid billable-unit charges.
Missing Attributes
Some attributes are stored in metadata rather than mapped Muster fields. Raise an issue with your operator if mappings do not work as expected.