compression with a quality contract

Getting Started

Install, then run distil onboard — one command that detects your agent (Claude Code, Codex, Gemini CLI) and billing, wires the savings status line, and hands you the exact next steps. No code change. Prefer to kick the tyres first? distil bench certifies the numbers offline with no API key.


Install

Distil ships as a PyPI package (distil-llm) with a stdlib-only core — zero runtime dependencies. The corpus is bundled in every distribution.

Just want it now? distil onboard ensures you have everything — if you run it ephemerally it installs distil permanently, then wires the status line, routes your agent, and offers to make distil the default. One command (needs uv):
uvx --from distil-llm distil onboard   # sets up everything — incl. a permanent install
Prefer pipx? pipx install distil-llm && distil onboard. Just want to see it prove itself first? distil bench runs the certified gate in ~10s, no API key. Everything else below is an alternative, not a requirement.
The one gotcha — the name. The package is distil-llm but the command is distil (the bare distil name was taken on PyPI). So it's pipx install distil-llm → run distil …. pipx install distil installs the wrong thing.
Python 3.9+ required. Install Distil isolated from your system Python — modern macOS and Linux reject system-wide pip install with error: externally-managed-environment (PEP 668). Every method below is isolated and safe.
Hit Could not find a version that satisfies the requirement distil-llm (from versions: none)? The package is on PyPI — that error means your pip/pipx is on a Python below the package floor. Distil supports Python 3.9+ (what macOS ships), so a current install just works; on an even older Python, let uv provision one: uvx --python 3.12 --from distil-llm distil bench (or uv tool install --python 3.12 distil-llm). Check yours with python3 --version.
Zero install · recommended

uvx

Run straight from PyPI, no install step. Prereq: uv.

uvx --from distil-llm distil bench
Isolated CLI · recommended

pipx

Permanent install in its own venv — PEP 668-safe. Prereq: Python 3.9+, pipx (brew install pipx).

pipx install distil-llm
distil certify
macOS / Linux

Homebrew

Installs into an isolated keg — doesn't touch your Python. Prereq: Homebrew and git (third-party taps are cloned with git — run brew install git if you see Error: Git is unavailable).

brew install dshakes/tap/distil
distil bench
Container

Docker

Reproducible image, ideal for CI or sandboxed environments. Prereq: Docker.

docker build -t distil .
docker run distil bench
Single file

Zipapp (.pyz)

One portable executable — copy it anywhere. Prereq: Python 3.9+ (no install).

make pyz
python dist/distil.pyz bench
In a venv / project

pip

For embedding the library in your own project. Prereq: an active virtualenv.

python3 -m venv .venv && source .venv/bin/activate
pip install distil-llm
Node / TypeScript / other languages? Run distil proxy (or distil wrap -- <your-agent>) and point your SDK's baseURL at it. No code changes, any language. The CLI needs Python 3.9+; your app does not.

Use it on your agent — the 30-second path

Easiest: let distil onboard set you up and hand you the exact commands for your environment — it detects your agent and billing, wires the status line, and prints a guided tour:

distil onboard   # one command: detects your setup, wires the status line, guides you

Or do it by hand. distil wrap launches your coding agent with its API traffic routed through compression — no config, no code change.

# Claude Code on a metered API key — saves real $$:
distil wrap --expand -- claude
# Claude Code on a Pro/Max subscription — flat-rate, ToS-safe (trims context, not $):
distil wrap --lossless-only -- claude
# Codex, Gemini CLI, or any agent — same pattern:
distil wrap --expand -- codex

Then watch genuine, measured savings from your traffic (not estimates):

distil leaderboard          # cumulative tokens + $ saved, from the local ledger
distil dashboard            # live terminal TUI — token-trim + decision-equiv bars, Ctrl-C to exit
distil doctor               # diagnose your setup: ledger, shadow, proxy self-test, wiring

Wire the status line in one step with distil setup. The status line and distil dashboard auto-detect a flat-rate Claude subscription — the per-token dollar figure is notional, so it's dropped and the token reduction leads (override with DISTIL_SUBSCRIPTION=0/1). Inside Claude Code, /distil-stats, /distil-shadow, /distil-dashboard, and /distil-doctor surface this on demand.

Tired of typing distil wrap each session? Make it the default — once:

distil default              # managed shell alias so `claude` always routes through distil
distil default --always-on  # or a persistent proxy service covering every SDK
distil offboard             # later: remove distil's footprint (alias, service, status line) before uninstalling

distil default detects your shell (zsh / bash / fish / PowerShell) and billing mode, writes the right line to the rc file your shell actually reads, and tells you what it detected; distil default --undo reverses it. distil offboard is the clean inverse of onboarding — it undoes each piece (asking first), keeps your savings ledger unless --purge, and prints the right uninstall command. See the CLI reference for both.

Will it save me money? Only on metered / pay-as-you-go billing (an API key): fewer tokens → fewer dollars. On a subscription you're flat-rate, so there's no per-token bill to cut — Distil still trims context and latency. Honest about coding agents: on short sessions the win is modest (~7% — the agent re-expands most of what it edits); the real savings land on long, many-turn sessions with large context the model never re-reads. Prefer an in-process hook or an org-wide proxy? See Integrations.

Billing-grade tokenizer (optional)

The default tokenizer is an offline heuristic — compression ratios are robust, but absolute dollar figures are approximate. For billing-grade token counts and live-model certification, install the live extra:

