CLI Reference
Every subcommand, its flags, and a real sample output. All commands work offline with zero API key — the bundled corpus is self-contained.
distil --version prints the installed version.
Default trajectory: when
--trajectory is omitted, the bundled sre-disk-incident sample trajectory is used.
distil bench
The corpus-wide CI gate. Iterates over all 7 trajectories, prices four strategies, runs the TOST non-inferiority gate on each, and confirms aggressive lossy compression is still rejected. Exits 0 only if every trajectory passes.
Flags
| Flag | Default | Description |
|---|---|---|
--tokenizer | heuristic | heuristic (offline, zero deps) or anthropic (billing-grade count_tokens) |
--pricing | claude-opus-4-8 | Model pricing to use (see pricing catalog) |
--margin | 0.02 | TOST indifference margin (max tolerable mean score drop) |
--alpha | 0.05 | Significance level for the non-inferiority test |
--record | off | Append corpus-aggregate savings to the local savings ledger |
--corpus | bundled corpus | Run on a custom corpus dir (e.g. from distil ingest) |
--savings-only | off | Report token savings only; skip the decision-equivalence gate (for real-trace corpora without DECISION labels) |
$ distil bench corpus gate — 7 trajectories | model claude-opus-4-8 | tokenizer=heuristic domain trajectory $ saved distil aggr pruned --------------------------------------------------------------------------- ops/sre sre-disk-incident 33.1% PASS FAIL 615 coding coding-bugfix 28.7% PASS FAIL 736 support support-refund 32.6% PASS FAIL 765 research research-synthesis 25.7% PASS FAIL 809 data-analysis data-analysis-sql 18.1% PASS FAIL 965 devops devops-rollback 25.0% PASS FAIL 857 finance finance-reconcile 29.1% PASS FAIL 1014 --------------------------------------------------------------------------- aggregate: distil cuts $0.14212 -> $0.10402 (26.8% cheaper) losslessly; 5761 tokens prunable. GATE: PASS — every trajectory certified non-inferior; aggressive rejected on all.
distil savings
Price four compression strategies on a single trajectory and show per-strategy cost, savings percentage, and cache hit tokens. The key result: naive compress + cache costs more than baseline because it busts the prefix cache.
Flags
| Flag | Default | Description |
|---|---|---|
--trajectory, -t | bundled sample | Path to a trajectory JSON file |
--tokenizer | heuristic | heuristic or anthropic (billing-grade) |
--pricing | claude-opus-4-8 | Model pricing to use |
--output-tokens-per-turn | 0 | Assumed output tokens per turn (for output cost accounting) |
--record | off | Append this run to the local savings ledger |
$ distil savings --pricing claude-opus-4-8 model claude-opus-4-8 | 4 turns | tokenizer=heuristic (≈, not billing-grade) strategy $ / run vs baseline cache hits ------------------------------------------------------------------------ baseline (no cache, no compress) 0.01524 0.0% 0 cache only 0.01115 26.8% 1,028 naive compress + cache 0.01691 -11.0% 0 distil (cache-aware lossless) 0.01019 33.1% 1,028 ------------------------------------------------------------------------ distil cuts $0.01524 -> $0.01019 per run (33.1% cheaper), losslessly. note: naive compression costs $0.01691 — 66% MORE than distil despite fewer tokens, because it busts the prefix cache.
distil certify
Run the TOST non-inferiority gate on a single trajectory and strategy. Exits 0 on PASS, 1 on FAIL. Use this as the unit-level quality check; use bench for the corpus-wide gate.
Flags
| Flag | Default | Description |
|---|---|---|
--trajectory, -t | bundled sample | Path to a trajectory JSON file |
--strategy | distil | Strategy to certify: distil, aggressive, naive, none |
--runner | deterministic | deterministic (offline, uses ground-truth markers) or anthropic (live model, requires key) |
--margin | 0.02 | TOST indifference margin |
--alpha | 0.05 | Significance level |
$ distil certify --strategy distil certifying strategy 'distil' on 'sre-disk-incident' (runner=deterministic) turn 0: ok turn 1: ok turn 2: ok turn 3: ok decision-equivalence match rate: 100.0% TOST non-inferiority (margin=0.02, alpha=0.05): mean diff=+0.000, p=0.0000 VERDICT: PASS (certified non-inferior) $ distil certify --strategy aggressive certifying strategy 'aggressive' on 'sre-disk-incident' (runner=deterministic) turn 0: DIVERGED baseline: use_tool(bash, {"cmd":"df -h /"}) compressed: use_tool(bash, {"cmd":"ls /"}) turn 1: DIVERGED turn 2: DIVERGED turn 3: DIVERGED decision-equivalence match rate: 0.0% TOST non-inferiority (margin=0.02, alpha=0.05): mean diff=-1.000, p=1.0000 VERDICT: FAIL (NOT certified — would degrade quality)
distil prune
Causal ablation: for each context block, remove it and replay the trajectory. Blocks that never change a decision are causally inert — provably free to drop.
Flags
| Flag | Default | Description |
|---|---|---|
--trajectory, -t | bundled sample | Path to a trajectory JSON file |
$ distil prune causal ablation over 'sre-disk-incident' — what is free to drop? block occ tokens verdict doc-0 4 312 PRUNE (causally inert) doc-1 4 303 PRUNE (causally inert) obs-0 4 234 keep (changed a decision) obs-1 4 198 keep (changed a decision) system 4 218 keep (changed a decision) tokens provably free to drop: 615 across 2 block(s).
distil compress
Apply the distil strategy to a trajectory turn by turn and report the per-turn token reduction and overall reversibility status.
Flags
| Flag | Default | Description |
|---|---|---|
--trajectory, -t | bundled sample | Path to a trajectory JSON file |
--tokenizer | heuristic | heuristic or anthropic |
$ distil compress
trajectory 'sre-disk-incident' (4 turns, tokenizer=heuristic)
turn before after saved
0 1824 1312 28.1%
1 1956 1401 28.4%
2 2108 1518 28.0%
3 2340 1689 27.8%
ALL 8228 5920 28.1%
reversible: yes (Tier-0/1) — 3 blocks digested, originals recoverable locally
distil verify
Byte-fidelity gate: confirms every Tier-0/1 compression across the corpus is byte-reversible, and that frozen history never mutates between turns (append-only invariant). Exits 0 only if all checks pass.
No flags. Runs across the full bundled corpus.
$ distil verify byte-fidelity gate — reversibility + append-only across the corpus sre-disk-incident reversible + append-only: ok coding-bugfix reversible + append-only: ok support-refund reversible + append-only: ok research-synthesis reversible + append-only: ok data-analysis-sql reversible + append-only: ok devops-rollback reversible + append-only: ok finance-reconcile reversible + append-only: ok FIDELITY: PASS — Tier-0/1 byte-reversible and history append-only across the corpus.
distil validate
Adversarial real-path gate. Where verify and bench check the bundled corpus, validate drives the compressor itself against a battery of diverse and hostile inputs — huge and unicode and deeply-nested tool output, malformed blocks, marker-injection (content that mimics distil's own << handle=… >> stubs), and secret-looking strings — and asserts the load-bearing guarantees hold on every one:
- reversibility — every digest handle recovers its exact original bytes;
- reject-if-bigger — a compressed block is never larger than its original;
- recency-exact — the agent's most-recent tool output is byte-identical;
- fail-open — no input, however hostile, makes the compressor raise;
- content-free — prompt/response/tool text reaches only the local restore store, never any telemetry file (proven with a unique marker scanned across state).
This is the layer that catches real-traffic bugs a green unit suite misses. Runs in CI on every push; exits non-zero on any violation.
$ distil validate distil validate — adversarial real-path invariant harness VALIDATE: PASS — 60/60 checks across 12 adversarial cases (reversibility · reject-if-bigger · recency · fail-open · content-free).
distil holdout
Holdout A/B savings measurement. Partitions the corpus into a control group (held out, not counted toward the headline) and a treatment group, reports savings with a bootstrap 95% confidence interval.
Flags
| Flag | Default | Description |
|---|---|---|
--tokenizer | heuristic | heuristic or anthropic |
--pricing | claude-opus-4-8 | Model pricing to use |
--control-fraction | 0.2 | Fraction of corpus held out as control group |
$ distil holdout
holdout A/B savings measurement (deterministic partition + bootstrap CI)
treatment savings: 27.4% [95% CI: 24.1%–30.8%]
control group mean savings: 26.1% (held out, not counted toward the headline)
distil reset
Archives the savings ledger to savings.jsonl.reset-<utc-timestamp> (non-destructive — history stays on disk for audit) so the statusline and distil stats restart from zero on current accounting (records booked only after a confirmed upstream 2xx). Use it when a ledger is dominated by pre-1.10 records, whose savings may be overstated — distil stats warns when that is the case. --shadow also archives and resets the decision-equivalence counter.
$ distil reset
savings ledger archived → ~/.distil/savings.jsonl.reset-20260705-213313Z
651 runs, 114,079,320 tokens saved (643 pre-1.10 records) — kept for audit, no longer counted
fresh start: all new records use post-1.10 accounting (booked only after upstream 2xx).
distil leaderboard
Shows cumulative savings from all runs recorded via --record flags on savings and bench, plus genuine traffic recorded by distil proxy/wrap. Local-first; no data leaves the machine unless you opt in to community sharing (not currently enabled). Aliased as distil stats — both names run the identical command. If the ledger contains pre-1.10 records (booked before upstream success — savings may be overstated), the output says so and points to distil reset.
Flags
| Flag | Default | Description |
|---|---|---|
--html | off | Render your savings ledger as a self-contained HTML page at this path |
--json | off | Machine-readable output: the full savings summary plus decision-equivalence/shadow-sample counts when shadow data exists |
$ distil leaderboard
distil savings ledger — ~/.local/share/distil/ledger.jsonl
runs recorded: 12
total tokens saved: 42,816
total dollars saved: $0.51234
by trajectory:
corpus-aggregate $0.38112
sre-disk-incident $0.13122
(local-first; community sharing is opt-in and not enabled.)
distil dissect
Everything Distil knows about one wrap session, in a single report — or a picker when no session id is given. Leads with a plain-language "what happened", then drills into savings by model and by mechanism (digest vs cache-delta), the digest inventory (blocks by kind, largest folds, re-fold churn, restore recoverability), billed usage captured from API responses with a heuristic-calibration figure, latency by path (the --expand buffering tax as a measured number), and a "worth your attention" anomaly list that auto-detects silent-failure signatures (proxy bypass, a shadow that is configured but recording nothing, an expand path that never intercepts). Session telemetry is content-free (sizes, hashes, token counts — never prompt or response text) and written fail-open, off the request path. Optional --transcript correlation names tools/files/prompts by joining the session with the agent's own log; everything else stays content-free.
$ distil dissect # pick a recent session $ distil dissect s4821-3 --html report.html $ distil dissect --serve # localhost portal, all sessions
distil proxy
HTTP proxy that applies Distil compression to every LLM API request passing through it. Drop-in for any client that honours a base_url parameter — Anthropic SDK, OpenAI SDK, LiteLLM, LangChain, etc. As real traffic flows through it, the actual per-request token reduction is accumulated into your local ledger (see distil leaderboard) — genuine savings, numbers only, never content.
Flags
| Flag | Default | Description |
|---|---|---|
--host | 127.0.0.1 | Interface to bind on |
--port | 8788 | Port to listen on |
--upstream | https://api.anthropic.com | Real LLM API base URL to forward to |
--lossless-only | off | Policy mode: Tier-0 verbatim only — no lossy output-shaping, no tool injection (--expand), no Tier-1 digest stubs. Folds directly into verbatim (no separate --verbatim needed). Use for subscription / OAuth sessions. |
--verbatim | off | Skip the Tier-1 digest; Tier-0 only (JSON minify + collapse exact-duplicate runs). Interactive-safe, lower savings. Use where distil_expand recovery is unavailable. |
--shape-output | off | Enable generation-side verbosity shaping: light (conservative) or aggressive. Injects a role:"system" directive before the request. Gated by --lossless-only: shaping is skipped when --lossless-only is set. No subscription key detection. |
--expand | off | Inject the distil_expand tool so the model can pull digested blocks back on demand |
--shadow | off (direct use; distil wrap defaults to 0.02) | Shadow-mode sampling rate (0–1): re-run a fraction of requests uncompressed to measure live decision-equivalence. --shadow 0 disables; --shadow 1.0 = full-sampling validation (~3× tokens — prove equivalence in minutes, then drop back). |
--session-delta | off | Enable cross-turn cache-delta encoding (deduplicates repeated stable blocks across the session) |
--async | off | Use the async aiohttp backend (distil/aproxy.py) for high-concurrency streaming. Requires pip install 'distil-llm[async]'. Async event loop handles thousands of concurrent sessions. Note: the async backend supports lossless / verbatim / output-shaping, but not --expand, --session-delta, or --shadow yet — pass those on the standard (sync) proxy, which handles typical agent concurrency. Combining them with --async prints a notice and ignores them. |
--pricing | claude-opus-4-8 | Model used to price the genuine dollar savings recorded to the ledger |
--no-record | off | Do not record genuine savings to the local ledger |
$ distil proxy --port 8788 --upstream https://api.anthropic.com
distil proxy listening on http://127.0.0.1:8788
→ upstream: https://api.anthropic.com
→ recording genuine savings → distil leaderboard
Point your SDK at the proxy:
# Anthropic SDK client = anthropic.Anthropic(base_url="http://localhost:8788") # OpenAI-compatible client client = openai.OpenAI(base_url="http://localhost:8788/v1") # LiteLLM response = litellm.completion(api_base="http://localhost:8788", ...)
The proxy adds up to 9 response headers (not all appear on every request):
x-distil-compressed: 1— compression was appliedx-distil-tokens-saved: <n>— heuristic estimate of tokens savedx-distil-expanded: 1— present when--expandfired and a digest was resolvedx-distil-cache-prefix-msgs: <n>— leading messages left byte-identical vs the previous turn, the prompt-cache-read region (with--session-delta)x-distil-cache-refs: <n>— total cache-delta references (with--session-delta)x-distil-cache-delta: <n>— delta-encoded references (with--session-delta)x-distil-cache-tokens-saved: <n>— tokens saved by cache-delta (with--session-delta)x-distil-output-shaping: light|aggressive— present when output shaping firedx-distil-shadow: sampled— present when this request was sampled for shadow-mode comparison
All other paths and HTTP methods are forwarded unchanged.
See Adapters for the full story on in-process vs. proxy adoption.
distil wrap
The zero-config way to route an existing agent or CLI through Distil. distil wrap starts the proxy on an ephemeral local port, injects the right env var into the child process's environment, runs your command to completion, then tears the proxy down — flushing genuine savings to your ledger and printing the Proof Ledger on exit. No code change, no manual env wiring, no leftover server.
Per-agent presets — when the command name is claude, codex, gemini, or aider, distil auto-selects the env var and upstream endpoint for that agent. A preset: <agent> detected → <VAR> line confirms the selection at startup. Explicit --env-var and --upstream always override the preset. cursor-agent is deliberately omitted — its env var is undocumented upstream; use --env-var to configure it manually.
# claude → ANTHROPIC_BASE_URL auto-set $ distil wrap -- claude -p "summarize this repo" distil wrap → proxy http://127.0.0.1:54xxx (upstream https://api.anthropic.com) preset: Claude Code detected → ANTHROPIC_BASE_URL → ANTHROPIC_BASE_URL=http://127.0.0.1:54xxx # codex → OPENAI_BASE_URL auto-set; gemini → GOOGLE_GEMINI_BASE_URL $ distil wrap --expand -- codex $ distil wrap --expand -- gemini # Lossless-only policy mode (subscription / OAuth) $ distil wrap --lossless-only -- my-agent --task build # Any other agent — specify the env var explicitly $ distil wrap --env-var MY_BASE_URL --upstream https://api.example.com -- my-agent
Everything after -- is the command and its arguments, run verbatim. The child's exit code is propagated. Use --env-var to point a different variable at the proxy (e.g. an OpenAI-compatible tool), and the same --upstream, --lossless-only, --verbatim, --shape-output, --pricing and --no-record flags as distil proxy. On clean exit (and Ctrl-C), the Proof Ledger is printed to stderr.
Flags
| Flag | Default | Description |
|---|---|---|
--host | 127.0.0.1 | Bind address for the ephemeral proxy |
--upstream | https://api.anthropic.com | Real LLM API base URL to forward to |
--env-var | ANTHROPIC_BASE_URL | Environment variable to point at the proxy in the child process |
--lossless-only | off | Policy mode: Tier-0 verbatim only — no lossy output-shaping, no tool injection (--expand), no Tier-1 digest stubs. Folds directly into verbatim (no separate --verbatim needed). Use for subscription / OAuth sessions. |
--verbatim | off | Skip the Tier-1 digest; Tier-0 only. Interactive-safe, lower savings. |
--shape-output | off | Generation-side verbosity shaping (gated by --lossless-only) |
--pricing | claude-opus-4-8 | Model used to price genuine dollar savings recorded to the ledger |
--no-record | off | Do not record genuine savings to the local ledger |
--expand | off | Inject the distil_expand tool so the model can pull digested blocks back on demand |
--shadow | 0.02 (2%, opt-out) | Fraction of requests re-issued uncompressed to measure live decision-equivalence. --shadow 0 disables; --shadow 1.0 = full-sampling validation (~3× tokens — proves equivalence fast, use then drop back). See status line for how to read the de segment. |
--session-delta | off | Enable cross-turn cache-delta encoding |
Proof Ledger (automatic on wrap exit)
The Proof Ledger is printed automatically to stderr at the end of every distil wrap session (clean exit or Ctrl-C). It is silent when no requests were proxied. Opt out with DISTIL_NO_LEDGER=1.
distil proof ledger — session 47m
tokens 84,213 → 62,440 (25.9% smaller)
cost $0.42 → $0.31 calibrated to your billed usage (n=58, ±2.1%)
shadow 0 decision changes (n=12 A/B, 8 A/A — below verdict threshold, gathering)
restore 100% recoverable (3 digests, all handles live, TTL 14d)
All numbers come from the same on-disk sources as the leaderboard — no new estimators. The shadow line respects the verdict-threshold gates (VERDICT_MIN_AB=50, VERDICT_MIN_AA=30): below threshold it shows sample counts with a "gathering" label rather than claiming equivalence over thin evidence. The cost line shows "calibrated to your billed usage (n=N, ±X%)" once enough proxy traffic has accumulated to correct the heuristic; "estimated · calibrating (N/20 samples)" until then. The restore line checks that every handle written this session is still live (not expired past DISTIL_RESTORE_TTL_DAYS).
distil output-savings
Measure the realized output-token reduction from --shape-output. Reads a JSONL file of {"baseline": ..., "shaped": ...} pairs and reports the A/B result with a bootstrap 95% confidence interval.
Flags
| Flag | Default | Description |
|---|---|---|
--input | bundled fixture | Path to a JSONL file of {"baseline": ..., "shaped": ...} pairs |
$ distil output-savings --input shaped_pairs.jsonl output-token A/B — n=8 turns baseline mean output tokens: 487 shaped mean output tokens: 134 reduction: 72.5% [95% CI: 67.5%–77.1%] (illustrative) answer preserved: 100% (semantic match gate, n=8)
distil ingest
Convert a recorded Anthropic or OpenAI request log (JSONL) into a Distil trajectory. One input file produces one trajectory — there is no session grouping. Real-trace corpora have no DECISION labels, so the offline gate is savings-only; certify quality live with distil certify --runner anthropic.
Flags
| Flag | Default | Description |
|---|---|---|
--input | required | Path to the JSONL log file (Anthropic or OpenAI request format) |
--out | ./ingested-corpus | Output directory where the trajectory will be written |
--provider | anthropic | anthropic or openai — selects the input format |
--model | claude-opus-4-8 | Model name to tag the trajectory with (must be in the pricing catalog) |
$ distil ingest --input prod.jsonl --out ./mycorpus ingesting prod.jsonl → ./mycorpus corpus written to ./mycorpus/ $ distil bench --corpus ./mycorpus --savings-only corpus gate — savings-only (no DECISION labels) aggregate savings: 28.3% [95% CI: 25.1%–31.6%] note: certify with --runner anthropic for a live quality gate.
distil perf
Throughput benchmark: measures raw distil-compression rate and in-process adapter latency. Runs a tight loop of compress() calls with the bundled trajectory and reports throughput (ops/sec) and p50/p95/p99 latency.
Flags
| Flag | Default | Description |
|---|---|---|
--iterations | 200 | Number of compression iterations to run |
$ distil perf distil performance benchmark compressor throughput: ~27,000 distil-compressions/sec in-process adapter p50: 0.006 ms in-process adapter p95: 0.011 ms in-process adapter p99: 0.018 ms (measured on Apple M-series, CPython 3.12, bundled sre-disk-incident trajectory)
distil gateway
Managed multi-tenant HTTP gateway with per-tenant token and dollar accounting, a live auto-refreshing dashboard at /distil/dashboard, and a JSON stats API at /distil/stats. Extends the basic proxy with tenant identification and savings attribution. Default port is 8789 (distinct from distil proxy's 8788).
Flags
| Flag | Default | Description |
|---|---|---|
--host | 127.0.0.1 | Interface to bind on. Pass 0.0.0.0 for network-accessible deployments. |
--port | 8789 | Port to listen on. |
--upstream | https://api.anthropic.com | Real LLM API base URL (set once at startup, cannot be overridden per-request). |
--pricing | claude-opus-4-8 | Model pricing key for per-tenant dollar accounting. |
--lossless-only | off | Policy mode: Tier-0 verbatim only — no lossy output-shaping, no tool injection (--expand), no Tier-1 digest stubs. Folds directly into verbatim (no separate --verbatim needed). Use for subscription / OAuth sessions. |
--verbatim | off | Skip the Tier-1 digest; Tier-0 only. Interactive-safe, lower savings. |
--admin-token | unset (env DISTIL_GATEWAY_TOKEN) | Bearer token required for /distil/stats and /distil/dashboard. Mandatory for those routes on non-loopback binds — without it, /distil/* is disabled entirely rather than left open. |
--trust-tenant-header | off | Honor the client-supplied x-distil-tenant header for accounting. Off by default: tenant identity is derived from the credential hash, never a client-writable header, so no caller can book its traffic under another tenant's line. |
$ distil gateway --port 8789 --upstream https://api.anthropic.com
distil gateway listening on http://127.0.0.1:8789
dashboard: http://127.0.0.1:8789/distil/dashboard
→ upstream: https://api.anthropic.com
Tenant identification: by default, a stable anonymized key hash (anon-<sha256(credential)[:8]>) derived from x-api-key or authorization, falling back to "default" when no credential is present. The client-supplied x-distil-tenant header is ignored unless the operator opts in with --trust-tenant-header — otherwise any caller could write that header and book its usage under a different tenant's accounting line. Raw API keys are never stored or logged.
$ curl -s http://127.0.0.1:8789/distil/stats | python3 -m json.tool
{
"tenants": [
{
"tenant": "team-search",
"requests": 142,
"tokens_saved": 52416,
"dollars_saved": 0.026208,
"pct_saved": 28.44
}
],
"totals": {
"requests": 142,
"tokens_saved": 52416,
"dollars_saved": 0.026208,
"pct_saved": 28.44
}
}
See Deploy & Security → Managed gateway for the full deployment guide including multi-tenant setup and security model.
distil gateway keys
Issue, list, and revoke dsk- gateway keys for inbound auth. Keys are stored hashed (SHA-256) — the gateway never keeps the raw secret. Auth fails closed: a bad or missing key returns 401. The upstream (Anthropic / OpenAI key) is never exposed to callers.
Key auth is active when at least one key has been issued, or when the gateway starts with --require-keys. Without keys and without --require-keys, the default anonymous hash-based tenant IDs still work.
# Issue a key for a tenant (prints the dsk- secret once — store it) $ distil gateway keys issue --tenant team-search Issued key for team-search: dsk-a3f9c2e18b4d7a... (shown once — copy it now) # List all issued keys (shows tenant, key prefix, created, last-used) $ distil gateway keys list # Revoke a key by its dsk- prefix $ distil gateway keys revoke dsk-a3f9c2e1 # Start gateway requiring a key on every request $ distil gateway --require-keys --upstream https://api.anthropic.com # Per-tenant rate and quota limits (gateway-wide defaults) $ distil gateway --tenant-rpm 60 --tenant-daily-tokens 1000000 \ --upstream https://api.anthropic.com
gateway keys flags
| Flag | Default | Description |
|---|---|---|
gateway --require-keys | off | Force key auth even before any keys are issued. |
gateway --tenant-rpm | 0 (unlimited) | Gateway-wide requests-per-minute cap per tenant. |
gateway --tenant-daily-tokens | 0 (unlimited) | Gateway-wide daily input-token cap per tenant. |
keys issue --tenant <name> | required | Tenant label to associate with the new key. |
keys revoke <prefix> | required | First hex characters of the key to revoke (as shown by keys list). |
distil train-transformer
Fine-tune a token-classification transformer (default: google/bert_uncased_L-2_H-128_A-2, ~4 MB) for line keep/drop and export it to ONNX. Requires the train extras: pip install 'distil-llm[train]'. The resulting checkpoint is specific to your corpus text distribution — retrain on your own traces for best production results.
LogisticKeepModel (built-in, zero deps, 96.4% acc / 0.98 F1) is the default for all users. Run train-transformer only when you have a corpus of real traces and want to upgrade to the ONNX transformer adapter.
Flags
| Flag | Default | Description |
|---|---|---|
--out | distil-keep-transformer | Output directory. Receives model.onnx, tokenizer files, and metrics.json. |
--base-model | google/bert_uncased_L-2_H-128_A-2 | HuggingFace model ID to fine-tune. Swap for distilbert-base-uncased for higher accuracy at larger size. |
--epochs | 3 | Number of training epochs. |
$ pip install 'distil-llm[train]' $ distil train-transformer --out ./my-keep-model Epoch 1/3 train_loss=0.4821 Epoch 2/3 train_loss=0.2134 Epoch 3/3 train_loss=0.1589 Exported ONNX to ./my-keep-model/model.onnx Accuracy=0.9630 F1=0.9812 Load with: TransformerKeepModel.from_pretrained('./my-keep-model/model.onnx', './my-keep-model')
Output layout in --out directory:
model.onnx— exported ONNX graph (inputs:input_ids,attention_mask)tokenizer.json,tokenizer_config.json,vocab.txt— tokenizer forTransformerKeepModel.from_pretrainedmetrics.json— held-out accuracy / F1 from the training run
A demo checkpoint trained on the bundled corpus (96.3% acc / 0.98 F1) is attached to the v0.1.0 release. See Techniques → Keep-model codec for the full design.
distil eval
The certified compression frontier — the proof artifact no competitor publishes. Sweeps compression aggressiveness levels, measures (token savings, decision-equivalence) per level over the corpus, and marks which are certified by the non-inferiority gate. Locates the cliff past which lossy compression starts dropping agent decisions, with distil sitting safely inside the certified region.
Flags
| Flag | Default | Description |
|---|---|---|
--corpus | bundled corpus | Custom corpus dir (e.g. from distil ingest on benchmark traces) |
--runner | deterministic | deterministic (offline, structural equivalence) or anthropic (live model, requires API key) |
--out | off | Write the raw curve as a timestamped JSONL file to this directory |
$ distil eval certified compression frontier (runner=deterministic) level savings equiv certified curve -------------------------------------------------------------------------- distil (cache-aware) 8.4% 100% ✔ PASS ██ truncate@1200 7.2% 79% ✘ — ██ truncate@700 20.0% 36% ✘ — ████ truncate@300 41.3% 0% ✘ — █████████ -------------------------------------------------------------------------- distil: 8.4% savings @ 100% decision-equivalence — certified. certified ceiling: 8.4% savings (beyond this, lossy compression drops decisions and the gate rejects it).
See Research & Frontier → Certified compression frontier for the full design.
distil benchmark
Head-to-head against the competing technique families — sliding-window truncation, extractive importance pruning, abstractive summarization, naive minification — each a faithful reference implementation, all scored through the same decision-equivalence + non-inferiority gate and the same cache-aware cost model. Ranks by certified dollar savings; lossy methods that drop decisions are disqualified however much they saved. Register a real external tool to verify. See the Benchmark page for the standings and methodology.
Flags
| Flag | Default | Description |
|---|---|---|
--external | — | module:function[:Name] — register a real compressor (list[str]→list[str] over per-turn block texts), measured on the identical axes. Repeatable. |
--corpus | bundled corpus | Custom corpus dir (e.g. ingested benchmark traces) |
--runner | deterministic | deterministic (offline) or anthropic (live model) |
--pricing | claude-opus-4-8 | Model pricing for the cache-aware cost model |
--tokenizer | heuristic | heuristic or anthropic (billing-grade) |
--margin | 0.02 | TOST non-inferiority margin |
--alpha | 0.05 | Significance level for the non-inferiority test |
--html / --out | off | Render the standings as HTML / write raw results JSONL |
$ distil benchmark compression benchmark (model=claude-opus-4-8, runner=deterministic, corpus gate) technique tok save $ save equiv certified reversible --------------------------------------------------------------------------------- distil-causal 33.8% 37.4% 100% ✔ PASS lossy truncate-tail 26.9% 33.0% 25% ✘ fails lossy summarize 19.7% 24.2% 0% ✘ fails lossy distil-lossless 8.4% 10.3% 100% ✔ PASS byte-exact extractive-prune 7.4% 9.0% 68% ✘ fails lossy --------------------------------------------------------------------------------- LEADER (certified $ savings): distil-causal — 37.4% cheaper, 100% decision-equivalent.
distil frontier
The savings-vs-equivalence dial. 100% decision-equivalence is the certified-safe default; relax the target and Distil spends a bounded divergence budget (floor((1−target) × turns) turns) on the highest-value turns, falling back to byte-exact everywhere else. The trade is explicit and reported — never hidden. Trace the full curve with distil frontier.
Flags
| Flag | Default | Description |
|---|---|---|
--targets | 1.0,0.97,0.95,0.90,0.80 | Comma-separated equivalence targets in (0,1] |
--corpus | bundled corpus | Custom corpus dir |
--runner | deterministic | deterministic or anthropic (live model) |
--samples | 3 | Majority-vote samples for the anthropic runner |
$ distil frontier --corpus benchmarks/corpus_xl savings-vs-equivalence dial (runner=deterministic) target achieved equiv token savings curve ---------------------------------------------------------------------- 100% 100% 58.1% ████████████████ 95% 100% 58.1% ████████████████ 80% 82% 62.9% ██████████████████ ----------------------------------------------------------------------
distil conformal
The Decision-Equivalence Risk Certificate (DERC). You choose a risk budget — a maximum decision-change rate α — and Distil certifies the most aggressive compression level whose risk is provably below it, distribution-free and finite-sample, via conformal risk control (Learn-Then-Test / Conformal Risk Control). Unlike the dial, this is a statistical guarantee with a confidence level, calibrated on your own traffic. See Research & Frontier → DERC.
Flags
| Flag | Default | Description |
|---|---|---|
--alpha | 0.05 | Risk target: max tolerable decision-change rate |
--delta | 0.05 | LTT failure probability; confidence is 1−δ (ignored by CRC) |
--method | ltt | ltt (high-probability bound) or crc (expected-rate bound) |
--corpus | bundled corpus | Calibration corpus dir (use your own traffic via distil ingest) |
--runner | deterministic | deterministic or anthropic (live model) |
--samples | 3 | Majority-vote samples for the anthropic runner |
$ distil conformal --corpus benchmarks/corpus_xl --alpha 0.05 Decision-Equivalence Risk Certificate (DERC) method : LTT (Learn-Then-Test) risk target : α = 0.05 (max allowed decision-change rate) confidence : 95% (1 − δ) calibration : n = 320 turns, runner = deterministic ---------------------------------------------------------------- ✔ CERTIFIED 'lossless' → 57.4% token savings
distil online
One self-distilling round: collects causal labels from your corpus (certified-safe drops discovered by the ablation engine), retrains the logistic keep-model, and promotes the new weights only if they pass the non-inferiority gate on every trajectory. Never-regressing by construction — a cycle that would degrade quality is silently discarded.
Flags
| Flag | Default | Description |
|---|---|---|
--corpus | bundled corpus | Corpus dir of traffic to learn from. Use distil ingest to convert real logs first. |
--promote-to | off (dry run) | If certified, write the new weights as a LogisticKeepModel-compatible JSON file to this path. |
$ distil online --corpus ./mycorpus --promote-to ./weights/keep_model.json
self-distilling round — keep-model learns from causal labels, gated by non-inferiority
n_labels: 286
accuracy: 0.873
f1: 0.932
certified: True
promoted: True
See Research & Frontier → Self-distilling keep-model for the full loop design.
distil query-relevance
Query-aware salience, phase 2. Trains and certifies the learned semantic-relevance model from distil’s own expand flywheel — the content-free record of which digested block the agent pulled back, paired with the query live at digest time. It pins lines that answer the query without sharing a word with it (“what’s the retry limit?” → max_attempts = 5), on top of the phase-1 lexical keeps.
The model is additive-only (it can only widen the keep set, so reversibility and the decision-equivalence certificate are untouched) and gated: weights are promoted only if held-out recall beats the phase-1 lexical baseline at a precision floor. With no promoted weights, behavior is exactly phase 1. Collection runs automatically under distil wrap --expand; it stores only numeric feature vectors and expand outcomes — never a prompt, a line, or a query term.
Flags
| Flag | Default | Description |
|---|---|---|
--min-samples | 200 | Minimum accumulated flywheel labels before a candidate is eligible for promotion. |
$ distil query-relevance
query-aware salience (phase 2) — learn semantic line↔query relevance from the expand
flywheel; promote only if it beats the phase-1 lexical baseline (additive-only, safe)
promoted: true
recall: 0.910 baseline_recall: 0.520 precision: 0.880
distil federated-leaderboard
Build a verifiable federated savings leaderboard from HMAC-signed, content-free savings aggregates. Verifies each submission's signature and counts only certified savings toward headline totals — savings you can verify. No prompt or response text ever leaves the contributing instance.
Flags
| Flag | Default | Description |
|---|---|---|
--dir | required | Directory containing submissions.jsonl (one signed aggregate per line) |
--keys | off | Path to a JSON file mapping instance_id → HMAC key. Submissions with no matching key are rejected. |
--html | off | Write a self-contained leaderboard HTML page to this path |
$ distil federated-leaderboard \
--dir ./submissions \
--keys ./instance-keys.json \
--html ./leaderboard.html
verifiable savings — 3 verified instance(s), 0 rejected
totals (certified only): {'tokens_saved': 142816, 'dollars_saved': 0.071408, 'runs': 47, 'instances': 3}
leaderboard html → ./leaderboard.html
See Research & Frontier → Verifiable federated telemetry for the signing model and ed25519 upgrade path.
distil certify-trajectories
The trajectory-level risk certificate — bounds end-to-end task degradation, not per-step decision-equivalence. Reads a JSONL of matched full/compressed task runs and certifies P(degradation risk ≤ alpha) ≥ 1-delta using Conformal Risk Control (Hoeffding–Bentkus), the invariant that transfers to real task success where per-step certificates (DERC) can't. See Concepts → The trajectory-level certificate for the full framing and honest assumptions.
Flags
| Flag | Default | Description |
|---|---|---|
outcomes | required | Path to a JSONL file of matched runs, one object per line: {"task_id": ..., "full_success": bool, "compressed_success": bool} |
--alpha | 0.05 | Maximum end-to-end degradation risk to certify |
--delta | 0.05 | Confidence budget — certificate holds at confidence 1−δ |
--json | off | Machine-readable output: the full certificate fields plus statement |
$ distil certify-trajectories outcomes.jsonl --alpha 0.05 --delta 0.05 distil trajectory-risk certificate matched trajectories : 200 observed degradation : 0.50% risk bound (1-δ) : 3.20% certified (α=0.05, δ=0.05) : True With confidence 95%, compression degrades at most 5.0% of tasks the full context would have solved (observed 0.50% over 200 matched trajectories). Valid under: future trajectories exchangeable with calibration; loss = measured end-to-end task outcome; calibration held out from compressor tuning; recalibrate on drift
Refuses to certify (exit 1, not a false PASS) below min_n=20 matched trajectories — a certificate from a handful of samples is noise dressed up as a guarantee. Exits 0 only when certified is true.
distil onboard
One command to set up distil and learn to use it. Detects your environment — OS, package managers, agent CLIs (Claude Code / Codex / Gemini), how distil is installed, the optional anthropic extra, and whether you're on a flat-rate Claude subscription — then wires the savings status line and prints a next-steps guide tailored to what it found: how to route the detected agent (subscription-safe vs metered), validate outcomes with shadow mode, watch savings on the dashboard, run the test gate, and re-verify with distil doctor. --dry-run changes nothing; works on macOS and Windows.
distil default
Make distil the default for your agent so you don't type distil wrap every session. Writes a single managed, marked, backed-up, idempotent block to the shell rc distil detects for this machine — zsh (.zshrc), bash (.bashrc/.bash_profile), fish (config.fish), or PowerShell ($PROFILE) — using the correct syntax for each (alias / function / export / set -gx). An explicit $SHELL wins over file-existence guesses, and the command prints what it detected (shell, rc, agent, mode) rather than acting blind. --always-on instead installs a persistent proxy service (launchd / systemd) plus ANTHROPIC_BASE_URL so every base-URL-honoring SDK routes through distil — universal, but a daemon you keep alive and a single point of failure if it's down. --undo removes whichever is installed; --agent, --mode, --rc override the detected defaults. distil onboard offers it interactively.
distil offboard
Remove distil's footprint — the inverse of distil onboard. Undoes the shell default (the distil default alias/env block), stops and removes the always-on proxy service, and unwires the status line from Claude Code settings — asking before each (non-interactive without --yes removes nothing). Your savings ledger and shadow data are kept unless you pass --purge. It can't uninstall the running package itself, so it prints the exact uninstall command for how distil was installed (pipx / uv / pip). A foreign status line (one you set yourself) is left untouched.
distil dashboard
Live terminal dashboard of your cumulative savings — a framed panel with Unicode bars for token-trim and decision-equivalence, original → compressed tokens and cost, a recent-decisions strip, and per-trajectory bars. Uses the alternate screen (like htop): redraws in place and restores your prompt on exit. --once prints a single frame; --interval sets refresh seconds. On a flat-rate subscription the dollar figures are dropped automatically (DISTIL_SUBSCRIPTION overrides).
distil statusline
One compact line, designed to be a segment inside your own composite status line — every character earns its place:
# a proxy session is active (or was, within the last 4h) distil · ▼75.0K −62% $0.31 · Σ27.0M # no recent session — falls back to the lifetime figure distil · Σ27.0M saved −50% $96.10
The status line opens with a mode chip showing which compression mode is active: ⬢ digest (neon cyan), ◇ lossless (green), or ▪ verbatim (dim). Example: distil · ⬢ digest · ▼5.7M · 39% smaller · total ▼460.9M · de 47/50.
▼ is tokens saved and −N% the trim rate of the active scope — the current proxy session while one is live (last activity <4h ago), lifetime otherwise; Σ is the lifetime tokens-saved figure, shown as a compact suffix next to a live session or as the headline when idle. The dollar figure only appears on metered/PAYG billing (dropped entirely on a flat-rate subscription) and only when it's non-zero. Dropped by design: the orig→compressed pair (derivable from the trim %) and run counts. The de (decision-equivalence) segment appears as de n/50 while collecting shadow samples, then as de N.N% with a ✓/⚠/✗ verdict once 50 A/B samples + 30 A/A samples accrue (A/B = compressed-vs-original; A/A = same request replayed against itself — the sampling-noise baseline). Its color is alarm-only, not decoration — calm brand hue while ≥99%, warning under 99%, alarm under 95%. Full breakdown: distil stats / distil dashboard. Wire it with distil setup or the plugin.
distil shadow-stats
Report the live decision-equivalence measured by shadow mode on real traffic: the percentage of sampled requests where the agent's chosen next action was identical with vs without compression, and how many samples that's based on. Start sampling with distil wrap --shadow 0.1 -- claude.
Flags
| Flag | Default | Description |
|---|---|---|
--json | off | Machine-readable output: samples, changes, decision_change_rate, decision_equivalence |
distil doctor
Diagnose a distil setup end-to-end: distil/Python version, the savings ledger (subscription-aware), shadow-validation status, an in-process proxy round-trip self-test (proves the proxy machinery works with no network), the optional anthropic extra and API key, and — for Claude Code — status-line wiring and subscription detection. Exits non-zero if any check fails, so it doubles as a setup gate.
Flags
| Flag | Default | Description |
|---|---|---|
--no-color | off | Disable ANSI colors in the check list |
--json | off | Machine-readable output (CI-gateable): {"checks": [...], "ok": bool}, one object per check with name/status/detail/hint |
distil setup
Wire the distil savings status line into Claude Code's settings.json in one step. Idempotent (no-op if already wired), never clobbers an existing status line without --force (which backs it up first), and preserves all other settings.
Full CI gate
$ make gate # Runs: tests + corpus non-inferiority (distil bench) + byte-fidelity (distil verify) pytest ... 181 passed in 4.2s corpus gate: PASS fidelity gate: PASS