How do I switch from Langfuse to Rius for the Claude Agent SDK?
Rius traces the Claude Agent SDK with an observe wrapper and one manual generation, so you replace your Langfuse calls with those two pieces. The wrapper makes each task the root agent span. The generation wraps the query() loop and is filled from the run’s ResultMessage, which gives you the model, tokens and cost for the turn. Python is the only language with this integration today.
How it works
The Claude Agent SDK drives the Claude Code runtime in a separate process. No model client runs in your process, so auto-instrumentation has nothing to hook and the generation you write is the only record of the turn. Rius needs two explicit pieces:
- An
observewrapper withkind=SpanKind.AGENTmakes each task the root span. - A generation opened with
rius.start_as_current_generationwraps thequery()loop. You fill it from theResultMessage: token usage, the real model id and the collected answer. Rius computes cost on its side from the model and tokens. You never send a cost value.
The Langfuse calls you use today have close Rius equivalents:
| Langfuse (Python) | Rius (Python) |
|---|---|
Langfuse(...) or get_client() | rius.init(api_key="ri_...", service_name="my-agent") |
@observe() | @rius.observe, or @rius.observe(name=..., kind=SpanKind.AGENT) for the task root |
start_as_current_observation(as_type="generation", model=...) | rius.start_as_current_generation(name, model=..., provider=...) |
span.update(input=..., output=...) | span.set_input(...) / span.set_output(...) |
gen.update(usage_details=...) | gen.set_usage(input_tokens=..., output_tokens=...) |
propagate_attributes(session_id=...) | with rius.session(session_id): |
propagate_attributes(user_id=...) | with rius.user(user_id): |
One Rius API key replaces the Langfuse key pair. The full concept mapping is in Migrate from Langfuse to Rius, and the general approach is in Migrate to Rius.
Set it up
- Install the SDK next to the Claude Agent SDK. The base package is enough, with no extra. The
claudeCLI must be installed and authenticated.
pip install glassflow-rius claude-agent-sdk- Remove the Langfuse client setup and decorators from the agent code. Initialize Rius once, wrap the task, and open one generation around the
query()loop. This is the complete integration:
import rius
from claude_agent_sdk import (
AssistantMessage, ClaudeAgentOptions, ResultMessage, TextBlock, query,
)
from rius import SpanKind
rius.init(api_key="ri_...", service_name="my-agent")
MODEL = "claude-haiku-4-5"
@rius.observe(name="handle-task", kind=SpanKind.AGENT)
async def handle_task(task: str) -> str:
options = ClaudeAgentOptions(model=MODEL, max_turns=1)
parts = []
with rius.start_as_current_generation(
"claude_agent_turn", model=MODEL, provider="anthropic", input=task
) as gen:
async for message in query(prompt=task, options=options):
if isinstance(message, AssistantMessage):
parts.extend(
b.text for b in message.content if isinstance(b, TextBlock)
)
elif isinstance(message, ResultMessage):
usage = message.usage or {}
gen.set_usage(
input_tokens=usage.get("input_tokens"),
output_tokens=usage.get("output_tokens"),
cache_read_input_tokens=usage.get("cache_read_input_tokens"),
# Anthropic's payload still calls the write count "cache creation"
cache_write_input_tokens=usage.get("cache_creation_input_tokens"),
)
models = list((message.model_usage or {}).keys())
if models:
gen.set_response_model(models[0])
answer = "".join(parts)
gen.set_output(answer)
return answer-
Use a full model id such as
claude-haiku-4-5, not an alias such ashaiku, for both the CLI option and the generation. Cost is computed from the requested model, and an alias produces a generation with no cost. -
Pass the cache counts from the
ResultMessageas they are, and keepprovider="anthropic". With that provider,set_usage()adds the cache counts toinput_tokensfor you, so cache-heavy runs are not undercounted. -
Port your session and user scoping. Wrap each task in
rius.session(...). The Claude Agent SDK’s ownsession_idmakes a natural Rius session id. Userius.user(...)for the end user. -
Run one task and open the trace in the console. While you evaluate, you can keep the rest of your services reporting to Langfuse and run the swapped code in this one service. When the numbers match what you need, remove the Langfuse SDK and revoke its keys.
What you get
Each task shows up as its own trace with the model, tokens and cost for the turn, grouped by session and user if you set them. A task looks like this:
handle-task AGENT 5.0s
└─ claude_agent_turn LLM 5.0s claude-haiku-4-5, tokens + costCost is a list-price estimate in US dollars, computed from the model and the token counts you set, with cache reads and writes at their own rates.
The generation records the turn’s totals only. Tool calls, sub-turns and file operations happen inside the Claude Code process and produce no spans of their own. The runtime uses whatever auth Claude Code has, and an exported ANTHROPIC_API_KEY takes precedence over a claude.ai login. The integration’s own page covers the Claude Agent SDK setup in full, and the integrations list shows how the other frameworks are traced.
When something else fits
- If you rely on Langfuse scores, evals, datasets or prompt management, keep those workflows there. Rius does not offer them, and you can send traces to both tools.
- Existing traces stay in Langfuse. Rius starts from the first span you send it, and it is a managed service only, so it cannot be self-hosted.