Skip to Content

Migrate from Traceloop to Rius

Traceloop’s SDK is OpenLLMetry, and OpenLLMetry is plain OpenTelemetry: it exports OTLP to whichever endpoint you give it. That makes migrating from Traceloop to Rius a configuration change. Keep your OpenLLMetry instrumentation, decorators included, and point its exporter at Rius: your agents, tools and LLM calls show up as such. This guide maps the concepts, walks the two migration paths, and ends with a cutover checklist. Run both backends in parallel during the evaluation; nothing here requires a hard switch.

TL;DR: Keep OpenLLMetry and change its export settings: the base URL, the auth header, and, in Python, turning off metrics (Path A). Your @workflow, @task, @agent and @tool decorators keep working. Swapping to the Rius SDK (Path B) is optional. Fan out to both backends while you compare, then cut over.

Why migrate from Traceloop to Rius

The same OpenLLMetry traces you send Traceloop today gain session and user grouping, cost computed server-side, alerts, and an MCP server so your coding agent can query production traces. With the Rius SDK you also see which agent processes are alive, and, with partial spans turned on, runs still in progress.

Concept mapping: Traceloop vs Rius

TraceloopRiusNotes
TraceTraceSame OpenTelemetry trace, same tree view.
@workflow / withWorkflow spanCHAIN spanCHAIN is the span kind for a generic step.
@task / withTask spanCHAIN spanSame as a workflow.
@agent / withAgent spanAGENT spanShown as an agent, named after the decorated function.
@tool / withTool spanTOOL spanShown as a tool, named after the decorated function.
traceloop.entity.input / outputSpan input and outputRendered on the span’s Input / Output tab.
LLM span (openai.chat, anthropic.chat)GenerationModel, token counts, and cost computed server-side.
Conversation (@conversation, set_conversation_id, withConversation)SessionOpenLLMetry writes gen_ai.conversation.id, which Rius reads as the session.
Association properties (user_id, chat_id, …)Span attributesStored under traceloop.association.properties.* and inspectable, but not mapped to Rius users or sessions.
Project and environmentAgent (named by service.name)Rius groups traces by agent, named by service.name. Give each environment (production, staging) its own workspace and API key.
Monitors, evaluators, guardrails, prompt registry, playgrounds, datasets, experimentsNot availableSee what does not carry over.

Choose your path

  • Keep OpenLLMetry (recommended for most teams): change the export target and nothing else. Your decorators, auto-instrumentation and conversation ids carry over as they are. Path A.
  • Move to the Rius SDK: worth it if you want Rius-native session and user scopes, the SDK’s privacy controls, or partial spans for long runs. The decorators map one to one. Path B.

Path A: keep OpenLLMetry (re-point the exporter)

Re-pointing switches off Traceloop’s platform features in Python. Traceloop.init() builds its platform client only when the base URL is Traceloop’s. With the base URL set to Rius, Traceloop.get() raises, so get_prompt(), guardrails, user feedback, datasets and experiments stop working. If your code uses any of them, keep OpenLLMetry pointed at Traceloop and add Rius through the Collector fan-out instead.

