How does Rius read OpenInference prompt cache token attributes?
Rius reads the cache details that OpenInference records under its llm.token_count.* attributes and moves them onto the gen_ai.usage.* token keys when a span is ingested. Cache reads and writes are then priced at their own rates in the cost Rius computes. In the console you search and filter on the gen_ai.* keys, not on the llm.token_count.* names you sent.
How it works
OpenInference instrumentors write the prompt and completion counts as llm.token_count.prompt and llm.token_count.completion, with cache and reasoning details alongside them under llm.token_count.*. At ingest, Rius normalizes these onto the canonical keys and removes the source keys it mapped:
llm.token_count.promptfillsgen_ai.usage.input_tokens. This count includes cached tokens.llm.token_count.completionfillsgen_ai.usage.output_tokens. This count includes reasoning tokens.llm.token_count.prompt_details.cache_readandllm.token_count.prompt_details.cache_writefillgen_ai.usage.cache_read.input_tokensandgen_ai.usage.cache_write.input_tokens. Both are subsets of the input count.llm.token_count.completion_details.reasoningfillsgen_ai.usage.reasoning.output_tokens, a subset of the output count.
The cache-write count also has an older GenAI spelling, gen_ai.usage.cache_creation.input_tokens. Rius normalizes that onto gen_ai.usage.cache_write.input_tokens too.
Normalization fills gaps and never overwrites. If a span already carries a native gen_ai.* key, that value wins.
Cost follows from the normalized values. Rius computes it on the server from the request model and the token counts: input, output, cache read and cache write, each priced at its own rate. For an OpenInference span, the request model comes from llm.request.model_name, else the model member of llm.invocation_parameters, else llm.model_name. A span needs a request model and at least one count. Any count it leaves out is read as 0. A model the price catalog does not know gets no cost. Costs are list-price estimates in US dollars, and you never send a cost attribute.
The Rius SDKs apply the same OpenInference mapping inside your process before export. The only difference is that they skip the llm.model_name fallback, which only the platform applies.
Set it up
- Wire an OpenInference instrumentor to the Rius ingest endpoint without the SDK. Raise the span attribute limit in the same place, because OpenInference writes one attribute per message and tool field and OpenTelemetry keeps only 128 per span by default:
from opentelemetry.sdk.trace import SpanLimits, TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from openinference.instrumentation.openai import OpenAIInstrumentor
# Raise the span attribute limit, so long agent loops keep every attribute
provider = TracerProvider(span_limits=SpanLimits(max_span_attributes=4096))
provider.add_span_processor(
BatchSpanProcessor(
OTLPSpanExporter(
endpoint="https://ingest.eu.console.rius-glassflow.com/v1/traces",
headers={"Authorization": "Bearer <your API key>"},
)
)
)
OpenAIInstrumentor().instrument(tracer_provider=provider)- In any other language, raise the limit through the environment instead:
export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=4096-
Make a call that uses prompt caching. Rius reads the cache counts the instrumentor recorded on the span. You set nothing for them.
-
Search and filter on the canonical keys, such as
gen_ai.usage.input_tokensandgen_ai.usage.cache_read.input_tokens. A filter onllm.token_count.promptmatches nothing, because Rius removes that key after mapping it.
The full wiring for this sender, including Node exporter choices, is on the OpenInference page. If your app is Python and you want the SDK to wire the exporter and the attribute limit for you, use the SDK integrations instead.
What you get
- Token usage on OpenInference spans, including the cache read and cache write counts, stored under the
gen_ai.usage.*keys. - Server-side cost for each call, with cache reads and writes priced at their own rates, as long as the span has a known request model and at least one count.
- The same model analytics that native spans get, because the values sit on the same keys.
- One set of keys to search and filter on across senders. OpenInference, OpenLLMetry and native GenAI spans all end up under the canonical names.
The span attribute reference lists every normalized family and the native-wins rule in its supported-conventions matrix. For how this sender compares with the SDK and other paths, see Trace LLM calls with OpenTelemetry. For the interoperability section as a whole, see Interoperability.
When something else fits
- If you already emit the
gen_ai.usage.*keys, includinggen_ai.usage.cache_read.input_tokensandgen_ai.usage.cache_write.input_tokens, Rius uses them as sent and no OpenInference mapping is involved. - If you want the attribute limit, privacy controls and exporter wired for you in a Python or TypeScript app, the Rius SDK integrations cover the same instrumentors.