How do I move my instrumentation code from Traceloop to Rius?
Rius takes the OpenTelemetry traces that Traceloop’s OpenLLMetry already emits, so you keep your instrumentation code and change only where it exports. You change the base URL and the auth header, and in Python you also turn off metrics. Your workflow, task, agent and tool decorators keep working. Switching to the Rius SDK is optional, and the decorators map one to one.
How it works
OpenLLMetry is plain OpenTelemetry, so it exports OTLP to whichever endpoint you give it. Rius accepts OTLP over HTTP with a Bearer API key, which makes this a settings change. Here is how each Traceloop concept appears in Rius:
- Workflow and task spans appear as generic steps (the
CHAINspan kind). - Agent spans appear as agent spans, named after the decorated function.
- Tool spans appear as tool spans, named after the decorated function.
- Entity input and output (
traceloop.entity.inputandoutput) show on the span’s Input / Output tab. - LLM calls appear as generations with model, token counts and cost. Rius computes the cost from the spans, as a list-price estimate in US dollars.
- Conversations (
@conversation,set_conversation_id,withConversation) become sessions, because OpenLLMetry writesgen_ai.conversation.idand Rius reads it as the session. - Association properties such as
user_idandchat_idare stored astraceloop.association.properties.*attributes. You can inspect and filter on them, but Rius does not treat them as users or sessions. - Project and environment become an agent, named by
service.name. Give each environment its own workspace and API key.
In Python, re-pointing OpenLLMetry at Rius switches off Traceloop’s platform client. get_prompt(), guardrails, user feedback, datasets and experiments stop working. If your code calls any of them, keep OpenLLMetry pointed at Traceloop and add Rius as a second destination through a Collector instead.
The Traceloop migration guide covers every detail, and Migrate to Rius compares the paths for other tools.
Set it up
-
Create a Rius workspace and an API key. Rius keys start with
ri_. -
Re-point the exporter (Path A). Remove
TRACELOOP_API_KEYfrom your deployment and set these instead:export TRACELOOP_BASE_URL="https://ingest.eu.console.rius-glassflow.com" export TRACELOOP_HEADERS="Authorization=Bearer%20<your Rius API key>" export TRACELOOP_METRICS_ENABLED="false" # Python SDK onlyTRACELOOP_HEADERStakes comma-separatedkey=valuepairs, with%20for the space. OpenLLMetry appends/v1/tracesitself. Use anhttporhttpsURL, because Rius accepts OTLP over HTTP only. -
Or set the same values in code. In TypeScript:
import * as traceloop from '@traceloop/node-server-sdk' traceloop.initialize({ appName: 'my-agent', baseUrl: 'https://ingest.eu.console.rius-glassflow.com', headers: { Authorization: `Bearer ${process.env.RIUS_API_KEY}` }, })appName(app_namein Python) becomesservice.name, which names the agent in Rius. In Python, leftoverTRACELOOP_BASE_URLandTRACELOOP_HEADERSvalues override the arguments toTraceloop.init(), so clear old ones. TypeScript works the other way round. -
Turn off metrics in Python. The Python SDK also exports OTLP metrics to the same base URL. Rius ingests traces only, so each export logs a 404.
TRACELOOP_METRICS_ENABLED=falsestops it, and traces are unaffected either way. -
Set
user.idif you want users. OpenLLMetry does not set it. Set it with the OpenTelemetry API inside your outermost decorated function, because Rius lists a trace under the user of its root span:from opentelemetry import trace trace.get_current_span().set_attribute("user.id", user_id)Set the conversation id before the root span starts for the same reason.
-
Optionally swap to the Rius SDK (Path B). The decorators map onto
observewith a span kind:import rius from rius import SpanKind rius.init(service_name="trip-planner") # reads RIUS_API_KEY @rius.observe(kind=SpanKind.TOOL, tool_name="lookup_weather") def lookup_weather(city: str) -> str: ... @rius.observe(kind=SpanKind.AGENT, agent_name="weather_agent") def weather_agent(city: str) -> str: ...@workflowand@taskbecome a bare@rius.observe(name=...).@conversationbecomeswith rius.session(cid):. Association properties for users becomewith rius.user(uid):. In TypeScript,withWorkflow,withTask,withAgentandwithToolbecomeobservewrappers with the matching kind, andwithConversationbecomeswithSession.Remove OpenLLMetry in the same process, or LLM calls are recorded twice. Span names also change: unnamed agent and tool spans become
invoke_agentandexecute_toolnames, and workflow and task spans use their plain name. Update saved searches that match on span names. -
Verify one request. Check that the trace tree shows your workflow, agent and tool spans, that the generation has model and token counts, that cost appears, and that traces with a conversation id share a session.
What you get
- Your existing OpenLLMetry spans, decorators included, arrive with their structure intact. You rewrite no instrumentation.
- Each model call shows its model, tokens and cost, computed by Rius.
- Conversation ids group traces into sessions, and
user.idgroups them by user. - Alerts and the Rius MCP server work on the same traces, so your coding agent can query production runs.
- With the Rius SDK you also see which agent processes are alive, and with partial spans turned on you see runs still in progress.
- During evaluation you can send the same spans to both backends and cut over only when Rius shows what you need.
When something else fits
- Keep Traceloop for anything that has no counterpart in Rius: monitors and evaluators, guardrails, the prompt registry and playgrounds, datasets and experiments, user feedback and annotations, and Traceloop Hub. Rius is a managed service only, so it has no self-hosted option.
- Existing traces stay where they are. Rius starts from the first span you send.
- If you depend on those platform features, fan out to both backends through an OpenTelemetry Collector rather than cutting over fully.