Project Info
This project did not submit a demo video on Devpost.
Inspiration
Most compression demos brag about cutting tokens and say nothing about whether the answer survived. Deleting tokens is easy; deleting the right ones is the problem. Right now, frontier LLM models seem to be the best at compressing inputs over classical models like TF-IDF or even lower scale transformer models. Since directly asking frontier LLM like Claude directly is too expensive, we fine tuned a smaller bidirectional encoder based model. Since you can't fine-tune Claude directly, we wanted to see if a small model could learn Claude's sense of what matters and run that compression locally, on every request, for free.
What it does
Given a context and a question, a 66M-parameter model keeps only the sentences needed to answer, down to whatever token budget you set. It's wrapped in an eval that closes the loop (compress, ask Claude, grade the answer) and reports tokens, accuracy, and dollars against a classical baseline, so you see the trade-off instead of trusting it.
How we built it
We framed compression as per-token keep/drop classification instead of generative rewriting. Generation can mangle the exact tokens that matter and costs a decode per request; classification is one forward pass, can't hallucinate, and gives exact label alignment for distillation (no fuzzy-matching a teacher paraphrase back onto the source, which is the noisy step in LLMLingua-2's pipeline). The backbone is a bidirectional encoder (DistilBERT), not a causal SLM like Qwen, because keeping a token depends on context on both sides of it, and a decoder only sees the left. The trainer is model-agnostic (AutoModelForTokenClassification), so the backbone is a one-line swap. For labels, we show Claude the context as a numbered sentence list and ask which indices answer the query. That maps exactly to per-token labels and matches our inference granularity, so train and test agree. Input is Query: … Context: … with query tokens masked out, so relevance is learned conditioned on the question. KEEP is only ~2% of tokens, so we don't threshold at 0.5; we use the model as a ranker (sort by keep-probability) and a separate budget controller fills to the target ratio. That same controller is shared by BM25 and the model, so the comparison changes one variable: the scorer.
Challenges we ran into
BM25 is far stronger than people give it credit for, and understanding why was most of the work. The rare entity name is an exact-match jackpot for a lexical scorer, so on plain retrieval it ties the model. To test semantic compression we built an adversarial slice: synonym queries the answer avoids, plus filler that echoes the query's keyword but carries no answer. Even then it didn't separate until we realized BM25 keeps the whole entity block as a unit, so the answer only gets dropped when the budget is smaller than that block. We tuned the benchmark into that regime. Separately, the class imbalance almost produced an all-DROP model, and our lost-in-the-middle chart came back flat (Haiku reads short contexts fine), so we cut it rather than ship a result that didn't hold.
Accomplishments we're proud of
A model distilled from ~200 Claude-labeled examples that trains in about 10 seconds on a MacBook and produces a real, measured win: at 20% keep (~33% of the tokens) it holds 70% accuracy while BM25 falls to 18%, with full context at 80% and the same budget for both. We're nearly as proud of the restraint: every number comes from Anthropic's own token counter and real API usage, we report where the model only ties classical, and we deleted the chart that lied.
What we learned
Learned compression beats classical only in a specific regime: when relevance is semantic rather than lexical, and when the budget is tight enough to force a real choice. Being able to construct that case on purpose was as much of the work as the model. We also learned how cheap distilling a judgment can be, and that in compression the model is the easy half; proving the answer survived is the hard half, so build the eval first.
What's next
A drop-in proxy: point your Anthropic base_url at TokenC and context gets compressed before it reaches the model, around half off your token bill with one line of config. After that, a stronger backbone and token-level keep/drop for finer control, real long-context benchmarks like HotpotQA and LongBench, and training on diverse multilingual text so it generalizes well past the demo.
TokenC — distill Claude's context compression into a tiny model
The Token Company Compression Challenge. Cut the tokens you send an LLM while preserving — and with the hybrid, improving — answer quality, by distilling Claude's relevance judgment into a small, local keep/drop token classifier (the LLMLingua-2 recipe, with Claude as the teacher).

