Installation and configuration
The Rius SDK for Python is published as glassflow-rius and imported as
rius; it requires Python 3.10 or newer. The TypeScript SDK is published as
@glassflow-ai/rius and requires Node 18 or newer. Both SDKs are open source
under the MIT license, on GitHub:
Python and
TypeScript .
Python
pip install glassflow-riusTo auto-instrument the LLM libraries you use, install their extras (see Integrations):
pip install "glassflow-rius[openai]" # one library
pip install "glassflow-rius[instruments]" # everythingAn extra installs the instrumentation for a library, never the library
itself: glassflow-rius[anthropic] brings in the Anthropic instrumentation
and expects anthropic to already be in your environment, which it is in
any project that calls Anthropic. Rius deliberately does not depend on
provider SDKs, so it never pins or upgrades the versions your code runs
against. If an extra is installed without its library, init() logs a
DependencyConflict from OpenTelemetry and that integration stays off; see
Troubleshooting.
MCP needs no extra: Rius instruments it directly rather than through a
separate package, so that integration turns itself on whenever mcp is
importable.
Initializing
Call init() once, as early as possible in your process:
Python
import rius
client = rius.init(
api_key="ri_...",
service_name="my-agent",
)Every argument is optional and can also come from an environment variable. Explicit arguments win over environment variables, which win over defaults.
Naming your agent
Set service_name (RIUS_SERVICE_NAME) explicitly. It is the
service.name resource attribute stamped on every span and how you find
your agent in trace filters, analytics, and cost breakdowns. It defaults
to unknown_service.
Optionally set agent_name (RIUS_AGENT_NAME) to control how instances
group in the Agents view. It defaults to
service_name; set it separately only when several services form one
logical agent.
Keep the name constant across replicas and deploys. Each process start is a fresh instance under the same name, so three replicas show as three instances of one agent. Keep version numbers out of the name and put them where they belong instead:
service_version(RIUS_SERVICE_VERSION) is the build or release this process runs, stamped asservice.version. Unset, it falls back to aservice.versioninOTEL_RESOURCE_ATTRIBUTES, and otherwise the attribute is left off rather than filled with a placeholder.main_agent_version(RIUS_MAIN_AGENT_VERSION) is the version of the agent definition, meaning its prompt, tools, and policy, stamped asrius.main_agent.version. It moves independently of the build, so the SDK never derives one from the other.
Two more optional identity settings describe the agent itself:
main_agent_id (RIUS_MAIN_AGENT_ID) is a stable identifier such as a
registry id, and main_agent_description (RIUS_MAIN_AGENT_DESCRIPTION)
says what the agent does. Use an id that survives restarts and deploys: an id
minted at process start would turn every restart into a new agent, and
service.instance.id already identifies the process.
The SDK stamps the agent’s identity under the OpenTelemetry GenAI
conventions’ gen_ai.main_agent.* keys, next to the older
rius.main_agent.* ones. gen_ai.main_agent.id is main_agent_id when you
set it (the argument, then RIUS_MAIN_AGENT_ID), then a
gen_ai.main_agent.id you already put in OTEL_RESOURCE_ATTRIBUTES, and
otherwise the agent name. So without an id, renaming the agent also changes
gen_ai.main_agent.id. Set main_agent_id if you want an identity that
survives a rename. When the id falls back to the name, the SDK logs a one-time
hint at debug level: on the rius.client logger in Python, and on the
OpenTelemetry diag logger in TypeScript, which is silent unless you install
one with diag.setLogger(..., DiagLogLevel.DEBUG).
Renaming does not re-key history: stored spans keep the old service.name
and stay filterable under it, while the new name starts empty. In the
Agents view the old name’s instances report gone and linger until their
heartbeat records are garbage-collected. Treat the name as an identifier,
not a label.
Configuration reference
Python
| Argument | Environment variable | Default | What it does |
|---|---|---|---|
endpoint | RIUS_ENDPOINT | https://ingest.eu.console.rius-glassflow.com | Base OTLP endpoint. Traces post to {endpoint}/v1/traces. |
api_key | RIUS_API_KEY | None | Sent as Authorization: Bearer <key>. If you pass your own Authorization header via headers, it wins. None is valid for OTLP backends that need no auth. |
service_name | RIUS_SERVICE_NAME | unknown_service | The service.name resource attribute on every span. Set it; it is how you find your agent later. See Naming your agent. |
service_version | RIUS_SERVICE_VERSION | None | The service.version resource attribute, for example your release or image tag. Unset, a service.version in OTEL_RESOURCE_ATTRIBUTES applies; otherwise the attribute is omitted. |
headers | none | {} | Extra headers for the exporter. |
disabled | RIUS_DISABLED | False | Kill switch. When true, no exporter is attached and spans are dropped in-process. |
sample_rate | RIUS_SAMPLE_RATE | 1.0 | Head sampling rate for root traces. See Sampling. |
capture_content | RIUS_CAPTURE_CONTENT | True | When false, prompt and response content is stripped at export; metadata still flows. See Privacy controls. |
mask | none | None | Redaction callable applied to content attributes at export. See Privacy controls. |
instruments | none | None | Which integrations to enable: None enables all installed, [] disables, ["openai"] restricts. See Integrations. |
span_exporter | none | None | Override the OTLP exporter (mostly for tests). |
heartbeat | RIUS_HEARTBEAT | True | The agent-lifetime heartbeat thread: pings {endpoint}/v1/heartbeat from init() until process exit, powering the Agents view. Since 1.5.1 it needs credentials: without api_key or an Authorization entry in headers (or a custom heartbeat_transport), no heartbeat is sent. On by default since 0.10.0; set False (or RIUS_HEARTBEAT=false) to opt out. Delivery semantics in Reliability. |
heartbeat_interval | RIUS_HEARTBEAT_INTERVAL | 15 | Seconds between pings, clamped to 5-300. Keep it at 15 or below: the platform marks an instance stale after 30 seconds and gone after 60 seconds without a ping, whatever the interval, so a longer interval makes a live instance flap to stale. |
agent_name | RIUS_AGENT_NAME | service_name | The identity heartbeat instances and spans group under in the Agents view, stamped on the resource as gen_ai.main_agent.name, gen_ai.agent.name, and the deprecated rius.main_agent.name. Set it when several services are one logical agent. See Naming your agent. |
main_agent_id | RIUS_MAIN_AGENT_ID | None | A stable identifier for the agent this process runs, stamped as gen_ai.main_agent.id and the deprecated rius.main_agent.id. Unset, gen_ai.main_agent.id is a gen_ai.main_agent.id in OTEL_RESOURCE_ATTRIBUTES, else the agent name, so renaming the agent changes it; rius.main_agent.id is then left off. Never a per-process id. |
main_agent_description | RIUS_MAIN_AGENT_DESCRIPTION | None | What the agent does, stamped as gen_ai.main_agent.description and the deprecated rius.main_agent.description. |
main_agent_version | RIUS_MAIN_AGENT_VERSION | None | The version of the agent definition (prompt, tools, policy), stamped as rius.main_agent.version. Independent of service_version. |
heartbeat_transport | none | None | Override the heartbeat HTTP transport (mostly for tests, like span_exporter). |
partial_spans | RIUS_PARTIAL_SPANS | False | Export a content-free snapshot of every span at start, so in-flight and crashed work is visible. See Partial spans. |
partial_spans_delay | RIUS_PARTIAL_SPANS_DELAY | 0.0 | Seconds to hold a snapshot before sending it, clamped to 0-60. A span that finishes within the delay sends no snapshot at all. |
session_id | RIUS_SESSION_ID | None | Process-wide session.id for every span, for an agent that handles one session per process. A session scope overrides it; do not set it in a server handling many users. |
bridge_foreign_provider | RIUS_BRIDGE_FOREIGN_PROVIDER | False | When another OpenTelemetry SDK (Langfuse, Traceloop, Logfire, MLflow, OpenLIT, Laminar) already owns the global tracer provider, also send that provider’s spans to Rius, so your LLM calls keep their parents. Spans whose parent Rius never received are flagged rius.parent.foreign either way. |
workspaces | none | None | Multi-workspace routing: a mapping of alias to API key. Spans inside rius.workspace(alias) export with that workspace’s key; {} opts in with routes added later via register_workspace(). See routing traces to the right workspace. |
workspace_exporter_factory | none | None | Override how per-workspace exporters are built from an API key (mostly for tests, like span_exporter). |
set_global | none | True | Register the provider as the global OpenTelemetry provider. |
Boolean environment variables accept 1, true, yes, or on, and 0,
false, no, or off, in any case. Since 1.5.1, an unrecognized value
(including an empty one) logs a warning and falls back to the default rather
than being read as false. Out-of-range
sample_rate values are clamped to [0.0, 1.0] with a warning.
Only the RIUS_* names are read. Releases before 1.0.0 also accepted the
older GLASSFLOW_* spellings with a deprecation warning; from 1.0.0 those
are ignored, so rename any that remain in your environment.
Calling init() twice
The OpenTelemetry global provider is write-once, so a second global init()
while a client is active logs a warning and returns the existing client
unchanged. To reconfigure, call client.shutdown() first, which drains
pending spans and releases the slot. With heartbeats enabled, shutdown()
also sends the final stopped ping, which is how the platform tells a
clean exit from a crash (see
status semantics).
If your process needs an isolated pipeline next to an existing one, use a scoped client instead:
Python
client = rius.init(set_global=False, service_name="side-pipeline")
tracer = client.get_tracer()A scoped client does not claim the global slot, and it does not enable
auto-instrumentation unless you pass instruments=[...] explicitly, because
instrumentors patch libraries process-wide.
What every span carries
The SDK stamps these resource attributes on all spans it exports:
service.name, from your configuration, andservice.versionwhen you set oneservice.instance.id, minted once perinit(), which is the id the heartbeat reports too, so a trace can be joined to the process that ran itgen_ai.agent.name, the resolved agent namegen_ai.main_agent.id,gen_ai.main_agent.name, andgen_ai.main_agent.description, the OpenTelemetry GenAI conventions’ main agent. The id is the configured main agent id, otherwise the agent name (see Naming your agent). The name and description are only sent alongside the id, and the description only when set. With no id and no usable name (empty, or theunknown_serviceplaceholder), the SDK sends none of these keys from its own settings. Anygen_ai.main_agent.*value you set yourself inOTEL_RESOURCE_ATTRIBUTESis kept, not overwritten.rius.main_agent.name,rius.main_agent.id, andrius.main_agent.description, deprecated in favor of thegen_ai.main_agent.*keys and still sent through the deprecation window.rius.main_agent.nameis left off when no service or agent name was configured, rather than claimingunknown_serviceas an identity, andrius.main_agent.idcarries only an id you configured, never the name.rius.main_agent.version, when set. It has no upstream equivalent and is not deprecated.telemetry.distro.name: glassflow-riusandtelemetry.distro.version- OpenTelemetry’s own
telemetry.sdk.name,telemetry.sdk.language, andtelemetry.sdk.version(in TypeScript since 1.2.0)
Spans created by the SDK itself use the tracer scope name rius (glassflow
in releases before 1.0.0 of both SDKs).
Span attribute limit
OpenTelemetry keeps at most 128 attributes per span by default and silently drops the rest. That is too few for an auto-instrumented agent: the OpenInference instrumentors write one attribute per message field and per tool field, so a tool-calling loop with around ten tools passes 128 after roughly six to nine turns. The spans that matter most then lose data without any error. In Python the oldest keys go first, which are the model, the request parameters, the tool definitions, and the system prompt; in TypeScript the span stops accepting new keys, which are the usage, the output messages, and the finish reason.
init() therefore raises the per-span attribute count limit to 4096. If you
set OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT or OTEL_ATTRIBUTE_COUNT_LIMIT to a
value OpenTelemetry can use, your value wins and OpenTelemetry applies its
usual precedence between the two. A blank or unusable value (not a number,
or in Python not a non-negative integer) is not treated as a choice: the SDK
applies 4096 instead, and the Python SDK logs a warning saying so. One
exception in Python: a non-integer OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT makes
OpenTelemetry itself raise when its tracing module is imported. The attribute value
length is not limited; the SDK caps its own JSON attributes at 32 KB.
If you run OpenInference instrumentors without the SDK, nothing raises the limit for you; see OpenInference.
Next steps
- Tracing your code: tracing functions with
observe, spans, and generations. - Integrations: tracing OpenAI, Anthropic, LangChain, and friends without manual spans.
- Monitoring live agents: what the heartbeat powers, including the status semantics.