SmartCrusher
Statistical JSON and array compression that keeps important items and drops the rest.
SmartCrusher is Headroom's compressor for JSON tool outputs. This is the compressor that fires automatically when ContentRouter detects JSON arrays.
Lossless-first, not keep/drop-first
By default SmartCrusher tries a lossless compaction first: if the array is cleanly tabular (a consistent set of keys across items), it re-encodes every item into a compact csv-schema form and keeps it if that saves at least 30% of the bytes -- no items are dropped, they're just represented more compactly. Only when that lossless fold doesn't clear the 30% bar (or the array isn't cleanly tabular) does SmartCrusher fall back to the scored keep/drop behavior described below. See headroom/transforms/smart_crusher.py:415-419. Set with_compaction=False on SmartCrusher(...) to force the pre-lossless keep/drop-only path (this is also what Headroom's retention-property tests use).
How It Works
When the lossy keep/drop path runs, SmartCrusher doesn't blindly truncate arrays -- it scores each item across five dimensions:
- First/Last items -- Context for pagination and recency
- Error items -- 100% preservation of error states (never dropped)
- Anomalies -- Statistical outliers (> 2 standard deviations from the mean)
- Relevant items -- Matches to the user's query via BM25/embeddings
- Change points -- Significant transitions in data
On that path, the target size is max_items_after_crush (15 by default): a 1,000-item array becomes ~15 items with all the information the LLM actually needs (measured: a 1,000-item array with with_compaction=False compressed to 16 items).
What Gets Preserved
| Category | Preserved | Why |
|---|---|---|
| Errors | 100% | Critical for debugging |
| First N | 100% | Context and pagination |
| Last N | 100% | Recency |
| Anomalies | All | Unusual values matter |
| Relevant | Top K | Match user's query |
| Others | Sampled | Statistical representation |
Quick Start
import { } from "headroom-ai";
// SmartCrusher fires automatically for JSON tool outputs
const = [
{ : "system" as , : "You are a helpful assistant." },
{ : "user" as , : "Find errors in the last 24 hours" },
{
: "tool" as ,
: .({ : new (1000).({ : "ok" }) }),
: "call_1",
},
];
const = await ();
.(`Tokens saved: ${.tokensSaved}`);
// SmartCrusher keeps errors, anomalies, and relevant itemsimport json
from headroom import SmartCrusher
crusher = SmartCrusher()
# crush() takes a JSON string, not a dict
tool_output = json.dumps({"results": ["...1000 items..."]})
# Returns a CrushResult; the compressed JSON string is `.compressed`
result = crusher.crush(tool_output, query="user's question")
print(result.compressed)
print(result.strategy) # e.g. "lossless:table(...)" or "smart_sample(1000->15)"Configuration
import { } from "headroom-ai";
// Configure via the Headroom proxy or HeadroomClient
const = await (messages, {
: "gpt-4o",
: 10000, // SmartCrusher will reduce JSON to fit
});
.(`Transforms: ${.transformsApplied}`);
// ["smart_crusher", "cache_aligner"]from headroom import SmartCrusher, SmartCrusherConfig
config = SmartCrusherConfig(
min_tokens_to_crush=200, # Only compress if > 200 tokens
max_items_after_crush=15, # Keep at most 15 items (lossy path only)
first_fraction=0.3, # Keep first 30% of items
last_fraction=0.15, # Keep last 15% of items
variance_threshold=2.0, # Statistical variance threshold
preserve_change_points=True, # Keep significant transitions
)
crusher = SmartCrusher(config)
result = crusher.crush(tool_output, query="find payment failures")Configuration Options
| Option | Default | Description |
|---|---|---|
min_tokens_to_crush | 200 | Only compress arrays with more than this many tokens |
min_items_to_analyze | 5 | Minimum items before analyzing for compression |
max_items_after_crush | 15 | Maximum items to keep after compression |
variance_threshold | 2.0 | Statistical variance threshold for analysis |
uniqueness_threshold | 0.1 | Uniqueness threshold for deduplication |
similarity_threshold | 0.8 | Similarity threshold for grouping |
preserve_change_points | True | Preserve significant transitions in data |
first_fraction | 0.3 | Fraction of items always kept from the start |
last_fraction | 0.15 | Fraction of items always kept from the end |
dedup_identical_items | True | Deduplicate identical items |
use_feedback_hints | True | Use TOIN feedback hints for scoring |
Example: Before and After
Consider a tool that returns 1,000 search results:
# Before compression: 45,000 tokens
{
"results": [
{"id": 1, "status": "ok", "message": "Success", "timestamp": "..."},
{"id": 2, "status": "ok", "message": "Success", "timestamp": "..."},
# ... 995 more "ok" results ...
{"id": 998, "status": "error", "message": "Connection timeout", "timestamp": "..."},
{"id": 999, "status": "ok", "message": "Success", "timestamp": "..."},
{"id": 1000, "status": "ok", "message": "Success", "timestamp": "..."},
]
}
# After SmartCrusher's lossy keep/drop path (with_compaction=False):
# ~15-16 items kept -- first items, last items, and the error at id=998The LLM sees the structure, the error, and a representative sample -- everything it needs to answer "find errors in the last 24 hours" without wading through 1,000 identical success responses. Note this specific shape (mostly-identical items, one error) is also exactly what the lossless table fold favors (see the callout above), so with default settings this array is more likely to be re-encoded compactly with every item still present than reduced to ~15 items -- the numbers above illustrate the scored keep/drop path, not a guarantee of which path a given array takes.
Automatic routing
You don't need to call SmartCrusher directly. The ContentRouter detects JSON arrays and routes them to SmartCrusher automatically. Direct usage is available when you want fine-grained control over the configuration.