Headline result (from demo.ipynb, measured)
Semantic multi-doc QA with lexical-trap distractors, downstream reader = Claude Haiku 4.5:
| approach | accuracy | tokens |
|---|---|---|
| full context | 80% | 431 |
| BM25 (classical) @ 20% keep | 18% | 129 |
| trained model (extractive) @ 20% keep | 70% | 141 |
| hybrid (model + rephrase) | 92% | 77 |
Two results:
- The extractive model stays robust under aggressive compression — 70% accuracy vs BM25's 18% at the same token budget. The rare-token lexical traps fool BM25 into dropping the answer; the model distilled from Claude keeps it.
- The hybrid hits both halves of the challenge at once: the trained model does the cheap local bulk cut, then a Haiku densifier rephrases only the survivors. Result: 92% accuracy at 77 tokens — higher than full context (80%) at ~18% of the tokens. Denoising the context before the reader sees it removes the distractors that trip it up.
The 66M-param compressor trains in ~10 seconds on a MacBook (MPS).
Why a learned compressor beats classical
BM25 ranks sentences by surface-word overlap. The moment the query and the answer don't share words — "Where is X based?" answered by "X runs everything out of Helsinki" — and the context is full of lexical traps ("X based its culture on remote-first work"), BM25 spends its budget on the traps and drops the answer. The model, trained to imitate which sentences Claude says are needed, learns the meaning-level mapping and keeps the answer.
Extractive vs hybrid
The trained model is extractive — it scores each sentence's keep-probability (query-conditioned) and deletes to a budget. It can only delete, never reword, which is a feature: one cheap forward pass, no hallucination, exact values preserved.
The hybrid adds an abstractive last mile: after the extractive bulk cut, a Haiku densifier rewrites the surviving sentences into dense facts (values kept verbatim). It runs on a small, already-relevant input, so it's cheap and has little room to invent — and the cost math strongly favors it when the downstream reader is a larger model than the densifier.
How it works
(context, query)
│ distill.py
▼ Claude (teacher) picks the sentences needed to answer the query
→ per-token KEEP/DROP labels (data/*.jsonl)
│ train_compressor.py
▼ DistilBERT (student) fine-tuned as a query-aware keep/drop classifier
→ compressor_model/ (class-weighted loss; KEEP is ~2%)
│ neural.py
▼ rank sentences by mean KEEP-probability, fill to a token budget
→ extractive compress(); compress_hybrid() adds a densify() last mile
│ tokenc.py (eval harness)
▼ full vs BM25 vs model vs hybrid → accuracy / tokens / $ → demo.ipynb
- Teacher labeling (
distill.py): Claude sees the context as a numbered sentence list and returns the indices needed — exact token-label alignment, no fuzzy matching. Mixed lexical + semantic slices; seeds disjoint from the eval set. - Student (
train_compressor.py):AutoModelForTokenClassification(defaultdistilbert-base-uncased; swap with--backbone). A bidirectional encoder is the right architecture for keep/drop — each token sees both directions. Class-weighted loss because KEEP is rare; we treat the model as a ranker, not a 0.5-threshold classifier. - Inference (
neural.py): per-sentence KEEP-probability → rank → fill to budget → re-emit in order.compress_hybrid()adds the densify pass. Sentence scores are ratio-independent and disk-cached, so a whole Pareto sweep reuses them. - Eval (
tokenc.py): controllable multi-doc QA with distractors; Claude as the downstream reader; objective substring grading; every LLM call disk-cached so the demo re-runs instantly.
Files
| file | what |
|---|---|
tokenc.py | engine: BM25 baseline, budget controller, benchmark generator, eval harness, densify, pricing, caching |
distill.py | generate KEEP/DROP labels from Claude (--offline for an API-free dry run) |
train_compressor.py | fine-tune the keep/drop classifier (--smoke for a fast pipeline+timing check) |
neural.py | load the trained model; compress (extractive) and compress_hybrid (extractive + rephrase) |
make_figures.py | render performance.png and pareto.png |
build_notebook.py | regenerate demo.ipynb |
demo.ipynb | the story + charts + interactive keep-rate slider (pre-executed) |
smoke_test.py | offline sanity checks (no API key) |
Quickstart
bash setup.sh # venv + deps + Jupyter kernel
echo 'ANTHROPIC_API_KEY=sk-ant-...' > .env # auto-loaded by every script
./.venv/bin/python distill.py --n 240 # distill labels from Claude (~5 min, cached)
./.venv/bin/python train_compressor.py # train the model (~10 s on MPS)
./.venv/bin/python make_figures.py # render performance.png / pareto.png
./.venv/bin/jupyter notebook demo.ipynb # open the demo
The notebook ships pre-executed — open it to see the charts immediately; re-run is instant (cached). The keep-rate slider cell is the booth demo: drag it and watch tokens & cost fall while the answer stays correct, then flip to BM25 to watch it break.
Product framing
A drop-in proxy: point your Anthropic base_url at TokenC; we compress every prompt's context before it hits the model — fewer input tokens, same or better answers, one line of config. The extractive compressor is a small model you run locally; the hybrid adds a cheap rephrase pass when you want the last mile.
Honest notes
- The benchmark is synthetic and controllable by design — it lets us dial the exact regime (lexical traps, compression budget) where classical methods fail and a learned one wins, with objective grading. The methods are content-agnostic and run on any text (try the slider on your own).
- The extractive model maintains quality under aggressive compression; the hybrid measurably improves over full-context accuracy here (92% vs 80%) by denoising before the reader. The rephrase is a small extra LLM call on already-pruned input — the cost trade-off favors it when the reader is a bigger model than the densifier.
- Models/pricing from the current Claude lineup (Haiku 4.5 $1/$5, Sonnet 4.6 $3/$15, Opus 4.8 $5/$25 per 1M in/out). Token counts use Anthropic's own counter / real API
usage, never tiktoken.
Analysis
View
Metric
No commits on this project resolved to a GitHub account.
Technology
- AnthropicIn code
- PythonIn code
2 of 2 appear in the indexed code.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
70 KB
Source files
9
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
gsankar967/tokenc
14 files · 450 KB · @ e395f9c
Structure
Application logic
9 files · 64%Domain rules, services and shared utilities.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- Python89%
- Markdown10%
- Shell1%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
requirements.txt
pypi · 5- anthropic
- ipykernel
- jupyter
- matplotlib
- numpy
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.