Skip to Content
Rius

Which OpenTelemetry attribute should my spans use for model name?

Rius reads the model name from the GenAI convention key gen_ai.request.model, so set that on every span for a model call. Rius also reads gen_ai.response.model for the model the provider reports, and it accepts OpenInference’s llm.* model attributes by moving them onto the GenAI keys at ingest. Cost is computed from gen_ai.request.model plus the token counts, so this one attribute decides whether a call gets a cost.

How it works

  • Request model. gen_ai.request.model is the model you asked for. Rius uses it for model analytics, for the per-model cost breakdown and, with the token counts, for the cost of the call.
  • Response model. gen_ai.response.model is the model the provider reports. Send it when you have it. It is separate from the request model and does not replace it.
  • Counting LLM calls. A span with neither a span kind nor an operation name still counts as an LLM call when it carries gen_ai.request.model.
  • OpenInference attributes. For spans from OpenInference, Rius fills the request model from llm.request.model_name, else the model member of llm.invocation_parameters, else llm.model_name. The response model comes from llm.response.model_name, else llm.model_name, but only when the span’s status is OK. On an ERROR or UNSET span, llm.model_name may still hold the requested model, so Rius does not read it as the response model.
  • Vercel AI SDK. For versions 5 and 6, the ai.model.* attributes are normalized at ingest, along with tokens and messages.
  • Which key wins. If a span carries both a native gen_ai.* key and an OpenInference name, the native key wins. Normalization fills gaps and never overwrites. Rius removes the source keys it mapped and stores the value under the GenAI key.
  • Where to search. Search and filter on gen_ai.request.model, not on llm.model_name.

The Rius SDKs apply the same OpenInference mapping in your process before export, except for the llm.model_name fallback, which only the platform applies. The full list of attributes is in the span attribute reference.

Set it up

  1. Set gen_ai.request.model on the span for each model call, with the token counts beside it. A span named the way the GenAI conventions ask, such as chat gpt-4o, reads the same as spans from the Rius SDKs. Here is an example span, shown as its attributes:
{ "gen_ai.operation.name": "chat", "gen_ai.provider.name": "openai", "gen_ai.request.model": "gpt-4o", "gen_ai.response.model": "gpt-4o-2024-08-06", "gen_ai.usage.input_tokens": 812, "gen_ai.usage.output_tokens": 164 }
  1. Set gen_ai.operation.name to chat, text_completion or generate_content, or set openinference.span.kind to LLM. Setting either one makes Rius derive the other.
  2. Send token counts as gen_ai.usage.input_tokens and gen_ai.usage.output_tokens. Never send a cost attribute. Rius computes cost from the model and the counts, and a span with no model, a model the price catalog does not know, or no counts gets no cost.
  3. If you export without the Rius SDKs, raise OpenTelemetry’s attribute limit. OpenTelemetry SDKs keep at most 128 attributes per span and drop the rest without an error, and a span that loses its model attribute gets no model analytics or cost:
export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=4096
  1. Point your exporter at Rius. Trace LLM calls with OpenTelemetry covers the endpoint, headers and each kind of sender.

What you get

  • Model analytics and a per-model cost breakdown, grouped by gen_ai.request.model.
  • A cost for each call, from the request model and the token counts, each count priced at its own rate.
  • Spans that follow the GenAI conventions are counted as LLM calls in the console and in alerts.
  • Spans from OpenInference land on the same model key as spans that already use the GenAI convention, so one filter finds both.
  • The requested and the served model side by side when you send gen_ai.response.model.

When something else fits

  • If your spans come from OpenInference or the Vercel AI SDK 5 or 6, you can leave their model attributes as they are, because Rius maps them to gen_ai.request.model at ingest.
  • If you write the instrumentation yourself, set gen_ai.request.model directly rather than an OpenInference name, so the span needs no mapping.

Still stuck? Get help

Last updated on