OpenLLMetry sends traces to TRACELOOP_BASE_URL (default https://api.traceloop.com) with your TRACELOOP_API_KEY as a Bearer token, and appends /v1/traces itself. Rius takes OTLP/HTTP with a Bearer API key too, so the change is the base URL and the header:

# Before: Traceloop export TRACELOOP_API_KEY="<your Traceloop API key>" # After: Rius 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 only, see below

Remove TRACELOOP_API_KEY from your deployment: the Rius header replaces it. TRACELOOP_HEADERS takes key=value pairs separated by commas, with %20 for the space. Or set the same values in code:

import os from traceloop.sdk import Traceloop Traceloop.init( app_name="my-agent", api_endpoint="https://ingest.eu.console.rius-glassflow.com", headers={"Authorization": f"Bearer {os.environ['RIUS_API_KEY']}"}, )
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}` }, })

app_name / appName becomes the service.name, which names the agent your traces are grouped under in Rius. Three details trip people up:

  • Leftover environment variables win in Python. In Python, TRACELOOP_BASE_URL and TRACELOOP_HEADERS override the Traceloop.init() arguments (TypeScript is the reverse), so remove old values from your deployment.
  • Turn off OpenLLMetry metrics in Python. The Python SDK also exports OTLP metrics to the same base URL. Rius ingests traces only, so those requests fail with a 404 that is logged on every export. TRACELOOP_METRICS_ENABLED=false stops them. Traces are unaffected either way.
  • Use an http or https URL. OpenLLMetry picks OTLP/HTTP for an http(s):// base URL and gRPC otherwise, and Rius accepts OTLP/HTTP only.

What your traces look like in Rius

With no code changes, this is what you see in Rius:

  • LLM calls are generations with the request and response model, input and output tokens, cache-read tokens, and cost computed server-side.

  • Decorator spans keep their structure. Agents and tools appear as agent and tool spans with their names, and workflows and tasks appear as generic steps. traceloop.entity.input and output show up as the span’s input and output.

  • Conversations become sessions. @conversation, set_conversation_id() and withConversation() stamp gen_ai.conversation.id on every span in scope, and Rius groups the traces into one session by it. Rius lists each trace under the session of its root span (the top-level span of the trace), so set the conversation id before that span starts. In TypeScript, withConversation() also adds a root workflow span.

  • Association properties ride along as attributes. user_id, chat_id and any other property arrive as traceloop.association.properties.<key>. You can see and filter on them in trace inspection, but Rius does not read them as its user or session.

  • Users need user.id. OpenLLMetry does not set it. Keep OpenLLMetry and set it with the OpenTelemetry API. Rius lists each trace under the user of its root span, so set it inside your outermost decorated function (the @workflow or @agent that starts the trace), and ideally on the other spans of the trace too:

    from opentelemetry import trace trace.get_current_span().set_attribute("user.id", user_id)

Older OpenLLMetry releases work too; the OpenLLMetry page has the version details.

Side by side through a Collector

For an evaluation period, keep sending to Traceloop and add Rius as a second destination. Point OpenLLMetry at an OpenTelemetry Collector (TRACELOOP_BASE_URL=http://<collector-host>:4318, no headers) and fan out from there:

receivers: otlp: protocols: http: endpoint: 0.0.0.0:4318 processors: batch: exporters: otlphttp/traceloop: endpoint: https://api.traceloop.com headers: authorization: Bearer ${env:TRACELOOP_API_KEY} otlphttp/rius: endpoint: https://ingest.eu.console.rius-glassflow.com encoding: proto compression: gzip headers: authorization: Bearer ${env:RIUS_API_KEY} service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlphttp/traceloop, otlphttp/rius]

Both backends now see identical spans, so you can compare trace trees, token counts, and cost on the same traffic before deciding. The Collector page covers the exporter details, including why it must be otlphttp and not otlp.

With the Collector in between, OpenLLMetry’s Python SDK still sends its metrics to the Collector. The config above has no metrics pipeline, so set TRACELOOP_METRICS_ENABLED=false or add a metrics pipeline that exports to Traceloop only.

Path B: swap OpenLLMetry for the Rius SDK

The Rius SDK (Python and TypeScript) instruments OpenAI, Anthropic, LangChain and other supported libraries on init(), so your LLM calls are covered without decorators. The OpenLLMetry decorators map onto observe with a span kind:

Traceloop (Python)Rius (Python)
Traceloop.init(app_name="my-agent", api_key=...)rius.init(api_key="ri_...", service_name="my-agent")
@workflow(name="plan")@rius.observe(name="plan") (the default kind is CHAIN)
@task(name="summarize")@rius.observe(name="summarize")
@agent(name="planner")@rius.observe(kind=SpanKind.AGENT, agent_name="planner")
@tool(name="search")@rius.observe(kind=SpanKind.TOOL, tool_name="search")
@conversation(conversation_id=cid) / set_conversation_id(cid)with rius.session(cid):
Traceloop.set_association_properties({"user_id": uid})with rius.user(uid):, which stamps user.id on every span in scope
TRACELOOP_TRACE_CONTENT=falseThe SDK’s privacy controls

In TypeScript, withWorkflow, withTask, withAgent and withTool become observe wrappers with the matching kind, and withConversation becomes withSession.

A decorated agent, before and after:

# Traceloop from traceloop.sdk import Traceloop from traceloop.sdk.decorators import agent, tool Traceloop.init(app_name="trip-planner") @tool(name="lookup_weather") def lookup_weather(city: str) -> str: ... @agent(name="weather_agent") def weather_agent(city: str) -> str: ...
# Rius 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: ...

Two differences worth knowing before you start:

  • Remove OpenLLMetry when you add the Rius SDK. Both instrument the same client libraries with different instrumentors, so running both produces duplicate LLM spans. Run one or the other per process.
  • Span names follow the GenAI conventions. Unnamed AGENT and TOOL spans are called invoke_agent <name> and execute_tool <name> rather than OpenLLMetry’s <name>.agent and <name>.tool, and workflow and task spans are called by their plain name rather than <name>.workflow and <name>.task. Update saved searches that match on span names. See Span names and identity.

What does not carry over from Traceloop

OpenLLMetry is open source and carries over completely; the Traceloop platform behind it does not. The Traceloop platform features below have no Rius equivalent:

  • Monitors and evaluators: no LLM-as-a-judge or built-in evaluators running on production spans. Rius alerts cover errors, loops, and metric thresholds, not output quality.
  • Guardrails: OpenLLMetry’s guardrail decorator runs Traceloop evaluators inline and needs a Traceloop API key. Rius has nothing that blocks or rewrites model output.
  • Prompt registry and playgrounds: no versioned prompts, no get_prompt(), no prompt deployment. Prompt content on spans is captured and inspectable, but there is no registry.
  • Datasets and experiments: no managed datasets or experiment runs, and no CI integration for them.
  • User feedback and annotation tasks: no annotation objects on traces.
  • Association properties as entities: user_id, chat_id and the rest are stored and filterable as attributes, but Rius does not group by them. Rius users come from user.id (or enduser.id), which OpenLLMetry does not set; sessions come from the conversation id. See setting user.id.
  • Traceloop Hub: Rius has no LLM gateway or proxy.
  • Self-hosting: Rius is a hosted service only. Traceloop offers hybrid and full self-hosted deployments.
  • Historical data: existing traces stay in Traceloop; Rius starts from the first span you send it.
  • OpenLLMetry metrics: Rius ingests traces, not OTLP metrics. Token, cost, and latency analytics are computed from the spans instead.

If any of these are load-bearing in your workflow, keep Traceloop for those pieces and send traces to both through the Collector fan-out rather than cutting over completely.

Traceloop to Rius cutover checklist

  1. Create a Rius workspace and API key (quickstart). New organizations start with a free trial (30 days or 500,000 spans, whichever comes first), then a paid plan; see Plans and billing.
  2. Pick your path: keep OpenLLMetry or swap to the Rius SDK.
  3. Start with fan-out, not a switch: send the same traces to both backends (Collector fan-out), or re-point one service while the rest still report to Traceloop.
  4. Verify one traced request end to end: the trace tree shows your workflow, agent and tool spans, the generation shows model and token counts, cost appears, and traces with a conversation id share a session.
  5. Compare both backends on real traffic for a few days: trace counts, token totals, cost.
  6. Move anything that reads from Traceloop (dashboards, Slack digests, monitors) to Rius equivalents such as alerts, or deliberately leave them behind.
  7. Remove the Traceloop exporter, and revoke the Traceloop API key once nothing sends to it and no SDK feature still calls the Traceloop API.

Frequently asked questions

Do I have to re-instrument my code to migrate from Traceloop?

No. OpenLLMetry exports standard OTLP, so you change the base URL and the auth header and your existing spans, decorators included, arrive in Rius with their structure intact. What your traces look like in Rius shows how each one appears.

Can I run Traceloop and Rius at the same time?

Yes. Route OpenLLMetry through an OpenTelemetry Collector and export to both during the evaluation. The migration is reversible until you revoke your Traceloop API key.

Do my workflow, task, agent and tool decorators still work?

Yes. They are part of OpenLLMetry, not the Traceloop platform. Agents and tools appear as agent and tool spans with their names, and workflows and tasks appear as generic steps.

Does Rius support OpenLLMetry for TypeScript?

Yes. @traceloop/node-server-sdk exports the same OTLP/HTTP spans as the Python SDK; set baseUrl and headers in initialize() or use the same environment variables.

Last updated on