Skip to main content
A trace records what a run did: one span per Predict call, carrying the rendered prompt, the parsed output, and a request fingerprint. Replay serves a later run back from that recording instead of a live provider. The dsrs CLI that checks, formats, and serves .dsrs artifacts has its own page: CLI.

Recording and replaying a run

Wrap any call in capture to get the run back as a value, then persist it as JSONL:
ReplayMode::Strict proves the pipeline still behaves exactly as recorded: every call is served from the log, with zero provider calls.
ReplayMode::UntilDivergence replays the unchanged prefix free and goes live only from the first call your change actually touches:
Each span’s fingerprint covers the full rendered prompt and the model settings; divergence is detected at the first call whose fingerprint differs from its recording, and only calls from there run live.

Capture

capture records every Predict call on the current task into a Trace while the scope is active.
  • capture(f) runs the closure and returns its result plus the recorded Trace.
  • capture_with_meta(meta, f) is the same with caller-provided rollout metadata (TraceMeta: rollout input, candidate hash, free-form tags). A missing trace_id or start time is minted at scope start.
Scoping is task-local: spawned subtasks do not inherit the scope, and nested scopes are exclusive (the innermost records). With no scope active the cost is one task-local probe per call. Traces serialize to JSONL via Trace::to_jsonl and Trace::from_jsonl.

What a span records

One span is one Predict invocation. At a high level it records:
  • Who ran: the component name (the same name the params system uses, so spans join back to tunable slots) plus a per-component sequence number; (component, seq) is unique per trace and is what replay keys on.
  • What went in: the rendered prompt (an interned system-and-demos prefix plus the live suffix), the typed input fields as JSON, and the redacted model config.
  • What happened inside: ordered events, one Exchange per provider round-trip and one ToolRun per tool execution.
  • What came out: the raw assistant text, the parsed output fields, aggregated token usage, and any error (kinds: lm, parse, tool, cancelled).
  • What it was worth (optional): a span-level Eval, present only when a metric assigned per-span credit through TypedMetric::evaluate_spans (see Evaluation). Demo harvesting prefers it over the whole-rollout score; the field is omitted from the JSONL entirely when absent, so eval-free traces serialize exactly as before.
  • A request fingerprint: request_hash, a stable hash over the redacted model config plus the full rendered prompt. This is the replay key and the determinism check.
  • Timing and completeness: start time, duration, and a complete flag (false when the span was truncated or redacted; replay refuses incomplete spans).

Replay

replay serves Predict calls from a recorded trace instead of a live provider: each call’s request_hash is compared against the next recorded span for its component, and on a match the recorded output is served with zero API calls and no tool execution.

Modes

ReplayReport fields

Replay error kinds

Replay scoping mirrors capture: task-local, not inherited by spawned subtasks, innermost scope wins. Compose replay outside with capture inside to record a counterfactual rollout while serving its unchanged prefix from the base trace.
Traces serialize after a run finishes (Trace::to_jsonl). For live, human-readable console output while a program runs, call init_tracing (documented on Utils): it installs a process-global pretty tracing subscriber (respecting RUST_LOG, defaulting to dspy_rs=debug) and is independent of trace capture. There are no built-in exporters — the JSONL trace format is stable and self-describing, so external projections (observability, RL datasets) are serialization-side work on top of it.

See also

  • CLI: dsrs serve returns the capture-scope trace artifact from POST /run?trace=1
  • Utils: init_tracing for live pretty console output
  • Optimizers: traces as the evidence base for reflective optimization
  • Evaluation: the evaluation loop that hands each rollout’s trace to your metric
  • Example: 24-frontdesk-replay.rs, capture, strict replay, and until-divergence replay end to end
  • Example: 12-tracing.rs, scoped trace capture for a composed module
  • Example: 17-pretty-tracing.rs, init_tracing output against an offline LM