SDK Reference
OpenAI Wrapping

OpenAI Wrapping

@grounded/tracing includes a lightweight OpenAI wrapper for two call families:

  • responses.create
  • chat.completions.create

Basic Use

import OpenAI from 'openai';
import { GroundedTracing } from '@grounded/tracing';
 
const tracing = new GroundedTracing({
  publishableKey: process.env.GROUNDED_PUBLISHABLE_KEY!,
});
 
const openai = tracing.wrapOpenAI(new OpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
}));

Best Practice: Run Inside A Trace Context

Use trace.run() so the wrapper can always find the active trace:

const trace = tracing.startTrace({
  name: 'support.reply',
  sessionId: 'chat_123',
  userId: 'user_123',
  resource: { agentId: 'agt_123' },
});
 
await trace.run(async () => {
  await openai.responses.create({
    model: 'gpt-5-mini',
    input: 'where is my refund?',
  });
});
 
await trace.end({ status: 'ok' });
await tracing.flush();

What The Wrapper Captures

For each wrapped call, Grounded creates a span with:

  • span name such as openai.responses.create
  • kind: 'llm'
  • input payload
  • extracted output text when possible
  • token usage when the response exposes it
  • error state if the request throws

Context Resolution

The wrapper looks for a trace in this order:

  1. the active AsyncLocalStorage trace created by trace.run()
  2. if there is exactly one active trace in the process, that trace

If no active trace can be resolved, the OpenAI call still works, but no Grounded span is created.

Example With Explicit Span Around OpenAI

const trace = tracing.startTrace({
  name: 'support.reply',
  sessionId: 'chat_123',
  userId: 'user_123',
  resource: { agentId: 'agt_123' },
});
 
try {
  await trace.run(async () => {
    const response = await openai.chat.completions.create({
      model: 'gpt-5-mini',
      messages: [
        { role: 'user', content: 'where is my refund?' },
      ],
    });
 
    trace.score({
      name: 'llm_succeeded',
      value: true,
      dataType: 'boolean',
    });
 
    await trace.end({
      status: 'ok',
      output: response,
    });
  });
} catch (error) {
  await trace.end({
    status: 'error',
    error,
  });
  throw error;
}

Limits

  • the wrapper only covers the two call families above
  • it does not auto-create the outer trace for you
  • it does not infer agentId; you still need that on the trace resource