> ## Documentation Index
> Fetch the complete documentation index at: https://tracevell.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Vercel AI SDK observability

> Trace Vercel AI SDK calls in Next.js and Node.js: model turns, tool calls, streaming, sub-agents, token usage, and cost.

## Overview

Tracevell is OpenTelemetry-native, so it captures full traces of every [Vercel AI SDK](https://ai-sdk.dev/) call: `generateText`, `streamText`, `embed`, tool calls, and provider-level request and response payloads.

What Tracevell captures:

* Prompts, messages, and system instructions sent to the model.
* Model output, reasoning, and structured object results.
* Tool calls, arguments, and tool results.
* Token counts, latency, and cost per call.
* Provider identity (OpenAI, Anthropic, Google, Mistral, and others) and model name.

The AI SDK changed its telemetry API in v7. The setup below shows both styles. On AI SDK v7 you register Tracevell once and every call is traced. On v5 and v6 you initialize Tracevell and pass its tracer per call. Pick the tab that matches your `ai` version.

## Getting Started

> [tab] AI SDK v7
>
> **1. Install**
>
> ```bash theme={null}
> npm install @lmnr-ai/lmnr@latest ai@latest @ai-sdk/openai@latest
> ```
>
> Swap `@ai-sdk/openai` for any provider adapter you use ([full list](https://ai-sdk.dev/providers/ai-sdk-providers)).
>
> **2. Set environment variables**
>
> ```bash theme={null}
> # .env
> LMNR_PROJECT_API_KEY=your-tracevell-project-api-key
> OPENAI_API_KEY=your-openai-api-key
> ```
>
> To get the project API key, go to the Tracevell dashboard, click the project settings,
> and generate a project API key. This is available both in the cloud and in the self-hosted version of Tracevell.
>
> Specify the key at `Tracevell` initialization. If not specified,
> Tracevell will look for the key in the `LMNR_PROJECT_API_KEY` environment variable.
>
> **3. Register Tracevell telemetry**
>
> Register Tracevell once at the entry point of your application, before the first AI SDK call. This one line initializes all of Tracevell's tracing and wires it into the AI SDK.
>
> ```typescript theme={null}
> import { registerTelemetry } from 'ai';
> import { LaminarAiSdkTelemetry } from '@lmnr-ai/lmnr';
> import 'dotenv/config';
>
> registerTelemetry(new LaminarAiSdkTelemetry());
> ```
>
> `new LaminarAiSdkTelemetry()` initializes Tracevell for you (it calls `Laminar.initialize()` internally if Tracevell isn't already initialized), so you don't need a separate `Laminar.initialize()` call. Pass options to the constructor to configure it: see [Configure the integration](#configure-the-integration).
>
> > [!NOTE]
> >
> > `@lmnr-ai/lmnr` also exports `registerAiSdkTelemetry()`, a one-line equivalent that registers the same integration without importing `registerTelemetry` from `ai`:
> >
> > ```typescript theme={null}
> > import { registerAiSdkTelemetry } from '@lmnr-ai/lmnr';
> >
> > registerAiSdkTelemetry(); // same as registerTelemetry(new LaminarAiSdkTelemetry())
> > ```
> >
> > It takes the same options object as `LaminarAiSdkTelemetry` (including `laminarOptions`). Reach for it when you want to register telemetry before the AI SDK is loaded, or to keep the import surface to a single package.
>
> **4. Use the AI SDK as usual**
>
> No per-call configuration. Every `generate*` and `stream*` call is traced automatically.
>
> ```typescript theme={null}
> import { openai } from '@ai-sdk/openai';
> import { generateText } from 'ai';
>
> const { text } = await generateText({
>   model: openai('gpt-5-mini'),
>   prompt: 'What is tracevell flow?',
> });
> ```
>
> The same applies to `streamText` and `embed`.

> [tab] AI SDK v5 and v6
>
> **1. Install**
>
> ```bash theme={null}
> npm install @lmnr-ai/lmnr@latest ai@latest @ai-sdk/openai@latest
> ```
>
> Swap `@ai-sdk/openai` for any provider adapter you use ([full list](https://ai-sdk.dev/providers/ai-sdk-providers)).
>
> **2. Set environment variables**
>
> ```bash theme={null}
> # .env
> LMNR_PROJECT_API_KEY=your-tracevell-project-api-key
> OPENAI_API_KEY=your-openai-api-key
> ```
>
> To get the project API key, go to the Tracevell dashboard, click the project settings,
> and generate a project API key. This is available both in the cloud and in the self-hosted version of Tracevell.
>
> Specify the key at `Tracevell` initialization. If not specified,
> Tracevell will look for the key in the `LMNR_PROJECT_API_KEY` environment variable.
>
> **3. Initialize Tracevell**
>
> Call `Laminar.initialize()` once at the entry point of your application, before any AI SDK call runs.
>
> ```typescript theme={null}
> import { Tracevell } from '@lmnr-ai/lmnr';
> import 'dotenv/config';
>
> Laminar.initialize();
> ```
>
> **4. Pass the tracer to AI SDK calls**
>
> Enable `experimental_telemetry` and hand it Tracevell's tracer. Do this on every `generate*` and `stream*` call you want traced.
>
> ```typescript theme={null}
> import { openai } from '@ai-sdk/openai';
> import { generateText } from 'ai';
> import { getTracer } from '@lmnr-ai/lmnr';
>
> const { text } = await generateText({
>   model: openai('gpt-5-mini'),
>   prompt: 'What is tracevell flow?',
>   experimental_telemetry: {
>     isEnabled: true,
>     tracer: getTracer(),
>   },
> });
> ```
>
> The same pattern works for `streamText`, `generateObject`, `streamObject`, and `embed`.

## Turn telemetry off for one call

With `registerTelemetry`, tracing is on for every call by default. To disable it for a single call site, pass a `telemetry` block with `isEnabled: false`. This overrides the global registration for that call only.

```typescript theme={null}
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';

const { text } = await generateText({
  model: openai('gpt-5-mini'),
  prompt: 'Summarize this internal document.',
  // not traced, even though Tracevell is registered globally
  telemetry: { isEnabled: false },
});
```

The same `telemetry` block also takes `functionId`, which names the call's span:

```typescript theme={null}
const { text } = await generateText({
  model: openai('gpt-5-mini'),
  prompt: 'Draft a release note.',
  telemetry: { functionId: 'release-notes' },
});
```

> [!NOTE]
>
> On AI SDK v5 and v6, the equivalent control is the `experimental_telemetry` block: omit it (or set `isEnabled: false`) to skip tracing for a call, and pass `functionId` / `metadata` there.

## Attach metadata, sessions, user IDs, and tags

On AI SDK v7, per-call context goes in `runtimeContext`. The AI SDK only sends a `runtimeContext` key to telemetry when you opt it in with `telemetry.includeRuntimeContext`, so list every key you want in Tracevell there.

```typescript theme={null}
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';

const { text } = await generateText({
  model: openai('gpt-5-mini'),
  prompt: 'Draft a release note.',
  runtimeContext: {
    userId: 'user-123',
    sessionId: 'session-456',
    tags: ['release-notes', 'beta'],
    env: 'production',
    region: 'us-west',
  },
  telemetry: {
    functionId: 'release-notes',
    includeRuntimeContext: {
      userId: true,
      sessionId: true,
      tags: true,
      env: true,
      region: true,
    },
  },
});
```

Tracevell reads the opted-in keys like this:

| Key | Becomes |
| - | - |
| `userId` | The trace's [user ID](https://tracevell.com/docs/tracing/structure/user-id.md) |
| `sessionId` | The trace's [session ID](https://tracevell.com/docs/tracing/structure/sessions.md) |
| `tags` | [Tags](https://tracevell.com/docs/tracing/structure/tags.md) on the call's span. Must be an array of strings. |
| Any other key | A [metadata](https://tracevell.com/docs/tracing/structure/metadata.md) key-value pair on the trace |

Keys you leave out of `includeRuntimeContext` stay in your application and never reach Tracevell, so you can keep request-scoped values in `runtimeContext` without exporting them.

> [!NOTE]
>
> Reading `runtimeContext` requires `@lmnr-ai/lmnr` 0.8.49 or later.

## Configure the integration

`LaminarAiSdkTelemetry` takes an options object. All options are optional.

> [accordion] laminarOptions: configure the Tracevell SDK
>
> `laminarOptions` accepts exactly what you would pass to `Laminar.initialize()`. Use it to set the project API key, base URL, or any other init option in one place, so you don't need a separate `Laminar.initialize()` call.
>
> ```typescript theme={null}
> import { registerTelemetry } from 'ai';
> import { LaminarAiSdkTelemetry } from '@lmnr-ai/lmnr';
>
> registerTelemetry(
>   new LaminarAiSdkTelemetry({
>     laminarOptions: {
>       projectApiKey: process.env.LMNR_PROJECT_API_KEY,
>     },
>   }),
> );
> ```
>
> If Tracevell is already initialized when the integration is constructed, `laminarOptions` is ignored.

> [accordion] recordInputs and recordOutputs: control payload capture
>
> Both default to `true`. Set either to `false` to keep prompt or response content off your spans (for sensitive data or to reduce payload size). Token counts, finish reasons, latency, and cost are always recorded, regardless of these flags.
>
> ```typescript theme={null}
> registerTelemetry(
>   new LaminarAiSdkTelemetry({
>     recordInputs: false,
>     recordOutputs: false,
>   }),
> );
> ```

> [accordion] createStepSpan: add a span per step
>
> Defaults to `false`. Set it to `true` to emit an extra span for each step of a multi-step generation, so the trace shows the step boundaries explicitly in addition to the LLM and tool spans.
>
> ```typescript theme={null}
> registerTelemetry(
>   new LaminarAiSdkTelemetry({
>     createStepSpan: true,
>   }),
> );
> ```

## Flush spans before exit

In short-lived scripts and serverless runtimes, the process can exit before Tracevell finishes sending its last batch of spans. Call `Laminar.shutdown()` at the end of a script, or `Laminar.flush()` at a checkpoint you want to force out, to make sure the final spans land.

```typescript theme={null}
import { Tracevell } from '@lmnr-ai/lmnr';

// ... your AI SDK calls ...

await Laminar.shutdown();
```

This applies to both integration styles. Long-running servers don't need it: the batch processor flushes on its own schedule.

## Next.js setup

In Next.js, Tracevell lives in `instrumentation.ts`, which Next.js auto-loads before any route or server component runs.

**1. Install**

```bash theme={null}
npm install @lmnr-ai/lmnr@latest ai@latest @ai-sdk/openai@latest
```

**2. Set environment variables**

```bash theme={null}
# .env.local
LMNR_PROJECT_API_KEY=your-tracevell-project-api-key
OPENAI_API_KEY=your-openai-api-key
```

**3. Update next.config.ts**

Tell Next.js to treat Tracevell as an external server package. Tracevell depends on OpenTelemetry, which uses Node-specific APIs Next.js cannot bundle.

```typescript next.config.ts theme={null}
const nextConfig = {
  serverExternalPackages: ['@lmnr-ai/lmnr'],
};

export default nextConfig;
```

More on this option in the [Next.js docs](https://nextjs.org/docs/app/api-reference/config/next-config-js/serverExternalPackages).

**4. Initialize in instrumentation.ts**

Create `instrumentation.ts` at the project root.

> [tab] AI SDK v7
>
> ```typescript instrumentation.ts theme={null}
> export async function register() {
>   if (process.env.NEXT_RUNTIME === 'nodejs') {
>     const { registerTelemetry } = await import('ai');
>     const { LaminarAiSdkTelemetry } = await import('@lmnr-ai/lmnr');
>
>     registerTelemetry(
>       new LaminarAiSdkTelemetry({
>         laminarOptions: { projectApiKey: process.env.LMNR_PROJECT_API_KEY },
>       }),
>     );
>   }
> }
> ```

> [tab] AI SDK v5 and v6
>
> ```typescript instrumentation.ts theme={null}
> export async function register() {
>   if (process.env.NEXT_RUNTIME === 'nodejs') {
>     const { Tracevell } = await import('@lmnr-ai/lmnr');
>
>     Laminar.initialize({
>       projectApiKey: process.env.LMNR_PROJECT_API_KEY,
>     });
>   }
> }
> ```

> [!NOTE]
>
> Tracevell only runs in the `nodejs` runtime. The guard skips Edge runtime routes automatically.

> [!NOTE]
>
> If you already use `@vercel/otel` or `@sentry/nextjs`, initialize them first, then Tracevell. See [Coexisting with @vercel/otel](#coexisting-with-@vercel/otel) below.

**5. Use the AI SDK in a route handler**

> [tab] AI SDK v7
>
> ```typescript app/api/chat/route.ts theme={null}
> import { openai } from '@ai-sdk/openai';
> import { streamText } from 'ai';
>
> export async function POST(req: Request) {
>   const { messages } = await req.json();
>
>   const result = streamText({
>     model: openai('gpt-5-mini'),
>     system: 'You are a helpful assistant.',
>     messages,
>   });
>
>   return result.toTextStreamResponse();
> }
> ```

> [tab] AI SDK v5 and v6
>
> ```typescript app/api/chat/route.ts theme={null}
> import { openai } from '@ai-sdk/openai';
> import { streamText } from 'ai';
> import { getTracer } from '@lmnr-ai/lmnr';
>
> export async function POST(req: Request) {
>   const { messages } = await req.json();
>
>   const result = streamText({
>     model: openai('gpt-5-mini'),
>     system: 'You are a helpful assistant.',
>     messages,
>     experimental_telemetry: {
>       isEnabled: true,
>       tracer: getTracer(),
>     },
>   });
>
>   return result.toTextStreamResponse();
> }
> ```

## Tracing a multi-step agent

The AI SDK runs the tool-calling loop for you: the model calls a tool, the AI SDK runs the tool's `execute`, feeds the result back, and repeats until the model stops calling tools or a stop condition trips. Tracevell records every step as a span under one trace, so the transcript shows each tool call and its result in order.

Two shapes are common. `generateText` with `tools` and `stopWhen` is the workhorse. `ToolLoopAgent` is a thin wrapper if you want to reuse the same tools and instructions across many calls.

### generateText with tools

```typescript theme={null}
import { openai } from '@ai-sdk/openai';
import { generateText, stepCountIs, tool } from 'ai';
import { z } from 'zod';

const result = await generateText({
  model: openai('gpt-5-mini'),
  system:
    'You are a concise travel assistant. Call tools to look up weather and local time before answering.',
  prompt:
    'I have meetings in Paris and Tokyo tomorrow. What is the weather and local time in each city right now?',
  tools: {
    weather: tool({
      description: 'Get the current weather for a city.',
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => {
        const res = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`);
        const data = await res.json();
        const c = data.current_condition[0];
        return { tempC: Number(c.temp_C), condition: c.weatherDesc[0].value };
      },
    }),
    timezone: tool({
      description: 'Get the current local time for a city.',
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => {
        const res = await fetch(
          `https://timeapi.io/api/time/current/zone?timeZone=${encodeURIComponent(
            `Europe/${city}`,
          )}`,
        );
        return res.ok ? await res.json() : { error: 'unknown city' };
      },
    }),
  },
  stopWhen: stepCountIs(5),
  runtimeContext: { env: 'production' },
  telemetry: {
    functionId: 'travel-assistant',
    includeRuntimeContext: { env: true },
  },
});

console.log(result.text);
console.log(`Completed in ${result.steps.length} steps`);
```

`stopWhen: stepCountIs(5)` caps the loop at five model-and-tool rounds. You can combine conditions (`stopWhen: [stepCountIs(20), hasToolCall('finalize')]`) or pass a custom predicate.

> [!NOTE]
>
> On AI SDK v5 and v6, replace the `runtimeContext` and `telemetry` blocks with `experimental_telemetry: { isEnabled: true, tracer: getTracer(), functionId: 'travel-assistant', metadata: { env: 'production' } }`.

### ToolLoopAgent

Reuse the same configuration across calls by constructing a `ToolLoopAgent` once:

```typescript theme={null}
import { openai } from '@ai-sdk/openai';
import { ToolLoopAgent, stepCountIs } from 'ai';
import { weather, timezone } from './tools';

const travelAgent = new ToolLoopAgent({
  model: openai('gpt-5-mini'),
  instructions:
    'You are a concise travel assistant. Call tools to look up weather and local time before answering.',
  tools: { weather, timezone },
  stopWhen: stepCountIs(5),
});

const result = await travelAgent.generate({
  prompt: 'Weather and local time in Paris and Tokyo?',
});
```

Both shapes produce the same trace structure in Tracevell: one parent span per call, nested tool-call spans, and the final model output in the transcript.

### Decisions with experimental\_decide

`experimental_decide` answers typed questions (choice, boolean, score) about a piece of state in one call, using `openai.decisionModel('gpt-6-luna')`. With `@lmnr-ai/lmnr` 0.8.50 or later, each call becomes an `ai.decide` LLM span with the state as input, the questions in the span's output schema, and the answers as output. See [Decisions API](https://tracevell.com/docs/integrations/openai.md#decisions-api) for a full example.

## Debugging with the replay debugger

The [Tracevell debugger](https://tracevell.com/docs/debugger/introduction.md) records a run, lets you edit your code, and replays from a cached checkpoint so unchanged LLM calls are served from cache. For the AI SDK, caching needs one extra step: wrap each model with `wrapLanguageModel`, which hashes the call's input and asks the Tracevell backend for a cached response. The telemetry wiring above is unchanged.

```typescript theme={null}
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { wrapLanguageModel } from '@lmnr-ai/lmnr';

const { text } = await generateText({
  // wrapLanguageModel is what enables debugger caching
  model: wrapLanguageModel(openai('gpt-5-mini')),
  prompt: 'What is tracevell flow?',
});
```

See [AI SDK caching](https://tracevell.com/docs/debugger/setup.md#ai-sdk-caching) for the full debugger setup, including the v5 and v6 telemetry variant.

## See what happened in a trace

Open the trace in Tracevell and you get the transcript view: system prompt, user messages, model output, tool calls, and tool results laid out as a conversation. Sub-agents collapse to their input and final output so you read what mattered, not a tree of span names.

![Vercel AI SDK trace in Tracevell](https://mintcdn.com/laminarai/RPYdoMdfsv6y-QUL/images/tutorials/vercel-ai-sdk-trace.png?fit=max\&auto=format\&n=RPYdoMdfsv6y-QUL\&q=85\&s=af7b8310f98f02d8e65fd06b9a14829e)

Vercel AI SDK trace in Tracevell: the generateText call with tool invocations, the model response, and each step shown as a transcript entry.

Tool calls appear as nested spans with their arguments and return values captured. More on the trace UX: [Viewing traces](https://tracevell.com/docs/platform/viewing-traces.md).

## Track outcomes with Signals

Traces answer *what happened on this run*. **[Signals](https://tracevell.com/docs/signals/introduction.md) answer the cross-trace question**: *how often does the agent recommend a product that wasn't in stock, when does a tool call return an empty result, which generateText calls fan out into more steps than expected*. A Signal pairs a plain-language instruction with a JSON output schema. Tracevell runs it live on new traces (Triggers) or backfills it across history (Jobs) and records a structured event every time it matches. From there you [query](https://tracevell.com/docs/platform/sql-editor.md), [cluster](https://tracevell.com/docs/signals/clusters.md), and [alert](https://tracevell.com/docs/signals/alerts.md) on events across every trace.

> [!NOTE]
>
> Every new project ships with a **Failure Detector** Signal that categorizes issues on any trace over 1000 tokens. Open it from the Signals sidebar to see events as soon as your AI SDK traces arrive.

## Query across traces

* **[SQL editor](https://tracevell.com/docs/platform/sql-editor.md)** for ad-hoc queries across traces, spans, signals, and evals.
* **SQL API** for programmatic access from scripts and pipelines.
* **[CLI](https://tracevell.com/docs/platform/cli.md)** (`lmnr-cli sql query`) for terminal-driven queries and piping JSON into shell tools or coding agents.
* **[MCP server](https://tracevell.com/docs/platform/mcp.md)** to query Tracevell from Claude Code, Cursor, or Codex.

## Grouping calls inside one route

If a single route makes multiple LLM calls, wrap them in `observe` to group them under one parent span.

```typescript app/api/chat/route.ts theme={null}
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { observe } from '@lmnr-ai/lmnr';
import { NextResponse } from 'next/server';

export const POST = async (req: Request) => {
  const { topic } = await req.json();

  const result = await observe(
    { name: 'POST /api/chat' },
    async () => {
      const { text: outline } = await generateText({
        model: openai('gpt-5-mini'),
        prompt: `Outline an article about ${topic}.`,
      });

      const { text: draft } = await generateText({
        model: openai('gpt-5-mini'),
        prompt: `Write a draft based on: ${outline}`,
      });

      return { outline, draft };
    },
  );

  return NextResponse.json(result);
};
```

You also get grouping for free if another tracing library (for example `@vercel/otel` or `@sentry/nextjs`) is already wrapping your handler.

See the full [observe reference](https://tracevell.com/docs/tracing/structure/observe-decorator.md) for session IDs, user IDs, metadata, and tags on the parent span. For per-call context on AI SDK v7, use [`runtimeContext`](#attach-metadata-sessions-user-ids-and-tags).

## Coexisting with @vercel/otel

If you already register a tracer provider with `@vercel/otel`, do not call `Laminar.initialize()` (that would register a second provider). Plug Tracevell's span processor into the existing one instead:

```typescript instrumentation.ts theme={null}
import { registerOTel } from '@vercel/otel';

export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    const { registerTelemetry } = await import("ai");
    const { initializeLaminarInstrumentations, LaminarAiSdkTelemetry } =
      await import('@lmnr-ai/lmnr');

    // Next.js telemetry
    registerOTel({
      serviceName: 'my-service',
      instrumentations: initializeLaminarInstrumentations(),
    });

    // Tracevell AI SDK telemetry. Or `Laminar.initialize()`
    // for AI SDK before v7.
    registerTelemetry(new LaminarAiSdkTelemetry());
  }
}
```

On AI SDK v7, register Tracevell telemetry after this so calls are traced without a per-call tracer. On v5 and v6, keep passing `getTracer()` per call.

## Migrating from v5 or v6 to v7

If you already trace the AI SDK with the `experimental_telemetry` + `getTracer()` pattern, here is how to move to the v7 style. The trace shapes are nearly identical, so your existing traces, Signals, and dashboards keep working.

**1. Upgrade the AI SDK**

Update `ai`, `@lmnr-ai/lmnr`, and every `@ai-sdk/*` provider package you use to the latest version.

```bash theme={null}
npm install ai@latest @lmnr-ai/lmnr@latest @ai-sdk/openai@latest
```

**2. Register Tracevell once instead of initializing**

Replace the `Laminar.initialize()` call at your entry point with a single `registerTelemetry` call. The integration initializes Tracevell for you, so move any `Laminar.initialize()` options into `laminarOptions`.

```typescript theme={null}
// Before (v5 / v6)
import { Tracevell } from '@lmnr-ai/lmnr';
Laminar.initialize({ projectApiKey: process.env.LMNR_PROJECT_API_KEY });

// After (v7)
import { registerTelemetry } from 'ai';
import { LaminarAiSdkTelemetry } from '@lmnr-ai/lmnr';
registerTelemetry(
  new LaminarAiSdkTelemetry({
    laminarOptions: { projectApiKey: process.env.LMNR_PROJECT_API_KEY },
  }),
);
```

**3. Drop experimental\_telemetry from your call sites**

Once Tracevell is registered, every call is traced. Remove the per-call `experimental_telemetry` block.

```typescript theme={null}
// Before (v5 / v6)
const { text } = await generateText({
  model: openai('gpt-5-mini'),
  prompt: 'What is tracevell flow?',
  experimental_telemetry: { isEnabled: true, tracer: getTracer() },
});

// After (v7)
const { text } = await generateText({
  model: openai('gpt-5-mini'),
  prompt: 'What is tracevell flow?',
});
```

**4. Move any per-call settings to the telemetry block**

If a call passed `functionId` through `experimental_telemetry`, or you want to turn tracing off for it, use the `telemetry` block instead. Metadata moves to `runtimeContext`, with each key opted in through `telemetry.includeRuntimeContext` (see [Attach metadata, sessions, user IDs, and tags](#attach-metadata-sessions-user-ids-and-tags)).

```typescript theme={null}
const { text } = await generateText({
  model: openai('gpt-5-mini'),
  prompt: 'Draft a release note.',
  runtimeContext: { env: 'production' },
  telemetry: {
    functionId: 'release-notes',
    includeRuntimeContext: { env: true },
  },
  // or: telemetry: { isEnabled: false } to skip this call
});
```

**5. Keep wrapLanguageModel for the debugger**

The [debugger](https://tracevell.com/docs/debugger/setup.md#ai-sdk-caching) still needs `wrapLanguageModel` around each model for caching. That step is unchanged across versions.

**6. Keep flushing at exit**

`await Laminar.shutdown()` (or `Laminar.flush()`) is still required at the end of short-lived scripts and serverless runtimes so the last spans are sent.

## Troubleshooting

> [accordion] I don't see any traces in Tracevell
>
> * Confirm `LMNR_PROJECT_API_KEY` is set in the runtime environment, not just your shell.
> * Tracevell must be registered (v7: `registerTelemetry(new LaminarAiSdkTelemetry())`) or initialized (v5/v6: `Laminar.initialize()`) before the first AI SDK call. In Next.js, that means it lives in `instrumentation.ts`.
> * On v5 and v6, `experimental_telemetry` is opt-in per call. If you forget to pass `{ isEnabled: true, tracer: getTracer() }`, the call is not traced. On v7, check you didn't pass `telemetry: { isEnabled: false }`.
> * Short-lived scripts can exit before spans flush. Call `await Laminar.shutdown()` at the end.
> * Edge runtime is not supported. Make sure your route runs in `nodejs`.

> [accordion] Build fails with OpenTelemetry / require errors
>
> Add `@lmnr-ai/lmnr` to `serverExternalPackages` in `next.config.ts`. OpenTelemetry uses Node-specific APIs that Next.js cannot bundle.

> [accordion] Using Next.js \< 15
>
> `instrumentation.ts` is experimental before Next.js 15. Enable it in `next.config.js`:
>
> ```javascript next.config.js theme={null}
> module.exports = {
>   experimental: { instrumentationHook: true },
> };
> ```

> [accordion] Mixing the AI SDK with a direct OpenAI or Anthropic client
>
> Direct SDK calls are auto-instrumented by `Laminar.initialize()` in Node.js. In Next.js, imports inside `instrumentation.ts` are not visible to the rest of the app, so auto-instrumentation may miss them. Call `Laminar.patch({ OpenAI, anthropic })` where you construct the client:
>
> ```typescript lib/llm-clients.ts theme={null}
> import { OpenAI } from 'openai';
> import * as anthropic from '@anthropic-ai/sdk';
> import { Tracevell } from '@lmnr-ai/lmnr';
>
> Laminar.patch({ OpenAI, anthropic });
>
> export const openai = new OpenAI();
> export const claude = new anthropic.Anthropic();
> ```

## What's next

> [card] [Viewing traces](https://tracevell.com/docs/platform/viewing-traces.md)
>
> Read the transcript view, filter, and search across traces.

> [card] [Signals](https://tracevell.com/docs/signals/introduction.md)
>
> Detect behaviors and failures across every run, then query, cluster, and alert on them.

> [card] [SQL editor and MCP server](https://tracevell.com/docs/platform/sql-editor.md)
>
> Query traces programmatically from the UI, API, or your IDE.

> [card] [Tracing structure](https://tracevell.com/docs/tracing/structure/overview.md)
>
> Sessions, user IDs, metadata, and tags.

## Related integrations

> [card] [Mastra](https://tracevell.com/docs/integrations/mastra.md)
>
> Trace Mastra agents, workflows, and tools in TypeScript.

> [card] [eve](https://tracevell.com/docs/integrations/eve.md)
>
> Trace eve durable agents, their turns, and tool calls.

> [card] [OpenAI](https://tracevell.com/docs/integrations/openai.md)
>
> Mixing the AI SDK with the OpenAI SDK directly? Trace it here.

> [card] [Anthropic](https://tracevell.com/docs/integrations/anthropic.md)
>
> Mixing the AI SDK with the Anthropic SDK directly? Trace it here.

> [card] [OpenAI Agents SDK](https://tracevell.com/docs/integrations/openai-agents-sdk.md)
>
> Trace agent runs, handoffs, and tool calls.

> [card] [All integrations](https://tracevell.com/docs/integrations.md)
>
> Browse every provider, framework, coding agent, and browser integration Tracevell supports.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.