Skip to Content
RiusInteroperabilitySpan attribute reference

Span attribute reference

The wire format Rius reads. If you emit spans from anything other than the SDKs (a vanilla OpenTelemetry SDK, a collector pipeline, another language), this page tells you which attributes to set so your traces get the same analytics as SDK-emitted ones.

Where to send spans

OTLP/HTTP with protobuf encoding:

POST https://ingest.eu.console.rius-glassflow.com/v1/traces Authorization: Bearer <your API key> Content-Type: application/x-protobuf

The contract in full:

  • Host: region-qualified. All Rius endpoints carry the region in the subdomain (ingest.eu.console.rius-glassflow.com, eu.console.rius-glassflow.com); use your workspace’s region. Examples throughout these docs use eu.
  • Protocol: OTLP/HTTP, protobuf encoding only (http/protobuf). OTLP/JSON is rejected with 415; there is no gRPC endpoint. Protobuf is the default encoding of every official OTLP/HTTP exporter and of the collector’s otlphttp exporter, so this only matters if you changed it.
  • Compression: Content-Encoding: gzip is accepted (and worth enabling; span batches compress well).
  • Body limit: 16 MiB per request. Exporters’ default batch sizes sit far below this.
  • Auth: Authorization: Bearer with an API key from the console. 401 means the key is wrong or revoked.

Setup for each common sender is one page away: a vanilla OpenTelemetry SDK, OpenLLMetry, OpenInference, or an OpenTelemetry Collector.

Span taxonomy

AttributeValuesEffect
openinference.span.kindAGENT, LLM, TOOL, RETRIEVER, EMBEDDING, CHAINClassifies the span in trace views and per-kind analytics
gen_ai.operation.namechat, text_completion, embeddings, execute_tool, invoke_agent, …The OpenTelemetry GenAI operation taxonomy; set it alongside the kind where one maps

LLM analytics

These drive model analytics and cost computation:

AttributeTypeNotes
gen_ai.request.modelstringThe requested model
gen_ai.usage.input_tokensintPrompt tokens
gen_ai.usage.output_tokensintCompletion tokens
gen_ai.provider.namestringe.g. openai, anthropic
gen_ai.response.modelstringModel reported by the provider
gen_ai.request.<param>anyRequest parameters, e.g. gen_ai.request.temperature
gen_ai.response.finish_reasonsstring[]Why generation stopped

Cost is computed server-side from gen_ai.request.model plus the two gen_ai.usage.* token counts. A span missing any of the three gets no cost. Never send a cost attribute; send tokens and the model name.

Content

AttributeApplies toNotes
gen_ai.input.messages / gen_ai.output.messagesLLM spansJSON in the GenAI message shape (below)
input.value / output.valueany spanGeneric input/output, JSON or plain text

Content attributes are stored for trace inspection and are the ones covered by the SDK’s privacy controls when you export through it.

The GenAI message shape

gen_ai.input.messages and gen_ai.output.messages carry a JSON array of messages, each {"role", "parts": [...]}. Text parts are {"type": "text", "content": ...}; tool interactions have dedicated part types:

[ {"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"}] } ]

This is the OpenTelemetry GenAI message schema; the SDK’s set_input() / set_output() normalize OpenAI-style dicts into it (message formats). When emitting directly, produce this shape yourself so message rendering in the console works. Spans carrying it render their prompt and completion on the span’s Input / Output tab:

Prompt and completion rendered from GenAI message attributes

Spans without these attributes show “Content not captured” there; the rest of the span (timings, tokens, cost) is unaffected.

Span events

Event nameMeaning
gen_ai.first_tokenFirst streamed chunk arrived; time to first token is derived as this event’s time minus the span start
exceptionStandard OpenTelemetry exception event (record_exception); surfaces in error analysis

For streaming LLM calls, emit gen_ai.first_token once, when the first content chunk arrives. The SDK’s record_first_token() does exactly this.

Resource attributes

AttributeEffect
service.nameThe service every trace view and filter groups by. Always set it.

Supported conventions

What each attribute family gets today. “Analytics” means the promoted columns above (taxonomy, model, tokens, cost, TTFT); “stored” means the attribute lands with the span and is searchable in trace inspection either way.

ConventionAttributesAnalyticsStored
OTel GenAIgen_ai.* as documented above✅ full
OpenInference kindopeninference.span.kind✅ taxonomy
OpenInference generic I/Oinput.value / output.value✅ content
OpenInference LLMllm.model_name, llm.provider, llm.token_count.*, llm.finish_reason, llm.input_messages.* / llm.output_messages.*✅ normalized at ingest
OpenLLMetry / Tracelooptraceloop.entity.*, legacy gen_ai.prompt.* / gen_ai.completion.*⏳ planned
Anything elseyour own attributesno

“Normalized at ingest” means the platform copies the values into their gen_ai.* equivalents before storage: llm.model_name fills the request and response model, llm.token_count.prompt/completion fill the token counts (which is what makes cost computation work), and the flattened llm.input_messages.* / llm.output_messages.* are reassembled into the GenAI message shape. Native gen_ai.* keys always win; normalization fills gaps, never overwrites. The OpenInference first-token span event is likewise renamed to gen_ai.first_token.

The practical consequence of the remaining ⏳ row: spans arrive, nest correctly, and are fully inspectable, but model, token, and cost analytics stay empty until that mapping ships. To get full analytics from those emitters today, also set the gen_ai.* attributes from the tables above; duplicating an attribute in two conventions is harmless.

Unknown attributes are never dropped and never an error.

Next steps

Last updated on