# with pipx (isolated):
pipx install 'distil-llm[live]'
# or one-off with uvx:  uvx --from 'distil-llm[live]' distil certify --runner anthropic
export ANTHROPIC_API_KEY=sk-ant-…
distil savings --tokenizer anthropic
distil certify --runner anthropic

Validate it preserved your outcomes

Compression is only safe if your agent makes the same decision it would have on full context. --shadow proves that on your live traffic: it samples a fraction of requests and runs each one twice — once compressed, once with the full prompt — then compares the agent's chosen next action (the tool call it decides to make), not the prose. The two are decision-equivalent when those signatures match.

distil wrap --shadow 0.1 -- claude   # one command: wraps your agent + shadows 10%
distil shadow-stats                  # live decision-equivalence rate (or /distil-shadow)

The rate also shows in the status line once shadow has samples — read it as “of the requests I checked, N% produced the identical next action with vs without compression.” Start at 0.1; if equivalence holds high, dial compression up — if it dips, back off. Distil fails safe to full context.

Honest scope: this certifies next-action equivalence — a proxy, not end-to-end task success. On real SWE-bench Verified (E7) the proxy does not fully transfer once compression gets aggressive — which is exactly why you monitor the live rate and keep the gate conservative.


60-second quickstart

Three commands cover the core workflow. All run against the bundled multi-domain corpus — no API key required. Run them in order the first time: bench to confirm savings and the gate pass, certify to inspect a single strategy, prune to see what's actually free to drop.

1. Benchmark savings across 7 domains

distil bench is the corpus-wide CI gate. It prices four strategies, certifies Distil is non-inferior on every trajectory, and confirms the gate rejects the aggressive lossy strategy. Run this in CI to catch any regression.

$ 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) reversibly; 5761 tokens causally prunable.

GATE: PASS — every trajectory certified non-inferior; aggressive rejected on all.

2. Certify a strategy

Run the TOST non-inferiority gate on a single trajectory and strategy. Exits 0 on pass, 1 on fail — plug it directly into any CI pipeline.

$ 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)

3. Discover causally inert blocks

distil prune ablates each context block, replays the trajectory, and identifies what never changed a decision. Those blocks are provably free to drop — not heuristically likely, provably safe.

$ 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).

Drop-in adoption: wrap(client)

The fastest path to production compression — no changes to your call sites. Wrap your existing Anthropic client once and every subsequent messages.create call is transparently compressed and cache-pinned.

import anthropic
from distil.adapters.anthropic import wrap

# One-line drop-in — no call-site changes needed
client = wrap(anthropic.Anthropic())

# Works exactly like the original client
response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    system="You are a helpful assistant.",
    messages=[{"role": "user", "content": "Analyse this log output…"}],
)

The wrapper applies Tier-0 lossless transforms (JSON minification, run-length collapse) and Tier-1 reversible digests to large tool results. It also pins the stable system prefix for cache-control so repeated calls get the ~10× cache-read discount automatically.

No call-site changes. The wrapper is a pure structural proxy — it duck-types against any object that exposes a messages.create(**kwargs) method. It imports nothing from the Anthropic SDK itself, so it also works in test environments where the SDK is not installed.

Alternative: provider proxy

Prefer a network-level approach that works with any SDK, framework, or language? See the Adapters page for distil proxy.


Use it on your workflow — recipes

Pick the row that matches you. Every command is real; watch it work with distil leaderboard (genuine cumulative savings) and the x-distil-* response headers.

Coding agents — Claude Code · Codex · Gemini CLI

Wrap the agent; its traffic routes through compression with zero code change.

# subscription/OAuth-safe (ToS-safe, no tool injection)
distil wrap --lossless-only -- claude
# interactive: model sees content un-digested (Tier-0 only)
distil wrap --lossless-only --verbatim -- claude
# PAYG: aggressive reversible digest; the model recovers detail on demand
distil wrap --expand -- claude
# coding sessions with re-reads: send the diff, not the whole file
distil wrap --session-delta -- claude

Any SDK app — chatbots, RAG, agents, batch (non-coding)

# standalone proxy — point any base_url SDK at it, any language
distil proxy
distil proxy --upstream https://generativelanguage.googleapis.com   # Google Gemini
# or in-process, no sidecar (LiteLLM / LangChain)
from distil.integrations import litellm as distil_litellm
distil_litellm.completion(model="claude-opus-4-8", messages=[...])

See it, prove it, get more out of it

GoalCommand
Set up + a guided tour (start here)distil onboard
Make distil the default (no per-session wrap)distil default · undo distil default --undo
Remove distil's footprint (before uninstalling)distil offboard · clear data distil offboard --purge
Diagnose your setup (ledger, shadow, proxy self-test, wiring)distil doctor
Wire the savings status line into Claude Codedistil setup
Watch genuine savings accumulatedistil leaderboard · live TUI distil dashboard
Live decision-equivalence on real trafficdistil wrap --shadow 0.1 -- claudedistil shadow-stats
Certify on your domaindistil ingest --input prod.jsonl --out ./mycorpusdistil conformal --corpus ./mycorpus
Let agents recover digested detail (MCP)distil mcp — exposes distil_compress/_expand/_savings
Live savings in your Claude Code status lineinstall the plugins/distil plugin → distil statusline
Self-improving keep policydistil learn / distil online (flywheel; only ever gets more conservative)
Rule of thumb. Subscription / interactive → --lossless-only (add --verbatim). PAYG / autonomous → default digest (add --expand). Coding sessions with re-reads → add --session-delta.

What to read next