Skip to Content

Migrate from Helicone to Rius

Helicone and Rius capture LLM calls in different ways. Helicone is mainly a proxy: your client sends every request through a Helicone base URL, and Helicone logs it on the way through, with features configured by Helicone-* request headers. Rius is OpenTelemetry-based: the Rius SDK instruments your LLM client inside your process and exports spans (one record per step, such as an LLM call), so nothing sits between your code and the provider.

Migrating is a small code change: add the Rius SDK, point the client back at the provider if you no longer need the gateway, and move the values you sent as Helicone headers onto Rius sessions, users and span attributes. This guide maps the concepts, walks the paths, and ends with a cutover checklist.

TL;DR: Call rius.init(), then remove the Helicone base URL and Helicone-Auth header, or keep them if you rely on the gateway. Move your Helicone-* session, user and property headers onto Rius equivalents (see the checklist). Rius is not a gateway: caching, rate limits, retries and fallbacks stay with whatever serves them today.

Why migrate from Helicone to Rius

Rius is agent observability. You get:

  • traces with the full tree of agent, tool, retrieval and model steps, not LLM requests only
  • sessions, per-user views and server-side cost
  • alerts
  • with the Rius SDK, which agent processes are alive, and, with partial spans turned on, runs still in progress
  • an MCP server so your coding agent can query production traces

What Helicone offers beyond observability is covered in what does not carry over.

Concept mapping: Helicone vs Rius

HeliconeRiusNotes
Request (one logged LLM call)GenerationAn LLM span with model, tokens, and messages. Auto-instrumentation creates one per call.
Session trace (Helicone-Session-Path)TraceA span started inside another span becomes its child, so the tree comes from your code’s call structure, not from a path string.
Session (Helicone-Session-Id)Sessionrius.session(id) stamps session.id on every span in scope.
Session name (Helicone-Session-Name)Root span name or attributeNo separate session name field. Name the span that wraps the work, or set an attribute.
User (Helicone-User-Id)Userrius.user(id) stamps user.id; the Users page lists each user with their traces, failures, error rate and cost.
Custom properties (Helicone-Property-*)Span or resource attributesFilterable in the trace list. See custom properties.
CostCostComputed server-side from the model and token counts.
Omit logs (Helicone-Omit-Request, Helicone-Omit-Response)capture_content=FalseStrips prompts and completions at export; metadata still flows. See privacy controls.
AlertsAlertsRule-based alerts with webhook, email and Slack channels.
Caching, rate limits, retries, fallbacksNot availableRius is not a gateway. See what does not carry over.
Prompts, datasets, scores, feedbackNot availableSee what does not carry over.

Choose your path

  • You call through the Helicone proxy or AI Gateway (oai.helicone.ai, anthropic.helicone.ai, ai-gateway.helicone.ai, or a Helicone-Auth header): instrument with the Rius SDK, then decide whether to keep the gateway in front of the provider.
  • You use Helicone’s async logger (helicone-async or @helicone/async): replace the logger with the Rius SDK. Both instrument your client in process, so the swap is one call.

Path A: instrument your client with the Rius SDK

Install the SDK with the integrations for the providers you call. In Python the extras install the instrumentation. In TypeScript you install the instrumentation packages next to the SDK: the @arizeai/openinference-instrumentation-* packages are the OpenInference instrumentations the Rius TypeScript SDK attaches for each provider.

pip install "glassflow-rius[openai,anthropic]" export RIUS_API_KEY="ri_..."

Point the client back at the provider

With Helicone, the provider client carries a Helicone base URL and an auth header. With Rius, the client is the provider’s default one, and init() traces every call it makes:

