How do I trace the OpenAI API in TypeScript?
Rius traces OpenAI calls in TypeScript once you install the SDK and the OpenAI instrumentation package and call init() before you create the client. Your call sites stay unchanged. Each chat completion arrives as a span with the model, token usage, input and output messages, and a cost that Rius computes.
How it works
The TypeScript SDK, @glassflow-ai/rius, has no extras. The OpenAI integration is an optional peer dependency, @arizeai/openinference-instrumentation-openai, that you install yourself. A plain install of the SDK patches nothing.
init()is synchronous. It enables the OpenAI integration if that package is installed, and it patches the client library for the whole process.- Attaching happens asynchronously. Await
readyto get the names of the integrations that attached, so you can confirmopenaiis among them. - Call
init()before you create the client, so the patch is in place when you make a request. - Cost is never something you send. Rius computes it from the model and the token counts.
The integration works in CommonJS and pure-ESM apps. In a CommonJS app, require openai before you call init(). A require that happens only after init() stays untraced, even though ready lists openai. The OpenAI integration page covers this rule and the rest of the setup.
Set it up
- Install the SDK with its OpenTelemetry API peer, then the OpenAI instrumentation. Your project already has the
openaipackage. The full install options are on the installation page.
npm install @glassflow-ai/rius @opentelemetry/api
npm install @arizeai/openinference-instrumentation-openai-
Call
init()once, as early as possible, and give the agent a constantserviceName. That name is how you find your agent in trace filters and cost breakdowns. Pass your Rius API key, which starts withri_. -
Make a call. Wrapping it in
observenests the model call inside your function’s run instead of leaving it as a standalone trace. Name the wrapped function, because an anonymous one traces as anonymous.
import { init, observe } from '@glassflow-ai/rius'
import OpenAI from 'openai'
const rius = init({ apiKey: 'ri_...', serviceName: 'my-agent' })
await rius.ready // the OpenAI patch is in place before the first call
const client = new OpenAI() // reads OPENAI_API_KEY
const handle = observe(async function handle(query: string) {
const completion = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: query }],
})
return completion.choices[0].message.content ?? ''
})
await handle('What should I check when a collector drops spans?')
await rius.shutdown() // export queued spans before the process exits-
If you stream, ask OpenAI to send usage with the final chunk by setting
stream_optionsto includeinclude_usageset to true. Without it, a streamed span has no token counts and so no cost. -
If you deploy a pnpm app to a file-tracing host, such as a Vercel function, the SDK may not find the package, and
readyleaves outopenai. Pass the modules you import withinstrumentModules. The integrations page shows how.
What you get
Run one call, then open the trace in the console. You see:
- A span for each model call, with the resolved model name (for example
gpt-4o-mini-2024-07-18), input and output token counts, and the input and output messages. - A cost for each call, in US dollars. It is a list-price estimate from the model and token counts, not a billed amount.
- Your
handlefunction as the parent, with the model call nested inside it. - Failed calls as ERROR spans. A call OpenAI rejects, such as a 404 for an unknown model, a 429 or a timeout, is recorded with
error.typenaming the error class, for exampleopenai.NotFoundError. This needs@arizeai/openinference-instrumentation-openai4.4.1 or newer.
Prompt and response content follows your privacy controls. Set captureContent: false and prompts and responses are stripped at export, while metadata still flows. A mask callback redacts content attributes at export.
When something else fits
- Streamed chat completions in TypeScript carry no time to first token. If you need it, trace the streamed call as a manual generation, as described on the OpenAI integration page.
- If you call models through the Vercel AI SDK or LangChain, or you work in Python, use that integration’s own recipe from the Integrations page.