CLI & Environment Config
Installing bir-sdk adds a standard-library-only bir command for inspecting
local data and sending it to a server.
Commands
bir traces # list local traces, newest first
bir traces --limit 20 --json # machine-readable output
bir traces --name checkout --status error --since 2026-01-01 # filter the listing
bir show <trace-id> # print one trace as an indented event tree
bir show <trace-id> --json # nested {event, children} JSON tree
bir stats # summarize counts, tokens, cost, and latency
bir stats --json # the same figures as machine-readable JSON
bir stats --status error --since 2026-01-01 # summarize a filtered subset
bir prune --before 2026-01-01 # preview removing traces older than a date
bir prune --keep-last 500 --yes # keep only the 500 newest traces (writes)
bir tail # follow the local trace file
bir experiments # list local experiments and scores
bir experiment-show <id> # one experiment's summary and per-example scores
bir experiment-show <id> --json # nested experiment JSON for scripts
bir experiment-report <id> # self-contained HTML report to stdout
bir experiment-report <id> --format markdown --output report.md # write a file
bir send # send events to the default local server
bir send-experiment .bir/experiments/<name>-<id>.jsonl
bir eval-gate baseline.jsonl candidate.jsonl --tolerance 0.01
bir eval-gate baseline.jsonl candidate.jsonl \
--tolerance 0.01 --score-tolerance latency_under=0.05 --missing-score regress
bir export-otel --endpoint http://localhost:4318/v1/traces # needs the 'otel' extra
bir config # print the effective resolved configuration
bir config --json # the same fields as machine-readable JSON
| Command | What it does |
|---|---|
bir traces [--path P] [--limit N] [--json] [--include-rotated] [--name SUBSTRING] [--status {success,error}] [--since ISO] [--until ISO] |
List trace time, status, duration, event count, and name; optionally filtered. |
bir show TRACE_ID [--path P] [--include-rotated] [--json] |
Print one trace as an indented event tree, or a nested JSON tree. |
bir stats [--path P] [--include-rotated] [--json] [--name SUBSTRING] [--status {success,error}] [--since ISO] [--until ISO] |
Summarize trace counts, token usage, cost per currency, and latency; optionally filtered. |
bir prune [--path P] [--include-rotated] [--before ISO] [--keep-last N] [--status {success,error}] [--dry-run] [--yes] |
Destructive. Remove whole old/unwanted traces from the local store. Safe by default. |
bir tail [--path P] |
Follow a trace file and print new events until interrupted. |
bir experiments [--dir D] [--json] |
List local experiment summaries. |
bir experiment-show EXPERIMENT_ID [--dir D] [--json] |
Print one experiment's summary and per-example results. |
bir experiment-report EXPERIMENT_ID [--dir D] [--format {html,markdown}] [--output PATH] |
Render one experiment to a self-contained HTML or Markdown report. |
bir send [--path P] [--server URL] [--include-rotated] [--mark-sent] [--retries N] [--backoff SECONDS] [--timeout SECONDS] |
Send local events and print the upload result. |
bir send-experiment PATH [--server URL] [--retries N] [--backoff SECONDS] |
Send a saved experiment and summary, retrying transient failures. |
bir eval-gate BASELINE CANDIDATE [--tolerance N] [--score-tolerance NAME=VALUE] [--missing-score {ignore,regress}] [--per-example] |
Fail when a shared aggregate evaluator regresses past tolerance. |
bir export-otel --endpoint URL [--path P] [--include-rotated] [--header KEY=VALUE] [--service-name NAME] [--environment ENV] [--timeout SECONDS] |
Export local traces to an OTLP endpoint via the optional otel extra. |
bir config [--json] |
Print the effective resolved SDK configuration (read-only). |
Every command accepts --help. Trace commands use .bir/traces.jsonl by
default; experiment listing uses .bir/experiments; send commands target
http://127.0.0.1:8000.
bir traces can narrow the listing before printing: --name keeps traces whose
name contains a case-sensitive substring, --status {success,error} keeps traces
with that exact status, and --since/--until keep traces whose start time falls
within those inclusive ISO 8601 bounds (a value without an offset is treated as
UTC; a malformed timestamp exits non-zero). Filters combine with AND, apply to both
the table and --json, and are applied before --limit so --limit counts only
matching traces. With no filters the output is unchanged.
bir show TRACE_ID reads the same files as bir traces, finds the trace with
that id, and renders its events as a tree ordered by parent/child: each line
shows the event type, name, status, and duration, plus the model and token usage
on generations and the value on scores. --json emits a deterministic nested
{"event": ..., "children": [...]} tree of the same data for scripts. An unknown
trace id prints nothing to stdout and exits non-zero.
bir stats aggregates the same local traces into a one-screen summary: the total
trace count with the success and error splits, summed input/output/total token
usage over generation events, summed cost grouped by currency, and trace latency
count, mean, and p95 (the nearest-rank 95th percentile, computed with the standard
library). Costs in different currencies are reported on their own lines and never
summed together, so a store mixing USD and EUR shows both. --json emits the same
figures as a deterministic object for scripts. An empty store exits 0 with zeroed
counts and - latency. Latency is read from each trace's root duration, so partial
traces split across rotated files are counted only when --include-rotated brings
in their root.
bir stats accepts the same --name, --status, --since, and --until filters
as bir traces, with identical semantics, so you can summarize a subset (e.g.
errors only, or usage since yesterday). Filters combine with AND and apply before
aggregation, so every figure — counts, tokens, cost, and latency — reflects only the
matching traces. An empty filtered result still exits 0 with zeroed counts; with no
filters the output is unchanged.
bir prune is the destructive counterpart that reclaims space: it removes
whole traces from the local store so a long-lived .bir/traces.jsonl (and its
rotated siblings) does not grow without bound. It operates on whole traces and
never splits one across the keep/drop boundary, rewriting each file via a temp
file and atomic replace under the same advisory lock an append takes, so a
concurrent writer can never interleave and a partial failure leaves the original
file intact. Selection: --before ISO removes traces whose start time precedes
the cutoff, --keep-last N removes all but the N most recent, and --status
{success,error} restricts removal to that status (--before and --keep-last
combine by union; --status is applied as a further restriction). It is safe by
default in two ways: a bare bir prune with no selection filter is rejected so
the store can never be wiped by accident, and even with a filter it only previews
unless you pass --yes — without it (or with --dry-run, which always wins) it
prints what it would remove and writes nothing. The summary is
removed=<traces> kept=<traces> events=<dropped> bytes=<reclaimed> (a dry run adds
(dry run; pass --yes to apply)). --include-rotated extends pruning to the
size-rotated siblings; without it only the active file is rewritten. An empty store
and a run that matches nothing both exit 0 and write nothing. Pruning never touches
experiments, only traces.
bir export-otel replays local traces to an OpenTelemetry/OTLP endpoint using
the optional otel extra (pip install 'bir-sdk[otel]'), reading the same files
as bir traces (with --path and --include-rotated). --endpoint is required;
--header KEY=VALUE is repeatable for backend auth (only the first = splits the
key from the value), and --service-name, --environment, and --timeout are
forwarded to the exporter. The exported OpenTelemetry Resource records
service.name plus, when the traces recorded them, deployment.environment (from
configure(environment=...)) and bir.source (from configure(source=...)), and
generation spans gain gen_ai.system when an integration recorded the provider.
--environment sets deployment.environment explicitly and overrides whatever the
traces recorded; without it, the value is derived from the traces and, when one run
mixes environments or sources, the conflicting attribute moves from the Resource
onto each span (bir.environment / bir.source) instead of being dropped. It
prints how many traces and spans were exported and exits non-zero with an install
hint when the extra is missing. The export only reads the local JSONL; it never
writes to or alters it.
--include-rotated on bir traces, bir show, bir stats, bir send, and
bir export-otel also reads
size-rotated trace files (traces.jsonl.1 ..) created by
configure(max_bytes=...), oldest-first alongside the active file. It is off by
default, so these commands operate on the active file only unless the flag is
passed. bir send --include-rotated deduplicates events by ID when a rotated
file overlaps the active one.
bir experiment-show EXPERIMENT_ID reads the same --dir directory as
bir experiments (default .bir/experiments), finds the experiment with that id,
and prints its summary header (name, status, example and error counts, run times),
a table of evaluator aggregate means, and a per-example table of id, status, and
scores. --json emits a deterministic nested object with the summary fields and a
results list of per-example example_id, status, scores, and error. An
unknown experiment id prints nothing to stdout and exits non-zero.
bir experiment-report EXPERIMENT_ID resolves the experiment the same way as
bir experiment-show (the same --dir directory, the same non-zero exit and
clean stdout for an unknown id) and renders it to a self-contained report
combining the summary, the evaluator aggregate means, and a per-example table of
id, status, and scores. --format chooses html (default; a standalone document
with inline styles and no external assets) or markdown. The report is written to
stdout, or to --output PATH (creating parent directories) with a confirmation
line on stdout instead. Output is deterministic and every experiment-derived
string is escaped for the chosen format, so already-redacted example text cannot
inject markup. The same rendering is available in Python as
bir.evals.render_experiment_report.
bir send exposes the same options as send_events(). --mark-sent records the
event IDs the server accepts in a <trace_path>.sent sidecar and skips them on
later sends, so re-running a send is cheap and idempotent (off by default; the
sidecar never touches the trace JSONL). --retries (default 2), --backoff
seconds (default 0.5), and --timeout seconds (default 10) tune the same
transient-failure handling described below, accept non-negative values only, and
the delay between attempts is backoff * 2**attempt. See
Sending to a Server.
bir send-experiment retries transient upload failures (network errors,
timeouts, and HTTP 5xx) with exponential backoff. --retries (default 2) and
--backoff seconds (default 0.5) accept non-negative values only, and the
delay between attempts is backoff * 2**attempt. HTTP 4xx, a missing file, and
an invalid server response fail immediately. See
Sending to a Server.
bir config answers "what configuration is active right now?" without a Python
REPL. It prints the effective, resolved settings of the live SDK configuration —
the absolute trace_path, the capture_inputs/capture_outputs flags, the
enabled master switch, sample_rate and any exact-name sample_rules, the
service_name/environment/source trace metadata, the max_bytes/backup_count
rotation settings, and the max_value_length/max_collection_items capture-size
limits — followed by which BIR_* environment variables are currently set. It
reflects everything an explicit configure(...) call changed in-process. It is
strictly read-only: it never mutates configuration and always exits 0. To keep
it non-leaky, the additional redaction rules and the local model_prices table are
reported as counts only (never the patterns or prices themselves), and only the
names of set BIR_* variables are listed, never their values. --json emits
the same fields as a deterministic, sorted object for scripts.
Commands print failures to stderr and exit non-zero for missing or malformed
files, server failures, and failed eval gates. JSON output on traces, show,
stats, experiments, experiment-show, and config is suitable for scripts.
Environment configuration
Bir reads these variables once when the bir package is imported:
| Variable | Meaning | Default |
|---|---|---|
BIR_TRACE_PATH |
Local trace JSONL path. | .bir/traces.jsonl |
BIR_CAPTURE_INPUTS |
Enable input capture. | false |
BIR_CAPTURE_OUTPUTS |
Enable output capture. | false |
BIR_DISABLED |
Master kill switch: a truthy value records nothing (inverse of enabled). |
false |
BIR_SAMPLE_RATE |
Trace recording probability from 0.0 to 1.0. |
1.0 |
BIR_SERVICE_NAME |
Service name on trace roots. | unset |
BIR_ENVIRONMENT |
Deployment environment on trace roots. | unset |
BIR_SOURCE |
Trace source tag on trace roots (metadata.source). |
unset |
BIR_MAX_VALUE_LENGTH |
Truncate captured strings longer than this many characters. | unlimited |
BIR_MAX_COLLECTION_ITEMS |
Keep at most this many items of a captured list/mapping. | unlimited |
export BIR_TRACE_PATH=/var/log/bir/traces.jsonl
export BIR_CAPTURE_INPUTS=false
export BIR_CAPTURE_OUTPUTS=false
export BIR_DISABLED=0
export BIR_SAMPLE_RATE=0.1
export BIR_SERVICE_NAME=rag-api
export BIR_ENVIRONMENT=production
export BIR_SOURCE=checkout-api
export BIR_MAX_VALUE_LENGTH=10000
export BIR_MAX_COLLECTION_ITEMS=100
Boolean values accept 1, true, yes, and on, or 0, false, no, and
off, case-insensitively. BIR_DISABLED is the master kill switch and the
inverse of the enabled setting: a truthy value turns all recording off (every
primitive still runs your code and still raises, but nothing is written), while
an explicit configure(enabled=...) always wins over it. BIR_MAX_VALUE_LENGTH
and BIR_MAX_COLLECTION_ITEMS
take a non-negative integer and bound captured values only (truncating after
redaction); see Capture & Privacy.
Invalid values raise a configuration error.
Explicit calls take precedence:
from bir import configure
configure(sample_rate=1.0, environment="staging")
Capture remains disabled unless explicitly enabled. See Capture & Privacy before recording application payloads.