Distillr v0.1.0

Design notes

Why the ledger is in Phase 0

The spec's key principle: observability must never feel bolted on. So every Pipeline.run produces a CompressionResult with per-stage StageReports and a removal manifest, and Ledger.record writes the same shape the hosted dashboard will read later. SQLite locally, Postgres hosted, one schema.

Measuring honestly

Stage contract

class Stage(Protocol):
    name: str

    def run(self, data, ctx: Context) -> tuple[data, StageReport]: ...

Stages are dataclasses with explicit knobs. Each knob maps to a Removal.reason so the manifest explains itself ("low relevance (score 1.81)", "in drop_fields", "string over 400 chars", "beyond top_k=12").

Retrieval ranking

BM25 over a simple word tokenizer with a stop list. It is not embeddings, on purpose: zero dependencies, deterministic, fast, and good enough to pass the Phase 0 gate on structured data where the query names entities (ids, names, cities, error strings). RetrieveStage(scorer=...) accepts any (query, texts) -> scores callable for embedding-based ranking later.

Chat history gets special handling: system messages and the last N turns are pinned; the remaining budget is filled with the most relevant older turns. This keeps the "needle in the middle" (an order id from turn 9) when the final question refers back to it.

TOON

distillr/core/toon.py implements the public TOON shape: key: value, key[N]: a,b,c for primitive arrays, key[N]{f1,f2}: plus one row per line for uniform arrays of flat objects, and a - list for mixed arrays. Strings are quoted only when ambiguous. A decoder round-trips everything, and the test suite proves it on nested, mixed, unicode and delimiter-containing data. If the upstream spec moves, this file is the only place that changes.

Audit survivability

Every removal keeps a preview. check_answer(answer, manifest, kept_text) extracts distinctive tokens (identifiers, numbers, uncommon words) from each preview and flags any that appear in the answer but not in the text the model actually received. Risk is high for paths that look like ids, amounts, dates or contact fields, medium for whole items and truncations, low for empty-field and duplicate removals.

This is a heuristic, not a proof. It catches the common failure (the model "remembers" a row we cut, or hallucinates one that looks like it) and gives the dashboard a trend line. Phase 1 can add an LLM-judge mode that asks whether the answer needed any removed item.

What Phase 0 deliberately skips