# Before: through the Helicone proxy import os from openai import OpenAI from anthropic import Anthropic openai_client = OpenAI( base_url="https://oai.helicone.ai/v1", default_headers={"Helicone-Auth": f"Bearer {os.environ['HELICONE_API_KEY']}"}, ) anthropic_client = Anthropic( base_url="https://anthropic.helicone.ai", default_headers={"Helicone-Auth": f"Bearer {os.environ['HELICONE_API_KEY']}"}, )
# After: direct to the provider, traced by Rius import rius from openai import OpenAI from anthropic import Anthropic rius.init(service_name="my-agent") # reads RIUS_API_KEY openai_client = OpenAI() # default base URL, OPENAI_API_KEY anthropic_client = Anthropic() # default base URL, ANTHROPIC_API_KEY

Each call becomes a generation with the model, input and output tokens (including cache reads and writes), and the messages; Rius computes cost from the model and tokens. Calling a provider other than OpenAI or Anthropic? Check the integrations list, or trace the call as a manual generation.

If you use the AI Gateway with Helicone-managed provider keys or credits, switch to your own provider keys before you remove the gateway. Rius never holds provider keys, so without your own keys the calls fail.

Keep the gateway and run side by side

If you rely on gateway features, such as caching, rate limits or fallbacks, leave the Helicone base URL and headers in place and add rius.init() anyway. The Rius integrations patch the client library in your process, not the network path, so calls are traced whatever base URL the client uses. This is also the side-by-side setup: every call is logged by Helicone on the network path and traced by Rius in your process, so you can compare request counts, token totals and cost on the same traffic, then remove the Helicone base URL when you are ready.

Two differences when you keep the gateway:

  • Cache hits: Rius instruments the client in your process, so a response Helicone serves from its cache is recorded like any other call: a generation with the token counts the response carries, priced from those tokens at the provider’s rates. During side by side, Rius cost is higher than Helicone’s by your cache-hit share.
  • Model names: use plain model names (gpt-4o-mini), not the gateway’s gpt-4o-mini/openai form, or calls show tokens but no cost.

Path B: replace the Helicone async logger

Helicone’s async logger instruments your provider client in process and sends the calls to Helicone without the proxy. The Rius SDK works the same way, so the swap is one call. Install the SDK as in Path A, then:

# Before: Helicone async logger from helicone_async import HeliconeAsyncLogger logger = HeliconeAsyncLogger(api_key=HELICONE_API_KEY) logger.init()
# After: Rius SDK import rius rius.init(service_name="my-agent") # reads RIUS_API_KEY

Replace logger.set_properties(...) (withProperties in TypeScript) with the session, user and attribute equivalents in map Helicone headers and properties. Remove the Helicone logger rather than running both in one process: both register the process-wide OpenTelemetry tracer provider, OpenTelemetry keeps only the first registration, and the instrumented calls reach only one of the two tools. To compare the two backends, run the logger in one service and the Rius SDK in another, or compare a deploy on each.

Map Helicone headers and properties

These replace the Helicone-* headers (or the async logger’s properties), whichever path you took.

Sessions

Helicone groups requests with Helicone-Session-Id and arranges them with Helicone-Session-Path. In Rius you scope a session around the work, and the tree comes from how your spans nest:

# Before: Helicone session headers on each request response = openai_client.chat.completions.create( model="gpt-4o-mini", messages=messages, extra_headers={ "Helicone-Session-Id": session_id, "Helicone-Session-Path": "/ticket/answer", "Helicone-Session-Name": "Support ticket", }, )
# After: a Rius session scope and a parent span with rius.session(session_id): with rius.start_as_current_span("support_ticket"): response = openai_client.chat.completions.create( model="gpt-4o-mini", messages=messages, )

Every span started inside the scope, including the auto-instrumented LLM call, carries session.id. To keep the structure of a Helicone-Session-Path such as /ticket/answer, nest spans the same way: a ticket span with an answer span started inside it. Sessions covers scopes and the process-wide default.

Users

Helicone-User-Id maps to a user scope:

with rius.user(request.user_id): handle(request) # every span of the request carries user.id

Users appear on the Users page and as a user filter on the trace list.

Custom properties

