compression with a quality contract

Getting Started

Install in one command, route your coding agent (Claude Code, Codex, Gemini CLI) through Distil with distil wrap — no code change — and watch genuine savings on your real traffic. 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? One command:
pipx install distil-llm   # then: distil bench
No pipx? Zero-install with uv: uvx --from distil-llm distil bench. 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.11+ 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.
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.11+, 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.11+ (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.11+; 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