Traces and Spans
RDK uses a hierarchical tracing model based on two core concepts:Traces
A trace represents a complete operation in your application, such as handling a user request or processing a task. Each trace has:- A unique ID
- A name (from your
@observedecorator ortrace()context manager) - A start time and optional end time
- A collection of spans
- Optional metadata (tags, user_id, session_id, version)
Spans
A span represents a single operation within a trace, such as an LLM call or tool execution. Each span has:- A unique ID
- A reference to its parent trace
- A type (LLM, CHAIN, TOOL, FUNCTION)
- Input and output data
- Timing information
- Token usage (for LLM spans)
- Status (RUNNING, SUCCESS, ERROR)
Span Types
RDK categorizes spans into four types:Auto-instrumentation
When RDK initializes (automatically on the first@observe call), it patches supported LLM SDKs to capture calls. This means you don’t need to modify your existing LLM calls — RDK intercepts them and creates spans automatically.
BAML is not auto-instrumented. You must call
b = instrument_baml(b) after init(). See BAML Integration.How It Works
- RDK patches the SDK’s request methods
- When you call an LLM, RDK creates a span before the call
- The original call executes normally
- RDK captures the response, token usage, and timing
- The span is queued for batching
Creating Traces
With @observe (recommended for functions)
With trace() (for scripts and notebooks)
Decorator Options
Manual Spans
For custom code that isn’t automatically captured, usespan():
Batching and Transport
RDK batches spans to reduce network overhead:Thread Safety
RDK usescontextvars for thread-safe trace context:
- Each thread/task has its own trace context
- Async operations preserve context correctly
- You can safely use RDK in multi-threaded applications

