How does Rius read OpenTelemetry GenAI prompt and completion message attributes?
Rius reads prompt and completion messages from the gen_ai.input.messages and gen_ai.output.messages attributes and shows them on each span’s Input / Output tab. Both hold a JSON array of messages in the OpenTelemetry GenAI message shape. If you send older or flattened spellings from OpenLLMetry or OpenInference, Rius reassembles them into that same shape at ingest.
How it works
Rius reads these attributes on the span for each model call:
gen_ai.input.messagesholds the prompt messages.gen_ai.output.messagesholds the completion messages.gen_ai.system_instructionsholds a system prompt sent separately from the chat history, as a JSON array of text parts. Rius counts it as the system prompt when it breaks down a call’s context.gen_ai.tool.definitionsholds the tool definitions the model was shown.
The two message attributes carry a JSON array. Each message is an object with a role and a list of parts. A text part has the type text and a content value. Tool interactions have their own part types: tool_call carries an id, a name and arguments, and tool_call_response carries the id and the response.
Rius also reads other spellings and normalizes them at ingest. Normalizing means Rius moves the values onto their canonical keys before storage and removes the source keys it mapped:
- From OpenInference, the flattened
llm.input_messages.*andllm.output_messages.*attributes are reassembled into the GenAI message shape. Anthropic’ssystemrequest parameter becomes the leading system message. - From OpenLLMetry, the flattened
gen_ai.prompt.*andgen_ai.completion.*attributes are reassembled into the same shape. - Native
gen_ai.*keys always win. Normalization fills gaps and never overwrites a value you sent.
For a tool span, gen_ai.tool.call.arguments and gen_ai.tool.call.result move to input.value and output.value, so the tool’s input and output render like any other span. Search on input.value and output.value for those.
Before storage, Rius replaces inline media bytes in messages with a placeholder that records the media type and size. It never stores credential header attributes.
Set it up
-
Send your spans to the Rius OTLP endpoint over HTTP, with protobuf recommended and gzip accepted. There is no gRPC endpoint. The span attribute reference covers the full contract, and Interoperability links to a setup guide for each sender.
POST https://ingest.eu.console.rius-glassflow.com/v1/traces Authorization: Bearer <your API key> Content-Type: application/x-protobuf -
On the span for each model call, set
gen_ai.input.messagesandgen_ai.output.messagesas JSON strings in this shape. When you emit spans directly, produce this shape yourself so message rendering works in the console.[ {"role": "user", "parts": [{"type": "text", "content": "What is 2+2?"}]}, { "role": "assistant", "parts": [ {"type": "tool_call", "id": "call_1", "name": "calculator", "arguments": "{\"expr\": \"2+2\"}"} ] }, { "role": "tool", "parts": [{"type": "tool_call_response", "id": "call_1", "response": "4"}] } ] -
Set
gen_ai.request.modeland thegen_ai.usage.*token counts on the same span. Messages drive what you read, while the model and token counts drive cost. Rius computes cost itself, so you never send a cost attribute. -
If you export without the Rius SDK, raise the OpenTelemetry attribute limit. SDKs keep 128 attributes per span by default and drop the rest without an error. Instrumentations that flatten messages into one attribute per field, OpenInference among them, can pass that within a few turns of an agent loop, and the span then loses its messages. The Rius SDKs raise the limit to 4096 for you.
export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=4096 -
For a wider comparison of every way to send traces, see Trace LLM calls with OpenTelemetry.
What you get
- The prompt and completion for each call render on the span’s Input / Output tab, including tool calls and tool responses.
- A span without these attributes shows “Content not captured” on that tab. Its timings, tokens and cost are unaffected.
- Content attributes are the ones covered by the SDK’s privacy controls. With
RIUS_CAPTURE_CONTENTset to false, the SDKs strip prompt and response content at export. - Rius uses the messages, the system instructions and the tool definitions to estimate how a call’s input tokens divide across the parts of its context, when the span carries no size record from the SDK.
- Spans from OpenInference or OpenLLMetry land under the canonical keys. Filter on
gen_ai.usage.input_tokens, not onllm.token_count.promptorgen_ai.usage.prompt_tokens, which match nothing after normalization. - Attributes sent as OTLP arrays, such as
gen_ai.response.finish_reasons, are stored as plain JSON like["stop"]. Write saved searches and alerts against that form.
When something else fits
- If your app already uses OpenLLMetry or OpenInference, you can leave its message attributes as they are, because Rius normalizes them. Use the dedicated sender pages linked from Interoperability for that setup.
- If you would rather not build the JSON shape yourself, the Rius SDKs normalize OpenAI-style message dicts into it for you.
- If prompts must not leave your process, turn content capture off. You then keep timings, tokens and cost but lose the Input / Output view.