Skip to Content
Rius

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 ready to get the names of the integrations that attached, so you can confirm openai is 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

  1. Install the SDK with its OpenTelemetry API peer, then the OpenAI instrumentation. Your project already has the openai package. The full install options are on the installation page.
npm install @glassflow-ai/rius @opentelemetry/api npm install @arizeai/openinference-instrumentation-openai
  1. Call init() once, as early as possible, and give the agent a constant serviceName. That name is how you find your agent in trace filters and cost breakdowns. Pass your Rius API key, which starts with ri_.

  2. Make a call. Wrapping it in observe nests 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
  1. If you stream, ask OpenAI to send usage with the final chunk by setting stream_options to include include_usage set to true. Without it, a streamed span has no token counts and so no cost.

  2. If you deploy a pnpm app to a file-tracing host, such as a Vercel function, the SDK may not find the package, and ready leaves out openai. Pass the modules you import with instrumentModules. 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 handle function 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.type naming the error class, for example openai.NotFoundError. This needs @arizeai/openinference-instrumentation-openai 4.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.

Still stuck? Get help

Last updated on