Skip to Content
Rius

What does Rius record when my agent calls the OpenAI API?

Rius records each OpenAI chat completion as an LLM span with the model, token usage, and input and output messages, then computes cost from the model and tokens. Request parameters, finish reasons, the completion id and tool definitions land on canonical GenAI keys. A call OpenAI rejects becomes an ERROR span. Streamed calls need one option to carry token counts, covered below.

How it works

When you call init(), the Rius SDK detects the OpenAI instrumentation and patches the client library process-wide. Every call from anywhere in your process is traced, and your call sites stay as they are. The OpenAI integration is one of several on the Integrations page.

Each chat completion becomes one span of LLM kind. It carries:

  • Model. The resolved model name, for example gpt-4o-mini-2024-07-18.
  • Token usage. Input and output token counts, which are the inputs Rius needs for cost.
  • Messages. The input and output messages of the call.
  • Request details. The request parameters, finish reasons, the completion id and the tool definitions, mapped onto the canonical GenAI keys.
  • Errors. A call OpenAI rejects, such as a 404 for an unknown model, a 429 or a timeout, is recorded as an ERROR span. error.type names the error class, for example openai.NotFoundError.
  • Time to first token. On streamed calls, the Python integration records it with no manual code.

Cost is not something you send. Rius computes it from the model and token counts as a list-price estimate in US dollars, using LiteLLM’s public model-price catalog. A model the catalog does not know gets an empty cost, not zero.

If a wrapper such as LangChain or LiteLLM sits over the OpenAI call, both spans report the call’s tokens. Rius counts each call once in token and cost totals, and the wrapper span still shows the run’s total.

Set it up

  1. Install the instrumentation. The extra installs the instrumentation, not openai itself, so name the package in a fresh environment. Without openai, init() logs a DependencyConflict and the integration stays off.
pip install "glassflow-rius[openai]" openai
  1. Call init() before you make requests, then use the client as before.
import rius from openai import OpenAI rius.init(api_key="ri_...", service_name="my-agent") client = OpenAI() # use the client exactly as before
  1. In TypeScript, install the instrumentation peer. It must be version 4.4.1 or newer. Call init() before you create the client.
npm install @arizeai/openinference-instrumentation-openai
import { init } from '@glassflow-ai/rius' import OpenAI from 'openai' const rius = init({ apiKey: 'ri_...', serviceName: 'my-agent' }) const client = new OpenAI() // use the client exactly as before
  1. In TypeScript, await rius.ready to see which integrations attached. It tells you whether openai is among them. In a CommonJS app, require openai before init(), because a require that happens after init() stays untraced. On a pnpm app deployed to a file-tracing host, such as a Vercel function, pass your imported modules with instrumentModules. The Integrations page shows how.

  2. For streamed calls, ask OpenAI to send usage with the final chunk. Without it, streamed spans have no token counts, and no tokens means no cost.

stream = client.chat.completions.create( model="gpt-4o-mini", messages=messages, stream=True, stream_options={"include_usage": True}, )
const stream = await client.chat.completions.create({ model: 'gpt-4o-mini', messages, stream: true, stream_options: { include_usage: true }, })
  1. Run one call and open the trace in the console. The span shows the resolved model, the token counts and a cost. Wrap the call in observe to see it nested inside your agent’s run instead of as a standalone trace.

What you get

  • Every OpenAI call in your process appears as a span with its model, tokens, messages and cost, with no changes at the call sites.
  • Cost per call as a list-price estimate, computed by Rius from the model and tokens.
  • Failed calls show up as ERROR spans with the error class in error.type, so rate limits, timeouts and unknown models are easy to find.
  • Tool definitions and request parameters sit next to the call, so you can see what the model was offered and how it was configured.
  • Token and cost totals that count each model call once, even under a wrapper framework.

Known limits to plan around:

  • A streamed call without include_usage has no token counts and so no cost.
  • In TypeScript, streamed chat completions carry no time to first token, because the OpenInference OpenAI instrumentation for Node does not record it. They do carry token usage and cost when you set include_usage.
  • A few setups still count a call twice. The Integrations page links to the cases and says which layer to keep.
  • Prompt and response content follows your privacy controls. capture_content=False (captureContent: false in TypeScript) and masking apply to instrumented calls exactly like manual ones.

When something else fits

  • If you need time to first token on streamed calls in TypeScript, trace that call as a manual generation instead of relying on the integration.
  • If your OpenAI calls run through a framework such as the OpenAI Agents SDK or CrewAI, start from that framework’s page on Integrations, since it covers what lands in the trace for that setup.
  • If you need exact billed amounts, use your provider’s own billing, because Rius costs are list-price estimates.

Still stuck? Get help

Last updated on