Concepts
The Rius data model in one page: what a trace is, what spans and generations are made of, and which parts the platform computes for you.
The model at a glance
Five nouns cover everything Rius shows you:
- An agent is the program you built and named, like
support-agent. It is the unit you monitor: health, cost, errors. - An instance is one running copy of an agent. Run three replicas and you have three instances; every process start (or restart) mints a fresh instance id. Instances are what heartbeats track.
- A trace is one complete unit of work an agent did, from request to answer, with a duration and a status.
- A span is one step inside a trace: a model call, a tool call, a retrieval. A trace is the tree of its spans.
- A session is one conversation or job as your users see it. You assign the id, and it groups traces across turns.
An agent runs as one or more instances; each instance produces traces made of spans. Sessions cut across that hierarchy: one instance serves many sessions over its lifetime, and one session can be served by several instances, so the two relate only through the traces they share. Typical proportions, for scale: an agent with 3 instances might see hundreds of sessions, thousands of traces, and tens of thousands of spans in a day.
The rest of this page defines each part precisely, from traces down.
Traces
A trace is one end-to-end agent run. It begins when your agent starts handling a piece of work (a request, a job, a conversation turn) and ends when that work is done. Everything the run did along the way lives inside the trace, so the trace list is effectively a list of agent runs.
Every trace has exactly one root span. The root’s name, duration, and
status are what run-level views show: a failed root means a failed run, the
root’s duration is the run’s latency. Traces belong to exactly one
workspace, determined by the API key they were sent
with, and are grouped by the service.name your SDK or exporter sets.
Spans
A span is one step inside a run: a tool call, a retrieval, a chain stage, an LLM call. Spans nest the standard OpenTelemetry way (parent context), which is what produces the waterfall. Each span carries a kind that classifies it in trace views and per-kind analytics:
| Kind | Meaning |
|---|---|
AGENT | An agent invocation or run |
LLM | A model call (generations use this) |
TOOL | A tool execution |
RETRIEVER | A retrieval or search step |
EMBEDDING | An embedding computation |
CHAIN | A generic processing step (the default) |
On the wire the kind is the openinference.span.kind attribute, set for you
by the SDK’s kind= arguments and by auto-instrumentation. Exceptions are
recorded as standard OpenTelemetry span events and, in the SDK’s scoped
forms, give the span ERROR
status; the waterfall highlights the path from the root to the failing span.
Partial spans
A span normally leaves your process exactly once, when it ends. That is fine for work that finishes in a second, and poor for an agent: while a run is in flight there is nothing to look at, and if the process dies mid-run the span is never exported at all, so the run you most want to see is the one that leaves no trace.
Partial spans fix that by exporting a second, content-free copy of every sampled span at the moment it starts. The platform stores that snapshot as an unfinished span, and the real span replaces it on completion. The replacement is silent: the two carry the same trace id, span id and start time, which is the identity the storage layer matches on, so a finished run looks exactly as it would have without the feature.
What makes them worth enabling is the snapshot that never gets replaced. A process that crashes, is OOM-killed, or is shut down mid-run leaves its snapshot behind, and that snapshot is the durable record of what the agent was doing when it died.
A snapshot carries identity and taxonomy only, never content, whatever your instrumentation recorded. Inputs, outputs and prompts appear when the real span lands. That is deliberate: a pending span is a “this is happening” signal, not an early peek at data.
Trace status follows from this directly. A trace whose spans have all ended
reads as completed, or failed if any span errored; a trace with a span
still open reads as running… (the filter value is incomplete).
Exploring traces has the full
status semantics, and partial spans are off by default: see
export behavior for the two settings and
what they cost.
Generations
A generation is an LLM span with model semantics: the requested and
response model, the provider, token usage, request parameters, finish
reasons, and optionally the input and output messages. On the wire these are
OpenTelemetry GenAI (gen_ai.*) attributes; in the console they power the
span’s Model section and the model analytics.
For streamed calls, a generation can carry a gen_ai.first_token span
event marking the moment the first chunk arrived. Time to first token is
derived from it, which is the latency number streaming users actually feel.
Sessions
A session groups the traces of one conversation or job. Where a trace is a causal unit and stays short, a session is a correlation unit your application assigns, open ended and spanning turns: each turn of a chat is its own trace, and the session is what ties them together. Because the grouping rides on traces, a session is independent of the process serving it: a conversation that outlives a deploy or moves between replicas stays one session.
The application mints the id and the SDK stamps it as the session.id
attribute on every span in scope, so a session can correlate with an id you
already have, such as a conversation id or a job id.
Tracing your code shows the two ways to set
it, per scope or process-wide.
A trace without a session id is grouped as its own single-trace session.
That keeps ungrouped traces visible, and it has a reading worth knowing:
when an agent’s session count equals its trace count, nothing is setting
session.id, and those sessions are not meaningful groupings.
Content and metadata
Span data splits into two families with different privacy treatment:
- Content: prompts, completions, tool arguments and results
(
gen_ai.input.messages,gen_ai.output.messages,input.value,output.value). Content is what the Input / Output tab renders, and it is what the SDK’s privacy controls strip or mask at export time. - Metadata: everything else, including names, timings, kinds, models, token counts, and statuses. Metadata always flows, so a span with stripped content still gets full timing, token, and cost analytics.
Computed server-side
Two things you never send because the platform derives them:
- Cost is computed from
gen_ai.request.modelplus the twogen_ai.usage.*token counts, using model pricing maintained server-side. Send token usage, never cost; your instrumentation stays free of pricing tables. - Time to first token is derived from the
gen_ai.first_tokenevent’s timestamp minus the span start.
Agent instances and liveness
Traces tell you what an agent did, partial spans tell you what it is doing right now, and heartbeats tell you whether the process is alive at all. With the heartbeat enabled, every instrumented process pings the platform on a fixed interval for its whole lifetime, and each process is one instance with a fresh identity per start. Instances group under an agent name (by default the service name; see Naming your agent), so three replicas of one agent show as three instances under one name.
The same instance id also rides every span the process exports, as the
service.instance.id resource attribute. That is the join between the two
halves of the model: from an instance you can find the traces it produced,
and from a trace you can tell which process ran it, including one that is
no longer alive.
Liveness is another server-side computation: the platform classifies each
instance from its last ping (running, ready, stale, gone,
stopped) and never trusts the client’s clock or claims. The distinction
doing the real work is stopped versus gone: a clean shutdown announces
itself with a final ping, so an instance that just vanishes reads as a
crash. Monitoring live agents has the full status
semantics.
Convention-native by design
The model above is not a proprietary schema. Rius reads established OpenTelemetry conventions directly (OTel GenAI for generations, OpenInference for span kinds and generic I/O), so traces from third-party instrumentation land with the same analytics as SDK-emitted ones. The span attribute reference is the full wire contract.
Next steps
- Tracing your code: tracing functions with
observe, spans, and generations in the SDK. - Span attribute reference: every attribute the platform reads, for sending without the SDK.
- Exploring traces: the trace list, waterfall, and span detail in the console.
- Monitoring live agents: instances, heartbeats, and the full status semantics.