Skip to main content

OpenAI

The @mutagent/openai package provides observeOpenAI(), a wrapper that adds automatic tracing to any OpenAI client instance. Using JavaScript Proxy, it intercepts all method calls without replacing the client — chat completions, embeddings, images, audio, moderations, and every other SDK method work exactly as before, with full tracing.

Installation

bun add @mutagent/openai @mutagent/sdk
Peer dependencies: @mutagent/sdk >=0.1.0, openai >=4.0.0

Quick Start

1

Initialize tracing

Call initTracing() once at application startup.
import { initTracing } from '@mutagent/sdk/tracing';

initTracing({ apiKey: process.env.MUTAGENT_API_KEY! });
2

Wrap your OpenAI client

Call observeOpenAI() to wrap your existing client. All methods are preserved.
import OpenAI from 'openai';
import { observeOpenAI } from '@mutagent/openai';

const openai = observeOpenAI(new OpenAI());
3

Use as normal

Every API call is automatically traced — no code changes needed.
const completion = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Hello!' }],
});

console.log(completion.choices[0].message.content);

Full Example

import { initTracing } from '@mutagent/sdk/tracing';
import OpenAI from 'openai';
import { observeOpenAI } from '@mutagent/openai';

// 1. Initialize SDK tracing (once at app startup)
initTracing({ apiKey: process.env.MUTAGENT_API_KEY! });

// 2. Wrap the OpenAI client
const openai = observeOpenAI(new OpenAI());

// 3. All methods are traced automatically
const completion = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Explain observability in 3 sentences.' },
  ],
});

// Embeddings — also traced
const embedding = await openai.embeddings.create({
  model: 'text-embedding-3-small',
  input: 'The quick brown fox',
});

// Images — also traced
const image = await openai.images.generate({
  model: 'dall-e-3',
  prompt: 'A sunset over mountains',
});

Streaming

Streaming is fully supported. The wrapper intercepts the async iterator to accumulate response text and closes the span when the stream completes.
const stream = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Tell me a story' }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content || '');
}
// Span is automatically closed when the stream ends
The stream wrapper preserves the original async iterator interface. Your existing streaming code works without changes.

Options

Pass options to observeOpenAI() for session tracking and custom span naming:
const openai = observeOpenAI(new OpenAI(), {
  generationName: 'my-app',     // Custom span name prefix
  sessionId: 'chat-session-123', // Group traces by session
  userId: 'user-456',           // Attribute traces to a user
  tags: ['production', 'v2'],   // Filter traces by tag
});
OptionTypeDescription
generationNamestringCustom span name prefix. Defaults to auto-detected method path (e.g., chat.completions.create)
sessionIdstringGroup related traces into a session
userIdstringAttribute traces to a specific user
tagsstring[]Tags for filtering in the dashboard

What Gets Traced

The Proxy wrapper intercepts all method calls on the OpenAI client. Known methods get structured tracing:
MethodSpan KindData Captured
chat.completions.createllm.chatInput messages, output messages, model, token usage
completions.createllm.completionInput text, output text, model, token usage
embeddings.createllm.embeddingInput text, embedding dimensions, model, token usage
All other methodsllm.chatRaw request/response data
Streaming callsSame as aboveAccumulated text, model
ErrorsAnyError message, status set to error

Token Usage Tracking

For non-streaming calls, token metrics are automatically extracted from the OpenAI response:
  • inputTokensusage.prompt_tokens
  • outputTokensusage.completion_tokens
  • totalTokensusage.total_tokens
The model and provider (always "openai") are recorded in span metrics.
For streaming calls, token usage is available when you set stream_options: { include_usage: true } in the request parameters.

How It Works

observeOpenAI() uses a JavaScript Proxy that:
  1. Intercepts property access on the OpenAI client via the get trap
  2. Recursively wraps nested objects — accessing client.chat returns a new Proxy, so client.chat.completions.create() is intercepted
  3. Wraps function calls with startSpan() / endSpan() from @mutagent/sdk/tracing
  4. Handles streaming by wrapping AsyncIterable responses to accumulate text before closing the span
This means every method on the OpenAI SDK is automatically traced — including methods added in future SDK versions.

Migration from MutagentOpenAI

MutagentOpenAI is deprecated. It only traced chat.completions.create() — all other SDK methods were missing. Use observeOpenAI() instead.
Before (deprecated):
import { MutagentOpenAI } from '@mutagent/openai';
const client = new MutagentOpenAI({ apiKey: '...' });
client.chat.completions.create(...)  // ✅ traced
client.embeddings.create(...)         // ❌ not available
After (recommended):
import OpenAI from 'openai';
import { observeOpenAI } from '@mutagent/openai';
const client = observeOpenAI(new OpenAI());
client.chat.completions.create(...)    // ✅ traced
client.embeddings.create(...)           // ✅ traced
client.images.generate(...)             // ✅ traced

CLI Shortcut

mutagent integrate openai
This auto-detects the openai package in your package.json and generates ready-to-use configuration code.

Python

Looking for the Python OpenAI integration? See the Python OpenAI guide.