Spaces:
Build error
Rewrite README + add Integration Guide
Browse filesREADME: 694 → 203 lines. Crisp, scannable, links to docs.
- compress() as the hero quickstart (not proxy)
- Integration table: compress(), LiteLLM, ASGI, proxy, Agno, LangChain
- LangChain marked as experimental
- "Already have a proxy?" callout linking to Integration Guide
- Architecture: ContentRouter (not SmartCrusher) as the primary compressor
New: docs/integration-guide.md
- Detailed setup for every integration path
- compress() with Anthropic, OpenAI, LiteLLM, raw HTTP
- LiteLLM callback + LiteLLM proxy ASGI middleware
- ASGI middleware for any FastAPI/Starlette app
- Compression hooks for advanced customization
- FAQ section
Fix: compress() uses default pipeline (CacheAligner + ContentRouter +
IntelligentContext) instead of manually specifying SmartCrusher.
- README.md +92 -499
- docs/integration-guide.md +292 -0
- headroom/compress.py +8 -12
|
@@ -29,7 +29,6 @@
|
|
| 29 |
</a>
|
| 30 |
</p>
|
| 31 |
|
| 32 |
-
|
| 33 |
---
|
| 34 |
|
| 35 |
## Demo
|
|
@@ -40,570 +39,164 @@
|
|
| 40 |
|
| 41 |
---
|
| 42 |
|
| 43 |
-
##
|
| 44 |
-
|
| 45 |
-
**The setup:** 100 production log entries. One critical error buried at position 67.
|
| 46 |
-
|
| 47 |
-
<details>
|
| 48 |
-
<summary><b>BEFORE:</b> 100 log entries (18,952 chars) - click to expand</summary>
|
| 49 |
|
| 50 |
-
```
|
| 51 |
-
[
|
| 52 |
-
{"timestamp": "2024-12-15T00:00:00Z", "level": "INFO", "service": "api-gateway", "message": "Request processed successfully - latency=50ms", "request_id": "req-000000", "status_code": 200},
|
| 53 |
-
{"timestamp": "2024-12-15T01:01:00Z", "level": "INFO", "service": "user-service", "message": "Request processed successfully - latency=51ms", "request_id": "req-000001", "status_code": 200},
|
| 54 |
-
{"timestamp": "2024-12-15T02:02:00Z", "level": "INFO", "service": "inventory", "message": "Request processed successfully - latency=52ms", "request_id": "req-000002", "status_code": 200},
|
| 55 |
-
// ... 64 more INFO entries ...
|
| 56 |
-
{"timestamp": "2024-12-15T03:47:23Z", "level": "FATAL", "service": "payment-gateway", "message": "Connection pool exhausted", "error_code": "PG-5523", "resolution": "Increase max_connections to 500 in config/database.yml", "affected_transactions": 1847},
|
| 57 |
-
// ... 32 more INFO entries ...
|
| 58 |
-
]
|
| 59 |
-
```
|
| 60 |
-
</details>
|
| 61 |
-
|
| 62 |
-
**AFTER:** Headroom compresses to 6 entries (1,155 chars):
|
| 63 |
-
|
| 64 |
-
```json
|
| 65 |
-
[
|
| 66 |
-
{"timestamp": "2024-12-15T00:00:00Z", "level": "INFO", "service": "api-gateway", ...},
|
| 67 |
-
{"timestamp": "2024-12-15T01:01:00Z", "level": "INFO", "service": "user-service", ...},
|
| 68 |
-
{"timestamp": "2024-12-15T02:02:00Z", "level": "INFO", "service": "inventory", ...},
|
| 69 |
-
{"timestamp": "2024-12-15T03:47:23Z", "level": "FATAL", "service": "payment-gateway", "error_code": "PG-5523", "resolution": "Increase max_connections to 500 in config/database.yml", "affected_transactions": 1847},
|
| 70 |
-
{"timestamp": "2024-12-15T02:38:00Z", "level": "INFO", "service": "inventory", ...},
|
| 71 |
-
{"timestamp": "2024-12-15T03:39:00Z", "level": "INFO", "service": "auth", ...}
|
| 72 |
-
]
|
| 73 |
```
|
| 74 |
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
---
|
| 78 |
-
|
| 79 |
-
**The question we asked Claude:** "What caused the outage? What's the error code? What's the fix?"
|
| 80 |
-
|
| 81 |
-
| | Baseline | Headroom |
|
| 82 |
-
|--|----------|----------|
|
| 83 |
-
| Input tokens | 10,144 | 1,260 |
|
| 84 |
-
| Correct answers | **4/4** | **4/4** |
|
| 85 |
-
|
| 86 |
-
Both responses: *"payment-gateway service, error PG-5523, fix: Increase max_connections to 500, 1,847 transactions affected"*
|
| 87 |
-
|
| 88 |
-
**87.6% fewer tokens. Same answer.**
|
| 89 |
-
|
| 90 |
-
Run it yourself: `python examples/needle_in_haystack_test.py`
|
| 91 |
-
|
| 92 |
-
---
|
| 93 |
-
|
| 94 |
-
## Accuracy Benchmarks
|
| 95 |
-
|
| 96 |
-
> **Headroom's guarantee: compress without losing accuracy.**
|
| 97 |
-
|
| 98 |
-
We validate against established open-source benchmarks. Full methodology and reproducible tests: [Benchmarks Documentation](https://chopratejas.github.io/headroom/benchmarks/)
|
| 99 |
-
|
| 100 |
-
| Benchmark | Metric | Result | Status |
|
| 101 |
-
|-----------|--------|--------|--------|
|
| 102 |
-
| [Scrapinghub Article Extraction](https://huggingface.co/datasets/allenai/scrapinghub-article-extraction-benchmark) | F1 Score | **0.919** (baseline: 0.958) | :white_check_mark: |
|
| 103 |
-
| [Scrapinghub Article Extraction](https://huggingface.co/datasets/allenai/scrapinghub-article-extraction-benchmark) | Recall | **98.2%** | :white_check_mark: |
|
| 104 |
-
| [Scrapinghub Article Extraction](https://huggingface.co/datasets/allenai/scrapinghub-article-extraction-benchmark) | Compression | **94.9%** | :white_check_mark: |
|
| 105 |
-
| SmartCrusher (JSON) | Accuracy | **100%** (4/4 correct) | :white_check_mark: |
|
| 106 |
-
| SmartCrusher (JSON) | Compression | **87.6%** | :white_check_mark: |
|
| 107 |
-
| Multi-Tool Agent | Accuracy | **100%** (all findings) | :white_check_mark: |
|
| 108 |
-
| Multi-Tool Agent | Compression | **76.3%** | :white_check_mark: |
|
| 109 |
-
|
| 110 |
-
**Why recall matters most**: For LLM applications, capturing all relevant information is critical. 98.2% recall means nearly all content is preserved — LLMs can answer questions accurately from compressed context.
|
| 111 |
-
|
| 112 |
-
<details>
|
| 113 |
-
<summary><b>Run benchmarks yourself</b></summary>
|
| 114 |
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
|
|
|
| 118 |
|
| 119 |
-
|
| 120 |
-
|
|
|
|
|
|
|
| 121 |
|
| 122 |
-
|
| 123 |
-
pytest tests/test_evals/test_html_oss_benchmarks.py::TestQAAccuracyPreservation -v -s
|
| 124 |
```
|
| 125 |
|
| 126 |
-
|
| 127 |
|
| 128 |
---
|
| 129 |
|
| 130 |
-
##
|
| 131 |
-
|
| 132 |
-
**The setup:** An Agno agent with 4 tools (GitHub Issues, ArXiv Papers, Code Search, Database Logs) investigating a memory leak. Total tool output: 62,323 chars (~15,580 tokens).
|
| 133 |
-
|
| 134 |
-
```python
|
| 135 |
-
from agno.agent import Agent
|
| 136 |
-
from agno.models.anthropic import Claude
|
| 137 |
-
from headroom.integrations.agno import HeadroomAgnoModel
|
| 138 |
-
|
| 139 |
-
# Wrap your model - that's it!
|
| 140 |
-
base_model = Claude(id="claude-sonnet-4-20250514")
|
| 141 |
-
model = HeadroomAgnoModel(wrapped_model=base_model)
|
| 142 |
-
|
| 143 |
-
agent = Agent(model=model, tools=[search_github, search_arxiv, search_code, query_db])
|
| 144 |
-
response = agent.run("Investigate the memory leak and recommend a fix")
|
| 145 |
-
```
|
| 146 |
-
|
| 147 |
-
**Results with Claude Sonnet:**
|
| 148 |
-
|
| 149 |
-
| | Baseline | Headroom |
|
| 150 |
-
|--|----------|----------|
|
| 151 |
-
| Tokens sent to API | 15,662 | 6,100 |
|
| 152 |
-
| API requests | 2 | 2 |
|
| 153 |
-
| Tool calls | 4 | 4 |
|
| 154 |
-
| Duration | 26.5s | 27.0s |
|
| 155 |
|
| 156 |
-
|
| 157 |
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
-
|
| 161 |
|
| 162 |
---
|
| 163 |
|
| 164 |
## How It Works
|
| 165 |
|
| 166 |
-
> Headroom optimizes LLM context *before* it hits the provider —
|
| 167 |
-
> without changing your agent logic or tools.
|
| 168 |
-
|
| 169 |
-
```mermaid
|
| 170 |
-
flowchart LR
|
| 171 |
-
User["Your App"]
|
| 172 |
-
Entry["Headroom"]
|
| 173 |
-
Transform["Context<br/>Optimization"]
|
| 174 |
-
LLM["LLM Provider"]
|
| 175 |
-
Response["Response"]
|
| 176 |
-
|
| 177 |
-
User --> Entry --> Transform --> LLM --> Response
|
| 178 |
```
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
CA["Cache Aligner<br/><i>Stabilizes dynamic tokens</i>"]
|
| 187 |
-
SC["Smart Crusher<br/><i>Removes redundant tool output</i>"]
|
| 188 |
-
CM["Intelligent Context<br/><i>Score-based token fitting</i>"]
|
| 189 |
-
CA --> SC --> CM
|
| 190 |
-
end
|
| 191 |
-
|
| 192 |
-
subgraph CCR["CCR: Compress-Cache-Retrieve"]
|
| 193 |
-
Store[("Compressed<br/>Store")]
|
| 194 |
-
Tool["Retrieve Tool"]
|
| 195 |
-
Tool <--> Store
|
| 196 |
-
end
|
| 197 |
-
|
| 198 |
-
LLM["LLM Provider"]
|
| 199 |
-
|
| 200 |
-
CM --> LLM
|
| 201 |
-
SC -. "Stores originals" .-> Store
|
| 202 |
-
LLM -. "Requests full context<br/>if needed" .-> Tool
|
| 203 |
```
|
| 204 |
|
| 205 |
-
|
| 206 |
-
> It compresses aggressively and retrieves precisely.
|
| 207 |
-
|
| 208 |
-
### What actually happens
|
| 209 |
-
|
| 210 |
-
1. **Headroom intercepts context** — Tool outputs, logs, search results, and intermediate agent steps.
|
| 211 |
-
|
| 212 |
-
2. **Dynamic content is stabilized** — Timestamps, UUIDs, request IDs are normalized so prompts cache cleanly.
|
| 213 |
-
|
| 214 |
-
3. **Low-signal content is removed** — Repetitive or redundant data is crushed, not truncated.
|
| 215 |
-
|
| 216 |
-
4. **Original data is preserved** — Full content is stored separately and retrieved *only if the LLM asks*.
|
| 217 |
-
|
| 218 |
-
5. **Provider caches finally work** — Headroom aligns prompts so OpenAI, Anthropic, and Google caches actually hit.
|
| 219 |
-
|
| 220 |
-
For deep technical details, see [Architecture Documentation](docs/ARCHITECTURE.md).
|
| 221 |
|
| 222 |
---
|
| 223 |
|
| 224 |
-
##
|
| 225 |
-
|
| 226 |
-
- **Zero code changes** - works as a transparent proxy
|
| 227 |
-
- **47-92% savings** - depends on your workload (tool-heavy = more savings)
|
| 228 |
-
- **Image compression** - 40-90% reduction via trained ML router (OpenAI, Anthropic, Google)
|
| 229 |
-
- **Reversible compression** - LLM retrieves original data via CCR
|
| 230 |
-
- **Content-aware** - code, logs, JSON, images each handled optimally
|
| 231 |
-
- **Provider caching** - automatic prefix optimization for cache hits
|
| 232 |
-
- **Framework native** - LangChain, Agno, MCP, agents supported
|
| 233 |
-
|
| 234 |
-
---
|
| 235 |
-
|
| 236 |
-
## 30-Second Quickstart
|
| 237 |
-
|
| 238 |
-
### Option 1: Proxy (Zero Code Changes)
|
| 239 |
-
|
| 240 |
-
```bash
|
| 241 |
-
pip install "headroom-ai[all]" # Recommended for best performance
|
| 242 |
-
headroom proxy --port 8787
|
| 243 |
-
```
|
| 244 |
-
|
| 245 |
-
> **Note:** First startup downloads ML models (~500MB) for optimal compression. This is a one-time download.
|
| 246 |
-
|
| 247 |
-
**Dashboard:** Open http://localhost:8787/dashboard to see real-time stats, token savings, and request history.
|
| 248 |
-
|
| 249 |
-
Point your tools at the proxy:
|
| 250 |
-
|
| 251 |
-
```bash
|
| 252 |
-
# Claude Code
|
| 253 |
-
ANTHROPIC_BASE_URL=http://localhost:8787 claude
|
| 254 |
-
|
| 255 |
-
# Any OpenAI-compatible client
|
| 256 |
-
OPENAI_BASE_URL=http://localhost:8787/v1 cursor
|
| 257 |
-
```
|
| 258 |
-
|
| 259 |
-
**Enable Persistent Memory** - Claude remembers across conversations:
|
| 260 |
-
|
| 261 |
-
```bash
|
| 262 |
-
headroom proxy --memory
|
| 263 |
-
```
|
| 264 |
-
|
| 265 |
-
Memory auto-detects your provider (Anthropic, OpenAI, Gemini) and uses the appropriate format:
|
| 266 |
-
- **Anthropic**: Uses native memory tool (`memory_20250818`) - works with Claude Code subscriptions
|
| 267 |
-
- **OpenAI/Gemini/Others**: Uses function calling format
|
| 268 |
-
- All providers share the same semantic vector store for search
|
| 269 |
-
|
| 270 |
-
Set `x-headroom-user-id` header for per-user memory isolation (defaults to 'default').
|
| 271 |
-
|
| 272 |
-
**Claude Code Subscription Users** - Use MCP for CCR (Compress-Cache-Retrieve):
|
| 273 |
-
|
| 274 |
-
If you use Claude Code with a subscription (not API key), you need MCP to enable the `headroom_retrieve` tool:
|
| 275 |
-
|
| 276 |
-
```bash
|
| 277 |
-
# One-time setup
|
| 278 |
-
pip install "headroom-ai[mcp]"
|
| 279 |
-
headroom mcp install
|
| 280 |
-
|
| 281 |
-
# Every time you code
|
| 282 |
-
headroom proxy # Terminal 1
|
| 283 |
-
claude # Terminal 2 - now has headroom_retrieve!
|
| 284 |
-
```
|
| 285 |
-
|
| 286 |
-
What this does:
|
| 287 |
-
- Configures Claude Code to use Headroom's MCP server (`~/.claude/mcp.json`)
|
| 288 |
-
- When the proxy compresses large tool outputs, Claude sees markers like `[47 items compressed... hash=abc123]`
|
| 289 |
-
- Claude can call `headroom_retrieve` to get the full original content when needed
|
| 290 |
-
|
| 291 |
-
Check your setup:
|
| 292 |
-
```bash
|
| 293 |
-
headroom mcp status
|
| 294 |
-
```
|
| 295 |
-
|
| 296 |
-
<details>
|
| 297 |
-
<summary><b>Why MCP for subscriptions?</b></summary>
|
| 298 |
-
|
| 299 |
-
- **API users** can inject custom tools directly via the Messages API
|
| 300 |
-
- **Subscription users** use Claude Code's built-in tool set and can't inject tools programmatically
|
| 301 |
-
- **MCP** (Model Context Protocol) is Claude's official way to extend tools - it works with subscriptions
|
| 302 |
-
|
| 303 |
-
The MCP server exposes `headroom_retrieve` so Claude can request uncompressed content when the compressed summary isn't enough.
|
| 304 |
-
</details>
|
| 305 |
-
|
| 306 |
-
**Using AWS Bedrock, Google Vertex, or Azure?** Route through Headroom:
|
| 307 |
-
|
| 308 |
-
```bash
|
| 309 |
-
# AWS Bedrock - Terminal 1: Start proxy
|
| 310 |
-
export AWS_ACCESS_KEY_ID="AKIA..."
|
| 311 |
-
export AWS_SECRET_ACCESS_KEY="..."
|
| 312 |
-
export AWS_REGION="us-east-1"
|
| 313 |
-
headroom proxy --backend bedrock --region us-east-1
|
| 314 |
-
|
| 315 |
-
# AWS Bedrock - Terminal 2: Run Claude Code
|
| 316 |
-
export ANTHROPIC_API_KEY="sk-ant-dummy" # Any value works! Headroom ignores it.
|
| 317 |
-
export ANTHROPIC_BASE_URL="http://localhost:8787"
|
| 318 |
-
# IMPORTANT: Do NOT set CLAUDE_CODE_USE_BEDROCK=1 (Headroom handles Bedrock routing)
|
| 319 |
-
claude
|
| 320 |
-
```
|
| 321 |
-
|
| 322 |
-
<details>
|
| 323 |
-
<summary><b>VS Code settings.json for Bedrock</b> (click to expand)</summary>
|
| 324 |
-
|
| 325 |
-
```json
|
| 326 |
-
{
|
| 327 |
-
"claudeCode.environmentVariables": [
|
| 328 |
-
{ "name": "ANTHROPIC_API_KEY", "value": "sk-ant-dummy" },
|
| 329 |
-
{ "name": "ANTHROPIC_BASE_URL", "value": "http://localhost:8787" },
|
| 330 |
-
{ "name": "AWS_ACCESS_KEY_ID", "value": "AKIA..." },
|
| 331 |
-
{ "name": "AWS_SECRET_ACCESS_KEY", "value": "..." },
|
| 332 |
-
{ "name": "AWS_REGION", "value": "us-east-1" }
|
| 333 |
-
]
|
| 334 |
-
}
|
| 335 |
-
```
|
| 336 |
-
|
| 337 |
-
**Do NOT include** `CLAUDE_CODE_USE_BEDROCK` - Headroom handles the Bedrock routing.
|
| 338 |
-
</details>
|
| 339 |
-
|
| 340 |
-
**Using OpenRouter?** Access 400+ models through a single API:
|
| 341 |
-
|
| 342 |
-
```bash
|
| 343 |
-
# OpenRouter - Terminal 1: Start proxy
|
| 344 |
-
export OPENROUTER_API_KEY="sk-or-v1-..."
|
| 345 |
-
headroom proxy --backend openrouter
|
| 346 |
-
|
| 347 |
-
# OpenRouter - Terminal 2: Run your client
|
| 348 |
-
export ANTHROPIC_API_KEY="sk-ant-dummy" # Any value works! Headroom ignores it.
|
| 349 |
-
export ANTHROPIC_BASE_URL="http://localhost:8787"
|
| 350 |
-
# Use OpenRouter model names in your requests:
|
| 351 |
-
# - anthropic/claude-3.5-sonnet
|
| 352 |
-
# - openai/gpt-4o
|
| 353 |
-
# - google/gemini-pro
|
| 354 |
-
# - meta-llama/llama-3-70b-instruct
|
| 355 |
-
# See all models: https://openrouter.ai/models
|
| 356 |
-
```
|
| 357 |
-
|
| 358 |
-
```bash
|
| 359 |
-
# Google Vertex AI
|
| 360 |
-
headroom proxy --backend vertex_ai --region us-central1
|
| 361 |
-
|
| 362 |
-
# Azure OpenAI
|
| 363 |
-
headroom proxy --backend azure --region eastus
|
| 364 |
-
```
|
| 365 |
-
|
| 366 |
-
### Option 2: LangChain Integration
|
| 367 |
-
|
| 368 |
-
```bash
|
| 369 |
-
pip install "headroom-ai[langchain]"
|
| 370 |
-
```
|
| 371 |
-
|
| 372 |
-
```python
|
| 373 |
-
from langchain_openai import ChatOpenAI
|
| 374 |
-
from headroom.integrations import HeadroomChatModel
|
| 375 |
-
|
| 376 |
-
# Wrap your model - that's it!
|
| 377 |
-
llm = HeadroomChatModel(ChatOpenAI(model="gpt-4o"))
|
| 378 |
-
|
| 379 |
-
# Use exactly like before
|
| 380 |
-
response = llm.invoke("Hello!")
|
| 381 |
-
```
|
| 382 |
-
|
| 383 |
-
See the full [LangChain Integration Guide](docs/langchain.md) for memory, retrievers, agents, and more.
|
| 384 |
-
|
| 385 |
-
### Option 3: Agno Integration
|
| 386 |
-
|
| 387 |
-
```bash
|
| 388 |
-
pip install "headroom-ai[agno]"
|
| 389 |
-
```
|
| 390 |
-
|
| 391 |
-
```python
|
| 392 |
-
from agno.agent import Agent
|
| 393 |
-
from agno.models.openai import OpenAIChat
|
| 394 |
-
from headroom.integrations.agno import HeadroomAgnoModel
|
| 395 |
-
|
| 396 |
-
# Wrap your model - that's it!
|
| 397 |
-
model = HeadroomAgnoModel(OpenAIChat(id="gpt-4o"))
|
| 398 |
-
agent = Agent(model=model)
|
| 399 |
-
|
| 400 |
-
# Use exactly like before
|
| 401 |
-
response = agent.run("Hello!")
|
| 402 |
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
|
|
|
|
|
|
|
|
|
| 406 |
|
| 407 |
-
|
| 408 |
|
| 409 |
---
|
| 410 |
|
| 411 |
-
##
|
| 412 |
|
| 413 |
-
|
|
| 414 |
-
|-----------
|
| 415 |
-
|
|
| 416 |
-
| **
|
| 417 |
-
| **
|
| 418 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 419 |
|
| 420 |
---
|
| 421 |
|
| 422 |
## Features
|
| 423 |
|
| 424 |
-
| Feature |
|
| 425 |
-
|---------|-------------|
|
| 426 |
-
| **
|
| 427 |
-
| **
|
| 428 |
-
| **
|
| 429 |
-
| **
|
| 430 |
-
| **
|
| 431 |
-
| **
|
| 432 |
-
| **
|
| 433 |
-
| **
|
| 434 |
-
| **
|
| 435 |
-
| **
|
| 436 |
-
| **
|
| 437 |
-
| **LLMLingua-2** | ML-based 20x compression (opt-in) | [LLMLingua](docs/llmlingua.md) |
|
| 438 |
-
| **Code-Aware** | AST-based code compression (tree-sitter) | [Transforms](docs/transforms.md) |
|
| 439 |
-
| **Evals Framework** | Prove compression preserves accuracy (12+ datasets) | [Evals](headroom/evals/README.md) |
|
| 440 |
|
| 441 |
---
|
| 442 |
|
| 443 |
-
##
|
| 444 |
-
|
| 445 |
-
Skeptical? Good. We built a comprehensive evaluation framework to **prove** compression preserves accuracy.
|
| 446 |
|
| 447 |
```bash
|
| 448 |
-
#
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
python -m headroom.evals quick
|
| 453 |
-
|
| 454 |
-
# Run on real datasets
|
| 455 |
-
python -m headroom.evals benchmark --dataset hotpotqa -n 100
|
| 456 |
-
```
|
| 457 |
-
|
| 458 |
-
### How Evals Work
|
| 459 |
-
|
| 460 |
-
```
|
| 461 |
-
Original Context ───► LLM ───► Response A
|
| 462 |
-
│
|
| 463 |
-
Compressed Context ─► LLM ───► Response B
|
| 464 |
-
│
|
| 465 |
-
Compare A vs B │
|
| 466 |
-
─────────────────
|
| 467 |
-
F1 Score: 0.95
|
| 468 |
-
Semantic Similarity: 0.97
|
| 469 |
-
Ground Truth Match: ✓
|
| 470 |
-
─────────────────
|
| 471 |
-
PASS: Accuracy preserved
|
| 472 |
```
|
| 473 |
|
| 474 |
-
### Available Datasets (12+)
|
| 475 |
-
|
| 476 |
-
| Category | Datasets |
|
| 477 |
-
|----------|----------|
|
| 478 |
-
| **RAG** | HotpotQA, Natural Questions, TriviaQA, MS MARCO, SQuAD |
|
| 479 |
-
| **Long Context** | LongBench (4K-128K tokens), NarrativeQA |
|
| 480 |
-
| **Tool Use** | BFCL (function calling), ToolBench, Built-in samples |
|
| 481 |
-
| **Code** | CodeSearchNet, HumanEval |
|
| 482 |
-
|
| 483 |
-
### CI Integration
|
| 484 |
-
|
| 485 |
-
```yaml
|
| 486 |
-
# GitHub Actions
|
| 487 |
-
- name: Run Compression Evals
|
| 488 |
-
run: python -m headroom.evals quick -n 20
|
| 489 |
-
env:
|
| 490 |
-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
| 491 |
-
```
|
| 492 |
-
|
| 493 |
-
Exit code 0 if accuracy ≥ 90%, 1 otherwise.
|
| 494 |
-
|
| 495 |
-
See the full [Evals Documentation](headroom/evals/README.md) for datasets, metrics, and programmatic API.
|
| 496 |
-
|
| 497 |
-
---
|
| 498 |
-
|
| 499 |
-
## Verified Performance
|
| 500 |
-
|
| 501 |
-
These numbers are from actual API calls, not estimates:
|
| 502 |
-
|
| 503 |
-
| Scenario | Before | After | Savings | Verified |
|
| 504 |
-
|----------|--------|-------|---------|----------|
|
| 505 |
-
| Code search (100 results) | 17,765 tokens | 1,408 tokens | 92% | Claude Sonnet |
|
| 506 |
-
| SRE incident debugging | 65,694 tokens | 5,118 tokens | 92% | GPT-4o |
|
| 507 |
-
| Codebase exploration | 78,502 tokens | 41,254 tokens | 47% | GPT-4o |
|
| 508 |
-
| GitHub issue triage | 54,174 tokens | 14,761 tokens | 73% | GPT-4o |
|
| 509 |
-
|
| 510 |
-
**Overhead**: ~1-5ms compression latency
|
| 511 |
-
|
| 512 |
-
**When savings are highest**: Tool-heavy workloads (search, logs, database queries)
|
| 513 |
-
**When savings are lowest**: Conversation-heavy workloads with minimal tool use
|
| 514 |
-
|
| 515 |
-
---
|
| 516 |
-
|
| 517 |
-
## Providers
|
| 518 |
-
|
| 519 |
-
| Provider | Token Counting | Cache Optimization |
|
| 520 |
-
|----------|----------------|-------------------|
|
| 521 |
-
| OpenAI | tiktoken (exact) | Automatic prefix caching |
|
| 522 |
-
| Anthropic | Official API | cache_control blocks |
|
| 523 |
-
| Google | Official API | Context caching |
|
| 524 |
-
| Cohere | Official API | - |
|
| 525 |
-
| Mistral | Official tokenizer | - |
|
| 526 |
-
|
| 527 |
-
New models auto-supported via naming pattern detection.
|
| 528 |
-
|
| 529 |
-
---
|
| 530 |
-
|
| 531 |
-
## Safety Guarantees
|
| 532 |
-
|
| 533 |
-
- **Never removes human content** - user/assistant messages preserved
|
| 534 |
-
- **Never breaks tool ordering** - tool calls and responses stay paired
|
| 535 |
-
- **Parse failures are no-ops** - malformed content passes through unchanged
|
| 536 |
-
- **Compression is reversible** - LLM retrieves original data via CCR
|
| 537 |
-
|
| 538 |
---
|
| 539 |
|
| 540 |
## Installation
|
| 541 |
|
| 542 |
```bash
|
| 543 |
-
|
| 544 |
-
pip install "headroom-ai[all]"
|
| 545 |
-
|
| 546 |
-
#
|
| 547 |
-
pip install headroom-ai
|
| 548 |
-
pip install "headroom-ai[
|
| 549 |
-
pip install "headroom-ai[
|
| 550 |
-
pip install "headroom-ai[langchain]" # LangChain integration
|
| 551 |
-
pip install "headroom-ai[agno]" # Agno agent framework
|
| 552 |
-
pip install "headroom-ai[evals]" # Evaluation framework
|
| 553 |
-
pip install "headroom-ai[code]" # AST-based code compression
|
| 554 |
-
pip install "headroom-ai[llmlingua]" # ML-based compression
|
| 555 |
```
|
| 556 |
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
> **First-time startup:** Headroom downloads ML models (~500MB) on first run for optimal compression. This is cached locally and only happens once.
|
| 560 |
|
| 561 |
---
|
| 562 |
|
| 563 |
## Documentation
|
| 564 |
|
| 565 |
-
|
|
| 566 |
-
|---
|
| 567 |
-
| [
|
| 568 |
-
| [
|
|
|
|
|
|
|
|
|
|
| 569 |
| [Evals Framework](headroom/evals/README.md) | Prove compression preserves accuracy |
|
| 570 |
-
| [
|
| 571 |
-
| [Agno
|
| 572 |
-
| [
|
| 573 |
-
| [Proxy Guide](docs/proxy.md) | Production deployment |
|
| 574 |
| [Configuration](docs/configuration.md) | All options |
|
| 575 |
-
| [CCR Guide](docs/ccr.md) | Reversible compression |
|
| 576 |
-
| [MCP Guide](docs/mcp.md) | Claude Code subscription support |
|
| 577 |
-
| [Metrics](docs/metrics.md) | Monitoring |
|
| 578 |
-
| [Troubleshooting](docs/troubleshooting.md) | Common issues |
|
| 579 |
-
|
| 580 |
-
---
|
| 581 |
-
|
| 582 |
-
## Who's Using Headroom?
|
| 583 |
-
|
| 584 |
-
> Add your project here! [Open a PR](https://github.com/chopratejas/headroom/pulls) or [start a discussion](https://github.com/chopratejas/headroom/discussions).
|
| 585 |
|
| 586 |
---
|
| 587 |
|
| 588 |
## Contributing
|
| 589 |
|
| 590 |
```bash
|
| 591 |
-
git clone https://github.com/chopratejas/headroom.git
|
| 592 |
-
|
| 593 |
-
pip install -e ".[dev]"
|
| 594 |
-
pytest
|
| 595 |
```
|
| 596 |
|
| 597 |
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
| 598 |
-
|
| 599 |
---
|
| 600 |
|
| 601 |
## License
|
| 602 |
|
| 603 |
-
Apache License 2.0
|
| 604 |
-
|
| 605 |
-
---
|
| 606 |
-
|
| 607 |
-
<p align="center">
|
| 608 |
-
<sub>Built for the AI developer community</sub>
|
| 609 |
-
</p>
|
|
|
|
| 29 |
</a>
|
| 30 |
</p>
|
| 31 |
|
|
|
|
| 32 |
---
|
| 33 |
|
| 34 |
## Demo
|
|
|
|
| 39 |
|
| 40 |
---
|
| 41 |
|
| 42 |
+
## Quick Start
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
+
```bash
|
| 45 |
+
pip install "headroom-ai[all]"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
```
|
| 47 |
|
| 48 |
+
```python
|
| 49 |
+
from headroom import compress
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
messages = [
|
| 52 |
+
{"role": "user", "content": "What caused the outage?"},
|
| 53 |
+
{"role": "tool", "content": huge_log_output, "tool_call_id": "call_1"},
|
| 54 |
+
]
|
| 55 |
|
| 56 |
+
result = compress(messages, model="claude-sonnet-4-5-20250929")
|
| 57 |
+
# result.messages → same format, 50-90% fewer tokens
|
| 58 |
+
# result.tokens_saved → 8,000
|
| 59 |
+
# result.compression_ratio → 0.87
|
| 60 |
|
| 61 |
+
response = client.messages.create(model="claude-sonnet-4-5-20250929", messages=result.messages)
|
|
|
|
| 62 |
```
|
| 63 |
|
| 64 |
+
**Same answer. 87% fewer tokens.**
|
| 65 |
|
| 66 |
---
|
| 67 |
|
| 68 |
+
## How to Use Headroom
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
+
Headroom is a compression library, not just a proxy. Use whichever integration fits your stack:
|
| 71 |
|
| 72 |
+
| You have... | Use this | Code |
|
| 73 |
+
|-------------|----------|------|
|
| 74 |
+
| Any Python app | `compress()` | `result = compress(messages, model="gpt-4o")` |
|
| 75 |
+
| LiteLLM | Callback | `litellm.callbacks = [HeadroomCallback()]` |
|
| 76 |
+
| Python proxy (FastAPI) | ASGI Middleware | `app.add_middleware(CompressionMiddleware)` |
|
| 77 |
+
| Claude Code / Cursor | Proxy | `ANTHROPIC_BASE_URL=http://localhost:8787 claude` |
|
| 78 |
+
| Agno agents | Wrap model | `HeadroomAgnoModel(your_model)` |
|
| 79 |
+
| LangChain | Wrap model | `HeadroomChatModel(your_llm)` *(experimental)* |
|
| 80 |
|
| 81 |
+
**Already have a proxy?** You don't need another one. See the **[Integration Guide](docs/integration-guide.md)** for detailed setup with LiteLLM, ASGI middleware, and direct `compress()` usage.
|
| 82 |
|
| 83 |
---
|
| 84 |
|
| 85 |
## How It Works
|
| 86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
```
|
| 88 |
+
Your App → Headroom → LLM Provider
|
| 89 |
+
↓
|
| 90 |
+
CacheAligner: stabilizes prefix for KV cache hits
|
| 91 |
+
ContentRouter: routes to optimal compressor per content type
|
| 92 |
+
→ SmartCrusher (JSON) | CodeCompressor (code) | LLMLingua (text)
|
| 93 |
+
IntelligentContext: score-based token fitting
|
| 94 |
+
CCR: stores originals for retrieval if LLM needs more
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
```
|
| 96 |
|
| 97 |
+
Headroom never throws data away. It compresses aggressively and retrieves precisely.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
---
|
| 100 |
|
| 101 |
+
## Verified Performance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
+
| Scenario | Tokens Before | Tokens After | Savings |
|
| 104 |
+
|----------|--------------|-------------|---------|
|
| 105 |
+
| Code search (100 results) | 17,765 | 1,408 | **92%** |
|
| 106 |
+
| SRE incident debugging | 65,694 | 5,118 | **92%** |
|
| 107 |
+
| Codebase exploration | 78,502 | 41,254 | **47%** |
|
| 108 |
+
| GitHub issue triage | 54,174 | 14,761 | **73%** |
|
| 109 |
|
| 110 |
+
**Overhead**: 1-5ms. **Accuracy**: [benchmarked](docs/benchmarks.md) across 12+ datasets.
|
| 111 |
|
| 112 |
---
|
| 113 |
|
| 114 |
+
## Integrations
|
| 115 |
|
| 116 |
+
| Integration | Status | Docs |
|
| 117 |
+
|-------------|--------|------|
|
| 118 |
+
| `compress()` — one function | **Stable** | [Integration Guide](docs/integration-guide.md) |
|
| 119 |
+
| LiteLLM callback | **Stable** | [Integration Guide](docs/integration-guide.md#litellm) |
|
| 120 |
+
| ASGI middleware | **Stable** | [Integration Guide](docs/integration-guide.md#asgi-middleware) |
|
| 121 |
+
| Proxy server | **Stable** | [Proxy Docs](docs/proxy.md) |
|
| 122 |
+
| Agno | **Stable** | [Agno Guide](docs/agno.md) |
|
| 123 |
+
| MCP (Claude Code) | **Stable** | [MCP Guide](docs/mcp.md) |
|
| 124 |
+
| Strands | **Stable** | [Strands Guide](docs/strands.md) |
|
| 125 |
+
| LangChain | **Experimental** | [LangChain Guide](docs/langchain.md) |
|
| 126 |
|
| 127 |
---
|
| 128 |
|
| 129 |
## Features
|
| 130 |
|
| 131 |
+
| Feature | What it does |
|
| 132 |
+
|---------|-------------|
|
| 133 |
+
| **Content Router** | Auto-detects content type, routes to optimal compressor |
|
| 134 |
+
| **SmartCrusher** | Statistically compresses JSON arrays (tool outputs, API responses) |
|
| 135 |
+
| **CodeCompressor** | AST-aware code compression (Python, JS, Go, Rust, Java) |
|
| 136 |
+
| **LLMLingua-2** | ML-based 20x text compression |
|
| 137 |
+
| **CCR** | Reversible compression — LLM retrieves originals when needed |
|
| 138 |
+
| **CacheAligner** | Stabilizes prefixes for provider KV cache hits |
|
| 139 |
+
| **IntelligentContext** | Score-based context management with learned importance |
|
| 140 |
+
| **Image Compression** | 40-90% token reduction via trained ML router |
|
| 141 |
+
| **Memory** | Persistent memory across conversations |
|
| 142 |
+
| **Compression Hooks** | Customize compression with pre/post hooks |
|
| 143 |
+
| **Query Echo** | Re-injects user question after compressed data for better attention |
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
---
|
| 146 |
|
| 147 |
+
## Cloud Providers
|
|
|
|
|
|
|
| 148 |
|
| 149 |
```bash
|
| 150 |
+
headroom proxy --backend bedrock --region us-east-1 # AWS Bedrock
|
| 151 |
+
headroom proxy --backend vertex_ai --region us-central1 # Google Vertex
|
| 152 |
+
headroom proxy --backend azure # Azure OpenAI
|
| 153 |
+
headroom proxy --backend openrouter # OpenRouter (400+ models)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
```
|
| 155 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
---
|
| 157 |
|
| 158 |
## Installation
|
| 159 |
|
| 160 |
```bash
|
| 161 |
+
pip install headroom-ai # Core library
|
| 162 |
+
pip install "headroom-ai[all]" # Everything (recommended)
|
| 163 |
+
pip install "headroom-ai[proxy]" # Proxy server
|
| 164 |
+
pip install "headroom-ai[mcp]" # MCP for Claude Code
|
| 165 |
+
pip install "headroom-ai[agno]" # Agno integration
|
| 166 |
+
pip install "headroom-ai[langchain]" # LangChain (experimental)
|
| 167 |
+
pip install "headroom-ai[evals]" # Evaluation framework
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
```
|
| 169 |
|
| 170 |
+
Python 3.10+
|
|
|
|
|
|
|
| 171 |
|
| 172 |
---
|
| 173 |
|
| 174 |
## Documentation
|
| 175 |
|
| 176 |
+
| | |
|
| 177 |
+
|---|---|
|
| 178 |
+
| [Integration Guide](docs/integration-guide.md) | LiteLLM, ASGI, compress(), proxy |
|
| 179 |
+
| [Proxy Docs](docs/proxy.md) | Proxy server configuration |
|
| 180 |
+
| [Architecture](docs/ARCHITECTURE.md) | How the pipeline works |
|
| 181 |
+
| [CCR Guide](docs/ccr.md) | Reversible compression |
|
| 182 |
+
| [Benchmarks](docs/benchmarks.md) | Accuracy validation |
|
| 183 |
| [Evals Framework](headroom/evals/README.md) | Prove compression preserves accuracy |
|
| 184 |
+
| [Memory](docs/memory.md) | Persistent memory |
|
| 185 |
+
| [Agno](docs/agno.md) | Agno agent framework |
|
| 186 |
+
| [MCP](docs/mcp.md) | Claude Code subscriptions |
|
|
|
|
| 187 |
| [Configuration](docs/configuration.md) | All options |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
---
|
| 190 |
|
| 191 |
## Contributing
|
| 192 |
|
| 193 |
```bash
|
| 194 |
+
git clone https://github.com/chopratejas/headroom.git && cd headroom
|
| 195 |
+
pip install -e ".[dev]" && pytest
|
|
|
|
|
|
|
| 196 |
```
|
| 197 |
|
|
|
|
|
|
|
| 198 |
---
|
| 199 |
|
| 200 |
## License
|
| 201 |
|
| 202 |
+
Apache License 2.0 — see [LICENSE](LICENSE).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -0,0 +1,292 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Integration Guide
|
| 2 |
+
|
| 3 |
+
You don't need to run the Headroom proxy. Headroom is a compression library that works with **any** LLM client, proxy, or framework.
|
| 4 |
+
|
| 5 |
+
## Pick Your Path
|
| 6 |
+
|
| 7 |
+
| You have... | Use this | Setup |
|
| 8 |
+
|-------------|----------|-------|
|
| 9 |
+
| Any Python app | [`compress()`](#compress-function) | 2 lines |
|
| 10 |
+
| LiteLLM | [LiteLLM callback](#litellm) | 1 line |
|
| 11 |
+
| A Python proxy (FastAPI, custom) | [ASGI middleware](#asgi-middleware) | 1 line |
|
| 12 |
+
| Claude Code / Cursor | [Headroom proxy](#proxy) | 1 env var |
|
| 13 |
+
| Agno agents | [Agno integration](#agno) | Wrap model |
|
| 14 |
+
| LangChain | [LangChain integration](#langchain) | Wrap model |
|
| 15 |
+
| Non-Python app | [Headroom proxy](#proxy) | HTTP |
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
## compress() Function
|
| 20 |
+
|
| 21 |
+
The simplest integration. Works with any LLM client.
|
| 22 |
+
|
| 23 |
+
```python
|
| 24 |
+
from headroom import compress
|
| 25 |
+
|
| 26 |
+
# Before sending to your LLM:
|
| 27 |
+
result = compress(messages, model="claude-sonnet-4-5-20250929")
|
| 28 |
+
response = your_client.create(messages=result.messages) # Fewer tokens, same answer
|
| 29 |
+
|
| 30 |
+
print(f"Saved {result.tokens_saved} tokens ({result.compression_ratio:.0%})")
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
### With Anthropic SDK
|
| 34 |
+
|
| 35 |
+
```python
|
| 36 |
+
from anthropic import Anthropic
|
| 37 |
+
from headroom import compress
|
| 38 |
+
|
| 39 |
+
client = Anthropic()
|
| 40 |
+
messages = [
|
| 41 |
+
{"role": "user", "content": "What went wrong?"},
|
| 42 |
+
{"role": "assistant", "content": "Let me check.", "tool_use": [...]},
|
| 43 |
+
{"role": "user", "content": [{"type": "tool_result", "content": huge_json}]},
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
+
compressed = compress(messages, model="claude-sonnet-4-5-20250929")
|
| 47 |
+
response = client.messages.create(
|
| 48 |
+
model="claude-sonnet-4-5-20250929",
|
| 49 |
+
messages=compressed.messages,
|
| 50 |
+
max_tokens=1000,
|
| 51 |
+
)
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
### With OpenAI SDK
|
| 55 |
+
|
| 56 |
+
```python
|
| 57 |
+
from openai import OpenAI
|
| 58 |
+
from headroom import compress
|
| 59 |
+
|
| 60 |
+
client = OpenAI()
|
| 61 |
+
messages = [
|
| 62 |
+
{"role": "user", "content": "Analyze these results"},
|
| 63 |
+
{"role": "tool", "content": big_json_output, "tool_call_id": "call_1"},
|
| 64 |
+
]
|
| 65 |
+
|
| 66 |
+
compressed = compress(messages, model="gpt-4o")
|
| 67 |
+
response = client.chat.completions.create(
|
| 68 |
+
model="gpt-4o",
|
| 69 |
+
messages=compressed.messages,
|
| 70 |
+
)
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
### With LiteLLM (direct)
|
| 74 |
+
|
| 75 |
+
```python
|
| 76 |
+
import litellm
|
| 77 |
+
from headroom import compress
|
| 78 |
+
|
| 79 |
+
messages = [...]
|
| 80 |
+
compressed = compress(messages, model="bedrock/claude-sonnet")
|
| 81 |
+
response = litellm.completion(model="bedrock/claude-sonnet", messages=compressed.messages)
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
### With any HTTP client
|
| 85 |
+
|
| 86 |
+
```python
|
| 87 |
+
import httpx
|
| 88 |
+
from headroom import compress
|
| 89 |
+
|
| 90 |
+
compressed = compress(messages, model="claude-sonnet-4-5-20250929")
|
| 91 |
+
httpx.post("https://api.anthropic.com/v1/messages", json={
|
| 92 |
+
"model": "claude-sonnet-4-5-20250929",
|
| 93 |
+
"messages": compressed.messages,
|
| 94 |
+
}, headers={"X-Api-Key": api_key, "anthropic-version": "2023-06-01"})
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
### What compress() returns
|
| 98 |
+
|
| 99 |
+
```python
|
| 100 |
+
result = compress(messages, model="gpt-4o")
|
| 101 |
+
result.messages # list[dict] — compressed messages, same format as input
|
| 102 |
+
result.tokens_before # int — original token count
|
| 103 |
+
result.tokens_after # int — compressed token count
|
| 104 |
+
result.tokens_saved # int — tokens removed
|
| 105 |
+
result.compression_ratio # float — 0.0 (no savings) to 1.0 (100% removed)
|
| 106 |
+
result.transforms_applied # list[str] — what ran (e.g., ["router:smart_crusher:0.35"])
|
| 107 |
+
```
|
| 108 |
+
|
| 109 |
+
---
|
| 110 |
+
|
| 111 |
+
## LiteLLM
|
| 112 |
+
|
| 113 |
+
If you're already using LiteLLM as your LLM gateway, add Headroom as a callback:
|
| 114 |
+
|
| 115 |
+
```python
|
| 116 |
+
import litellm
|
| 117 |
+
from headroom.integrations.litellm_callback import HeadroomCallback
|
| 118 |
+
|
| 119 |
+
litellm.callbacks = [HeadroomCallback()]
|
| 120 |
+
|
| 121 |
+
# All calls now compressed automatically
|
| 122 |
+
response = litellm.completion(model="gpt-4o", messages=[...])
|
| 123 |
+
response = litellm.completion(model="bedrock/claude-sonnet", messages=[...])
|
| 124 |
+
response = litellm.completion(model="azure/gpt-4o", messages=[...])
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
The callback compresses messages in LiteLLM's `pre_call_hook` before they're sent to the provider. Works with all 100+ LiteLLM-supported providers.
|
| 128 |
+
|
| 129 |
+
### With LiteLLM Proxy
|
| 130 |
+
|
| 131 |
+
If you run LiteLLM as a proxy server, use the ASGI middleware instead:
|
| 132 |
+
|
| 133 |
+
```python
|
| 134 |
+
# In your LiteLLM proxy startup
|
| 135 |
+
from litellm.proxy.proxy_server import app
|
| 136 |
+
from headroom.integrations.asgi import CompressionMiddleware
|
| 137 |
+
|
| 138 |
+
app.add_middleware(CompressionMiddleware)
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
Or use the callback in your LiteLLM config:
|
| 142 |
+
|
| 143 |
+
```yaml
|
| 144 |
+
# litellm_config.yaml
|
| 145 |
+
litellm_settings:
|
| 146 |
+
callbacks: ["headroom.integrations.litellm_callback.HeadroomCallback"]
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
---
|
| 150 |
+
|
| 151 |
+
## ASGI Middleware
|
| 152 |
+
|
| 153 |
+
Drop-in middleware for any ASGI application (FastAPI, Starlette, LiteLLM proxy, custom proxies).
|
| 154 |
+
|
| 155 |
+
```python
|
| 156 |
+
from headroom.integrations.asgi import CompressionMiddleware
|
| 157 |
+
|
| 158 |
+
# FastAPI
|
| 159 |
+
app = FastAPI()
|
| 160 |
+
app.add_middleware(CompressionMiddleware)
|
| 161 |
+
|
| 162 |
+
# Starlette
|
| 163 |
+
app = Starlette(routes=[...])
|
| 164 |
+
app.add_middleware(CompressionMiddleware)
|
| 165 |
+
|
| 166 |
+
# LiteLLM proxy
|
| 167 |
+
from litellm.proxy.proxy_server import app
|
| 168 |
+
app.add_middleware(CompressionMiddleware)
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
The middleware intercepts POST requests to `/v1/messages`, `/v1/chat/completions`, `/v1/responses`, and `/chat/completions`. All other requests pass through untouched.
|
| 172 |
+
|
| 173 |
+
Response headers include:
|
| 174 |
+
- `x-headroom-compressed: true` — compression was applied
|
| 175 |
+
- `x-headroom-tokens-saved: 1234` — tokens removed
|
| 176 |
+
|
| 177 |
+
---
|
| 178 |
+
|
| 179 |
+
## Proxy
|
| 180 |
+
|
| 181 |
+
The Headroom proxy is a standalone HTTP server. Best for non-Python apps or tools that only support base URL configuration (Claude Code, Cursor).
|
| 182 |
+
|
| 183 |
+
```bash
|
| 184 |
+
pip install "headroom-ai[all]"
|
| 185 |
+
headroom proxy --port 8787
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
```bash
|
| 189 |
+
# Claude Code
|
| 190 |
+
ANTHROPIC_BASE_URL=http://localhost:8787 claude
|
| 191 |
+
|
| 192 |
+
# Cursor / Any OpenAI client
|
| 193 |
+
OPENAI_BASE_URL=http://localhost:8787/v1 cursor
|
| 194 |
+
```
|
| 195 |
+
|
| 196 |
+
### With Cloud Providers
|
| 197 |
+
|
| 198 |
+
```bash
|
| 199 |
+
# AWS Bedrock
|
| 200 |
+
headroom proxy --backend bedrock --region us-east-1
|
| 201 |
+
|
| 202 |
+
# Google Vertex AI
|
| 203 |
+
headroom proxy --backend vertex_ai --region us-central1
|
| 204 |
+
|
| 205 |
+
# Azure OpenAI
|
| 206 |
+
headroom proxy --backend azure
|
| 207 |
+
|
| 208 |
+
# OpenRouter (400+ models)
|
| 209 |
+
OPENROUTER_API_KEY=sk-or-... headroom proxy --backend openrouter
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
See [Proxy Documentation](proxy.md) for all options.
|
| 213 |
+
|
| 214 |
+
---
|
| 215 |
+
|
| 216 |
+
## Agno
|
| 217 |
+
|
| 218 |
+
Full integration with the Agno agent framework.
|
| 219 |
+
|
| 220 |
+
```python
|
| 221 |
+
from agno.agent import Agent
|
| 222 |
+
from agno.models.anthropic import Claude
|
| 223 |
+
from headroom.integrations.agno import HeadroomAgnoModel
|
| 224 |
+
|
| 225 |
+
model = HeadroomAgnoModel(Claude(id="claude-sonnet-4-20250514"))
|
| 226 |
+
agent = Agent(model=model, tools=[your_tools])
|
| 227 |
+
response = agent.run("Investigate the issue")
|
| 228 |
+
|
| 229 |
+
print(f"Tokens saved: {model.total_tokens_saved}")
|
| 230 |
+
```
|
| 231 |
+
|
| 232 |
+
See [Agno Guide](agno.md) for hooks, multi-provider, and streaming.
|
| 233 |
+
|
| 234 |
+
---
|
| 235 |
+
|
| 236 |
+
## LangChain
|
| 237 |
+
|
| 238 |
+
> **Experimental.** Core compression works. Streaming callbacks and async chains are still being tested.
|
| 239 |
+
|
| 240 |
+
```python
|
| 241 |
+
from langchain_openai import ChatOpenAI
|
| 242 |
+
from headroom.integrations import HeadroomChatModel
|
| 243 |
+
|
| 244 |
+
llm = HeadroomChatModel(ChatOpenAI(model="gpt-4o"))
|
| 245 |
+
response = llm.invoke("Hello!")
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
See [LangChain Guide](langchain.md) for details and known limitations.
|
| 249 |
+
|
| 250 |
+
---
|
| 251 |
+
|
| 252 |
+
## Compression Hooks (Advanced)
|
| 253 |
+
|
| 254 |
+
Customize compression behavior without modifying Headroom's code:
|
| 255 |
+
|
| 256 |
+
```python
|
| 257 |
+
from headroom import compress, CompressionHooks, CompressContext
|
| 258 |
+
|
| 259 |
+
class MyHooks(CompressionHooks):
|
| 260 |
+
def pre_compress(self, messages, ctx):
|
| 261 |
+
# Modify messages before compression (dedup, filter, inject)
|
| 262 |
+
return messages
|
| 263 |
+
|
| 264 |
+
def compute_biases(self, messages, ctx):
|
| 265 |
+
# Per-message compression aggressiveness
|
| 266 |
+
# >1.0 = keep more, <1.0 = compress more
|
| 267 |
+
return {5: 1.5, 6: 0.5} # Keep message 5, compress message 6
|
| 268 |
+
|
| 269 |
+
def post_compress(self, event):
|
| 270 |
+
# Observe results (logging, analytics, learning)
|
| 271 |
+
print(f"Saved {event.tokens_saved} tokens")
|
| 272 |
+
|
| 273 |
+
result = compress(messages, model="gpt-4o", hooks=MyHooks())
|
| 274 |
+
```
|
| 275 |
+
|
| 276 |
+
See [Architecture](ARCHITECTURE.md) for how hooks integrate with the pipeline.
|
| 277 |
+
|
| 278 |
+
---
|
| 279 |
+
|
| 280 |
+
## FAQ
|
| 281 |
+
|
| 282 |
+
**Q: Does Headroom change the response format?**
|
| 283 |
+
No. Your LLM returns the same response format. Headroom only modifies the input messages.
|
| 284 |
+
|
| 285 |
+
**Q: What if compression removes something the LLM needs?**
|
| 286 |
+
Headroom stores originals in CCR (Compress-Cache-Retrieve). The LLM can call `headroom_retrieve` to get full uncompressed content. Compression summaries tell the LLM what's available.
|
| 287 |
+
|
| 288 |
+
**Q: Does it work with streaming?**
|
| 289 |
+
Yes. Compression happens before the request is sent. Streaming responses are unaffected.
|
| 290 |
+
|
| 291 |
+
**Q: How much latency does it add?**
|
| 292 |
+
1-5ms for compression. The token savings typically save more time on the LLM side than compression adds.
|
|
@@ -183,17 +183,13 @@ def _get_pipeline() -> Any:
|
|
| 183 |
if _pipeline is not None:
|
| 184 |
return _pipeline
|
| 185 |
|
| 186 |
-
from headroom.transforms import
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
# auto-detects the right tokenizer per model:
|
| 195 |
-
# OpenAI → tiktoken (exact), Anthropic → calibrated estimation,
|
| 196 |
-
# Open models → HuggingFace (if installed)
|
| 197 |
-
)
|
| 198 |
logger.debug("Headroom compression pipeline initialized")
|
| 199 |
return _pipeline
|
|
|
|
| 183 |
if _pipeline is not None:
|
| 184 |
return _pipeline
|
| 185 |
|
| 186 |
+
from headroom.transforms import TransformPipeline
|
| 187 |
+
|
| 188 |
+
# Default pipeline: CacheAligner → ContentRouter → IntelligentContext
|
| 189 |
+
# CacheAligner: stabilizes prefix for provider KV cache hits
|
| 190 |
+
# ContentRouter: routes to the right compressor per content type
|
| 191 |
+
# (SmartCrusher for JSON, CodeCompressor for code, LLMLingua for text)
|
| 192 |
+
# IntelligentContext: enforces token limits with score-based dropping
|
| 193 |
+
_pipeline = TransformPipeline()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
logger.debug("Headroom compression pipeline initialized")
|
| 195 |
return _pipeline
|