A Helicone-Property-* header tags one request. In Rius, tags are span attributes. How you carry them over depends on what the property describes:

  • Per request (a ticket id, a conversation, a feature): set it on the span that wraps the work.

    with rius.start_as_current_span("support_ticket") as span: span.set_attribute("app.ticket_id", ticket_id) span.set_attribute("app.feature", "auto-reply") response = openai_client.chat.completions.create(...)

    The trace list finds the trace when any of its spans carries the value.

  • Per deployment (app version, region): put it on the resource (the attributes that describe the process, set with the standard OpenTelemetry OTEL_RESOURCE_ATTRIBUTES variable), so every span from the process carries it. Keep each environment (production, staging) in its own workspace with its own API key rather than tagging it here.

    export OTEL_RESOURCE_ATTRIBUTES="service.version=1.4.2,app.region=eu"

Filter on either kind in the trace list with Add filter; see search and filter, and declare an attribute with a type to filter it numerically.

What does not carry over from Helicone

Rius is an observability platform, not a gateway, and today it has no equivalent for:

  • Gateway features: no proxy, so no response caching, no rate limits, no automatic retries, no provider or model fallbacks, and no unified API across providers. If you depend on these, keep the Helicone gateway or move them to another gateway or your own client code. Rius traces the calls either way.
  • Provider key management and credits: Rius never holds your provider keys and never bills for model usage. You call providers with your own keys.
  • Prompt management: no versioned prompts, prompt deployment or prompt playground. Prompt content on spans is captured and inspectable, but there is no registry.
  • Scores, feedback, datasets and evals: no score or feedback objects on traces, no curated datasets, and no eval runs.
  • Security and moderation: no prompt-injection detection or moderation layer, since those run in the request path.
  • Property and product analytics: attributes are filterable, but there is no cost or metric breakdown per custom property, and no active-user, retention or cohort analytics.
  • Self-hosting: Rius is a hosted service only.
  • Historical data: existing traces stay in Helicone; Rius starts from the first span you send it.

If caching, rate limits or fallbacks are load-bearing in production, do not remove the gateway as part of the migration. Add Rius next to it first, then replace the gateway as a separate project if you want to.

Helicone 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. Install the Rius SDK with your providers’ integrations and call rius.init() at startup (installation).
  3. Keep both backends on the same traffic for now. With the proxy or AI Gateway, keep the Helicone base URL and headers (side by side). With the async logger, run it and the Rius SDK in separate services or deploys (Path B).
  4. Verify one call end to end in Rius: the trace appears under the agent named by the service_name you passed to rius.init() (serviceName in TypeScript), the generation shows model and token counts, and cost appears.
  5. Replace Helicone-Session-Id with rius.session(), Helicone-User-Id with rius.user() (withSession and withUser in TypeScript), and Helicone-Property-* with span or resource attributes. Check the session and user show on the trace.
  6. Compare both backends on real traffic for a few days: request and LLM call counts, token totals, cost.
  7. Decide what happens to gateway features you use. Keep the gateway, or move caching, retries and fallbacks elsewhere first.
  8. Move anything that reads from Helicone (dashboards, alerts, the query API) to the Rius equivalents, or deliberately leave it behind.
  9. Remove the Helicone base URL, Helicone-* headers and async logger, and revoke the Helicone API key once nothing sends to it.

Frequently asked questions

Can I point the Helicone proxy at Rius?

No. The Helicone proxy logs to Helicone, and Rius does not accept Helicone’s request logs. Rius receives OpenTelemetry spans, which the Rius SDK (or any OpenTelemetry instrumentation) creates in your process.

Do I have to remove the Helicone gateway to use Rius?

No. The Rius SDK instruments your client library, so calls are traced whether they go to the provider directly or through the Helicone gateway. Keep the gateway as long as you need its features.

Does Rius add latency to my LLM calls?

The Rius SDK exports spans in the background after each span ends, so the call itself does not wait on Rius, and nothing proxies the request. If Rius is unreachable, your calls still go through; see reliability.

Can I move my Helicone history into Rius?

No. There is no import from Helicone. Keep Helicone, or an export from its query API, for history, and start Rius traces from the cutover.

Last updated on