compression with a quality contract

Library API

Embed distil in your own agent — Python or TypeScript, in-process, no proxy and no daemon.

The proxy is still the zero-config path, and it is the one that reaches the reversible digest tier. Reach for the library when you are building the agent: you already hold the message list, and you want compression without a process to keep alive — a serverless function, an edge worker, a CI job.

Python

from distil import compress_messages, expand_handle

result = compress_messages(messages)          # OpenAI/Anthropic-style dicts
print(f"{result.saved_pct:.1f}% smaller")
response = client.messages.create(model=..., messages=result.messages)

original = expand_handle(result.handles[0])   # byte-exact, any time, any process

Tool results get the reversible digest; user and system text get lossless transforms only; the model's own turns are never rewritten. The input list is never mutated, and unchanged messages come back by identity. Pass verbatim=True for lossless-only with no handles.

Named compress_messages/expand_handle rather than compress/expand because distil.compress and distil.expand are modules. Python binds a submodule onto its parent on import, so a top-level export with those names would resolve to the function in a fresh interpreter and to the module in any program that had touched the submodule. A test enumerates submodules and fails on any future collision.

TypeScript

import { compress } from "distil-llm";

const r = compress(messages);                 // never mutates the input
console.log(`${r.savedPct.toFixed(1)}% smaller`);

And as Vercel AI SDK middleware:

import { wrapLanguageModel } from "ai";
import { distilMiddleware } from "distil-llm";

const model = wrapLanguageModel({ model: myModel, middleware: distilMiddleware() });

The middleware implements transformParams only. Compression happens on the way in; wrapping the response would mean rewriting the model's own output. Tool results are handled in both the v5 (output.value) and v4 (result) shapes, so an SDK bump cannot silently stop compressing the largest thing in an agent's context.

The tier boundary — and why it is where it is

PathTierCertificate
distil wrapdigest (reversible)✅ covered
proxy / gatewaydigest (reversible)✅ covered
MCP serverdigest (reversible)✅ covered
in-process librarylossless onlyn/a — nothing is elided

The in-process libraries are lossless-tier on purpose. The digest mints restore handles whose originals must live in one store shared with the proxy and the MCP server — otherwise expand fails to resolve a handle the model can see. It is also the tier the decision-equivalence certificate measures, so a second implementation of it would be a second thing to certify, and nobody would notice when the two drifted.

The TypeScript port is held byte-identical

A JS implementation that merely compressed similarly would hand you a guarantee that does not describe the code you are running. So the port is checked against the Python engine over a shared corpus, and CI fails on any divergence.

Where byte-identity is not achievable the port declines rather than emitting output the certificate does not cover — Python renders an integral float as 1.0 and JS renders it 1; JS objects hoist integer-like keys. Both are detected on the source and the text is left byte-exact. Tests assert the declines and assert that safe cases still compress, so conformance cannot be achieved by declining everything.

Framework hooks

FrameworkEntry point
LangChaindistil.integrations.langchain.compress_messages
LangGraphdistil.integrations.langgraph.pre_model_hook()
LiteLLMdistil.integrations.litellm.compress(kwargs)
Agnodistil.integrations.agno.compressed_model(model)
Strandsdistil.integrations.strands.compressing_hook()
Vercel AI SDKdistilMiddleware() (npm)

Every one is duck-typed and never imports its framework. That is what keeps distil a zero-dependency install, and it means a framework release cannot break the integration.

Runnable examples

Each of these runs as-is: python_library.py · js_library.ts · js_ai_sdk_middleware.ts · python_agno.py · python_strands.py