OpenAI Wrapping
@grounded/tracing includes a lightweight OpenAI wrapper for two call families:
responses.createchat.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:
- the active
AsyncLocalStoragetrace created bytrace.run() - 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