Skip to Content

How do I move my instrumentation code from Arize Phoenix to Rius?

Rius reads the OpenInference spans your Phoenix instrumentation already produces, so you keep your instrumentors and change only where the exporter sends them. Point register() or your OTLP exporter at Rius, or replace register() with rius.init(). Evals, datasets, annotations and prompt management have no counterpart in Rius.

How it works

Phoenix and Rius both build on OpenTelemetry and OpenInference, so the migration is a routing change. The mapping is:

  • Project becomes agent. Rius groups traces by the service.name resource attribute, within the workspace your API key belongs to. Phoenix’s openinference.project.name is stored but does not drive grouping. If you use one project per environment, give each environment its own workspace and API key.
  • Span kind is read as is from openinference.span.kind: AGENT, LLM, TOOL, CHAIN, RETRIEVER, EMBEDDING and RERANKER.
  • LLM spans are read as generations, so they get model, token and cost analytics.
  • Sessions and users use the same session.id and user.id attributes.
  • Metadata and tags are stored as span attributes and can be filtered by key, with no dedicated views.
  • Cost is computed by Rius from the model and token counts. You send tokens, never cost.

You have two paths. Path A keeps your instrumentation and re-points the exporter, which is also how you run both backends side by side. Path B replaces register() with rius.init(), which wires the same OpenInference instrumentors for you. The overall approach is in Migrate to Rius, and the full walkthrough is in Migrate from Arize Phoenix.

Set it up

  1. Path A: re-point register(). Pass the full traces URL, the protocol and your Rius API key in code:
import os from opentelemetry.sdk.resources import Resource from phoenix.otel import register tracer_provider = register( endpoint="https://ingest.eu.console.rius-glassflow.com/v1/traces", protocol="http/protobuf", api_key=os.environ["RIUS_API_KEY"], batch=True, resource=Resource.create({"service.name": "my-agent"}), auto_instrument=True, )
  1. Check three details. Pass endpoint and protocol in code, because setting only PHOENIX_COLLECTOR_ENDPOINT to the Rius URL sends spans to the wrong port. Set service.name, or traces arrive as unknown_service. Use batch=True in production, because register() defaults to a processor that exports each span synchronously.

  2. Set session and user on the root span. Your using_session, using_user and using_attributes context managers keep working, because they stamp session.id and user.id. They only stamp spans created by OpenInference, so a root span from a plain OpenTelemetry tracer misses them and the trace shows as a single-trace session with no user. Start root spans from the OpenInference tracer, or set the attributes yourself:

with tracer.start_as_current_span( "handle-turn", attributes={"session.id": conversation_id, "user.id": user_id}, ): run_agent(message)
  1. Raise the span attribute limit if you stay off the Rius SDK. OpenInference writes one attribute per message and tool field, and OpenTelemetry keeps 128 per span by default, so long agent loops lose data on later turns. Set OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=4096, or pass SpanLimits.

  2. Path B: switch to the Rius SDK. Install the extra for your library, set the key, then swap register() for rius.init():

pip install "glassflow-rius[openai]" export RIUS_API_KEY="<your API key>"
# Before from phoenix.otel import register from openinference.instrumentation import using_session register(project_name="my-agent", auto_instrument=True) with using_session(conversation_id): run_agent(message)
# After import rius rius.init(service_name="my-agent") with rius.session(conversation_id): run_agent(message)
  1. Map the rest of the tracing API. rius.init() instruments every supported library it finds, so your own instrument() calls and auto_instrument=True go away.
Phoenix / OpenInferenceRius SDK
using_session(session_id)with rius.session(session_id):
using_user(user_id)with rius.user(user_id):
@tracer.chain@rius.observe
@tracer.agent@rius.observe(kind=rius.SpanKind.AGENT)
@tracer.tool@rius.observe(kind=rius.SpanKind.TOOL)
  1. Switch using_session and using_user to rius.session and rius.user in the same change. The OpenInference versions do not stamp spans you start with @rius.observe, which are usually the root.

  2. Run both backends first. Keep register() pointed at Phoenix and add Rius as a second span processor with replace_default_processor=False. Without that argument, Phoenix’s exporter is removed and your Phoenix project goes quiet.

What you get

  • The same trace trees, with LLM spans shown as generations that carry model, token counts and a list-price cost estimate.
  • Traces grouped by agent, session and user, with no re-instrumentation.
  • On Path B, privacy controls, the 4096 span attribute limit by default, and heartbeats that show which agent processes are alive.
  • A reversible cutover, because both backends receive identical spans until you remove the Phoenix exporter.

Some things have no counterpart in Rius: evals, datasets and experiments, prompt management and the playground, annotations and feedback, custom model prices, self-hosting, and import of historical Phoenix traces. Phoenix-only attributes such as openinference.project.name, metadata and tags are stored and filterable by key, but they do not drive grouping or analytics.

When something else fits

If evals, datasets or annotations are central to your workflow, keep Phoenix for those pieces and send traces to both backends rather than cutting over completely. If you need your existing trace history, keep your Phoenix instance or an export of it, since Rius starts from the first span you send.

Still stuck? Get help

Last updated on