Skip to Content
RiusSDKIntegrationsClaude Agent SDK

Claude Agent SDK

Trace agents built with the Claude Agent SDK  with Rius. This framework is different from the others on this page’s siblings: it drives the Claude Code runtime in a separate process, so no model client runs in your process and auto-instrumentation has nothing to hook. Coverage is manual, and still gives you per-task traces with tokens and cost. The runnable example behind this guide lives in agent-observability-examples .

How coverage works

Two pieces, both explicit:

  • An observe wrapper makes each task the root AGENT span.
  • A manual generation wraps the query() loop and is filled from the run’s ResultMessage: token usage, the real model id, and the collected answer. Cost is then computed server-side as for any generation.

Setup

pip install glassflow-rius claude-agent-sdk

The base SDK is enough; no extra applies. The claude CLI must be installed and authenticated.

import rius from claude_agent_sdk import ( AssistantMessage, ClaudeAgentOptions, ResultMessage, TextBlock, query, ) from rius import SpanKind rius.init(api_key="gf_...", 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"), ) 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 (claude-haiku-4-5), not an alias (haiku), for both the CLI option and the generation. Cost is computed from the requested model, and the pricing table matches real model ids only; the alias produces a costless generation.

What lands where

In the frameworkIn the trace
A taskThe root AGENT span (your observe wrapper)
The whole runtime turnOne generation with model, tokens, cost
The runtime’s internal stepsNothing; see blind spots

A task looks like this (real trace from the example, claude-agent-sdk 0.2.148):

handle-task AGENT 5.0s └─ claude_agent_turn LLM 5.0s claude-haiku-4-5, tokens + cost

Blind spots

Verified against claude-agent-sdk 0.2.148:

  • The runtime’s inner work is invisible. Tool calls, sub-turns, and file operations happen inside the Claude Code process and produce no spans; the generation records the turn’s totals only.
  • Cached tokens are not recorded. set_usage carries input and output tokens; the ResultMessage’s cache token counts have nowhere to go, so recorded cost undercounts cache-heavy runs relative to the ResultMessage’s own total_cost_usd.
  • Auth is the CLI’s. The runtime uses whatever auth Claude Code has; an exported ANTHROPIC_API_KEY takes precedence over a claude.ai login.

Sessions

One conversation is one session: scope each task with rius.session(). The SDK’s own session_id (resumable conversations) makes a natural Rius session id.

Verify

Run one task, then open the trace in the console: the root AGENT span and the generation with a full model id, tokens, and cost should be there. A generation without cost means an alias was used as the model id.

Last updated on