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.nameresource attribute, within the workspace your API key belongs to. Phoenix’sopeninference.project.nameis 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,EMBEDDINGandRERANKER. - LLM spans are read as generations, so they get model, token and cost analytics.
- Sessions and users use the same
session.idanduser.idattributes. - 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
- 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,
)-
Check three details. Pass
endpointandprotocolin code, because setting onlyPHOENIX_COLLECTOR_ENDPOINTto the Rius URL sends spans to the wrong port. Setservice.name, or traces arrive asunknown_service. Usebatch=Truein production, becauseregister()defaults to a processor that exports each span synchronously. -
Set session and user on the root span. Your
using_session,using_userandusing_attributescontext managers keep working, because they stampsession.idanduser.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)-
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 passSpanLimits. -
Path B: switch to the Rius SDK. Install the extra for your library, set the key, then swap
register()forrius.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)- Map the rest of the tracing API.
rius.init()instruments every supported library it finds, so your owninstrument()calls andauto_instrument=Truego away.
| Phoenix / OpenInference | Rius 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) |
-
Switch
using_sessionandusing_usertorius.sessionandrius.userin the same change. The OpenInference versions do not stamp spans you start with@rius.observe, which are usually the root. -
Run both backends first. Keep
register()pointed at Phoenix and add Rius as a second span processor withreplace_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.