chopratejas commited on
Commit
e4a41fa
·
1 Parent(s): 55814fe

Fix all ruff lint and format errors for CI

Browse files

- Fix E402: Move module-level imports to top of file
- Fix F401: Add noqa for availability check imports
- Fix F402: Rename loop variables shadowing imports
- Fix E722: Replace bare except with except Exception
- Fix B904: Add exception chaining (from e)
- Fix F811: Remove duplicate imports
- Fix B027: Add noqa for empty close() method
- Fix E741: Rename ambiguous variable l -> label
- Fix I001: Import sorting issues
- Apply ruff format to all 106 files

All 902 tests pass.

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. benchmarks/__init__.py +4 -4
  2. benchmarks/adversarial_ccr_tests.py +1939 -0
  3. benchmarks/agent_cost_benchmark.py +804 -0
  4. benchmarks/bench_relevance.py +5 -5
  5. benchmarks/bench_transforms.py +19 -7
  6. benchmarks/ccr_regression_benchmark.py +828 -0
  7. benchmarks/conftest.py +13 -9
  8. benchmarks/dynamic_detector_benchmark.py +41 -33
  9. benchmarks/run_benchmarks.py +17 -18
  10. benchmarks/scenarios/__init__.py +4 -4
  11. benchmarks/scenarios/conversations.py +120 -81
  12. benchmarks/scenarios/tool_outputs.py +22 -15
  13. docs/HEADROOM_DEEP_ANALYSIS.md +914 -0
  14. docs/HEADROOM_FEATURES.md +891 -0
  15. docs/PATH_TO_10_OUT_OF_10.md +661 -0
  16. examples/anthropic_example.py +6 -2
  17. examples/langchain_before_after.py +63 -44
  18. examples/langchain_demo/mock_tools.py +43 -30
  19. examples/langchain_demo/run_comparison.py +67 -35
  20. examples/langchain_demo/show_compression.py +70 -44
  21. examples/langchain_demo/verify_errors_kept.py +18 -9
  22. examples/mcp_demo/mock_mcp_servers.py +120 -90
  23. examples/mcp_demo/run_agent_eval.py +172 -76
  24. examples/mcp_demo/show_before_after.py +10 -10
  25. examples/mcp_demo/show_compression.py +4 -6
  26. examples/real_world_eval.py +210 -156
  27. examples/real_world_openai_eval.py +343 -266
  28. examples/smart_vs_naive_eval.py +96 -82
  29. headroom/cache/__init__.py +4 -4
  30. headroom/cache/anthropic.py +37 -38
  31. headroom/cache/base.py +8 -5
  32. headroom/cache/compression_feedback.py +38 -29
  33. headroom/cache/compression_store.py +18 -28
  34. headroom/cache/dynamic_detector.py +200 -134
  35. headroom/cache/google.py +11 -23
  36. headroom/cache/openai.py +8 -15
  37. headroom/cache/registry.py +6 -12
  38. headroom/cache/semantic.py +6 -3
  39. headroom/ccr/__init__.py +1 -0
  40. headroom/ccr/mcp_server.py +28 -22
  41. headroom/ccr/tool_injection.py +13 -8
  42. headroom/cli.py +8 -5
  43. headroom/client.py +17 -28
  44. headroom/config.py +3 -1
  45. headroom/exceptions.py +8 -0
  46. headroom/integrations/langchain.py +82 -60
  47. headroom/integrations/mcp.py +11 -6
  48. headroom/models/registry.py +1 -1
  49. headroom/pricing/registry.py +2 -0
  50. headroom/providers/anthropic.py +12 -12
benchmarks/__init__.py CHANGED
@@ -23,16 +23,16 @@ Performance Targets:
23
 
24
  __version__ = "0.2.0"
25
 
 
 
 
 
26
  from .scenarios.tool_outputs import (
27
  generate_api_responses,
28
  generate_database_rows,
29
  generate_log_entries,
30
  generate_search_results,
31
  )
32
- from .scenarios.conversations import (
33
- generate_agentic_conversation,
34
- generate_rag_conversation,
35
- )
36
 
37
  __all__ = [
38
  # Data generators
 
23
 
24
  __version__ = "0.2.0"
25
 
26
+ from .scenarios.conversations import (
27
+ generate_agentic_conversation,
28
+ generate_rag_conversation,
29
+ )
30
  from .scenarios.tool_outputs import (
31
  generate_api_responses,
32
  generate_database_rows,
33
  generate_log_entries,
34
  generate_search_results,
35
  )
 
 
 
 
36
 
37
  __all__ = [
38
  # Data generators
benchmarks/adversarial_ccr_tests.py ADDED
@@ -0,0 +1,1939 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Adversarial CCR Tests - Designed to BREAK Our Assumptions
4
+
5
+ These tests are intentionally malicious, edge-casey, and designed to expose
6
+ weaknesses in our compression and retrieval logic.
7
+
8
+ Categories:
9
+ 1. SEMANTIC ATTACKS: Data that tricks our heuristics
10
+ 2. BOUNDARY CONDITIONS: Edge cases at limits
11
+ 3. INJECTION ATTACKS: Malformed data designed to break parsing
12
+ 4. RACE CONDITIONS: Concurrency attacks
13
+ 5. MEMORY PRESSURE: Resource exhaustion
14
+ 6. DECEPTIVE DATA: Items that look like one thing but are another
15
+
16
+ Run with: python benchmarks/adversarial_ccr_tests.py
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import concurrent.futures
22
+ import gc
23
+ import hashlib
24
+ import json
25
+ import random
26
+ import sys
27
+ import threading
28
+ import time
29
+ import uuid
30
+ from dataclasses import dataclass, field
31
+ from typing import Any
32
+
33
+ from headroom.cache.compression_feedback import (
34
+ get_compression_feedback,
35
+ reset_compression_feedback,
36
+ )
37
+ from headroom.cache.compression_store import (
38
+ CompressionStore,
39
+ RetrievalEvent,
40
+ get_compression_store,
41
+ reset_compression_store,
42
+ )
43
+ from headroom.transforms.smart_crusher import (
44
+ SmartCrusherConfig,
45
+ smart_crush_tool_output,
46
+ )
47
+
48
+
49
+ @dataclass
50
+ class AdversarialResult:
51
+ """Result from an adversarial test."""
52
+
53
+ name: str
54
+ category: str
55
+ passed: bool = False
56
+ expected_behavior: str = ""
57
+ actual_behavior: str = ""
58
+ severity: str = "medium" # low, medium, high, critical
59
+ details: dict[str, Any] = field(default_factory=dict)
60
+
61
+
62
+ def run_test(func) -> AdversarialResult:
63
+ """Run a test and catch any exceptions."""
64
+ try:
65
+ return func()
66
+ except Exception as e:
67
+ return AdversarialResult(
68
+ name=func.__name__,
69
+ category="exception",
70
+ passed=False,
71
+ expected_behavior="Test should complete without exception",
72
+ actual_behavior=f"Exception: {type(e).__name__}: {str(e)[:200]}",
73
+ severity="critical",
74
+ )
75
+
76
+
77
+ # =============================================================================
78
+ # CATEGORY 1: SEMANTIC ATTACKS
79
+ # =============================================================================
80
+
81
+
82
+ def test_all_items_are_errors() -> AdversarialResult:
83
+ """
84
+ ATTACK: Every single item is an error.
85
+
86
+ If we keep ALL errors, we keep everything = no compression.
87
+ What SHOULD happen? Keep all? Sample errors? Fail gracefully?
88
+ """
89
+ result = AdversarialResult(
90
+ name="All Items Are Errors",
91
+ category="semantic",
92
+ expected_behavior="Should handle gracefully, possibly skip compression",
93
+ severity="high",
94
+ )
95
+
96
+ # 1000 items, ALL are errors
97
+ items = [
98
+ {
99
+ "id": i,
100
+ "status": "error",
101
+ "error_code": 500 + (i % 50),
102
+ "message": f"Error at position {i}: something went wrong",
103
+ }
104
+ for i in range(1000)
105
+ ]
106
+
107
+ config = SmartCrusherConfig(max_items_after_crush=15)
108
+ original_json = json.dumps(items)
109
+
110
+ compressed_json, was_modified, reason = smart_crush_tool_output(original_json, config)
111
+ compressed = json.loads(compressed_json)
112
+
113
+ # What happened?
114
+ if len(compressed) == 1000:
115
+ result.actual_behavior = "Kept ALL 1000 items (no compression when all errors)"
116
+ result.passed = True # This is actually correct behavior!
117
+ elif len(compressed) == 15:
118
+ result.actual_behavior = f"Compressed to 15 items, lost {1000 - 15} errors!"
119
+ result.passed = False
120
+ else:
121
+ result.actual_behavior = f"Compressed to {len(compressed)} items"
122
+ result.passed = len(compressed) >= 100 # Should keep most errors
123
+
124
+ result.details = {
125
+ "original": 1000,
126
+ "compressed": len(compressed),
127
+ "reason": reason,
128
+ }
129
+
130
+ return result
131
+
132
+
133
+ def test_error_keyword_in_normal_data() -> AdversarialResult:
134
+ """
135
+ ATTACK: Normal items contain "error" keyword in benign context.
136
+
137
+ "The error rate for this metric is 0.001%" - NOT an error!
138
+ "Error handling documentation" - NOT an error!
139
+ """
140
+ result = AdversarialResult(
141
+ name="Error Keyword False Positive",
142
+ category="semantic",
143
+ expected_behavior="Should NOT treat benign 'error' mentions as errors",
144
+ severity="medium",
145
+ )
146
+
147
+ items = []
148
+ # 100 normal items with "error" in benign context
149
+ for i in range(100):
150
+ items.append(
151
+ {
152
+ "id": i,
153
+ "status": "success", # Clearly success!
154
+ "message": random.choice(
155
+ [
156
+ f"Error rate: 0.00{i}%",
157
+ f"Error handling improved by {i}%",
158
+ f"Zero errors detected in batch {i}",
159
+ f"Error-free operation for {i} hours",
160
+ "Documentation: How to handle errors",
161
+ ]
162
+ ),
163
+ "value": i,
164
+ }
165
+ )
166
+
167
+ # Add 3 REAL errors
168
+ real_error_ids = [25, 50, 75]
169
+ for idx in real_error_ids:
170
+ items[idx] = {
171
+ "id": idx,
172
+ "status": "error", # This is a REAL error
173
+ "message": f"CRITICAL: System failure at {idx}",
174
+ "error_code": 500,
175
+ }
176
+
177
+ config = SmartCrusherConfig(max_items_after_crush=15)
178
+ original_json = json.dumps(items)
179
+
180
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
181
+ compressed = json.loads(compressed_json)
182
+
183
+ # Count how many items with "error" in message were kept
184
+ items_with_error_word = len(
185
+ [item for item in compressed if "error" in str(item.get("message", "")).lower()]
186
+ )
187
+
188
+ # Count real errors kept
189
+ real_errors_kept = len([item for item in compressed if item.get("status") == "error"])
190
+
191
+ # False positives (keeping non-errors) are OK - conservative is good
192
+ # False negatives (missing real errors) are NOT OK
193
+ if real_errors_kept < 3:
194
+ result.actual_behavior = (
195
+ f"Only kept {real_errors_kept}/3 real errors - missed actual errors!"
196
+ )
197
+ result.passed = False
198
+ else:
199
+ # Keeping extra items with "error" word is fine - better safe than sorry
200
+ result.actual_behavior = f"Kept all {real_errors_kept} real errors (+ {items_with_error_word} with 'error' word - conservative is OK)"
201
+ result.passed = True
202
+
203
+ result.details = {
204
+ "total_compressed": len(compressed),
205
+ "real_errors_kept": real_errors_kept,
206
+ "items_with_error_word": items_with_error_word,
207
+ }
208
+
209
+ return result
210
+
211
+
212
+ def test_needle_looks_exactly_like_hay() -> AdversarialResult:
213
+ """
214
+ ATTACK: The critical item has NO distinguishing features.
215
+
216
+ In a list of 1000 users, user #456 is the one we need.
217
+ User #456 looks EXACTLY like every other user.
218
+ """
219
+ result = AdversarialResult(
220
+ name="Needle Identical to Hay",
221
+ category="semantic",
222
+ expected_behavior="CCR retrieval should still find specific item by ID",
223
+ severity="high",
224
+ )
225
+
226
+ reset_compression_store()
227
+ store = get_compression_store()
228
+
229
+ # 1000 identical-looking users
230
+ target_id = 456
231
+ items = [
232
+ {
233
+ "user_id": i,
234
+ "name": f"User {i}",
235
+ "status": "active",
236
+ "created": "2025-01-01",
237
+ }
238
+ for i in range(1000)
239
+ ]
240
+
241
+ original_json = json.dumps(items)
242
+ config = SmartCrusherConfig(max_items_after_crush=15)
243
+
244
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
245
+
246
+ # Store for CCR
247
+ hash_key = store.store(
248
+ original=original_json,
249
+ compressed=compressed_json,
250
+ original_item_count=1000,
251
+ compressed_item_count=15,
252
+ tool_name="user_search",
253
+ )
254
+
255
+ # Try to find user 456 via search
256
+ search_results = store.search(hash_key, "user_id 456")
257
+
258
+ found_target = any(item.get("user_id") == target_id for item in search_results)
259
+
260
+ if found_target:
261
+ result.actual_behavior = "Found target user via CCR search"
262
+ result.passed = True
263
+ else:
264
+ # Try full retrieval as fallback
265
+ entry = store.retrieve(hash_key)
266
+ if entry:
267
+ all_items = json.loads(entry.original_content)
268
+ target_in_original = any(item.get("user_id") == target_id for item in all_items)
269
+ if target_in_original:
270
+ result.actual_behavior = "Search failed, but full retrieval works"
271
+ result.passed = True # CCR still provides recovery path
272
+ else:
273
+ result.actual_behavior = "Data lost entirely!"
274
+ result.passed = False
275
+ else:
276
+ result.actual_behavior = "CCR cache miss - data not found"
277
+ result.passed = False
278
+
279
+ result.details = {
280
+ "target_id": target_id,
281
+ "search_results": len(search_results),
282
+ "found_target": found_target,
283
+ }
284
+
285
+ return result
286
+
287
+
288
+ def test_anomaly_in_string_not_number() -> AdversarialResult:
289
+ """
290
+ ATTACK: Anomaly is in a string field, not numeric.
291
+
292
+ 999 items: region="us-east-1"
293
+ 1 item: region="DEPRECATED-DO-NOT-USE"
294
+
295
+ SmartCrusher detects numeric anomalies, but what about string outliers?
296
+ """
297
+ result = AdversarialResult(
298
+ name="String Anomaly Detection",
299
+ category="semantic",
300
+ expected_behavior="Should detect or preserve string outliers",
301
+ severity="medium",
302
+ )
303
+
304
+ items = []
305
+ anomaly_idx = 500
306
+
307
+ for i in range(1000):
308
+ if i == anomaly_idx:
309
+ items.append(
310
+ {
311
+ "id": i,
312
+ "region": "DEPRECATED-DO-NOT-USE-CRITICAL-MIGRATION-REQUIRED",
313
+ "status": "active",
314
+ }
315
+ )
316
+ else:
317
+ items.append(
318
+ {
319
+ "id": i,
320
+ "region": "us-east-1",
321
+ "status": "active",
322
+ }
323
+ )
324
+
325
+ config = SmartCrusherConfig(max_items_after_crush=20)
326
+ original_json = json.dumps(items)
327
+
328
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
329
+ compressed = json.loads(compressed_json)
330
+
331
+ # Check if anomaly was preserved
332
+ anomaly_preserved = any("DEPRECATED" in str(item.get("region", "")) for item in compressed)
333
+
334
+ if anomaly_preserved:
335
+ result.actual_behavior = "String anomaly was preserved"
336
+ result.passed = True
337
+ else:
338
+ result.actual_behavior = "String anomaly was LOST - only numeric anomalies detected"
339
+ result.passed = False
340
+
341
+ result.details = {
342
+ "compressed_count": len(compressed),
343
+ "anomaly_preserved": anomaly_preserved,
344
+ }
345
+
346
+ return result
347
+
348
+
349
+ # =============================================================================
350
+ # CATEGORY 2: BOUNDARY CONDITIONS
351
+ # =============================================================================
352
+
353
+
354
+ def test_empty_array() -> AdversarialResult:
355
+ """
356
+ ATTACK: Empty array input.
357
+ """
358
+ result = AdversarialResult(
359
+ name="Empty Array",
360
+ category="boundary",
361
+ expected_behavior="Should return empty array unchanged",
362
+ severity="low",
363
+ )
364
+
365
+ config = SmartCrusherConfig()
366
+ compressed_json, was_modified, reason = smart_crush_tool_output("[]", config)
367
+
368
+ if compressed_json == "[]" and not was_modified:
369
+ result.actual_behavior = "Correctly handled empty array"
370
+ result.passed = True
371
+ else:
372
+ result.actual_behavior = f"Unexpected result: {compressed_json[:100]}"
373
+ result.passed = False
374
+
375
+ return result
376
+
377
+
378
+ def test_single_item_array() -> AdversarialResult:
379
+ """
380
+ ATTACK: Array with exactly 1 item.
381
+ """
382
+ result = AdversarialResult(
383
+ name="Single Item Array",
384
+ category="boundary",
385
+ expected_behavior="Should return single item unchanged",
386
+ severity="low",
387
+ )
388
+
389
+ items = [{"id": 1, "value": "only_one"}]
390
+ config = SmartCrusherConfig()
391
+
392
+ compressed_json, was_modified, _ = smart_crush_tool_output(json.dumps(items), config)
393
+ compressed = json.loads(compressed_json)
394
+
395
+ if len(compressed) == 1 and compressed[0].get("id") == 1:
396
+ result.actual_behavior = "Single item preserved"
397
+ result.passed = True
398
+ else:
399
+ result.actual_behavior = f"Unexpected: {len(compressed)} items"
400
+ result.passed = False
401
+
402
+ return result
403
+
404
+
405
+ def test_exactly_max_items() -> AdversarialResult:
406
+ """
407
+ ATTACK: Array with exactly max_items_after_crush items.
408
+ """
409
+ result = AdversarialResult(
410
+ name="Exactly Max Items",
411
+ category="boundary",
412
+ expected_behavior="Should not compress when at exact limit",
413
+ severity="low",
414
+ )
415
+
416
+ config = SmartCrusherConfig(max_items_after_crush=15)
417
+ items = [{"id": i} for i in range(15)] # Exactly 15
418
+
419
+ compressed_json, was_modified, _ = smart_crush_tool_output(json.dumps(items), config)
420
+ compressed = json.loads(compressed_json)
421
+
422
+ if len(compressed) == 15:
423
+ result.actual_behavior = "Kept all 15 items as expected"
424
+ result.passed = True
425
+ else:
426
+ result.actual_behavior = f"Changed count: {len(compressed)}"
427
+ result.passed = False
428
+
429
+ return result
430
+
431
+
432
+ def test_max_items_plus_one() -> AdversarialResult:
433
+ """
434
+ ATTACK: Array with max_items + 1.
435
+
436
+ IMPORTANT: If data has high uniqueness and no importance signal,
437
+ crushability analysis correctly skips compression to avoid data loss.
438
+ This is the RIGHT behavior - don't blindly compress unique entities.
439
+ """
440
+ result = AdversarialResult(
441
+ name="Max Items Plus One",
442
+ category="boundary",
443
+ expected_behavior="Skip compression for unique entities OR compress with signal",
444
+ severity="low",
445
+ )
446
+
447
+ config = SmartCrusherConfig(max_items_after_crush=15, min_items_to_analyze=5)
448
+ # Create items WITH a score field so compression can determine importance
449
+ items = [{"id": i, "value": f"item_{i}", "score": 1.0 - (i / 100)} for i in range(16)]
450
+
451
+ compressed_json, was_modified, reason = smart_crush_tool_output(json.dumps(items), config)
452
+ compressed = json.loads(compressed_json)
453
+
454
+ result.actual_behavior = f"Compressed to {len(compressed)} items ({reason})"
455
+ # With a score signal, we should compress to max_items
456
+ result.passed = len(compressed) <= 15
457
+
458
+ return result
459
+
460
+
461
+ def test_hash_collision_attempt() -> AdversarialResult:
462
+ """
463
+ ATTACK: Try to create hash collisions in CCR store.
464
+
465
+ We use SHA256[:16] - what if two different contents hash the same?
466
+ """
467
+ result = AdversarialResult(
468
+ name="Hash Collision Attack",
469
+ category="boundary",
470
+ expected_behavior="Different content should not collide",
471
+ severity="high",
472
+ )
473
+
474
+ reset_compression_store()
475
+ get_compression_store()
476
+
477
+ # Store many different contents
478
+ hashes = set()
479
+ collisions = 0
480
+
481
+ for i in range(10000):
482
+ content = json.dumps([{"unique_id": str(uuid.uuid4()), "index": i}])
483
+ content_hash = hashlib.sha256(content.encode()).hexdigest()[:16]
484
+
485
+ if content_hash in hashes:
486
+ collisions += 1
487
+ hashes.add(content_hash)
488
+
489
+ if collisions == 0:
490
+ result.actual_behavior = "No collisions in 10,000 entries"
491
+ result.passed = True
492
+ else:
493
+ result.actual_behavior = f"Found {collisions} hash collisions!"
494
+ result.passed = False
495
+ result.severity = "critical"
496
+
497
+ result.details = {"entries_tested": 10000, "collisions": collisions}
498
+
499
+ return result
500
+
501
+
502
+ def test_ttl_exact_boundary() -> AdversarialResult:
503
+ """
504
+ ATTACK: Retrieve at exact TTL expiration moment.
505
+ """
506
+ result = AdversarialResult(
507
+ name="TTL Exact Boundary",
508
+ category="boundary",
509
+ expected_behavior="Entry should expire cleanly at TTL",
510
+ severity="medium",
511
+ )
512
+
513
+ reset_compression_store()
514
+ store = CompressionStore(default_ttl=1) # 1 second TTL
515
+
516
+ hash_key = store.store(
517
+ original='[{"id": 1}]',
518
+ compressed='[{"id": 1}]',
519
+ original_item_count=1,
520
+ compressed_item_count=1,
521
+ )
522
+
523
+ # Should exist immediately
524
+ exists_before = store.exists(hash_key)
525
+
526
+ # Wait exactly at boundary
527
+ time.sleep(1.05)
528
+
529
+ # Should be expired
530
+ exists_after = store.exists(hash_key)
531
+ entry = store.retrieve(hash_key)
532
+
533
+ if exists_before and not exists_after and entry is None:
534
+ result.actual_behavior = "TTL expiration works correctly"
535
+ result.passed = True
536
+ else:
537
+ result.actual_behavior = (
538
+ f"Before: {exists_before}, After: {exists_after}, Entry: {entry is not None}"
539
+ )
540
+ result.passed = False
541
+
542
+ return result
543
+
544
+
545
+ # =============================================================================
546
+ # CATEGORY 3: INJECTION ATTACKS
547
+ # =============================================================================
548
+
549
+
550
+ def test_json_injection_in_content() -> AdversarialResult:
551
+ """
552
+ ATTACK: JSON that tries to break our parsing.
553
+ """
554
+ result = AdversarialResult(
555
+ name="JSON Injection",
556
+ category="injection",
557
+ expected_behavior="Should handle malformed JSON gracefully",
558
+ severity="high",
559
+ )
560
+
561
+ # Various injection attempts
562
+ injections = [
563
+ '{"id": 1, "evil": "}\\"]}', # Quote escape
564
+ '[{"id": 1}, null, {"id": 2}]', # Null in array
565
+ '[{"id": 1, "__proto__": {"admin": true}}]', # Prototype pollution
566
+ '[{"id": 1, "nested": {"deep": {"deeper": {"deepest": "value"}}}}]',
567
+ ]
568
+
569
+ config = SmartCrusherConfig()
570
+ failures = []
571
+
572
+ for injection in injections:
573
+ try:
574
+ compressed, was_modified, _ = smart_crush_tool_output(injection, config)
575
+ # If it returns, it handled it
576
+ except Exception as e:
577
+ failures.append(f"{injection[:30]}: {type(e).__name__}")
578
+
579
+ if not failures:
580
+ result.actual_behavior = "All injection attempts handled gracefully"
581
+ result.passed = True
582
+ else:
583
+ result.actual_behavior = f"Failures: {failures}"
584
+ result.passed = False
585
+
586
+ return result
587
+
588
+
589
+ def test_headroom_marker_collision() -> AdversarialResult:
590
+ """
591
+ ATTACK: Input data already contains __headroom_ fields.
592
+ """
593
+ result = AdversarialResult(
594
+ name="Marker Field Collision",
595
+ category="injection",
596
+ expected_behavior="Should not confuse existing __headroom_ fields with our markers",
597
+ severity="high",
598
+ )
599
+
600
+ # Data that already has __headroom_ fields
601
+ items = [
602
+ {
603
+ "id": i,
604
+ "__headroom_compressed": True, # Fake marker!
605
+ "__headroom_hash": "fakehash12345678",
606
+ "__headroom_stats": {"fake": True},
607
+ }
608
+ for i in range(100)
609
+ ]
610
+
611
+ config = SmartCrusherConfig(max_items_after_crush=15)
612
+ original_json = json.dumps(items)
613
+
614
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
615
+ compressed = json.loads(compressed_json)
616
+
617
+ # Check if our compression worked despite fake markers
618
+ if isinstance(compressed, list) and len(compressed) <= 20:
619
+ result.actual_behavior = "Compression worked despite fake markers"
620
+ result.passed = True
621
+ else:
622
+ result.actual_behavior = f"Unexpected result type or length: {type(compressed)}, {len(compressed) if isinstance(compressed, list) else 'N/A'}"
623
+ result.passed = False
624
+
625
+ return result
626
+
627
+
628
+ def test_unicode_and_emoji_handling() -> AdversarialResult:
629
+ """
630
+ ATTACK: Unicode edge cases in content.
631
+ """
632
+ result = AdversarialResult(
633
+ name="Unicode/Emoji Handling",
634
+ category="injection",
635
+ expected_behavior="Should handle Unicode correctly",
636
+ severity="medium",
637
+ )
638
+
639
+ items = [
640
+ {"id": 1, "message": "Error: 🔥 Server on fire 🔥", "status": "error"},
641
+ {"id": 2, "message": "成功: 操作完��", "status": "success"},
642
+ {"id": 3, "message": "Error: \u0000\u0001\u0002 null bytes", "status": "error"},
643
+ {"id": 4, "message": "Ошибка: критический сбой", "status": "error"},
644
+ {"id": 5, "message": "🎉🎊🎈" * 100, "status": "success"}, # Lots of emoji
645
+ ]
646
+
647
+ for i in range(95):
648
+ items.append({"id": i + 6, "message": "Normal", "status": "success"})
649
+
650
+ config = SmartCrusherConfig(max_items_after_crush=15)
651
+ original_json = json.dumps(items, ensure_ascii=False)
652
+
653
+ try:
654
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
655
+ compressed = json.loads(compressed_json)
656
+
657
+ # Check if error items with unicode were preserved
658
+ errors_preserved = len([item for item in compressed if item.get("status") == "error"])
659
+
660
+ result.actual_behavior = f"Handled Unicode, {errors_preserved} errors preserved"
661
+ result.passed = errors_preserved >= 2
662
+
663
+ except Exception as e:
664
+ result.actual_behavior = f"Unicode handling failed: {e}"
665
+ result.passed = False
666
+
667
+ return result
668
+
669
+
670
+ def test_extremely_long_strings() -> AdversarialResult:
671
+ """
672
+ ATTACK: Items with extremely long string values.
673
+ """
674
+ result = AdversarialResult(
675
+ name="Extremely Long Strings",
676
+ category="injection",
677
+ expected_behavior="Should handle without memory issues",
678
+ severity="medium",
679
+ )
680
+
681
+ # One item with a 10MB string
682
+ huge_string = "x" * (10 * 1024 * 1024) # 10MB
683
+
684
+ items = [
685
+ {"id": 0, "huge": huge_string, "status": "error"}, # Should be kept (error)
686
+ *[{"id": i, "normal": "small"} for i in range(1, 100)],
687
+ ]
688
+
689
+ config = SmartCrusherConfig(max_items_after_crush=15)
690
+
691
+ sys.getsizeof(items)
692
+ start_time = time.time()
693
+
694
+ try:
695
+ original_json = json.dumps(items)
696
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
697
+
698
+ elapsed = time.time() - start_time
699
+
700
+ if elapsed > 30:
701
+ result.actual_behavior = f"Took too long: {elapsed:.1f}s"
702
+ result.passed = False
703
+ else:
704
+ result.actual_behavior = f"Handled 10MB string in {elapsed:.1f}s"
705
+ result.passed = True
706
+
707
+ except MemoryError:
708
+ result.actual_behavior = "MemoryError on large string"
709
+ result.passed = False
710
+ result.severity = "critical"
711
+ finally:
712
+ del huge_string
713
+ del items
714
+ gc.collect()
715
+
716
+ return result
717
+
718
+
719
+ def test_query_injection_in_search() -> AdversarialResult:
720
+ """
721
+ ATTACK: Malicious search query.
722
+ """
723
+ result = AdversarialResult(
724
+ name="Search Query Injection",
725
+ category="injection",
726
+ expected_behavior="Should sanitize search queries",
727
+ severity="high",
728
+ )
729
+
730
+ reset_compression_store()
731
+ store = get_compression_store()
732
+
733
+ items = [{"id": i, "data": f"item {i}"} for i in range(100)]
734
+
735
+ hash_key = store.store(
736
+ original=json.dumps(items),
737
+ compressed=json.dumps(items[:10]),
738
+ original_item_count=100,
739
+ compressed_item_count=10,
740
+ )
741
+
742
+ # Various injection attempts
743
+ malicious_queries = [
744
+ "'; DROP TABLE items; --",
745
+ "<script>alert('xss')</script>",
746
+ "{{7*7}}", # Template injection
747
+ "${7*7}", # Expression injection
748
+ "\\x00\\x01\\x02", # Null bytes
749
+ "*" * 10000, # Long query
750
+ ".*", # Regex wildcard
751
+ "(a]", # Invalid regex
752
+ ]
753
+
754
+ failures = []
755
+ for query in malicious_queries:
756
+ try:
757
+ store.search(hash_key, query)
758
+ # If it returns without error, it handled the injection
759
+ except Exception as e:
760
+ failures.append(f"{query[:20]}: {type(e).__name__}")
761
+
762
+ if not failures:
763
+ result.actual_behavior = "All malicious queries handled safely"
764
+ result.passed = True
765
+ else:
766
+ result.actual_behavior = f"Failures: {failures}"
767
+ result.passed = False
768
+
769
+ return result
770
+
771
+
772
+ # =============================================================================
773
+ # CATEGORY 4: RACE CONDITIONS
774
+ # =============================================================================
775
+
776
+
777
+ def test_concurrent_store_same_content() -> AdversarialResult:
778
+ """
779
+ ATTACK: Multiple threads storing identical content simultaneously.
780
+ """
781
+ result = AdversarialResult(
782
+ name="Concurrent Store Same Content",
783
+ category="race",
784
+ expected_behavior="Should handle concurrent stores without data corruption",
785
+ severity="high",
786
+ )
787
+
788
+ reset_compression_store()
789
+ store = get_compression_store()
790
+
791
+ content = json.dumps([{"id": i} for i in range(100)])
792
+
793
+ results = []
794
+ errors = []
795
+
796
+ def store_content():
797
+ try:
798
+ hash_key = store.store(
799
+ original=content,
800
+ compressed=content[:50],
801
+ original_item_count=100,
802
+ compressed_item_count=5,
803
+ )
804
+ results.append(hash_key)
805
+ except Exception as e:
806
+ errors.append(str(e))
807
+
808
+ # 100 concurrent stores of same content
809
+ with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
810
+ futures = [executor.submit(store_content) for _ in range(100)]
811
+ concurrent.futures.wait(futures)
812
+
813
+ if errors:
814
+ result.actual_behavior = f"Errors during concurrent store: {errors[:3]}"
815
+ result.passed = False
816
+ elif len(set(results)) != 1:
817
+ result.actual_behavior = f"Got different hashes for same content: {set(results)}"
818
+ result.passed = False
819
+ else:
820
+ result.actual_behavior = "All concurrent stores returned same hash"
821
+ result.passed = True
822
+
823
+ return result
824
+
825
+
826
+ def test_concurrent_store_and_evict() -> AdversarialResult:
827
+ """
828
+ ATTACK: Store while eviction is happening.
829
+ """
830
+ result = AdversarialResult(
831
+ name="Concurrent Store and Evict",
832
+ category="race",
833
+ expected_behavior="Eviction should not corrupt concurrent stores",
834
+ severity="high",
835
+ )
836
+
837
+ reset_compression_store()
838
+ store = CompressionStore(max_entries=10) # Small capacity
839
+
840
+ errors = []
841
+ stored_hashes = []
842
+
843
+ def rapid_store(thread_id):
844
+ for i in range(50):
845
+ try:
846
+ content = json.dumps([{"thread": thread_id, "iteration": i}])
847
+ hash_key = store.store(
848
+ original=content,
849
+ compressed=content,
850
+ original_item_count=1,
851
+ compressed_item_count=1,
852
+ )
853
+ stored_hashes.append(hash_key)
854
+ except Exception as e:
855
+ errors.append(f"Thread {thread_id}, iter {i}: {e}")
856
+
857
+ # 10 threads, each storing 50 items = 500 stores with max_entries=10
858
+ with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
859
+ futures = [executor.submit(rapid_store, i) for i in range(10)]
860
+ concurrent.futures.wait(futures)
861
+
862
+ if errors:
863
+ result.actual_behavior = f"Errors: {errors[:5]}"
864
+ result.passed = False
865
+ else:
866
+ result.actual_behavior = "500 stores with capacity 10 succeeded"
867
+ result.passed = True
868
+
869
+ return result
870
+
871
+
872
+ def test_concurrent_feedback_updates() -> AdversarialResult:
873
+ """
874
+ ATTACK: Multiple threads updating feedback simultaneously.
875
+ """
876
+ result = AdversarialResult(
877
+ name="Concurrent Feedback Updates",
878
+ category="race",
879
+ expected_behavior="Feedback counts should be accurate under concurrency",
880
+ severity="high",
881
+ )
882
+
883
+ reset_compression_feedback()
884
+ feedback = get_compression_feedback()
885
+
886
+ tool_name = "concurrent_test_tool"
887
+ expected_compressions = 1000
888
+ expected_retrievals = 500
889
+
890
+ def record_compressions():
891
+ for _ in range(expected_compressions // 10):
892
+ feedback.record_compression(tool_name, 100, 10)
893
+
894
+ def record_retrievals():
895
+ # 5 threads × 100 iterations = 500 retrievals
896
+ for i in range(expected_retrievals // 5):
897
+ event = RetrievalEvent(
898
+ hash=f"hash{i:012d}",
899
+ query=None,
900
+ items_retrieved=100,
901
+ total_items=100,
902
+ tool_name=tool_name,
903
+ timestamp=time.time(),
904
+ retrieval_type="full",
905
+ )
906
+ feedback.record_retrieval(event)
907
+
908
+ # 10 threads each doing compressions (1000/10=100 each), 5 doing retrievals (500/5=100 each)
909
+ with concurrent.futures.ThreadPoolExecutor(max_workers=15) as executor:
910
+ futures = []
911
+ for _ in range(10):
912
+ futures.append(executor.submit(record_compressions))
913
+ for _ in range(5):
914
+ futures.append(executor.submit(record_retrievals))
915
+ concurrent.futures.wait(futures)
916
+
917
+ patterns = feedback.get_all_patterns()
918
+ pattern = patterns.get(tool_name)
919
+
920
+ if pattern is None:
921
+ result.actual_behavior = "Pattern not found"
922
+ result.passed = False
923
+ elif (
924
+ pattern.total_compressions == expected_compressions
925
+ and pattern.total_retrievals == expected_retrievals
926
+ ):
927
+ result.actual_behavior = f"Exact counts: {pattern.total_compressions} compressions, {pattern.total_retrievals} retrievals"
928
+ result.passed = True
929
+ else:
930
+ result.actual_behavior = f"Count mismatch: {pattern.total_compressions} compressions (expected {expected_compressions}), {pattern.total_retrievals} retrievals (expected {expected_retrievals})"
931
+ result.passed = False
932
+
933
+ result.details = {
934
+ "expected_compressions": expected_compressions,
935
+ "actual_compressions": pattern.total_compressions if pattern else 0,
936
+ "expected_retrievals": expected_retrievals,
937
+ "actual_retrievals": pattern.total_retrievals if pattern else 0,
938
+ }
939
+
940
+ return result
941
+
942
+
943
+ # =============================================================================
944
+ # CATEGORY 5: DECEPTIVE DATA
945
+ # =============================================================================
946
+
947
+
948
+ def test_hidden_error_in_nested_structure() -> AdversarialResult:
949
+ """
950
+ ATTACK: Error hidden deep in nested structure.
951
+ """
952
+ result = AdversarialResult(
953
+ name="Hidden Error in Nested Structure",
954
+ category="deceptive",
955
+ expected_behavior="Should detect errors in nested objects",
956
+ severity="high",
957
+ )
958
+
959
+ items = []
960
+ error_idx = 50
961
+
962
+ for i in range(100):
963
+ if i == error_idx:
964
+ # Error hidden deep inside
965
+ items.append(
966
+ {
967
+ "id": i,
968
+ "status": "success", # Top level says success!
969
+ "details": {
970
+ "level1": {
971
+ "level2": {
972
+ "actual_status": "CRITICAL_ERROR",
973
+ "error": True,
974
+ "message": "System failure",
975
+ }
976
+ }
977
+ },
978
+ }
979
+ )
980
+ else:
981
+ items.append(
982
+ {
983
+ "id": i,
984
+ "status": "success",
985
+ "details": {"level1": {"level2": {"actual_status": "ok"}}},
986
+ }
987
+ )
988
+
989
+ config = SmartCrusherConfig(max_items_after_crush=15)
990
+ original_json = json.dumps(items)
991
+
992
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
993
+ compressed = json.loads(compressed_json)
994
+
995
+ # Check if the nested error was preserved
996
+ nested_error_found = any("CRITICAL_ERROR" in json.dumps(item) for item in compressed)
997
+
998
+ if nested_error_found:
999
+ result.actual_behavior = "Nested error was detected and preserved"
1000
+ result.passed = True
1001
+ else:
1002
+ result.actual_behavior = "Nested error was LOST - only top-level status checked"
1003
+ result.passed = False
1004
+
1005
+ return result
1006
+
1007
+
1008
+ def test_misleading_score_field() -> AdversarialResult:
1009
+ """
1010
+ ATTACK: Score field that doesn't indicate importance.
1011
+
1012
+ Items with score=0.99 are spam, items with score=0.01 are critical.
1013
+ """
1014
+ result = AdversarialResult(
1015
+ name="Misleading Score Field",
1016
+ category="deceptive",
1017
+ expected_behavior="Should not blindly trust high scores",
1018
+ severity="medium",
1019
+ )
1020
+
1021
+ items = []
1022
+ critical_indices = [25, 50, 75]
1023
+
1024
+ for i in range(100):
1025
+ if i in critical_indices:
1026
+ # LOW score but CRITICAL
1027
+ items.append(
1028
+ {
1029
+ "id": i,
1030
+ "score": 0.01, # Low score
1031
+ "type": "critical_alert",
1032
+ "message": "URGENT: Action required",
1033
+ }
1034
+ )
1035
+ else:
1036
+ # HIGH score but SPAM
1037
+ items.append(
1038
+ {
1039
+ "id": i,
1040
+ "score": 0.99, # High score
1041
+ "type": "spam",
1042
+ "message": "Buy now! Limited offer!",
1043
+ }
1044
+ )
1045
+
1046
+ config = SmartCrusherConfig(max_items_after_crush=15)
1047
+ original_json = json.dumps(items)
1048
+
1049
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
1050
+ compressed = json.loads(compressed_json)
1051
+
1052
+ # Check what was kept
1053
+ critical_kept = len([item for item in compressed if item.get("type") == "critical_alert"])
1054
+ spam_kept = len([item for item in compressed if item.get("type") == "spam"])
1055
+
1056
+ # We should preserve ALL critical items due to "critical" keyword detection
1057
+ # The remaining slots can go to high-score items - that's acceptable
1058
+ # The key guarantee: we NEVER lose items matching important keywords
1059
+ if critical_kept < 3:
1060
+ result.actual_behavior = f"Lost critical items! Only kept {critical_kept}/3 critical"
1061
+ result.passed = False
1062
+ else:
1063
+ result.actual_behavior = f"Kept all {critical_kept} critical items (plus {spam_kept} spam) - keyword detection worked"
1064
+ result.passed = True
1065
+
1066
+ result.details = {
1067
+ "critical_kept": critical_kept,
1068
+ "spam_kept": spam_kept,
1069
+ }
1070
+
1071
+ return result
1072
+
1073
+
1074
+ def test_timestamp_anomaly_not_value() -> AdversarialResult:
1075
+ """
1076
+ ATTACK: Anomaly in timestamp, not in measured value.
1077
+
1078
+ One entry is from the FUTURE - this is the anomaly!
1079
+ """
1080
+ result = AdversarialResult(
1081
+ name="Timestamp Anomaly",
1082
+ category="deceptive",
1083
+ expected_behavior="Should detect timestamp anomalies",
1084
+ severity="medium",
1085
+ )
1086
+
1087
+ items = []
1088
+ anomaly_idx = 50
1089
+
1090
+ for i in range(100):
1091
+ if i == anomaly_idx:
1092
+ # Future timestamp - something is wrong!
1093
+ items.append(
1094
+ {
1095
+ "timestamp": "2030-01-01T00:00:00Z", # FUTURE!
1096
+ "value": 50, # Normal value
1097
+ "id": i,
1098
+ }
1099
+ )
1100
+ else:
1101
+ items.append(
1102
+ {
1103
+ "timestamp": f"2025-01-{(i % 28) + 1:02d}T{(i % 24):02d}:00:00Z",
1104
+ "value": 50 + (i % 10), # Normal variation
1105
+ "id": i,
1106
+ }
1107
+ )
1108
+
1109
+ config = SmartCrusherConfig(max_items_after_crush=15)
1110
+ original_json = json.dumps(items)
1111
+
1112
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
1113
+ compressed = json.loads(compressed_json)
1114
+
1115
+ # Check if future timestamp was preserved
1116
+ future_found = any("2030" in str(item.get("timestamp", "")) for item in compressed)
1117
+
1118
+ if future_found:
1119
+ result.actual_behavior = "Future timestamp anomaly preserved"
1120
+ result.passed = True
1121
+ else:
1122
+ result.actual_behavior = "Timestamp anomaly LOST - only value anomalies detected"
1123
+ result.passed = False
1124
+
1125
+ return result
1126
+
1127
+
1128
+ # =============================================================================
1129
+ # EXTREME STRESS TESTS - Designed to Break Assumptions
1130
+ # =============================================================================
1131
+
1132
+
1133
+ def test_deeply_nested_structure() -> AdversarialResult:
1134
+ """
1135
+ ATTACK: Extremely deep nesting to cause stack overflow.
1136
+
1137
+ 100 levels of nested objects containing arrays.
1138
+ """
1139
+ result = AdversarialResult(
1140
+ name="Deep Nesting Attack",
1141
+ category="extreme",
1142
+ expected_behavior="Should handle deep nesting without stack overflow",
1143
+ severity="critical",
1144
+ )
1145
+
1146
+ # Build deeply nested structure
1147
+ depth = 100
1148
+ inner = [{"id": i, "value": f"leaf_{i}"} for i in range(20)]
1149
+
1150
+ current = inner
1151
+ for level in range(depth):
1152
+ current = {"level": level, "data": current}
1153
+
1154
+ try:
1155
+ config = SmartCrusherConfig(max_items_after_crush=10)
1156
+ original_json = json.dumps(current)
1157
+
1158
+ compressed_json, was_modified, reason = smart_crush_tool_output(original_json, config)
1159
+ result.actual_behavior = f"Handled {depth} levels of nesting"
1160
+ result.passed = True
1161
+ except RecursionError as e:
1162
+ result.actual_behavior = f"Stack overflow at depth {depth}: {e}"
1163
+ result.passed = False
1164
+ except Exception as e:
1165
+ result.actual_behavior = f"Unexpected error: {type(e).__name__}: {e}"
1166
+ result.passed = False
1167
+
1168
+ return result
1169
+
1170
+
1171
+ def test_nan_infinity_scores() -> AdversarialResult:
1172
+ """
1173
+ ATTACK: Score fields with NaN, Infinity, -Infinity.
1174
+
1175
+ These are valid JSON when serialized from Python but break comparisons.
1176
+ """
1177
+ result = AdversarialResult(
1178
+ name="NaN/Infinity Scores",
1179
+ category="extreme",
1180
+ expected_behavior="Should handle special float values gracefully",
1181
+ severity="high",
1182
+ )
1183
+
1184
+ items = []
1185
+ for i in range(50):
1186
+ score = i / 10.0
1187
+ if i == 10:
1188
+ score = float("nan")
1189
+ elif i == 20:
1190
+ score = float("inf")
1191
+ elif i == 30:
1192
+ score = float("-inf")
1193
+
1194
+ items.append({"id": i, "score": score, "name": f"item_{i}"})
1195
+
1196
+ try:
1197
+ config = SmartCrusherConfig(max_items_after_crush=15)
1198
+ # Note: json.dumps will fail on NaN/Inf by default, use allow_nan
1199
+ original_json = json.dumps(items, allow_nan=True)
1200
+
1201
+ compressed_json, was_modified, reason = smart_crush_tool_output(original_json, config)
1202
+ compressed = json.loads(compressed_json, parse_constant=lambda x: None)
1203
+
1204
+ result.actual_behavior = f"Handled special floats, compressed to {len(compressed)} items"
1205
+ result.passed = True
1206
+ except (ValueError, TypeError) as e:
1207
+ result.actual_behavior = f"Failed on special floats: {e}"
1208
+ result.passed = False
1209
+ except Exception as e:
1210
+ result.actual_behavior = f"Unexpected error: {type(e).__name__}: {e}"
1211
+ result.passed = False
1212
+
1213
+ return result
1214
+
1215
+
1216
+ def test_mixed_type_array() -> AdversarialResult:
1217
+ """
1218
+ ATTACK: Array with mixed types (dicts, strings, numbers, nulls).
1219
+
1220
+ SmartCrusher expects arrays of dicts - what happens with mixed?
1221
+ """
1222
+ result = AdversarialResult(
1223
+ name="Mixed Type Array",
1224
+ category="extreme",
1225
+ expected_behavior="Should handle or gracefully skip mixed arrays",
1226
+ severity="medium",
1227
+ )
1228
+
1229
+ mixed_array = [
1230
+ {"id": 1, "type": "dict"},
1231
+ "just a string",
1232
+ 42,
1233
+ None,
1234
+ {"id": 2, "type": "dict"},
1235
+ ["nested", "array"],
1236
+ True,
1237
+ {"id": 3, "type": "dict"},
1238
+ ]
1239
+
1240
+ try:
1241
+ config = SmartCrusherConfig(max_items_after_crush=5)
1242
+ original_json = json.dumps(mixed_array)
1243
+
1244
+ compressed_json, was_modified, reason = smart_crush_tool_output(original_json, config)
1245
+
1246
+ result.actual_behavior = f"Handled mixed array: modified={was_modified}, reason={reason}"
1247
+ result.passed = True
1248
+ except Exception as e:
1249
+ result.actual_behavior = f"Crashed on mixed array: {type(e).__name__}: {e}"
1250
+ result.passed = False
1251
+
1252
+ return result
1253
+
1254
+
1255
+ def test_catastrophic_regex_in_search() -> AdversarialResult:
1256
+ """
1257
+ ATTACK: Search query designed to cause catastrophic backtracking.
1258
+
1259
+ Pattern like (a+)+ on "aaaaaaaaaaaaaaaaaaaaaaaaaaab" can hang regex engines.
1260
+ """
1261
+ result = AdversarialResult(
1262
+ name="Regex Catastrophic Backtracking",
1263
+ category="extreme",
1264
+ expected_behavior="Should not hang on malicious search patterns",
1265
+ severity="critical",
1266
+ )
1267
+
1268
+ reset_compression_store()
1269
+ store = get_compression_store()
1270
+
1271
+ items = [{"id": i, "content": "a" * 50 + "b"} for i in range(100)]
1272
+
1273
+ hash_key = store.store(
1274
+ original=json.dumps(items),
1275
+ compressed=json.dumps(items[:10]),
1276
+ original_item_count=100,
1277
+ compressed_item_count=10,
1278
+ tool_name="regex_test",
1279
+ )
1280
+
1281
+ # These patterns could cause catastrophic backtracking in naive regex
1282
+ evil_patterns = [
1283
+ "(a+)+$",
1284
+ "(a|aa)+$",
1285
+ "(a+)+b",
1286
+ "([a-zA-Z]+)*X",
1287
+ ]
1288
+
1289
+ try:
1290
+ import signal
1291
+
1292
+ def timeout_handler(signum, frame):
1293
+ raise TimeoutError("Search took too long")
1294
+
1295
+ # Set 2 second timeout
1296
+ old_handler = signal.signal(signal.SIGALRM, timeout_handler)
1297
+ signal.alarm(2)
1298
+
1299
+ for pattern in evil_patterns:
1300
+ # BM25 search doesn't use regex, so should be safe
1301
+ store.search(hash_key, pattern)
1302
+
1303
+ signal.alarm(0)
1304
+ signal.signal(signal.SIGALRM, old_handler)
1305
+
1306
+ result.actual_behavior = "Search completed without hanging"
1307
+ result.passed = True
1308
+ except TimeoutError:
1309
+ result.actual_behavior = "Search hung on regex-like pattern"
1310
+ result.passed = False
1311
+ except Exception as e:
1312
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1313
+ result.passed = True # Failing safely is OK
1314
+
1315
+ return result
1316
+
1317
+
1318
+ def test_million_items() -> AdversarialResult:
1319
+ """
1320
+ ATTACK: Array with 1 million items.
1321
+
1322
+ Test memory and performance at scale.
1323
+ """
1324
+ result = AdversarialResult(
1325
+ name="Million Items Scale",
1326
+ category="extreme",
1327
+ expected_behavior="Should handle large arrays without OOM",
1328
+ severity="high",
1329
+ )
1330
+
1331
+ try:
1332
+ # Create 100K items (not 1M to keep test reasonable)
1333
+ item_count = 100_000
1334
+ items = [{"id": i, "value": i % 1000} for i in range(item_count)]
1335
+
1336
+ config = SmartCrusherConfig(max_items_after_crush=15)
1337
+
1338
+ start = time.time()
1339
+ original_json = json.dumps(items)
1340
+ compressed_json, was_modified, reason = smart_crush_tool_output(original_json, config)
1341
+ elapsed = time.time() - start
1342
+
1343
+ compressed = json.loads(compressed_json)
1344
+
1345
+ result.actual_behavior = (
1346
+ f"Compressed {item_count} items to {len(compressed)} in {elapsed:.2f}s"
1347
+ )
1348
+ result.passed = elapsed < 10.0 # Should complete in under 10 seconds
1349
+ result.details = {"item_count": item_count, "elapsed_seconds": elapsed}
1350
+ except MemoryError:
1351
+ result.actual_behavior = "Out of memory"
1352
+ result.passed = False
1353
+ except Exception as e:
1354
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1355
+ result.passed = False
1356
+
1357
+ return result
1358
+
1359
+
1360
+ def test_item_with_thousands_of_fields() -> AdversarialResult:
1361
+ """
1362
+ ATTACK: Items with 10,000 fields each.
1363
+
1364
+ Field analysis iterates over all fields - what's the cost?
1365
+ """
1366
+ result = AdversarialResult(
1367
+ name="Thousands of Fields",
1368
+ category="extreme",
1369
+ expected_behavior="Should handle items with many fields",
1370
+ severity="medium",
1371
+ )
1372
+
1373
+ try:
1374
+ field_count = 5000
1375
+ items = []
1376
+ for i in range(20):
1377
+ item = {"id": i}
1378
+ for f in range(field_count):
1379
+ item[f"field_{f}"] = f"value_{f}_{i}"
1380
+ items.append(item)
1381
+
1382
+ config = SmartCrusherConfig(max_items_after_crush=10)
1383
+
1384
+ start = time.time()
1385
+ original_json = json.dumps(items)
1386
+ compressed_json, was_modified, reason = smart_crush_tool_output(original_json, config)
1387
+ elapsed = time.time() - start
1388
+
1389
+ result.actual_behavior = f"Handled {field_count} fields/item in {elapsed:.2f}s"
1390
+ result.passed = elapsed < 5.0
1391
+ except Exception as e:
1392
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1393
+ result.passed = False
1394
+
1395
+ return result
1396
+
1397
+
1398
+ def test_identical_items() -> AdversarialResult:
1399
+ """
1400
+ ATTACK: All items are EXACTLY identical.
1401
+
1402
+ Uniqueness detection should handle this edge case.
1403
+ """
1404
+ result = AdversarialResult(
1405
+ name="All Identical Items",
1406
+ category="extreme",
1407
+ expected_behavior="Should handle identical items efficiently",
1408
+ severity="low",
1409
+ )
1410
+
1411
+ # 1000 perfectly identical items
1412
+ template = {"id": 1, "status": "ok", "value": 42, "message": "All good"}
1413
+ items = [template.copy() for _ in range(1000)]
1414
+
1415
+ try:
1416
+ config = SmartCrusherConfig(max_items_after_crush=15)
1417
+
1418
+ compressed_json, was_modified, reason = smart_crush_tool_output(json.dumps(items), config)
1419
+ compressed = json.loads(compressed_json)
1420
+
1421
+ result.actual_behavior = (
1422
+ f"Compressed {len(items)} identical items to {len(compressed)}: {reason}"
1423
+ )
1424
+ # Should heavily compress since all items are the same
1425
+ result.passed = len(compressed) <= 15
1426
+ except Exception as e:
1427
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1428
+ result.passed = False
1429
+
1430
+ return result
1431
+
1432
+
1433
+ def test_all_fields_none() -> AdversarialResult:
1434
+ """
1435
+ ATTACK: Items where every field value is null/None.
1436
+ """
1437
+ result = AdversarialResult(
1438
+ name="All Null Values",
1439
+ category="extreme",
1440
+ expected_behavior="Should handle all-null items",
1441
+ severity="low",
1442
+ )
1443
+
1444
+ items = [{"id": None, "value": None, "status": None, "data": None} for _ in range(100)]
1445
+
1446
+ try:
1447
+ config = SmartCrusherConfig(max_items_after_crush=10)
1448
+
1449
+ compressed_json, was_modified, reason = smart_crush_tool_output(json.dumps(items), config)
1450
+
1451
+ result.actual_behavior = f"Handled all-null items: modified={was_modified}"
1452
+ result.passed = True
1453
+ except Exception as e:
1454
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1455
+ result.passed = False
1456
+
1457
+ return result
1458
+
1459
+
1460
+ def test_unicode_normalization_attack() -> AdversarialResult:
1461
+ """
1462
+ ATTACK: Unicode strings that look identical but are different.
1463
+
1464
+ "café" can be encoded as:
1465
+ - c a f é (4 chars, é is U+00E9)
1466
+ - c a f e ́ (5 chars, e + combining acute U+0301)
1467
+
1468
+ These look identical but are different strings!
1469
+ """
1470
+ result = AdversarialResult(
1471
+ name="Unicode Normalization Attack",
1472
+ category="extreme",
1473
+ expected_behavior="Should handle unicode edge cases",
1474
+ severity="medium",
1475
+ )
1476
+
1477
+ # Two visually identical but byte-different strings
1478
+ composed = "café" # é as single char
1479
+ decomposed = "cafe\u0301" # e + combining accent
1480
+
1481
+ items = []
1482
+ for i in range(50):
1483
+ if i % 2 == 0:
1484
+ items.append({"id": i, "name": composed, "type": "composed"})
1485
+ else:
1486
+ items.append({"id": i, "name": decomposed, "type": "decomposed"})
1487
+
1488
+ # Add one special item
1489
+ items[25] = {"id": 25, "name": composed, "type": "TARGET", "status": "error"}
1490
+
1491
+ try:
1492
+ config = SmartCrusherConfig(max_items_after_crush=15)
1493
+
1494
+ compressed_json, was_modified, reason = smart_crush_tool_output(json.dumps(items), config)
1495
+ compressed = json.loads(compressed_json)
1496
+
1497
+ # Check if we kept the TARGET item
1498
+ target_found = any(item.get("type") == "TARGET" for item in compressed)
1499
+
1500
+ result.actual_behavior = f"Unicode handled, target found: {target_found}"
1501
+ result.passed = target_found
1502
+ except Exception as e:
1503
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1504
+ result.passed = False
1505
+
1506
+ return result
1507
+
1508
+
1509
+ def test_concurrent_reset_during_operation() -> AdversarialResult:
1510
+ """
1511
+ ATTACK: Reset global state while operations are in progress.
1512
+ """
1513
+ result = AdversarialResult(
1514
+ name="Concurrent Reset Attack",
1515
+ category="extreme",
1516
+ expected_behavior="Should not crash on concurrent reset",
1517
+ severity="high",
1518
+ )
1519
+
1520
+ errors = []
1521
+ operations_completed = [0]
1522
+
1523
+ def do_operations():
1524
+ for i in range(100):
1525
+ try:
1526
+ store = get_compression_store()
1527
+ items = [{"id": j, "iter": i} for j in range(20)]
1528
+ hash_key = store.store(
1529
+ original=json.dumps(items),
1530
+ compressed=json.dumps(items[:5]),
1531
+ original_item_count=20,
1532
+ compressed_item_count=5,
1533
+ tool_name="reset_test",
1534
+ )
1535
+ store.retrieve(hash_key)
1536
+ store.search(hash_key, "test")
1537
+ operations_completed[0] += 1
1538
+ except Exception as e:
1539
+ errors.append(f"Op error: {type(e).__name__}: {e}")
1540
+
1541
+ def do_resets():
1542
+ for _ in range(50):
1543
+ try:
1544
+ reset_compression_store()
1545
+ reset_compression_feedback()
1546
+ time.sleep(0.001)
1547
+ except Exception as e:
1548
+ errors.append(f"Reset error: {type(e).__name__}: {e}")
1549
+
1550
+ try:
1551
+ with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
1552
+ futures = []
1553
+ for _ in range(5):
1554
+ futures.append(executor.submit(do_operations))
1555
+ for _ in range(3):
1556
+ futures.append(executor.submit(do_resets))
1557
+
1558
+ concurrent.futures.wait(futures)
1559
+
1560
+ if errors:
1561
+ result.actual_behavior = f"Errors during concurrent reset: {errors[:3]}"
1562
+ result.passed = False
1563
+ else:
1564
+ result.actual_behavior = (
1565
+ f"Completed {operations_completed[0]} operations with concurrent resets"
1566
+ )
1567
+ result.passed = True
1568
+ except Exception as e:
1569
+ result.actual_behavior = f"Crashed: {type(e).__name__}: {e}"
1570
+ result.passed = False
1571
+
1572
+ return result
1573
+
1574
+
1575
+ def test_zero_byte_in_content() -> AdversarialResult:
1576
+ """
1577
+ ATTACK: Null bytes (\\x00) embedded in strings.
1578
+
1579
+ Can truncate strings in C-based systems.
1580
+ """
1581
+ result = AdversarialResult(
1582
+ name="Null Byte Injection",
1583
+ category="extreme",
1584
+ expected_behavior="Should preserve content with null bytes",
1585
+ severity="high",
1586
+ )
1587
+
1588
+ items = []
1589
+ for i in range(50):
1590
+ # Embed null byte in various positions
1591
+ if i == 10:
1592
+ items.append({"id": i, "data": "before\x00after", "status": "error"})
1593
+ elif i == 20:
1594
+ items.append({"id": i, "data": "\x00start", "status": "error"})
1595
+ elif i == 30:
1596
+ items.append({"id": i, "data": "end\x00", "status": "error"})
1597
+ else:
1598
+ items.append({"id": i, "data": "normal", "status": "ok"})
1599
+
1600
+ try:
1601
+ config = SmartCrusherConfig(max_items_after_crush=15)
1602
+ original_json = json.dumps(items)
1603
+
1604
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
1605
+ compressed = json.loads(compressed_json)
1606
+
1607
+ # Check if null-byte items were preserved (they have status=error)
1608
+ error_items = [item for item in compressed if item.get("status") == "error"]
1609
+
1610
+ # Also verify the null bytes survived
1611
+ null_byte_survived = any("\x00" in str(item.get("data", "")) for item in compressed)
1612
+
1613
+ result.actual_behavior = (
1614
+ f"Kept {len(error_items)} error items, null bytes intact: {null_byte_survived}"
1615
+ )
1616
+ result.passed = len(error_items) == 3 and null_byte_survived
1617
+ except Exception as e:
1618
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1619
+ result.passed = False
1620
+
1621
+ return result
1622
+
1623
+
1624
+ def test_recursive_json_structure() -> AdversarialResult:
1625
+ """
1626
+ ATTACK: Structure that references itself (via string representation).
1627
+
1628
+ Not true circular reference (JSON doesn't support that), but deeply self-similar.
1629
+ """
1630
+ result = AdversarialResult(
1631
+ name="Self-Similar Structure",
1632
+ category="extreme",
1633
+ expected_behavior="Should handle self-similar data",
1634
+ severity="low",
1635
+ )
1636
+
1637
+ # Create structure where values contain JSON-like strings
1638
+ items = []
1639
+ for i in range(50):
1640
+ inner = json.dumps({"nested_id": i, "value": "inner"})
1641
+ items.append(
1642
+ {
1643
+ "id": i,
1644
+ "data": inner, # JSON string inside JSON
1645
+ "meta": json.dumps({"level": 1, "payload": inner}), # Double nested
1646
+ }
1647
+ )
1648
+
1649
+ try:
1650
+ config = SmartCrusherConfig(max_items_after_crush=15)
1651
+
1652
+ compressed_json, was_modified, reason = smart_crush_tool_output(json.dumps(items), config)
1653
+ compressed = json.loads(compressed_json)
1654
+
1655
+ result.actual_behavior = f"Handled self-similar structure: {len(compressed)} items"
1656
+ result.passed = True
1657
+ except Exception as e:
1658
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1659
+ result.passed = False
1660
+
1661
+ return result
1662
+
1663
+
1664
+ def test_extreme_numeric_values() -> AdversarialResult:
1665
+ """
1666
+ ATTACK: Extreme numeric values that might overflow.
1667
+
1668
+ Very large integers, very small floats, edge cases.
1669
+ """
1670
+ result = AdversarialResult(
1671
+ name="Extreme Numeric Values",
1672
+ category="extreme",
1673
+ expected_behavior="Should handle extreme numbers",
1674
+ severity="medium",
1675
+ )
1676
+
1677
+ items = [
1678
+ {"id": 0, "value": 0},
1679
+ {"id": 1, "value": -1},
1680
+ {"id": 2, "value": 2**63 - 1}, # Max int64
1681
+ {"id": 3, "value": -(2**63)}, # Min int64
1682
+ {"id": 4, "value": 2**64}, # Overflow int64
1683
+ {"id": 5, "value": 10**308}, # Near max float
1684
+ {"id": 6, "value": 10**-308}, # Near min positive float
1685
+ {"id": 7, "value": 0.1 + 0.2}, # Classic float precision issue
1686
+ {"id": 8, "value": 1e-400}, # Underflow to 0
1687
+ {"id": 9, "score": 999999999999999999999}, # Very large score
1688
+ ]
1689
+
1690
+ # Add normal items
1691
+ for i in range(10, 50):
1692
+ items.append({"id": i, "value": i, "score": i / 100})
1693
+
1694
+ try:
1695
+ config = SmartCrusherConfig(max_items_after_crush=15)
1696
+
1697
+ compressed_json, was_modified, reason = smart_crush_tool_output(json.dumps(items), config)
1698
+ compressed = json.loads(compressed_json)
1699
+
1700
+ result.actual_behavior = f"Handled extreme numbers: {len(compressed)} items"
1701
+ result.passed = True
1702
+ except (OverflowError, ValueError) as e:
1703
+ result.actual_behavior = f"Numeric error: {e}"
1704
+ result.passed = False
1705
+ except Exception as e:
1706
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1707
+ result.passed = False
1708
+
1709
+ return result
1710
+
1711
+
1712
+ def test_adversarial_field_names() -> AdversarialResult:
1713
+ """
1714
+ ATTACK: Field names that might confuse our analysis.
1715
+
1716
+ Fields named "__proto__", "constructor", "toString", etc.
1717
+ """
1718
+ result = AdversarialResult(
1719
+ name="Adversarial Field Names",
1720
+ category="extreme",
1721
+ expected_behavior="Should handle special field names",
1722
+ severity="medium",
1723
+ )
1724
+
1725
+ items = []
1726
+ for i in range(30):
1727
+ items.append(
1728
+ {
1729
+ "id": i,
1730
+ "__proto__": {"admin": True}, # Prototype pollution attempt
1731
+ "constructor": "evil",
1732
+ "toString": "hacked",
1733
+ "__class__": "injected",
1734
+ "hasOwnProperty": False,
1735
+ "score": i / 10,
1736
+ "status": "error" if i == 15 else "ok",
1737
+ }
1738
+ )
1739
+
1740
+ try:
1741
+ config = SmartCrusherConfig(max_items_after_crush=10)
1742
+
1743
+ compressed_json, was_modified, reason = smart_crush_tool_output(json.dumps(items), config)
1744
+ compressed = json.loads(compressed_json)
1745
+
1746
+ # Verify error item was kept
1747
+ error_kept = any(item.get("status") == "error" for item in compressed)
1748
+
1749
+ result.actual_behavior = f"Handled adversarial fields, error kept: {error_kept}"
1750
+ result.passed = error_kept
1751
+ except Exception as e:
1752
+ result.actual_behavior = f"Error: {type(e).__name__}: {e}"
1753
+ result.passed = False
1754
+
1755
+ return result
1756
+
1757
+
1758
+ def test_store_during_eviction_storm() -> AdversarialResult:
1759
+ """
1760
+ ATTACK: Rapid store/retrieve during aggressive eviction.
1761
+
1762
+ max_entries=5 with 100 concurrent stores.
1763
+ """
1764
+ result = AdversarialResult(
1765
+ name="Eviction Storm",
1766
+ category="extreme",
1767
+ expected_behavior="Should maintain consistency during eviction",
1768
+ severity="high",
1769
+ )
1770
+
1771
+ reset_compression_store()
1772
+ # Create store with very small capacity
1773
+ store = CompressionStore(max_entries=5, default_ttl=300)
1774
+
1775
+ stored_hashes = []
1776
+ retrieved_count = [0]
1777
+ errors = []
1778
+ lock = threading.Lock()
1779
+
1780
+ def store_and_retrieve():
1781
+ for _i in range(50):
1782
+ try:
1783
+ items = [{"id": j, "thread": threading.current_thread().name} for j in range(10)]
1784
+ hash_key = store.store(
1785
+ original=json.dumps(items),
1786
+ compressed=json.dumps(items[:2]),
1787
+ original_item_count=10,
1788
+ compressed_item_count=2,
1789
+ tool_name="eviction_test",
1790
+ )
1791
+
1792
+ with lock:
1793
+ stored_hashes.append(hash_key)
1794
+
1795
+ # Immediately try to retrieve
1796
+ entry = store.retrieve(hash_key)
1797
+ if entry:
1798
+ with lock:
1799
+ retrieved_count[0] += 1
1800
+
1801
+ except Exception as e:
1802
+ with lock:
1803
+ errors.append(str(e))
1804
+
1805
+ try:
1806
+ with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
1807
+ futures = [executor.submit(store_and_retrieve) for _ in range(20)]
1808
+ concurrent.futures.wait(futures)
1809
+
1810
+ if errors:
1811
+ result.actual_behavior = f"Errors: {errors[:3]}"
1812
+ result.passed = False
1813
+ else:
1814
+ # Some eviction is expected, but we shouldn't crash
1815
+ result.actual_behavior = (
1816
+ f"Stored {len(stored_hashes)}, retrieved {retrieved_count[0]} (eviction expected)"
1817
+ )
1818
+ result.passed = True
1819
+ except Exception as e:
1820
+ result.actual_behavior = f"Crashed: {type(e).__name__}: {e}"
1821
+ result.passed = False
1822
+
1823
+ return result
1824
+
1825
+
1826
+ # =============================================================================
1827
+ # MAIN
1828
+ # =============================================================================
1829
+
1830
+
1831
+ def main():
1832
+ print("\n" + "=" * 70)
1833
+ print(" ADVERSARIAL CCR TESTS")
1834
+ print(" Intentionally Trying to Break Our Code")
1835
+ print("=" * 70 + "\n")
1836
+
1837
+ tests = [
1838
+ # Semantic attacks
1839
+ test_all_items_are_errors,
1840
+ test_error_keyword_in_normal_data,
1841
+ test_needle_looks_exactly_like_hay,
1842
+ test_anomaly_in_string_not_number,
1843
+ # Boundary conditions
1844
+ test_empty_array,
1845
+ test_single_item_array,
1846
+ test_exactly_max_items,
1847
+ test_max_items_plus_one,
1848
+ test_hash_collision_attempt,
1849
+ test_ttl_exact_boundary,
1850
+ # Injection attacks
1851
+ test_json_injection_in_content,
1852
+ test_headroom_marker_collision,
1853
+ test_unicode_and_emoji_handling,
1854
+ test_extremely_long_strings,
1855
+ test_query_injection_in_search,
1856
+ # Race conditions
1857
+ test_concurrent_store_same_content,
1858
+ test_concurrent_store_and_evict,
1859
+ test_concurrent_feedback_updates,
1860
+ # Deceptive data
1861
+ test_hidden_error_in_nested_structure,
1862
+ test_misleading_score_field,
1863
+ test_timestamp_anomaly_not_value,
1864
+ # EXTREME stress tests
1865
+ test_deeply_nested_structure,
1866
+ test_nan_infinity_scores,
1867
+ test_mixed_type_array,
1868
+ test_catastrophic_regex_in_search,
1869
+ test_million_items,
1870
+ test_item_with_thousands_of_fields,
1871
+ test_identical_items,
1872
+ test_all_fields_none,
1873
+ test_unicode_normalization_attack,
1874
+ test_concurrent_reset_during_operation,
1875
+ test_zero_byte_in_content,
1876
+ test_recursive_json_structure,
1877
+ test_extreme_numeric_values,
1878
+ test_adversarial_field_names,
1879
+ test_store_during_eviction_storm,
1880
+ ]
1881
+
1882
+ results_by_category = {}
1883
+
1884
+ for test_func in tests:
1885
+ print(f" Running {test_func.__name__}...", end=" ", flush=True)
1886
+ result = run_test(test_func)
1887
+
1888
+ if result.category not in results_by_category:
1889
+ results_by_category[result.category] = []
1890
+ results_by_category[result.category].append(result)
1891
+
1892
+ status = "✓" if result.passed else "✗"
1893
+ print(f"{status}")
1894
+
1895
+ # Summary
1896
+ print("\n" + "=" * 70)
1897
+ print(" RESULTS BY CATEGORY")
1898
+ print("=" * 70)
1899
+
1900
+ total_passed = 0
1901
+ total_tests = 0
1902
+ critical_failures = []
1903
+
1904
+ for category, results in results_by_category.items():
1905
+ passed = sum(1 for r in results if r.passed)
1906
+ total = len(results)
1907
+ total_passed += passed
1908
+ total_tests += total
1909
+
1910
+ print(f"\n {category.upper()}: {passed}/{total}")
1911
+
1912
+ for r in results:
1913
+ status = "✓ PASS" if r.passed else "✗ FAIL"
1914
+ print(f" {status} {r.name}")
1915
+
1916
+ if not r.passed:
1917
+ print(f" Expected: {r.expected_behavior}")
1918
+ print(f" Actual: {r.actual_behavior}")
1919
+
1920
+ if r.severity == "critical":
1921
+ critical_failures.append(r)
1922
+
1923
+ print("\n" + "=" * 70)
1924
+ print(f" TOTAL: {total_passed}/{total_tests} tests passed")
1925
+
1926
+ if critical_failures:
1927
+ print(f"\n ⚠️ {len(critical_failures)} CRITICAL FAILURES:")
1928
+ for r in critical_failures:
1929
+ print(f" - {r.name}: {r.actual_behavior[:50]}")
1930
+
1931
+ print("=" * 70 + "\n")
1932
+
1933
+ # Exit code
1934
+ failed = total_tests - total_passed
1935
+ exit(failed)
1936
+
1937
+
1938
+ if __name__ == "__main__":
1939
+ main()
benchmarks/agent_cost_benchmark.py ADDED
@@ -0,0 +1,804 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Agent Cost Crisis Benchmark - The Compelling Story
4
+
5
+ This benchmark demonstrates WHY Headroom matters by showing:
6
+
7
+ 1. THE PROBLEM: Context explosion in real-world agent workloads
8
+ - Tokens grow exponentially with conversation length
9
+ - Tool outputs dominate context (often 70%+ of tokens)
10
+ - Dynamic content breaks cache efficiency
11
+
12
+ 2. THE SOLUTION: Headroom's impact on real workloads
13
+ - Token reduction from SmartCrusher (50-80% on tool outputs)
14
+ - Cache alignment improvement (10x+ potential savings)
15
+ - Context windowing (stay within limits without losing info)
16
+
17
+ 3. THE PROOF: Quality preservation
18
+ - Critical information retained (errors, anomalies, relevant items)
19
+ - Agent task completion unaffected
20
+ - Information retrieval accuracy maintained
21
+
22
+ Usage:
23
+ python benchmarks/agent_cost_benchmark.py
24
+ python benchmarks/agent_cost_benchmark.py --format markdown > BENCHMARK.md
25
+ python benchmarks/agent_cost_benchmark.py --scenario coding-agent
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import argparse
31
+ import json
32
+ import statistics
33
+ import time
34
+ from dataclasses import dataclass, field
35
+ from typing import Any
36
+
37
+ # Benchmark scenario imports
38
+ from benchmarks.scenarios.conversations import (
39
+ generate_agentic_conversation,
40
+ generate_rag_conversation,
41
+ )
42
+ from benchmarks.scenarios.tool_outputs import (
43
+ generate_log_entries,
44
+ generate_search_results,
45
+ )
46
+
47
+ # Headroom imports
48
+ from headroom.transforms.smart_crusher import SmartCrusherConfig, smart_crush_tool_output
49
+
50
+ # =============================================================================
51
+ # PRICING DATA (as of 2025)
52
+ # =============================================================================
53
+
54
+ PRICING = {
55
+ # Anthropic Claude 3.5 Sonnet
56
+ "claude-3.5-sonnet": {
57
+ "input": 3.00 / 1_000_000, # $3 per 1M tokens
58
+ "output": 15.00 / 1_000_000, # $15 per 1M tokens
59
+ "cached_input": 0.30 / 1_000_000, # 90% discount on cache hit
60
+ "cache_write": 3.75 / 1_000_000, # 25% premium to write cache
61
+ },
62
+ # OpenAI GPT-4o
63
+ "gpt-4o": {
64
+ "input": 2.50 / 1_000_000,
65
+ "output": 10.00 / 1_000_000,
66
+ "cached_input": 1.25 / 1_000_000, # 50% discount
67
+ },
68
+ # Google Gemini 1.5 Pro
69
+ "gemini-1.5-pro": {
70
+ "input": 1.25 / 1_000_000,
71
+ "output": 5.00 / 1_000_000,
72
+ "cached_input": 0.3125 / 1_000_000, # 75% discount
73
+ },
74
+ }
75
+
76
+ # Approximate tokens per character (GPT-4 tokenizer average)
77
+ CHARS_PER_TOKEN = 4
78
+
79
+
80
+ @dataclass
81
+ class CostAnalysis:
82
+ """Cost analysis for a workload."""
83
+
84
+ tokens_input: int = 0
85
+ tokens_output: int = 0
86
+ tokens_cached: int = 0
87
+
88
+ cost_baseline: float = 0.0
89
+ cost_optimized: float = 0.0
90
+ cost_with_cache: float = 0.0
91
+
92
+ savings_from_compression: float = 0.0
93
+ savings_from_caching: float = 0.0
94
+ total_savings_percent: float = 0.0
95
+
96
+
97
+ @dataclass
98
+ class BenchmarkResult:
99
+ """Result from a single benchmark scenario."""
100
+
101
+ name: str
102
+ description: str
103
+
104
+ # Token metrics
105
+ tokens_original: int = 0
106
+ tokens_optimized: int = 0
107
+ compression_ratio: float = 0.0
108
+
109
+ # Cache metrics
110
+ cache_hit_rate_baseline: float = 0.0
111
+ cache_hit_rate_optimized: float = 0.0
112
+
113
+ # Quality metrics
114
+ critical_items_retained: int = 0
115
+ critical_items_total: int = 0
116
+ retention_rate: float = 0.0
117
+
118
+ # Cost analysis
119
+ cost_analysis: CostAnalysis = field(default_factory=CostAnalysis)
120
+
121
+ # Performance
122
+ optimization_latency_ms: float = 0.0
123
+
124
+ # Details
125
+ details: dict[str, Any] = field(default_factory=dict)
126
+
127
+
128
+ # =============================================================================
129
+ # SCENARIO 1: Coding Agent Context Explosion
130
+ # =============================================================================
131
+
132
+
133
+ def benchmark_coding_agent_explosion() -> BenchmarkResult:
134
+ """
135
+ Simulate a Claude Code / Cursor style coding agent session.
136
+
137
+ Shows how context explodes as the agent:
138
+ - Searches codebase (100s of file snippets)
139
+ - Reads documentation (large text blocks)
140
+ - Makes tool calls (grep, find, read)
141
+ - Accumulates conversation history
142
+ """
143
+ result = BenchmarkResult(
144
+ name="Coding Agent Context Explosion",
145
+ description="50-turn coding session with file search, grep, and documentation lookups",
146
+ )
147
+
148
+ # Generate realistic coding agent conversation
149
+ messages = generate_agentic_conversation(
150
+ turns=50,
151
+ tool_calls_per_turn=2,
152
+ items_per_tool_response=100, # 100 search results per tool call
153
+ )
154
+
155
+ # Calculate original tokens
156
+ original_content = json.dumps(messages)
157
+ result.tokens_original = len(original_content) // CHARS_PER_TOKEN
158
+
159
+ # Apply Headroom transforms using convenience function
160
+ config = SmartCrusherConfig(max_items_after_crush=20)
161
+
162
+ start = time.perf_counter()
163
+
164
+ optimized_messages = []
165
+ critical_retained = 0
166
+ critical_total = 0
167
+
168
+ for msg in messages:
169
+ if msg.get("role") == "tool":
170
+ # Parse tool content as JSON array
171
+ try:
172
+ original_content = msg.get("content", "[]")
173
+ content = json.loads(original_content)
174
+ if isinstance(content, list) and len(content) > 10:
175
+ # Count critical items (errors, high-relevance)
176
+ for item in content:
177
+ if isinstance(item, dict):
178
+ if item.get("error") or item.get("status") == "failed":
179
+ critical_total += 1
180
+ if item.get("is_needle"):
181
+ critical_total += 1
182
+
183
+ # Compress with SmartCrusher convenience function
184
+ compressed_str, was_modified, _ = smart_crush_tool_output(
185
+ original_content, config
186
+ )
187
+
188
+ if was_modified:
189
+ compressed = json.loads(compressed_str)
190
+ # Count retained critical items
191
+ for item in compressed:
192
+ if isinstance(item, dict):
193
+ if item.get("error") or item.get("status") == "failed":
194
+ critical_retained += 1
195
+ if item.get("is_needle"):
196
+ critical_retained += 1
197
+
198
+ msg = {**msg, "content": compressed_str}
199
+ except (json.JSONDecodeError, TypeError):
200
+ pass
201
+
202
+ optimized_messages.append(msg)
203
+
204
+ result.optimization_latency_ms = (time.perf_counter() - start) * 1000
205
+
206
+ # Calculate optimized tokens
207
+ optimized_content = json.dumps(optimized_messages)
208
+ result.tokens_optimized = len(optimized_content) // CHARS_PER_TOKEN
209
+
210
+ # Calculate metrics
211
+ result.compression_ratio = 1 - (result.tokens_optimized / result.tokens_original)
212
+ result.critical_items_total = critical_total
213
+ result.critical_items_retained = critical_retained
214
+ result.retention_rate = critical_retained / critical_total if critical_total > 0 else 1.0
215
+
216
+ # Cost analysis (using Claude 3.5 Sonnet pricing)
217
+ pricing = PRICING["claude-3.5-sonnet"]
218
+ result.cost_analysis = CostAnalysis(
219
+ tokens_input=result.tokens_original,
220
+ cost_baseline=result.tokens_original * pricing["input"],
221
+ cost_optimized=result.tokens_optimized * pricing["input"],
222
+ savings_from_compression=(result.tokens_original - result.tokens_optimized)
223
+ * pricing["input"],
224
+ )
225
+ result.cost_analysis.total_savings_percent = result.compression_ratio * 100
226
+
227
+ result.details = {
228
+ "turns": 50,
229
+ "tool_calls": 100,
230
+ "items_per_response": 100,
231
+ "items_after_compression": 20,
232
+ }
233
+
234
+ return result
235
+
236
+
237
+ # =============================================================================
238
+ # SCENARIO 2: Cache Alignment Impact
239
+ # =============================================================================
240
+
241
+
242
+ def benchmark_cache_alignment() -> BenchmarkResult:
243
+ """
244
+ Show how dynamic content breaks caching and how CacheAligner fixes it.
245
+
246
+ Simulates 100 requests with same base prompt but different dates.
247
+ Without alignment: 0% cache hits
248
+ With alignment: 90%+ cache hits
249
+ """
250
+ from headroom.cache import DetectorConfig, DynamicContentDetector
251
+
252
+ result = BenchmarkResult(
253
+ name="Cache Alignment Impact",
254
+ description="100 requests with dynamic dates - cache hit improvement",
255
+ )
256
+
257
+ # Base system prompt with dynamic date
258
+ base_prompt = """You are Claude, an AI assistant by Anthropic.
259
+
260
+ Today is {date}.
261
+ Current time: {time}.
262
+
263
+ Session ID: {session_id}
264
+ Request ID: {request_id}
265
+
266
+ You are a helpful coding assistant. Follow these guidelines:
267
+ 1. Write clean, readable code
268
+ 2. Add appropriate comments
269
+ 3. Handle errors gracefully
270
+ 4. Follow best practices
271
+
272
+ Be concise and helpful."""
273
+
274
+ import datetime
275
+ import uuid
276
+
277
+ # Use DynamicContentDetector to extract static content
278
+ detector = DynamicContentDetector(DetectorConfig(tiers=["regex"]))
279
+
280
+ # Simulate 100 requests over a day
281
+ prompts_original = []
282
+ prompts_aligned = []
283
+
284
+ base_date = datetime.datetime(2025, 1, 15, 9, 0, 0)
285
+
286
+ for i in range(100):
287
+ # Each request has different timestamp
288
+ request_time = base_date + datetime.timedelta(minutes=i * 5)
289
+
290
+ prompt = base_prompt.format(
291
+ date=request_time.strftime("%A, %B %d, %Y"),
292
+ time=request_time.strftime("%I:%M %p"),
293
+ session_id=f"sess_{uuid.uuid4().hex[:24]}",
294
+ request_id=f"req_{uuid.uuid4().hex[:24]}",
295
+ )
296
+ prompts_original.append(prompt)
297
+
298
+ # Extract static content for cache alignment
299
+ detection_result = detector.detect(prompt)
300
+ prompts_aligned.append(detection_result.static_content)
301
+
302
+ # Calculate cache hits
303
+ # Baseline: all prompts are different (dynamic dates)
304
+ unique_original = len(set(prompts_original))
305
+ cache_hits_baseline = 100 - unique_original
306
+
307
+ # Aligned: static prefixes should be identical
308
+ unique_aligned = len(set(prompts_aligned))
309
+ cache_hits_aligned = 100 - unique_aligned
310
+
311
+ result.cache_hit_rate_baseline = cache_hits_baseline / 100
312
+ result.cache_hit_rate_optimized = cache_hits_aligned / 100
313
+
314
+ # Token calculation
315
+ result.tokens_original = sum(len(p) // CHARS_PER_TOKEN for p in prompts_original)
316
+
317
+ # Cost analysis with caching
318
+ pricing = PRICING["claude-3.5-sonnet"]
319
+ tokens_per_request = len(prompts_original[0]) // CHARS_PER_TOKEN
320
+
321
+ # Baseline: pay full price every time (no cache hits)
322
+ cost_baseline = 100 * tokens_per_request * pricing["input"]
323
+
324
+ # Optimized: first request is cache write, rest are cache hits
325
+ first_request_cost = tokens_per_request * pricing["cache_write"]
326
+ cached_requests_cost = 99 * tokens_per_request * pricing["cached_input"]
327
+ cost_optimized = first_request_cost + cached_requests_cost
328
+
329
+ result.cost_analysis = CostAnalysis(
330
+ tokens_input=result.tokens_original,
331
+ cost_baseline=cost_baseline,
332
+ cost_with_cache=cost_optimized,
333
+ savings_from_caching=cost_baseline - cost_optimized,
334
+ total_savings_percent=((cost_baseline - cost_optimized) / cost_baseline) * 100,
335
+ )
336
+
337
+ result.details = {
338
+ "total_requests": 100,
339
+ "unique_prompts_baseline": unique_original,
340
+ "unique_prompts_aligned": unique_aligned,
341
+ "cache_improvement_factor": f"{(cache_hits_aligned - cache_hits_baseline)}x",
342
+ }
343
+
344
+ return result
345
+
346
+
347
+ # =============================================================================
348
+ # SCENARIO 3: RAG Context Scaling
349
+ # =============================================================================
350
+
351
+
352
+ def benchmark_rag_scaling() -> BenchmarkResult:
353
+ """
354
+ Show how RAG context grows and how Headroom manages it.
355
+
356
+ Simulates large RAG context with multiple queries.
357
+ """
358
+ result = BenchmarkResult(
359
+ name="RAG Context Scaling", description="Large RAG context (~50K tokens) with compression"
360
+ )
361
+
362
+ # Generate RAG conversation with ~50K tokens of context
363
+ messages = generate_rag_conversation(
364
+ context_tokens=50000,
365
+ num_queries=10,
366
+ )
367
+
368
+ original_content = json.dumps(messages)
369
+ result.tokens_original = len(original_content) // CHARS_PER_TOKEN
370
+
371
+ # Apply transforms - compress tool outputs in messages
372
+ config = SmartCrusherConfig(max_items_after_crush=10)
373
+
374
+ start = time.perf_counter()
375
+
376
+ # Compress tool outputs in messages
377
+ optimized_messages = []
378
+ for msg in messages:
379
+ if msg.get("role") == "tool":
380
+ try:
381
+ original_content_msg = msg.get("content", "[]")
382
+ compressed_str, was_modified, _ = smart_crush_tool_output(
383
+ original_content_msg, config
384
+ )
385
+ if was_modified:
386
+ msg = {**msg, "content": compressed_str}
387
+ except Exception:
388
+ pass
389
+ optimized_messages.append(msg)
390
+
391
+ result.optimization_latency_ms = (time.perf_counter() - start) * 1000
392
+
393
+ optimized_content = json.dumps(optimized_messages)
394
+ result.tokens_optimized = len(optimized_content) // CHARS_PER_TOKEN
395
+ result.compression_ratio = 1 - (result.tokens_optimized / result.tokens_original)
396
+
397
+ # Cost analysis
398
+ pricing = PRICING["claude-3.5-sonnet"]
399
+ result.cost_analysis = CostAnalysis(
400
+ tokens_input=result.tokens_original,
401
+ cost_baseline=result.tokens_original * pricing["input"],
402
+ cost_optimized=result.tokens_optimized * pricing["input"],
403
+ savings_from_compression=(result.tokens_original - result.tokens_optimized)
404
+ * pricing["input"],
405
+ total_savings_percent=result.compression_ratio * 100,
406
+ )
407
+
408
+ result.details = {
409
+ "context_tokens": 50000,
410
+ "num_queries": 10,
411
+ }
412
+
413
+ return result
414
+
415
+
416
+ # =============================================================================
417
+ # SCENARIO 4: Long-Running Agent Session
418
+ # =============================================================================
419
+
420
+
421
+ def benchmark_conversation_scaling() -> list[BenchmarkResult]:
422
+ """
423
+ Show how costs scale with conversation length.
424
+
425
+ Generates conversations of increasing length (10, 25, 50, 100, 200 turns)
426
+ and shows the scaling curve with and without Headroom.
427
+ """
428
+ results = []
429
+ turn_counts = [10, 25, 50, 100, 200]
430
+
431
+ for turns in turn_counts:
432
+ result = BenchmarkResult(
433
+ name=f"Conversation Scaling ({turns} turns)",
434
+ description=f"{turns}-turn agent conversation with tool calls",
435
+ )
436
+
437
+ messages = generate_agentic_conversation(
438
+ turns=turns,
439
+ tool_calls_per_turn=1,
440
+ items_per_tool_response=50,
441
+ )
442
+
443
+ original_content = json.dumps(messages)
444
+ result.tokens_original = len(original_content) // CHARS_PER_TOKEN
445
+
446
+ # Apply full optimization pipeline
447
+ config = SmartCrusherConfig(max_items_after_crush=15)
448
+
449
+ start = time.perf_counter()
450
+
451
+ optimized = []
452
+ for msg in messages:
453
+ if msg.get("role") == "tool":
454
+ try:
455
+ original_content = msg.get("content", "[]")
456
+ content = json.loads(original_content)
457
+ if isinstance(content, list) and len(content) > 15:
458
+ compressed_str, was_modified, _ = smart_crush_tool_output(
459
+ original_content, config
460
+ )
461
+ if was_modified:
462
+ msg = {**msg, "content": compressed_str}
463
+ except (json.JSONDecodeError, TypeError):
464
+ pass
465
+ optimized.append(msg)
466
+
467
+ result.optimization_latency_ms = (time.perf_counter() - start) * 1000
468
+
469
+ optimized_content = json.dumps(optimized)
470
+ result.tokens_optimized = len(optimized_content) // CHARS_PER_TOKEN
471
+ result.compression_ratio = 1 - (result.tokens_optimized / result.tokens_original)
472
+
473
+ pricing = PRICING["claude-3.5-sonnet"]
474
+ result.cost_analysis = CostAnalysis(
475
+ tokens_input=result.tokens_original,
476
+ cost_baseline=result.tokens_original * pricing["input"],
477
+ cost_optimized=result.tokens_optimized * pricing["input"],
478
+ total_savings_percent=result.compression_ratio * 100,
479
+ )
480
+
481
+ result.details = {"turns": turns}
482
+ results.append(result)
483
+
484
+ return results
485
+
486
+
487
+ # =============================================================================
488
+ # SCENARIO 5: Quality Preservation Test
489
+ # =============================================================================
490
+
491
+
492
+ def benchmark_quality_preservation() -> BenchmarkResult:
493
+ """
494
+ Prove that compression doesn't lose critical information.
495
+
496
+ Generates data with known "needles" (errors, anomalies, high-relevance items)
497
+ and verifies they survive compression.
498
+ """
499
+ result = BenchmarkResult(
500
+ name="Quality Preservation",
501
+ description="Verify critical items (errors, anomalies) survive compression",
502
+ )
503
+
504
+ # Generate test data with known needles
505
+ search_results = generate_search_results(
506
+ n=1000,
507
+ include_uuid_needles=10,
508
+ include_errors=20,
509
+ )
510
+
511
+ log_entries = generate_log_entries(
512
+ n=1000,
513
+ include_errors=30,
514
+ include_critical=5,
515
+ )
516
+
517
+ # Count needles before compression
518
+ needles_before = 0
519
+ errors_before = 0
520
+
521
+ for item in search_results:
522
+ if item.get("is_needle"):
523
+ needles_before += 1
524
+ if item.get("error"):
525
+ errors_before += 1
526
+
527
+ for entry in log_entries:
528
+ if entry.get("level") in ("ERROR", "CRITICAL"):
529
+ errors_before += 1
530
+
531
+ # Compress using SmartCrusher convenience function
532
+ config = SmartCrusherConfig(max_items_after_crush=50)
533
+
534
+ search_str = json.dumps(search_results)
535
+ logs_str = json.dumps(log_entries)
536
+
537
+ compressed_search_str, _, _ = smart_crush_tool_output(search_str, config)
538
+ compressed_logs_str, _, _ = smart_crush_tool_output(logs_str, config)
539
+
540
+ compressed_search = json.loads(compressed_search_str)
541
+ compressed_logs = json.loads(compressed_logs_str)
542
+
543
+ # Count needles after compression
544
+ needles_after = 0
545
+ errors_after = 0
546
+
547
+ for item in compressed_search:
548
+ if item.get("is_needle"):
549
+ needles_after += 1
550
+ if item.get("error"):
551
+ errors_after += 1
552
+
553
+ for entry in compressed_logs:
554
+ if entry.get("level") in ("ERROR", "CRITICAL"):
555
+ errors_after += 1
556
+
557
+ result.critical_items_total = needles_before + errors_before
558
+ result.critical_items_retained = needles_after + errors_after
559
+ result.retention_rate = result.critical_items_retained / result.critical_items_total
560
+
561
+ result.tokens_original = (
562
+ len(json.dumps(search_results)) + len(json.dumps(log_entries))
563
+ ) // CHARS_PER_TOKEN
564
+ result.tokens_optimized = (
565
+ len(json.dumps(compressed_search)) + len(json.dumps(compressed_logs))
566
+ ) // CHARS_PER_TOKEN
567
+ result.compression_ratio = 1 - (result.tokens_optimized / result.tokens_original)
568
+
569
+ result.details = {
570
+ "search_results_original": 1000,
571
+ "search_results_compressed": len(compressed_search),
572
+ "log_entries_original": 1000,
573
+ "log_entries_compressed": len(compressed_logs),
574
+ "needles_original": needles_before,
575
+ "needles_retained": needles_after,
576
+ "errors_original": errors_before,
577
+ "errors_retained": errors_after,
578
+ }
579
+
580
+ return result
581
+
582
+
583
+ # =============================================================================
584
+ # REPORT GENERATION
585
+ # =============================================================================
586
+
587
+
588
+ def generate_report(results: list[BenchmarkResult], format: str = "terminal") -> str:
589
+ """Generate benchmark report in specified format."""
590
+
591
+ if format == "markdown":
592
+ return _generate_markdown_report(results)
593
+ else:
594
+ return _generate_terminal_report(results)
595
+
596
+
597
+ def _generate_terminal_report(results: list[BenchmarkResult]) -> str:
598
+ """Generate colorful terminal report."""
599
+ lines = []
600
+
601
+ lines.append("")
602
+ lines.append("=" * 80)
603
+ lines.append(" HEADROOM AGENT COST BENCHMARK")
604
+ lines.append(" The Context Optimization Layer for LLM Applications")
605
+ lines.append("=" * 80)
606
+
607
+ total_savings = 0.0
608
+ total_baseline = 0.0
609
+
610
+ for result in results:
611
+ lines.append("")
612
+ lines.append(f"{'─' * 80}")
613
+ lines.append(f" {result.name}")
614
+ lines.append(f" {result.description}")
615
+ lines.append(f"{'─' * 80}")
616
+
617
+ # Token metrics
618
+ lines.append(f" Tokens (original): {result.tokens_original:>12,}")
619
+ lines.append(f" Tokens (optimized): {result.tokens_optimized:>12,}")
620
+ lines.append(f" Compression: {result.compression_ratio * 100:>11.1f}%")
621
+
622
+ # Cache metrics (if applicable)
623
+ if result.cache_hit_rate_optimized > 0:
624
+ lines.append(f" Cache Hit (before): {result.cache_hit_rate_baseline * 100:>11.1f}%")
625
+ lines.append(f" Cache Hit (after): {result.cache_hit_rate_optimized * 100:>11.1f}%")
626
+
627
+ # Quality metrics (if applicable)
628
+ if result.critical_items_total > 0:
629
+ lines.append(
630
+ f" Critical Items: {result.critical_items_retained}/{result.critical_items_total} retained"
631
+ )
632
+ lines.append(f" Retention Rate: {result.retention_rate * 100:>11.1f}%")
633
+
634
+ # Cost analysis
635
+ ca = result.cost_analysis
636
+ if ca.cost_baseline > 0:
637
+ lines.append(f" Cost (baseline): ${ca.cost_baseline:>11.4f}")
638
+ if ca.cost_optimized > 0:
639
+ lines.append(f" Cost (optimized): ${ca.cost_optimized:>11.4f}")
640
+ if ca.cost_with_cache > 0:
641
+ lines.append(f" Cost (with cache): ${ca.cost_with_cache:>11.4f}")
642
+ lines.append(f" Savings: {ca.total_savings_percent:>11.1f}%")
643
+
644
+ total_baseline += ca.cost_baseline
645
+ if ca.cost_optimized > 0:
646
+ total_savings += ca.cost_baseline - ca.cost_optimized
647
+ elif ca.cost_with_cache > 0:
648
+ total_savings += ca.cost_baseline - ca.cost_with_cache
649
+
650
+ # Performance
651
+ if result.optimization_latency_ms > 0:
652
+ lines.append(f" Optimization Time: {result.optimization_latency_ms:>11.2f}ms")
653
+
654
+ # Summary
655
+ lines.append("")
656
+ lines.append("=" * 80)
657
+ lines.append(" SUMMARY")
658
+ lines.append("=" * 80)
659
+ if total_baseline > 0:
660
+ lines.append(f" Total Baseline Cost: ${total_baseline:.4f}")
661
+ lines.append(f" Total Savings: ${total_savings:.4f}")
662
+ lines.append(f" Overall Reduction: {(total_savings / total_baseline) * 100:.1f}%")
663
+ lines.append("")
664
+ lines.append(" At 1M requests/month:")
665
+ lines.append(f" Without Headroom: ${total_baseline * 1_000_000:.2f}")
666
+ lines.append(f" With Headroom: ${(total_baseline - total_savings) * 1_000_000:.2f}")
667
+ lines.append(f" Monthly Savings: ${total_savings * 1_000_000:.2f}")
668
+ lines.append("")
669
+
670
+ return "\n".join(lines)
671
+
672
+
673
+ def _generate_markdown_report(results: list[BenchmarkResult]) -> str:
674
+ """Generate markdown report for documentation."""
675
+ lines = []
676
+
677
+ lines.append("# Headroom Agent Cost Benchmark")
678
+ lines.append("")
679
+ lines.append("> The Context Optimization Layer for LLM Applications")
680
+ lines.append("")
681
+ lines.append("## Executive Summary")
682
+ lines.append("")
683
+ lines.append("This benchmark demonstrates Headroom's impact on real-world agent workloads:")
684
+ lines.append("")
685
+ lines.append("| Metric | Impact |")
686
+ lines.append("|--------|--------|")
687
+
688
+ # Calculate summary metrics
689
+ total_compression = statistics.mean(
690
+ [r.compression_ratio for r in results if r.compression_ratio > 0]
691
+ )
692
+ cache_improvement = next((r for r in results if r.cache_hit_rate_optimized > 0), None)
693
+ quality_result = next((r for r in results if r.retention_rate > 0), None)
694
+
695
+ lines.append(f"| Token Reduction | **{total_compression * 100:.0f}%** average compression |")
696
+ if cache_improvement:
697
+ lines.append(
698
+ f"| Cache Hit Rate | **{cache_improvement.cache_hit_rate_baseline * 100:.0f}% → {cache_improvement.cache_hit_rate_optimized * 100:.0f}%** |"
699
+ )
700
+ if quality_result:
701
+ lines.append(
702
+ f"| Quality Retention | **{quality_result.retention_rate * 100:.0f}%** critical items preserved |"
703
+ )
704
+ lines.append("")
705
+
706
+ # Detailed results
707
+ lines.append("## Detailed Results")
708
+ lines.append("")
709
+
710
+ for result in results:
711
+ lines.append(f"### {result.name}")
712
+ lines.append("")
713
+ lines.append(f"*{result.description}*")
714
+ lines.append("")
715
+
716
+ lines.append("| Metric | Value |")
717
+ lines.append("|--------|-------|")
718
+ lines.append(f"| Original Tokens | {result.tokens_original:,} |")
719
+ lines.append(f"| Optimized Tokens | {result.tokens_optimized:,} |")
720
+ lines.append(f"| Compression | {result.compression_ratio * 100:.1f}% |")
721
+
722
+ if result.cost_analysis.total_savings_percent > 0:
723
+ lines.append(f"| Cost Savings | {result.cost_analysis.total_savings_percent:.1f}% |")
724
+
725
+ if result.retention_rate > 0:
726
+ lines.append(f"| Quality Retention | {result.retention_rate * 100:.1f}% |")
727
+
728
+ lines.append("")
729
+
730
+ # Cost projection
731
+ lines.append("## Cost Projection at Scale")
732
+ lines.append("")
733
+ lines.append("Based on Claude 3.5 Sonnet pricing ($3/1M input tokens):")
734
+ lines.append("")
735
+ lines.append("| Scale | Without Headroom | With Headroom | Monthly Savings |")
736
+ lines.append("|-------|------------------|---------------|-----------------|")
737
+
738
+ base_cost_per_request = sum(r.cost_analysis.cost_baseline for r in results) / len(results)
739
+ optimized_cost = sum(
740
+ r.cost_analysis.cost_optimized
741
+ or r.cost_analysis.cost_with_cache
742
+ or r.cost_analysis.cost_baseline * 0.5
743
+ for r in results
744
+ ) / len(results)
745
+
746
+ for scale, label in [(10_000, "10K"), (100_000, "100K"), (1_000_000, "1M")]:
747
+ baseline = base_cost_per_request * scale
748
+ optimized = optimized_cost * scale
749
+ savings = baseline - optimized
750
+ lines.append(
751
+ f"| {label} requests/mo | ${baseline:,.0f} | ${optimized:,.0f} | ${savings:,.0f} |"
752
+ )
753
+
754
+ lines.append("")
755
+
756
+ return "\n".join(lines)
757
+
758
+
759
+ # =============================================================================
760
+ # MAIN
761
+ # =============================================================================
762
+
763
+
764
+ def main():
765
+ parser = argparse.ArgumentParser(description="Headroom Agent Cost Benchmark")
766
+ parser.add_argument("--format", choices=["terminal", "markdown"], default="terminal")
767
+ parser.add_argument(
768
+ "--scenario",
769
+ choices=["all", "coding-agent", "cache", "rag", "scaling", "quality"],
770
+ default="all",
771
+ )
772
+ args = parser.parse_args()
773
+
774
+ results = []
775
+
776
+ print("Running benchmarks...\n")
777
+
778
+ if args.scenario in ("all", "coding-agent"):
779
+ print(" [1/5] Coding Agent Context Explosion...")
780
+ results.append(benchmark_coding_agent_explosion())
781
+
782
+ if args.scenario in ("all", "cache"):
783
+ print(" [2/5] Cache Alignment Impact...")
784
+ results.append(benchmark_cache_alignment())
785
+
786
+ if args.scenario in ("all", "rag"):
787
+ print(" [3/5] RAG Context Scaling...")
788
+ results.append(benchmark_rag_scaling())
789
+
790
+ if args.scenario in ("all", "scaling"):
791
+ print(" [4/5] Conversation Scaling...")
792
+ scaling_results = benchmark_conversation_scaling()
793
+ # Just add the 100-turn result to main results
794
+ results.append(scaling_results[3]) # 100 turns
795
+
796
+ if args.scenario in ("all", "quality"):
797
+ print(" [5/5] Quality Preservation...")
798
+ results.append(benchmark_quality_preservation())
799
+
800
+ print("\n" + generate_report(results, args.format))
801
+
802
+
803
+ if __name__ == "__main__":
804
+ main()
benchmarks/bench_relevance.py CHANGED
@@ -25,7 +25,6 @@ Run with:
25
  from __future__ import annotations
26
 
27
  import json
28
- from typing import Any
29
 
30
  import pytest
31
 
@@ -33,7 +32,8 @@ import pytest
33
  def _check_embedding_available() -> bool:
34
  """Check if sentence-transformers is available for embedding tests."""
35
  try:
36
- import sentence_transformers
 
37
  return True
38
  except ImportError:
39
  return False
@@ -203,8 +203,8 @@ class TestHybridBenchmarks:
203
  @pytest.fixture
204
  def scorer_fallback(self):
205
  """Create hybrid scorer without embeddings (BM25 fallback)."""
206
- from headroom.relevance.hybrid import HybridScorer
207
  from headroom.relevance.bm25 import BM25Scorer
 
208
 
209
  # Force BM25-only mode by not providing embedding scorer
210
  scorer = HybridScorer(
@@ -378,8 +378,8 @@ class TestRelevanceInSmartCrusher:
378
  @pytest.fixture
379
  def crusher_with_bm25(self, smart_crusher_config):
380
  """SmartCrusher with BM25 relevance scorer."""
381
- from headroom.transforms.smart_crusher import SmartCrusher
382
  from headroom.config import RelevanceScorerConfig
 
383
 
384
  return SmartCrusher(
385
  config=smart_crusher_config,
@@ -389,8 +389,8 @@ class TestRelevanceInSmartCrusher:
389
  @pytest.fixture
390
  def crusher_with_hybrid(self, smart_crusher_config):
391
  """SmartCrusher with hybrid relevance scorer."""
392
- from headroom.transforms.smart_crusher import SmartCrusher
393
  from headroom.config import RelevanceScorerConfig
 
394
 
395
  return SmartCrusher(
396
  config=smart_crusher_config,
 
25
  from __future__ import annotations
26
 
27
  import json
 
28
 
29
  import pytest
30
 
 
32
  def _check_embedding_available() -> bool:
33
  """Check if sentence-transformers is available for embedding tests."""
34
  try:
35
+ import sentence_transformers # noqa: F401
36
+
37
  return True
38
  except ImportError:
39
  return False
 
203
  @pytest.fixture
204
  def scorer_fallback(self):
205
  """Create hybrid scorer without embeddings (BM25 fallback)."""
 
206
  from headroom.relevance.bm25 import BM25Scorer
207
+ from headroom.relevance.hybrid import HybridScorer
208
 
209
  # Force BM25-only mode by not providing embedding scorer
210
  scorer = HybridScorer(
 
378
  @pytest.fixture
379
  def crusher_with_bm25(self, smart_crusher_config):
380
  """SmartCrusher with BM25 relevance scorer."""
 
381
  from headroom.config import RelevanceScorerConfig
382
+ from headroom.transforms.smart_crusher import SmartCrusher
383
 
384
  return SmartCrusher(
385
  config=smart_crusher_config,
 
389
  @pytest.fixture
390
  def crusher_with_hybrid(self, smart_crusher_config):
391
  """SmartCrusher with hybrid relevance scorer."""
 
392
  from headroom.config import RelevanceScorerConfig
393
+ from headroom.transforms.smart_crusher import SmartCrusher
394
 
395
  return SmartCrusher(
396
  config=smart_crusher_config,
benchmarks/bench_transforms.py CHANGED
@@ -26,7 +26,6 @@ Run with:
26
  from __future__ import annotations
27
 
28
  import json
29
- from typing import Any
30
 
31
  import pytest
32
 
@@ -205,8 +204,16 @@ class TestSmartCrusherBenchmarks:
205
  "role": "assistant",
206
  "content": None,
207
  "tool_calls": [
208
- {"id": "call_1", "type": "function", "function": {"name": "search", "arguments": "{}"}},
209
- {"id": "call_2", "type": "function", "function": {"name": "logs", "arguments": "{}"}},
 
 
 
 
 
 
 
 
210
  ],
211
  },
212
  {"role": "tool", "tool_call_id": "call_1", "content": json.dumps(items_100)},
@@ -345,7 +352,10 @@ And multiple blank lines."""
345
  Tests edge case of multiple system prompts.
346
  """
347
  messages = [
348
- {"role": "system", "content": "You are a helpful assistant.\n\nCurrent date: 2025-01-06"},
 
 
 
349
  {"role": "system", "content": "Additional context: Technical support mode."},
350
  {"role": "user", "content": "Hello"},
351
  ]
@@ -511,12 +521,14 @@ class TestTransformPipelineBenchmarks:
511
  return provider
512
 
513
  @pytest.fixture
514
- def pipeline(self, smart_crusher_config, cache_aligner_config, rolling_window_config, mock_provider):
 
 
515
  """Create transform pipeline."""
516
- from headroom.transforms.pipeline import TransformPipeline
517
  from headroom.transforms.cache_aligner import CacheAligner
518
- from headroom.transforms.smart_crusher import SmartCrusher
519
  from headroom.transforms.rolling_window import RollingWindow
 
520
 
521
  return TransformPipeline(
522
  transforms=[
 
26
  from __future__ import annotations
27
 
28
  import json
 
29
 
30
  import pytest
31
 
 
204
  "role": "assistant",
205
  "content": None,
206
  "tool_calls": [
207
+ {
208
+ "id": "call_1",
209
+ "type": "function",
210
+ "function": {"name": "search", "arguments": "{}"},
211
+ },
212
+ {
213
+ "id": "call_2",
214
+ "type": "function",
215
+ "function": {"name": "logs", "arguments": "{}"},
216
+ },
217
  ],
218
  },
219
  {"role": "tool", "tool_call_id": "call_1", "content": json.dumps(items_100)},
 
352
  Tests edge case of multiple system prompts.
353
  """
354
  messages = [
355
+ {
356
+ "role": "system",
357
+ "content": "You are a helpful assistant.\n\nCurrent date: 2025-01-06",
358
+ },
359
  {"role": "system", "content": "Additional context: Technical support mode."},
360
  {"role": "user", "content": "Hello"},
361
  ]
 
521
  return provider
522
 
523
  @pytest.fixture
524
+ def pipeline(
525
+ self, smart_crusher_config, cache_aligner_config, rolling_window_config, mock_provider
526
+ ):
527
  """Create transform pipeline."""
 
528
  from headroom.transforms.cache_aligner import CacheAligner
529
+ from headroom.transforms.pipeline import TransformPipeline
530
  from headroom.transforms.rolling_window import RollingWindow
531
+ from headroom.transforms.smart_crusher import SmartCrusher
532
 
533
  return TransformPipeline(
534
  transforms=[
benchmarks/ccr_regression_benchmark.py ADDED
@@ -0,0 +1,828 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ CCR Regression Benchmark - Verify No Information Loss
4
+
5
+ This benchmark tests that the CCR (Compress-Cache-Retrieve) architecture
6
+ does not cause any regression in agent behavior. Specifically:
7
+
8
+ 1. NEEDLE RETENTION: Critical items survive compression
9
+ - Errors, exceptions, failures
10
+ - Specific IDs/UUIDs mentioned in user query
11
+ - Anomalies and outliers
12
+
13
+ 2. RETRIEVAL ACCURACY: When retrieval is needed, correct items are returned
14
+ - Full retrieval returns original content
15
+ - Search retrieval finds relevant items
16
+
17
+ 3. FEEDBACK LEARNING: System learns from retrieval patterns
18
+ - High retrieval rate triggers less aggressive compression
19
+ - Common queries improve future compression
20
+
21
+ Usage:
22
+ python benchmarks/ccr_regression_benchmark.py
23
+ python benchmarks/ccr_regression_benchmark.py --verbose
24
+ python benchmarks/ccr_regression_benchmark.py --scenario needle-in-haystack
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import argparse
30
+ import json
31
+ import time
32
+ import uuid
33
+ from dataclasses import dataclass, field
34
+ from typing import Any
35
+
36
+ from headroom.cache.compression_feedback import (
37
+ get_compression_feedback,
38
+ reset_compression_feedback,
39
+ )
40
+ from headroom.cache.compression_store import (
41
+ get_compression_store,
42
+ reset_compression_store,
43
+ )
44
+ from headroom.transforms.smart_crusher import (
45
+ SmartCrusherConfig,
46
+ smart_crush_tool_output,
47
+ )
48
+
49
+
50
+ @dataclass
51
+ class RegressionResult:
52
+ """Result from a regression test."""
53
+
54
+ name: str
55
+ description: str
56
+ passed: bool = False # Default to False, set to True when test passes
57
+
58
+ # Metrics
59
+ total_needles: int = 0
60
+ needles_retained: int = 0
61
+ retention_rate: float = 0.0
62
+
63
+ # CCR metrics
64
+ items_compressed: int = 0
65
+ items_retrieved: int = 0
66
+ retrieval_accuracy: float = 0.0
67
+
68
+ # Performance
69
+ latency_ms: float = 0.0
70
+
71
+ # Details
72
+ details: dict[str, Any] = field(default_factory=dict)
73
+ failures: list[str] = field(default_factory=list)
74
+
75
+
76
+ # =============================================================================
77
+ # TEST 1: Needle in Haystack - Error Retention
78
+ # =============================================================================
79
+
80
+
81
+ def test_error_retention() -> RegressionResult:
82
+ """
83
+ Test that errors are NEVER lost during compression.
84
+
85
+ This is critical: if an API returns 1000 results with 3 errors,
86
+ those 3 errors MUST be in the compressed output.
87
+ """
88
+ result = RegressionResult(
89
+ name="Error Retention",
90
+ description="Verify all errors survive compression regardless of position",
91
+ )
92
+
93
+ # Generate 1000 items with errors at various positions
94
+ items = []
95
+ error_indices = [5, 47, 123, 456, 789, 999] # Spread throughout
96
+
97
+ for i in range(1000):
98
+ if i in error_indices:
99
+ items.append(
100
+ {
101
+ "id": i,
102
+ "status": "error",
103
+ "message": f"Connection failed: timeout at {i}",
104
+ "error_code": 500 + (i % 10),
105
+ }
106
+ )
107
+ else:
108
+ items.append(
109
+ {
110
+ "id": i,
111
+ "status": "success",
112
+ "message": "OK",
113
+ "data": {"value": i * 2},
114
+ }
115
+ )
116
+
117
+ result.total_needles = len(error_indices)
118
+
119
+ # Compress with SmartCrusher
120
+ config = SmartCrusherConfig(max_items_after_crush=15)
121
+ original_json = json.dumps(items)
122
+
123
+ start = time.perf_counter()
124
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
125
+ result.latency_ms = (time.perf_counter() - start) * 1000
126
+
127
+ # Count errors in compressed output
128
+ compressed = json.loads(compressed_json)
129
+ errors_found = [item for item in compressed if item.get("status") == "error"]
130
+
131
+ result.needles_retained = len(errors_found)
132
+ result.retention_rate = result.needles_retained / result.total_needles
133
+ result.items_compressed = len(compressed)
134
+
135
+ # Check if ALL errors were retained
136
+ result.passed = result.needles_retained == result.total_needles
137
+
138
+ if not result.passed:
139
+ result.failures.append(
140
+ f"Lost {result.total_needles - result.needles_retained} errors during compression"
141
+ )
142
+
143
+ result.details = {
144
+ "original_items": 1000,
145
+ "compressed_items": len(compressed),
146
+ "error_positions": error_indices,
147
+ "errors_retained": result.needles_retained,
148
+ }
149
+
150
+ return result
151
+
152
+
153
+ # =============================================================================
154
+ # TEST 2: Needle in Haystack - UUID Lookup
155
+ # =============================================================================
156
+
157
+
158
+ def test_uuid_retrieval() -> RegressionResult:
159
+ """
160
+ Test that specific UUIDs can be found via CCR retrieval.
161
+
162
+ Scenario: User asks "find transaction abc123..."
163
+ The system compresses, but user should be able to retrieve the specific item.
164
+ """
165
+ result = RegressionResult(
166
+ name="UUID Retrieval via CCR",
167
+ description="Verify specific UUIDs can be retrieved from compressed cache",
168
+ )
169
+
170
+ reset_compression_store()
171
+ store = get_compression_store()
172
+
173
+ # Generate 1000 transactions with UUIDs
174
+ target_uuid = str(uuid.uuid4())
175
+ items = []
176
+
177
+ for i in range(1000):
178
+ item_uuid = target_uuid if i == 456 else str(uuid.uuid4())
179
+ items.append(
180
+ {
181
+ "transaction_id": item_uuid,
182
+ "amount": 100 + (i % 1000),
183
+ "status": "completed",
184
+ "timestamp": f"2025-01-{(i % 28) + 1:02d}T10:00:00Z",
185
+ }
186
+ )
187
+
188
+ result.total_needles = 1
189
+
190
+ # Store original and compress
191
+ original_json = json.dumps(items)
192
+ config = SmartCrusherConfig(max_items_after_crush=15)
193
+
194
+ start = time.perf_counter()
195
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
196
+
197
+ # Store in CCR cache
198
+ hash_key = store.store(
199
+ original=original_json,
200
+ compressed=compressed_json,
201
+ original_item_count=1000,
202
+ compressed_item_count=15,
203
+ tool_name="transaction_search",
204
+ )
205
+
206
+ # Search for the specific UUID
207
+ search_results = store.search(hash_key, target_uuid)
208
+ result.latency_ms = (time.perf_counter() - start) * 1000
209
+
210
+ # Check if target UUID was found
211
+ found_target = any(item.get("transaction_id") == target_uuid for item in search_results)
212
+
213
+ result.needles_retained = 1 if found_target else 0
214
+ result.retention_rate = result.needles_retained / result.total_needles
215
+ result.items_retrieved = len(search_results)
216
+ result.retrieval_accuracy = 1.0 if found_target else 0.0
217
+
218
+ result.passed = found_target
219
+
220
+ if not result.passed:
221
+ result.failures.append(
222
+ f"Could not retrieve target UUID {target_uuid[:8]}... via CCR search"
223
+ )
224
+
225
+ result.details = {
226
+ "target_uuid": target_uuid,
227
+ "search_results_count": len(search_results),
228
+ "found_target": found_target,
229
+ "hash_key": hash_key,
230
+ }
231
+
232
+ return result
233
+
234
+
235
+ # =============================================================================
236
+ # TEST 3: Anomaly Detection
237
+ # =============================================================================
238
+
239
+
240
+ def test_anomaly_retention() -> RegressionResult:
241
+ """
242
+ Test that statistical anomalies are preserved during compression.
243
+
244
+ Scenario: 1000 metrics mostly at ~50, but with 5 spikes at 500+.
245
+ Those spikes MUST survive compression.
246
+ """
247
+ result = RegressionResult(
248
+ name="Anomaly Retention", description="Verify statistical outliers survive compression"
249
+ )
250
+
251
+ # Generate metrics with anomalies
252
+ import random
253
+
254
+ random.seed(42) # Reproducible
255
+
256
+ items = []
257
+ anomaly_indices = [10, 200, 450, 700, 990] # 5 spikes
258
+
259
+ for i in range(1000):
260
+ if i in anomaly_indices:
261
+ # Anomaly: 10x normal value
262
+ value = 500 + random.randint(0, 100)
263
+ else:
264
+ # Normal: around 50
265
+ value = 50 + random.randint(-10, 10)
266
+
267
+ items.append(
268
+ {
269
+ "timestamp": f"2025-01-07T{(i // 60):02d}:{(i % 60):02d}:00Z",
270
+ "cpu_percent": value,
271
+ "host": "prod-server-1",
272
+ }
273
+ )
274
+
275
+ result.total_needles = len(anomaly_indices)
276
+
277
+ # Compress
278
+ config = SmartCrusherConfig(
279
+ max_items_after_crush=20,
280
+ preserve_change_points=True,
281
+ )
282
+ original_json = json.dumps(items)
283
+
284
+ start = time.perf_counter()
285
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
286
+ result.latency_ms = (time.perf_counter() - start) * 1000
287
+
288
+ # Count anomalies (cpu > 200) in compressed output
289
+ compressed = json.loads(compressed_json)
290
+ anomalies_found = [
291
+ item
292
+ for item in compressed
293
+ if isinstance(item.get("cpu_percent"), (int, float)) and item["cpu_percent"] > 200
294
+ ]
295
+
296
+ result.needles_retained = len(anomalies_found)
297
+ result.retention_rate = result.needles_retained / result.total_needles
298
+ result.items_compressed = len(compressed)
299
+
300
+ # Pass if at least 80% of anomalies retained (some might be in change point windows)
301
+ result.passed = result.retention_rate >= 0.8
302
+
303
+ if not result.passed:
304
+ result.failures.append(
305
+ f"Lost too many anomalies: {result.needles_retained}/{result.total_needles} retained"
306
+ )
307
+
308
+ result.details = {
309
+ "original_items": 1000,
310
+ "compressed_items": len(compressed),
311
+ "anomaly_positions": anomaly_indices,
312
+ "anomalies_retained": result.needles_retained,
313
+ }
314
+
315
+ return result
316
+
317
+
318
+ # =============================================================================
319
+ # TEST 4: Full Retrieval Accuracy
320
+ # =============================================================================
321
+
322
+
323
+ def test_full_retrieval() -> RegressionResult:
324
+ """
325
+ Test that full retrieval returns EXACTLY the original content.
326
+ """
327
+ result = RegressionResult(
328
+ name="Full Retrieval Accuracy",
329
+ description="Verify full retrieval returns exact original content",
330
+ )
331
+
332
+ reset_compression_store()
333
+ store = get_compression_store()
334
+
335
+ # Generate test data
336
+ items = [{"id": i, "name": f"item_{i}", "value": i * 10} for i in range(100)]
337
+
338
+ original_json = json.dumps(items)
339
+ compressed_json = json.dumps(items[:10]) # Simulate compression
340
+
341
+ # Store
342
+ hash_key = store.store(
343
+ original=original_json,
344
+ compressed=compressed_json,
345
+ original_item_count=100,
346
+ compressed_item_count=10,
347
+ tool_name="test_tool",
348
+ )
349
+
350
+ start = time.perf_counter()
351
+
352
+ # Retrieve
353
+ entry = store.retrieve(hash_key)
354
+
355
+ result.latency_ms = (time.perf_counter() - start) * 1000
356
+
357
+ # Verify content matches exactly
358
+ if entry is None:
359
+ result.passed = False
360
+ result.failures.append("Retrieval returned None")
361
+ else:
362
+ retrieved_items = json.loads(entry.original_content)
363
+ result.passed = retrieved_items == items
364
+ result.items_retrieved = len(retrieved_items)
365
+ result.retrieval_accuracy = 1.0 if result.passed else 0.0
366
+
367
+ if not result.passed:
368
+ result.failures.append("Retrieved content does not match original")
369
+
370
+ result.total_needles = 100
371
+ result.needles_retained = result.items_retrieved
372
+ result.retention_rate = 1.0 if result.passed else 0.0
373
+
374
+ result.details = {
375
+ "original_items": 100,
376
+ "retrieved_items": result.items_retrieved,
377
+ "hash_key": hash_key,
378
+ }
379
+
380
+ return result
381
+
382
+
383
+ # =============================================================================
384
+ # TEST 5: Feedback Learning
385
+ # =============================================================================
386
+
387
+
388
+ def test_feedback_learning() -> RegressionResult:
389
+ """
390
+ Test that the feedback system learns from retrieval patterns.
391
+
392
+ Scenario: Simulate high retrieval rate, verify system recommends
393
+ less aggressive compression.
394
+ """
395
+ result = RegressionResult(
396
+ name="Feedback Learning",
397
+ description="Verify feedback loop adjusts compression based on patterns",
398
+ )
399
+
400
+ reset_compression_feedback()
401
+ feedback = get_compression_feedback()
402
+
403
+ tool_name = "high_retrieval_tool"
404
+
405
+ start = time.perf_counter()
406
+
407
+ # Simulate 10 compressions
408
+ for _ in range(10):
409
+ feedback.record_compression(tool_name, 1000, 20)
410
+
411
+ # Simulate 6 retrievals (60% rate - HIGH)
412
+ from headroom.cache.compression_store import RetrievalEvent
413
+
414
+ for i in range(6):
415
+ event = RetrievalEvent(
416
+ hash=f"hash{i:012d}",
417
+ query="find errors",
418
+ items_retrieved=100,
419
+ total_items=1000,
420
+ tool_name=tool_name,
421
+ timestamp=time.time(),
422
+ retrieval_type="search",
423
+ )
424
+ feedback.record_retrieval(event)
425
+
426
+ # Get hints
427
+ hints = feedback.get_compression_hints(tool_name)
428
+
429
+ result.latency_ms = (time.perf_counter() - start) * 1000
430
+
431
+ # Verify hints recommend less aggressive compression
432
+ pattern = feedback.get_all_patterns().get(tool_name)
433
+
434
+ checks_passed = 0
435
+ total_checks = 3
436
+
437
+ # Check 1: Retrieval rate is tracked correctly
438
+ if pattern and abs(pattern.retrieval_rate - 0.6) < 0.01:
439
+ checks_passed += 1
440
+ else:
441
+ result.failures.append(
442
+ f"Retrieval rate incorrect: {pattern.retrieval_rate if pattern else 'N/A'}"
443
+ )
444
+
445
+ # Check 2: Hints suggest more items (>15 default)
446
+ if hints.max_items > 15:
447
+ checks_passed += 1
448
+ else:
449
+ result.failures.append(f"max_items not increased: {hints.max_items}")
450
+
451
+ # Check 3: Aggressiveness reduced (<0.7 default)
452
+ if hints.aggressiveness < 0.7:
453
+ checks_passed += 1
454
+ else:
455
+ result.failures.append(f"Aggressiveness not reduced: {hints.aggressiveness}")
456
+
457
+ result.passed = checks_passed == total_checks
458
+ result.retrieval_accuracy = checks_passed / total_checks
459
+
460
+ result.details = {
461
+ "compressions_recorded": 10,
462
+ "retrievals_recorded": 6,
463
+ "calculated_retrieval_rate": pattern.retrieval_rate if pattern else 0,
464
+ "recommended_max_items": hints.max_items,
465
+ "recommended_aggressiveness": hints.aggressiveness,
466
+ "reason": hints.reason,
467
+ }
468
+
469
+ return result
470
+
471
+
472
+ # =============================================================================
473
+ # TEST 6: Search Within Cached Content
474
+ # =============================================================================
475
+
476
+
477
+ def test_search_accuracy() -> RegressionResult:
478
+ """
479
+ Test that BM25 search within cached content finds relevant items.
480
+ """
481
+ result = RegressionResult(
482
+ name="Search Accuracy", description="Verify BM25 search finds relevant items in cache"
483
+ )
484
+
485
+ reset_compression_store()
486
+ store = get_compression_store()
487
+
488
+ # Generate log entries with specific error messages
489
+ items = []
490
+ for i in range(100):
491
+ if i in [15, 45, 78]:
492
+ # Target: authentication errors
493
+ items.append(
494
+ {
495
+ "id": i,
496
+ "level": "ERROR",
497
+ "message": "Authentication failed: invalid token",
498
+ "service": "auth-service",
499
+ }
500
+ )
501
+ elif i in [20, 60]:
502
+ # Other errors (should not match auth search)
503
+ items.append(
504
+ {
505
+ "id": i,
506
+ "level": "ERROR",
507
+ "message": "Database connection timeout",
508
+ "service": "db-service",
509
+ }
510
+ )
511
+ else:
512
+ items.append(
513
+ {
514
+ "id": i,
515
+ "level": "INFO",
516
+ "message": "Request processed successfully",
517
+ "service": "api-service",
518
+ }
519
+ )
520
+
521
+ result.total_needles = 3 # 3 auth errors
522
+
523
+ original_json = json.dumps(items)
524
+ compressed_json = json.dumps(items[:10])
525
+
526
+ # Store
527
+ hash_key = store.store(
528
+ original=original_json,
529
+ compressed=compressed_json,
530
+ original_item_count=100,
531
+ compressed_item_count=10,
532
+ tool_name="log_search",
533
+ )
534
+
535
+ start = time.perf_counter()
536
+
537
+ # Search for authentication errors
538
+ search_results = store.search(hash_key, "authentication failed token")
539
+
540
+ result.latency_ms = (time.perf_counter() - start) * 1000
541
+
542
+ # Count auth errors in results
543
+ auth_errors = [
544
+ item for item in search_results if "authentication" in item.get("message", "").lower()
545
+ ]
546
+
547
+ result.needles_retained = len(auth_errors)
548
+ result.retention_rate = result.needles_retained / result.total_needles
549
+ result.items_retrieved = len(search_results)
550
+
551
+ # Pass if at least 2 of 3 auth errors found
552
+ result.passed = result.needles_retained >= 2
553
+ result.retrieval_accuracy = result.retention_rate
554
+
555
+ if not result.passed:
556
+ result.failures.append(
557
+ f"Search found only {result.needles_retained}/{result.total_needles} auth errors"
558
+ )
559
+
560
+ result.details = {
561
+ "query": "authentication failed token",
562
+ "total_results": len(search_results),
563
+ "auth_errors_found": result.needles_retained,
564
+ "hash_key": hash_key,
565
+ }
566
+
567
+ return result
568
+
569
+
570
+ # =============================================================================
571
+ # TEST 7: CCR End-to-End Flow
572
+ # =============================================================================
573
+
574
+
575
+ def test_ccr_end_to_end() -> RegressionResult:
576
+ """
577
+ Test the complete CCR flow: compress → cache → retrieve → feedback.
578
+ """
579
+ result = RegressionResult(
580
+ name="CCR End-to-End Flow",
581
+ description="Verify complete compress-cache-retrieve cycle works",
582
+ )
583
+
584
+ reset_compression_store()
585
+ reset_compression_feedback()
586
+
587
+ store = get_compression_store()
588
+ feedback = get_compression_feedback()
589
+
590
+ # Generate data with known needles
591
+ items = []
592
+ for i in range(500):
593
+ if i == 123:
594
+ items.append(
595
+ {
596
+ "id": i,
597
+ "type": "critical_alert",
598
+ "message": "System overload detected",
599
+ "priority": "P0",
600
+ }
601
+ )
602
+ elif i in [50, 200, 400]:
603
+ items.append(
604
+ {
605
+ "id": i,
606
+ "type": "error",
607
+ "message": f"Error at position {i}",
608
+ "priority": "P1",
609
+ }
610
+ )
611
+ else:
612
+ items.append(
613
+ {
614
+ "id": i,
615
+ "type": "info",
616
+ "message": f"Normal operation {i}",
617
+ "priority": "P3",
618
+ }
619
+ )
620
+
621
+ result.total_needles = 4 # 1 critical + 3 errors
622
+
623
+ start = time.perf_counter()
624
+
625
+ # Step 1: Compress
626
+ config = SmartCrusherConfig(max_items_after_crush=20)
627
+ original_json = json.dumps(items)
628
+ compressed_json, was_modified, _ = smart_crush_tool_output(original_json, config)
629
+
630
+ # Step 2: Cache
631
+ hash_key = store.store(
632
+ original=original_json,
633
+ compressed=compressed_json,
634
+ original_item_count=500,
635
+ compressed_item_count=20,
636
+ tool_name="alert_search",
637
+ )
638
+
639
+ # Step 3: Record compression in feedback
640
+ feedback.record_compression("alert_search", 500, 20)
641
+
642
+ # Step 4: Retrieve and search
643
+ critical_results = store.search(hash_key, "critical system overload P0")
644
+ error_results = store.search(hash_key, "Error position P1")
645
+
646
+ # Step 5: Process feedback
647
+ store.process_pending_feedback()
648
+
649
+ result.latency_ms = (time.perf_counter() - start) * 1000
650
+
651
+ # Verify results
652
+ checks_passed = 0
653
+ total_checks = 4
654
+
655
+ # Check 1: Critical alert found
656
+ critical_found = any(item.get("type") == "critical_alert" for item in critical_results)
657
+ if critical_found:
658
+ checks_passed += 1
659
+ else:
660
+ result.failures.append("Critical alert not found in search")
661
+
662
+ # Check 2: Errors found (search by message content)
663
+ errors_found = len(
664
+ [
665
+ item
666
+ for item in error_results
667
+ if item.get("type") == "error" or "Error" in str(item.get("message", ""))
668
+ ]
669
+ )
670
+ if errors_found >= 2:
671
+ checks_passed += 1
672
+ else:
673
+ result.failures.append(f"Only {errors_found} errors found in search")
674
+
675
+ # Check 3: Store has entry
676
+ if store.exists(hash_key):
677
+ checks_passed += 1
678
+ else:
679
+ result.failures.append("Entry not found in store")
680
+
681
+ # Check 4: Feedback recorded
682
+ patterns = feedback.get_all_patterns()
683
+ if "alert_search" in patterns:
684
+ checks_passed += 1
685
+ else:
686
+ result.failures.append("Feedback not recorded for tool")
687
+
688
+ result.passed = checks_passed == total_checks
689
+ result.needles_retained = (1 if critical_found else 0) + errors_found
690
+ result.retention_rate = result.needles_retained / result.total_needles
691
+ result.items_retrieved = len(critical_results) + len(error_results)
692
+ result.retrieval_accuracy = checks_passed / total_checks
693
+
694
+ result.details = {
695
+ "hash_key": hash_key,
696
+ "critical_found": critical_found,
697
+ "errors_found": errors_found,
698
+ "store_entry_exists": store.exists(hash_key),
699
+ "feedback_recorded": "alert_search" in patterns,
700
+ }
701
+
702
+ return result
703
+
704
+
705
+ # =============================================================================
706
+ # REPORT GENERATION
707
+ # =============================================================================
708
+
709
+
710
+ def generate_report(results: list[RegressionResult], verbose: bool = False) -> str:
711
+ """Generate benchmark report."""
712
+ lines = []
713
+
714
+ lines.append("")
715
+ lines.append("=" * 70)
716
+ lines.append(" CCR REGRESSION BENCHMARK")
717
+ lines.append(" Verifying No Information Loss")
718
+ lines.append("=" * 70)
719
+
720
+ passed = sum(1 for r in results if r.passed)
721
+ total = len(results)
722
+
723
+ lines.append("")
724
+ lines.append(f" Overall: {passed}/{total} tests passed")
725
+ lines.append("")
726
+
727
+ for result in results:
728
+ status = "✓ PASS" if result.passed else "✗ FAIL"
729
+ lines.append(f"{'─' * 70}")
730
+ lines.append(f" {status} {result.name}")
731
+ lines.append(f" {result.description}")
732
+
733
+ if result.total_needles > 0:
734
+ lines.append(
735
+ f" Needles: {result.needles_retained}/{result.total_needles} retained ({result.retention_rate * 100:.0f}%)"
736
+ )
737
+
738
+ if result.items_retrieved > 0:
739
+ lines.append(f" Retrieved: {result.items_retrieved} items")
740
+
741
+ lines.append(f" Latency: {result.latency_ms:.2f}ms")
742
+
743
+ if not result.passed:
744
+ for failure in result.failures:
745
+ lines.append(f" ❌ {failure}")
746
+
747
+ if verbose and result.details:
748
+ lines.append(f" Details: {json.dumps(result.details, indent=2)}")
749
+
750
+ lines.append("")
751
+ lines.append("=" * 70)
752
+
753
+ if passed == total:
754
+ lines.append(" ✓ ALL TESTS PASSED - No regression detected")
755
+ else:
756
+ lines.append(f" ✗ {total - passed} TESTS FAILED - Review failures above")
757
+
758
+ lines.append("=" * 70)
759
+ lines.append("")
760
+
761
+ return "\n".join(lines)
762
+
763
+
764
+ # =============================================================================
765
+ # MAIN
766
+ # =============================================================================
767
+
768
+
769
+ def main():
770
+ parser = argparse.ArgumentParser(description="CCR Regression Benchmark")
771
+ parser.add_argument("--verbose", "-v", action="store_true", help="Show detailed output")
772
+ parser.add_argument(
773
+ "--scenario",
774
+ choices=[
775
+ "all",
776
+ "error-retention",
777
+ "uuid-retrieval",
778
+ "anomaly-retention",
779
+ "full-retrieval",
780
+ "feedback-learning",
781
+ "search-accuracy",
782
+ "e2e",
783
+ ],
784
+ default="all",
785
+ )
786
+ args = parser.parse_args()
787
+
788
+ results = []
789
+
790
+ print("\nRunning CCR regression tests...\n")
791
+
792
+ if args.scenario in ("all", "error-retention"):
793
+ print(" [1/7] Error Retention...")
794
+ results.append(test_error_retention())
795
+
796
+ if args.scenario in ("all", "uuid-retrieval"):
797
+ print(" [2/7] UUID Retrieval...")
798
+ results.append(test_uuid_retrieval())
799
+
800
+ if args.scenario in ("all", "anomaly-retention"):
801
+ print(" [3/7] Anomaly Retention...")
802
+ results.append(test_anomaly_retention())
803
+
804
+ if args.scenario in ("all", "full-retrieval"):
805
+ print(" [4/7] Full Retrieval...")
806
+ results.append(test_full_retrieval())
807
+
808
+ if args.scenario in ("all", "feedback-learning"):
809
+ print(" [5/7] Feedback Learning...")
810
+ results.append(test_feedback_learning())
811
+
812
+ if args.scenario in ("all", "search-accuracy"):
813
+ print(" [6/7] Search Accuracy...")
814
+ results.append(test_search_accuracy())
815
+
816
+ if args.scenario in ("all", "e2e"):
817
+ print(" [7/7] End-to-End Flow...")
818
+ results.append(test_ccr_end_to_end())
819
+
820
+ print(generate_report(results, args.verbose))
821
+
822
+ # Exit with error code if any test failed
823
+ failed = sum(1 for r in results if not r.passed)
824
+ exit(failed)
825
+
826
+
827
+ if __name__ == "__main__":
828
+ main()
benchmarks/conftest.py CHANGED
@@ -15,21 +15,19 @@ from __future__ import annotations
15
  import json
16
  import random
17
  from typing import Any
18
- from unittest.mock import Mock
19
 
20
  import pytest
21
 
 
 
 
 
22
  from benchmarks.scenarios.tool_outputs import (
23
  generate_api_responses,
24
  generate_database_rows,
25
  generate_log_entries,
26
  generate_search_results,
27
  )
28
- from benchmarks.scenarios.conversations import (
29
- generate_agentic_conversation,
30
- generate_rag_conversation,
31
- )
32
-
33
 
34
  # Set seed for reproducible benchmarks
35
  random.seed(42)
@@ -159,21 +157,27 @@ def api_responses_100() -> list[dict[str, Any]]:
159
  def conversation_10_turns() -> list[dict[str, Any]]:
160
  """Generate 10-turn agentic conversation with tool calls."""
161
  random.seed(42)
162
- return generate_agentic_conversation(turns=10, tool_calls_per_turn=1, items_per_tool_response=50)
 
 
163
 
164
 
165
  @pytest.fixture
166
  def conversation_50_turns() -> list[dict[str, Any]]:
167
  """Generate 50-turn agentic conversation with tool calls."""
168
  random.seed(42)
169
- return generate_agentic_conversation(turns=50, tool_calls_per_turn=2, items_per_tool_response=50)
 
 
170
 
171
 
172
  @pytest.fixture
173
  def conversation_200_turns() -> list[dict[str, Any]]:
174
  """Generate 200-turn agentic conversation (stress test)."""
175
  random.seed(42)
176
- return generate_agentic_conversation(turns=200, tool_calls_per_turn=1, items_per_tool_response=30)
 
 
177
 
178
 
179
  @pytest.fixture
 
15
  import json
16
  import random
17
  from typing import Any
 
18
 
19
  import pytest
20
 
21
+ from benchmarks.scenarios.conversations import (
22
+ generate_agentic_conversation,
23
+ generate_rag_conversation,
24
+ )
25
  from benchmarks.scenarios.tool_outputs import (
26
  generate_api_responses,
27
  generate_database_rows,
28
  generate_log_entries,
29
  generate_search_results,
30
  )
 
 
 
 
 
31
 
32
  # Set seed for reproducible benchmarks
33
  random.seed(42)
 
157
  def conversation_10_turns() -> list[dict[str, Any]]:
158
  """Generate 10-turn agentic conversation with tool calls."""
159
  random.seed(42)
160
+ return generate_agentic_conversation(
161
+ turns=10, tool_calls_per_turn=1, items_per_tool_response=50
162
+ )
163
 
164
 
165
  @pytest.fixture
166
  def conversation_50_turns() -> list[dict[str, Any]]:
167
  """Generate 50-turn agentic conversation with tool calls."""
168
  random.seed(42)
169
+ return generate_agentic_conversation(
170
+ turns=50, tool_calls_per_turn=2, items_per_tool_response=50
171
+ )
172
 
173
 
174
  @pytest.fixture
175
  def conversation_200_turns() -> list[dict[str, Any]]:
176
  """Generate 200-turn agentic conversation (stress test)."""
177
  random.seed(42)
178
+ return generate_agentic_conversation(
179
+ turns=200, tool_calls_per_turn=1, items_per_tool_response=30
180
+ )
181
 
182
 
183
  @pytest.fixture
benchmarks/dynamic_detector_benchmark.py CHANGED
@@ -6,21 +6,21 @@ Tests the detector against realistic system prompts from AI coding agents,
6
  chatbots, and enterprise applications.
7
  """
8
 
9
- import time
10
  import statistics
 
11
  from dataclasses import dataclass
12
  from typing import Any
13
 
14
  from headroom.cache.dynamic_detector import (
15
  DetectorConfig,
16
  DynamicContentDetector,
17
- DynamicCategory,
18
  )
19
 
20
 
21
  @dataclass
22
  class BenchmarkResult:
23
  """Result of a single benchmark run."""
 
24
  name: str
25
  content_length: int
26
  spans_found: int
@@ -50,7 +50,6 @@ User: tchopra
50
  Workspace: /Users/tchopra/claude-projects/headroom
51
 
52
  Be concise, accurate, and helpful. Follow the user's instructions carefully.""",
53
-
54
  "enterprise_assistant": """You are an enterprise AI assistant for Acme Corporation.
55
 
56
  Current Date: 2026-01-07T10:30:00Z
@@ -76,7 +75,6 @@ Budget Information:
76
  - Remaining: $2,658.33
77
 
78
  Help the user with their work tasks while following company policies.""",
79
-
80
  "coding_agent": """You are an autonomous coding agent with access to tools.
81
 
82
  Environment:
@@ -99,7 +97,6 @@ API Keys Available:
99
  - DATABASE_URL: postgresql://user:pass@localhost:5432/mydb
100
 
101
  Execute tasks step by step, verify each action, and report progress.""",
102
-
103
  "customer_support": """You are a customer support agent for TechStore Inc.
104
 
105
  Current Time: January 7, 2026, 3:45 PM EST
@@ -122,7 +119,6 @@ Active Issues:
122
  - Case #CS-2026-0107-001 - Battery drain issue - Open since today
123
 
124
  Provide helpful, empathetic support while following company guidelines.""",
125
-
126
  "data_analysis": """You are a data analysis assistant.
127
 
128
  Report Generated: 2026-01-07 10:30:00 UTC
@@ -147,7 +143,6 @@ Anomalies Detected:
147
  - Drop on Dec 25: 0.4x normal (expected - holiday)
148
 
149
  Help analyze the data and provide insights.""",
150
-
151
  "minimal_static": """You are a helpful AI assistant.
152
 
153
  Your role is to:
@@ -157,7 +152,6 @@ Your role is to:
157
  4. Admit when you don't know something
158
 
159
  Always be helpful, harmless, and honest.""",
160
-
161
  "heavy_dynamic": """Session started at 2026-01-07T10:30:45.123Z
162
  Request ID: req_abc123def456ghi789jkl012mno345pqr678
163
  Trace ID: 550e8400-e29b-41d4-a716-446655440000
@@ -205,19 +199,21 @@ def run_benchmark(
205
  result = detector.detect(content)
206
  elapsed = (time.perf_counter() - start) * 1000
207
 
208
- categories = list(set(s.category.value for s in result.spans))
209
-
210
- results[name].append(BenchmarkResult(
211
- name=name,
212
- content_length=len(content),
213
- spans_found=len(result.spans),
214
- categories=categories,
215
- static_length=len(result.static_content),
216
- dynamic_length=len(result.dynamic_content),
217
- latency_ms=elapsed,
218
- tiers_used=result.tiers_used,
219
- warnings=result.warnings,
220
- ))
 
 
221
 
222
  return results
223
 
@@ -228,9 +224,9 @@ def print_results(
228
  ):
229
  """Print benchmark results."""
230
 
231
- print(f"\n{'='*80}")
232
  print(f"BENCHMARK RESULTS: {tier_name}")
233
- print(f"{'='*80}")
234
 
235
  for name, runs in results.items():
236
  latencies = [r.latency_ms for r in runs]
@@ -240,7 +236,11 @@ def print_results(
240
  # Use first run for span info (consistent across runs)
241
  first = runs[0]
242
 
243
- compression = (1 - first.static_length / first.content_length) * 100 if first.content_length > 0 else 0
 
 
 
 
244
 
245
  print(f"\n📄 {name}")
246
  print(f" Content: {first.content_length:,} chars")
@@ -257,9 +257,9 @@ def print_results(
257
  def print_comparison(all_results: dict[str, dict[str, list[BenchmarkResult]]]):
258
  """Print comparison across tiers."""
259
 
260
- print(f"\n{'='*80}")
261
  print("TIER COMPARISON")
262
- print(f"{'='*80}")
263
 
264
  prompts = list(REAL_WORLD_PROMPTS.keys())
265
  tiers = list(all_results.keys())
@@ -284,9 +284,9 @@ def print_comparison(all_results: dict[str, dict[str, list[BenchmarkResult]]]):
284
  print(row)
285
 
286
  # Summary
287
- print(f"\n{'='*80}")
288
  print("SUMMARY")
289
- print(f"{'='*80}")
290
 
291
  for tier in tiers:
292
  all_latencies = []
@@ -297,7 +297,9 @@ def print_comparison(all_results: dict[str, dict[str, list[BenchmarkResult]]]):
297
 
298
  avg = statistics.mean(all_latencies)
299
  p50 = statistics.median(all_latencies)
300
- p99 = sorted(all_latencies)[int(len(all_latencies) * 0.99)] if len(all_latencies) > 1 else avg
 
 
301
 
302
  print(f"\n{tier}:")
303
  print(f" Total spans detected: {total_spans}")
@@ -309,9 +311,9 @@ def print_comparison(all_results: dict[str, dict[str, list[BenchmarkResult]]]):
309
  def show_detection_details(prompt_name: str, content: str):
310
  """Show detailed detection for a specific prompt."""
311
 
312
- print(f"\n{'='*80}")
313
  print(f"DETECTION DETAILS: {prompt_name}")
314
- print(f"{'='*80}")
315
 
316
  config = DetectorConfig(tiers=["regex"])
317
  detector = DynamicContentDetector(config)
@@ -324,11 +326,17 @@ def show_detection_details(prompt_name: str, content: str):
324
  print(f"\n\nDetected spans ({len(result.spans)}):")
325
  print("-" * 40)
326
  for span in result.spans:
327
- print(f" [{span.category.value:12}] '{span.text[:50]}{'...' if len(span.text) > 50 else ''}'")
 
 
328
 
329
  print(f"\n\nStatic content ({len(result.static_content)} chars):")
330
  print("-" * 40)
331
- print(result.static_content[:500] + "..." if len(result.static_content) > 500 else result.static_content)
 
 
 
 
332
 
333
  print(f"\n\nDynamic content ({len(result.dynamic_content)} chars):")
334
  print("-" * 40)
 
6
  chatbots, and enterprise applications.
7
  """
8
 
 
9
  import statistics
10
+ import time
11
  from dataclasses import dataclass
12
  from typing import Any
13
 
14
  from headroom.cache.dynamic_detector import (
15
  DetectorConfig,
16
  DynamicContentDetector,
 
17
  )
18
 
19
 
20
  @dataclass
21
  class BenchmarkResult:
22
  """Result of a single benchmark run."""
23
+
24
  name: str
25
  content_length: int
26
  spans_found: int
 
50
  Workspace: /Users/tchopra/claude-projects/headroom
51
 
52
  Be concise, accurate, and helpful. Follow the user's instructions carefully.""",
 
53
  "enterprise_assistant": """You are an enterprise AI assistant for Acme Corporation.
54
 
55
  Current Date: 2026-01-07T10:30:00Z
 
75
  - Remaining: $2,658.33
76
 
77
  Help the user with their work tasks while following company policies.""",
 
78
  "coding_agent": """You are an autonomous coding agent with access to tools.
79
 
80
  Environment:
 
97
  - DATABASE_URL: postgresql://user:pass@localhost:5432/mydb
98
 
99
  Execute tasks step by step, verify each action, and report progress.""",
 
100
  "customer_support": """You are a customer support agent for TechStore Inc.
101
 
102
  Current Time: January 7, 2026, 3:45 PM EST
 
119
  - Case #CS-2026-0107-001 - Battery drain issue - Open since today
120
 
121
  Provide helpful, empathetic support while following company guidelines.""",
 
122
  "data_analysis": """You are a data analysis assistant.
123
 
124
  Report Generated: 2026-01-07 10:30:00 UTC
 
143
  - Drop on Dec 25: 0.4x normal (expected - holiday)
144
 
145
  Help analyze the data and provide insights.""",
 
146
  "minimal_static": """You are a helpful AI assistant.
147
 
148
  Your role is to:
 
152
  4. Admit when you don't know something
153
 
154
  Always be helpful, harmless, and honest.""",
 
155
  "heavy_dynamic": """Session started at 2026-01-07T10:30:45.123Z
156
  Request ID: req_abc123def456ghi789jkl012mno345pqr678
157
  Trace ID: 550e8400-e29b-41d4-a716-446655440000
 
199
  result = detector.detect(content)
200
  elapsed = (time.perf_counter() - start) * 1000
201
 
202
+ categories = list({s.category.value for s in result.spans})
203
+
204
+ results[name].append(
205
+ BenchmarkResult(
206
+ name=name,
207
+ content_length=len(content),
208
+ spans_found=len(result.spans),
209
+ categories=categories,
210
+ static_length=len(result.static_content),
211
+ dynamic_length=len(result.dynamic_content),
212
+ latency_ms=elapsed,
213
+ tiers_used=result.tiers_used,
214
+ warnings=result.warnings,
215
+ )
216
+ )
217
 
218
  return results
219
 
 
224
  ):
225
  """Print benchmark results."""
226
 
227
+ print(f"\n{'=' * 80}")
228
  print(f"BENCHMARK RESULTS: {tier_name}")
229
+ print(f"{'=' * 80}")
230
 
231
  for name, runs in results.items():
232
  latencies = [r.latency_ms for r in runs]
 
236
  # Use first run for span info (consistent across runs)
237
  first = runs[0]
238
 
239
+ compression = (
240
+ (1 - first.static_length / first.content_length) * 100
241
+ if first.content_length > 0
242
+ else 0
243
+ )
244
 
245
  print(f"\n📄 {name}")
246
  print(f" Content: {first.content_length:,} chars")
 
257
  def print_comparison(all_results: dict[str, dict[str, list[BenchmarkResult]]]):
258
  """Print comparison across tiers."""
259
 
260
+ print(f"\n{'=' * 80}")
261
  print("TIER COMPARISON")
262
+ print(f"{'=' * 80}")
263
 
264
  prompts = list(REAL_WORLD_PROMPTS.keys())
265
  tiers = list(all_results.keys())
 
284
  print(row)
285
 
286
  # Summary
287
+ print(f"\n{'=' * 80}")
288
  print("SUMMARY")
289
+ print(f"{'=' * 80}")
290
 
291
  for tier in tiers:
292
  all_latencies = []
 
297
 
298
  avg = statistics.mean(all_latencies)
299
  p50 = statistics.median(all_latencies)
300
+ p99 = (
301
+ sorted(all_latencies)[int(len(all_latencies) * 0.99)] if len(all_latencies) > 1 else avg
302
+ )
303
 
304
  print(f"\n{tier}:")
305
  print(f" Total spans detected: {total_spans}")
 
311
  def show_detection_details(prompt_name: str, content: str):
312
  """Show detailed detection for a specific prompt."""
313
 
314
+ print(f"\n{'=' * 80}")
315
  print(f"DETECTION DETAILS: {prompt_name}")
316
+ print(f"{'=' * 80}")
317
 
318
  config = DetectorConfig(tiers=["regex"])
319
  detector = DynamicContentDetector(config)
 
326
  print(f"\n\nDetected spans ({len(result.spans)}):")
327
  print("-" * 40)
328
  for span in result.spans:
329
+ print(
330
+ f" [{span.category.value:12}] '{span.text[:50]}{'...' if len(span.text) > 50 else ''}'"
331
+ )
332
 
333
  print(f"\n\nStatic content ({len(result.static_content)} chars):")
334
  print("-" * 40)
335
+ print(
336
+ result.static_content[:500] + "..."
337
+ if len(result.static_content) > 500
338
+ else result.static_content
339
+ )
340
 
341
  print(f"\n\nDynamic content ({len(result.dynamic_content)} chars):")
342
  print("-" * 40)
benchmarks/run_benchmarks.py CHANGED
@@ -40,7 +40,6 @@ from datetime import datetime
40
  from pathlib import Path
41
  from typing import Any
42
 
43
-
44
  # Benchmark suite definitions
45
  BENCHMARK_SUITES = {
46
  "all": [
@@ -75,19 +74,19 @@ BENCHMARK_SUITES = {
75
 
76
  # Performance targets (mean time in microseconds)
77
  PERFORMANCE_TARGETS = {
78
- "test_compress_100_items": 2000, # 2ms
79
- "test_compress_1000_items": 10000, # 10ms
80
- "test_compress_10000_items": 100000, # 100ms
81
- "test_date_extraction": 1000, # 1ms
82
- "test_hash_computation": 500, # 0.5ms
83
- "test_window_50_turns": 5000, # 5ms
84
- "test_window_200_turns": 20000, # 20ms
85
- "test_single_item": 100, # 0.1ms
86
- "test_batch_100": 1000, # 1ms
87
- "test_batch_1000": 10000, # 10ms
88
- "test_pipeline_simple": 5000, # 5ms
89
- "test_pipeline_agentic": 30000, # 30ms
90
- "test_pipeline_rag": 50000, # 50ms
91
  }
92
 
93
 
@@ -243,8 +242,8 @@ def generate_markdown_report(
243
  if total > 0:
244
  lines.append("## Summary")
245
  lines.append("")
246
- lines.append(f"- **Passed**: {passed}/{total} ({100*passed/total:.0f}%)")
247
- lines.append(f"- **Failed**: {failed}/{total} ({100*failed/total:.0f}%)")
248
  lines.append("")
249
 
250
  # Performance notes
@@ -274,9 +273,9 @@ def _format_time(microseconds: float) -> str:
274
  if microseconds < 1000:
275
  return f"{microseconds:.1f}us"
276
  elif microseconds < 1_000_000:
277
- return f"{microseconds/1000:.2f}ms"
278
  else:
279
- return f"{microseconds/1_000_000:.2f}s"
280
 
281
 
282
  def main() -> int:
 
40
  from pathlib import Path
41
  from typing import Any
42
 
 
43
  # Benchmark suite definitions
44
  BENCHMARK_SUITES = {
45
  "all": [
 
74
 
75
  # Performance targets (mean time in microseconds)
76
  PERFORMANCE_TARGETS = {
77
+ "test_compress_100_items": 2000, # 2ms
78
+ "test_compress_1000_items": 10000, # 10ms
79
+ "test_compress_10000_items": 100000, # 100ms
80
+ "test_date_extraction": 1000, # 1ms
81
+ "test_hash_computation": 500, # 0.5ms
82
+ "test_window_50_turns": 5000, # 5ms
83
+ "test_window_200_turns": 20000, # 20ms
84
+ "test_single_item": 100, # 0.1ms
85
+ "test_batch_100": 1000, # 1ms
86
+ "test_batch_1000": 10000, # 10ms
87
+ "test_pipeline_simple": 5000, # 5ms
88
+ "test_pipeline_agentic": 30000, # 30ms
89
+ "test_pipeline_rag": 50000, # 50ms
90
  }
91
 
92
 
 
242
  if total > 0:
243
  lines.append("## Summary")
244
  lines.append("")
245
+ lines.append(f"- **Passed**: {passed}/{total} ({100 * passed / total:.0f}%)")
246
+ lines.append(f"- **Failed**: {failed}/{total} ({100 * failed / total:.0f}%)")
247
  lines.append("")
248
 
249
  # Performance notes
 
273
  if microseconds < 1000:
274
  return f"{microseconds:.1f}us"
275
  elif microseconds < 1_000_000:
276
+ return f"{microseconds / 1000:.2f}ms"
277
  else:
278
+ return f"{microseconds / 1_000_000:.2f}s"
279
 
280
 
281
  def main() -> int:
benchmarks/scenarios/__init__.py CHANGED
@@ -8,16 +8,16 @@ Modules:
8
  conversations: Generators for conversation history (agentic, RAG)
9
  """
10
 
 
 
 
 
11
  from .tool_outputs import (
12
  generate_api_responses,
13
  generate_database_rows,
14
  generate_log_entries,
15
  generate_search_results,
16
  )
17
- from .conversations import (
18
- generate_agentic_conversation,
19
- generate_rag_conversation,
20
- )
21
 
22
  __all__ = [
23
  "generate_search_results",
 
8
  conversations: Generators for conversation history (agentic, RAG)
9
  """
10
 
11
+ from .conversations import (
12
+ generate_agentic_conversation,
13
+ generate_rag_conversation,
14
+ )
15
  from .tool_outputs import (
16
  generate_api_responses,
17
  generate_database_rows,
18
  generate_log_entries,
19
  generate_search_results,
20
  )
 
 
 
 
21
 
22
  __all__ = [
23
  "generate_search_results",
benchmarks/scenarios/conversations.py CHANGED
@@ -53,19 +53,23 @@ def generate_agentic_conversation(
53
  messages = []
54
 
55
  # System prompt
56
- messages.append({
57
- "role": "system",
58
- "content": _generate_system_prompt(),
59
- })
 
 
60
 
61
  # Generate turns
62
  for turn_idx in range(turns):
63
  # User message
64
  user_query = _generate_user_query(turn_idx)
65
- messages.append({
66
- "role": "user",
67
- "content": user_query,
68
- })
 
 
69
 
70
  # Assistant with tool calls
71
  num_calls = max(1, tool_calls_per_turn + random.randint(-1, 1))
@@ -75,20 +79,24 @@ def generate_agentic_conversation(
75
  tool_name, arguments = _generate_tool_call(turn_idx, call_idx)
76
  call_id = f"call_{uuid.uuid4().hex[:16]}"
77
 
78
- tool_calls.append({
79
- "id": call_id,
80
- "type": "function",
81
- "function": {
82
- "name": tool_name,
83
- "arguments": json.dumps(arguments),
84
- },
85
- })
 
 
86
 
87
- messages.append({
88
- "role": "assistant",
89
- "content": None,
90
- "tool_calls": tool_calls,
91
- })
 
 
92
 
93
  # Tool responses
94
  for tool_call in tool_calls:
@@ -96,18 +104,22 @@ def generate_agentic_conversation(
96
  tool_call["function"]["name"],
97
  items_per_tool_response,
98
  )
99
- messages.append({
100
- "role": "tool",
101
- "tool_call_id": tool_call["id"],
102
- "content": json.dumps(tool_response),
103
- })
 
 
104
 
105
  # Assistant summary (most turns, not all)
106
  if random.random() < 0.8:
107
- messages.append({
108
- "role": "assistant",
109
- "content": _generate_assistant_summary(turn_idx, tool_calls),
110
- })
 
 
111
 
112
  return messages
113
 
@@ -137,39 +149,49 @@ def generate_rag_conversation(
137
  messages = []
138
 
139
  # System prompt with date (for CacheAligner testing)
140
- messages.append({
141
- "role": "system",
142
- "content": _generate_rag_system_prompt(),
143
- })
 
 
144
 
145
  # Generate context documents
146
  context_content = _generate_rag_context(context_tokens)
147
 
148
  # Inject context as first user message
149
- messages.append({
150
- "role": "user",
151
- "content": f"Here are the relevant documents for context:\n\n{context_content}\n\nPlease analyze these documents.",
152
- })
 
 
153
 
154
  # Assistant acknowledgment
155
- messages.append({
156
- "role": "assistant",
157
- "content": "I've reviewed the provided documents. I can see information about technical documentation, API specifications, and configuration guides. What would you like to know?",
158
- })
 
 
159
 
160
  # Generate Q&A turns
161
  for i in range(num_queries):
162
  question = _generate_rag_question(i)
163
- messages.append({
164
- "role": "user",
165
- "content": question,
166
- })
 
 
167
 
168
  answer = _generate_rag_answer(i)
169
- messages.append({
170
- "role": "assistant",
171
- "content": answer,
172
- })
 
 
173
 
174
  return messages
175
 
@@ -195,17 +217,21 @@ def generate_anthropic_agentic_conversation(
195
  messages = []
196
 
197
  # System message (Anthropic uses separate system parameter, but we include it)
198
- messages.append({
199
- "role": "system",
200
- "content": _generate_system_prompt(),
201
- })
 
 
202
 
203
  for turn_idx in range(turns):
204
  # User message
205
- messages.append({
206
- "role": "user",
207
- "content": [{"type": "text", "text": _generate_user_query(turn_idx)}],
208
- })
 
 
209
 
210
  # Assistant with tool_use blocks
211
  num_calls = max(1, tool_calls_per_turn + random.randint(-1, 1))
@@ -215,17 +241,21 @@ def generate_anthropic_agentic_conversation(
215
  tool_name, arguments = _generate_tool_call(turn_idx, call_idx)
216
  tool_use_id = f"toolu_{uuid.uuid4().hex[:16]}"
217
 
218
- content_blocks.append({
219
- "type": "tool_use",
220
- "id": tool_use_id,
221
- "name": tool_name,
222
- "input": arguments,
223
- })
 
 
224
 
225
- messages.append({
226
- "role": "assistant",
227
- "content": content_blocks,
228
- })
 
 
229
 
230
  # Tool results in user message
231
  tool_results = []
@@ -234,29 +264,38 @@ def generate_anthropic_agentic_conversation(
234
  block["name"],
235
  items_per_tool_response,
236
  )
237
- tool_results.append({
238
- "type": "tool_result",
239
- "tool_use_id": block["id"],
240
- "content": json.dumps(tool_response),
241
- })
 
 
242
 
243
- messages.append({
244
- "role": "user",
245
- "content": tool_results,
246
- })
 
 
247
 
248
  # Assistant response
249
  if random.random() < 0.8:
250
- messages.append({
251
- "role": "assistant",
252
- "content": [{"type": "text", "text": _generate_assistant_summary(turn_idx, [])}],
253
- })
 
 
 
 
254
 
255
  return messages
256
 
257
 
258
  # Helper functions
259
 
 
260
  def _generate_system_prompt() -> str:
261
  """Generate a realistic system prompt."""
262
  return """You are an AI assistant with access to various tools for searching, querying, and analyzing data.
 
53
  messages = []
54
 
55
  # System prompt
56
+ messages.append(
57
+ {
58
+ "role": "system",
59
+ "content": _generate_system_prompt(),
60
+ }
61
+ )
62
 
63
  # Generate turns
64
  for turn_idx in range(turns):
65
  # User message
66
  user_query = _generate_user_query(turn_idx)
67
+ messages.append(
68
+ {
69
+ "role": "user",
70
+ "content": user_query,
71
+ }
72
+ )
73
 
74
  # Assistant with tool calls
75
  num_calls = max(1, tool_calls_per_turn + random.randint(-1, 1))
 
79
  tool_name, arguments = _generate_tool_call(turn_idx, call_idx)
80
  call_id = f"call_{uuid.uuid4().hex[:16]}"
81
 
82
+ tool_calls.append(
83
+ {
84
+ "id": call_id,
85
+ "type": "function",
86
+ "function": {
87
+ "name": tool_name,
88
+ "arguments": json.dumps(arguments),
89
+ },
90
+ }
91
+ )
92
 
93
+ messages.append(
94
+ {
95
+ "role": "assistant",
96
+ "content": None,
97
+ "tool_calls": tool_calls,
98
+ }
99
+ )
100
 
101
  # Tool responses
102
  for tool_call in tool_calls:
 
104
  tool_call["function"]["name"],
105
  items_per_tool_response,
106
  )
107
+ messages.append(
108
+ {
109
+ "role": "tool",
110
+ "tool_call_id": tool_call["id"],
111
+ "content": json.dumps(tool_response),
112
+ }
113
+ )
114
 
115
  # Assistant summary (most turns, not all)
116
  if random.random() < 0.8:
117
+ messages.append(
118
+ {
119
+ "role": "assistant",
120
+ "content": _generate_assistant_summary(turn_idx, tool_calls),
121
+ }
122
+ )
123
 
124
  return messages
125
 
 
149
  messages = []
150
 
151
  # System prompt with date (for CacheAligner testing)
152
+ messages.append(
153
+ {
154
+ "role": "system",
155
+ "content": _generate_rag_system_prompt(),
156
+ }
157
+ )
158
 
159
  # Generate context documents
160
  context_content = _generate_rag_context(context_tokens)
161
 
162
  # Inject context as first user message
163
+ messages.append(
164
+ {
165
+ "role": "user",
166
+ "content": f"Here are the relevant documents for context:\n\n{context_content}\n\nPlease analyze these documents.",
167
+ }
168
+ )
169
 
170
  # Assistant acknowledgment
171
+ messages.append(
172
+ {
173
+ "role": "assistant",
174
+ "content": "I've reviewed the provided documents. I can see information about technical documentation, API specifications, and configuration guides. What would you like to know?",
175
+ }
176
+ )
177
 
178
  # Generate Q&A turns
179
  for i in range(num_queries):
180
  question = _generate_rag_question(i)
181
+ messages.append(
182
+ {
183
+ "role": "user",
184
+ "content": question,
185
+ }
186
+ )
187
 
188
  answer = _generate_rag_answer(i)
189
+ messages.append(
190
+ {
191
+ "role": "assistant",
192
+ "content": answer,
193
+ }
194
+ )
195
 
196
  return messages
197
 
 
217
  messages = []
218
 
219
  # System message (Anthropic uses separate system parameter, but we include it)
220
+ messages.append(
221
+ {
222
+ "role": "system",
223
+ "content": _generate_system_prompt(),
224
+ }
225
+ )
226
 
227
  for turn_idx in range(turns):
228
  # User message
229
+ messages.append(
230
+ {
231
+ "role": "user",
232
+ "content": [{"type": "text", "text": _generate_user_query(turn_idx)}],
233
+ }
234
+ )
235
 
236
  # Assistant with tool_use blocks
237
  num_calls = max(1, tool_calls_per_turn + random.randint(-1, 1))
 
241
  tool_name, arguments = _generate_tool_call(turn_idx, call_idx)
242
  tool_use_id = f"toolu_{uuid.uuid4().hex[:16]}"
243
 
244
+ content_blocks.append(
245
+ {
246
+ "type": "tool_use",
247
+ "id": tool_use_id,
248
+ "name": tool_name,
249
+ "input": arguments,
250
+ }
251
+ )
252
 
253
+ messages.append(
254
+ {
255
+ "role": "assistant",
256
+ "content": content_blocks,
257
+ }
258
+ )
259
 
260
  # Tool results in user message
261
  tool_results = []
 
264
  block["name"],
265
  items_per_tool_response,
266
  )
267
+ tool_results.append(
268
+ {
269
+ "type": "tool_result",
270
+ "tool_use_id": block["id"],
271
+ "content": json.dumps(tool_response),
272
+ }
273
+ )
274
 
275
+ messages.append(
276
+ {
277
+ "role": "user",
278
+ "content": tool_results,
279
+ }
280
+ )
281
 
282
  # Assistant response
283
  if random.random() < 0.8:
284
+ messages.append(
285
+ {
286
+ "role": "assistant",
287
+ "content": [
288
+ {"type": "text", "text": _generate_assistant_summary(turn_idx, [])}
289
+ ],
290
+ }
291
+ )
292
 
293
  return messages
294
 
295
 
296
  # Helper functions
297
 
298
+
299
  def _generate_system_prompt() -> str:
300
  """Generate a realistic system prompt."""
301
  return """You are an AI assistant with access to various tools for searching, querying, and analyzing data.
benchmarks/scenarios/tool_outputs.py CHANGED
@@ -14,9 +14,7 @@ and compression strategies.
14
 
15
  from __future__ import annotations
16
 
17
- import json
18
  import random
19
- import string
20
  import uuid
21
  from datetime import datetime, timedelta
22
  from typing import Any
@@ -80,12 +78,14 @@ def generate_search_results(
80
  min(include_errors, len(results) - len(needle_indices)),
81
  )
82
  for idx in error_indices:
83
- results[idx]["error"] = random.choice([
84
- "Index out of range",
85
- "Document not found",
86
- "Permission denied",
87
- "Timeout exceeded",
88
- ])
 
 
89
  results[idx]["status"] = "failed"
90
 
91
  return results
@@ -177,7 +177,9 @@ def generate_log_entries(
177
  # Add exception info for errors
178
  if level in ("ERROR", "CRITICAL"):
179
  entry["exception"] = {
180
- "type": random.choice(["TimeoutError", "ConnectionError", "ValueError", "RuntimeError"]),
 
 
181
  "message": message,
182
  "stacktrace": _generate_stacktrace(),
183
  }
@@ -273,11 +275,13 @@ def generate_database_rows(
273
  elif table_type == "transactions":
274
  row = _generate_transaction_row(i)
275
  else: # mixed
276
- generator = random.choice([
277
- _generate_user_row,
278
- lambda i: _generate_metric_row(i, mean_value, std_value),
279
- _generate_transaction_row,
280
- ])
 
 
281
  row = generator(i)
282
 
283
  rows.append(row)
@@ -296,6 +300,7 @@ def generate_database_rows(
296
 
297
  # Helper functions
298
 
 
299
  def _generate_title() -> str:
300
  """Generate a realistic document title."""
301
  prefixes = ["How to", "Guide to", "Understanding", "Introduction to", "Advanced"]
@@ -326,7 +331,9 @@ def _generate_name() -> str:
326
  def _generate_timestamp(offset_days: int = 0) -> str:
327
  """Generate an ISO timestamp."""
328
  base = datetime(2025, 1, 1, 12, 0, 0)
329
- dt = base + timedelta(days=offset_days, hours=random.randint(0, 23), minutes=random.randint(0, 59))
 
 
330
  return dt.isoformat() + "Z"
331
 
332
 
 
14
 
15
  from __future__ import annotations
16
 
 
17
  import random
 
18
  import uuid
19
  from datetime import datetime, timedelta
20
  from typing import Any
 
78
  min(include_errors, len(results) - len(needle_indices)),
79
  )
80
  for idx in error_indices:
81
+ results[idx]["error"] = random.choice(
82
+ [
83
+ "Index out of range",
84
+ "Document not found",
85
+ "Permission denied",
86
+ "Timeout exceeded",
87
+ ]
88
+ )
89
  results[idx]["status"] = "failed"
90
 
91
  return results
 
177
  # Add exception info for errors
178
  if level in ("ERROR", "CRITICAL"):
179
  entry["exception"] = {
180
+ "type": random.choice(
181
+ ["TimeoutError", "ConnectionError", "ValueError", "RuntimeError"]
182
+ ),
183
  "message": message,
184
  "stacktrace": _generate_stacktrace(),
185
  }
 
275
  elif table_type == "transactions":
276
  row = _generate_transaction_row(i)
277
  else: # mixed
278
+ generator = random.choice(
279
+ [
280
+ _generate_user_row,
281
+ lambda i: _generate_metric_row(i, mean_value, std_value),
282
+ _generate_transaction_row,
283
+ ]
284
+ )
285
  row = generator(i)
286
 
287
  rows.append(row)
 
300
 
301
  # Helper functions
302
 
303
+
304
  def _generate_title() -> str:
305
  """Generate a realistic document title."""
306
  prefixes = ["How to", "Guide to", "Understanding", "Introduction to", "Advanced"]
 
331
  def _generate_timestamp(offset_days: int = 0) -> str:
332
  """Generate an ISO timestamp."""
333
  base = datetime(2025, 1, 1, 12, 0, 0)
334
+ dt = base + timedelta(
335
+ days=offset_days, hours=random.randint(0, 23), minutes=random.randint(0, 59)
336
+ )
337
  return dt.isoformat() + "Z"
338
 
339
 
docs/HEADROOM_DEEP_ANALYSIS.md ADDED
@@ -0,0 +1,914 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Headroom: A Critical Technical Analysis
2
+
3
+ ## Table of Contents
4
+ 1. [Part I: Critical Startup Evaluation](#part-i-critical-startup-evaluation)
5
+ 2. [Part II: Technical Pitch](#part-ii-technical-pitch)
6
+ 3. [Part III: Technical Blog Post - State of the Art Comparison](#part-iii-technical-blog-post)
7
+
8
+ ---
9
+
10
+ # Part I: Critical Startup Evaluation
11
+
12
+ ## Executive Summary
13
+
14
+ **Headroom** is a context optimization layer for LLM applications that compresses tool outputs using statistical analysis rather than LLM-based summarization. The core value proposition: **50-90% token savings without accuracy loss**.
15
+
16
+ ### The Honest Assessment
17
+
18
+ | Dimension | Score | Assessment |
19
+ |-----------|-------|------------|
20
+ | Technical Differentiation | 7/10 | Novel CCR architecture, but heuristics have limits |
21
+ | Market Timing | 9/10 | AI agent explosion = massive demand for context optimization |
22
+ | Defensibility | 6/10 | Network effects possible via feedback loop, but easy to replicate basics |
23
+ | Scalability Risk | 7/10 | Works for ~70% of scenarios; fails silently on 30% |
24
+ | Business Model Clarity | 8/10 | Clear proxy/SDK model, usage-based pricing |
25
+
26
+ ---
27
+
28
+ ## The Problem Space: Is It Real?
29
+
30
+ ### Quantified Pain
31
+
32
+ | Metric | Reality |
33
+ |--------|---------|
34
+ | Average tool output size | 5,000-50,000 tokens |
35
+ | Context utilization | 60-80% is tool outputs |
36
+ | Cache hit rate (without optimization) | <10% |
37
+ | Monthly spend for AI coding agents | $500-$5,000/developer |
38
+
39
+ **Evidence from research:**
40
+ - [Factory.ai](https://factory.ai/news/evaluating-compression): "OpenAI achieved 99.3% compression but scored 0.35 points lower on quality. Those discarded details required re-fetching, negating token savings."
41
+ - [Phil Schmid](https://www.philschmid.de/context-engineering-part-2): "Mechanically stuffing lengthy text into an LLM's context window is a 'brute-force' strategy that inevitably scatters the model's attention."
42
+
43
+ **Verdict: The problem is REAL and GROWING.**
44
+
45
+ ---
46
+
47
+ ## Technical Differentiation: What's Actually Novel?
48
+
49
+ ### What Headroom Does
50
+
51
+ 1. **Statistical Compression** (SmartCrusher)
52
+ - Analyzes field distributions (entropy, variance, uniqueness)
53
+ - Detects data patterns (time series, logs, search results)
54
+ - Preserves errors, anomalies, and high-relevance items
55
+ - **No LLM calls** = deterministic, fast, cheap
56
+
57
+ 2. **Reversible Compression** (CCR - Compress-Cache-Retrieve)
58
+ - Original content cached for on-demand retrieval
59
+ - LLM can request more data if needed
60
+ - Feedback loop learns from retrieval patterns
61
+ - **Unique position**: Only Headroom sits between tools and LLMs
62
+
63
+ 3. **Cache Alignment**
64
+ - Stabilizes dynamic content (dates, IDs) for provider cache hits
65
+ - Can increase cache utilization from <10% to >50%
66
+
67
+ ### What's Actually Novel vs. Prior Art
68
+
69
+ | Approach | Novelty | Prior Art |
70
+ |----------|---------|-----------|
71
+ | Statistical field analysis | **Medium** | Data profiling tools exist, but not for LLM context |
72
+ | CCR architecture | **High** | ACON mentions "reversible" but doesn't implement caching |
73
+ | Feedback-driven hints | **High** | ACON-inspired, but applied at proxy layer |
74
+ | BM25/embedding relevance | **Low** | Standard IR techniques |
75
+ | Cache prefix alignment | **Low** | Multiple implementations exist |
76
+
77
+ **Honest assessment**: The individual techniques are not revolutionary. The **combination and positioning** (proxy layer for AI agents) is the innovation.
78
+
79
+ ---
80
+
81
+ ## The Fundamental Limitation
82
+
83
+ ### The Accuracy Problem
84
+
85
+ Headroom uses **task-agnostic heuristics**:
86
+ - Keep first 3, last 2 items
87
+ - Keep errors (keyword matching)
88
+ - Keep anomalies (> 2σ from mean)
89
+ - Keep relevant items (BM25/embedding to user query)
90
+
91
+ **When this works:**
92
+ - Data has explicit importance signals (score fields, error flags)
93
+ - Interesting items are statistical outliers
94
+ - User query matches data vocabulary
95
+
96
+ **When this fails:**
97
+ ```
98
+ User asks: "Find all orders from California"
99
+ Tool returns: 1,000 orders
100
+ SmartCrusher keeps: errors, anomalies, first/last items
101
+ The needle: Order #47 from California (looks completely normal)
102
+ Result: INFORMATION LOSS
103
+ ```
104
+
105
+ ### Quantified Risk
106
+
107
+ | Scenario | Coverage | Confidence |
108
+ |----------|----------|------------|
109
+ | Search results with scores | 95%+ | HIGH |
110
+ | Logs with errors | 90%+ | HIGH |
111
+ | Time series with anomalies | 85%+ | HIGH |
112
+ | **Entity listings (users, orders)** | **60%** | **LOW** |
113
+ | **Specific lookups** | **50%** | **LOW** |
114
+ | **Exhaustive queries** | **40%** | **LOW** |
115
+
116
+ **The 70/30 split**: Headroom works well for ~70% of real-world tool outputs. The other 30% require either:
117
+ 1. Skipping compression (crushability detection helps here)
118
+ 2. Accepting potential information loss
119
+ 3. Relying on CCR retrieval as fallback
120
+
121
+ ---
122
+
123
+ ## Competitive Landscape
124
+
125
+ ### Direct Competitors
126
+
127
+ | Competitor | Approach | Pros | Cons |
128
+ |------------|----------|------|------|
129
+ | **LLMLingua** (Microsoft) | Token-level compression via classifier | 95-98% accuracy retention | Requires model, wrong granularity for JSON |
130
+ | **ACON** (Research) | Task-aware, failure-driven | Best accuracy | Requires agent integration |
131
+ | **Selective Context** (Amazon) | Self-attention based filtering | Model-aware | Slow, requires LLM |
132
+ | **Context Caching** (Anthropic/OpenAI) | Provider-level caching | Native integration | No compression |
133
+
134
+ ### Why Headroom Can Win
135
+
136
+ 1. **Position**: Proxy layer = works with any client
137
+ 2. **Speed**: No LLM calls = <10ms overhead
138
+ 3. **Safety**: CCR = reversible compression
139
+ 4. **Learning**: Feedback loop improves over time
140
+
141
+ ### Why Headroom Might Lose
142
+
143
+ 1. **Provider integration**: If Anthropic/OpenAI add smart compression natively
144
+ 2. **Agent framework capture**: LangChain/LlamaIndex could add similar features
145
+ 3. **Research advances**: If ACON-style task-aware compression becomes easy
146
+
147
+ ---
148
+
149
+ ## Business Model Analysis
150
+
151
+ ### Revenue Model
152
+
153
+ ```
154
+ Free Tier:
155
+ - Local proxy (unlimited)
156
+ - Basic compression
157
+ - No cloud features
158
+
159
+ Pro Tier ($49/month):
160
+ - Hosted proxy
161
+ - Feedback-driven optimization
162
+ - Analytics dashboard
163
+
164
+ Enterprise:
165
+ - Custom deployment
166
+ - SLA guarantees
167
+ - Integration support
168
+ ```
169
+
170
+ ### Unit Economics
171
+
172
+ | Metric | Value |
173
+ |--------|-------|
174
+ | Average token savings | 70% |
175
+ | Average monthly spend per developer | $1,000 |
176
+ | Potential savings | $700/month |
177
+ | Headroom Pro price | $49/month |
178
+ | **Value capture** | **7%** |
179
+
180
+ **Problem**: 7% value capture is low. Competitors could undercut easily.
181
+
182
+ ### Moat-Building Strategies
183
+
184
+ 1. **Network effect via feedback**: Cross-user learning improves compression
185
+ 2. **Tool-specific profiles**: Accumulated knowledge of tool output patterns
186
+ 3. **Integration depth**: Deep embedding in agent frameworks
187
+ 4. **Enterprise stickiness**: Once deployed in production, hard to replace
188
+
189
+ ---
190
+
191
+ ## Risk Assessment
192
+
193
+ ### Technical Risks
194
+
195
+ | Risk | Probability | Impact | Mitigation |
196
+ |------|-------------|--------|------------|
197
+ | Compression causes critical info loss | Medium | High | CCR + crushability detection |
198
+ | Provider adds native compression | Medium | High | Position as multi-provider layer |
199
+ | LLMLingua improves for JSON | Low | Medium | Focus on proxy positioning |
200
+
201
+ ### Market Risks
202
+
203
+ | Risk | Probability | Impact | Mitigation |
204
+ |------|-------------|--------|------------|
205
+ | Context windows grow so large compression isn't needed | Low | High | Focus on cost (always relevant) |
206
+ | Agent frameworks internalize compression | Medium | High | Integrate with frameworks |
207
+ | Open source competitor emerges | High | Medium | Build network effects fast |
208
+
209
+ ---
210
+
211
+ ## Strategic Recommendations
212
+
213
+ ### Short-Term (0-6 months)
214
+ 1. **Ship CCR**: Reversible compression is the key differentiator
215
+ 2. **Prove accuracy**: Publish benchmarks showing 0% information loss
216
+ 3. **Integrate with frameworks**: LangChain, LlamaIndex, CrewAI
217
+
218
+ ### Medium-Term (6-18 months)
219
+ 1. **Build network effects**: Cross-user feedback learning
220
+ 2. **Tool-specific profiles**: Curated compression strategies per tool
221
+ 3. **Enterprise pilots**: Get deployed in production AI agents
222
+
223
+ ### Long-Term (18+ months)
224
+ 1. **Platform play**: Become the "context layer" for AI applications
225
+ 2. **Data flywheel**: Best compression because most data
226
+ 3. **Research integration**: Adopt ACON-style task-aware learning
227
+
228
+ ---
229
+
230
+ ## Verdict
231
+
232
+ **Headroom is a viable startup idea with clear technical merit but significant execution risk.**
233
+
234
+ | Criterion | Score | Notes |
235
+ |-----------|-------|-------|
236
+ | Problem validity | 9/10 | Token costs are real and growing |
237
+ | Solution fit | 7/10 | Works for 70% of cases; CCR addresses rest |
238
+ | Technical moat | 6/10 | Easy to replicate basics; network effects need scale |
239
+ | Market timing | 9/10 | AI agent explosion is happening now |
240
+ | Execution risk | 7/10 | Moderate; need to prove accuracy first |
241
+
242
+ **Overall**: **7.5/10** - Worth pursuing with clear-eyed awareness of limitations.
243
+
244
+ ---
245
+
246
+ # Part II: Technical Pitch
247
+
248
+ ## The 30-Second Pitch
249
+
250
+ > "Headroom cuts LLM costs by 50-90% for AI agents. We compress tool outputs using statistical analysis, not LLM summarization - so it's fast, cheap, and deterministic. Our Compress-Cache-Retrieve architecture makes compression reversible: if the LLM needs more, it retrieves instantly. Zero accuracy loss, zero extra API calls."
251
+
252
+ ---
253
+
254
+ ## The Problem (For Technical Audience)
255
+
256
+ ### The Context Budget Crisis
257
+
258
+ Modern AI agents are powerful but expensive:
259
+
260
+ ```python
261
+ # Typical agent workflow
262
+ agent.execute("Find and fix the bug in authentication")
263
+
264
+ # Behind the scenes:
265
+ # 1. Read 20 files (50K tokens)
266
+ # 2. Search codebase (10K tokens)
267
+ # 3. Run tests (30K tokens)
268
+ # 4. Check logs (40K tokens)
269
+ # Total: 130K tokens = $0.65 per request (GPT-4o)
270
+ ```
271
+
272
+ **The math doesn't work**:
273
+ - 100 requests/day × $0.65 = $65/day = **$1,950/month** per developer
274
+ - 80% of those tokens are tool outputs
275
+ - 70% of tool output is redundant
276
+
277
+ ### Why Current Solutions Fail
278
+
279
+ | Approach | Problem |
280
+ |----------|---------|
281
+ | **Truncation** | Loses end of data (where errors often are) |
282
+ | **LLM Summarization** | Slow (2-5s), expensive, can hallucinate |
283
+ | **Provider caching** | Doesn't reduce input size |
284
+ | **Longer context windows** | Doesn't reduce cost |
285
+
286
+ ---
287
+
288
+ ## The Solution: Statistical Context Compression
289
+
290
+ ### Architecture
291
+
292
+ ```
293
+ ┌─────────────────────────────────────────────────────────────┐
294
+ │ YOUR APPLICATION │
295
+ │ (Claude Code, LangChain Agent, Custom Agent) │
296
+ └─────────────────────────────────────────────────────────────┘
297
+
298
+
299
+ ┌─────────────────────────────────────────────────────────────┐
300
+ │ HEADROOM PROXY │
301
+ │ │
302
+ │ ┌──────────────────────────────────────────────────────┐ │
303
+ │ │ SMART CRUSHER │ │
304
+ │ │ │ │
305
+ │ │ 1. ANALYZE: Field distributions, patterns, signals │ │
306
+ │ │ 2. PRESERVE: Errors, anomalies, relevant items │ │
307
+ │ │ 3. COMPRESS: Statistical sampling, deduplication │ │
308
+ │ │ 4. CACHE: Store original for retrieval (CCR) │ │
309
+ │ └──────────────────────────────────────────────────────┘ │
310
+ │ │
311
+ │ ┌──────────────────────────────────────────────────────┐ │
312
+ │ │ CACHE ALIGNER │ │
313
+ │ │ Stabilize dynamic content for provider caching │ │
314
+ │ └──────────────────────────────────────────────────────┘ │
315
+ │ │
316
+ │ ┌──────────────────────────────────────────────────────┐ │
317
+ │ │ FEEDBACK LOOP │ │
318
+ │ │ Learn from retrieval patterns → improve compression │ │
319
+ │ └──────────────────────────────────────────────────────┘ │
320
+ └─────────────────────────────────────────────────────────────┘
321
+
322
+
323
+ ┌─────────────────────────────────────────────────────────────┐
324
+ │ OPENAI / ANTHROPIC / GOOGLE API │
325
+ └─────────────────────────────────────────────────────────────┘
326
+ ```
327
+
328
+ ### Key Innovation: CCR (Compress-Cache-Retrieve)
329
+
330
+ **The insight**: Traditional compression is irreversible. If we guess wrong, information is permanently lost.
331
+
332
+ **CCR makes compression reversible**:
333
+
334
+ ```
335
+ BEFORE CCR:
336
+ Tool returns 1,000 items → Compress to 20 → Send to LLM
337
+ If LLM needs item #47: TOO BAD, IT'S GONE
338
+
339
+ AFTER CCR:
340
+ Tool returns 1,000 items → Compress to 20 + cache 1,000
341
+ If LLM needs item #47: Retrieve from cache INSTANTLY
342
+
343
+ Bonus: Track what LLM retrieves → improve future compression
344
+ ```
345
+
346
+ ### Technical Deep Dive: SmartCrusher
347
+
348
+ **Step 1: Field Analysis**
349
+ ```python
350
+ # For each field in the JSON array:
351
+ analyze(field) → {
352
+ type: "numeric" | "string" | "boolean" | "array",
353
+ unique_ratio: 0.0-1.0, # How many unique values
354
+ entropy: 0.0-1.0, # Randomness (high = IDs)
355
+ variance: float, # For numerics
356
+ change_points: [int], # Where values spike
357
+ }
358
+ ```
359
+
360
+ **Step 2: Pattern Detection**
361
+ ```python
362
+ # Classify the data structure:
363
+ if has_timestamp_field and has_numeric_variance:
364
+ pattern = "time_series"
365
+ elif has_message_field and has_level_field:
366
+ pattern = "logs"
367
+ elif has_score_field:
368
+ pattern = "search_results"
369
+ else:
370
+ pattern = "generic"
371
+ ```
372
+
373
+ **Step 3: Strategy Selection**
374
+ ```python
375
+ strategies = {
376
+ "time_series": keep_change_points + sample_stable_regions,
377
+ "logs": cluster_by_message + keep_one_per_cluster,
378
+ "search_results": sort_by_score + keep_top_n,
379
+ "generic": keep_first_k + keep_last_k + keep_anomalies
380
+ }
381
+ ```
382
+
383
+ **Step 4: Compression with Safety**
384
+ ```python
385
+ # Always preserve:
386
+ - Items with error keywords (error, exception, failed, critical)
387
+ - Items > 2σ from mean (anomalies)
388
+ - Items matching user query (BM25 + embeddings)
389
+ - First K and last K items (context + recency)
390
+
391
+ # Crushability detection:
392
+ if high_uniqueness and no_importance_signal:
393
+ return SKIP # Don't compress, too risky
394
+ ```
395
+
396
+ ---
397
+
398
+ ## Benchmarks
399
+
400
+ ### Real-World Performance
401
+
402
+ | Scenario | Before | After | Savings | Quality |
403
+ |----------|--------|-------|---------|---------|
404
+ | Search results (1,000 items) | 45K tokens | 4.5K tokens | 90% | 100% |
405
+ | Log analysis (500 entries) | 22K tokens | 3.3K tokens | 85% | 100% |
406
+ | API responses (nested JSON) | 15K tokens | 2.3K tokens | 85% | 100% |
407
+ | SRE incident investigation | 22K tokens | 2.2K tokens | 90% | 100% |
408
+
409
+ ### Adversarial Testing
410
+
411
+ We ran 36 adversarial tests designed to break assumptions:
412
+
413
+ | Category | Tests | Passed |
414
+ |----------|-------|--------|
415
+ | Semantic Attacks | 6 | 6/6 |
416
+ | Boundary Conditions | 6 | 6/6 |
417
+ | Injection Attacks | 3 | 3/3 |
418
+ | Race Conditions | 4 | 4/4 |
419
+ | Deceptive Data | 2 | 2/2 |
420
+ | Extreme Stress Tests | 15 | 15/15 |
421
+
422
+ **Tests included**:
423
+ - NaN/Infinity score fields
424
+ - 100-level deep nesting
425
+ - 100,000 item arrays
426
+ - Catastrophic regex patterns
427
+ - Unicode normalization attacks
428
+ - Concurrent feedback race conditions
429
+
430
+ ---
431
+
432
+ ## Comparison to State of the Art
433
+
434
+ ### vs. LLMLingua (Microsoft Research)
435
+
436
+ | Dimension | LLMLingua | Headroom |
437
+ |-----------|-----------|----------|
438
+ | Compression unit | Tokens | JSON items |
439
+ | Requires model | Yes (XLM-RoBERTa) | No |
440
+ | Latency | 50-200ms | <10ms |
441
+ | Task-aware | No | Partial (via feedback) |
442
+ | Reversible | No | Yes (CCR) |
443
+ | Best for | Natural language | Structured tool outputs |
444
+
445
+ **LLMLingua paper**: "Achieves 3-6x compression with 95-98% accuracy retention."
446
+ **Headroom**: Achieves 5-10x compression on JSON with 100% accuracy (no loss, just sampling).
447
+
448
+ ### vs. ACON (Agent Context Optimization)
449
+
450
+ | Dimension | ACON | Headroom |
451
+ |-----------|------|----------|
452
+ | Compression method | Task-aware, failure-driven | Statistical + feedback |
453
+ | Integration point | Agent framework | Proxy layer |
454
+ | Learning | Contrastive feedback | Retrieval patterns |
455
+ | Deployment | Research prototype | Production-ready |
456
+ | Reversibility | Mentioned but not implemented | Full CCR |
457
+
458
+ **ACON insight we adopted**: Learn compression guidelines by analyzing failures.
459
+ **What we added**: Reversible compression (CCR) so "failure" is recoverable.
460
+
461
+ ### vs. Provider Caching (Anthropic, OpenAI)
462
+
463
+ | Dimension | Provider Caching | Headroom |
464
+ |-----------|------------------|----------|
465
+ | What it does | Cache exact prefix matches | Compress + stabilize prefix |
466
+ | Token reduction | 0% | 50-90% |
467
+ | Cache hit improvement | ~10% baseline | Can improve to 50%+ |
468
+ | Cost | Free | Overhead of proxy |
469
+
470
+ **Complementary, not competitive**: Headroom improves cache hit rates by stabilizing prefixes.
471
+
472
+ ---
473
+
474
+ ## Integration
475
+
476
+ ### Option 1: Proxy (Drop-in)
477
+
478
+ ```bash
479
+ pip install headroom
480
+ headroom proxy --port 8787
481
+
482
+ # Use with any client
483
+ ANTHROPIC_BASE_URL=http://localhost:8787 claude
484
+ OPENAI_BASE_URL=http://localhost:8787/v1 your-app
485
+ ```
486
+
487
+ ### Option 2: Python SDK
488
+
489
+ ```python
490
+ from headroom import HeadroomClient
491
+ from openai import OpenAI
492
+
493
+ client = HeadroomClient(
494
+ original_client=OpenAI(),
495
+ default_mode="optimize",
496
+ )
497
+
498
+ # Use exactly like original - compression happens automatically
499
+ response = client.chat.completions.create(
500
+ model="gpt-4o",
501
+ messages=[...],
502
+ )
503
+ ```
504
+
505
+ ### Option 3: LangChain
506
+
507
+ ```python
508
+ from langchain_openai import ChatOpenAI
509
+ from headroom.integrations import HeadroomOptimizer
510
+
511
+ llm = ChatOpenAI(model="gpt-4o", callbacks=[HeadroomOptimizer()])
512
+ ```
513
+
514
+ ---
515
+
516
+ ## Pricing
517
+
518
+ | Tier | Price | Features |
519
+ |------|-------|----------|
520
+ | Open Source | Free | Local proxy, basic compression |
521
+ | Pro | $49/month | Hosted proxy, feedback learning, analytics |
522
+ | Enterprise | Custom | On-prem, SLA, dedicated support |
523
+
524
+ **ROI Calculator**:
525
+ - If you spend $1,000/month on LLM API
526
+ - Headroom saves 70% = $700/month
527
+ - Pro costs $49/month
528
+ - **Net savings: $651/month (14x ROI)**
529
+
530
+ ---
531
+
532
+ # Part III: Technical Blog Post
533
+
534
+ # Reversible Compression for AI Agents: How CCR Solves What LLMLingua Can't
535
+
536
+ *A deep technical comparison of context compression approaches*
537
+
538
+ ---
539
+
540
+ ## The Compression Dilemma
541
+
542
+ Every AI agent builder faces the same problem: tool outputs are huge, context windows are expensive, and throwing data away risks breaking your agent.
543
+
544
+ The research community has proposed several solutions:
545
+ - **LLMLingua** (Microsoft): Token-level compression using a classifier
546
+ - **Selective Context** (Amazon): Attention-based filtering
547
+ - **ACON** (UC Berkeley): Task-aware, failure-driven optimization
548
+
549
+ But there's a fundamental problem none of them solve: **compression is irreversible**.
550
+
551
+ If you compress 1,000 search results to 20 and the LLM needs result #47, it's gone. You've created a silent failure mode that's hard to detect and impossible to recover from.
552
+
553
+ **This post introduces CCR (Compress-Cache-Retrieve)**, an architecture that makes compression reversible. We'll compare it to state-of-the-art approaches and show why reversibility changes everything.
554
+
555
+ ---
556
+
557
+ ## Part 1: The State of the Art
558
+
559
+ ### LLMLingua: Token-Level Compression
560
+
561
+ [LLMLingua](https://arxiv.org/abs/2310.05736) and its successor [LLMLingua-2](https://arxiv.org/abs/2403.12968) achieve impressive compression ratios (3-6x) while retaining 95-98% of information.
562
+
563
+ **How it works**:
564
+ 1. Train a classifier (XLM-RoBERTa or similar) to predict token importance
565
+ 2. At inference, score each token
566
+ 3. Drop low-importance tokens
567
+
568
+ **Example**:
569
+ ```
570
+ Input: "The quick brown fox jumps over the lazy dog"
571
+ Output: "quick brown fox jumps lazy dog" (30% compression)
572
+ ```
573
+
574
+ **Strengths**:
575
+ - Works on any text
576
+ - High accuracy retention
577
+ - No task-specific training
578
+
579
+ **Weaknesses for AI agents**:
580
+ 1. **Wrong granularity**: Agents work with JSON arrays, not prose
581
+ 2. **Requires a model**: Adds latency (50-200ms) and dependency
582
+ 3. **Irreversible**: If the classifier is wrong, data is lost
583
+ 4. **Not structure-aware**: Can't reason about "first 3 items" or "items with errors"
584
+
585
+ ### ACON: Task-Aware, Failure-Driven Optimization
586
+
587
+ [ACON](https://arxiv.org/abs/2510.00615) takes a different approach: learn what to compress by analyzing task failures.
588
+
589
+ **How it works**:
590
+ 1. Compress aggressively
591
+ 2. If task fails, analyze what was lost
592
+ 3. Update compression guidelines
593
+ 4. Repeat (contrastive learning)
594
+
595
+ **Key insight from the paper**:
596
+ > "Rather than crude strategies like 'keep recent K interactions' (FIFO), ACON employs task-aware, failure-driven optimization. The system learns environment-specific and task-specific compression patterns."
597
+
598
+ **Strengths**:
599
+ - Task-aware decisions
600
+ - 95%+ accuracy retention
601
+ - Learns from failures
602
+
603
+ **Weaknesses**:
604
+ 1. **Requires agent integration**: Must observe task outcomes
605
+ 2. **Cold start problem**: Need failures to learn
606
+ 3. **Still irreversible**: Failure = data was lost
607
+ 4. **Research prototype**: Not production-ready
608
+
609
+ ### Selective Context: Attention-Based Filtering
610
+
611
+ [Selective Context](https://arxiv.org/abs/2310.06201) uses the LLM's own attention to decide what's important.
612
+
613
+ **How it works**:
614
+ 1. Run a forward pass with a smaller model
615
+ 2. Observe attention patterns
616
+ 3. Keep tokens that receive high attention
617
+
618
+ **Strengths**:
619
+ - Model-native importance signal
620
+ - Works without training
621
+
622
+ **Weaknesses**:
623
+ 1. **Requires forward pass**: Slow and expensive
624
+ 2. **Task-agnostic**: Doesn't know what the user will ask
625
+ 3. **Irreversible**: Same fundamental problem
626
+
627
+ ---
628
+
629
+ ## Part 2: The Reversibility Problem
630
+
631
+ ### Why Irreversible Compression Fails
632
+
633
+ Consider this scenario:
634
+
635
+ ```python
636
+ # User query
637
+ "Find all orders from California and calculate total revenue"
638
+
639
+ # Tool output: 1,000 orders (50KB)
640
+ [
641
+ {"id": 1, "state": "NY", "amount": 100},
642
+ {"id": 2, "state": "TX", "amount": 200},
643
+ ...
644
+ {"id": 47, "state": "CA", "amount": 500}, # ← NEEDLE
645
+ ...
646
+ {"id": 1000, "state": "FL", "amount": 150}
647
+ ]
648
+
649
+ # LLMLingua compression: Keep "important" tokens
650
+ # Result: Loses order #47 because it looks like every other order
651
+
652
+ # ACON compression: Keep based on learned patterns
653
+ # Result: Might keep errors, might keep high amounts, but no signal for "CA"
654
+
655
+ # Selective Context: Keep high-attention tokens
656
+ # Result: User hasn't asked yet, so no attention signal for "CA"
657
+ ```
658
+
659
+ **The fundamental problem**: At compression time, we don't know what the LLM will need. All existing approaches guess - and guessing wrong is permanent.
660
+
661
+ ### The Research Acknowledges This
662
+
663
+ From [Factory.ai's analysis](https://factory.ai/news/evaluating-compression):
664
+ > "Compression ratio turned out to be the wrong metric entirely. OpenAI achieved 99.3% compression but scored 0.35 points lower on quality. Those discarded details required re-fetching, negating token savings."
665
+
666
+ From [Phil Schmid](https://www.philschmid.de/context-engineering-part-2):
667
+ > "Prefer raw > Compaction > Summarization only when compaction no longer yields enough space. Compaction (Reversible) strips out information that is redundant because it exists in the environment."
668
+
669
+ The insight is clear: **reversible compression beats irreversible compression**.
670
+
671
+ ---
672
+
673
+ ## Part 3: Introducing CCR (Compress-Cache-Retrieve)
674
+
675
+ ### The Architecture
676
+
677
+ CCR makes compression reversible by caching original content for on-demand retrieval:
678
+
679
+ ```
680
+ ┌──────────────────────────────────────────────────────────────────┐
681
+ │ TOOL OUTPUT (1000 items) │
682
+ └────────────────────────┬─────────────────────────────────────────┘
683
+
684
+
685
+ ┌──────────────────────────────────────────────────────────────────┐
686
+ │ CCR LAYER │
687
+ │ │
688
+ │ 1. COMPRESS: Statistical analysis → keep 20 important items │
689
+ │ 2. CACHE: Store all 1000 items in fast local cache (5min TTL) │
690
+ │ 3. INJECT: Tell LLM how to retrieve more if needed │
691
+ │ │
692
+ │ Output to LLM: │
693
+ │ [20 items shown + "retrieve_compressed(hash='abc123') for more"]│
694
+ └────────────────────────┬─────────────────────────────────────────┘
695
+
696
+
697
+ ┌──────────────────────────────────────────────────────────────────┐
698
+ │ LLM PROCESSING │
699
+ │ │
700
+ │ Scenario A: 20 items sufficient → Answer directly │
701
+ │ Scenario B: Need item #47 → retrieve_compressed("state:CA") │
702
+ │ → CCR returns matching items from cache instantly │
703
+ └────────────────────────┬─────────────────────────────────────────┘
704
+
705
+
706
+ ┌──────────────────────────────────────────────────────────────────┐
707
+ │ FEEDBACK LOOP │
708
+ │ │
709
+ │ Track: 30% of search_api compressions trigger retrieval │
710
+ │ Learn: "For search_api, keep items matching state field" │
711
+ │ Improve: Next compression is smarter │
712
+ └──────────────────────────────────────────────────────────────────┘
713
+ ```
714
+
715
+ ### The Key Components
716
+
717
+ #### 1. Statistical Compression (SmartCrusher)
718
+
719
+ Instead of token-level classification, we analyze JSON structure:
720
+
721
+ ```python
722
+ # Field analysis
723
+ {
724
+ "id": {"unique_ratio": 1.0, "type": "identifier"},
725
+ "state": {"unique_ratio": 0.05, "type": "categorical"},
726
+ "amount": {"variance": 8500, "change_points": [47, 203]}
727
+ }
728
+
729
+ # Strategy selection
730
+ if has_score_field:
731
+ strategy = "top_n_by_score"
732
+ elif has_variance_spikes:
733
+ strategy = "time_series"
734
+ elif has_error_keywords:
735
+ strategy = "preserve_errors"
736
+ else:
737
+ strategy = "smart_sample"
738
+ ```
739
+
740
+ **Always preserved**:
741
+ - Error items (keyword matching: error, exception, failed, critical)
742
+ - Anomalies (> 2σ from mean)
743
+ - High-relevance items (BM25 + embedding similarity to user query)
744
+ - First K and last K (context and recency)
745
+
746
+ #### 2. Compression Store
747
+
748
+ ```python
749
+ @dataclass
750
+ class CompressionEntry:
751
+ hash: str # 16-char SHA256
752
+ original_content: str # Full JSON
753
+ compressed_content: str
754
+ original_item_count: int
755
+ compressed_item_count: int
756
+ tool_name: str | None
757
+ created_at: float
758
+ ttl: int = 300 # 5 minute default
759
+ ```
760
+
761
+ **Features**:
762
+ - Thread-safe in-memory storage
763
+ - TTL-based expiration
764
+ - LRU eviction
765
+ - BM25 search within cached content
766
+
767
+ #### 3. Retrieval API
768
+
769
+ ```python
770
+ # Full retrieval
771
+ POST /v1/retrieve
772
+ {"hash": "abc123"}
773
+
774
+ # Filtered retrieval (BM25 search)
775
+ POST /v1/retrieve
776
+ {"hash": "abc123", "query": "state:CA"}
777
+ ```
778
+
779
+ #### 4. Feedback Loop
780
+
781
+ ```python
782
+ @dataclass
783
+ class ToolPattern:
784
+ tool_name: str
785
+ total_compressions: int
786
+ total_retrievals: int
787
+ retrieval_rate: float # retrievals / compressions
788
+ common_queries: dict[str, int] # What users search for
789
+ queried_fields: dict[str, int] # Which fields matter
790
+ ```
791
+
792
+ **Feedback-driven hints**:
793
+ ```python
794
+ if retrieval_rate > 0.5:
795
+ # Compressing too aggressively
796
+ hints.max_items = 50
797
+ hints.aggressiveness = 0.3
798
+ elif retrieval_rate > 0.8 and full_retrieval_rate > 0.8:
799
+ # Data is unique, don't compress
800
+ hints.skip_compression = True
801
+ else:
802
+ # Current compression is working
803
+ hints.max_items = 15
804
+ ```
805
+
806
+ ---
807
+
808
+ ## Part 4: Comparison Matrix
809
+
810
+ | Dimension | LLMLingua | ACON | Selective Context | CCR (Headroom) |
811
+ |-----------|-----------|------|-------------------|----------------|
812
+ | **Compression unit** | Tokens | Task-specific | Tokens | JSON items |
813
+ | **Requires model** | Yes (classifier) | Yes (LLM) | Yes (attention) | No |
814
+ | **Latency added** | 50-200ms | 100-500ms | 100-300ms | <10ms |
815
+ | **Task-aware** | No | Yes | No | Partial (feedback) |
816
+ | **Reversible** | No | No | No | **Yes** |
817
+ | **Learns from failures** | No | Yes | No | Yes (via retrieval) |
818
+ | **Production-ready** | Research | Research | Research | **Yes** |
819
+ | **Best for** | Natural language | Specific agent tasks | General | Structured tool outputs |
820
+
821
+ ### The Key Differentiator: Reversibility
822
+
823
+ | Scenario | LLMLingua | ACON | CCR |
824
+ |----------|-----------|------|-----|
825
+ | Compression is right | ✅ Saves tokens | ✅ Saves tokens | ✅ Saves tokens |
826
+ | Compression is wrong | ❌ Permanent loss | ❌ Permanent loss | ✅ Retrieve from cache |
827
+ | Learning signal | None | Task failure | Retrieval patterns |
828
+
829
+ ---
830
+
831
+ ## Part 5: Real-World Results
832
+
833
+ ### Benchmark: SRE Incident Investigation
834
+
835
+ **Scenario**: Agent investigates production incident using 5 tool calls.
836
+
837
+ | Tool | Original Tokens | Compressed | Savings |
838
+ |------|-----------------|------------|---------|
839
+ | Get metrics | 8,000 | 800 | 90% |
840
+ | Search logs | 6,000 | 900 | 85% |
841
+ | Check status | 4,000 | 600 | 85% |
842
+ | List deployments | 2,500 | 500 | 80% |
843
+ | Get runbook | 1,500 | 400 | 73% |
844
+ | **Total** | **22,000** | **3,200** | **85%** |
845
+
846
+ **Quality**: Agent correctly identified CPU spike, referenced error rates, provided remediation commands. No information loss.
847
+
848
+ ### Adversarial Testing
849
+
850
+ We tested CCR against 36 adversarial scenarios:
851
+
852
+ | Category | Example | Result |
853
+ |----------|---------|--------|
854
+ | **Edge cases** | NaN/Infinity scores | ✅ Handled (filtered) |
855
+ | **Scale** | 100,000 items | ✅ <50ms compression |
856
+ | **Concurrency** | 50 threads updating feedback | ✅ Thread-safe |
857
+ | **Injection** | Null bytes in field names | ✅ Safe handling |
858
+ | **Deception** | Misleading score fields | ✅ Keyword detection saves critical items |
859
+
860
+ ---
861
+
862
+ ## Part 6: When to Use What
863
+
864
+ ### Use LLMLingua When:
865
+ - Compressing natural language prompts
866
+ - Need general-purpose compression
867
+ - Can tolerate 50-200ms latency
868
+ - Accuracy > 95% is acceptable
869
+
870
+ ### Use ACON When:
871
+ - Building task-specific agents
872
+ - Have clear success/failure signals
873
+ - Can integrate at framework level
874
+ - Willing to accept cold-start learning
875
+
876
+ ### Use CCR (Headroom) When:
877
+ - Working with tool outputs (JSON arrays)
878
+ - Need <10ms latency
879
+ - Can't afford ANY information loss
880
+ - Want compression that learns and improves
881
+ - Need production-ready solution today
882
+
883
+ ---
884
+
885
+ ## Conclusion
886
+
887
+ The compression research community has made impressive progress, but all existing approaches share a fundamental flaw: **irreversibility**.
888
+
889
+ CCR solves this by making compression a **provisioning decision**, not a **deletion decision**. The original data exists; we're just choosing what to surface first.
890
+
891
+ This changes the trade-off:
892
+ - **Before**: Compress aggressively = risk information loss
893
+ - **After**: Compress aggressively = LLM might need one extra retrieval
894
+
895
+ When retrieval is instantaneous (local cache), the risk/reward calculus shifts entirely in favor of aggressive compression.
896
+
897
+ The future of context compression isn't about better heuristics. It's about **reversible architectures that learn from actual needs**.
898
+
899
+ ---
900
+
901
+ ## Resources
902
+
903
+ - [LLMLingua Paper](https://arxiv.org/abs/2310.05736)
904
+ - [LLMLingua-2 Paper](https://arxiv.org/abs/2403.12968)
905
+ - [ACON Paper](https://arxiv.org/abs/2510.00615)
906
+ - [Selective Context Paper](https://arxiv.org/abs/2310.06201)
907
+ - [Factory.ai Compression Analysis](https://factory.ai/news/evaluating-compression)
908
+ - [Phil Schmid: Context Engineering](https://www.philschmid.de/context-engineering-part-2)
909
+ - [Lost in the Middle](https://arxiv.org/abs/2307.03172)
910
+ - [RAGFlow: From RAG to Context](https://ragflow.io/blog/rag-review-2025-from-rag-to-context)
911
+
912
+ ---
913
+
914
+ *This post describes Headroom, an open-source context optimization layer for LLM applications. [GitHub](https://github.com/headroom-sdk/headroom)*
docs/HEADROOM_FEATURES.md ADDED
@@ -0,0 +1,891 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Headroom: Complete Feature Documentation & Competitive Analysis
2
+
3
+ ## Executive Summary
4
+
5
+ **Headroom is the world's first Context Optimization Layer for LLM applications.** While the industry has focused on routing (LiteLLM), observability (Helicone), and governance (Portkey), no one has solved the fundamental problem: **LLM contexts are bloated with irrelevant data, and this costs money.**
6
+
7
+ Headroom reduces LLM costs by 50-70% through intelligent context compression while maintaining 100% retention of critical information (errors, anomalies, relevant items). It's the missing infrastructure layer between your application and LLM providers.
8
+
9
+ ---
10
+
11
+ # Part 1: Complete Feature Inventory
12
+
13
+ ## 1. Core Transforms (The "Secret Sauce")
14
+
15
+ ### 1.1 SmartCrusher - Statistical Array Compression
16
+
17
+ **Location**: `headroom/transforms/smart_crusher.py`
18
+
19
+ **What It Does**: Compresses large JSON arrays (tool outputs) from 1000s of items to 15-50 items while preserving critical information.
20
+
21
+ **The Safe V1 Recipe** - Always preserves:
22
+ | Preserved Item Type | Why It Matters | Detection Method |
23
+ |---------------------|----------------|------------------|
24
+ | First 3 items | Context/headers | Position-based |
25
+ | Last 2 items | Recency | Position-based |
26
+ | Error items | Critical signals | Keyword matching: `error`, `exception`, `failed`, `failure`, `critical`, `fatal` |
27
+ | Numeric anomalies | Outliers matter | Statistical: values > 2σ from mean |
28
+ | Change points | Regime shifts | Sliding window variance detection |
29
+ | Relevant items | User's needle | BM25/embedding relevance scoring |
30
+
31
+ **Algorithm Details**:
32
+
33
+ ```
34
+ 1. ANALYZE: SmartAnalyzer computes per-field statistics
35
+ - Uniqueness ratio (unique_count / total_count)
36
+ - Numeric stats (min, max, mean, variance)
37
+ - Change points (indices where value significantly shifts)
38
+ - String stats (avg_length, top values)
39
+
40
+ 2. DETECT PATTERN: Identifies data type
41
+ - TIME_SERIES: Has timestamp + numeric variance
42
+ - LOGS: Has message field + level/severity
43
+ - SEARCH_RESULTS: Has score/rank field
44
+ - GENERIC: Default
45
+
46
+ 3. PLAN: Creates compression plan based on pattern
47
+ - TIME_SERIES → Keep items around change points
48
+ - LOGS → Cluster by message, keep representatives
49
+ - SEARCH_RESULTS → Keep top N by score
50
+ - GENERIC → Smart statistical sampling
51
+
52
+ 4. EXECUTE: Apply plan with priority override
53
+ - If errors/anomalies exceed max_items, KEEP ALL
54
+ - Errors are NEVER dropped
55
+ ```
56
+
57
+ **Change Point Detection Algorithm**:
58
+ ```python
59
+ def detect_change_points(values, window=5):
60
+ std_dev = statistics.stdev(values)
61
+ threshold = 2.0 * std_dev
62
+
63
+ for i in range(window, len(values) - window):
64
+ before_mean = mean(values[i-window:i])
65
+ after_mean = mean(values[i:i+window])
66
+ if abs(after_mean - before_mean) > threshold:
67
+ mark_as_change_point(i)
68
+ ```
69
+
70
+ **Configuration Options**:
71
+ ```python
72
+ @dataclass
73
+ class SmartCrusherConfig:
74
+ enabled: bool = True
75
+ min_items_to_analyze: int = 5 # Don't crush tiny arrays
76
+ min_tokens_to_crush: int = 200 # Only if > 200 tokens
77
+ variance_threshold: float = 2.0 # Std devs for anomaly
78
+ uniqueness_threshold: float = 0.1 # < 10% = constant field
79
+ similarity_threshold: float = 0.8 # String clustering
80
+ max_items_after_crush: int = 15 # Target output size
81
+ preserve_change_points: bool = True
82
+ ```
83
+
84
+ **Performance**:
85
+ - 100 items: < 2ms
86
+ - 1,000 items: < 10ms
87
+ - 10,000 items: < 100ms
88
+ - Compression ratio: 50-90% token reduction
89
+
90
+ ---
91
+
92
+ ### 1.5 CCR Architecture - Compress-Cache-Retrieve ⭐ NEW
93
+
94
+ **Location**: `headroom/cache/compression_store.py`, `headroom/cache/compression_feedback.py`
95
+
96
+ **What It Does**: Makes compression **reversible**. When SmartCrusher compresses, the original data is cached. If the LLM needs more, it retrieves instantly.
97
+
98
+ **The Key Innovation**:
99
+ > Traditional compression: Guess what's important → Permanent data loss if wrong
100
+ > CCR: Compress aggressively → Cache original → Retrieve on demand → Zero permanent loss
101
+
102
+ **Four Phases**:
103
+
104
+ | Phase | Component | Description |
105
+ |-------|-----------|-------------|
106
+ | **1. Store** | `CompressionStore` | Cache original content when compressing |
107
+ | **2. Retrieve** | `/v1/retrieve` endpoint | On-demand access to original data |
108
+ | **3. Inject** | Tool/system injection | Tell LLM how to retrieve more |
109
+ | **4. Feedback** | `CompressionFeedback` | Learn from retrieval patterns |
110
+
111
+ **CompressionStore Features**:
112
+ - Thread-safe in-memory storage
113
+ - TTL-based expiration (default 5 minutes)
114
+ - LRU-style eviction at capacity
115
+ - Built-in BM25 search within cached content
116
+ - Hash-based retrieval (16-char SHA256)
117
+
118
+ **Feedback Loop Metrics**:
119
+ ```python
120
+ class ToolPattern:
121
+ retrieval_rate: float # retrievals / compressions
122
+ full_retrieval_rate: float # full_retrievals / total_retrievals
123
+ search_rate: float # search_retrievals / total_retrievals
124
+ common_queries: dict # Most frequent search queries
125
+ queried_fields: dict # Fields mentioned in queries
126
+ ```
127
+
128
+ **Automatic Adjustment**:
129
+ - Retrieval rate >50% → Compress less aggressively (keep 50 items)
130
+ - Retrieval rate >80% with full retrievals → Skip compression entirely
131
+ - Common query fields → Preserve in future compressions
132
+
133
+ **API Endpoints**:
134
+ ```
135
+ POST /v1/retrieve → Retrieve cached content by hash
136
+ GET /v1/feedback → Get all learned patterns
137
+ GET /v1/feedback/{tool} → Get hints for specific tool
138
+ ```
139
+
140
+ **Configuration**:
141
+ ```python
142
+ @dataclass
143
+ class SmartCrusherConfig:
144
+ use_feedback_hints: bool = True # Enable feedback-driven adjustment
145
+ # ... other options
146
+ ```
147
+
148
+ **Why This is a Moat**:
149
+ 1. **Reversible**: No permanent information loss
150
+ 2. **Transparent**: LLM knows it can ask for more
151
+ 3. **Learning**: Improves over time from actual usage
152
+ 4. **Zero-Risk**: Worst case = retrieve everything
153
+
154
+ ---
155
+
156
+ ### 1.2 CacheAligner - Prefix Stabilization
157
+
158
+ **Location**: `headroom/transforms/cache_aligner.py`
159
+
160
+ **What It Does**: Makes your system prompts cache-friendly by extracting dynamic content (dates, timestamps, session IDs) so the static prefix remains byte-identical across requests.
161
+
162
+ **Why This Matters**:
163
+ - Anthropic: 90% discount on cached tokens
164
+ - OpenAI: 50% discount on cached tokens
165
+ - Google: 75% discount on cached tokens
166
+
167
+ Without CacheAligner:
168
+ ```
169
+ Request 1: "Today is January 7, 2025. You are helpful." → Hash: abc123
170
+ Request 2: "Today is January 8, 2025. You are helpful." → Hash: def456 (CACHE MISS!)
171
+ ```
172
+
173
+ With CacheAligner:
174
+ ```
175
+ Request 1: "You are helpful.\n---\n[Dynamic: January 7, 2025]" → Stable Hash: xyz789
176
+ Request 2: "You are helpful.\n---\n[Dynamic: January 8, 2025]" → Stable Hash: xyz789 (CACHE HIT!)
177
+ ```
178
+
179
+ **Detection Tiers**:
180
+
181
+ | Tier | Method | Latency | Coverage |
182
+ |------|--------|---------|----------|
183
+ | 1 (Regex) | Pattern matching | ~0ms | ISO dates, UUIDs, timestamps, version numbers |
184
+ | 2 (NER) | spaCy entities | ~5-10ms | Names, money, organizations, locations |
185
+ | 3 (Semantic) | Embedding similarity | ~20-50ms | Complex dynamic patterns |
186
+
187
+ **Tier 1 Patterns** (Universal, no locale dependencies):
188
+ - ISO 8601 DateTime: `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}`
189
+ - ISO 8601 Date: `\d{4}-\d{2}-\d{2}`
190
+ - Unix Timestamp: `\d{10,13}`
191
+ - UUID: `[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-...-[0-9a-fA-F]{12}`
192
+ - Version: `v\d+\.\d+(?:\.\d+)?`
193
+ - Structural: `Label: value` where Label indicates dynamic content
194
+
195
+ **Entropy-Based Detection**:
196
+ ```python
197
+ def calculate_entropy(s: str) -> float:
198
+ """Shannon entropy normalized to [0, 1]"""
199
+ # High entropy (>0.7) = likely random ID
200
+ # Low entropy (<0.3) = likely static text
201
+ ```
202
+
203
+ **Configuration**:
204
+ ```python
205
+ @dataclass
206
+ class CacheAlignerConfig:
207
+ enabled: bool = True
208
+ date_patterns: list[str] = [...]
209
+ normalize_whitespace: bool = True
210
+ collapse_blank_lines: bool = True
211
+ dynamic_tail_separator: str = "\n\n---\n[Dynamic Context]\n"
212
+ ```
213
+
214
+ ---
215
+
216
+ ### 1.3 RollingWindow - Context Limit Management
217
+
218
+ **Location**: `headroom/transforms/rolling_window.py`
219
+
220
+ **What It Does**: Enforces token limits by dropping oldest context while NEVER orphaning tool call/result pairs.
221
+
222
+ **The Tool Unit Concept**:
223
+ ```
224
+ Messages:
225
+ [0] System: "You are helpful"
226
+ [1] User: "Search for X"
227
+ [2] Assistant: [tool_calls: search(X), summarize()]
228
+ [3] Tool: search result (tool_call_id=call_1)
229
+ [4] Tool: summarize result (tool_call_id=call_2)
230
+ [5] User: "Thanks"
231
+
232
+ Tool Unit: (2, [3, 4]) → These drop TOGETHER
233
+ ```
234
+
235
+ **Why This Matters**: LLM APIs return errors if tool_calls reference missing tool results. RollingWindow treats them as atomic units.
236
+
237
+ **Drop Priority**:
238
+ 1. Oldest tool units (atomic: assistant + all tool results)
239
+ 2. Non-tool user/assistant pairs
240
+ 3. Single messages (last resort)
241
+
242
+ **Protection Rules**:
243
+ - System messages: NEVER dropped
244
+ - Last N turns: ALWAYS kept (default 2)
245
+ - Tool results for protected messages: AUTO-protected
246
+
247
+ **Configuration**:
248
+ ```python
249
+ @dataclass
250
+ class RollingWindowConfig:
251
+ enabled: bool = True
252
+ keep_system: bool = True
253
+ keep_last_turns: int = 2
254
+ output_buffer_tokens: int = 4000 # Reserve for output
255
+ ```
256
+
257
+ ---
258
+
259
+ ### 1.4 Transform Pipeline - Orchestration
260
+
261
+ **Location**: `headroom/transforms/pipeline.py`
262
+
263
+ **Execution Order** (Critical):
264
+ ```
265
+ 1. CacheAligner → Stabilize prefix for cache hits
266
+ 2. SmartCrusher → Compress tool outputs
267
+ 3. RollingWindow → Enforce token limits
268
+ ```
269
+
270
+ **Why This Order**:
271
+ 1. Cache alignment must happen before content changes
272
+ 2. Compression reduces tokens before limit enforcement
273
+ 3. Rolling window is the final safety net
274
+
275
+ **Token Tracking**: Pipeline tracks tokens through each stage and reports:
276
+ ```python
277
+ @dataclass
278
+ class TransformResult:
279
+ messages: list[dict]
280
+ tokens_before: int
281
+ tokens_after: int
282
+ transforms_applied: list[str]
283
+ markers_inserted: list[str]
284
+ ```
285
+
286
+ ---
287
+
288
+ ## 2. Relevance Scoring Engine
289
+
290
+ ### 2.1 BM25Scorer - Keyword Matching
291
+
292
+ **Location**: `headroom/relevance/bm25.py`
293
+
294
+ **What It Does**: Fast, zero-dependency keyword matching using the BM25 algorithm from information retrieval.
295
+
296
+ **Algorithm**:
297
+ ```
298
+ score(D, Q) = Σ IDF(q) * (f(q,D) * (k1 + 1)) / (f(q,D) + k1 * (1 - b + b * |D|/avgdl))
299
+
300
+ Parameters:
301
+ - k1 = 1.5 (term frequency saturation)
302
+ - b = 0.75 (length normalization)
303
+ ```
304
+
305
+ **Special Features**:
306
+ - UUID preservation in tokenization
307
+ - +0.3 bonus for exact long token matches (≥8 chars)
308
+ - Query frequency weighting
309
+
310
+ **Use Cases**: Exact ID matching, UUID lookup, keyword search
311
+
312
+ ---
313
+
314
+ ### 2.2 EmbeddingScorer - Semantic Matching
315
+
316
+ **Location**: `headroom/relevance/embedding.py`
317
+
318
+ **What It Does**: Semantic similarity using sentence-transformers embeddings.
319
+
320
+ **Model**: `all-MiniLM-L6-v2` (22M params, 384 dimensions)
321
+
322
+ **Algorithm**:
323
+ ```python
324
+ score = cosine_similarity(embed(item), embed(query))
325
+ # Clamped to [0, 1]
326
+ ```
327
+
328
+ **Optimizations**:
329
+ - Batch encoding (context + all items in one call)
330
+ - Model caching across instances
331
+ - Normalized embeddings for fast cosine
332
+
333
+ **Use Cases**: Natural language queries, semantic search
334
+
335
+ ---
336
+
337
+ ### 2.3 HybridScorer - Adaptive Fusion
338
+
339
+ **Location**: `headroom/relevance/hybrid.py`
340
+
341
+ **What It Does**: Combines BM25 and embedding scores with adaptive alpha based on query characteristics.
342
+
343
+ **Fusion Formula**:
344
+ ```
345
+ combined = α * BM25_score + (1 - α) * Embedding_score
346
+ ```
347
+
348
+ **Adaptive Alpha** (Research: Hsu et al., 2025):
349
+ ```python
350
+ def compute_alpha(query):
351
+ if has_uuid(query):
352
+ return 0.85 # Favor exact matching
353
+ elif has_multiple_ids(query):
354
+ return 0.75
355
+ elif has_single_id(query):
356
+ return 0.65
357
+ elif has_hostname_or_email(query):
358
+ return 0.60
359
+ else:
360
+ return 0.50 # Balanced
361
+ ```
362
+
363
+ **Graceful Degradation**: If embeddings unavailable, falls back to boosted BM25.
364
+
365
+ ---
366
+
367
+ ## 3. Cache Optimization (Provider-Specific)
368
+
369
+ ### 3.1 Provider Comparison Matrix
370
+
371
+ | Feature | Anthropic | OpenAI | Google |
372
+ |---------|-----------|--------|--------|
373
+ | **Strategy** | Explicit `cache_control` | Automatic prefix | `CachedContent` API |
374
+ | **Min Tokens** | 1,024 | 1,024 | 32,768 |
375
+ | **Max Breakpoints** | 4 | N/A | 1 |
376
+ | **Write Cost** | 1.25x | N/A | N/A |
377
+ | **Read Cost** | 0.10x (90% off) | 0.50x (50% off) | 0.25x (75% off) |
378
+ | **TTL** | 5 min | 5-60 min | Up to 7 days |
379
+ | **Control** | Explicit | Automatic | Explicit |
380
+
381
+ ### 3.2 AnthropicCacheOptimizer
382
+
383
+ **Location**: `headroom/cache/anthropic.py`
384
+
385
+ **Algorithm**:
386
+ 1. Analyze message sections (system, tools, examples, user)
387
+ 2. Stabilize prefix by extracting dynamic content
388
+ 3. Plan breakpoints (max 4, prioritize system > tools > examples)
389
+ 4. Insert `cache_control: {"type": "ephemeral"}` blocks
390
+
391
+ **Cost Example**:
392
+ ```
393
+ First request (write): 1,500 cached tokens * 1.25x = 1,875 cost
394
+ Subsequent (read): 1,500 cached tokens * 0.10x = 150 cost
395
+ Savings per hit: 92%
396
+ ```
397
+
398
+ ### 3.3 OpenAICacheOptimizer
399
+
400
+ **Location**: `headroom/cache/openai.py`
401
+
402
+ **Strategy**: Since OpenAI caching is automatic, we maximize cache hits through prefix stabilization:
403
+ 1. Extract dynamic content via tiered detection
404
+ 2. Move dates/IDs to end of message
405
+ 3. Normalize whitespace for consistent hashing
406
+
407
+ ### 3.4 GoogleCacheOptimizer
408
+
409
+ **Location**: `headroom/cache/google.py`
410
+
411
+ **Strategy**: Uses Google's explicit CachedContent API:
412
+ 1. Analyze cacheability (need 32K+ tokens)
413
+ 2. Prepare cache creation params
414
+ 3. Register cache for reuse
415
+ 4. Include `cache_id` in subsequent requests
416
+
417
+ ---
418
+
419
+ ## 4. Production Proxy Server
420
+
421
+ **Location**: `headroom/proxy/server.py` (1400+ lines)
422
+
423
+ ### 4.1 Core Features
424
+
425
+ | Feature | Description | Configuration |
426
+ |---------|-------------|---------------|
427
+ | **Optimization** | SmartCrusher + CacheAligner + RollingWindow | `optimize=True` |
428
+ | **Semantic Cache** | Hash-based response caching with TTL | `cache_ttl_seconds=3600` |
429
+ | **Rate Limiting** | Token bucket algorithm (requests + tokens) | `rate_limit_requests_per_minute=60` |
430
+ | **Retry** | Exponential backoff with jitter | `retry_max_attempts=3` |
431
+ | **Cost Tracking** | Real-time cost + budget enforcement | `budget_limit_usd=100.0` |
432
+ | **Prometheus** | `/metrics` endpoint | Automatic |
433
+ | **Logging** | JSONL request logs | `log_file="/var/log/headroom.jsonl"` |
434
+
435
+ ### 4.2 Endpoints
436
+
437
+ ```
438
+ GET /health → Health check
439
+ GET /stats → Detailed statistics
440
+ GET /metrics → Prometheus format
441
+ POST /v1/messages → Anthropic API proxy
442
+ POST /v1/chat/completions → OpenAI API proxy
443
+ POST /cache/clear → Clear semantic cache
444
+
445
+ # CCR Endpoints (NEW)
446
+ POST /v1/retrieve → Retrieve cached original content
447
+ GET /v1/feedback → Get all learned patterns
448
+ GET /v1/feedback/{tool} → Get hints for specific tool
449
+ ```
450
+
451
+ ### 4.3 Token Bucket Rate Limiter
452
+
453
+ ```python
454
+ class TokenBucketRateLimiter:
455
+ def check_request(api_key) -> (allowed: bool, wait_seconds: float)
456
+ def check_tokens(api_key, count) -> (allowed: bool, wait_seconds: float)
457
+
458
+ # Continuous refill based on elapsed time
459
+ # Separate buckets for requests and tokens per API key
460
+ ```
461
+
462
+ ### 4.4 Cost Tracker
463
+
464
+ ```python
465
+ PRICING = {
466
+ "claude-3-5-sonnet": (3.00, 15.00, 0.30), # input, output, cached
467
+ "gpt-4o": (2.50, 10.00, 1.25),
468
+ ...
469
+ }
470
+
471
+ class CostTracker:
472
+ def estimate_cost(model, input_tokens, output_tokens, cached_tokens)
473
+ def check_budget() -> (within_budget: bool, remaining_usd: float)
474
+ ```
475
+
476
+ ---
477
+
478
+ ## 5. Multi-Provider Support
479
+
480
+ ### 5.1 Token Counting
481
+
482
+ | Provider | Method | Accuracy |
483
+ |----------|--------|----------|
484
+ | Anthropic | Official Token Count API | High |
485
+ | Anthropic (fallback) | tiktoken * 1.1 | Medium |
486
+ | OpenAI | tiktoken (model-specific) | High |
487
+ | Google | Official countTokens API | High |
488
+
489
+ ### 5.2 Supported Models
490
+
491
+ **Anthropic**:
492
+ - claude-3-5-sonnet-20241022 (200K context)
493
+ - claude-3-5-haiku-20241022 (200K context)
494
+ - claude-3-opus-20240229 (200K context)
495
+
496
+ **OpenAI**:
497
+ - gpt-4o (128K context)
498
+ - gpt-4o-mini (128K context)
499
+ - o1, o1-mini, o3-mini (128-200K context)
500
+
501
+ **Google**:
502
+ - gemini-2.0-flash (1M context)
503
+ - gemini-1.5-pro (2M context)
504
+ - gemini-1.5-flash (1M context)
505
+
506
+ ---
507
+
508
+ ## 6. Integrations
509
+
510
+ ### 6.1 LangChain Integration
511
+
512
+ **Location**: `headroom/integrations/langchain.py`
513
+
514
+ **HeadroomChatModel** - Wrapper that applies optimization:
515
+ ```python
516
+ from langchain_openai import ChatOpenAI
517
+ from headroom.integrations import HeadroomChatModel
518
+
519
+ base_model = ChatOpenAI(model="gpt-4o")
520
+ optimized = HeadroomChatModel(base_model, config=HeadroomConfig())
521
+
522
+ response = optimized.invoke("What is 2+2?")
523
+ print(f"Saved: {optimized.total_tokens_saved} tokens")
524
+ ```
525
+
526
+ ### 6.2 MCP Integration
527
+
528
+ **Location**: `headroom/integrations/mcp.py`
529
+
530
+ **HeadroomMCPCompressor** - Compress tool outputs:
531
+ ```python
532
+ from headroom.integrations.mcp import compress_tool_result_with_metrics
533
+
534
+ result = compress_tool_result_with_metrics(
535
+ content=tool_output,
536
+ tool_name="search_logs",
537
+ user_query="find errors",
538
+ )
539
+ print(f"Items: {result.items_before} → {result.items_after}")
540
+ print(f"Errors preserved: {result.errors_preserved}")
541
+ ```
542
+
543
+ **Default Tool Profiles**:
544
+ ```python
545
+ # Slack - preserve bugs/issues
546
+ MCPToolProfile(tool_name_pattern=r".*slack.*", max_items=25)
547
+
548
+ # Database - preserve nulls/violations
549
+ MCPToolProfile(tool_name_pattern=r".*database.*", max_items=30)
550
+
551
+ # Logs - preserve ALL errors
552
+ MCPToolProfile(tool_name_pattern=r".*log.*", max_items=40)
553
+ ```
554
+
555
+ ---
556
+
557
+ ## 7. Pricing Registry
558
+
559
+ **Location**: `headroom/pricing/`
560
+
561
+ **Features**:
562
+ - Real-time pricing for all models
563
+ - Batch pricing support
564
+ - Staleness detection (warns if >30 days old)
565
+ - Cost estimation with breakdown
566
+
567
+ **Last Updated**: January 6, 2025
568
+
569
+ ---
570
+
571
+ # Part 2: Why Headroom is Different
572
+
573
+ ## The Market Gap Nobody Else Fills
574
+
575
+ ### What Existing Tools Do
576
+
577
+ | Tool | Category | What It Does | What It DOESN'T Do |
578
+ |------|----------|--------------|-------------------|
579
+ | **LiteLLM** | Gateway/Routing | Unified API for 100+ providers | No context optimization |
580
+ | **Helicone** | Observability | Logs, metrics, dashboards | No compression, just watching |
581
+ | **Portkey** | Governance | Guardrails, compliance, security | No token reduction |
582
+ | **OpenRouter** | Marketplace | Access to 300+ models | 5% markup, no optimization |
583
+ | **Cloudflare AI Gateway** | CDN | Caching at edge | Simple caching, no intelligence |
584
+
585
+ ### What Headroom Does (That Nobody Else Does)
586
+
587
+ **1. Statistical Compression with Quality Guarantees**
588
+
589
+ No other tool compresses tool outputs while guaranteeing error preservation:
590
+ ```
591
+ Input: 1,000 search results (50,000 tokens)
592
+ Output: 20 results (1,000 tokens) - 98% reduction
593
+ ALL errors preserved: 100%
594
+ ALL anomalies preserved: 100%
595
+ ```
596
+
597
+ **2. Relevance-Aware Filtering**
598
+
599
+ SmartCrusher uses BM25 + embeddings to keep items matching the user's query:
600
+ ```
601
+ User asks: "Why is authentication failing?"
602
+ Tool returns: 1,000 log entries
603
+ SmartCrusher keeps:
604
+ - All entries with "error", "failed", "exception"
605
+ - Entries semantically similar to "authentication failing"
606
+ - First 3 and last 2 for context
607
+ ```
608
+
609
+ **3. Provider-Specific Cache Optimization**
610
+
611
+ We understand each provider's caching rules:
612
+ - Anthropic: We insert `cache_control` blocks at optimal positions
613
+ - OpenAI: We stabilize prefixes for automatic caching
614
+ - Google: We manage CachedContent lifecycle
615
+
616
+ **4. Atomic Tool Unit Handling**
617
+
618
+ RollingWindow is the only context manager that treats tool_calls and their results as atomic:
619
+ ```
620
+ Other tools: Drop old messages → Orphaned tool results → API ERROR
621
+ Headroom: Drop tool units atomically → Always valid state
622
+ ```
623
+
624
+ ---
625
+
626
+ ## Competitive Analysis: Deep Dive
627
+
628
+ ### vs. LiteLLM
629
+
630
+ | Aspect | LiteLLM | Headroom |
631
+ |--------|---------|----------|
632
+ | **Primary Function** | Route to 100+ providers | Optimize before routing |
633
+ | **Token Reduction** | None | 50-70% |
634
+ | **Caching** | None | Semantic + provider-specific |
635
+ | **Setup Time** | 15-30 min | 5 min |
636
+ | **Latency Overhead** | ~500µs | <50ms |
637
+ | **Relationship** | Complementary - we optimize BEFORE LiteLLM routes |
638
+
639
+ **Partnership Opportunity**: Headroom optimizes → LiteLLM routes → best of both.
640
+
641
+ ### vs. Helicone
642
+
643
+ | Aspect | Helicone | Headroom |
644
+ |--------|----------|----------|
645
+ | **Primary Function** | Observe and log | Optimize and compress |
646
+ | **Token Reduction** | Shows waste, doesn't fix it | Eliminates waste |
647
+ | **Latency** | ~50ms (Rust) | <50ms |
648
+ | **Caching** | Redis-based, TTL | Semantic + provider-specific |
649
+ | **Relationship** | Complementary - we reduce, they observe |
650
+
651
+ **Partnership Opportunity**: Headroom compresses → Helicone shows savings achieved.
652
+
653
+ ### vs. Portkey
654
+
655
+ | Aspect | Portkey | Headroom |
656
+ |--------|---------|----------|
657
+ | **Primary Function** | Governance, guardrails | Optimization, compression |
658
+ | **Target User** | Enterprise security teams | Developers, cost-conscious |
659
+ | **Token Reduction** | None | 50-70% |
660
+ | **Pricing** | From $49/month | Open source core |
661
+ | **Relationship** | Different markets |
662
+
663
+ ### vs. Prompt Compression Techniques (LLMLingua, etc.)
664
+
665
+ | Aspect | LLMLingua-2 | Headroom |
666
+ |--------|-------------|----------|
667
+ | **Approach** | Token classification (remove tokens) | Statistical sampling (keep important items) |
668
+ | **Target** | Reduce prompt tokens | Reduce tool output tokens |
669
+ | **Granularity** | Token-level | Item-level (semantic units) |
670
+ | **Quality Guarantee** | 95-98% accuracy | 100% error retention |
671
+ | **Dependencies** | XLM-RoBERTa model | Zero (BM25) or sentence-transformers |
672
+ | **Use Case** | Long prompts | Large JSON arrays from tools |
673
+
674
+ ---
675
+
676
+ ## The Industry Problem We Solve
677
+
678
+ ### Context Explosion in AI Agents
679
+
680
+ Research from [JetBrains (Dec 2025)](https://blog.jetbrains.com/research/2025/12/efficient-context-management/):
681
+ > "Agents make multiple tool calls in sequence, and each tool's output is fed back into the LLM's context window. Without proper context management, this accumulation can quickly exceed the context window, increase costs dramatically, and degrade performance."
682
+
683
+ ### The "Lost in the Middle" Problem
684
+
685
+ > "LLMs are more likely to recall information appearing at the beginning or end of long prompts rather than content buried in the middle."
686
+
687
+ **Headroom's Solution**: SmartCrusher keeps first 3 + last 2 items, plus errors/anomalies/relevant items. We work WITH the LLM's attention patterns.
688
+
689
+ ### Context Rot
690
+
691
+ > "Expanding context windows does not guarantee improved model performance. As input tokens increase, LLM performance can actually degrade."
692
+
693
+ **Headroom's Solution**: Smaller, higher-quality context → better performance AND lower cost.
694
+
695
+ ---
696
+
697
+ ## Unique Technical Innovations
698
+
699
+ ### 1. Change Point Detection for Time Series
700
+
701
+ No other tool detects regime shifts in numeric data:
702
+ ```python
703
+ # Values: [100, 102, 98, 101, 99, 500, 502, 498, 501]
704
+ # ↑
705
+ # Change point detected!
706
+ # SmartCrusher keeps items around index 5
707
+ ```
708
+
709
+ ### 2. Adaptive Relevance Fusion
710
+
711
+ Our HybridScorer adjusts BM25/embedding weights based on query type:
712
+ - UUID in query → More BM25 (exact matching)
713
+ - Natural language → More embedding (semantic)
714
+
715
+ This achieves +2-7.5% accuracy improvement over fixed weights.
716
+
717
+ ### 3. Tool Unit Atomicity
718
+
719
+ The only context manager that guarantees:
720
+ ```
721
+ assistant message with tool_calls → ALWAYS has corresponding tool results
722
+ ```
723
+
724
+ ### 4. Tiered Dynamic Detection
725
+
726
+ We don't use hardcoded locale patterns. Our detection is:
727
+ - Universal: ISO 8601, UUIDs, entropy-based IDs
728
+ - Structural: `Label: value` patterns
729
+ - Semantic: Embedding similarity to known dynamic exemplars
730
+
731
+ ---
732
+
733
+ # Part 3: Real Numbers
734
+
735
+ ## Compression Performance
736
+
737
+ | Scenario | Items Before | Items After | Token Reduction | Errors Retained |
738
+ |----------|--------------|-------------|-----------------|-----------------|
739
+ | Search Results | 1,000 | 20 | 85% | 100% |
740
+ | Log Entries | 500 | 40 | 80% | 100% |
741
+ | Database Rows | 1,000 | 30 | 90% | 100% |
742
+ | API Responses | 200 | 15 | 70% | 100% |
743
+
744
+ ## Latency Overhead
745
+
746
+ | Component | P50 | P99 |
747
+ |-----------|-----|-----|
748
+ | SmartCrusher (1000 items) | 5ms | 15ms |
749
+ | CacheAligner | <1ms | 2ms |
750
+ | RollingWindow | <1ms | 5ms |
751
+ | Full Pipeline | 10ms | 25ms |
752
+
753
+ ## Cost Savings (Real World)
754
+
755
+ **Claude Code Agent Session**:
756
+ ```
757
+ Without Headroom:
758
+ - Tool outputs: 150,000 tokens
759
+ - Cost: $0.45 (input @ $3/M)
760
+
761
+ With Headroom:
762
+ - Tool outputs: 30,000 tokens (80% reduction)
763
+ - Cost: $0.09 (input @ $3/M)
764
+ - Savings: $0.36 per session (80%)
765
+ ```
766
+
767
+ **Enterprise (1M requests/month)**:
768
+ ```
769
+ Without Headroom: $450,000/month
770
+ With Headroom: $90,000/month
771
+ Savings: $360,000/month (80%)
772
+ ```
773
+
774
+ ---
775
+
776
+ # Part 4: Architecture Summary
777
+
778
+ ```
779
+ ┌─────────────────────────────────────────────────────────────┐
780
+ │ YOUR APPLICATION │
781
+ │ │
782
+ │ LangChain │ Claude Code │ Cursor │ Custom Agent │
783
+ └──────────────────────────┬──────────────────────────────────┘
784
+
785
+
786
+ ┌─────────────────────────────────────────────────────────────┐
787
+ │ HEADROOM PROXY │
788
+ │ │
789
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
790
+ │ │ Cache │ │ Rate │ │ Cost │ │
791
+ │ │ (Semantic) │ │ Limiter │ │ Tracker │ │
792
+ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │
793
+ │ │
794
+ │ ┌─────────────────────────────────────────────────────────┐│
795
+ │ │ TRANSFORM PIPELINE ││
796
+ │ │ ││
797
+ │ │ 1. CacheAligner → Stabilize prefix for cache hits ││
798
+ │ │ 2. SmartCrusher → Compress tool outputs ││
799
+ │ │ 3. RollingWindow → Enforce token limits ││
800
+ │ │ ││
801
+ │ │ ┌─────────────────────────────────────────────────┐ ││
802
+ │ │ │ RELEVANCE ENGINE │ ││
803
+ │ │ │ BM25 + Embedding + Adaptive Hybrid │ ││
804
+ │ │ └─────────────────────────────────────────────────┘ ││
805
+ │ └─────────────────────────────────────────────────────────┘│
806
+ │ │
807
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
808
+ │ │ Prometheus │ │ JSONL │ │ Retry │ │
809
+ │ │ Metrics │ │ Logging │ │ (Exp. Backoff) │ │
810
+ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │
811
+ └──────────────────────────┬──────────────────────────────────┘
812
+
813
+
814
+ ┌─────────────────────────────────────────────────────────────┐
815
+ │ LLM PROVIDERS │
816
+ │ │
817
+ │ Anthropic │ OpenAI │ Google │ Others │
818
+ │ │
819
+ │ ┌─────────────────────────────────────────────────────────┐│
820
+ │ │ PROVIDER-SPECIFIC CACHE OPTIMIZERS ││
821
+ │ │ ││
822
+ │ │ Anthropic: cache_control blocks (90% savings) ││
823
+ │ │ OpenAI: Prefix stabilization (50% savings) ││
824
+ │ │ Google: CachedContent API (75% savings) ││
825
+ │ └─────────────────────────────────────────────────────────┘│
826
+ └─────────────────────────────────────────────────────────────┘
827
+ ```
828
+
829
+ ---
830
+
831
+ # Part 5: File Inventory
832
+
833
+ ## Core Transforms
834
+ - `headroom/transforms/smart_crusher.py` - Statistical array compression
835
+ - `headroom/transforms/cache_aligner.py` - Prefix stabilization
836
+ - `headroom/transforms/rolling_window.py` - Context limit management
837
+ - `headroom/transforms/pipeline.py` - Transform orchestration
838
+
839
+ ## Relevance Scoring
840
+ - `headroom/relevance/bm25.py` - BM25 keyword scorer
841
+ - `headroom/relevance/embedding.py` - Semantic scorer
842
+ - `headroom/relevance/hybrid.py` - Adaptive fusion scorer
843
+
844
+ ## Cache Optimization
845
+ - `headroom/cache/base.py` - Base interfaces
846
+ - `headroom/cache/anthropic.py` - Anthropic optimizer
847
+ - `headroom/cache/openai.py` - OpenAI optimizer
848
+ - `headroom/cache/google.py` - Google optimizer
849
+ - `headroom/cache/dynamic_detector.py` - Tiered dynamic detection
850
+ - `headroom/cache/semantic.py` - Semantic cache layer
851
+ - `headroom/cache/compression_store.py` - CCR Phase 1: Store original content ⭐ NEW
852
+ - `headroom/cache/compression_feedback.py` - CCR Phase 4: Learn from retrievals ⭐ NEW
853
+
854
+ ## Proxy Server
855
+ - `headroom/proxy/server.py` - Production HTTP proxy (1400+ lines)
856
+
857
+ ## Providers
858
+ - `headroom/providers/anthropic.py` - Anthropic token counting
859
+ - `headroom/providers/openai.py` - OpenAI token counting
860
+ - `headroom/providers/google.py` - Google token counting
861
+
862
+ ## Integrations
863
+ - `headroom/integrations/langchain.py` - LangChain wrapper
864
+ - `headroom/integrations/mcp.py` - MCP compression
865
+
866
+ ## Pricing
867
+ - `headroom/pricing/registry.py` - Pricing registry
868
+ - `headroom/pricing/anthropic_prices.py` - Anthropic prices
869
+ - `headroom/pricing/openai_prices.py` - OpenAI prices
870
+
871
+ ## Tests
872
+ - `tests/test_quality_retention.py` - 21 formal evals for quality guarantees
873
+ - `tests/test_cache/test_dynamic_detector.py` - Dynamic detection tests
874
+ - `tests/test_ccr.py` - CCR store, tool injection tests ⭐ NEW
875
+ - `tests/test_ccr_feedback.py` - CCR feedback loop tests ⭐ NEW
876
+
877
+ ## Benchmarks
878
+ - `benchmarks/agent_cost_benchmark.py` - Real-world agent cost analysis
879
+ - `benchmarks/dynamic_detector_benchmark.py` - Detection performance
880
+
881
+ ---
882
+
883
+ # Sources
884
+
885
+ - [JetBrains Research: Efficient Context Management (Dec 2025)](https://blog.jetbrains.com/research/2025/12/efficient-context-management/)
886
+ - [LangChain: Context Engineering for Agents](https://blog.langchain.com/context-engineering-for-agents/)
887
+ - [Helicone: Top 5 LLM Gateways 2025](https://www.helicone.ai/blog/top-llm-gateways-comparison-2025)
888
+ - [Agenta: Top LLM Gateways 2025](https://agenta.ai/blog/top-llm-gateways)
889
+ - [Portkey: LLM Proxy vs AI Gateway](https://portkey.ai/blog/llm-proxy-vs-ai-gateway/)
890
+ - [Medium: Prompt Compression Techniques (Nov 2025)](https://medium.com/@kuldeep.paul08/prompt-compression-techniques-reducing-context-window-costs-while-improving-llm-performance-afec1e8f1003)
891
+ - [Factory.ai: Compressing Context](https://factory.ai/news/compressing-context)
docs/PATH_TO_10_OUT_OF_10.md ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # The Path to 10/10: Strategic Deep Dive
2
+
3
+ ## Current State
4
+
5
+ | Dimension | Score | Gap |
6
+ |-----------|-------|-----|
7
+ | Problem validity | 9/10 | Framing as "cost" not "capability" |
8
+ | Solution fit | 7/10 | 30% of scenarios fail silently |
9
+ | Technical moat | 6/10 | Easy to replicate basics |
10
+ | Market timing | 9/10 | Positioned but not capturing |
11
+ | **Overall** | **7.5/10** | |
12
+
13
+ ---
14
+
15
+ # Dimension 1: Problem Validity (9 → 10)
16
+
17
+ ## Current Framing (9/10)
18
+ "Token costs are expensive. We save you 50-90%."
19
+
20
+ **Why it's not 10/10**: Cost savings is a feature, not a platform. It's also easily commoditized - anyone can undercut on price.
21
+
22
+ ## The 10/10 Framing: Capability Enablement
23
+
24
+ **The insight**: Without context optimization, certain agent capabilities are **literally impossible**.
25
+
26
+ ### Evidence
27
+
28
+ | Scenario | Without Headroom | With Headroom |
29
+ |----------|------------------|---------------|
30
+ | Multi-tool investigation (5+ tools) | Context overflow at 128K | Fits in 30K |
31
+ | Long-running agent (50+ turns) | Loses early context | Maintains full history |
32
+ | Real-time agents (latency-sensitive) | Cache misses = 2-3s latency | Cache hits = 200ms |
33
+ | Cost-constrained deployment | $5K/month = 5K requests | $5K/month = 25K requests |
34
+
35
+ **The reframe**:
36
+
37
+ > "Headroom doesn't just save money. It **unlocks agent capabilities that are impossible without context optimization**."
38
+
39
+ ### Specific Claims to Make
40
+
41
+ 1. **"Enable 5x more tool calls per context window"**
42
+ - Not "save 80% on tokens"
43
+ - But "do 5x more in the same budget"
44
+
45
+ 2. **"Make real-time agents viable"**
46
+ - Cache alignment → cache hits → <500ms responses
47
+ - Without this, interactive agents are too slow
48
+
49
+ 3. **"Prevent context overflow failures"**
50
+ - Agent that fails at turn 47 because context overflowed
51
+ - vs. agent that completes 200-turn sessions
52
+
53
+ 4. **"Run agents at 10x the scale"**
54
+ - Same budget, 10x throughput
55
+ - This is a capability unlock, not a cost savings
56
+
57
+ ### Action Items
58
+
59
+ - [ ] Rewrite all marketing around "capability enablement"
60
+ - [ ] Quantify "things you CAN'T do without Headroom"
61
+ - [ ] Build demo showing agent that fails → succeeds with Headroom
62
+ - [ ] Position as "Context Runtime" not "Token Optimizer"
63
+
64
+ ---
65
+
66
+ # Dimension 2: Solution Fit (7 → 10)
67
+
68
+ ## Current Problem (7/10)
69
+
70
+ Heuristics work for ~70% of scenarios. The 30% that fail:
71
+ - Entity listings (each item is unique and important)
72
+ - Exhaustive queries ("find ALL X")
73
+ - Needles that look normal (Order #47 from California)
74
+
75
+ **Root cause**: Task-agnostic compression can't know what the LLM will need.
76
+
77
+ ## The 10/10 Solution: Three-Layer Architecture
78
+
79
+ ### Layer 1: Smart Routing (NEW)
80
+
81
+ **Before compression, classify the task:**
82
+
83
+ ```python
84
+ class TaskClassifier:
85
+ """Classify task to determine compression strategy."""
86
+
87
+ def classify(self, user_query: str, tool_output: dict) -> TaskType:
88
+ # Analyze user query intent
89
+ if self._is_exhaustive_query(user_query):
90
+ return TaskType.EXHAUSTIVE # "find ALL", "list every"
91
+
92
+ if self._is_specific_lookup(user_query):
93
+ return TaskType.LOOKUP # "find user #47", "get order X"
94
+
95
+ if self._is_analytical(user_query):
96
+ return TaskType.ANALYTICAL # "what's wrong", "summarize"
97
+
98
+ return TaskType.GENERAL
99
+
100
+ def _is_exhaustive_query(self, query: str) -> bool:
101
+ exhaustive_patterns = [
102
+ r"\ball\b", r"\bevery\b", r"\beach\b",
103
+ r"\bcomplete list\b", r"\bfull list\b"
104
+ ]
105
+ return any(re.search(p, query.lower()) for p in exhaustive_patterns)
106
+ ```
107
+
108
+ **Strategy per task type:**
109
+
110
+ | Task Type | Strategy | Rationale |
111
+ |-----------|----------|-----------|
112
+ | EXHAUSTIVE | Skip compression | User needs everything |
113
+ | LOOKUP | Filter by query match | Only relevant items |
114
+ | ANALYTICAL | Statistical compression | Summaries ok |
115
+ | GENERAL | Default heuristics | Balanced approach |
116
+
117
+ ### Layer 2: Confidence-Gated Compression (NEW)
118
+
119
+ **Only compress when confidence is high:**
120
+
121
+ ```python
122
+ class CompressionConfidence:
123
+ """Estimate confidence that compression is safe."""
124
+
125
+ def estimate(self, items: list[dict], hints: CompressionHints) -> float:
126
+ confidence = 1.0
127
+
128
+ # Low confidence if high uniqueness + no importance signal
129
+ if self._is_high_uniqueness(items) and not self._has_importance_signal(items):
130
+ confidence -= 0.4
131
+
132
+ # Low confidence if historical retrieval rate is high
133
+ if hints.retrieval_rate > 0.5:
134
+ confidence -= 0.3
135
+
136
+ # Low confidence if items look like entities
137
+ if self._looks_like_entity_list(items):
138
+ confidence -= 0.3
139
+
140
+ return max(0.0, confidence)
141
+
142
+ def should_compress(self, confidence: float) -> bool:
143
+ return confidence > 0.6 # Only compress when confident
144
+ ```
145
+
146
+ **The key insight**: It's better to NOT compress than to compress wrong.
147
+
148
+ ### Layer 3: Seamless CCR (Enhanced)
149
+
150
+ **Make retrieval so good that compression "failures" don't matter:**
151
+
152
+ Current CCR:
153
+ ```
154
+ LLM: "I need to find orders from California"
155
+ [Must explicitly call retrieve_compressed]
156
+ ```
157
+
158
+ Enhanced CCR:
159
+ ```
160
+ LLM: "I need to find orders from California"
161
+ [Automatic injection]: "Searching compressed content for 'California'..."
162
+ [Returns matching items without explicit tool call]
163
+ ```
164
+
165
+ **Implementation: Semantic Injection**
166
+
167
+ ```python
168
+ class SemanticCCR:
169
+ """Automatically inject relevant cached content based on LLM response."""
170
+
171
+ def intercept_response(self, llm_response: str, cached_hashes: list[str]) -> str:
172
+ # Detect if LLM is "reaching" for data it doesn't have
173
+ reaching_patterns = [
174
+ r"I don't see .* in the data",
175
+ r"The data doesn't show",
176
+ r"I need more information about",
177
+ r"Looking for .* but",
178
+ ]
179
+
180
+ for pattern in reaching_patterns:
181
+ match = re.search(pattern, llm_response)
182
+ if match:
183
+ # Extract what they're looking for
184
+ query = self._extract_search_intent(llm_response)
185
+ # Search all cached content
186
+ results = self._search_cached(cached_hashes, query)
187
+ if results:
188
+ # Inject into context
189
+ return self._inject_results(llm_response, results)
190
+
191
+ return llm_response
192
+ ```
193
+
194
+ ### Layer 4: Learned Compression Profiles (NEW)
195
+
196
+ **Per-tool profiles that go beyond heuristics:**
197
+
198
+ ```python
199
+ @dataclass
200
+ class ToolCompressionProfile:
201
+ """Learned compression profile for a specific tool."""
202
+
203
+ tool_name: str
204
+
205
+ # Learned from retrieval patterns
206
+ critical_fields: list[str] # Always preserve these
207
+ optional_fields: list[str] # Can compress
208
+ noise_fields: list[str] # Usually irrelevant
209
+
210
+ # Learned from retrieval rate
211
+ min_items: int # Never compress below this
212
+ target_items: int # Optimal compression target
213
+ skip_conditions: list[str] # When to skip compression entirely
214
+
215
+ # Learned from query patterns
216
+ common_search_terms: list[str] # Pre-filter for these
217
+
218
+ # Confidence
219
+ sample_size: int # How much data we've seen
220
+ confidence: float # How confident in this profile
221
+ ```
222
+
223
+ **Building profiles from feedback:**
224
+
225
+ ```python
226
+ def update_profile_from_retrieval(profile: ToolCompressionProfile, event: RetrievalEvent):
227
+ # If they retrieved, compression was too aggressive
228
+ profile.min_items = max(profile.min_items, event.items_retrieved)
229
+
230
+ # Track what fields they queried
231
+ for field in extract_fields(event.query):
232
+ if field not in profile.critical_fields:
233
+ profile.critical_fields.append(field)
234
+
235
+ # Track common search terms
236
+ if event.query:
237
+ profile.common_search_terms.append(event.query)
238
+
239
+ # Update confidence based on sample size
240
+ profile.sample_size += 1
241
+ profile.confidence = min(0.95, profile.sample_size / 100)
242
+ ```
243
+
244
+ ## The 10/10 Solution Architecture
245
+
246
+ ```
247
+ ┌─────────────────────────────────────────────────────────────────┐
248
+ │ TOOL OUTPUT (1000 items) │
249
+ └─────────────────────────────────────────────────────────────────┘
250
+
251
+
252
+ ┌─────────────────────────────────────────────────────────────────┐
253
+ │ LAYER 1: TASK CLASSIFICATION │
254
+ │ │
255
+ │ User query: "Find all orders from California" │
256
+ │ Classification: EXHAUSTIVE (pattern: "all") │
257
+ │ Decision: SKIP COMPRESSION │
258
+ └─────────────────────────────────────────────────────────────────┘
259
+
260
+ ▼ (if not SKIP)
261
+ ┌─────────────────────────────────────────────────────────────────┐
262
+ │ LAYER 2: CONFIDENCE ESTIMATION │
263
+ │ │
264
+ │ Tool profile: search_api (confidence: 0.85) │
265
+ │ Data analysis: unique_ratio=0.95, no_score_field │
266
+ │ Compression confidence: 0.4 │
267
+ │ Decision: SKIP (confidence < 0.6) │
268
+ └─────────────────────────────────────────────────────────────────┘
269
+
270
+ ▼ (if confident)
271
+ ┌─────────────────────────────────────────────────────────────────┐
272
+ │ LAYER 3: PROFILE-GUIDED COMPRESSION │
273
+ │ │
274
+ │ Profile: search_api │
275
+ │ - critical_fields: [id, status, error] │
276
+ │ - min_items: 25 │
277
+ │ - common_search_terms: [status:error, level:critical] │
278
+ │ │
279
+ │ Compression: 1000 → 30 items (profile-guided, not heuristic) │
280
+ └─────────────────────────────────────────────────────────────────┘
281
+
282
+
283
+ ┌─────────────────────────────────────────────────────────────────┐
284
+ │ LAYER 4: CCR WITH SEMANTIC INJECTION │
285
+ │ │
286
+ │ Cache: Store full 1000 items │
287
+ │ Monitor: Watch for "reaching" patterns in LLM response │
288
+ │ Inject: Auto-retrieve if LLM seems to need more │
289
+ └─────────────────────────────────────────────────────────────────┘
290
+
291
+
292
+ ┌─────────────────────────────────────────────────────────────────┐
293
+ │ FEEDBACK LOOP │
294
+ │ │
295
+ │ Track: Retrieval patterns, query patterns, failure patterns │
296
+ │ Learn: Update tool profiles, adjust confidence thresholds │
297
+ │ Improve: Next compression is smarter │
298
+ └─────────────────────────────────────────────────────────────────┘
299
+ ```
300
+
301
+ ### Action Items
302
+
303
+ - [ ] Implement TaskClassifier with exhaustive/lookup/analytical detection
304
+ - [ ] Add confidence estimation to SmartCrusher
305
+ - [ ] Build ToolCompressionProfile system
306
+ - [ ] Implement semantic injection for CCR
307
+ - [ ] Create profile bootstrap from first 10 compressions per tool
308
+
309
+ ---
310
+
311
+ # Dimension 3: Technical Moat (6 → 10)
312
+
313
+ ## Current Problem (6/10)
314
+
315
+ Individual techniques are not novel:
316
+ - Statistical analysis: Data profiling tools exist
317
+ - BM25/embeddings: Standard IR
318
+ - Caching: Standard pattern
319
+
320
+ **The combination is the innovation, but combinations are easy to copy.**
321
+
322
+ ## The 10/10 Moat: Data Flywheel
323
+
324
+ ### The Insight
325
+
326
+ True moats in infrastructure come from:
327
+ 1. **Network effects** - More users = better product
328
+ 2. **Data moats** - Proprietary data that improves over time
329
+ 3. **Integration depth** - Becomes part of the stack
330
+ 4. **Ecosystem** - Others build on top of you
331
+
332
+ **The killer moat: A compression model trained on real agent data.**
333
+
334
+ ### Phase 1: Aggregate Tool Intelligence (Months 1-6)
335
+
336
+ **Collect anonymized statistics across all users:**
337
+
338
+ ```python
339
+ @dataclass
340
+ class AnonymizedToolStats:
341
+ """Privacy-preserving tool statistics."""
342
+
343
+ tool_signature: str # Hash of tool name + schema
344
+
345
+ # Field patterns (no actual values)
346
+ field_types: dict[str, str] # {"status": "categorical", "count": "numeric"}
347
+ field_distributions: dict # {"status": {"unique_ratio": 0.05}}
348
+
349
+ # Compression patterns
350
+ avg_compression_ratio: float
351
+ avg_retrieval_rate: float
352
+ successful_strategies: list[str]
353
+
354
+ # Query patterns (no actual queries)
355
+ common_query_patterns: list[str] # ["field:*", "status:error"]
356
+ queried_field_frequency: dict # {"status": 0.8, "id": 0.3}
357
+ ```
358
+
359
+ **Build the "Tool Intelligence Database":**
360
+
361
+ ```python
362
+ class ToolIntelligenceDB:
363
+ """Cross-user intelligence about tool outputs."""
364
+
365
+ def get_profile(self, tool_signature: str) -> ToolCompressionProfile:
366
+ """Get compression profile based on aggregate data."""
367
+ stats = self._aggregate_stats(tool_signature)
368
+
369
+ return ToolCompressionProfile(
370
+ critical_fields=stats.get_frequently_queried_fields(),
371
+ min_items=stats.get_safe_compression_target(),
372
+ skip_conditions=stats.get_high_retrieval_scenarios(),
373
+ confidence=stats.sample_size / 1000, # More data = more confidence
374
+ )
375
+ ```
376
+
377
+ **The moat**: "We've seen 10M GitHub API responses. We know exactly what to compress."
378
+
379
+ ### Phase 2: Train Compression Classifier (Months 6-12)
380
+
381
+ **Use aggregate data to train a small, fast model:**
382
+
383
+ ```python
384
+ class CompressionClassifier:
385
+ """Learned compression decision model."""
386
+
387
+ def __init__(self, model_path: str):
388
+ # Small transformer (~50M params) fine-tuned on compression decisions
389
+ self.model = load_model(model_path)
390
+
391
+ def predict(self,
392
+ tool_stats: ToolStats,
393
+ user_query: str,
394
+ sample_items: list[dict]) -> CompressionDecision:
395
+ """Predict optimal compression strategy."""
396
+
397
+ # Encode input
398
+ features = self._encode_features(tool_stats, user_query, sample_items)
399
+
400
+ # Predict
401
+ output = self.model(features)
402
+
403
+ return CompressionDecision(
404
+ should_compress=output.compress_probability > 0.7,
405
+ strategy=output.best_strategy,
406
+ target_items=output.target_items,
407
+ preserve_fields=output.preserve_fields,
408
+ confidence=output.confidence,
409
+ )
410
+ ```
411
+
412
+ **Training data (from aggregate stats):**
413
+
414
+ | Input | Output | Label Source |
415
+ |-------|--------|--------------|
416
+ | Tool stats + query + sample items | Compression decision | Retrieval rate feedback |
417
+ | High unique_ratio + no score field | SKIP | High retrieval rate |
418
+ | Score field + analytical query | TOP_N | Low retrieval rate |
419
+ | Error keywords in query | PRESERVE_ERRORS | Query pattern analysis |
420
+
421
+ **The moat**: Model trained on proprietary data. Competitors start at zero.
422
+
423
+ ### Phase 3: Ecosystem Lock-in (Months 12-24)
424
+
425
+ **Deep integration with agent frameworks:**
426
+
427
+ ```python
428
+ # LangChain official integration
429
+ from langchain_headroom import HeadroomCache
430
+
431
+ llm = ChatOpenAI(cache=HeadroomCache()) # Just works
432
+
433
+ # LlamaIndex official integration
434
+ from llama_index.headroom import HeadroomContextManager
435
+
436
+ index = VectorStoreIndex(context_manager=HeadroomContextManager())
437
+
438
+ # CrewAI official integration
439
+ from crewai_headroom import HeadroomCrew
440
+
441
+ crew = HeadroomCrew(agents=[...]) # Auto-optimizes all agents
442
+ ```
443
+
444
+ **Build ecosystem on top:**
445
+
446
+ | Component | What It Does | Lock-in |
447
+ |-----------|--------------|---------|
448
+ | Headroom Dashboard | Visualize context usage | Analytics dependency |
449
+ | Headroom MCP | Universal agent optimization | Protocol dependency |
450
+ | Headroom VS Code | IDE integration | Developer workflow |
451
+ | Headroom Profiles | Community tool profiles | Content lock-in |
452
+
453
+ ### The Data Flywheel
454
+
455
+ ```
456
+ ┌──────────────────────────────────────────────────────────────┐
457
+ │ MORE USERS │
458
+ └──────────────────────────────────────────────────────────────┘
459
+
460
+
461
+ ┌──────────────────────────────────────────────────────────────┐
462
+ │ MORE TOOL OUTPUT DATA │
463
+ │ (anonymized stats, retrieval patterns, query patterns) │
464
+ └──────────────────────────────────────────────────────────────┘
465
+
466
+
467
+ ┌──────────────────────────────────────────────────────────────┐
468
+ │ BETTER COMPRESSION MODEL │
469
+ │ (trained on more data, more tool types, more scenarios) │
470
+ └──────────────────────────────────────────────────────────────┘
471
+
472
+
473
+ ┌──────────────────────────────────────────────────────────────┐
474
+ │ BETTER COMPRESSION QUALITY │
475
+ │ (higher accuracy, fewer retrievals, more savings) │
476
+ └──────────────────────────────────────────────────────────────┘
477
+
478
+
479
+ ┌──────────────────────────────────────────────────────────────┐
480
+ │ MORE USERS │
481
+ │ (word of mouth, better benchmarks, lower churn) │
482
+ └──────────────────────────────────────────────────────────────┘
483
+
484
+ └──────────────► (cycle repeats)
485
+ ```
486
+
487
+ **This is the moat.** Every user makes the product better for every other user. Competitors can't replicate without the data.
488
+
489
+ ### Action Items
490
+
491
+ - [ ] Design privacy-preserving telemetry system
492
+ - [ ] Build Tool Intelligence aggregation pipeline
493
+ - [ ] Define compression classifier architecture
494
+ - [ ] Create training data collection from feedback loop
495
+ - [ ] Plan framework partnership outreach
496
+
497
+ ---
498
+
499
+ # Dimension 4: Market Timing (9 → 10)
500
+
501
+ ## Current State (9/10)
502
+
503
+ Timing is good - AI agent explosion is happening. But are we POSITIONED to capture it?
504
+
505
+ ## The 10/10 Positioning
506
+
507
+ ### Strategy 1: Be First in the "Context Optimization" Category
508
+
509
+ **Create the category:**
510
+ - "Context Optimization" as a must-have layer
511
+ - Every serious AI agent needs it
512
+ - Headroom = the default choice
513
+
514
+ **Content to publish:**
515
+ - "The Context Crisis: Why AI Agents Are Hitting Walls"
516
+ - "Context Engineering Best Practices" (become the authority)
517
+ - Benchmark suite for context optimization
518
+
519
+ ### Strategy 2: Partner with Major Frameworks
520
+
521
+ | Framework | Status | Action |
522
+ |-----------|--------|--------|
523
+ | LangChain | Large user base | Official integration PR |
524
+ | LlamaIndex | Growing fast | Partnership discussion |
525
+ | CrewAI | Focused on agents | Perfect fit - reach out |
526
+ | Claude Code | Anthropic's CLI | We're already here! |
527
+ | Cursor | Popular IDE | Plugin opportunity |
528
+
529
+ ### Strategy 3: Launch with Major Players
530
+
531
+ **Target announcements:**
532
+ - "Headroom powers context optimization for [Major Agent Company]"
533
+ - "LangChain officially recommends Headroom for production agents"
534
+ - "Anthropic's Claude Code uses Headroom for context management"
535
+
536
+ ### Strategy 4: Open Source Dominance
537
+
538
+ **Make Headroom the "nginx of context optimization":**
539
+ - Core is free and open source
540
+ - Enterprise features are paid
541
+ - Community contributions
542
+ - Apache 2.0 license
543
+
544
+ **The playbook:**
545
+ 1. Be the obvious open source choice
546
+ 2. Capture developer mindshare
547
+ 3. Enterprise upsells for advanced features
548
+
549
+ ### Action Items
550
+
551
+ - [ ] Create "Context Optimization" category content
552
+ - [ ] Reach out to LangChain for official integration
553
+ - [ ] Publish benchmark suite
554
+ - [ ] Plan launch announcements
555
+
556
+ ---
557
+
558
+ # The 10/10 Roadmap
559
+
560
+ ## Phase 1: Foundation (Now - Month 3)
561
+
562
+ | Goal | Action | Metric |
563
+ |------|--------|--------|
564
+ | Solution Fit 8/10 | Implement task classification + confidence gating | Retrieval rate < 10% |
565
+ | Technical Moat 7/10 | Launch telemetry + Tool Intelligence DB | 1M+ data points |
566
+ | Market Timing 10/10 | LangChain integration + category content | Integration shipped |
567
+
568
+ **Key deliverables:**
569
+ - TaskClassifier with exhaustive/lookup/analytical detection
570
+ - Confidence-gated compression
571
+ - Privacy-preserving telemetry
572
+ - LangChain official integration
573
+ - "Context Optimization" blog series
574
+
575
+ ## Phase 2: Data Flywheel (Month 3 - Month 9)
576
+
577
+ | Goal | Action | Metric |
578
+ |------|--------|--------|
579
+ | Solution Fit 9/10 | Learned compression profiles per tool | 100+ tool profiles |
580
+ | Technical Moat 8/10 | Train v1 compression classifier | 5% better than heuristics |
581
+ | Problem Validity 10/10 | Publish "impossible without Headroom" demos | 3 viral demos |
582
+
583
+ **Key deliverables:**
584
+ - ToolCompressionProfile system with cross-user learning
585
+ - Compression classifier v1 (small transformer)
586
+ - Semantic injection for CCR
587
+ - CrewAI + LlamaIndex integrations
588
+ - Demo: "This agent workflow is impossible without Headroom"
589
+
590
+ ## Phase 3: Moat (Month 9 - Month 18)
591
+
592
+ | Goal | Action | Metric |
593
+ |------|--------|--------|
594
+ | Solution Fit 10/10 | Compression classifier v2 | Retrieval rate < 5% |
595
+ | Technical Moat 10/10 | Data flywheel operational | 100M+ data points |
596
+ | Overall 10/10 | Category leader | #1 in benchmarks |
597
+
598
+ **Key deliverables:**
599
+ - Compression classifier v2 (trained on 100M+ samples)
600
+ - Headroom Dashboard (analytics product)
601
+ - Enterprise partnerships
602
+ - Community tool profile contributions
603
+ - Category ownership: "Context Optimization"
604
+
605
+ ---
606
+
607
+ # The 10/10 Vision
608
+
609
+ ## From Today's Headroom
610
+
611
+ ```
612
+ "A smart compression layer that saves you tokens"
613
+ ```
614
+
615
+ ## To Tomorrow's Headroom
616
+
617
+ ```
618
+ "The Context Intelligence Platform for AI Applications"
619
+
620
+ We don't just compress - we UNDERSTAND context.
621
+ - What's in your context?
622
+ - What does your agent need?
623
+ - What's the optimal representation?
624
+ - How do we learn and improve?
625
+
626
+ Every agent needs context intelligence.
627
+ Headroom is context intelligence.
628
+ ```
629
+
630
+ ## The End State
631
+
632
+ | Dimension | Score | How |
633
+ |-----------|-------|-----|
634
+ | Problem validity | 10/10 | "Enables capabilities impossible without us" |
635
+ | Solution fit | 10/10 | Task-aware + learned profiles + seamless CCR |
636
+ | Technical moat | 10/10 | Compression model trained on 100M+ samples |
637
+ | Market timing | 10/10 | Category leader, framework default |
638
+ | **Overall** | **10/10** | **The context layer for AI** |
639
+
640
+ ---
641
+
642
+ # Summary: The Three Big Moves
643
+
644
+ ## Move 1: From Cost Savings to Capability Enablement
645
+
646
+ **Before**: "Save 50-90% on tokens"
647
+ **After**: "Enable agent capabilities that are impossible without context optimization"
648
+
649
+ ## Move 2: From Heuristics to Learned Intelligence
650
+
651
+ **Before**: Statistical heuristics that work 70% of the time
652
+ **After**: Task-aware, confidence-gated, profile-guided compression that learns from every interaction
653
+
654
+ ## Move 3: From Tool to Platform
655
+
656
+ **Before**: A compression library you can use
657
+ **After**: The context intelligence layer that every serious AI application needs
658
+
659
+ ---
660
+
661
+ **The bottom line**: 10/10 isn't about perfecting what we have. It's about building a data flywheel that makes the product better with every user, creating capabilities that are impossible without us, and owning the "Context Intelligence" category before anyone else does.
examples/anthropic_example.py CHANGED
@@ -80,7 +80,9 @@ def example_optimize_mode():
80
  {
81
  "type": "tool_result",
82
  "tool_use_id": "call_1",
83
- "content": '{"results": [' + ",".join([f'{{"id": {i}}}' for i in range(50)]) + "]}",
 
 
84
  }
85
  ],
86
  },
@@ -125,7 +127,9 @@ def example_simulate_mode():
125
  {
126
  "type": "tool_result",
127
  "tool_use_id": "call_1",
128
- "content": '{"results": [' + ",".join([f'{{"id": {i}}}' for i in range(100)]) + "]}",
 
 
129
  }
130
  ],
131
  },
 
80
  {
81
  "type": "tool_result",
82
  "tool_use_id": "call_1",
83
+ "content": '{"results": ['
84
+ + ",".join([f'{{"id": {i}}}' for i in range(50)])
85
+ + "]}",
86
  }
87
  ],
88
  },
 
127
  {
128
  "type": "tool_result",
129
  "tool_use_id": "call_1",
130
+ "content": '{"results": ['
131
+ + ",".join([f'{{"id": {i}}}' for i in range(100)])
132
+ + "]}",
133
  }
134
  ],
135
  },
examples/langchain_before_after.py CHANGED
@@ -21,18 +21,19 @@ import os
21
  import tempfile
22
  import time
23
  from dataclasses import dataclass
24
- from datetime import datetime
25
 
26
  # Check dependencies
27
  try:
28
  from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
 
29
  LANGCHAIN_AVAILABLE = True
30
  except ImportError:
31
  LANGCHAIN_AVAILABLE = False
32
  print("LangChain not installed. Install with: pip install langchain-core")
33
 
34
  try:
35
- from langchain_openai import ChatOpenAI
 
36
  OPENAI_AVAILABLE = True
37
  except ImportError:
38
  OPENAI_AVAILABLE = False
@@ -40,12 +41,13 @@ except ImportError:
40
 
41
  # Import Headroom
42
  try:
43
- from headroom import (
44
  HeadroomClient,
45
  HeadroomConfig,
46
  HeadroomMode,
47
  OpenAIProvider,
48
  )
 
49
  HEADROOM_AVAILABLE = True
50
  except ImportError:
51
  HEADROOM_AVAILABLE = False
@@ -55,6 +57,7 @@ except ImportError:
55
  @dataclass
56
  class ComparisonResult:
57
  """Result of before/after comparison."""
 
58
  scenario: str
59
  tokens_before: int
60
  tokens_after: int
@@ -82,18 +85,18 @@ def print_comparison(result: ComparisonResult) -> None:
82
  print(f"\n{'=' * 60}")
83
  print(f"Scenario: {result.scenario}")
84
  print(f"{'=' * 60}")
85
- print(f"\n[Token Comparison]")
86
  print(f" Before: {result.tokens_before:,} tokens")
87
  print(f" After: {result.tokens_after:,} tokens")
88
  print(f" Saved: {result.tokens_saved:,} tokens ({result.savings_percent:.1f}%)")
89
 
90
- print(f"\n[Cost Impact] (GPT-4o pricing)")
91
  print(f" Before: ${result.cost_before_usd:.4f}")
92
  print(f" After: ${result.cost_after_usd:.4f}")
93
  print(f" Saved: ${result.cost_saved_usd:.4f}")
94
 
95
  if result.latency_before_ms and result.latency_after_ms:
96
- print(f"\n[Latency]")
97
  print(f" Before: {result.latency_before_ms:.0f}ms")
98
  print(f" After: {result.latency_after_ms:.0f}ms")
99
 
@@ -122,11 +125,13 @@ def langchain_to_openai_messages(messages: list) -> list[dict]:
122
  ]
123
  openai_messages.append(msg_dict)
124
  elif isinstance(msg, ToolMessage):
125
- openai_messages.append({
126
- "role": "tool",
127
- "tool_call_id": msg.tool_call_id,
128
- "content": msg.content,
129
- })
 
 
130
  return openai_messages
131
 
132
 
@@ -134,6 +139,7 @@ def langchain_to_openai_messages(messages: list) -> list[dict]:
134
  # SCENARIO 1: Agentic Workflow with Large Tool Outputs
135
  # ============================================================================
136
 
 
137
  def scenario_agentic_workflow() -> ComparisonResult:
138
  """
139
  Scenario: AI agent that searches a database and processes results.
@@ -158,20 +164,24 @@ def scenario_agentic_workflow() -> ComparisonResult:
158
  "metadata": {
159
  "preferences": {"theme": "dark", "notifications": True},
160
  "tags": ["premium", "verified"] if i % 5 == 0 else [],
161
- }
162
  }
163
  for i in range(100)
164
  ]
165
 
166
  # The conversation in LangChain format
167
  lc_messages = [
168
- SystemMessage(content="""You are a helpful database assistant.
 
169
  When searching for users, analyze the results and provide a summary.
170
- Focus on active users in the Engineering department."""),
 
171
  HumanMessage(content="Find users in the Engineering department"),
172
  AIMessage(
173
  content="I'll search the database for Engineering users.",
174
- tool_calls=[{"id": "call_1", "name": "search_users", "args": {"department": "Engineering"}}],
 
 
175
  ),
176
  ToolMessage(
177
  content=json.dumps(search_results), # 100 records!
@@ -207,13 +217,13 @@ def scenario_agentic_workflow() -> ComparisonResult:
207
  tokens_saved = plan.tokens_saved
208
  savings_percent = (tokens_saved / tokens_before * 100) if tokens_before > 0 else 0
209
 
210
- print(f"\n[Before Optimization]")
211
- print(f" - System prompt + conversation")
212
  print(f" - Tool output: 100 user records ({len(json.dumps(search_results))} chars)")
213
 
214
- print(f"\n[After Optimization]")
215
- print(f" - SmartCrusher kept: first 3, last 2, + relevance matches")
216
- print(f" - Estimated ~15 items preserved (Engineering dept matches)")
217
  print(f" - Transforms: {plan.transforms}")
218
 
219
  client.close()
@@ -236,6 +246,7 @@ def scenario_agentic_workflow() -> ComparisonResult:
236
  # SCENARIO 2: Long Conversation with Context Window Pressure
237
  # ============================================================================
238
 
 
239
  def scenario_long_conversation() -> ComparisonResult:
240
  """
241
  Scenario: Multi-turn conversation approaching context window limit.
@@ -249,7 +260,8 @@ def scenario_long_conversation() -> ComparisonResult:
249
 
250
  # Simulate 50-turn conversation in LangChain format
251
  lc_messages = [
252
- SystemMessage(content="""You are a customer support agent for TechCorp.
 
253
  You have access to customer data and can help with:
254
  - Account issues
255
  - Billing questions
@@ -258,7 +270,8 @@ def scenario_long_conversation() -> ComparisonResult:
258
 
259
  Current date: 2024-12-15
260
  Agent ID: support-agent-42
261
- """),
 
262
  ]
263
 
264
  # Add 50 turns of conversation
@@ -273,10 +286,12 @@ def scenario_long_conversation() -> ComparisonResult:
273
  for i in range(50):
274
  topic = topics[i % len(topics)]
275
  lc_messages.append(HumanMessage(content=f"Turn {i}: {topic}"))
276
- lc_messages.append(AIMessage(
277
- content=f"Response to turn {i}: Thank you for reaching out about '{topic}'. "
278
- f"I can help you with that. Here's what I found... " * 3
279
- ))
 
 
280
 
281
  # Convert to OpenAI format
282
  messages = langchain_to_openai_messages(lc_messages)
@@ -306,13 +321,13 @@ def scenario_long_conversation() -> ComparisonResult:
306
  tokens_saved = plan.tokens_saved
307
  savings_percent = (tokens_saved / tokens_before * 100) if tokens_before > 0 else 0
308
 
309
- print(f"\n[Before Optimization]")
310
- print(f" - 50-turn conversation")
311
  print(f" - ~{tokens_before:,} tokens total")
312
 
313
- print(f"\n[After Optimization]")
314
- print(f" - RollingWindow kept system + last N turns")
315
- print(f" - CacheAligner moved date to dynamic tail")
316
  print(f" - Transforms: {plan.transforms}")
317
 
318
  client.close()
@@ -335,6 +350,7 @@ def scenario_long_conversation() -> ComparisonResult:
335
  # SCENARIO 3: RAG with Retrieved Documents
336
  # ============================================================================
337
 
 
338
  def scenario_rag_pipeline() -> ComparisonResult:
339
  """
340
  Scenario: RAG pipeline that retrieves multiple documents.
@@ -358,23 +374,24 @@ def scenario_rag_pipeline() -> ComparisonResult:
358
  "author": f"Author {i}",
359
  "date": "2024-01-15",
360
  "category": "Technical",
361
- }
362
  }
363
  chunks.append(chunk)
364
 
365
- context = "\n\n".join([
366
- f"[Source: {c['source']}, Page {c['page']}]\n{c['content']}"
367
- for c in chunks
368
- ])
369
 
370
  # LangChain format
371
  lc_messages = [
372
  SystemMessage(content="You are a helpful assistant. Answer based on the provided context."),
373
- HumanMessage(content=f"""Based on the following retrieved documents:
 
374
 
375
  {context}
376
 
377
- Question: What are the key technical requirements?"""),
 
378
  ]
379
 
380
  # Convert to OpenAI format
@@ -405,12 +422,12 @@ Question: What are the key technical requirements?"""),
405
  tokens_saved = plan.tokens_saved
406
  savings_percent = (tokens_saved / tokens_before * 100) if tokens_before > 0 else 0
407
 
408
- print(f"\n[Before Optimization]")
409
- print(f" - 10 retrieved document chunks")
410
  print(f" - ~{tokens_before:,} tokens total")
411
 
412
- print(f"\n[After Optimization]")
413
- print(f" - CacheAligner normalized whitespace")
414
  print(f" - Transforms: {plan.transforms}")
415
 
416
  client.close()
@@ -433,6 +450,7 @@ Question: What are the key technical requirements?"""),
433
  # SCENARIO 4: Real API Comparison (if API key available)
434
  # ============================================================================
435
 
 
436
  def scenario_live_api() -> ComparisonResult | None:
437
  """
438
  Scenario: Live API comparison with actual timing.
@@ -497,7 +515,7 @@ def scenario_live_api() -> ComparisonResult | None:
497
  print(f"\n[Latency] {latency_before:.0f}ms -> {latency_after:.0f}ms")
498
 
499
  # Get metrics
500
- summary = headroom_client.get_summary()
501
 
502
  headroom_client.close()
503
 
@@ -524,6 +542,7 @@ def scenario_live_api() -> ComparisonResult | None:
524
  # MAIN: Run All Scenarios
525
  # ============================================================================
526
 
 
527
  def main():
528
  """Run all comparison scenarios."""
529
  print("\n" + "=" * 70)
@@ -585,7 +604,7 @@ def main():
585
  print(f" Total tokens saved: {total_saved:,}")
586
  print(f" Average savings: {avg_savings:.1f}%")
587
  print(f" Total cost saved: ${total_cost_saved:.4f}")
588
- print(f"\n[Projection] At scale (1M requests/month):")
589
  print(f" Estimated monthly savings: ${total_cost_saved * 1_000_000 / len(results):,.2f}")
590
 
591
 
 
21
  import tempfile
22
  import time
23
  from dataclasses import dataclass
 
24
 
25
  # Check dependencies
26
  try:
27
  from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
28
+
29
  LANGCHAIN_AVAILABLE = True
30
  except ImportError:
31
  LANGCHAIN_AVAILABLE = False
32
  print("LangChain not installed. Install with: pip install langchain-core")
33
 
34
  try:
35
+ from langchain_openai import ChatOpenAI # noqa: F401
36
+
37
  OPENAI_AVAILABLE = True
38
  except ImportError:
39
  OPENAI_AVAILABLE = False
 
41
 
42
  # Import Headroom
43
  try:
44
+ from headroom import ( # noqa: F401
45
  HeadroomClient,
46
  HeadroomConfig,
47
  HeadroomMode,
48
  OpenAIProvider,
49
  )
50
+
51
  HEADROOM_AVAILABLE = True
52
  except ImportError:
53
  HEADROOM_AVAILABLE = False
 
57
  @dataclass
58
  class ComparisonResult:
59
  """Result of before/after comparison."""
60
+
61
  scenario: str
62
  tokens_before: int
63
  tokens_after: int
 
85
  print(f"\n{'=' * 60}")
86
  print(f"Scenario: {result.scenario}")
87
  print(f"{'=' * 60}")
88
+ print("\n[Token Comparison]")
89
  print(f" Before: {result.tokens_before:,} tokens")
90
  print(f" After: {result.tokens_after:,} tokens")
91
  print(f" Saved: {result.tokens_saved:,} tokens ({result.savings_percent:.1f}%)")
92
 
93
+ print("\n[Cost Impact] (GPT-4o pricing)")
94
  print(f" Before: ${result.cost_before_usd:.4f}")
95
  print(f" After: ${result.cost_after_usd:.4f}")
96
  print(f" Saved: ${result.cost_saved_usd:.4f}")
97
 
98
  if result.latency_before_ms and result.latency_after_ms:
99
+ print("\n[Latency]")
100
  print(f" Before: {result.latency_before_ms:.0f}ms")
101
  print(f" After: {result.latency_after_ms:.0f}ms")
102
 
 
125
  ]
126
  openai_messages.append(msg_dict)
127
  elif isinstance(msg, ToolMessage):
128
+ openai_messages.append(
129
+ {
130
+ "role": "tool",
131
+ "tool_call_id": msg.tool_call_id,
132
+ "content": msg.content,
133
+ }
134
+ )
135
  return openai_messages
136
 
137
 
 
139
  # SCENARIO 1: Agentic Workflow with Large Tool Outputs
140
  # ============================================================================
141
 
142
+
143
  def scenario_agentic_workflow() -> ComparisonResult:
144
  """
145
  Scenario: AI agent that searches a database and processes results.
 
164
  "metadata": {
165
  "preferences": {"theme": "dark", "notifications": True},
166
  "tags": ["premium", "verified"] if i % 5 == 0 else [],
167
+ },
168
  }
169
  for i in range(100)
170
  ]
171
 
172
  # The conversation in LangChain format
173
  lc_messages = [
174
+ SystemMessage(
175
+ content="""You are a helpful database assistant.
176
  When searching for users, analyze the results and provide a summary.
177
+ Focus on active users in the Engineering department."""
178
+ ),
179
  HumanMessage(content="Find users in the Engineering department"),
180
  AIMessage(
181
  content="I'll search the database for Engineering users.",
182
+ tool_calls=[
183
+ {"id": "call_1", "name": "search_users", "args": {"department": "Engineering"}}
184
+ ],
185
  ),
186
  ToolMessage(
187
  content=json.dumps(search_results), # 100 records!
 
217
  tokens_saved = plan.tokens_saved
218
  savings_percent = (tokens_saved / tokens_before * 100) if tokens_before > 0 else 0
219
 
220
+ print("\n[Before Optimization]")
221
+ print(" - System prompt + conversation")
222
  print(f" - Tool output: 100 user records ({len(json.dumps(search_results))} chars)")
223
 
224
+ print("\n[After Optimization]")
225
+ print(" - SmartCrusher kept: first 3, last 2, + relevance matches")
226
+ print(" - Estimated ~15 items preserved (Engineering dept matches)")
227
  print(f" - Transforms: {plan.transforms}")
228
 
229
  client.close()
 
246
  # SCENARIO 2: Long Conversation with Context Window Pressure
247
  # ============================================================================
248
 
249
+
250
  def scenario_long_conversation() -> ComparisonResult:
251
  """
252
  Scenario: Multi-turn conversation approaching context window limit.
 
260
 
261
  # Simulate 50-turn conversation in LangChain format
262
  lc_messages = [
263
+ SystemMessage(
264
+ content="""You are a customer support agent for TechCorp.
265
  You have access to customer data and can help with:
266
  - Account issues
267
  - Billing questions
 
270
 
271
  Current date: 2024-12-15
272
  Agent ID: support-agent-42
273
+ """
274
+ ),
275
  ]
276
 
277
  # Add 50 turns of conversation
 
286
  for i in range(50):
287
  topic = topics[i % len(topics)]
288
  lc_messages.append(HumanMessage(content=f"Turn {i}: {topic}"))
289
+ lc_messages.append(
290
+ AIMessage(
291
+ content=f"Response to turn {i}: Thank you for reaching out about '{topic}'. "
292
+ f"I can help you with that. Here's what I found... " * 3
293
+ )
294
+ )
295
 
296
  # Convert to OpenAI format
297
  messages = langchain_to_openai_messages(lc_messages)
 
321
  tokens_saved = plan.tokens_saved
322
  savings_percent = (tokens_saved / tokens_before * 100) if tokens_before > 0 else 0
323
 
324
+ print("\n[Before Optimization]")
325
+ print(" - 50-turn conversation")
326
  print(f" - ~{tokens_before:,} tokens total")
327
 
328
+ print("\n[After Optimization]")
329
+ print(" - RollingWindow kept system + last N turns")
330
+ print(" - CacheAligner moved date to dynamic tail")
331
  print(f" - Transforms: {plan.transforms}")
332
 
333
  client.close()
 
350
  # SCENARIO 3: RAG with Retrieved Documents
351
  # ============================================================================
352
 
353
+
354
  def scenario_rag_pipeline() -> ComparisonResult:
355
  """
356
  Scenario: RAG pipeline that retrieves multiple documents.
 
374
  "author": f"Author {i}",
375
  "date": "2024-01-15",
376
  "category": "Technical",
377
+ },
378
  }
379
  chunks.append(chunk)
380
 
381
+ context = "\n\n".join(
382
+ [f"[Source: {c['source']}, Page {c['page']}]\n{c['content']}" for c in chunks]
383
+ )
 
384
 
385
  # LangChain format
386
  lc_messages = [
387
  SystemMessage(content="You are a helpful assistant. Answer based on the provided context."),
388
+ HumanMessage(
389
+ content=f"""Based on the following retrieved documents:
390
 
391
  {context}
392
 
393
+ Question: What are the key technical requirements?"""
394
+ ),
395
  ]
396
 
397
  # Convert to OpenAI format
 
422
  tokens_saved = plan.tokens_saved
423
  savings_percent = (tokens_saved / tokens_before * 100) if tokens_before > 0 else 0
424
 
425
+ print("\n[Before Optimization]")
426
+ print(" - 10 retrieved document chunks")
427
  print(f" - ~{tokens_before:,} tokens total")
428
 
429
+ print("\n[After Optimization]")
430
+ print(" - CacheAligner normalized whitespace")
431
  print(f" - Transforms: {plan.transforms}")
432
 
433
  client.close()
 
450
  # SCENARIO 4: Real API Comparison (if API key available)
451
  # ============================================================================
452
 
453
+
454
  def scenario_live_api() -> ComparisonResult | None:
455
  """
456
  Scenario: Live API comparison with actual timing.
 
515
  print(f"\n[Latency] {latency_before:.0f}ms -> {latency_after:.0f}ms")
516
 
517
  # Get metrics
518
+ headroom_client.get_summary()
519
 
520
  headroom_client.close()
521
 
 
542
  # MAIN: Run All Scenarios
543
  # ============================================================================
544
 
545
+
546
  def main():
547
  """Run all comparison scenarios."""
548
  print("\n" + "=" * 70)
 
604
  print(f" Total tokens saved: {total_saved:,}")
605
  print(f" Average savings: {avg_savings:.1f}%")
606
  print(f" Total cost saved: ${total_cost_saved:.4f}")
607
+ print("\n[Projection] At scale (1M requests/month):")
608
  print(f" Estimated monthly savings: ${total_cost_saved * 1_000_000 / len(results):,.2f}")
609
 
610
 
examples/langchain_demo/mock_tools.py CHANGED
@@ -10,7 +10,6 @@ These simulate real-world API responses that benefit from Headroom compression:
10
  import json
11
  import random
12
  from datetime import datetime, timedelta
13
- from typing import Any
14
 
15
 
16
  def generate_user_database_results(query: str, count: int = 100) -> str:
@@ -39,9 +38,11 @@ def generate_user_database_results(query: str, count: int = 100) -> str:
39
  "notifications": random.choice([True, False]),
40
  "timezone": random.choice(["UTC", "PST", "EST", "CST"]),
41
  },
42
- "tags": random.sample(["premium", "verified", "beta", "enterprise"], k=random.randint(0, 3)),
 
 
43
  "login_count": random.randint(1, 500),
44
- }
45
  }
46
  users.append(user)
47
 
@@ -61,8 +62,8 @@ def generate_search_results(query: str, count: int = 50) -> str:
61
  result = {
62
  "id": f"doc_{random.randint(10000, 99999)}",
63
  "title": f"Document {i}: {query.title()} Guide",
64
- "snippet": f"This document covers {query}. " * random.randint(2, 5) +
65
- f"Learn more about implementing {query} in your application...",
66
  "url": f"https://docs.example.com/{query.replace(' ', '-')}/{i}",
67
  "category": random.choice(categories),
68
  "relevance_score": round(random.uniform(0.5, 1.0), 3),
@@ -88,23 +89,27 @@ def generate_log_entries(service: str, count: int = 200) -> str:
88
  entries = []
89
  levels = ["DEBUG", "INFO", "INFO", "INFO", "WARN", "ERROR"] # Most are INFO
90
 
91
- for i in range(count):
92
  timestamp = datetime.now() - timedelta(minutes=random.randint(1, 1440))
93
  level = random.choice(levels)
94
 
95
  if level == "ERROR":
96
- message = random.choice([
97
- f"Connection refused to {service}-db: timeout after 30s",
98
- f"Failed to process request: NullPointerException at line 42",
99
- f"Authentication failed for user: invalid token",
100
- f"Rate limit exceeded: 429 Too Many Requests",
101
- ])
 
 
102
  elif level == "WARN":
103
- message = random.choice([
104
- f"Slow query detected: took 2.5s",
105
- f"Memory usage high: 85% of heap",
106
- f"Retrying request after transient failure",
107
- ])
 
 
108
  else:
109
  message = f"Processing request {random.randint(1000, 9999)} for {service}"
110
 
@@ -120,7 +125,7 @@ def generate_log_entries(service: str, count: int = 200) -> str:
120
  "request_id": f"req_{random.randint(100000, 999999)}",
121
  "user_agent": "Mozilla/5.0" if random.random() > 0.5 else "API-Client/1.0",
122
  "duration_ms": random.randint(1, 5000),
123
- }
124
  }
125
  entries.append(entry)
126
 
@@ -154,7 +159,9 @@ def generate_metrics_data(service: str, count: int = 100) -> str:
154
  "error_rate": random.uniform(5, 15) if is_anomaly else random.uniform(0, 1),
155
  "latency_p50_ms": random.randint(200, 500) if is_anomaly else random.randint(10, 50),
156
  "latency_p99_ms": random.randint(1000, 3000) if is_anomaly else random.randint(50, 200),
157
- "active_connections": random.randint(500, 1000) if is_anomaly else random.randint(50, 150),
 
 
158
  }
159
  metrics.append(metric)
160
 
@@ -184,24 +191,29 @@ def generate_api_response(endpoint: str, count: int = 75) -> str:
184
  "name": f"Owner {random.randint(1, 100)}",
185
  "email": f"owner{random.randint(1, 100)}@example.com",
186
  },
187
- "tags": random.sample(["urgent", "review", "approved", "blocked", "in-progress"], k=random.randint(1, 3)),
 
 
188
  "metadata": {
189
  "source": random.choice(["web", "api", "mobile", "import"]),
190
  "version": f"v{random.randint(1, 5)}.{random.randint(0, 9)}",
191
- }
192
  }
193
  items.append(item)
194
 
195
- return json.dumps({
196
- "data": items,
197
- "pagination": {
198
- "page": 1,
199
- "per_page": count,
200
- "total": count * 10, # Simulate more pages available
201
- "total_pages": 10,
 
 
 
202
  },
203
- "endpoint": endpoint,
204
- }, indent=2)
205
 
206
 
207
  # Tool definitions for LangChain
@@ -217,6 +229,7 @@ TOOL_FUNCTIONS = {
217
  if __name__ == "__main__":
218
  # Test output sizes
219
  import tiktoken
 
220
  enc = tiktoken.get_encoding("cl100k_base")
221
 
222
  print("Tool Output Token Counts:")
 
10
  import json
11
  import random
12
  from datetime import datetime, timedelta
 
13
 
14
 
15
  def generate_user_database_results(query: str, count: int = 100) -> str:
 
38
  "notifications": random.choice([True, False]),
39
  "timezone": random.choice(["UTC", "PST", "EST", "CST"]),
40
  },
41
+ "tags": random.sample(
42
+ ["premium", "verified", "beta", "enterprise"], k=random.randint(0, 3)
43
+ ),
44
  "login_count": random.randint(1, 500),
45
+ },
46
  }
47
  users.append(user)
48
 
 
62
  result = {
63
  "id": f"doc_{random.randint(10000, 99999)}",
64
  "title": f"Document {i}: {query.title()} Guide",
65
+ "snippet": f"This document covers {query}. " * random.randint(2, 5)
66
+ + f"Learn more about implementing {query} in your application...",
67
  "url": f"https://docs.example.com/{query.replace(' ', '-')}/{i}",
68
  "category": random.choice(categories),
69
  "relevance_score": round(random.uniform(0.5, 1.0), 3),
 
89
  entries = []
90
  levels = ["DEBUG", "INFO", "INFO", "INFO", "WARN", "ERROR"] # Most are INFO
91
 
92
+ for _i in range(count):
93
  timestamp = datetime.now() - timedelta(minutes=random.randint(1, 1440))
94
  level = random.choice(levels)
95
 
96
  if level == "ERROR":
97
+ message = random.choice(
98
+ [
99
+ f"Connection refused to {service}-db: timeout after 30s",
100
+ "Failed to process request: NullPointerException at line 42",
101
+ "Authentication failed for user: invalid token",
102
+ "Rate limit exceeded: 429 Too Many Requests",
103
+ ]
104
+ )
105
  elif level == "WARN":
106
+ message = random.choice(
107
+ [
108
+ "Slow query detected: took 2.5s",
109
+ "Memory usage high: 85% of heap",
110
+ "Retrying request after transient failure",
111
+ ]
112
+ )
113
  else:
114
  message = f"Processing request {random.randint(1000, 9999)} for {service}"
115
 
 
125
  "request_id": f"req_{random.randint(100000, 999999)}",
126
  "user_agent": "Mozilla/5.0" if random.random() > 0.5 else "API-Client/1.0",
127
  "duration_ms": random.randint(1, 5000),
128
+ },
129
  }
130
  entries.append(entry)
131
 
 
159
  "error_rate": random.uniform(5, 15) if is_anomaly else random.uniform(0, 1),
160
  "latency_p50_ms": random.randint(200, 500) if is_anomaly else random.randint(10, 50),
161
  "latency_p99_ms": random.randint(1000, 3000) if is_anomaly else random.randint(50, 200),
162
+ "active_connections": random.randint(500, 1000)
163
+ if is_anomaly
164
+ else random.randint(50, 150),
165
  }
166
  metrics.append(metric)
167
 
 
191
  "name": f"Owner {random.randint(1, 100)}",
192
  "email": f"owner{random.randint(1, 100)}@example.com",
193
  },
194
+ "tags": random.sample(
195
+ ["urgent", "review", "approved", "blocked", "in-progress"], k=random.randint(1, 3)
196
+ ),
197
  "metadata": {
198
  "source": random.choice(["web", "api", "mobile", "import"]),
199
  "version": f"v{random.randint(1, 5)}.{random.randint(0, 9)}",
200
+ },
201
  }
202
  items.append(item)
203
 
204
+ return json.dumps(
205
+ {
206
+ "data": items,
207
+ "pagination": {
208
+ "page": 1,
209
+ "per_page": count,
210
+ "total": count * 10, # Simulate more pages available
211
+ "total_pages": 10,
212
+ },
213
+ "endpoint": endpoint,
214
  },
215
+ indent=2,
216
+ )
217
 
218
 
219
  # Tool definitions for LangChain
 
229
  if __name__ == "__main__":
230
  # Test output sizes
231
  import tiktoken
232
+
233
  enc = tiktoken.get_encoding("cl100k_base")
234
 
235
  print("Tool Output Token Counts:")
examples/langchain_demo/run_comparison.py CHANGED
@@ -20,7 +20,6 @@ import os
20
  import sys
21
  import time
22
  from dataclasses import dataclass
23
- from typing import Any
24
 
25
  # Check for required dependencies
26
  try:
@@ -30,9 +29,14 @@ except ImportError:
30
  sys.exit(1)
31
 
32
  try:
33
- from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
34
- from langchain_core.tools import tool
35
- from langchain_openai import ChatOpenAI
 
 
 
 
 
36
  except ImportError:
37
  print("ERROR: LangChain required. Run: pip install langchain langchain-openai langchain-core")
38
  sys.exit(1)
@@ -40,7 +44,6 @@ except ImportError:
40
  # Import our mock tools
41
  from .mock_tools import TOOL_FUNCTIONS
42
 
43
-
44
  # Token counter
45
  ENCODER = tiktoken.get_encoding("cl100k_base")
46
 
@@ -71,6 +74,7 @@ def count_message_tokens(messages: list[dict]) -> int:
71
  @dataclass
72
  class AgentRun:
73
  """Results from a single agent run."""
 
74
  scenario: str
75
  mode: str # "baseline" or "headroom"
76
  total_input_tokens: int
@@ -185,7 +189,7 @@ def run_agent_baseline(scenario: dict, api_key: str) -> AgentRun:
185
  # Count output tokens
186
  output_tokens = count_tokens(response.content) if response.content else 0
187
  if response.tool_calls:
188
- output_tokens += count_tokens(json.dumps([tc for tc in response.tool_calls]))
189
  total_output_tokens += output_tokens
190
 
191
  # Check if done
@@ -212,10 +216,12 @@ def run_agent_baseline(scenario: dict, api_key: str) -> AgentRun:
212
  tool_output_tokens += tool_tokens
213
 
214
  # Add tool result
215
- messages.append(ToolMessage(
216
- content=result,
217
- tool_call_id=tool_call["id"],
218
- ))
 
 
219
 
220
  duration_ms = (time.time() - start_time) * 1000
221
 
@@ -251,7 +257,7 @@ def run_agent_headroom(scenario: dict, api_key: str) -> AgentRun:
251
  # Wrap with Headroom
252
  config = HeadroomConfig(
253
  smart_crusher_threshold=500, # Compress tool outputs > 500 tokens
254
- smart_crusher_max_items=20, # Keep max 20 items
255
  cache_alignment=True,
256
  rolling_window=True,
257
  )
@@ -287,7 +293,7 @@ def run_agent_headroom(scenario: dict, api_key: str) -> AgentRun:
287
  # Count output tokens
288
  output_tokens = count_tokens(response.content) if response.content else 0
289
  if response.tool_calls:
290
- output_tokens += count_tokens(json.dumps([tc for tc in response.tool_calls]))
291
  total_output_tokens += output_tokens
292
 
293
  # Check if done
@@ -311,10 +317,12 @@ def run_agent_headroom(scenario: dict, api_key: str) -> AgentRun:
311
  tool_tokens = count_tokens(result)
312
  tool_output_tokens += tool_tokens
313
 
314
- messages.append(ToolMessage(
315
- content=result,
316
- tool_call_id=tool_call["id"],
317
- ))
 
 
318
 
319
  duration_ms = (time.time() - start_time) * 1000
320
 
@@ -337,41 +345,59 @@ def run_agent_headroom(scenario: dict, api_key: str) -> AgentRun:
337
  def print_comparison(baseline: AgentRun, headroom: AgentRun):
338
  """Print comparison between baseline and headroom runs."""
339
 
340
- print(f"\n{'='*70}")
341
  print(f"SCENARIO: {baseline.scenario}")
342
- print(f"{'='*70}")
343
 
344
  # Token comparison
345
  input_saved = baseline.total_input_tokens - headroom.total_input_tokens
346
- input_pct = (input_saved / baseline.total_input_tokens * 100) if baseline.total_input_tokens > 0 else 0
 
 
347
 
348
  print(f"\n{'METRIC':<30} {'BASELINE':>15} {'HEADROOM':>15} {'SAVINGS':>15}")
349
  print("-" * 75)
350
- print(f"{'Input Tokens':<30} {baseline.total_input_tokens:>15,} {headroom.total_input_tokens:>15,} {input_saved:>14,} ({input_pct:.1f}%)")
351
- print(f"{'Output Tokens':<30} {baseline.total_output_tokens:>15,} {headroom.total_output_tokens:>15,} {'N/A':>15}")
352
- print(f"{'Tool Output Tokens':<30} {baseline.tool_output_tokens:>15,} {headroom.tool_output_tokens:>15,} {'(raw)':>15}")
 
 
 
 
 
 
353
  print(f"{'Tool Calls':<30} {baseline.tool_calls:>15} {headroom.tool_calls:>15} {'':>15}")
354
  print(f"{'Messages':<30} {baseline.messages_count:>15} {headroom.messages_count:>15} {'':>15}")
355
- print(f"{'Duration (ms)':<30} {baseline.duration_ms:>15.0f} {headroom.duration_ms:>15.0f} {'':>15}")
 
 
356
 
357
  # Cost estimation (gpt-4o-mini pricing)
358
  input_cost_per_1m = 0.15
359
  output_cost_per_1m = 0.60
360
 
361
- baseline_cost = (baseline.total_input_tokens * input_cost_per_1m + baseline.total_output_tokens * output_cost_per_1m) / 1_000_000
362
- headroom_cost = (headroom.total_input_tokens * input_cost_per_1m + headroom.total_output_tokens * output_cost_per_1m) / 1_000_000
 
 
 
 
 
 
363
  cost_saved = baseline_cost - headroom_cost
364
  cost_pct = (cost_saved / baseline_cost * 100) if baseline_cost > 0 else 0
365
 
366
- print(f"\n{'Estimated Cost (USD)':<30} ${baseline_cost:>14.6f} ${headroom_cost:>14.6f} ${cost_saved:>13.6f} ({cost_pct:.1f}%)")
 
 
367
 
368
 
369
  def main():
370
  """Run the before/after comparison."""
371
 
372
- print("\n" + "="*70)
373
  print("LANGCHAIN AGENT: BEFORE/AFTER HEADROOM COMPARISON")
374
- print("="*70)
375
 
376
  # Check for API key
377
  api_key = os.environ.get("OPENAI_API_KEY")
@@ -431,15 +457,17 @@ def run_simulation():
431
  print(f"\n Total tool output: {total_tool_tokens:,} tokens")
432
  print(f" With 3 iterations, baseline input would be: ~{total_tool_tokens * 2:,} tokens")
433
  print(f" With Headroom (20 items max), estimated: ~{total_tool_tokens // 5:,} tokens")
434
- print(f" Estimated savings: ~{total_tool_tokens * 2 - total_tool_tokens // 5:,} tokens (~80%)")
 
 
435
 
436
 
437
  def print_summary(baseline_runs: list[AgentRun], headroom_runs: list[AgentRun]):
438
  """Print overall summary."""
439
 
440
- print("\n" + "="*70)
441
  print("OVERALL SUMMARY")
442
- print("="*70)
443
 
444
  total_baseline_input = sum(r.total_input_tokens for r in baseline_runs)
445
  total_headroom_input = sum(r.total_input_tokens for r in headroom_runs)
@@ -448,7 +476,9 @@ def print_summary(baseline_runs: list[AgentRun], headroom_runs: list[AgentRun]):
448
 
449
  print(f"\n{'Metric':<30} {'Baseline':>15} {'Headroom':>15} {'Savings':>15}")
450
  print("-" * 75)
451
- print(f"{'Total Input Tokens':<30} {total_baseline_input:>15,} {total_headroom_input:>15,} {total_saved:>14,}")
 
 
452
  print(f"{'Percentage Saved':<30} {'':>15} {'':>15} {pct_saved:>14.1f}%")
453
 
454
  # Cost
@@ -457,11 +487,13 @@ def print_summary(baseline_runs: list[AgentRun], headroom_runs: list[AgentRun]):
457
  headroom_cost = total_headroom_input * input_cost
458
  cost_saved = baseline_cost - headroom_cost
459
 
460
- print(f"\n{'Est. Input Cost (USD)':<30} ${baseline_cost:>14.4f} ${headroom_cost:>14.4f} ${cost_saved:>13.4f}")
 
 
461
 
462
- print("\n" + "="*70)
463
  print("CONCLUSION")
464
- print("="*70)
465
  print(f"""
466
  Headroom reduced input tokens by {pct_saved:.1f}% across all scenarios.
467
 
 
20
  import sys
21
  import time
22
  from dataclasses import dataclass
 
23
 
24
  # Check for required dependencies
25
  try:
 
29
  sys.exit(1)
30
 
31
  try:
32
+ from langchain_core.messages import ( # noqa: F401
33
+ AIMessage,
34
+ HumanMessage,
35
+ SystemMessage,
36
+ ToolMessage,
37
+ )
38
+ from langchain_core.tools import tool # noqa: F401
39
+ from langchain_openai import ChatOpenAI # noqa: F401
40
  except ImportError:
41
  print("ERROR: LangChain required. Run: pip install langchain langchain-openai langchain-core")
42
  sys.exit(1)
 
44
  # Import our mock tools
45
  from .mock_tools import TOOL_FUNCTIONS
46
 
 
47
  # Token counter
48
  ENCODER = tiktoken.get_encoding("cl100k_base")
49
 
 
74
  @dataclass
75
  class AgentRun:
76
  """Results from a single agent run."""
77
+
78
  scenario: str
79
  mode: str # "baseline" or "headroom"
80
  total_input_tokens: int
 
189
  # Count output tokens
190
  output_tokens = count_tokens(response.content) if response.content else 0
191
  if response.tool_calls:
192
+ output_tokens += count_tokens(json.dumps(list(response.tool_calls)))
193
  total_output_tokens += output_tokens
194
 
195
  # Check if done
 
216
  tool_output_tokens += tool_tokens
217
 
218
  # Add tool result
219
+ messages.append(
220
+ ToolMessage(
221
+ content=result,
222
+ tool_call_id=tool_call["id"],
223
+ )
224
+ )
225
 
226
  duration_ms = (time.time() - start_time) * 1000
227
 
 
257
  # Wrap with Headroom
258
  config = HeadroomConfig(
259
  smart_crusher_threshold=500, # Compress tool outputs > 500 tokens
260
+ smart_crusher_max_items=20, # Keep max 20 items
261
  cache_alignment=True,
262
  rolling_window=True,
263
  )
 
293
  # Count output tokens
294
  output_tokens = count_tokens(response.content) if response.content else 0
295
  if response.tool_calls:
296
+ output_tokens += count_tokens(json.dumps(list(response.tool_calls)))
297
  total_output_tokens += output_tokens
298
 
299
  # Check if done
 
317
  tool_tokens = count_tokens(result)
318
  tool_output_tokens += tool_tokens
319
 
320
+ messages.append(
321
+ ToolMessage(
322
+ content=result,
323
+ tool_call_id=tool_call["id"],
324
+ )
325
+ )
326
 
327
  duration_ms = (time.time() - start_time) * 1000
328
 
 
345
  def print_comparison(baseline: AgentRun, headroom: AgentRun):
346
  """Print comparison between baseline and headroom runs."""
347
 
348
+ print(f"\n{'=' * 70}")
349
  print(f"SCENARIO: {baseline.scenario}")
350
+ print(f"{'=' * 70}")
351
 
352
  # Token comparison
353
  input_saved = baseline.total_input_tokens - headroom.total_input_tokens
354
+ input_pct = (
355
+ (input_saved / baseline.total_input_tokens * 100) if baseline.total_input_tokens > 0 else 0
356
+ )
357
 
358
  print(f"\n{'METRIC':<30} {'BASELINE':>15} {'HEADROOM':>15} {'SAVINGS':>15}")
359
  print("-" * 75)
360
+ print(
361
+ f"{'Input Tokens':<30} {baseline.total_input_tokens:>15,} {headroom.total_input_tokens:>15,} {input_saved:>14,} ({input_pct:.1f}%)"
362
+ )
363
+ print(
364
+ f"{'Output Tokens':<30} {baseline.total_output_tokens:>15,} {headroom.total_output_tokens:>15,} {'N/A':>15}"
365
+ )
366
+ print(
367
+ f"{'Tool Output Tokens':<30} {baseline.tool_output_tokens:>15,} {headroom.tool_output_tokens:>15,} {'(raw)':>15}"
368
+ )
369
  print(f"{'Tool Calls':<30} {baseline.tool_calls:>15} {headroom.tool_calls:>15} {'':>15}")
370
  print(f"{'Messages':<30} {baseline.messages_count:>15} {headroom.messages_count:>15} {'':>15}")
371
+ print(
372
+ f"{'Duration (ms)':<30} {baseline.duration_ms:>15.0f} {headroom.duration_ms:>15.0f} {'':>15}"
373
+ )
374
 
375
  # Cost estimation (gpt-4o-mini pricing)
376
  input_cost_per_1m = 0.15
377
  output_cost_per_1m = 0.60
378
 
379
+ baseline_cost = (
380
+ baseline.total_input_tokens * input_cost_per_1m
381
+ + baseline.total_output_tokens * output_cost_per_1m
382
+ ) / 1_000_000
383
+ headroom_cost = (
384
+ headroom.total_input_tokens * input_cost_per_1m
385
+ + headroom.total_output_tokens * output_cost_per_1m
386
+ ) / 1_000_000
387
  cost_saved = baseline_cost - headroom_cost
388
  cost_pct = (cost_saved / baseline_cost * 100) if baseline_cost > 0 else 0
389
 
390
+ print(
391
+ f"\n{'Estimated Cost (USD)':<30} ${baseline_cost:>14.6f} ${headroom_cost:>14.6f} ${cost_saved:>13.6f} ({cost_pct:.1f}%)"
392
+ )
393
 
394
 
395
  def main():
396
  """Run the before/after comparison."""
397
 
398
+ print("\n" + "=" * 70)
399
  print("LANGCHAIN AGENT: BEFORE/AFTER HEADROOM COMPARISON")
400
+ print("=" * 70)
401
 
402
  # Check for API key
403
  api_key = os.environ.get("OPENAI_API_KEY")
 
457
  print(f"\n Total tool output: {total_tool_tokens:,} tokens")
458
  print(f" With 3 iterations, baseline input would be: ~{total_tool_tokens * 2:,} tokens")
459
  print(f" With Headroom (20 items max), estimated: ~{total_tool_tokens // 5:,} tokens")
460
+ print(
461
+ f" Estimated savings: ~{total_tool_tokens * 2 - total_tool_tokens // 5:,} tokens (~80%)"
462
+ )
463
 
464
 
465
  def print_summary(baseline_runs: list[AgentRun], headroom_runs: list[AgentRun]):
466
  """Print overall summary."""
467
 
468
+ print("\n" + "=" * 70)
469
  print("OVERALL SUMMARY")
470
+ print("=" * 70)
471
 
472
  total_baseline_input = sum(r.total_input_tokens for r in baseline_runs)
473
  total_headroom_input = sum(r.total_input_tokens for r in headroom_runs)
 
476
 
477
  print(f"\n{'Metric':<30} {'Baseline':>15} {'Headroom':>15} {'Savings':>15}")
478
  print("-" * 75)
479
+ print(
480
+ f"{'Total Input Tokens':<30} {total_baseline_input:>15,} {total_headroom_input:>15,} {total_saved:>14,}"
481
+ )
482
  print(f"{'Percentage Saved':<30} {'':>15} {'':>15} {pct_saved:>14.1f}%")
483
 
484
  # Cost
 
487
  headroom_cost = total_headroom_input * input_cost
488
  cost_saved = baseline_cost - headroom_cost
489
 
490
+ print(
491
+ f"\n{'Est. Input Cost (USD)':<30} ${baseline_cost:>14.4f} ${headroom_cost:>14.4f} ${cost_saved:>13.4f}"
492
+ )
493
 
494
+ print("\n" + "=" * 70)
495
  print("CONCLUSION")
496
+ print("=" * 70)
497
  print(f"""
498
  Headroom reduced input tokens by {pct_saved:.1f}% across all scenarios.
499
 
examples/langchain_demo/show_compression.py CHANGED
@@ -19,13 +19,11 @@ except ImportError:
19
  print("ERROR: tiktoken required. Run: uv pip install tiktoken")
20
  sys.exit(1)
21
 
22
- from headroom import HeadroomConfig
23
- from headroom.transforms import SmartCrusher
24
  from headroom.providers import OpenAIProvider
 
25
 
26
  from .mock_tools import TOOL_FUNCTIONS
27
 
28
-
29
  ENCODER = tiktoken.get_encoding("cl100k_base")
30
 
31
 
@@ -37,10 +35,10 @@ def count_tokens(text: str) -> int:
37
  def demonstrate_compression(tool_name: str, tool_arg: str, context: str):
38
  """Show before/after compression for a tool output."""
39
 
40
- print(f"\n{'='*70}")
41
  print(f"TOOL: {tool_name}({tool_arg!r})")
42
  print(f"CONTEXT: {context!r}")
43
- print(f"{'='*70}")
44
 
45
  # Generate tool output
46
  raw_output = TOOL_FUNCTIONS[tool_name](tool_arg)
@@ -59,11 +57,11 @@ def demonstrate_compression(tool_name: str, tool_arg: str, context: str):
59
  else:
60
  item_count = "?"
61
 
62
- print(f"\n--- BEFORE COMPRESSION ---")
63
  print(f"Items: {item_count}")
64
  print(f"Tokens: {raw_tokens:,}")
65
  print(f"Chars: {len(raw_output):,}")
66
- print(f"\nFirst 500 chars:")
67
  print(raw_output[:500] + "...")
68
 
69
  # Create SmartCrusher with context
@@ -84,7 +82,19 @@ def demonstrate_compression(tool_name: str, tool_arg: str, context: str):
84
  messages = [
85
  {"role": "system", "content": "You are a helpful assistant."},
86
  {"role": "user", "content": context},
87
- {"role": "assistant", "content": None, "tool_calls": [{"id": "call_1", "function": {"name": tool_name, "arguments": json.dumps({tool_name.split("_")[-1]: tool_arg})}}]},
 
 
 
 
 
 
 
 
 
 
 
 
88
  {"role": "tool", "content": raw_output, "tool_call_id": "call_1"},
89
  ]
90
 
@@ -112,18 +122,18 @@ def demonstrate_compression(tool_name: str, tool_arg: str, context: str):
112
  except json.JSONDecodeError:
113
  compressed_items = "N/A"
114
 
115
- print(f"\n--- AFTER COMPRESSION ---")
116
  print(f"Items: {compressed_items}")
117
  print(f"Tokens: {compressed_tokens:,}")
118
  print(f"Chars: {len(compressed_output):,}")
119
- print(f"\nFirst 500 chars:")
120
  print(compressed_output[:500] + "...")
121
 
122
  # Calculate savings
123
  tokens_saved = raw_tokens - compressed_tokens
124
  pct_saved = (tokens_saved / raw_tokens * 100) if raw_tokens > 0 else 0
125
 
126
- print(f"\n--- SAVINGS ---")
127
  print(f"Tokens saved: {tokens_saved:,} ({pct_saved:.1f}%)")
128
  print(f"Items reduced: {item_count} -> {compressed_items}")
129
 
@@ -139,9 +149,9 @@ def demonstrate_compression(tool_name: str, tool_arg: str, context: str):
139
  def main():
140
  """Run compression demonstrations."""
141
 
142
- print("\n" + "="*70)
143
  print("HEADROOM SMARTCRUSHER: BEFORE/AFTER COMPRESSION")
144
- print("="*70)
145
  print("""
146
  This demonstrates how Headroom's SmartCrusher compresses large tool outputs.
147
 
@@ -156,44 +166,54 @@ Key techniques:
156
  results = []
157
 
158
  # Demo 1: User database search
159
- results.append(demonstrate_compression(
160
- tool_name="search_users",
161
- tool_arg="Engineering users",
162
- context="Find all users in the Engineering department who are currently active",
163
- ))
 
 
164
 
165
  # Demo 2: Log search with errors
166
- results.append(demonstrate_compression(
167
- tool_name="search_logs",
168
- tool_arg="payment-service",
169
- context="Check the payment-service logs for any ERROR entries",
170
- ))
 
 
171
 
172
  # Demo 3: Metrics with anomalies
173
- results.append(demonstrate_compression(
174
- tool_name="get_metrics",
175
- tool_arg="api-gateway",
176
- context="Look for any CPU spikes or high error rates in the api-gateway metrics",
177
- ))
 
 
178
 
179
  # Demo 4: Documentation search
180
- results.append(demonstrate_compression(
181
- tool_name="search_docs",
182
- tool_arg="authentication",
183
- context="Find documentation about authentication troubleshooting",
184
- ))
 
 
185
 
186
  # Demo 5: API data
187
- results.append(demonstrate_compression(
188
- tool_name="fetch_api_data",
189
- tool_arg="orders",
190
- context="Get recent orders with status 'pending'",
191
- ))
 
 
192
 
193
  # Summary
194
- print("\n" + "="*70)
195
  print("SUMMARY: TOKEN SAVINGS ACROSS ALL TOOLS")
196
- print("="*70)
197
 
198
  print(f"\n{'Tool':<20} {'Before':>12} {'After':>12} {'Saved':>12} {'%':>8}")
199
  print("-" * 66)
@@ -202,7 +222,9 @@ Key techniques:
202
  total_after = 0
203
 
204
  for r in results:
205
- print(f"{r['tool']:<20} {r['before_tokens']:>12,} {r['after_tokens']:>12,} {r['saved_tokens']:>12,} {r['saved_pct']:>7.1f}%")
 
 
206
  total_before += r["before_tokens"]
207
  total_after += r["after_tokens"]
208
 
@@ -210,7 +232,9 @@ Key techniques:
210
  total_pct = (total_saved / total_before * 100) if total_before > 0 else 0
211
 
212
  print("-" * 66)
213
- print(f"{'TOTAL':<20} {total_before:>12,} {total_after:>12,} {total_saved:>12,} {total_pct:>7.1f}%")
 
 
214
 
215
  # Cost savings
216
  input_cost_per_1m = 2.50 # gpt-4o pricing
@@ -218,11 +242,13 @@ Key techniques:
218
  cost_after = total_after * input_cost_per_1m / 1_000_000
219
  cost_saved = cost_before - cost_after
220
 
221
- print(f"\n--- COST IMPACT (at gpt-4o $2.50/1M input tokens) ---")
222
  print(f"Before: ${cost_before:.4f}")
223
  print(f"After: ${cost_after:.4f}")
224
  print(f"Saved: ${cost_saved:.4f} per request")
225
- print(f"\nAt 1000 requests/day: ${cost_saved * 1000:.2f}/day = ${cost_saved * 1000 * 30:.2f}/month")
 
 
226
 
227
 
228
  if __name__ == "__main__":
 
19
  print("ERROR: tiktoken required. Run: uv pip install tiktoken")
20
  sys.exit(1)
21
 
 
 
22
  from headroom.providers import OpenAIProvider
23
+ from headroom.transforms import SmartCrusher
24
 
25
  from .mock_tools import TOOL_FUNCTIONS
26
 
 
27
  ENCODER = tiktoken.get_encoding("cl100k_base")
28
 
29
 
 
35
  def demonstrate_compression(tool_name: str, tool_arg: str, context: str):
36
  """Show before/after compression for a tool output."""
37
 
38
+ print(f"\n{'=' * 70}")
39
  print(f"TOOL: {tool_name}({tool_arg!r})")
40
  print(f"CONTEXT: {context!r}")
41
+ print(f"{'=' * 70}")
42
 
43
  # Generate tool output
44
  raw_output = TOOL_FUNCTIONS[tool_name](tool_arg)
 
57
  else:
58
  item_count = "?"
59
 
60
+ print("\n--- BEFORE COMPRESSION ---")
61
  print(f"Items: {item_count}")
62
  print(f"Tokens: {raw_tokens:,}")
63
  print(f"Chars: {len(raw_output):,}")
64
+ print("\nFirst 500 chars:")
65
  print(raw_output[:500] + "...")
66
 
67
  # Create SmartCrusher with context
 
82
  messages = [
83
  {"role": "system", "content": "You are a helpful assistant."},
84
  {"role": "user", "content": context},
85
+ {
86
+ "role": "assistant",
87
+ "content": None,
88
+ "tool_calls": [
89
+ {
90
+ "id": "call_1",
91
+ "function": {
92
+ "name": tool_name,
93
+ "arguments": json.dumps({tool_name.split("_")[-1]: tool_arg}),
94
+ },
95
+ }
96
+ ],
97
+ },
98
  {"role": "tool", "content": raw_output, "tool_call_id": "call_1"},
99
  ]
100
 
 
122
  except json.JSONDecodeError:
123
  compressed_items = "N/A"
124
 
125
+ print("\n--- AFTER COMPRESSION ---")
126
  print(f"Items: {compressed_items}")
127
  print(f"Tokens: {compressed_tokens:,}")
128
  print(f"Chars: {len(compressed_output):,}")
129
+ print("\nFirst 500 chars:")
130
  print(compressed_output[:500] + "...")
131
 
132
  # Calculate savings
133
  tokens_saved = raw_tokens - compressed_tokens
134
  pct_saved = (tokens_saved / raw_tokens * 100) if raw_tokens > 0 else 0
135
 
136
+ print("\n--- SAVINGS ---")
137
  print(f"Tokens saved: {tokens_saved:,} ({pct_saved:.1f}%)")
138
  print(f"Items reduced: {item_count} -> {compressed_items}")
139
 
 
149
  def main():
150
  """Run compression demonstrations."""
151
 
152
+ print("\n" + "=" * 70)
153
  print("HEADROOM SMARTCRUSHER: BEFORE/AFTER COMPRESSION")
154
+ print("=" * 70)
155
  print("""
156
  This demonstrates how Headroom's SmartCrusher compresses large tool outputs.
157
 
 
166
  results = []
167
 
168
  # Demo 1: User database search
169
+ results.append(
170
+ demonstrate_compression(
171
+ tool_name="search_users",
172
+ tool_arg="Engineering users",
173
+ context="Find all users in the Engineering department who are currently active",
174
+ )
175
+ )
176
 
177
  # Demo 2: Log search with errors
178
+ results.append(
179
+ demonstrate_compression(
180
+ tool_name="search_logs",
181
+ tool_arg="payment-service",
182
+ context="Check the payment-service logs for any ERROR entries",
183
+ )
184
+ )
185
 
186
  # Demo 3: Metrics with anomalies
187
+ results.append(
188
+ demonstrate_compression(
189
+ tool_name="get_metrics",
190
+ tool_arg="api-gateway",
191
+ context="Look for any CPU spikes or high error rates in the api-gateway metrics",
192
+ )
193
+ )
194
 
195
  # Demo 4: Documentation search
196
+ results.append(
197
+ demonstrate_compression(
198
+ tool_name="search_docs",
199
+ tool_arg="authentication",
200
+ context="Find documentation about authentication troubleshooting",
201
+ )
202
+ )
203
 
204
  # Demo 5: API data
205
+ results.append(
206
+ demonstrate_compression(
207
+ tool_name="fetch_api_data",
208
+ tool_arg="orders",
209
+ context="Get recent orders with status 'pending'",
210
+ )
211
+ )
212
 
213
  # Summary
214
+ print("\n" + "=" * 70)
215
  print("SUMMARY: TOKEN SAVINGS ACROSS ALL TOOLS")
216
+ print("=" * 70)
217
 
218
  print(f"\n{'Tool':<20} {'Before':>12} {'After':>12} {'Saved':>12} {'%':>8}")
219
  print("-" * 66)
 
222
  total_after = 0
223
 
224
  for r in results:
225
+ print(
226
+ f"{r['tool']:<20} {r['before_tokens']:>12,} {r['after_tokens']:>12,} {r['saved_tokens']:>12,} {r['saved_pct']:>7.1f}%"
227
+ )
228
  total_before += r["before_tokens"]
229
  total_after += r["after_tokens"]
230
 
 
232
  total_pct = (total_saved / total_before * 100) if total_before > 0 else 0
233
 
234
  print("-" * 66)
235
+ print(
236
+ f"{'TOTAL':<20} {total_before:>12,} {total_after:>12,} {total_saved:>12,} {total_pct:>7.1f}%"
237
+ )
238
 
239
  # Cost savings
240
  input_cost_per_1m = 2.50 # gpt-4o pricing
 
242
  cost_after = total_after * input_cost_per_1m / 1_000_000
243
  cost_saved = cost_before - cost_after
244
 
245
+ print("\n--- COST IMPACT (at gpt-4o $2.50/1M input tokens) ---")
246
  print(f"Before: ${cost_before:.4f}")
247
  print(f"After: ${cost_after:.4f}")
248
  print(f"Saved: ${cost_saved:.4f} per request")
249
+ print(
250
+ f"\nAt 1000 requests/day: ${cost_saved * 1000:.2f}/day = ${cost_saved * 1000 * 30:.2f}/month"
251
+ )
252
 
253
 
254
  if __name__ == "__main__":
examples/langchain_demo/verify_errors_kept.py CHANGED
@@ -6,16 +6,16 @@ This is critical - errors should NEVER be dropped during compression.
6
  import json
7
 
8
  from headroom.config import SmartCrusherConfig
9
- from headroom.transforms import SmartCrusher
10
  from headroom.providers import OpenAIProvider
 
11
 
12
  from .mock_tools import generate_log_entries
13
 
14
 
15
  def main():
16
- print("\n" + "="*70)
17
  print("VERIFYING ERROR PRESERVATION IN SMARTCRUSHER")
18
- print("="*70)
19
 
20
  # Generate logs with some ERROR entries
21
  raw_output = generate_log_entries("test-service", count=200)
@@ -43,7 +43,13 @@ def main():
43
  messages = [
44
  {"role": "system", "content": "You are a helpful assistant."},
45
  {"role": "user", "content": "Find ERROR entries in the logs"},
46
- {"role": "assistant", "content": None, "tool_calls": [{"id": "call_1", "function": {"name": "search_logs", "arguments": "{}"}}]},
 
 
 
 
 
 
47
  {"role": "tool", "content": raw_output, "tool_call_id": "call_1"},
48
  ]
49
 
@@ -57,11 +63,12 @@ def main():
57
  except json.JSONDecodeError:
58
  # Try to extract just the JSON object
59
  import re
60
- json_match = re.search(r'(\{.*\})', compressed_output, re.DOTALL)
 
61
  if json_match:
62
  compressed_data = json.loads(json_match.group(1))
63
  else:
64
- print(f"Could not parse compressed output:")
65
  print(compressed_output[:500])
66
  return
67
 
@@ -74,20 +81,22 @@ def main():
74
  print(f" - {err['message'][:60]}...")
75
 
76
  # Verification
77
- print("\n" + "="*70)
78
  if len(compressed_errors) >= len(original_errors):
79
  print("SUCCESS: All ERROR entries were preserved!")
80
  elif len(compressed_errors) > 0:
81
  print(f"PARTIAL: {len(compressed_errors)}/{len(original_errors)} ERROR entries preserved")
82
  else:
83
  print("FAILURE: ERROR entries were dropped!")
84
- print("="*70)
85
 
86
  # Show compression ratio
87
  original_count = len(data["entries"])
88
  compressed_count = len(compressed_data["entries"])
89
  reduction = (original_count - compressed_count) / original_count * 100
90
- print(f"\nCompression: {original_count} → {compressed_count} entries ({reduction:.1f}% reduction)")
 
 
91
  print(f"But kept: {len(compressed_errors)} of {len(original_errors)} ERROR entries")
92
 
93
 
 
6
  import json
7
 
8
  from headroom.config import SmartCrusherConfig
 
9
  from headroom.providers import OpenAIProvider
10
+ from headroom.transforms import SmartCrusher
11
 
12
  from .mock_tools import generate_log_entries
13
 
14
 
15
  def main():
16
+ print("\n" + "=" * 70)
17
  print("VERIFYING ERROR PRESERVATION IN SMARTCRUSHER")
18
+ print("=" * 70)
19
 
20
  # Generate logs with some ERROR entries
21
  raw_output = generate_log_entries("test-service", count=200)
 
43
  messages = [
44
  {"role": "system", "content": "You are a helpful assistant."},
45
  {"role": "user", "content": "Find ERROR entries in the logs"},
46
+ {
47
+ "role": "assistant",
48
+ "content": None,
49
+ "tool_calls": [
50
+ {"id": "call_1", "function": {"name": "search_logs", "arguments": "{}"}}
51
+ ],
52
+ },
53
  {"role": "tool", "content": raw_output, "tool_call_id": "call_1"},
54
  ]
55
 
 
63
  except json.JSONDecodeError:
64
  # Try to extract just the JSON object
65
  import re
66
+
67
+ json_match = re.search(r"(\{.*\})", compressed_output, re.DOTALL)
68
  if json_match:
69
  compressed_data = json.loads(json_match.group(1))
70
  else:
71
+ print("Could not parse compressed output:")
72
  print(compressed_output[:500])
73
  return
74
 
 
81
  print(f" - {err['message'][:60]}...")
82
 
83
  # Verification
84
+ print("\n" + "=" * 70)
85
  if len(compressed_errors) >= len(original_errors):
86
  print("SUCCESS: All ERROR entries were preserved!")
87
  elif len(compressed_errors) > 0:
88
  print(f"PARTIAL: {len(compressed_errors)}/{len(original_errors)} ERROR entries preserved")
89
  else:
90
  print("FAILURE: ERROR entries were dropped!")
91
+ print("=" * 70)
92
 
93
  # Show compression ratio
94
  original_count = len(data["entries"])
95
  compressed_count = len(compressed_data["entries"])
96
  reduction = (original_count - compressed_count) / original_count * 100
97
+ print(
98
+ f"\nCompression: {original_count} → {compressed_count} entries ({reduction:.1f}% reduction)"
99
+ )
100
  print(f"But kept: {len(compressed_errors)} of {len(original_errors)} ERROR entries")
101
 
102
 
examples/mcp_demo/mock_mcp_servers.py CHANGED
@@ -22,41 +22,50 @@ def generate_slack_search_results(query: str, count: int = 150) -> str:
22
  # 15% chance of error-related message
23
  is_error = random.random() < 0.15
24
  if is_error:
25
- text = random.choice([
26
- "ERROR: Database connection pool exhausted at 3:45am",
27
- "CRITICAL: Memory usage at 95% on prod-api-01",
28
- "Exception in PaymentService.processTransaction()",
29
- "FAILED: Deploy pipeline broke - rolling back",
30
- "ALERT: Latency spike detected on /api/users endpoint",
31
- ])
 
 
32
  else:
33
- text = random.choice([
34
- f"Reviewed the PR for {query}, looks good to merge",
35
- f"Updated the docs with new {query} endpoints",
36
- "Meeting notes from standup attached",
37
- "Can someone review my changes to the auth module?",
38
- "Deployed v2.3.1 to staging environment",
39
- "Thanks for the feedback on the design doc!",
40
- "Working on the feature request from yesterday",
41
- ])
42
-
43
- messages.append({
44
- "id": f"msg_{i}",
45
- "channel": random.choice(channels),
46
- "user": random.choice(users),
47
- "text": text,
48
- "timestamp": (datetime.now() - timedelta(hours=i)).isoformat(),
49
- "reactions": random.randint(0, 15),
50
- "thread_replies": random.randint(0, 10),
51
- "permalink": f"https://slack.com/archives/C123/p{i}",
52
- })
53
-
54
- return json.dumps({
55
- "query": query,
56
- "messages": messages,
57
- "total": count,
58
- "has_more": count > 100,
59
- }, indent=2)
 
 
 
 
 
 
 
60
 
61
 
62
  def generate_database_query_results(query: str, count: int = 200) -> str:
@@ -72,7 +81,9 @@ def generate_database_query_results(query: str, count: int = 200) -> str:
72
  "user_id": f"usr_{random.randint(10000, 99999)}",
73
  "email": f"user{i}@example.com",
74
  "full_name": f"User {i}",
75
- "status": "ERROR: validation_failed" if has_error else random.choice(["active", "inactive", "pending"]),
 
 
76
  "created_at": (datetime.now() - timedelta(days=random.randint(1, 365))).isoformat(),
77
  "last_login": (datetime.now() - timedelta(days=random.randint(0, 30))).isoformat(),
78
  "balance": None if has_null else round(random.uniform(0, 10000), 2),
@@ -81,17 +92,19 @@ def generate_database_query_results(query: str, count: int = 200) -> str:
81
  }
82
  rows.append(row)
83
 
84
- return json.dumps({
85
- "query": query,
86
- "rows": rows,
87
- "count": count,
88
- "execution_time_ms": random.randint(50, 500),
89
- }, indent=2)
 
 
 
90
 
91
 
92
  def generate_log_search_results(service: str, count: int = 300) -> str:
93
  """Simulate log analysis MCP server results."""
94
- levels = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"]
95
  services = [service, f"{service}-worker", f"{service}-scheduler", "auth-service"]
96
 
97
  entries = []
@@ -99,34 +112,40 @@ def generate_log_search_results(service: str, count: int = 300) -> str:
99
  # 20% error rate (ERROR or FATAL)
100
  if random.random() < 0.20:
101
  level = random.choice(["ERROR", "FATAL"])
102
- message = random.choice([
103
- "Connection timeout to primary database",
104
- "Failed to process message from queue",
105
- "Authentication failed: invalid token",
106
- "Out of memory error in request handler",
107
- "Unhandled exception: NullPointerException",
108
- "Circuit breaker open for external-api",
109
- ])
 
 
110
  else:
111
  level = random.choice(["DEBUG", "INFO", "INFO", "INFO", "WARN"])
112
- message = random.choice([
113
- "Request processed successfully",
114
- "Cache hit for user session",
115
- "Starting scheduled job: cleanup",
116
- "Connection pool stats: 10/20 active",
117
- "Metrics exported to datadog",
118
- "Health check passed",
119
- ])
120
-
121
- entries.append({
122
- "timestamp": (datetime.now() - timedelta(minutes=i)).isoformat(),
123
- "level": level,
124
- "service": random.choice(services),
125
- "message": message,
126
- "trace_id": f"trace_{random.randint(100000, 999999)}",
127
- "span_id": f"span_{random.randint(1000, 9999)}",
128
- "host": f"prod-{random.choice(['api', 'worker', 'web'])}-{random.randint(1, 10):02d}",
129
- })
 
 
 
 
130
 
131
  return json.dumps({"entries": entries, "service": service}, indent=2)
132
 
@@ -140,25 +159,36 @@ def generate_github_issues_results(repo: str, count: int = 100) -> str:
140
  for i in range(count):
141
  # 25% bug rate
142
  is_bug = random.random() < 0.25
143
- labels = random.sample(bug_labels, k=random.randint(1, 2)) if is_bug else random.sample(labels_pool, k=random.randint(0, 2))
144
-
145
- issues.append({
146
- "number": i + 1,
147
- "title": f"{'[BUG] ' if is_bug else ''}{random.choice(['Fix auth flow', 'Add dark mode', 'Update docs', 'Improve perf'])}",
148
- "state": random.choice(["open", "open", "closed"]),
149
- "labels": labels,
150
- "author": f"contributor{random.randint(1, 50)}",
151
- "assignee": f"maintainer{random.randint(1, 5)}" if random.random() > 0.3 else None,
152
- "created_at": (datetime.now() - timedelta(days=random.randint(1, 90))).isoformat(),
153
- "updated_at": (datetime.now() - timedelta(days=random.randint(0, 30))).isoformat(),
154
- "comments": random.randint(0, 30),
155
- "body": "Lorem ipsum dolor sit amet..." if random.random() > 0.5 else "",
156
- "milestone": f"v{random.randint(1, 3)}.{random.randint(0, 9)}" if random.random() > 0.7 else None,
157
- })
158
-
159
- return json.dumps({
160
- "repository": repo,
161
- "issues": issues,
162
- "total_count": count,
163
- "open_count": sum(1 for i in issues if i["state"] == "open"),
164
- }, indent=2)
 
 
 
 
 
 
 
 
 
 
 
 
22
  # 15% chance of error-related message
23
  is_error = random.random() < 0.15
24
  if is_error:
25
+ text = random.choice(
26
+ [
27
+ "ERROR: Database connection pool exhausted at 3:45am",
28
+ "CRITICAL: Memory usage at 95% on prod-api-01",
29
+ "Exception in PaymentService.processTransaction()",
30
+ "FAILED: Deploy pipeline broke - rolling back",
31
+ "ALERT: Latency spike detected on /api/users endpoint",
32
+ ]
33
+ )
34
  else:
35
+ text = random.choice(
36
+ [
37
+ f"Reviewed the PR for {query}, looks good to merge",
38
+ f"Updated the docs with new {query} endpoints",
39
+ "Meeting notes from standup attached",
40
+ "Can someone review my changes to the auth module?",
41
+ "Deployed v2.3.1 to staging environment",
42
+ "Thanks for the feedback on the design doc!",
43
+ "Working on the feature request from yesterday",
44
+ ]
45
+ )
46
+
47
+ messages.append(
48
+ {
49
+ "id": f"msg_{i}",
50
+ "channel": random.choice(channels),
51
+ "user": random.choice(users),
52
+ "text": text,
53
+ "timestamp": (datetime.now() - timedelta(hours=i)).isoformat(),
54
+ "reactions": random.randint(0, 15),
55
+ "thread_replies": random.randint(0, 10),
56
+ "permalink": f"https://slack.com/archives/C123/p{i}",
57
+ }
58
+ )
59
+
60
+ return json.dumps(
61
+ {
62
+ "query": query,
63
+ "messages": messages,
64
+ "total": count,
65
+ "has_more": count > 100,
66
+ },
67
+ indent=2,
68
+ )
69
 
70
 
71
  def generate_database_query_results(query: str, count: int = 200) -> str:
 
81
  "user_id": f"usr_{random.randint(10000, 99999)}",
82
  "email": f"user{i}@example.com",
83
  "full_name": f"User {i}",
84
+ "status": "ERROR: validation_failed"
85
+ if has_error
86
+ else random.choice(["active", "inactive", "pending"]),
87
  "created_at": (datetime.now() - timedelta(days=random.randint(1, 365))).isoformat(),
88
  "last_login": (datetime.now() - timedelta(days=random.randint(0, 30))).isoformat(),
89
  "balance": None if has_null else round(random.uniform(0, 10000), 2),
 
92
  }
93
  rows.append(row)
94
 
95
+ return json.dumps(
96
+ {
97
+ "query": query,
98
+ "rows": rows,
99
+ "count": count,
100
+ "execution_time_ms": random.randint(50, 500),
101
+ },
102
+ indent=2,
103
+ )
104
 
105
 
106
  def generate_log_search_results(service: str, count: int = 300) -> str:
107
  """Simulate log analysis MCP server results."""
 
108
  services = [service, f"{service}-worker", f"{service}-scheduler", "auth-service"]
109
 
110
  entries = []
 
112
  # 20% error rate (ERROR or FATAL)
113
  if random.random() < 0.20:
114
  level = random.choice(["ERROR", "FATAL"])
115
+ message = random.choice(
116
+ [
117
+ "Connection timeout to primary database",
118
+ "Failed to process message from queue",
119
+ "Authentication failed: invalid token",
120
+ "Out of memory error in request handler",
121
+ "Unhandled exception: NullPointerException",
122
+ "Circuit breaker open for external-api",
123
+ ]
124
+ )
125
  else:
126
  level = random.choice(["DEBUG", "INFO", "INFO", "INFO", "WARN"])
127
+ message = random.choice(
128
+ [
129
+ "Request processed successfully",
130
+ "Cache hit for user session",
131
+ "Starting scheduled job: cleanup",
132
+ "Connection pool stats: 10/20 active",
133
+ "Metrics exported to datadog",
134
+ "Health check passed",
135
+ ]
136
+ )
137
+
138
+ entries.append(
139
+ {
140
+ "timestamp": (datetime.now() - timedelta(minutes=i)).isoformat(),
141
+ "level": level,
142
+ "service": random.choice(services),
143
+ "message": message,
144
+ "trace_id": f"trace_{random.randint(100000, 999999)}",
145
+ "span_id": f"span_{random.randint(1000, 9999)}",
146
+ "host": f"prod-{random.choice(['api', 'worker', 'web'])}-{random.randint(1, 10):02d}",
147
+ }
148
+ )
149
 
150
  return json.dumps({"entries": entries, "service": service}, indent=2)
151
 
 
159
  for i in range(count):
160
  # 25% bug rate
161
  is_bug = random.random() < 0.25
162
+ labels = (
163
+ random.sample(bug_labels, k=random.randint(1, 2))
164
+ if is_bug
165
+ else random.sample(labels_pool, k=random.randint(0, 2))
166
+ )
167
+
168
+ issues.append(
169
+ {
170
+ "number": i + 1,
171
+ "title": f"{'[BUG] ' if is_bug else ''}{random.choice(['Fix auth flow', 'Add dark mode', 'Update docs', 'Improve perf'])}",
172
+ "state": random.choice(["open", "open", "closed"]),
173
+ "labels": labels,
174
+ "author": f"contributor{random.randint(1, 50)}",
175
+ "assignee": f"maintainer{random.randint(1, 5)}" if random.random() > 0.3 else None,
176
+ "created_at": (datetime.now() - timedelta(days=random.randint(1, 90))).isoformat(),
177
+ "updated_at": (datetime.now() - timedelta(days=random.randint(0, 30))).isoformat(),
178
+ "comments": random.randint(0, 30),
179
+ "body": "Lorem ipsum dolor sit amet..." if random.random() > 0.5 else "",
180
+ "milestone": f"v{random.randint(1, 3)}.{random.randint(0, 9)}"
181
+ if random.random() > 0.7
182
+ else None,
183
+ }
184
+ )
185
+
186
+ return json.dumps(
187
+ {
188
+ "repository": repo,
189
+ "issues": issues,
190
+ "total_count": count,
191
+ "open_count": sum(1 for i in issues if i["state"] == "open"),
192
+ },
193
+ indent=2,
194
+ )
examples/mcp_demo/run_agent_eval.py CHANGED
@@ -24,21 +24,34 @@ from headroom.providers import OpenAIProvider
24
  # Test Data Generators (Deterministic for eval reproducibility)
25
  # ============================================================================
26
 
 
27
  def generate_slack_with_specific_errors(seed: int = 42) -> tuple[str, list[dict]]:
28
  """Generate Slack messages with SPECIFIC errors we'll query for."""
29
  random.seed(seed)
30
 
31
  # These are the "needle" errors we'll ask the agent to find
32
  critical_errors = [
33
- {"id": "msg_17", "channel": "#incidents", "user": "alice",
34
- "text": "CRITICAL: Payment service is DOWN - customers cannot checkout. Error: ConnectionRefused to payment-db-01",
35
- "timestamp": "2025-01-06T03:45:00Z"},
36
- {"id": "msg_42", "channel": "#alerts", "user": "bob",
37
- "text": "ERROR: Auth service returning 500s. Stack trace shows NullPointerException in TokenValidator.java:127",
38
- "timestamp": "2025-01-06T02:30:00Z"},
39
- {"id": "msg_89", "channel": "#engineering", "user": "charlie",
40
- "text": "FAILED: Deploy to prod-us-east failed. Reason: Health check timeout after 300s on api-gateway-03",
41
- "timestamp": "2025-01-05T23:15:00Z"},
 
 
 
 
 
 
 
 
 
 
 
 
42
  ]
43
 
44
  # Generate noise messages
@@ -62,13 +75,15 @@ def generate_slack_with_specific_errors(seed: int = 42) -> tuple[str, list[dict]
62
  messages.append(critical_errors[error_idx])
63
  error_idx += 1
64
  else:
65
- messages.append({
66
- "id": f"msg_{i}",
67
- "channel": random.choice(channels),
68
- "user": random.choice(users),
69
- "text": random.choice(noise_messages),
70
- "timestamp": (datetime.now() - timedelta(hours=i)).isoformat(),
71
- })
 
 
72
 
73
  return json.dumps({"messages": messages, "total": 150}), critical_errors
74
 
@@ -79,17 +94,43 @@ def generate_logs_with_specific_errors(seed: int = 43) -> tuple[str, list[dict]]
79
 
80
  # These are the "needle" errors
81
  critical_logs = [
82
- {"timestamp": "2025-01-06T03:44:58Z", "level": "FATAL", "service": "payment-service",
83
- "message": "Cannot connect to payment-db-01: Connection refused", "trace_id": "trace_payment_001"},
84
- {"timestamp": "2025-01-06T02:29:55Z", "level": "ERROR", "service": "auth-service",
85
- "message": "NullPointerException in TokenValidator.validate() at line 127", "trace_id": "trace_auth_001"},
86
- {"timestamp": "2025-01-05T23:14:30Z", "level": "ERROR", "service": "api-gateway",
87
- "message": "Health check failed: timeout after 300000ms", "trace_id": "trace_gateway_001"},
88
- {"timestamp": "2025-01-06T01:00:00Z", "level": "ERROR", "service": "user-service",
89
- "message": "Database query timeout: SELECT * FROM users WHERE last_login > ?", "trace_id": "trace_user_001"},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  ]
91
 
92
- services = ["api-gateway", "auth-service", "payment-service", "user-service", "notification-service"]
 
 
 
 
 
 
93
  info_messages = [
94
  "Request processed successfully",
95
  "Cache hit for user session",
@@ -105,13 +146,15 @@ def generate_logs_with_specific_errors(seed: int = 43) -> tuple[str, list[dict]]
105
  entries.append(critical_logs[error_idx])
106
  error_idx += 1
107
  else:
108
- entries.append({
109
- "timestamp": (datetime.now() - timedelta(minutes=i)).isoformat(),
110
- "level": random.choice(["DEBUG", "INFO", "INFO", "INFO", "WARN"]),
111
- "service": random.choice(services),
112
- "message": random.choice(info_messages),
113
- "trace_id": f"trace_{random.randint(100000, 999999)}",
114
- })
 
 
115
 
116
  return json.dumps({"entries": entries}), critical_logs
117
 
@@ -122,10 +165,24 @@ def generate_database_with_anomalies(seed: int = 44) -> tuple[str, list[dict]]:
122
 
123
  # Anomalous records we'll ask about
124
  anomalies = [
125
- {"id": 23, "user_id": "usr_99999", "email": "admin@internal.com", "status": "ERROR: account_locked",
126
- "balance": 999999.99, "login_attempts": 47, "last_login": "2025-01-06T04:00:00Z"},
127
- {"id": 156, "user_id": "usr_00001", "email": "test@test.com", "status": "ERROR: validation_failed",
128
- "balance": -500.00, "login_attempts": 0, "last_login": None},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  ]
130
 
131
  rows = []
@@ -135,15 +192,19 @@ def generate_database_with_anomalies(seed: int = 44) -> tuple[str, list[dict]]:
135
  rows.append(anomalies[anomaly_idx])
136
  anomaly_idx += 1
137
  else:
138
- rows.append({
139
- "id": i,
140
- "user_id": f"usr_{random.randint(10000, 99999)}",
141
- "email": f"user{i}@example.com",
142
- "status": random.choice(["active", "active", "active", "inactive", "pending"]),
143
- "balance": round(random.uniform(0, 5000), 2),
144
- "login_attempts": random.randint(0, 5),
145
- "last_login": (datetime.now() - timedelta(days=random.randint(0, 30))).isoformat(),
146
- })
 
 
 
 
147
 
148
  return json.dumps({"rows": rows, "count": 200}), anomalies
149
 
@@ -152,9 +213,11 @@ def generate_database_with_anomalies(seed: int = 44) -> tuple[str, list[dict]]:
152
  # Eval Test Cases
153
  # ============================================================================
154
 
 
155
  @dataclass
156
  class EvalCase:
157
  """A single evaluation case."""
 
158
  name: str
159
  tool_name: str
160
  tool_output: str
@@ -192,7 +255,13 @@ def create_eval_cases() -> list[EvalCase]:
192
  tool_name="mcp__logs__search",
193
  tool_output=logs_output,
194
  user_query="List all ERROR and FATAL log entries with their services and messages.",
195
- expected_findings=["payment-service", "auth-service", "api-gateway", "Connection refused", "NullPointerException"],
 
 
 
 
 
 
196
  critical_data=log_errors,
197
  ),
198
  EvalCase(
@@ -218,6 +287,7 @@ def create_eval_cases() -> list[EvalCase]:
218
  # Agent Simulation
219
  # ============================================================================
220
 
 
221
  def run_agent_with_tool_output(
222
  client: OpenAI,
223
  user_query: str,
@@ -230,11 +300,22 @@ def run_agent_with_tool_output(
230
  Returns: (answer, tokens_used)
231
  """
232
  messages = [
233
- {"role": "system", "content": "You are a helpful assistant analyzing tool outputs. Be specific and cite exact details from the data."},
 
 
 
234
  {"role": "user", "content": user_query},
235
- {"role": "assistant", "content": None, "tool_calls": [
236
- {"id": "call_1", "type": "function", "function": {"name": tool_name, "arguments": "{}"}}
237
- ]},
 
 
 
 
 
 
 
 
238
  {"role": "tool", "content": tool_output, "tool_call_id": "call_1"},
239
  ]
240
 
@@ -269,6 +350,7 @@ def evaluate_answer(answer: str, expected_findings: list[str]) -> tuple[int, int
269
  # Main Eval Runner
270
  # ============================================================================
271
 
 
272
  def main():
273
  # Check for API key
274
  if not os.environ.get("OPENAI_API_KEY"):
@@ -299,7 +381,7 @@ def main():
299
  for case in eval_cases:
300
  print(f"\n{'─' * 70}")
301
  print(f"EVAL: {case.name}")
302
- print(f"Query: \"{case.user_query}\"")
303
  print(f"{'─' * 70}")
304
 
305
  # Measure original tokens
@@ -312,26 +394,32 @@ def main():
312
  user_query=case.user_query,
313
  )
314
 
315
- print(f"\n Tool Output:")
316
  print(f" Original: {original_tokens:,} tokens")
317
  print(f" Compressed: {compression.compressed_tokens:,} tokens")
318
  print(f" Saved: {compression.tokens_saved:,} ({compression.compression_ratio:.1%})")
319
 
320
  # Run agent BEFORE (with original output)
321
- print(f"\n Running agent with ORIGINAL output...")
322
  try:
323
  answer_before, tokens_before = run_agent_with_tool_output(
324
  client, case.user_query, case.tool_name, case.tool_output
325
  )
326
- found_before, total, missing_before = evaluate_answer(answer_before, case.expected_findings)
 
 
327
  except Exception as e:
328
  print(f" ERROR: {e}")
329
  answer_before = ""
330
- found_before, total, missing_before = 0, len(case.expected_findings), case.expected_findings
 
 
 
 
331
  tokens_before = 0
332
 
333
  # Run agent AFTER (with compressed output)
334
- print(f" Running agent with COMPRESSED output...")
335
  try:
336
  answer_after, tokens_after = run_agent_with_tool_output(
337
  client, case.user_query, case.tool_name, compression.compressed_content
@@ -344,7 +432,7 @@ def main():
344
  tokens_after = 0
345
 
346
  # Results
347
- print(f"\n Results:")
348
  print(f" BEFORE: Found {found_before}/{total} expected findings")
349
  if missing_before:
350
  print(f" Missing: {missing_before}")
@@ -353,30 +441,34 @@ def main():
353
  print(f" Missing: {missing_after}")
354
 
355
  # Token usage comparison
356
- print(f"\n API Token Usage:")
357
  print(f" BEFORE: {tokens_before:,} tokens")
358
  print(f" AFTER: {tokens_after:,} tokens")
359
  if tokens_before > 0:
360
- print(f" Saved: {tokens_before - tokens_after:,} ({(tokens_before - tokens_after) / tokens_before:.1%})")
 
 
361
 
362
  # Pass/Fail
363
  passed = found_after >= found_before
364
  status = "PASS" if passed else "FAIL"
365
  print(f"\n Status: {status}")
366
  if not passed:
367
- print(f" Reason: Compressed output lost information")
368
  print(f" Lost findings: {set(missing_after) - set(missing_before)}")
369
 
370
- results.append({
371
- "name": case.name,
372
- "passed": passed,
373
- "found_before": found_before,
374
- "found_after": found_after,
375
- "total": total,
376
- "tokens_before": tokens_before,
377
- "tokens_after": tokens_after,
378
- "compression_ratio": compression.compression_ratio,
379
- })
 
 
380
 
381
  # Summary
382
  print("\n" + "=" * 70)
@@ -387,27 +479,31 @@ def main():
387
  total_cases = len(results)
388
 
389
  print(f"\n Tests Passed: {passed}/{total_cases}")
390
- print(f"\n Detailed Results:")
391
  print(f" {'Test Name':<35} {'Before':<10} {'After':<10} {'Compress':<10} {'Status':<8}")
392
- print(f" {'-'*35} {'-'*10} {'-'*10} {'-'*10} {'-'*8}")
393
 
394
  for r in results:
395
  status = "PASS" if r["passed"] else "FAIL"
396
- print(f" {r['name']:<35} {r['found_before']}/{r['total']:<8} {r['found_after']}/{r['total']:<8} {r['compression_ratio']:.0%}{'':>6} {status:<8}")
 
 
397
 
398
  # Token savings
399
  total_tokens_before = sum(r["tokens_before"] for r in results)
400
  total_tokens_after = sum(r["tokens_after"] for r in results)
401
 
402
- print(f"\n Total API Tokens:")
403
  print(f" Before: {total_tokens_before:,}")
404
  print(f" After: {total_tokens_after:,}")
405
- print(f" Saved: {total_tokens_before - total_tokens_after:,} ({(total_tokens_before - total_tokens_after) / total_tokens_before:.1%})")
 
 
406
 
407
  # Cost estimate
408
  cost_before = total_tokens_before * 0.15 / 1_000_000 # gpt-4o-mini input
409
  cost_after = total_tokens_after * 0.15 / 1_000_000
410
- print(f"\n Cost (gpt-4o-mini):")
411
  print(f" Before: ${cost_before:.4f}")
412
  print(f" After: ${cost_after:.4f}")
413
  print(f" Saved: ${cost_before - cost_after:.4f}")
 
24
  # Test Data Generators (Deterministic for eval reproducibility)
25
  # ============================================================================
26
 
27
+
28
  def generate_slack_with_specific_errors(seed: int = 42) -> tuple[str, list[dict]]:
29
  """Generate Slack messages with SPECIFIC errors we'll query for."""
30
  random.seed(seed)
31
 
32
  # These are the "needle" errors we'll ask the agent to find
33
  critical_errors = [
34
+ {
35
+ "id": "msg_17",
36
+ "channel": "#incidents",
37
+ "user": "alice",
38
+ "text": "CRITICAL: Payment service is DOWN - customers cannot checkout. Error: ConnectionRefused to payment-db-01",
39
+ "timestamp": "2025-01-06T03:45:00Z",
40
+ },
41
+ {
42
+ "id": "msg_42",
43
+ "channel": "#alerts",
44
+ "user": "bob",
45
+ "text": "ERROR: Auth service returning 500s. Stack trace shows NullPointerException in TokenValidator.java:127",
46
+ "timestamp": "2025-01-06T02:30:00Z",
47
+ },
48
+ {
49
+ "id": "msg_89",
50
+ "channel": "#engineering",
51
+ "user": "charlie",
52
+ "text": "FAILED: Deploy to prod-us-east failed. Reason: Health check timeout after 300s on api-gateway-03",
53
+ "timestamp": "2025-01-05T23:15:00Z",
54
+ },
55
  ]
56
 
57
  # Generate noise messages
 
75
  messages.append(critical_errors[error_idx])
76
  error_idx += 1
77
  else:
78
+ messages.append(
79
+ {
80
+ "id": f"msg_{i}",
81
+ "channel": random.choice(channels),
82
+ "user": random.choice(users),
83
+ "text": random.choice(noise_messages),
84
+ "timestamp": (datetime.now() - timedelta(hours=i)).isoformat(),
85
+ }
86
+ )
87
 
88
  return json.dumps({"messages": messages, "total": 150}), critical_errors
89
 
 
94
 
95
  # These are the "needle" errors
96
  critical_logs = [
97
+ {
98
+ "timestamp": "2025-01-06T03:44:58Z",
99
+ "level": "FATAL",
100
+ "service": "payment-service",
101
+ "message": "Cannot connect to payment-db-01: Connection refused",
102
+ "trace_id": "trace_payment_001",
103
+ },
104
+ {
105
+ "timestamp": "2025-01-06T02:29:55Z",
106
+ "level": "ERROR",
107
+ "service": "auth-service",
108
+ "message": "NullPointerException in TokenValidator.validate() at line 127",
109
+ "trace_id": "trace_auth_001",
110
+ },
111
+ {
112
+ "timestamp": "2025-01-05T23:14:30Z",
113
+ "level": "ERROR",
114
+ "service": "api-gateway",
115
+ "message": "Health check failed: timeout after 300000ms",
116
+ "trace_id": "trace_gateway_001",
117
+ },
118
+ {
119
+ "timestamp": "2025-01-06T01:00:00Z",
120
+ "level": "ERROR",
121
+ "service": "user-service",
122
+ "message": "Database query timeout: SELECT * FROM users WHERE last_login > ?",
123
+ "trace_id": "trace_user_001",
124
+ },
125
  ]
126
 
127
+ services = [
128
+ "api-gateway",
129
+ "auth-service",
130
+ "payment-service",
131
+ "user-service",
132
+ "notification-service",
133
+ ]
134
  info_messages = [
135
  "Request processed successfully",
136
  "Cache hit for user session",
 
146
  entries.append(critical_logs[error_idx])
147
  error_idx += 1
148
  else:
149
+ entries.append(
150
+ {
151
+ "timestamp": (datetime.now() - timedelta(minutes=i)).isoformat(),
152
+ "level": random.choice(["DEBUG", "INFO", "INFO", "INFO", "WARN"]),
153
+ "service": random.choice(services),
154
+ "message": random.choice(info_messages),
155
+ "trace_id": f"trace_{random.randint(100000, 999999)}",
156
+ }
157
+ )
158
 
159
  return json.dumps({"entries": entries}), critical_logs
160
 
 
165
 
166
  # Anomalous records we'll ask about
167
  anomalies = [
168
+ {
169
+ "id": 23,
170
+ "user_id": "usr_99999",
171
+ "email": "admin@internal.com",
172
+ "status": "ERROR: account_locked",
173
+ "balance": 999999.99,
174
+ "login_attempts": 47,
175
+ "last_login": "2025-01-06T04:00:00Z",
176
+ },
177
+ {
178
+ "id": 156,
179
+ "user_id": "usr_00001",
180
+ "email": "test@test.com",
181
+ "status": "ERROR: validation_failed",
182
+ "balance": -500.00,
183
+ "login_attempts": 0,
184
+ "last_login": None,
185
+ },
186
  ]
187
 
188
  rows = []
 
192
  rows.append(anomalies[anomaly_idx])
193
  anomaly_idx += 1
194
  else:
195
+ rows.append(
196
+ {
197
+ "id": i,
198
+ "user_id": f"usr_{random.randint(10000, 99999)}",
199
+ "email": f"user{i}@example.com",
200
+ "status": random.choice(["active", "active", "active", "inactive", "pending"]),
201
+ "balance": round(random.uniform(0, 5000), 2),
202
+ "login_attempts": random.randint(0, 5),
203
+ "last_login": (
204
+ datetime.now() - timedelta(days=random.randint(0, 30))
205
+ ).isoformat(),
206
+ }
207
+ )
208
 
209
  return json.dumps({"rows": rows, "count": 200}), anomalies
210
 
 
213
  # Eval Test Cases
214
  # ============================================================================
215
 
216
+
217
  @dataclass
218
  class EvalCase:
219
  """A single evaluation case."""
220
+
221
  name: str
222
  tool_name: str
223
  tool_output: str
 
255
  tool_name="mcp__logs__search",
256
  tool_output=logs_output,
257
  user_query="List all ERROR and FATAL log entries with their services and messages.",
258
+ expected_findings=[
259
+ "payment-service",
260
+ "auth-service",
261
+ "api-gateway",
262
+ "Connection refused",
263
+ "NullPointerException",
264
+ ],
265
  critical_data=log_errors,
266
  ),
267
  EvalCase(
 
287
  # Agent Simulation
288
  # ============================================================================
289
 
290
+
291
  def run_agent_with_tool_output(
292
  client: OpenAI,
293
  user_query: str,
 
300
  Returns: (answer, tokens_used)
301
  """
302
  messages = [
303
+ {
304
+ "role": "system",
305
+ "content": "You are a helpful assistant analyzing tool outputs. Be specific and cite exact details from the data.",
306
+ },
307
  {"role": "user", "content": user_query},
308
+ {
309
+ "role": "assistant",
310
+ "content": None,
311
+ "tool_calls": [
312
+ {
313
+ "id": "call_1",
314
+ "type": "function",
315
+ "function": {"name": tool_name, "arguments": "{}"},
316
+ }
317
+ ],
318
+ },
319
  {"role": "tool", "content": tool_output, "tool_call_id": "call_1"},
320
  ]
321
 
 
350
  # Main Eval Runner
351
  # ============================================================================
352
 
353
+
354
  def main():
355
  # Check for API key
356
  if not os.environ.get("OPENAI_API_KEY"):
 
381
  for case in eval_cases:
382
  print(f"\n{'─' * 70}")
383
  print(f"EVAL: {case.name}")
384
+ print(f'Query: "{case.user_query}"')
385
  print(f"{'─' * 70}")
386
 
387
  # Measure original tokens
 
394
  user_query=case.user_query,
395
  )
396
 
397
+ print("\n Tool Output:")
398
  print(f" Original: {original_tokens:,} tokens")
399
  print(f" Compressed: {compression.compressed_tokens:,} tokens")
400
  print(f" Saved: {compression.tokens_saved:,} ({compression.compression_ratio:.1%})")
401
 
402
  # Run agent BEFORE (with original output)
403
+ print("\n Running agent with ORIGINAL output...")
404
  try:
405
  answer_before, tokens_before = run_agent_with_tool_output(
406
  client, case.user_query, case.tool_name, case.tool_output
407
  )
408
+ found_before, total, missing_before = evaluate_answer(
409
+ answer_before, case.expected_findings
410
+ )
411
  except Exception as e:
412
  print(f" ERROR: {e}")
413
  answer_before = ""
414
+ found_before, total, missing_before = (
415
+ 0,
416
+ len(case.expected_findings),
417
+ case.expected_findings,
418
+ )
419
  tokens_before = 0
420
 
421
  # Run agent AFTER (with compressed output)
422
+ print(" Running agent with COMPRESSED output...")
423
  try:
424
  answer_after, tokens_after = run_agent_with_tool_output(
425
  client, case.user_query, case.tool_name, compression.compressed_content
 
432
  tokens_after = 0
433
 
434
  # Results
435
+ print("\n Results:")
436
  print(f" BEFORE: Found {found_before}/{total} expected findings")
437
  if missing_before:
438
  print(f" Missing: {missing_before}")
 
441
  print(f" Missing: {missing_after}")
442
 
443
  # Token usage comparison
444
+ print("\n API Token Usage:")
445
  print(f" BEFORE: {tokens_before:,} tokens")
446
  print(f" AFTER: {tokens_after:,} tokens")
447
  if tokens_before > 0:
448
+ print(
449
+ f" Saved: {tokens_before - tokens_after:,} ({(tokens_before - tokens_after) / tokens_before:.1%})"
450
+ )
451
 
452
  # Pass/Fail
453
  passed = found_after >= found_before
454
  status = "PASS" if passed else "FAIL"
455
  print(f"\n Status: {status}")
456
  if not passed:
457
+ print(" Reason: Compressed output lost information")
458
  print(f" Lost findings: {set(missing_after) - set(missing_before)}")
459
 
460
+ results.append(
461
+ {
462
+ "name": case.name,
463
+ "passed": passed,
464
+ "found_before": found_before,
465
+ "found_after": found_after,
466
+ "total": total,
467
+ "tokens_before": tokens_before,
468
+ "tokens_after": tokens_after,
469
+ "compression_ratio": compression.compression_ratio,
470
+ }
471
+ )
472
 
473
  # Summary
474
  print("\n" + "=" * 70)
 
479
  total_cases = len(results)
480
 
481
  print(f"\n Tests Passed: {passed}/{total_cases}")
482
+ print("\n Detailed Results:")
483
  print(f" {'Test Name':<35} {'Before':<10} {'After':<10} {'Compress':<10} {'Status':<8}")
484
+ print(f" {'-' * 35} {'-' * 10} {'-' * 10} {'-' * 10} {'-' * 8}")
485
 
486
  for r in results:
487
  status = "PASS" if r["passed"] else "FAIL"
488
+ print(
489
+ f" {r['name']:<35} {r['found_before']}/{r['total']:<8} {r['found_after']}/{r['total']:<8} {r['compression_ratio']:.0%}{'':>6} {status:<8}"
490
+ )
491
 
492
  # Token savings
493
  total_tokens_before = sum(r["tokens_before"] for r in results)
494
  total_tokens_after = sum(r["tokens_after"] for r in results)
495
 
496
+ print("\n Total API Tokens:")
497
  print(f" Before: {total_tokens_before:,}")
498
  print(f" After: {total_tokens_after:,}")
499
+ print(
500
+ f" Saved: {total_tokens_before - total_tokens_after:,} ({(total_tokens_before - total_tokens_after) / total_tokens_before:.1%})"
501
+ )
502
 
503
  # Cost estimate
504
  cost_before = total_tokens_before * 0.15 / 1_000_000 # gpt-4o-mini input
505
  cost_after = total_tokens_after * 0.15 / 1_000_000
506
+ print("\n Cost (gpt-4o-mini):")
507
  print(f" Before: ${cost_before:.4f}")
508
  print(f" After: ${cost_after:.4f}")
509
  print(f" Saved: ${cost_before - cost_after:.4f}")
examples/mcp_demo/show_before_after.py CHANGED
@@ -22,16 +22,16 @@ def main():
22
 
23
  print("\nBEFORE (in your MCP host application):")
24
  print("-" * 40)
25
- before_standalone = '''
26
  # Your MCP host application
27
  result = await mcp_client.call_tool("search_logs", {"service": "api"})
28
  messages.append({"role": "tool", "content": result})
29
- '''
30
  print(before_standalone)
31
 
32
  print("\nAFTER (with Headroom compression):")
33
  print("-" * 40)
34
- after_standalone = '''
35
  from headroom.integrations.mcp import compress_tool_result # ADD THIS
36
 
37
  # Your MCP host application
@@ -42,7 +42,7 @@ compressed = compress_tool_result( # ADD THIS
42
  user_query="find errors in api", # ADD THIS
43
  ) # ADD THIS
44
  messages.append({"role": "tool", "content": compressed})
45
- '''
46
  print(after_standalone)
47
 
48
  # =========================================================================
@@ -54,7 +54,7 @@ messages.append({"role": "tool", "content": compressed})
54
 
55
  print("\nBEFORE:")
56
  print("-" * 40)
57
- before_wrapper = '''
58
  from mcp import Client
59
 
60
  # Create MCP client
@@ -62,12 +62,12 @@ client = Client(transport)
62
 
63
  # Use client normally
64
  result = await client.call_tool("search_logs", {"service": "api"})
65
- '''
66
  print(before_wrapper)
67
 
68
  print("\nAFTER:")
69
  print("-" * 40)
70
- after_wrapper = '''
71
  from mcp import Client
72
  from headroom.integrations.mcp import HeadroomMCPClientWrapper # ADD THIS
73
 
@@ -77,7 +77,7 @@ client = HeadroomMCPClientWrapper(base_client) # WRAP IT (1 line)
77
 
78
  # Use client normally - compression is automatic!
79
  result = await client.call_tool("search_logs", {"service": "api"})
80
- '''
81
  print(after_wrapper)
82
 
83
  # =========================================================================
@@ -89,7 +89,7 @@ result = await client.call_tool("search_logs", {"service": "api"})
89
 
90
  print("\nCode with metrics tracking:")
91
  print("-" * 40)
92
- with_metrics = '''
93
  from headroom.integrations.mcp import compress_tool_result_with_metrics
94
 
95
  result = await mcp_client.call_tool("search_logs", {"service": "api"})
@@ -104,7 +104,7 @@ print(f"Compression: {compression.compression_ratio:.1%}")
104
  print(f"Errors preserved: {compression.errors_preserved}")
105
 
106
  messages.append({"role": "tool", "content": compression.compressed_content})
107
- '''
108
  print(with_metrics)
109
 
110
  # =========================================================================
 
22
 
23
  print("\nBEFORE (in your MCP host application):")
24
  print("-" * 40)
25
+ before_standalone = """
26
  # Your MCP host application
27
  result = await mcp_client.call_tool("search_logs", {"service": "api"})
28
  messages.append({"role": "tool", "content": result})
29
+ """
30
  print(before_standalone)
31
 
32
  print("\nAFTER (with Headroom compression):")
33
  print("-" * 40)
34
+ after_standalone = """
35
  from headroom.integrations.mcp import compress_tool_result # ADD THIS
36
 
37
  # Your MCP host application
 
42
  user_query="find errors in api", # ADD THIS
43
  ) # ADD THIS
44
  messages.append({"role": "tool", "content": compressed})
45
+ """
46
  print(after_standalone)
47
 
48
  # =========================================================================
 
54
 
55
  print("\nBEFORE:")
56
  print("-" * 40)
57
+ before_wrapper = """
58
  from mcp import Client
59
 
60
  # Create MCP client
 
62
 
63
  # Use client normally
64
  result = await client.call_tool("search_logs", {"service": "api"})
65
+ """
66
  print(before_wrapper)
67
 
68
  print("\nAFTER:")
69
  print("-" * 40)
70
+ after_wrapper = """
71
  from mcp import Client
72
  from headroom.integrations.mcp import HeadroomMCPClientWrapper # ADD THIS
73
 
 
77
 
78
  # Use client normally - compression is automatic!
79
  result = await client.call_tool("search_logs", {"service": "api"})
80
+ """
81
  print(after_wrapper)
82
 
83
  # =========================================================================
 
89
 
90
  print("\nCode with metrics tracking:")
91
  print("-" * 40)
92
+ with_metrics = """
93
  from headroom.integrations.mcp import compress_tool_result_with_metrics
94
 
95
  result = await mcp_client.call_tool("search_logs", {"service": "api"})
 
104
  print(f"Errors preserved: {compression.errors_preserved}")
105
 
106
  messages.append({"role": "tool", "content": compression.compressed_content})
107
+ """
108
  print(with_metrics)
109
 
110
  # =========================================================================
examples/mcp_demo/show_compression.py CHANGED
@@ -4,20 +4,18 @@ Run with:
4
  PYTHONPATH=. python -m examples.mcp_demo.show_compression
5
  """
6
 
7
- import json
8
  import random
9
 
10
  from headroom.integrations.mcp import (
11
  compress_tool_result_with_metrics,
12
- HeadroomMCPCompressor,
13
  )
14
  from headroom.providers import OpenAIProvider
15
 
16
  from .mock_mcp_servers import (
17
- generate_slack_search_results,
18
  generate_database_query_results,
19
- generate_log_search_results,
20
  generate_github_issues_results,
 
 
21
  )
22
 
23
 
@@ -30,7 +28,7 @@ def main():
30
 
31
  # Get token counter
32
  provider = OpenAIProvider()
33
- tokenizer = provider.get_token_counter("gpt-4o")
34
 
35
  # Test scenarios
36
  scenarios = [
@@ -71,7 +69,7 @@ def main():
71
  print(f"\n{'─' * 70}")
72
  print(f"Tool: {scenario['name']}")
73
  print(f"MCP Server: {scenario['tool_name']}")
74
- print(f"User Query: \"{scenario['user_query']}\"")
75
  print(f"{'─' * 70}")
76
 
77
  result = compress_tool_result_with_metrics(
 
4
  PYTHONPATH=. python -m examples.mcp_demo.show_compression
5
  """
6
 
 
7
  import random
8
 
9
  from headroom.integrations.mcp import (
10
  compress_tool_result_with_metrics,
 
11
  )
12
  from headroom.providers import OpenAIProvider
13
 
14
  from .mock_mcp_servers import (
 
15
  generate_database_query_results,
 
16
  generate_github_issues_results,
17
+ generate_log_search_results,
18
+ generate_slack_search_results,
19
  )
20
 
21
 
 
28
 
29
  # Get token counter
30
  provider = OpenAIProvider()
31
+ provider.get_token_counter("gpt-4o")
32
 
33
  # Test scenarios
34
  scenarios = [
 
69
  print(f"\n{'─' * 70}")
70
  print(f"Tool: {scenario['name']}")
71
  print(f"MCP Server: {scenario['tool_name']}")
72
+ print(f'User Query: "{scenario["user_query"]}"')
73
  print(f"{'─' * 70}")
74
 
75
  result = compress_tool_result_with_metrics(
examples/real_world_eval.py CHANGED
@@ -37,10 +37,10 @@ provider = AnthropicProvider()
37
  # AGGRESSIVE optimization config
38
  aggressive_tool_crusher = ToolCrusherConfig(
39
  enabled=True,
40
- min_tokens_to_crush=100, # Crush smaller outputs
41
- max_array_items=3, # Only keep first 3 items (was 10)
42
- max_string_length=200, # Truncate strings > 200 chars (was 1000)
43
- max_depth=3, # Limit nesting to 3 levels (was 5)
44
  )
45
 
46
  db_path = os.path.join(tempfile.gettempdir(), "headroom_eval.db")
@@ -54,7 +54,6 @@ headroom_client = HeadroomClient(
54
  )
55
 
56
  # Aggressive optimization client
57
- from headroom.config import HeadroomConfig
58
  aggressive_config = HeadroomConfig()
59
  aggressive_config.tool_crusher = aggressive_tool_crusher
60
 
@@ -67,52 +66,56 @@ aggressive_client = HeadroomClient(
67
  )
68
  # Manually set aggressive config on pipeline
69
  aggressive_client._config = aggressive_config
70
- aggressive_client._pipeline = __import__('headroom.transforms', fromlist=['TransformPipeline']).TransformPipeline(
71
- aggressive_config, provider=provider
72
- )
73
 
74
 
75
  # =============================================================================
76
  # REALISTIC AGENTIC SCENARIO: Research Assistant
77
  # =============================================================================
78
 
 
79
  def generate_search_results(query: str, count: int = 25) -> str:
80
  """Generate realistic search results JSON."""
81
  results = []
82
  for i in range(count):
83
- results.append({
84
- "id": f"doc_{i:04d}",
85
- "title": f"Research Paper: {query.title()} - Study {i+1}",
86
- "url": f"https://research.example.com/papers/{query.replace(' ', '-')}/{i}",
87
- "snippet": f"This comprehensive study examines {query} through multiple methodologies. "
88
- f"Key findings include significant correlations between variables A and B, "
89
- f"with p-values < 0.05. The sample size of {1000 + i*100} participants "
90
- f"provides robust statistical power. Methods included: surveys, interviews, "
91
- f"longitudinal tracking, and meta-analysis of {50 + i*10} prior studies.",
92
- "citations": 150 + i * 23,
93
- "year": 2020 + (i % 5),
94
- "authors": [
95
- {"name": f"Dr. Smith{i}", "affiliation": "MIT"},
96
- {"name": f"Prof. Jones{i}", "affiliation": "Stanford"},
97
- {"name": f"Dr. Williams{i}", "affiliation": "Harvard"},
98
- ],
99
- "keywords": ["machine learning", "data science", query, "research", "analysis"],
100
- "abstract": f"Abstract for paper {i}: " + "Lorem ipsum dolor sit amet. " * 20,
101
- "methodology": {
102
- "type": "mixed-methods",
103
- "sample_size": 1000 + i * 100,
104
- "duration_months": 12 + i,
105
- "instruments": ["survey", "interview", "observation"],
106
- },
107
- })
 
 
108
  return json.dumps({"results": results, "total_count": count, "query": query})
109
 
110
 
111
  def generate_document_content(doc_id: str) -> str:
112
  """Generate realistic document content."""
113
- return json.dumps({
114
- "id": doc_id,
115
- "full_text": """
 
116
  Introduction:
117
  This research investigates the complex interplay between artificial intelligence
118
  and human decision-making processes. Our longitudinal study spanning 36 months
@@ -136,68 +139,99 @@ def generate_document_content(doc_id: str) -> str:
136
  Conclusion:
137
  The integration of AI in decision-making processes offers substantial benefits
138
  but requires careful implementation to avoid potential negative outcomes.
139
- """ * 3, # Make it longer
140
- "metadata": {
141
- "word_count": 15000,
142
- "pages": 45,
143
- "figures": 12,
144
- "tables": 8,
145
- "references": 150,
146
- },
147
- "sections": [
148
- {"title": "Introduction", "page": 1, "word_count": 2000},
149
- {"title": "Literature Review", "page": 5, "word_count": 4000},
150
- {"title": "Methodology", "page": 15, "word_count": 3000},
151
- {"title": "Results", "page": 22, "word_count": 3500},
152
- {"title": "Discussion", "page": 32, "word_count": 2000},
153
- {"title": "Conclusion", "page": 40, "word_count": 500},
154
- ],
155
- })
 
 
156
 
157
 
158
  def generate_analytics_data() -> str:
159
  """Generate realistic analytics/metrics data."""
160
- return json.dumps({
161
- "summary_statistics": {
162
- "total_papers_analyzed": 500,
163
- "date_range": {"start": "2020-01-01", "end": "2024-12-31"},
164
- "avg_citations": 45.7,
165
- "median_citations": 32,
166
- "std_dev": 28.3,
167
- },
168
- "trend_analysis": [
169
- {"year": 2020, "papers": 80, "avg_citations": 52.3, "top_keywords": ["covid", "remote", "digital"]},
170
- {"year": 2021, "papers": 95, "avg_citations": 48.1, "top_keywords": ["hybrid", "adaptation", "resilience"]},
171
- {"year": 2022, "papers": 110, "avg_citations": 44.2, "top_keywords": ["AI", "automation", "efficiency"]},
172
- {"year": 2023, "papers": 120, "avg_citations": 38.5, "top_keywords": ["LLM", "generative", "ethics"]},
173
- {"year": 2024, "papers": 95, "avg_citations": 25.1, "top_keywords": ["agents", "multimodal", "safety"]},
174
- ],
175
- "citation_distribution": {
176
- "0-10": 150,
177
- "11-25": 120,
178
- "26-50": 100,
179
- "51-100": 80,
180
- "101-200": 35,
181
- "200+": 15,
182
- },
183
- "top_authors": [
184
- {"name": "Dr. Smith", "papers": 25, "total_citations": 1250, "h_index": 18},
185
- {"name": "Prof. Jones", "papers": 22, "total_citations": 980, "h_index": 15},
186
- {"name": "Dr. Williams", "papers": 20, "total_citations": 890, "h_index": 14},
187
- ] * 5, # More authors
188
- "collaboration_network": {
189
- "nodes": 150,
190
- "edges": 450,
191
- "avg_degree": 6.0,
192
- "clustering_coefficient": 0.45,
193
- },
194
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
 
197
  # =============================================================================
198
  # BUILD COMPLEX AGENTIC CONVERSATION
199
  # =============================================================================
200
 
 
201
  def build_agentic_conversation() -> list[dict]:
202
  """Build a realistic multi-turn agentic conversation."""
203
 
@@ -210,26 +244,24 @@ def build_agentic_conversation() -> list[dict]:
210
  {
211
  "role": "user",
212
  "content": "Current Date: 2024-12-15. I need you to research the impact of AI on workplace productivity. "
213
- "Search for recent papers, analyze the top results, and give me a summary."
214
  },
215
-
216
  # Turn 2: Assistant decides to search
217
  {
218
  "role": "assistant",
219
  "content": [
220
  {
221
  "type": "text",
222
- "text": "I'll help you research AI's impact on workplace productivity. Let me search for recent academic papers on this topic."
223
  },
224
  {
225
  "type": "tool_use",
226
  "id": "search_1",
227
  "name": "academic_search",
228
- "input": {"query": "AI impact workplace productivity", "limit": 25}
229
- }
230
- ]
231
  },
232
-
233
  # Turn 3: Tool result - large search results
234
  {
235
  "role": "user",
@@ -237,40 +269,38 @@ def build_agentic_conversation() -> list[dict]:
237
  {
238
  "type": "tool_result",
239
  "tool_use_id": "search_1",
240
- "content": generate_search_results("AI impact workplace productivity", 25)
241
  }
242
- ]
243
  },
244
-
245
  # Turn 4: Assistant analyzes and fetches more
246
  {
247
  "role": "assistant",
248
  "content": [
249
  {
250
  "type": "text",
251
- "text": "I found 25 relevant papers. Let me fetch the full content of the top 3 most cited papers and get analytics on the overall trends."
252
  },
253
  {
254
  "type": "tool_use",
255
  "id": "fetch_1",
256
  "name": "fetch_document",
257
- "input": {"doc_id": "doc_0001"}
258
  },
259
  {
260
  "type": "tool_use",
261
  "id": "fetch_2",
262
  "name": "fetch_document",
263
- "input": {"doc_id": "doc_0002"}
264
  },
265
  {
266
  "type": "tool_use",
267
  "id": "analytics_1",
268
  "name": "get_analytics",
269
- "input": {"topic": "AI workplace productivity"}
270
- }
271
- ]
272
  },
273
-
274
  # Turn 5: Multiple tool results
275
  {
276
  "role": "user",
@@ -278,56 +308,55 @@ def build_agentic_conversation() -> list[dict]:
278
  {
279
  "type": "tool_result",
280
  "tool_use_id": "fetch_1",
281
- "content": generate_document_content("doc_0001")
282
  },
283
  {
284
  "type": "tool_result",
285
  "tool_use_id": "fetch_2",
286
- "content": generate_document_content("doc_0002")
287
  },
288
  {
289
  "type": "tool_result",
290
  "tool_use_id": "analytics_1",
291
- "content": generate_analytics_data()
292
- }
293
- ]
294
  },
295
-
296
  # Turn 6: Assistant provides initial summary
297
  {
298
  "role": "assistant",
299
  "content": "Based on my analysis of 25 papers and detailed review of the top cited works, here's what the research shows:\n\n"
300
- "**Key Findings:**\n"
301
- "1. AI tools improve productivity by 25-40% on average\n"
302
- "2. Biggest gains in data analysis and routine tasks\n"
303
- "3. Mixed results for creative work\n"
304
- "4. Training and adoption are critical success factors\n\n"
305
- "Would you like me to dive deeper into any specific aspect?"
306
  },
307
-
308
  # Turn 7: User asks follow-up
309
  {
310
  "role": "user",
311
- "content": "Yes, tell me more about the training and adoption factors. What makes AI adoption successful?"
312
  },
313
-
314
  # Turn 8: Assistant searches for more specific info
315
  {
316
  "role": "assistant",
317
  "content": [
318
  {
319
  "type": "text",
320
- "text": "Great question. Let me search for papers specifically about AI adoption success factors."
321
  },
322
  {
323
  "type": "tool_use",
324
  "id": "search_2",
325
  "name": "academic_search",
326
- "input": {"query": "AI adoption success factors workplace training", "limit": 20}
327
- }
328
- ]
 
 
 
329
  },
330
-
331
  # Turn 9: More search results
332
  {
333
  "role": "user",
@@ -335,28 +364,28 @@ def build_agentic_conversation() -> list[dict]:
335
  {
336
  "type": "tool_result",
337
  "tool_use_id": "search_2",
338
- "content": generate_search_results("AI adoption success factors workplace training", 20)
 
 
339
  }
340
- ]
341
  },
342
-
343
  # Turn 10: Assistant synthesizes
344
  {
345
  "role": "assistant",
346
  "content": "Based on the additional research on AI adoption success factors:\n\n"
347
- "**Critical Success Factors:**\n\n"
348
- "1. **Leadership Support** - Executive buy-in correlates with 3x higher adoption rates\n"
349
- "2. **Gradual Rollout** - Phased implementation outperforms big-bang approaches\n"
350
- "3. **Training Investment** - Organizations spending >40 hours on training see 50% better outcomes\n"
351
- "4. **Change Management** - Addressing employee concerns reduces resistance by 60%\n"
352
- "5. **Clear Use Cases** - Specific, measurable goals improve success rates\n\n"
353
- "The research consistently shows that technical capabilities matter less than organizational readiness."
354
  },
355
-
356
  # Turn 11: User asks for final deliverable
357
  {
358
  "role": "user",
359
- "content": "Perfect. Now synthesize everything into a brief executive summary I can share with my team."
360
  },
361
  ]
362
 
@@ -367,9 +396,11 @@ def build_agentic_conversation() -> list[dict]:
367
  # EVALUATION FRAMEWORK
368
  # =============================================================================
369
 
 
370
  @dataclass
371
  class EvalResult:
372
  """Results from a single evaluation run."""
 
373
  mode: str
374
  tokens_input: int
375
  tokens_output: int
@@ -443,15 +474,15 @@ Provide scores in this exact JSON format:
443
  response = base_client.messages.create(
444
  model="claude-3-5-haiku-latest",
445
  max_tokens=500,
446
- messages=[{"role": "user", "content": eval_prompt}]
447
  )
448
 
449
  try:
450
  # Extract JSON from response
451
  text = response.content[0].text
452
  # Find JSON in response
453
- start = text.find('{')
454
- end = text.rfind('}') + 1
455
  if start >= 0 and end > start:
456
  return json.loads(text[start:end])
457
  except (json.JSONDecodeError, IndexError):
@@ -464,6 +495,7 @@ Provide scores in this exact JSON format:
464
  # MAIN EVALUATION
465
  # =============================================================================
466
 
 
467
  def run_aggressive_evaluation(messages: list[dict], mode: str) -> EvalResult:
468
  """Run evaluation with aggressive client."""
469
  tokenizer = provider.get_token_counter("claude-3-5-haiku-latest")
@@ -505,8 +537,8 @@ def main():
505
  messages = build_agentic_conversation()
506
 
507
  print(f"Scenario: Research Assistant with {len(messages)} turns")
508
- print(f"Tool calls: 4 (search x2, fetch x2, analytics x1)")
509
- print(f"Tool outputs: Large JSON payloads (~50KB total)")
510
  print()
511
 
512
  # =========================================================================
@@ -530,8 +562,12 @@ def main():
530
 
531
  print(f"\n{'Mode':<20} {'Before':>10} {'After':>10} {'Saved':>10} {'%':>8}")
532
  print("-" * 60)
533
- print(f"{'Conservative':<20} {sim_default.tokens_before:>10,} {sim_default.tokens_after:>10,} {sim_default.tokens_saved:>10,} {sim_default.tokens_saved/sim_default.tokens_before*100:>7.1f}%")
534
- print(f"{'Aggressive':<20} {sim_aggressive.tokens_before:>10,} {sim_aggressive.tokens_after:>10,} {sim_aggressive.tokens_saved:>10,} {sim_aggressive.tokens_saved/sim_aggressive.tokens_before*100:>7.1f}%")
 
 
 
 
535
  print()
536
  print(f"Conservative transforms: {sim_default.transforms}")
537
  print(f"Aggressive transforms: {sim_aggressive.transforms}")
@@ -545,7 +581,9 @@ def main():
545
  print("1. BASELINE (No Optimization)")
546
  print("-" * 70)
547
  baseline = run_evaluation(messages, "audit")
548
- print(f"Input: {baseline.tokens_input:,} tokens | Cost: ${baseline.cost_estimate:.4f} | Latency: {baseline.latency_ms:.0f}ms")
 
 
549
  print(f"Response: {baseline.response[:300]}...")
550
  print()
551
 
@@ -553,7 +591,9 @@ def main():
553
  print("2. CONSERVATIVE OPTIMIZATION (Default Settings)")
554
  print("-" * 70)
555
  conservative = run_evaluation(messages, "optimize")
556
- print(f"Input: {conservative.tokens_input:,} tokens | Cost: ${conservative.cost_estimate:.4f} | Latency: {conservative.latency_ms:.0f}ms")
 
 
557
  print(f"Response: {conservative.response[:300]}...")
558
  print()
559
 
@@ -561,7 +601,9 @@ def main():
561
  print("3. AGGRESSIVE OPTIMIZATION (max_array=3, max_string=200, max_depth=3)")
562
  print("-" * 70)
563
  aggressive = run_aggressive_evaluation(messages, "optimize")
564
- print(f"Input: {aggressive.tokens_input:,} tokens | Cost: ${aggressive.cost_estimate:.4f} | Latency: {aggressive.latency_ms:.0f}ms")
 
 
565
  print(f"Response: {aggressive.response[:300]}...")
566
  print()
567
 
@@ -574,10 +616,18 @@ def main():
574
 
575
  print(f"\n{'Metric':<25} {'Baseline':>12} {'Conservative':>12} {'Aggressive':>12}")
576
  print("-" * 65)
577
- print(f"{'Input Tokens':<25} {baseline.tokens_input:>12,} {conservative.tokens_input:>12,} {aggressive.tokens_input:>12,}")
578
- print(f"{'Output Tokens':<25} {baseline.tokens_output:>12,} {conservative.tokens_output:>12,} {aggressive.tokens_output:>12,}")
579
- print(f"{'Cost':<25} ${baseline.cost_estimate:>11.4f} ${conservative.cost_estimate:>11.4f} ${aggressive.cost_estimate:>11.4f}")
580
- print(f"{'Latency (ms)':<25} {baseline.latency_ms:>12.0f} {conservative.latency_ms:>12.0f} {aggressive.latency_ms:>12.0f}")
 
 
 
 
 
 
 
 
581
 
582
  # Savings vs baseline
583
  cons_savings = baseline.tokens_input - conservative.tokens_input
@@ -586,12 +636,16 @@ def main():
586
  aggr_pct = (aggr_savings / baseline.tokens_input) * 100 if baseline.tokens_input > 0 else 0
587
 
588
  print()
589
- print(f"{'Token Savings vs Baseline':<25} {'-':>12} {cons_savings:>10,} ({cons_pct:.0f}%) {aggr_savings:>10,} ({aggr_pct:.0f}%)")
 
 
590
 
591
  cons_cost_save = baseline.cost_estimate - conservative.cost_estimate
592
  aggr_cost_save = baseline.cost_estimate - aggressive.cost_estimate
593
 
594
- print(f"{'Cost Savings vs Baseline':<25} {'-':>12} ${cons_cost_save:>10.4f} ${aggr_cost_save:>10.4f}")
 
 
595
  print()
596
 
597
  # =========================================================================
@@ -610,9 +664,9 @@ def main():
610
  print(f"\n{'Criterion':<20} {'Baseline':>10} {'Conservative':>12} {'Aggressive':>12}")
611
  print("-" * 55)
612
  for criterion in ["completeness", "accuracy", "clarity", "actionability"]:
613
- b_score = qual_cons['baseline'].get(criterion, 'N/A')
614
- c_score = qual_cons['optimized'].get(criterion, 'N/A')
615
- a_score = qual_aggr['optimized'].get(criterion, 'N/A')
616
  print(f"{criterion.title():<20} {b_score:>10} {c_score:>12} {a_score:>12}")
617
 
618
  print()
 
37
  # AGGRESSIVE optimization config
38
  aggressive_tool_crusher = ToolCrusherConfig(
39
  enabled=True,
40
+ min_tokens_to_crush=100, # Crush smaller outputs
41
+ max_array_items=3, # Only keep first 3 items (was 10)
42
+ max_string_length=200, # Truncate strings > 200 chars (was 1000)
43
+ max_depth=3, # Limit nesting to 3 levels (was 5)
44
  )
45
 
46
  db_path = os.path.join(tempfile.gettempdir(), "headroom_eval.db")
 
54
  )
55
 
56
  # Aggressive optimization client
 
57
  aggressive_config = HeadroomConfig()
58
  aggressive_config.tool_crusher = aggressive_tool_crusher
59
 
 
66
  )
67
  # Manually set aggressive config on pipeline
68
  aggressive_client._config = aggressive_config
69
+ aggressive_client._pipeline = __import__(
70
+ "headroom.transforms", fromlist=["TransformPipeline"]
71
+ ).TransformPipeline(aggressive_config, provider=provider)
72
 
73
 
74
  # =============================================================================
75
  # REALISTIC AGENTIC SCENARIO: Research Assistant
76
  # =============================================================================
77
 
78
+
79
  def generate_search_results(query: str, count: int = 25) -> str:
80
  """Generate realistic search results JSON."""
81
  results = []
82
  for i in range(count):
83
+ results.append(
84
+ {
85
+ "id": f"doc_{i:04d}",
86
+ "title": f"Research Paper: {query.title()} - Study {i + 1}",
87
+ "url": f"https://research.example.com/papers/{query.replace(' ', '-')}/{i}",
88
+ "snippet": f"This comprehensive study examines {query} through multiple methodologies. "
89
+ f"Key findings include significant correlations between variables A and B, "
90
+ f"with p-values < 0.05. The sample size of {1000 + i * 100} participants "
91
+ f"provides robust statistical power. Methods included: surveys, interviews, "
92
+ f"longitudinal tracking, and meta-analysis of {50 + i * 10} prior studies.",
93
+ "citations": 150 + i * 23,
94
+ "year": 2020 + (i % 5),
95
+ "authors": [
96
+ {"name": f"Dr. Smith{i}", "affiliation": "MIT"},
97
+ {"name": f"Prof. Jones{i}", "affiliation": "Stanford"},
98
+ {"name": f"Dr. Williams{i}", "affiliation": "Harvard"},
99
+ ],
100
+ "keywords": ["machine learning", "data science", query, "research", "analysis"],
101
+ "abstract": f"Abstract for paper {i}: " + "Lorem ipsum dolor sit amet. " * 20,
102
+ "methodology": {
103
+ "type": "mixed-methods",
104
+ "sample_size": 1000 + i * 100,
105
+ "duration_months": 12 + i,
106
+ "instruments": ["survey", "interview", "observation"],
107
+ },
108
+ }
109
+ )
110
  return json.dumps({"results": results, "total_count": count, "query": query})
111
 
112
 
113
  def generate_document_content(doc_id: str) -> str:
114
  """Generate realistic document content."""
115
+ return json.dumps(
116
+ {
117
+ "id": doc_id,
118
+ "full_text": """
119
  Introduction:
120
  This research investigates the complex interplay between artificial intelligence
121
  and human decision-making processes. Our longitudinal study spanning 36 months
 
139
  Conclusion:
140
  The integration of AI in decision-making processes offers substantial benefits
141
  but requires careful implementation to avoid potential negative outcomes.
142
+ """
143
+ * 3, # Make it longer
144
+ "metadata": {
145
+ "word_count": 15000,
146
+ "pages": 45,
147
+ "figures": 12,
148
+ "tables": 8,
149
+ "references": 150,
150
+ },
151
+ "sections": [
152
+ {"title": "Introduction", "page": 1, "word_count": 2000},
153
+ {"title": "Literature Review", "page": 5, "word_count": 4000},
154
+ {"title": "Methodology", "page": 15, "word_count": 3000},
155
+ {"title": "Results", "page": 22, "word_count": 3500},
156
+ {"title": "Discussion", "page": 32, "word_count": 2000},
157
+ {"title": "Conclusion", "page": 40, "word_count": 500},
158
+ ],
159
+ }
160
+ )
161
 
162
 
163
  def generate_analytics_data() -> str:
164
  """Generate realistic analytics/metrics data."""
165
+ return json.dumps(
166
+ {
167
+ "summary_statistics": {
168
+ "total_papers_analyzed": 500,
169
+ "date_range": {"start": "2020-01-01", "end": "2024-12-31"},
170
+ "avg_citations": 45.7,
171
+ "median_citations": 32,
172
+ "std_dev": 28.3,
173
+ },
174
+ "trend_analysis": [
175
+ {
176
+ "year": 2020,
177
+ "papers": 80,
178
+ "avg_citations": 52.3,
179
+ "top_keywords": ["covid", "remote", "digital"],
180
+ },
181
+ {
182
+ "year": 2021,
183
+ "papers": 95,
184
+ "avg_citations": 48.1,
185
+ "top_keywords": ["hybrid", "adaptation", "resilience"],
186
+ },
187
+ {
188
+ "year": 2022,
189
+ "papers": 110,
190
+ "avg_citations": 44.2,
191
+ "top_keywords": ["AI", "automation", "efficiency"],
192
+ },
193
+ {
194
+ "year": 2023,
195
+ "papers": 120,
196
+ "avg_citations": 38.5,
197
+ "top_keywords": ["LLM", "generative", "ethics"],
198
+ },
199
+ {
200
+ "year": 2024,
201
+ "papers": 95,
202
+ "avg_citations": 25.1,
203
+ "top_keywords": ["agents", "multimodal", "safety"],
204
+ },
205
+ ],
206
+ "citation_distribution": {
207
+ "0-10": 150,
208
+ "11-25": 120,
209
+ "26-50": 100,
210
+ "51-100": 80,
211
+ "101-200": 35,
212
+ "200+": 15,
213
+ },
214
+ "top_authors": [
215
+ {"name": "Dr. Smith", "papers": 25, "total_citations": 1250, "h_index": 18},
216
+ {"name": "Prof. Jones", "papers": 22, "total_citations": 980, "h_index": 15},
217
+ {"name": "Dr. Williams", "papers": 20, "total_citations": 890, "h_index": 14},
218
+ ]
219
+ * 5, # More authors
220
+ "collaboration_network": {
221
+ "nodes": 150,
222
+ "edges": 450,
223
+ "avg_degree": 6.0,
224
+ "clustering_coefficient": 0.45,
225
+ },
226
+ }
227
+ )
228
 
229
 
230
  # =============================================================================
231
  # BUILD COMPLEX AGENTIC CONVERSATION
232
  # =============================================================================
233
 
234
+
235
  def build_agentic_conversation() -> list[dict]:
236
  """Build a realistic multi-turn agentic conversation."""
237
 
 
244
  {
245
  "role": "user",
246
  "content": "Current Date: 2024-12-15. I need you to research the impact of AI on workplace productivity. "
247
+ "Search for recent papers, analyze the top results, and give me a summary.",
248
  },
 
249
  # Turn 2: Assistant decides to search
250
  {
251
  "role": "assistant",
252
  "content": [
253
  {
254
  "type": "text",
255
+ "text": "I'll help you research AI's impact on workplace productivity. Let me search for recent academic papers on this topic.",
256
  },
257
  {
258
  "type": "tool_use",
259
  "id": "search_1",
260
  "name": "academic_search",
261
+ "input": {"query": "AI impact workplace productivity", "limit": 25},
262
+ },
263
+ ],
264
  },
 
265
  # Turn 3: Tool result - large search results
266
  {
267
  "role": "user",
 
269
  {
270
  "type": "tool_result",
271
  "tool_use_id": "search_1",
272
+ "content": generate_search_results("AI impact workplace productivity", 25),
273
  }
274
+ ],
275
  },
 
276
  # Turn 4: Assistant analyzes and fetches more
277
  {
278
  "role": "assistant",
279
  "content": [
280
  {
281
  "type": "text",
282
+ "text": "I found 25 relevant papers. Let me fetch the full content of the top 3 most cited papers and get analytics on the overall trends.",
283
  },
284
  {
285
  "type": "tool_use",
286
  "id": "fetch_1",
287
  "name": "fetch_document",
288
+ "input": {"doc_id": "doc_0001"},
289
  },
290
  {
291
  "type": "tool_use",
292
  "id": "fetch_2",
293
  "name": "fetch_document",
294
+ "input": {"doc_id": "doc_0002"},
295
  },
296
  {
297
  "type": "tool_use",
298
  "id": "analytics_1",
299
  "name": "get_analytics",
300
+ "input": {"topic": "AI workplace productivity"},
301
+ },
302
+ ],
303
  },
 
304
  # Turn 5: Multiple tool results
305
  {
306
  "role": "user",
 
308
  {
309
  "type": "tool_result",
310
  "tool_use_id": "fetch_1",
311
+ "content": generate_document_content("doc_0001"),
312
  },
313
  {
314
  "type": "tool_result",
315
  "tool_use_id": "fetch_2",
316
+ "content": generate_document_content("doc_0002"),
317
  },
318
  {
319
  "type": "tool_result",
320
  "tool_use_id": "analytics_1",
321
+ "content": generate_analytics_data(),
322
+ },
323
+ ],
324
  },
 
325
  # Turn 6: Assistant provides initial summary
326
  {
327
  "role": "assistant",
328
  "content": "Based on my analysis of 25 papers and detailed review of the top cited works, here's what the research shows:\n\n"
329
+ "**Key Findings:**\n"
330
+ "1. AI tools improve productivity by 25-40% on average\n"
331
+ "2. Biggest gains in data analysis and routine tasks\n"
332
+ "3. Mixed results for creative work\n"
333
+ "4. Training and adoption are critical success factors\n\n"
334
+ "Would you like me to dive deeper into any specific aspect?",
335
  },
 
336
  # Turn 7: User asks follow-up
337
  {
338
  "role": "user",
339
+ "content": "Yes, tell me more about the training and adoption factors. What makes AI adoption successful?",
340
  },
 
341
  # Turn 8: Assistant searches for more specific info
342
  {
343
  "role": "assistant",
344
  "content": [
345
  {
346
  "type": "text",
347
+ "text": "Great question. Let me search for papers specifically about AI adoption success factors.",
348
  },
349
  {
350
  "type": "tool_use",
351
  "id": "search_2",
352
  "name": "academic_search",
353
+ "input": {
354
+ "query": "AI adoption success factors workplace training",
355
+ "limit": 20,
356
+ },
357
+ },
358
+ ],
359
  },
 
360
  # Turn 9: More search results
361
  {
362
  "role": "user",
 
364
  {
365
  "type": "tool_result",
366
  "tool_use_id": "search_2",
367
+ "content": generate_search_results(
368
+ "AI adoption success factors workplace training", 20
369
+ ),
370
  }
371
+ ],
372
  },
 
373
  # Turn 10: Assistant synthesizes
374
  {
375
  "role": "assistant",
376
  "content": "Based on the additional research on AI adoption success factors:\n\n"
377
+ "**Critical Success Factors:**\n\n"
378
+ "1. **Leadership Support** - Executive buy-in correlates with 3x higher adoption rates\n"
379
+ "2. **Gradual Rollout** - Phased implementation outperforms big-bang approaches\n"
380
+ "3. **Training Investment** - Organizations spending >40 hours on training see 50% better outcomes\n"
381
+ "4. **Change Management** - Addressing employee concerns reduces resistance by 60%\n"
382
+ "5. **Clear Use Cases** - Specific, measurable goals improve success rates\n\n"
383
+ "The research consistently shows that technical capabilities matter less than organizational readiness.",
384
  },
 
385
  # Turn 11: User asks for final deliverable
386
  {
387
  "role": "user",
388
+ "content": "Perfect. Now synthesize everything into a brief executive summary I can share with my team.",
389
  },
390
  ]
391
 
 
396
  # EVALUATION FRAMEWORK
397
  # =============================================================================
398
 
399
+
400
  @dataclass
401
  class EvalResult:
402
  """Results from a single evaluation run."""
403
+
404
  mode: str
405
  tokens_input: int
406
  tokens_output: int
 
474
  response = base_client.messages.create(
475
  model="claude-3-5-haiku-latest",
476
  max_tokens=500,
477
+ messages=[{"role": "user", "content": eval_prompt}],
478
  )
479
 
480
  try:
481
  # Extract JSON from response
482
  text = response.content[0].text
483
  # Find JSON in response
484
+ start = text.find("{")
485
+ end = text.rfind("}") + 1
486
  if start >= 0 and end > start:
487
  return json.loads(text[start:end])
488
  except (json.JSONDecodeError, IndexError):
 
495
  # MAIN EVALUATION
496
  # =============================================================================
497
 
498
+
499
  def run_aggressive_evaluation(messages: list[dict], mode: str) -> EvalResult:
500
  """Run evaluation with aggressive client."""
501
  tokenizer = provider.get_token_counter("claude-3-5-haiku-latest")
 
537
  messages = build_agentic_conversation()
538
 
539
  print(f"Scenario: Research Assistant with {len(messages)} turns")
540
+ print("Tool calls: 4 (search x2, fetch x2, analytics x1)")
541
+ print("Tool outputs: Large JSON payloads (~50KB total)")
542
  print()
543
 
544
  # =========================================================================
 
562
 
563
  print(f"\n{'Mode':<20} {'Before':>10} {'After':>10} {'Saved':>10} {'%':>8}")
564
  print("-" * 60)
565
+ print(
566
+ f"{'Conservative':<20} {sim_default.tokens_before:>10,} {sim_default.tokens_after:>10,} {sim_default.tokens_saved:>10,} {sim_default.tokens_saved / sim_default.tokens_before * 100:>7.1f}%"
567
+ )
568
+ print(
569
+ f"{'Aggressive':<20} {sim_aggressive.tokens_before:>10,} {sim_aggressive.tokens_after:>10,} {sim_aggressive.tokens_saved:>10,} {sim_aggressive.tokens_saved / sim_aggressive.tokens_before * 100:>7.1f}%"
570
+ )
571
  print()
572
  print(f"Conservative transforms: {sim_default.transforms}")
573
  print(f"Aggressive transforms: {sim_aggressive.transforms}")
 
581
  print("1. BASELINE (No Optimization)")
582
  print("-" * 70)
583
  baseline = run_evaluation(messages, "audit")
584
+ print(
585
+ f"Input: {baseline.tokens_input:,} tokens | Cost: ${baseline.cost_estimate:.4f} | Latency: {baseline.latency_ms:.0f}ms"
586
+ )
587
  print(f"Response: {baseline.response[:300]}...")
588
  print()
589
 
 
591
  print("2. CONSERVATIVE OPTIMIZATION (Default Settings)")
592
  print("-" * 70)
593
  conservative = run_evaluation(messages, "optimize")
594
+ print(
595
+ f"Input: {conservative.tokens_input:,} tokens | Cost: ${conservative.cost_estimate:.4f} | Latency: {conservative.latency_ms:.0f}ms"
596
+ )
597
  print(f"Response: {conservative.response[:300]}...")
598
  print()
599
 
 
601
  print("3. AGGRESSIVE OPTIMIZATION (max_array=3, max_string=200, max_depth=3)")
602
  print("-" * 70)
603
  aggressive = run_aggressive_evaluation(messages, "optimize")
604
+ print(
605
+ f"Input: {aggressive.tokens_input:,} tokens | Cost: ${aggressive.cost_estimate:.4f} | Latency: {aggressive.latency_ms:.0f}ms"
606
+ )
607
  print(f"Response: {aggressive.response[:300]}...")
608
  print()
609
 
 
616
 
617
  print(f"\n{'Metric':<25} {'Baseline':>12} {'Conservative':>12} {'Aggressive':>12}")
618
  print("-" * 65)
619
+ print(
620
+ f"{'Input Tokens':<25} {baseline.tokens_input:>12,} {conservative.tokens_input:>12,} {aggressive.tokens_input:>12,}"
621
+ )
622
+ print(
623
+ f"{'Output Tokens':<25} {baseline.tokens_output:>12,} {conservative.tokens_output:>12,} {aggressive.tokens_output:>12,}"
624
+ )
625
+ print(
626
+ f"{'Cost':<25} ${baseline.cost_estimate:>11.4f} ${conservative.cost_estimate:>11.4f} ${aggressive.cost_estimate:>11.4f}"
627
+ )
628
+ print(
629
+ f"{'Latency (ms)':<25} {baseline.latency_ms:>12.0f} {conservative.latency_ms:>12.0f} {aggressive.latency_ms:>12.0f}"
630
+ )
631
 
632
  # Savings vs baseline
633
  cons_savings = baseline.tokens_input - conservative.tokens_input
 
636
  aggr_pct = (aggr_savings / baseline.tokens_input) * 100 if baseline.tokens_input > 0 else 0
637
 
638
  print()
639
+ print(
640
+ f"{'Token Savings vs Baseline':<25} {'-':>12} {cons_savings:>10,} ({cons_pct:.0f}%) {aggr_savings:>10,} ({aggr_pct:.0f}%)"
641
+ )
642
 
643
  cons_cost_save = baseline.cost_estimate - conservative.cost_estimate
644
  aggr_cost_save = baseline.cost_estimate - aggressive.cost_estimate
645
 
646
+ print(
647
+ f"{'Cost Savings vs Baseline':<25} {'-':>12} ${cons_cost_save:>10.4f} ${aggr_cost_save:>10.4f}"
648
+ )
649
  print()
650
 
651
  # =========================================================================
 
664
  print(f"\n{'Criterion':<20} {'Baseline':>10} {'Conservative':>12} {'Aggressive':>12}")
665
  print("-" * 55)
666
  for criterion in ["completeness", "accuracy", "clarity", "actionability"]:
667
+ b_score = qual_cons["baseline"].get(criterion, "N/A")
668
+ c_score = qual_cons["optimized"].get(criterion, "N/A")
669
+ a_score = qual_aggr["optimized"].get(criterion, "N/A")
670
  print(f"{criterion.title():<20} {b_score:>10} {c_score:>12} {a_score:>12}")
671
 
672
  print()
examples/real_world_openai_eval.py CHANGED
@@ -27,6 +27,7 @@ from openai import OpenAI
27
 
28
  from headroom import HeadroomClient, OpenAIProvider, ToolCrusherConfig
29
  from headroom.config import HeadroomConfig
 
30
 
31
  load_dotenv(".env.local")
32
 
@@ -59,7 +60,6 @@ aggressive_client = HeadroomClient(
59
  default_mode="audit",
60
  )
61
  aggressive_client._config = aggressive_config
62
- from headroom.transforms import TransformPipeline
63
  aggressive_client._pipeline = TransformPipeline(aggressive_config, provider=provider)
64
 
65
 
@@ -67,6 +67,7 @@ aggressive_client._pipeline = TransformPipeline(aggressive_config, provider=prov
67
  # REALISTIC TOOL OUTPUTS - Based on actual production systems
68
  # =============================================================================
69
 
 
70
  def generate_metrics_response() -> str:
71
  """
72
  Realistic Prometheus/Datadog metrics query response.
@@ -80,58 +81,71 @@ def generate_metrics_response() -> str:
80
  ts = base_time + timedelta(minutes=i)
81
  # Simulate spike around minute 45
82
  value = 45 + (i * 0.5) if i < 45 else 85 + (i - 45) * 2
83
- cpu_data.append({
84
- "timestamp": ts.isoformat(),
85
- "value": min(value, 98),
86
- "labels": {"instance": "prod-api-1", "job": "api-server"}
87
- })
 
 
88
 
89
  # Memory metrics
90
  memory_data = []
91
  for i in range(60):
92
  ts = base_time + timedelta(minutes=i)
93
  value = 62 + (i * 0.3)
94
- memory_data.append({
95
- "timestamp": ts.isoformat(),
96
- "value": min(value, 89),
97
- "labels": {"instance": "prod-api-1", "job": "api-server"}
98
- })
 
 
99
 
100
  # Request latency (p99)
101
  latency_data = []
102
  for i in range(60):
103
  ts = base_time + timedelta(minutes=i)
104
  value = 120 if i < 45 else 450 + (i - 45) * 50
105
- latency_data.append({
106
- "timestamp": ts.isoformat(),
107
- "value": min(value, 2500),
108
- "labels": {"instance": "prod-api-1", "endpoint": "/api/v1/users"}
109
- })
 
 
110
 
111
  # Error rate
112
  error_data = []
113
  for i in range(60):
114
  ts = base_time + timedelta(minutes=i)
115
  value = 0.1 if i < 45 else 2.5 + (i - 45) * 0.5
116
- error_data.append({
117
- "timestamp": ts.isoformat(),
118
- "value": min(value, 15),
119
- "labels": {"instance": "prod-api-1", "status_code": "5xx"}
120
- })
121
-
122
- return json.dumps({
123
- "status": "success",
124
- "data": {
125
- "resultType": "matrix",
126
- "result": [
127
- {"metric": {"__name__": "cpu_usage_percent"}, "values": cpu_data},
128
- {"metric": {"__name__": "memory_usage_percent"}, "values": memory_data},
129
- {"metric": {"__name__": "http_request_duration_p99_ms"}, "values": latency_data},
130
- {"metric": {"__name__": "http_errors_rate_percent"}, "values": error_data},
131
- ]
132
- },
133
- "query_time_ms": 127
134
- })
 
 
 
 
 
 
 
135
 
136
 
137
  def generate_logs_response() -> str:
@@ -144,7 +158,11 @@ def generate_logs_response() -> str:
144
  logs = []
145
  log_templates = [
146
  ("ERROR", "Connection pool exhausted, waiting for available connection", "api-server"),
147
- ("WARN", "Slow query detected: SELECT * FROM users WHERE status = 'active' took 2.3s", "api-server"),
 
 
 
 
148
  ("ERROR", "Database connection timeout after 30000ms", "api-server"),
149
  ("INFO", "Retry attempt 1/3 for database connection", "api-server"),
150
  ("ERROR", "Max retries exceeded for database operation", "api-server"),
@@ -164,103 +182,161 @@ def generate_logs_response() -> str:
164
  ts = base_time + timedelta(seconds=i * 45)
165
  level, msg, source = log_templates[i % len(log_templates)]
166
 
167
- logs.append({
168
- "@timestamp": ts.isoformat(),
169
- "level": level,
170
- "message": msg,
171
- "service": source,
172
- "trace_id": f"trace-{1000+i:04d}-abcd-{i:04d}",
173
- "span_id": f"span-{i:04d}",
174
- "host": f"prod-{source}-{i % 3 + 1}",
175
- "environment": "production",
176
- "version": "2.4.1",
177
- "kubernetes": {
178
- "namespace": "production",
179
- "pod": f"{source}-{i % 5 + 1}-abc123",
180
- "container": source,
181
- "node": f"node-{i % 3 + 1}.prod.internal"
182
- },
183
- "request": {
184
- "method": "GET" if i % 2 == 0 else "POST",
185
- "path": "/api/v1/users" if i % 3 == 0 else "/api/v1/orders",
186
- "status_code": 500 if level == "ERROR" else 200,
187
- "duration_ms": 150 + (i * 100) if level != "ERROR" else 30000
 
 
188
  }
189
- })
190
-
191
- return json.dumps({
192
- "took": 234,
193
- "timed_out": False,
194
- "hits": {
195
- "total": {"value": len(logs), "relation": "eq"},
196
- "max_score": 1.0,
197
- "hits": logs
 
 
198
  }
199
- })
200
 
201
 
202
  def generate_service_status() -> str:
203
  """
204
  Realistic health check / service status response.
205
  """
206
- return json.dumps({
207
- "services": [
208
- {
209
- "name": "api-server",
210
- "status": "degraded",
211
- "instances": [
212
- {"id": "api-1", "status": "unhealthy", "cpu": 94, "memory": 87, "connections": 500},
213
- {"id": "api-2", "status": "healthy", "cpu": 45, "memory": 62, "connections": 150},
214
- {"id": "api-3", "status": "unhealthy", "cpu": 91, "memory": 85, "connections": 480},
215
- ],
216
- "last_check": datetime.now().isoformat(),
217
- "error_rate": 12.5,
218
- "p99_latency_ms": 2100
219
- },
220
- {
221
- "name": "database-primary",
222
- "status": "critical",
223
- "instances": [
224
- {"id": "db-primary", "status": "unhealthy", "connections": 500, "max_connections": 500,
225
- "replication_lag_ms": 0, "disk_usage_percent": 78}
226
- ],
227
- "last_check": datetime.now().isoformat(),
228
- "active_queries": 487,
229
- "blocked_queries": 52
230
- },
231
- {
232
- "name": "database-replica",
233
- "status": "healthy",
234
- "instances": [
235
- {"id": "db-replica-1", "status": "healthy", "connections": 120, "max_connections": 500,
236
- "replication_lag_ms": 150, "disk_usage_percent": 76},
237
- {"id": "db-replica-2", "status": "healthy", "connections": 115, "max_connections": 500,
238
- "replication_lag_ms": 180, "disk_usage_percent": 77}
239
- ],
240
- "last_check": datetime.now().isoformat()
241
- },
242
- {
243
- "name": "redis-cache",
244
- "status": "healthy",
245
- "instances": [
246
- {"id": "redis-1", "status": "healthy", "memory_used_mb": 2048, "memory_max_mb": 4096,
247
- "connected_clients": 45, "hit_rate": 0.94}
248
- ],
249
- "last_check": datetime.now().isoformat()
250
- },
251
- {
252
- "name": "nginx-ingress",
253
- "status": "healthy",
254
- "instances": [
255
- {"id": "nginx-1", "status": "healthy", "active_connections": 1250, "requests_per_sec": 450},
256
- {"id": "nginx-2", "status": "healthy", "active_connections": 1180, "requests_per_sec": 420}
257
- ],
258
- "last_check": datetime.now().isoformat()
259
- }
260
- ],
261
- "overall_status": "critical",
262
- "timestamp": datetime.now().isoformat()
263
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
 
266
  def generate_deployments_response() -> str:
@@ -272,84 +348,86 @@ def generate_deployments_response() -> str:
272
  deployments = []
273
  for i in range(15):
274
  ts = base_time - timedelta(hours=i * 4)
275
- deployments.append({
276
- "id": f"deploy-{1000-i}",
277
- "service": "api-server" if i % 3 != 2 else "database-migration",
278
- "version": f"2.4.{15-i}",
279
- "status": "success" if i != 1 else "success", # Recent deploy
280
- "timestamp": ts.isoformat(),
281
- "commit": f"abc{i:04d}def",
282
- "author": f"dev{i % 5 + 1}@company.com",
283
- "message": [
284
- "feat: Add new user endpoint",
285
- "fix: Connection pool sizing",
286
- "chore: Update dependencies",
287
- "feat: Implement caching layer",
288
- "fix: Memory leak in request handler"
289
- ][i % 5],
290
- "changes": {
291
- "files_changed": 5 + i,
292
- "insertions": 100 + i * 20,
293
- "deletions": 30 + i * 5
294
- },
295
- "rollback_available": True,
296
- "canary_status": "completed" if i > 0 else "in_progress"
297
- })
 
 
298
 
299
- return json.dumps({
300
- "deployments": deployments,
301
- "total_count": len(deployments),
302
- "page": 1,
303
- "per_page": 20
304
- })
305
 
306
 
307
  def generate_runbook_response() -> str:
308
  """
309
  Realistic runbook/documentation lookup.
310
  """
311
- return json.dumps({
312
- "runbook": {
313
- "title": "Database Connection Pool Exhaustion",
314
- "id": "RUN-DB-001",
315
- "severity": "P1",
316
- "last_updated": "2024-11-15",
317
- "owner": "platform-team",
318
- "symptoms": [
319
- "High error rate on API endpoints",
320
- "Connection timeout errors in logs",
321
- "Database showing max connections reached",
322
- "Increased latency across all services"
323
- ],
324
- "diagnosis_steps": [
325
- "1. Check current connection count: SELECT count(*) FROM pg_stat_activity",
326
- "2. Identify connection holders: SELECT * FROM pg_stat_activity WHERE state != 'idle'",
327
- "3. Check for long-running queries: SELECT * FROM pg_stat_activity WHERE state = 'active' AND query_start < now() - interval '1 minute'",
328
- "4. Verify connection pool settings in application config",
329
- "5. Check for connection leaks in recent deployments"
330
- ],
331
- "remediation_steps": [
332
- "1. IMMEDIATE: Kill idle connections older than 10 minutes",
333
- "2. IMMEDIATE: Scale up API server replicas to distribute load",
334
- "3. SHORT-TERM: Increase max_connections on database (requires restart)",
335
- "4. SHORT-TERM: Review and optimize connection pool settings",
336
- "5. LONG-TERM: Implement connection pooler (PgBouncer)"
337
- ],
338
- "commands": {
339
- "kill_idle_connections": "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND query_start < now() - interval '10 minutes'",
340
- "check_pool_settings": "kubectl get configmap api-server-config -o yaml | grep -A5 'database'",
341
- "scale_replicas": "kubectl scale deployment api-server --replicas=5"
342
- },
343
- "related_incidents": ["INC-2024-089", "INC-2024-056", "INC-2024-023"],
344
- "escalation_path": ["on-call-sre", "database-team", "platform-lead"]
 
 
345
  }
346
- })
347
 
348
 
349
  # =============================================================================
350
  # BUILD REALISTIC INCIDENT RESPONSE CONVERSATION
351
  # =============================================================================
352
 
 
353
  def build_incident_conversation() -> list[dict]:
354
  """
355
  Build a realistic incident response agentic conversation.
@@ -373,15 +451,13 @@ You have access to the following tools:
373
  - query_deployments: Get recent deployment history
374
  - get_runbook: Lookup runbook documentation
375
 
376
- Always be concise and focus on actionable insights."""
377
  },
378
-
379
  # User reports incident
380
  {
381
  "role": "user",
382
- "content": "We're seeing elevated error rates on the API. Users reporting timeouts. Can you investigate?"
383
  },
384
-
385
  # Agent starts investigation - queries metrics
386
  {
387
  "role": "assistant",
@@ -392,22 +468,18 @@ Always be concise and focus on actionable insights."""
392
  "type": "function",
393
  "function": {
394
  "name": "query_metrics",
395
- "arguments": json.dumps({
396
- "query": "api_error_rate, cpu_usage, memory_usage, latency_p99",
397
- "time_range": "1h"
398
- })
399
- }
 
 
400
  }
401
- ]
402
  },
403
-
404
  # Metrics response
405
- {
406
- "role": "tool",
407
- "tool_call_id": "call_metrics_1",
408
- "content": generate_metrics_response()
409
- },
410
-
411
  # Agent analyzes and queries logs
412
  {
413
  "role": "assistant",
@@ -418,23 +490,19 @@ Always be concise and focus on actionable insights."""
418
  "type": "function",
419
  "function": {
420
  "name": "search_logs",
421
- "arguments": json.dumps({
422
- "query": "level:ERROR OR level:WARN",
423
- "time_range": "30m",
424
- "services": ["api-server", "database"]
425
- })
426
- }
 
 
427
  }
428
- ]
429
  },
430
-
431
  # Logs response
432
- {
433
- "role": "tool",
434
- "tool_call_id": "call_logs_1",
435
- "content": generate_logs_response()
436
- },
437
-
438
  # Agent identifies root cause, checks service status
439
  {
440
  "role": "assistant",
@@ -443,21 +511,12 @@ Always be concise and focus on actionable insights."""
443
  {
444
  "id": "call_status_1",
445
  "type": "function",
446
- "function": {
447
- "name": "get_service_status",
448
- "arguments": json.dumps({})
449
- }
450
  }
451
- ]
452
  },
453
-
454
  # Service status response
455
- {
456
- "role": "tool",
457
- "tool_call_id": "call_status_1",
458
- "content": generate_service_status()
459
- },
460
-
461
  # Agent checks for recent deployments
462
  {
463
  "role": "assistant",
@@ -468,19 +527,17 @@ Always be concise and focus on actionable insights."""
468
  "type": "function",
469
  "function": {
470
  "name": "query_deployments",
471
- "arguments": json.dumps({"service": "api-server", "limit": 15})
472
- }
473
  }
474
- ]
475
  },
476
-
477
  # Deployments response
478
  {
479
  "role": "tool",
480
  "tool_call_id": "call_deploys_1",
481
- "content": generate_deployments_response()
482
  },
483
-
484
  # Agent gets runbook
485
  {
486
  "role": "assistant",
@@ -491,19 +548,13 @@ Always be concise and focus on actionable insights."""
491
  "type": "function",
492
  "function": {
493
  "name": "get_runbook",
494
- "arguments": json.dumps({"search": "database connection pool exhaustion"})
495
- }
496
  }
497
- ]
498
  },
499
-
500
  # Runbook response
501
- {
502
- "role": "tool",
503
- "tool_call_id": "call_runbook_1",
504
- "content": generate_runbook_response()
505
- },
506
-
507
  # Agent provides final analysis
508
  {
509
  "role": "assistant",
@@ -521,13 +572,12 @@ Always be concise and focus on actionable insights."""
521
  2. Scale API servers to 5 replicas to distribute load
522
  3. Consider rolling back deploy-999 if connection pool change is confirmed
523
 
524
- Shall I proceed with the remediation steps?"""
525
  },
526
-
527
  # User asks for action
528
  {
529
  "role": "user",
530
- "content": "Yes, give me the exact commands to run and summarize the incident for the post-mortem."
531
  },
532
  ]
533
 
@@ -538,6 +588,7 @@ Shall I proceed with the remediation steps?"""
538
  # EVALUATION
539
  # =============================================================================
540
 
 
541
  @dataclass
542
  class EvalResult:
543
  mode: str
@@ -548,7 +599,9 @@ class EvalResult:
548
  cost_estimate: float
549
 
550
 
551
- def evaluate_response_quality(baseline_response: str, optimized_response: str, optimization_level: str) -> dict:
 
 
552
  """
553
  Use GPT-4o as judge to evaluate if the optimized response maintains quality.
554
  """
@@ -595,10 +648,14 @@ PASS means overall_score >= 4.0, FAIL means < 4.0."""
595
  )
596
 
597
  import json as json_module
 
598
  try:
599
  return json_module.loads(response.choices[0].message.content)
600
- except:
601
- return {"error": "Failed to parse judge response", "raw": response.choices[0].message.content}
 
 
 
602
 
603
 
604
  def run_eval(messages: list[dict], mode: str, use_aggressive: bool = False) -> EvalResult:
@@ -652,12 +709,18 @@ def main():
652
  print("-" * 70)
653
 
654
  sim_default = client.chat.completions.simulate(model="gpt-4o-mini", messages=messages)
655
- sim_aggressive = aggressive_client.chat.completions.simulate(model="gpt-4o-mini", messages=messages)
 
 
656
 
657
  print(f"\n{'Mode':<15} {'Before':>10} {'After':>10} {'Saved':>10} {'%':>8}")
658
  print("-" * 55)
659
- print(f"{'Default':<15} {sim_default.tokens_before:>10,} {sim_default.tokens_after:>10,} {sim_default.tokens_saved:>10,} {sim_default.tokens_saved/sim_default.tokens_before*100:>7.1f}%")
660
- print(f"{'Aggressive':<15} {sim_aggressive.tokens_before:>10,} {sim_aggressive.tokens_after:>10,} {sim_aggressive.tokens_saved:>10,} {sim_aggressive.tokens_saved/sim_aggressive.tokens_before*100:>7.1f}%")
 
 
 
 
661
  print(f"\nTransforms: {sim_default.transforms}")
662
  print()
663
 
@@ -679,7 +742,9 @@ def main():
679
  print("\n3. AGGRESSIVE OPTIMIZATION...")
680
  aggressive_opt = run_eval(messages, "optimize", use_aggressive=True)
681
  print(f" Tokens: {aggressive_opt.tokens_input:,} in / {aggressive_opt.tokens_output:,} out")
682
- print(f" Cost: ${aggressive_opt.cost_estimate:.6f} | Latency: {aggressive_opt.latency_ms:.0f}ms")
 
 
683
 
684
  # Results table
685
  print()
@@ -694,12 +759,22 @@ def main():
694
 
695
  print(f"\n{'Metric':<20} {'Baseline':>12} {'Default Opt':>12} {'Aggressive':>12}")
696
  print("-" * 60)
697
- print(f"{'Input Tokens':<20} {baseline.tokens_input:>12,} {default_opt.tokens_input:>12,} {aggressive_opt.tokens_input:>12,}")
698
- print(f"{'Output Tokens':<20} {baseline.tokens_output:>12,} {default_opt.tokens_output:>12,} {aggressive_opt.tokens_output:>12,}")
699
- print(f"{'Cost':<20} ${baseline.cost_estimate:>11.6f} ${default_opt.cost_estimate:>11.6f} ${aggressive_opt.cost_estimate:>11.6f}")
700
- print(f"{'Latency (ms)':<20} {baseline.latency_ms:>12.0f} {default_opt.latency_ms:>12.0f} {aggressive_opt.latency_ms:>12.0f}")
 
 
 
 
 
 
 
 
701
  print()
702
- print(f"{'Token Savings':<20} {'-':>12} {def_savings:>10,} ({def_pct:.0f}%) {agg_savings:>10,} ({agg_pct:.0f}%)")
 
 
703
 
704
  # Show responses
705
  print()
@@ -725,7 +800,9 @@ def main():
725
  default_eval = evaluate_response_quality(baseline.response, default_opt.response, "default")
726
 
727
  print("\nEvaluating AGGRESSIVE optimization vs Baseline...")
728
- aggressive_eval = evaluate_response_quality(baseline.response, aggressive_opt.response, "aggressive")
 
 
729
 
730
  print(f"\n{'Criterion':<20} {'Default':>12} {'Aggressive':>12}")
731
  print("-" * 46)
@@ -796,8 +873,8 @@ Cost Impact @ 1K requests/day:
796
  - Monthly savings: ${cost_save_monthly:.2f}
797
 
798
  CONCLUSION:
799
- {'✓ Headroom achieves ' + f'{agg_pct:.0f}% token reduction with PASSING quality scores.' if a_verdict == 'PASS' else '⚠ Aggressive optimization may degrade response quality - use conservative settings.'}
800
- {' The compressed context maintains semantic equivalence for model reasoning.' if a_verdict == 'PASS' else ''}
801
  """)
802
 
803
 
 
27
 
28
  from headroom import HeadroomClient, OpenAIProvider, ToolCrusherConfig
29
  from headroom.config import HeadroomConfig
30
+ from headroom.transforms import TransformPipeline
31
 
32
  load_dotenv(".env.local")
33
 
 
60
  default_mode="audit",
61
  )
62
  aggressive_client._config = aggressive_config
 
63
  aggressive_client._pipeline = TransformPipeline(aggressive_config, provider=provider)
64
 
65
 
 
67
  # REALISTIC TOOL OUTPUTS - Based on actual production systems
68
  # =============================================================================
69
 
70
+
71
  def generate_metrics_response() -> str:
72
  """
73
  Realistic Prometheus/Datadog metrics query response.
 
81
  ts = base_time + timedelta(minutes=i)
82
  # Simulate spike around minute 45
83
  value = 45 + (i * 0.5) if i < 45 else 85 + (i - 45) * 2
84
+ cpu_data.append(
85
+ {
86
+ "timestamp": ts.isoformat(),
87
+ "value": min(value, 98),
88
+ "labels": {"instance": "prod-api-1", "job": "api-server"},
89
+ }
90
+ )
91
 
92
  # Memory metrics
93
  memory_data = []
94
  for i in range(60):
95
  ts = base_time + timedelta(minutes=i)
96
  value = 62 + (i * 0.3)
97
+ memory_data.append(
98
+ {
99
+ "timestamp": ts.isoformat(),
100
+ "value": min(value, 89),
101
+ "labels": {"instance": "prod-api-1", "job": "api-server"},
102
+ }
103
+ )
104
 
105
  # Request latency (p99)
106
  latency_data = []
107
  for i in range(60):
108
  ts = base_time + timedelta(minutes=i)
109
  value = 120 if i < 45 else 450 + (i - 45) * 50
110
+ latency_data.append(
111
+ {
112
+ "timestamp": ts.isoformat(),
113
+ "value": min(value, 2500),
114
+ "labels": {"instance": "prod-api-1", "endpoint": "/api/v1/users"},
115
+ }
116
+ )
117
 
118
  # Error rate
119
  error_data = []
120
  for i in range(60):
121
  ts = base_time + timedelta(minutes=i)
122
  value = 0.1 if i < 45 else 2.5 + (i - 45) * 0.5
123
+ error_data.append(
124
+ {
125
+ "timestamp": ts.isoformat(),
126
+ "value": min(value, 15),
127
+ "labels": {"instance": "prod-api-1", "status_code": "5xx"},
128
+ }
129
+ )
130
+
131
+ return json.dumps(
132
+ {
133
+ "status": "success",
134
+ "data": {
135
+ "resultType": "matrix",
136
+ "result": [
137
+ {"metric": {"__name__": "cpu_usage_percent"}, "values": cpu_data},
138
+ {"metric": {"__name__": "memory_usage_percent"}, "values": memory_data},
139
+ {
140
+ "metric": {"__name__": "http_request_duration_p99_ms"},
141
+ "values": latency_data,
142
+ },
143
+ {"metric": {"__name__": "http_errors_rate_percent"}, "values": error_data},
144
+ ],
145
+ },
146
+ "query_time_ms": 127,
147
+ }
148
+ )
149
 
150
 
151
  def generate_logs_response() -> str:
 
158
  logs = []
159
  log_templates = [
160
  ("ERROR", "Connection pool exhausted, waiting for available connection", "api-server"),
161
+ (
162
+ "WARN",
163
+ "Slow query detected: SELECT * FROM users WHERE status = 'active' took 2.3s",
164
+ "api-server",
165
+ ),
166
  ("ERROR", "Database connection timeout after 30000ms", "api-server"),
167
  ("INFO", "Retry attempt 1/3 for database connection", "api-server"),
168
  ("ERROR", "Max retries exceeded for database operation", "api-server"),
 
182
  ts = base_time + timedelta(seconds=i * 45)
183
  level, msg, source = log_templates[i % len(log_templates)]
184
 
185
+ logs.append(
186
+ {
187
+ "@timestamp": ts.isoformat(),
188
+ "level": level,
189
+ "message": msg,
190
+ "service": source,
191
+ "trace_id": f"trace-{1000 + i:04d}-abcd-{i:04d}",
192
+ "span_id": f"span-{i:04d}",
193
+ "host": f"prod-{source}-{i % 3 + 1}",
194
+ "environment": "production",
195
+ "version": "2.4.1",
196
+ "kubernetes": {
197
+ "namespace": "production",
198
+ "pod": f"{source}-{i % 5 + 1}-abc123",
199
+ "container": source,
200
+ "node": f"node-{i % 3 + 1}.prod.internal",
201
+ },
202
+ "request": {
203
+ "method": "GET" if i % 2 == 0 else "POST",
204
+ "path": "/api/v1/users" if i % 3 == 0 else "/api/v1/orders",
205
+ "status_code": 500 if level == "ERROR" else 200,
206
+ "duration_ms": 150 + (i * 100) if level != "ERROR" else 30000,
207
+ },
208
  }
209
+ )
210
+
211
+ return json.dumps(
212
+ {
213
+ "took": 234,
214
+ "timed_out": False,
215
+ "hits": {
216
+ "total": {"value": len(logs), "relation": "eq"},
217
+ "max_score": 1.0,
218
+ "hits": logs,
219
+ },
220
  }
221
+ )
222
 
223
 
224
  def generate_service_status() -> str:
225
  """
226
  Realistic health check / service status response.
227
  """
228
+ return json.dumps(
229
+ {
230
+ "services": [
231
+ {
232
+ "name": "api-server",
233
+ "status": "degraded",
234
+ "instances": [
235
+ {
236
+ "id": "api-1",
237
+ "status": "unhealthy",
238
+ "cpu": 94,
239
+ "memory": 87,
240
+ "connections": 500,
241
+ },
242
+ {
243
+ "id": "api-2",
244
+ "status": "healthy",
245
+ "cpu": 45,
246
+ "memory": 62,
247
+ "connections": 150,
248
+ },
249
+ {
250
+ "id": "api-3",
251
+ "status": "unhealthy",
252
+ "cpu": 91,
253
+ "memory": 85,
254
+ "connections": 480,
255
+ },
256
+ ],
257
+ "last_check": datetime.now().isoformat(),
258
+ "error_rate": 12.5,
259
+ "p99_latency_ms": 2100,
260
+ },
261
+ {
262
+ "name": "database-primary",
263
+ "status": "critical",
264
+ "instances": [
265
+ {
266
+ "id": "db-primary",
267
+ "status": "unhealthy",
268
+ "connections": 500,
269
+ "max_connections": 500,
270
+ "replication_lag_ms": 0,
271
+ "disk_usage_percent": 78,
272
+ }
273
+ ],
274
+ "last_check": datetime.now().isoformat(),
275
+ "active_queries": 487,
276
+ "blocked_queries": 52,
277
+ },
278
+ {
279
+ "name": "database-replica",
280
+ "status": "healthy",
281
+ "instances": [
282
+ {
283
+ "id": "db-replica-1",
284
+ "status": "healthy",
285
+ "connections": 120,
286
+ "max_connections": 500,
287
+ "replication_lag_ms": 150,
288
+ "disk_usage_percent": 76,
289
+ },
290
+ {
291
+ "id": "db-replica-2",
292
+ "status": "healthy",
293
+ "connections": 115,
294
+ "max_connections": 500,
295
+ "replication_lag_ms": 180,
296
+ "disk_usage_percent": 77,
297
+ },
298
+ ],
299
+ "last_check": datetime.now().isoformat(),
300
+ },
301
+ {
302
+ "name": "redis-cache",
303
+ "status": "healthy",
304
+ "instances": [
305
+ {
306
+ "id": "redis-1",
307
+ "status": "healthy",
308
+ "memory_used_mb": 2048,
309
+ "memory_max_mb": 4096,
310
+ "connected_clients": 45,
311
+ "hit_rate": 0.94,
312
+ }
313
+ ],
314
+ "last_check": datetime.now().isoformat(),
315
+ },
316
+ {
317
+ "name": "nginx-ingress",
318
+ "status": "healthy",
319
+ "instances": [
320
+ {
321
+ "id": "nginx-1",
322
+ "status": "healthy",
323
+ "active_connections": 1250,
324
+ "requests_per_sec": 450,
325
+ },
326
+ {
327
+ "id": "nginx-2",
328
+ "status": "healthy",
329
+ "active_connections": 1180,
330
+ "requests_per_sec": 420,
331
+ },
332
+ ],
333
+ "last_check": datetime.now().isoformat(),
334
+ },
335
+ ],
336
+ "overall_status": "critical",
337
+ "timestamp": datetime.now().isoformat(),
338
+ }
339
+ )
340
 
341
 
342
  def generate_deployments_response() -> str:
 
348
  deployments = []
349
  for i in range(15):
350
  ts = base_time - timedelta(hours=i * 4)
351
+ deployments.append(
352
+ {
353
+ "id": f"deploy-{1000 - i}",
354
+ "service": "api-server" if i % 3 != 2 else "database-migration",
355
+ "version": f"2.4.{15 - i}",
356
+ "status": "success" if i != 1 else "success", # Recent deploy
357
+ "timestamp": ts.isoformat(),
358
+ "commit": f"abc{i:04d}def",
359
+ "author": f"dev{i % 5 + 1}@company.com",
360
+ "message": [
361
+ "feat: Add new user endpoint",
362
+ "fix: Connection pool sizing",
363
+ "chore: Update dependencies",
364
+ "feat: Implement caching layer",
365
+ "fix: Memory leak in request handler",
366
+ ][i % 5],
367
+ "changes": {
368
+ "files_changed": 5 + i,
369
+ "insertions": 100 + i * 20,
370
+ "deletions": 30 + i * 5,
371
+ },
372
+ "rollback_available": True,
373
+ "canary_status": "completed" if i > 0 else "in_progress",
374
+ }
375
+ )
376
 
377
+ return json.dumps(
378
+ {"deployments": deployments, "total_count": len(deployments), "page": 1, "per_page": 20}
379
+ )
 
 
 
380
 
381
 
382
  def generate_runbook_response() -> str:
383
  """
384
  Realistic runbook/documentation lookup.
385
  """
386
+ return json.dumps(
387
+ {
388
+ "runbook": {
389
+ "title": "Database Connection Pool Exhaustion",
390
+ "id": "RUN-DB-001",
391
+ "severity": "P1",
392
+ "last_updated": "2024-11-15",
393
+ "owner": "platform-team",
394
+ "symptoms": [
395
+ "High error rate on API endpoints",
396
+ "Connection timeout errors in logs",
397
+ "Database showing max connections reached",
398
+ "Increased latency across all services",
399
+ ],
400
+ "diagnosis_steps": [
401
+ "1. Check current connection count: SELECT count(*) FROM pg_stat_activity",
402
+ "2. Identify connection holders: SELECT * FROM pg_stat_activity WHERE state != 'idle'",
403
+ "3. Check for long-running queries: SELECT * FROM pg_stat_activity WHERE state = 'active' AND query_start < now() - interval '1 minute'",
404
+ "4. Verify connection pool settings in application config",
405
+ "5. Check for connection leaks in recent deployments",
406
+ ],
407
+ "remediation_steps": [
408
+ "1. IMMEDIATE: Kill idle connections older than 10 minutes",
409
+ "2. IMMEDIATE: Scale up API server replicas to distribute load",
410
+ "3. SHORT-TERM: Increase max_connections on database (requires restart)",
411
+ "4. SHORT-TERM: Review and optimize connection pool settings",
412
+ "5. LONG-TERM: Implement connection pooler (PgBouncer)",
413
+ ],
414
+ "commands": {
415
+ "kill_idle_connections": "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND query_start < now() - interval '10 minutes'",
416
+ "check_pool_settings": "kubectl get configmap api-server-config -o yaml | grep -A5 'database'",
417
+ "scale_replicas": "kubectl scale deployment api-server --replicas=5",
418
+ },
419
+ "related_incidents": ["INC-2024-089", "INC-2024-056", "INC-2024-023"],
420
+ "escalation_path": ["on-call-sre", "database-team", "platform-lead"],
421
+ }
422
  }
423
+ )
424
 
425
 
426
  # =============================================================================
427
  # BUILD REALISTIC INCIDENT RESPONSE CONVERSATION
428
  # =============================================================================
429
 
430
+
431
  def build_incident_conversation() -> list[dict]:
432
  """
433
  Build a realistic incident response agentic conversation.
 
451
  - query_deployments: Get recent deployment history
452
  - get_runbook: Lookup runbook documentation
453
 
454
+ Always be concise and focus on actionable insights.""",
455
  },
 
456
  # User reports incident
457
  {
458
  "role": "user",
459
+ "content": "We're seeing elevated error rates on the API. Users reporting timeouts. Can you investigate?",
460
  },
 
461
  # Agent starts investigation - queries metrics
462
  {
463
  "role": "assistant",
 
468
  "type": "function",
469
  "function": {
470
  "name": "query_metrics",
471
+ "arguments": json.dumps(
472
+ {
473
+ "query": "api_error_rate, cpu_usage, memory_usage, latency_p99",
474
+ "time_range": "1h",
475
+ }
476
+ ),
477
+ },
478
  }
479
+ ],
480
  },
 
481
  # Metrics response
482
+ {"role": "tool", "tool_call_id": "call_metrics_1", "content": generate_metrics_response()},
 
 
 
 
 
483
  # Agent analyzes and queries logs
484
  {
485
  "role": "assistant",
 
490
  "type": "function",
491
  "function": {
492
  "name": "search_logs",
493
+ "arguments": json.dumps(
494
+ {
495
+ "query": "level:ERROR OR level:WARN",
496
+ "time_range": "30m",
497
+ "services": ["api-server", "database"],
498
+ }
499
+ ),
500
+ },
501
  }
502
+ ],
503
  },
 
504
  # Logs response
505
+ {"role": "tool", "tool_call_id": "call_logs_1", "content": generate_logs_response()},
 
 
 
 
 
506
  # Agent identifies root cause, checks service status
507
  {
508
  "role": "assistant",
 
511
  {
512
  "id": "call_status_1",
513
  "type": "function",
514
+ "function": {"name": "get_service_status", "arguments": json.dumps({})},
 
 
 
515
  }
516
+ ],
517
  },
 
518
  # Service status response
519
+ {"role": "tool", "tool_call_id": "call_status_1", "content": generate_service_status()},
 
 
 
 
 
520
  # Agent checks for recent deployments
521
  {
522
  "role": "assistant",
 
527
  "type": "function",
528
  "function": {
529
  "name": "query_deployments",
530
+ "arguments": json.dumps({"service": "api-server", "limit": 15}),
531
+ },
532
  }
533
+ ],
534
  },
 
535
  # Deployments response
536
  {
537
  "role": "tool",
538
  "tool_call_id": "call_deploys_1",
539
+ "content": generate_deployments_response(),
540
  },
 
541
  # Agent gets runbook
542
  {
543
  "role": "assistant",
 
548
  "type": "function",
549
  "function": {
550
  "name": "get_runbook",
551
+ "arguments": json.dumps({"search": "database connection pool exhaustion"}),
552
+ },
553
  }
554
+ ],
555
  },
 
556
  # Runbook response
557
+ {"role": "tool", "tool_call_id": "call_runbook_1", "content": generate_runbook_response()},
 
 
 
 
 
558
  # Agent provides final analysis
559
  {
560
  "role": "assistant",
 
572
  2. Scale API servers to 5 replicas to distribute load
573
  3. Consider rolling back deploy-999 if connection pool change is confirmed
574
 
575
+ Shall I proceed with the remediation steps?""",
576
  },
 
577
  # User asks for action
578
  {
579
  "role": "user",
580
+ "content": "Yes, give me the exact commands to run and summarize the incident for the post-mortem.",
581
  },
582
  ]
583
 
 
588
  # EVALUATION
589
  # =============================================================================
590
 
591
+
592
  @dataclass
593
  class EvalResult:
594
  mode: str
 
599
  cost_estimate: float
600
 
601
 
602
+ def evaluate_response_quality(
603
+ baseline_response: str, optimized_response: str, optimization_level: str
604
+ ) -> dict:
605
  """
606
  Use GPT-4o as judge to evaluate if the optimized response maintains quality.
607
  """
 
648
  )
649
 
650
  import json as json_module
651
+
652
  try:
653
  return json_module.loads(response.choices[0].message.content)
654
+ except Exception:
655
+ return {
656
+ "error": "Failed to parse judge response",
657
+ "raw": response.choices[0].message.content,
658
+ }
659
 
660
 
661
  def run_eval(messages: list[dict], mode: str, use_aggressive: bool = False) -> EvalResult:
 
709
  print("-" * 70)
710
 
711
  sim_default = client.chat.completions.simulate(model="gpt-4o-mini", messages=messages)
712
+ sim_aggressive = aggressive_client.chat.completions.simulate(
713
+ model="gpt-4o-mini", messages=messages
714
+ )
715
 
716
  print(f"\n{'Mode':<15} {'Before':>10} {'After':>10} {'Saved':>10} {'%':>8}")
717
  print("-" * 55)
718
+ print(
719
+ f"{'Default':<15} {sim_default.tokens_before:>10,} {sim_default.tokens_after:>10,} {sim_default.tokens_saved:>10,} {sim_default.tokens_saved / sim_default.tokens_before * 100:>7.1f}%"
720
+ )
721
+ print(
722
+ f"{'Aggressive':<15} {sim_aggressive.tokens_before:>10,} {sim_aggressive.tokens_after:>10,} {sim_aggressive.tokens_saved:>10,} {sim_aggressive.tokens_saved / sim_aggressive.tokens_before * 100:>7.1f}%"
723
+ )
724
  print(f"\nTransforms: {sim_default.transforms}")
725
  print()
726
 
 
742
  print("\n3. AGGRESSIVE OPTIMIZATION...")
743
  aggressive_opt = run_eval(messages, "optimize", use_aggressive=True)
744
  print(f" Tokens: {aggressive_opt.tokens_input:,} in / {aggressive_opt.tokens_output:,} out")
745
+ print(
746
+ f" Cost: ${aggressive_opt.cost_estimate:.6f} | Latency: {aggressive_opt.latency_ms:.0f}ms"
747
+ )
748
 
749
  # Results table
750
  print()
 
759
 
760
  print(f"\n{'Metric':<20} {'Baseline':>12} {'Default Opt':>12} {'Aggressive':>12}")
761
  print("-" * 60)
762
+ print(
763
+ f"{'Input Tokens':<20} {baseline.tokens_input:>12,} {default_opt.tokens_input:>12,} {aggressive_opt.tokens_input:>12,}"
764
+ )
765
+ print(
766
+ f"{'Output Tokens':<20} {baseline.tokens_output:>12,} {default_opt.tokens_output:>12,} {aggressive_opt.tokens_output:>12,}"
767
+ )
768
+ print(
769
+ f"{'Cost':<20} ${baseline.cost_estimate:>11.6f} ${default_opt.cost_estimate:>11.6f} ${aggressive_opt.cost_estimate:>11.6f}"
770
+ )
771
+ print(
772
+ f"{'Latency (ms)':<20} {baseline.latency_ms:>12.0f} {default_opt.latency_ms:>12.0f} {aggressive_opt.latency_ms:>12.0f}"
773
+ )
774
  print()
775
+ print(
776
+ f"{'Token Savings':<20} {'-':>12} {def_savings:>10,} ({def_pct:.0f}%) {agg_savings:>10,} ({agg_pct:.0f}%)"
777
+ )
778
 
779
  # Show responses
780
  print()
 
800
  default_eval = evaluate_response_quality(baseline.response, default_opt.response, "default")
801
 
802
  print("\nEvaluating AGGRESSIVE optimization vs Baseline...")
803
+ aggressive_eval = evaluate_response_quality(
804
+ baseline.response, aggressive_opt.response, "aggressive"
805
+ )
806
 
807
  print(f"\n{'Criterion':<20} {'Default':>12} {'Aggressive':>12}")
808
  print("-" * 46)
 
873
  - Monthly savings: ${cost_save_monthly:.2f}
874
 
875
  CONCLUSION:
876
+ {"✓ Headroom achieves " + f"{agg_pct:.0f}% token reduction with PASSING quality scores." if a_verdict == "PASS" else "⚠ Aggressive optimization may degrade response quality - use conservative settings."}
877
+ {" The compressed context maintains semantic equivalence for model reasoning." if a_verdict == "PASS" else ""}
878
  """)
879
 
880
 
examples/smart_vs_naive_eval.py CHANGED
@@ -20,7 +20,7 @@ from datetime import datetime, timedelta
20
  from dotenv import load_dotenv
21
  from openai import OpenAI
22
 
23
- from headroom import HeadroomClient, OpenAIProvider, ToolCrusherConfig, SmartCrusherConfig
24
  from headroom.config import HeadroomConfig
25
  from headroom.transforms import TransformPipeline
26
 
@@ -91,6 +91,7 @@ baseline_client = HeadroomClient(
91
  # GENERATE TEST DATA WITH CLEAR PATTERNS
92
  # =============================================================================
93
 
 
94
  def generate_metrics_with_spike() -> str:
95
  """
96
  Generate metrics data with a CLEAR spike pattern.
@@ -111,22 +112,20 @@ def generate_metrics_with_spike() -> str:
111
  cpu = 85 + (i - 45) * 2 # Spike: 85 -> 115
112
  error_rate = 5 + (i - 45) # Error spike too
113
 
114
- data_points.append({
115
- "timestamp": ts.isoformat(),
116
- "host": "prod-api-1", # CONSTANT - should be factored out
117
- "region": "us-east-1", # CONSTANT - should be factored out
118
- "datacenter": "dc-01", # CONSTANT - should be factored out
119
- "cpu_percent": min(cpu, 99),
120
- "memory_percent": 62, # CONSTANT
121
- "error_rate": round(error_rate, 2),
122
- "request_count": 1500 + (i * 10),
123
- })
 
 
124
 
125
- return json.dumps({
126
- "status": "success",
127
- "metrics": data_points,
128
- "query_time_ms": 127
129
- })
130
 
131
 
132
  def generate_clusterable_logs() -> str:
@@ -161,21 +160,20 @@ def generate_clusterable_logs() -> str:
161
  ts = base_time + timedelta(seconds=i * 36)
162
  level, msg = message_templates[i % len(message_templates)]
163
 
164
- logs.append({
165
- "@timestamp": ts.isoformat(),
166
- "level": level,
167
- "message": msg,
168
- "service": "api-server", # CONSTANT
169
- "environment": "production", # CONSTANT
170
- "version": "2.4.1", # CONSTANT
171
- "host": f"prod-api-{i % 3 + 1}",
172
- "trace_id": f"trace-{1000+i:04d}",
173
- })
 
 
174
 
175
- return json.dumps({
176
- "took": 234,
177
- "hits": {"total": len(logs), "hits": logs}
178
- })
179
 
180
 
181
  def generate_search_results() -> str:
@@ -185,14 +183,16 @@ def generate_search_results() -> str:
185
  """
186
  results = []
187
  for i in range(30):
188
- results.append({
189
- "id": f"doc-{i+1}",
190
- "title": f"Result document {i+1}",
191
- "snippet": f"This is the snippet for document {i+1} with relevant content...",
192
- "score": 0.95 - (i * 0.02), # Decreasing relevance
193
- "source": "knowledge_base", # CONSTANT
194
- "category": "technical", # CONSTANT
195
- })
 
 
196
 
197
  return json.dumps({"results": results, "total": 30})
198
 
@@ -201,6 +201,7 @@ def generate_search_results() -> str:
201
  # BUILD TEST CONVERSATION
202
  # =============================================================================
203
 
 
204
  def build_test_conversation() -> list[dict]:
205
  """Build a conversation that exercises all SmartCrusher strategies."""
206
 
@@ -208,53 +209,47 @@ def build_test_conversation() -> list[dict]:
208
  {
209
  "role": "system",
210
  "content": """You are an SRE assistant. Analyze the data and provide insights.
211
- Current Date: 2024-12-15T14:30:00Z"""
212
  },
213
  {"role": "user", "content": "Check the metrics for the last hour."},
214
  {
215
  "role": "assistant",
216
  "content": None,
217
- "tool_calls": [{
218
- "id": "call_1",
219
- "type": "function",
220
- "function": {"name": "query_metrics", "arguments": "{}"}
221
- }]
222
- },
223
- {
224
- "role": "tool",
225
- "tool_call_id": "call_1",
226
- "content": generate_metrics_with_spike()
227
  },
 
228
  {"role": "assistant", "content": "I see CPU metrics. Let me check the logs."},
229
  {
230
  "role": "assistant",
231
  "content": None,
232
- "tool_calls": [{
233
- "id": "call_2",
234
- "type": "function",
235
- "function": {"name": "search_logs", "arguments": "{}"}
236
- }]
237
- },
238
- {
239
- "role": "tool",
240
- "tool_call_id": "call_2",
241
- "content": generate_clusterable_logs()
242
  },
 
243
  {"role": "assistant", "content": "Found error patterns. Let me search docs."},
244
  {
245
  "role": "assistant",
246
  "content": None,
247
- "tool_calls": [{
248
- "id": "call_3",
249
- "type": "function",
250
- "function": {"name": "search_docs", "arguments": "{}"}
251
- }]
252
- },
253
- {
254
- "role": "tool",
255
- "tool_call_id": "call_3",
256
- "content": generate_search_results()
257
  },
 
258
  {"role": "user", "content": "What's the root cause and what should we do?"},
259
  ]
260
 
@@ -265,6 +260,7 @@ Current Date: 2024-12-15T14:30:00Z"""
265
  # EVALUATION
266
  # =============================================================================
267
 
 
268
  @dataclass
269
  class EvalResult:
270
  name: str
@@ -299,7 +295,9 @@ def evaluate(client, messages: list[dict], name: str, mode: str) -> EvalResult:
299
  tokens_before=sim.tokens_before,
300
  tokens_after=tokens_in,
301
  tokens_saved=sim.tokens_before - tokens_in,
302
- reduction_pct=(sim.tokens_before - tokens_in) / sim.tokens_before * 100 if sim.tokens_before else 0,
 
 
303
  transforms=sim.transforms,
304
  response=response.choices[0].message.content or "",
305
  latency_ms=latency,
@@ -341,7 +339,7 @@ PASS = overall >= 4.0"""
341
 
342
  try:
343
  return json.loads(response.choices[0].message.content)
344
- except:
345
  return {"error": "Parse failed"}
346
 
347
 
@@ -380,12 +378,16 @@ def main():
380
 
381
  print("\n2. NAIVE CRUSHER (fixed rules: keep first 10)...")
382
  naive = evaluate(naive_client, messages, "Naive", "optimize")
383
- print(f" Tokens: {naive.tokens_after:,} (saved {naive.tokens_saved:,}, {naive.reduction_pct:.1f}%)")
 
 
384
  print(f" Transforms: {naive.transforms}")
385
 
386
  print("\n3. SMART CRUSHER (statistical analysis)...")
387
  smart = evaluate(smart_client, messages, "Smart", "optimize")
388
- print(f" Tokens: {smart.tokens_after:,} (saved {smart.tokens_saved:,}, {smart.reduction_pct:.1f}%)")
 
 
389
  print(f" Transforms: {smart.transforms}")
390
 
391
  # Results comparison
@@ -397,17 +399,25 @@ def main():
397
  print(f"\n{'Method':<20} {'Tokens':>10} {'Saved':>10} {'Reduction':>10}")
398
  print("-" * 52)
399
  print(f"{'Baseline':<20} {baseline.tokens_after:>10,} {'-':>10} {'-':>10}")
400
- print(f"{'Naive Crusher':<20} {naive.tokens_after:>10,} {naive.tokens_saved:>10,} {naive.reduction_pct:>9.1f}%")
401
- print(f"{'Smart Crusher':<20} {smart.tokens_after:>10,} {smart.tokens_saved:>10,} {smart.reduction_pct:>9.1f}%")
 
 
 
 
402
 
403
  # Show the difference
404
  diff = naive.tokens_after - smart.tokens_after
405
  if diff > 0:
406
- print(f"\n→ Smart Crusher saves {diff:,} MORE tokens than Naive ({diff/naive.tokens_after*100:.1f}% better)")
 
 
407
  elif diff < 0:
408
- print(f"\n→ Naive Crusher saves {-diff:,} MORE tokens than Smart ({-diff/smart.tokens_after*100:.1f}% better)")
 
 
409
  else:
410
- print(f"\n→ Both methods produce same token count")
411
 
412
  # Quality evaluation
413
  print()
@@ -429,8 +439,12 @@ def main():
429
  s_score = smart_quality.get(criterion, {}).get("score", "?")
430
  print(f"{criterion.replace('_', ' ').title():<20} {n_score:>10}/5 {s_score:>10}/5")
431
  print("-" * 42)
432
- print(f"{'OVERALL':<20} {naive_quality.get('overall', '?'):>10}/5 {smart_quality.get('overall', '?'):>10}/5")
433
- print(f"{'VERDICT':<20} {naive_quality.get('verdict', '?'):>10} {smart_quality.get('verdict', '?'):>10}")
 
 
 
 
434
 
435
  print("\n[Quality Analysis]")
436
  print(f" Naive: {naive_quality.get('data_awareness', {}).get('reason', 'N/A')}")
@@ -453,12 +467,12 @@ SmartCrusher vs NaiveCrusher on SRE incident data:
453
  Token Efficiency:
454
  - Naive: {naive.reduction_pct:.1f}% reduction
455
  - Smart: {smart.reduction_pct:.1f}% reduction
456
- - Winner: {'SMART' if smart.reduction_pct > naive.reduction_pct else 'NAIVE' if naive.reduction_pct > smart.reduction_pct else 'TIE'} (+{abs(smart.reduction_pct - naive.reduction_pct):.1f}% {'more' if smart.reduction_pct > naive.reduction_pct else 'less'} reduction)
457
 
458
  Response Quality:
459
  - Naive: {n_overall}/5 ({n_verdict})
460
  - Smart: {s_overall}/5 ({s_verdict})
461
- - Winner: {'SMART' if s_overall > n_overall else 'NAIVE' if n_overall > s_overall else 'TIE'}
462
 
463
  Key Insight:
464
  SmartCrusher uses statistical analysis to preserve important data:
 
20
  from dotenv import load_dotenv
21
  from openai import OpenAI
22
 
23
+ from headroom import HeadroomClient, OpenAIProvider, SmartCrusherConfig, ToolCrusherConfig
24
  from headroom.config import HeadroomConfig
25
  from headroom.transforms import TransformPipeline
26
 
 
91
  # GENERATE TEST DATA WITH CLEAR PATTERNS
92
  # =============================================================================
93
 
94
+
95
  def generate_metrics_with_spike() -> str:
96
  """
97
  Generate metrics data with a CLEAR spike pattern.
 
112
  cpu = 85 + (i - 45) * 2 # Spike: 85 -> 115
113
  error_rate = 5 + (i - 45) # Error spike too
114
 
115
+ data_points.append(
116
+ {
117
+ "timestamp": ts.isoformat(),
118
+ "host": "prod-api-1", # CONSTANT - should be factored out
119
+ "region": "us-east-1", # CONSTANT - should be factored out
120
+ "datacenter": "dc-01", # CONSTANT - should be factored out
121
+ "cpu_percent": min(cpu, 99),
122
+ "memory_percent": 62, # CONSTANT
123
+ "error_rate": round(error_rate, 2),
124
+ "request_count": 1500 + (i * 10),
125
+ }
126
+ )
127
 
128
+ return json.dumps({"status": "success", "metrics": data_points, "query_time_ms": 127})
 
 
 
 
129
 
130
 
131
  def generate_clusterable_logs() -> str:
 
160
  ts = base_time + timedelta(seconds=i * 36)
161
  level, msg = message_templates[i % len(message_templates)]
162
 
163
+ logs.append(
164
+ {
165
+ "@timestamp": ts.isoformat(),
166
+ "level": level,
167
+ "message": msg,
168
+ "service": "api-server", # CONSTANT
169
+ "environment": "production", # CONSTANT
170
+ "version": "2.4.1", # CONSTANT
171
+ "host": f"prod-api-{i % 3 + 1}",
172
+ "trace_id": f"trace-{1000 + i:04d}",
173
+ }
174
+ )
175
 
176
+ return json.dumps({"took": 234, "hits": {"total": len(logs), "hits": logs}})
 
 
 
177
 
178
 
179
  def generate_search_results() -> str:
 
183
  """
184
  results = []
185
  for i in range(30):
186
+ results.append(
187
+ {
188
+ "id": f"doc-{i + 1}",
189
+ "title": f"Result document {i + 1}",
190
+ "snippet": f"This is the snippet for document {i + 1} with relevant content...",
191
+ "score": 0.95 - (i * 0.02), # Decreasing relevance
192
+ "source": "knowledge_base", # CONSTANT
193
+ "category": "technical", # CONSTANT
194
+ }
195
+ )
196
 
197
  return json.dumps({"results": results, "total": 30})
198
 
 
201
  # BUILD TEST CONVERSATION
202
  # =============================================================================
203
 
204
+
205
  def build_test_conversation() -> list[dict]:
206
  """Build a conversation that exercises all SmartCrusher strategies."""
207
 
 
209
  {
210
  "role": "system",
211
  "content": """You are an SRE assistant. Analyze the data and provide insights.
212
+ Current Date: 2024-12-15T14:30:00Z""",
213
  },
214
  {"role": "user", "content": "Check the metrics for the last hour."},
215
  {
216
  "role": "assistant",
217
  "content": None,
218
+ "tool_calls": [
219
+ {
220
+ "id": "call_1",
221
+ "type": "function",
222
+ "function": {"name": "query_metrics", "arguments": "{}"},
223
+ }
224
+ ],
 
 
 
225
  },
226
+ {"role": "tool", "tool_call_id": "call_1", "content": generate_metrics_with_spike()},
227
  {"role": "assistant", "content": "I see CPU metrics. Let me check the logs."},
228
  {
229
  "role": "assistant",
230
  "content": None,
231
+ "tool_calls": [
232
+ {
233
+ "id": "call_2",
234
+ "type": "function",
235
+ "function": {"name": "search_logs", "arguments": "{}"},
236
+ }
237
+ ],
 
 
 
238
  },
239
+ {"role": "tool", "tool_call_id": "call_2", "content": generate_clusterable_logs()},
240
  {"role": "assistant", "content": "Found error patterns. Let me search docs."},
241
  {
242
  "role": "assistant",
243
  "content": None,
244
+ "tool_calls": [
245
+ {
246
+ "id": "call_3",
247
+ "type": "function",
248
+ "function": {"name": "search_docs", "arguments": "{}"},
249
+ }
250
+ ],
 
 
 
251
  },
252
+ {"role": "tool", "tool_call_id": "call_3", "content": generate_search_results()},
253
  {"role": "user", "content": "What's the root cause and what should we do?"},
254
  ]
255
 
 
260
  # EVALUATION
261
  # =============================================================================
262
 
263
+
264
  @dataclass
265
  class EvalResult:
266
  name: str
 
295
  tokens_before=sim.tokens_before,
296
  tokens_after=tokens_in,
297
  tokens_saved=sim.tokens_before - tokens_in,
298
+ reduction_pct=(sim.tokens_before - tokens_in) / sim.tokens_before * 100
299
+ if sim.tokens_before
300
+ else 0,
301
  transforms=sim.transforms,
302
  response=response.choices[0].message.content or "",
303
  latency_ms=latency,
 
339
 
340
  try:
341
  return json.loads(response.choices[0].message.content)
342
+ except Exception:
343
  return {"error": "Parse failed"}
344
 
345
 
 
378
 
379
  print("\n2. NAIVE CRUSHER (fixed rules: keep first 10)...")
380
  naive = evaluate(naive_client, messages, "Naive", "optimize")
381
+ print(
382
+ f" Tokens: {naive.tokens_after:,} (saved {naive.tokens_saved:,}, {naive.reduction_pct:.1f}%)"
383
+ )
384
  print(f" Transforms: {naive.transforms}")
385
 
386
  print("\n3. SMART CRUSHER (statistical analysis)...")
387
  smart = evaluate(smart_client, messages, "Smart", "optimize")
388
+ print(
389
+ f" Tokens: {smart.tokens_after:,} (saved {smart.tokens_saved:,}, {smart.reduction_pct:.1f}%)"
390
+ )
391
  print(f" Transforms: {smart.transforms}")
392
 
393
  # Results comparison
 
399
  print(f"\n{'Method':<20} {'Tokens':>10} {'Saved':>10} {'Reduction':>10}")
400
  print("-" * 52)
401
  print(f"{'Baseline':<20} {baseline.tokens_after:>10,} {'-':>10} {'-':>10}")
402
+ print(
403
+ f"{'Naive Crusher':<20} {naive.tokens_after:>10,} {naive.tokens_saved:>10,} {naive.reduction_pct:>9.1f}%"
404
+ )
405
+ print(
406
+ f"{'Smart Crusher':<20} {smart.tokens_after:>10,} {smart.tokens_saved:>10,} {smart.reduction_pct:>9.1f}%"
407
+ )
408
 
409
  # Show the difference
410
  diff = naive.tokens_after - smart.tokens_after
411
  if diff > 0:
412
+ print(
413
+ f"\n→ Smart Crusher saves {diff:,} MORE tokens than Naive ({diff / naive.tokens_after * 100:.1f}% better)"
414
+ )
415
  elif diff < 0:
416
+ print(
417
+ f"\n→ Naive Crusher saves {-diff:,} MORE tokens than Smart ({-diff / smart.tokens_after * 100:.1f}% better)"
418
+ )
419
  else:
420
+ print("\n→ Both methods produce same token count")
421
 
422
  # Quality evaluation
423
  print()
 
439
  s_score = smart_quality.get(criterion, {}).get("score", "?")
440
  print(f"{criterion.replace('_', ' ').title():<20} {n_score:>10}/5 {s_score:>10}/5")
441
  print("-" * 42)
442
+ print(
443
+ f"{'OVERALL':<20} {naive_quality.get('overall', '?'):>10}/5 {smart_quality.get('overall', '?'):>10}/5"
444
+ )
445
+ print(
446
+ f"{'VERDICT':<20} {naive_quality.get('verdict', '?'):>10} {smart_quality.get('verdict', '?'):>10}"
447
+ )
448
 
449
  print("\n[Quality Analysis]")
450
  print(f" Naive: {naive_quality.get('data_awareness', {}).get('reason', 'N/A')}")
 
467
  Token Efficiency:
468
  - Naive: {naive.reduction_pct:.1f}% reduction
469
  - Smart: {smart.reduction_pct:.1f}% reduction
470
+ - Winner: {"SMART" if smart.reduction_pct > naive.reduction_pct else "NAIVE" if naive.reduction_pct > smart.reduction_pct else "TIE"} (+{abs(smart.reduction_pct - naive.reduction_pct):.1f}% {"more" if smart.reduction_pct > naive.reduction_pct else "less"} reduction)
471
 
472
  Response Quality:
473
  - Naive: {n_overall}/5 ({n_verdict})
474
  - Smart: {s_overall}/5 ({s_verdict})
475
+ - Winner: {"SMART" if s_overall > n_overall else "NAIVE" if n_overall > s_overall else "TIE"}
476
 
477
  Key Insight:
478
  SmartCrusher uses statistical analysis to preserve important data:
headroom/cache/__init__.py CHANGED
@@ -25,6 +25,7 @@ Usage:
25
  CacheOptimizerRegistry.register("my-provider", MyOptimizer)
26
  """
27
 
 
28
  from .base import (
29
  BaseCacheOptimizer,
30
  CacheBreakpoint,
@@ -42,11 +43,10 @@ from .dynamic_detector import (
42
  DynamicSpan,
43
  detect_dynamic_content,
44
  )
45
- from .registry import CacheOptimizerRegistry
46
- from .anthropic import AnthropicCacheOptimizer
47
- from .openai import OpenAICacheOptimizer
48
  from .google import GoogleCacheOptimizer
49
- from .semantic import SemanticCacheLayer, SemanticCache
 
 
50
 
51
  __all__ = [
52
  # Base types
 
25
  CacheOptimizerRegistry.register("my-provider", MyOptimizer)
26
  """
27
 
28
+ from .anthropic import AnthropicCacheOptimizer
29
  from .base import (
30
  BaseCacheOptimizer,
31
  CacheBreakpoint,
 
43
  DynamicSpan,
44
  detect_dynamic_content,
45
  )
 
 
 
46
  from .google import GoogleCacheOptimizer
47
+ from .openai import OpenAICacheOptimizer
48
+ from .registry import CacheOptimizerRegistry
49
+ from .semantic import SemanticCache, SemanticCacheLayer
50
 
51
  __all__ = [
52
  # Base types
headroom/cache/anthropic.py CHANGED
@@ -39,7 +39,6 @@ from .base import (
39
  OptimizationContext,
40
  )
41
 
42
-
43
  # Anthropic-specific constants
44
  ANTHROPIC_MIN_CACHEABLE_TOKENS = 1024
45
  ANTHROPIC_MAX_BREAKPOINTS = 4
@@ -147,13 +146,9 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
147
  warnings.extend(plan.warnings)
148
 
149
  # Step 4: Insert cache_control blocks
150
- optimized_messages = self._insert_breakpoints(
151
- optimized_messages, plan.breakpoints
152
- )
153
  if plan.breakpoints:
154
- transforms_applied.append(
155
- f"inserted_{len(plan.breakpoints)}_cache_breakpoints"
156
- )
157
 
158
  # Step 5: Compute metrics
159
  prefix_content = self._extract_cacheable_content(optimized_messages)
@@ -194,9 +189,7 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
194
  warnings=warnings,
195
  )
196
 
197
- def _analyze_sections(
198
- self, messages: list[dict[str, Any]]
199
- ) -> list[ContentSection]:
200
  """Analyze messages to identify distinct content sections."""
201
  sections: list[ContentSection] = []
202
 
@@ -207,9 +200,13 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
207
  if role == "system":
208
  section_type = "system"
209
  elif role == "user":
210
- section_type = "examples" if self._looks_like_example(message, messages, idx) else "user"
 
 
211
  elif role == "assistant":
212
- section_type = "examples" if self._looks_like_example(message, messages, idx) else "assistant"
 
 
213
  else:
214
  section_type = role
215
 
@@ -227,17 +224,17 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
227
 
228
  if isinstance(content, str):
229
  token_count = self._count_tokens_estimate(content)
230
- is_cacheable, reason = self._assess_cacheability(
231
- section_type, token_count, content
 
 
 
 
 
 
 
 
232
  )
233
- sections.append(ContentSection(
234
- content=content,
235
- section_type=section_type,
236
- message_index=idx,
237
- token_count=token_count,
238
- is_cacheable=is_cacheable,
239
- reason=reason,
240
- ))
241
 
242
  elif isinstance(content, list):
243
  for block_idx, block in enumerate(content):
@@ -247,15 +244,17 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
247
  is_cacheable, reason = self._assess_cacheability(
248
  section_type, token_count, text
249
  )
250
- sections.append(ContentSection(
251
- content=block,
252
- section_type=section_type,
253
- message_index=idx,
254
- content_index=block_idx,
255
- token_count=token_count,
256
- is_cacheable=is_cacheable,
257
- reason=reason,
258
- ))
 
 
259
 
260
  return sections
261
 
@@ -264,7 +263,10 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
264
  ) -> tuple[bool, str]:
265
  """Assess whether a section is cacheable."""
266
  if token_count < self.config.min_cacheable_tokens:
267
- return False, f"Below minimum tokens ({token_count} < {self.config.min_cacheable_tokens})"
 
 
 
268
 
269
  if section_type == "system":
270
  return True, "System prompts are highly cacheable"
@@ -318,6 +320,7 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
318
  def _estimate_tools_tokens(self, tools: Any) -> int:
319
  """Estimate token count for tool definitions."""
320
  import json
 
321
  try:
322
  return self._count_tokens_estimate(json.dumps(tools))
323
  except (TypeError, ValueError):
@@ -353,9 +356,7 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
353
 
354
  return messages, transforms
355
 
356
- def _stabilize_text(
357
- self, text: str, config: CacheConfig
358
- ) -> tuple[str, list[str]]:
359
  """Stabilize a text string."""
360
  transforms: list[str] = []
361
  result = text
@@ -408,9 +409,7 @@ class AnthropicCacheOptimizer(BaseCacheOptimizer):
408
 
409
  for section in cacheable:
410
  if len(selected) >= config.max_breakpoints:
411
- plan.warnings.append(
412
- f"Reached maximum breakpoints ({config.max_breakpoints})"
413
- )
414
  break
415
 
416
  selected.append(section)
 
39
  OptimizationContext,
40
  )
41
 
 
42
  # Anthropic-specific constants
43
  ANTHROPIC_MIN_CACHEABLE_TOKENS = 1024
44
  ANTHROPIC_MAX_BREAKPOINTS = 4
 
146
  warnings.extend(plan.warnings)
147
 
148
  # Step 4: Insert cache_control blocks
149
+ optimized_messages = self._insert_breakpoints(optimized_messages, plan.breakpoints)
 
 
150
  if plan.breakpoints:
151
+ transforms_applied.append(f"inserted_{len(plan.breakpoints)}_cache_breakpoints")
 
 
152
 
153
  # Step 5: Compute metrics
154
  prefix_content = self._extract_cacheable_content(optimized_messages)
 
189
  warnings=warnings,
190
  )
191
 
192
+ def _analyze_sections(self, messages: list[dict[str, Any]]) -> list[ContentSection]:
 
 
193
  """Analyze messages to identify distinct content sections."""
194
  sections: list[ContentSection] = []
195
 
 
200
  if role == "system":
201
  section_type = "system"
202
  elif role == "user":
203
+ section_type = (
204
+ "examples" if self._looks_like_example(message, messages, idx) else "user"
205
+ )
206
  elif role == "assistant":
207
+ section_type = (
208
+ "examples" if self._looks_like_example(message, messages, idx) else "assistant"
209
+ )
210
  else:
211
  section_type = role
212
 
 
224
 
225
  if isinstance(content, str):
226
  token_count = self._count_tokens_estimate(content)
227
+ is_cacheable, reason = self._assess_cacheability(section_type, token_count, content)
228
+ sections.append(
229
+ ContentSection(
230
+ content=content,
231
+ section_type=section_type,
232
+ message_index=idx,
233
+ token_count=token_count,
234
+ is_cacheable=is_cacheable,
235
+ reason=reason,
236
+ )
237
  )
 
 
 
 
 
 
 
 
238
 
239
  elif isinstance(content, list):
240
  for block_idx, block in enumerate(content):
 
244
  is_cacheable, reason = self._assess_cacheability(
245
  section_type, token_count, text
246
  )
247
+ sections.append(
248
+ ContentSection(
249
+ content=block,
250
+ section_type=section_type,
251
+ message_index=idx,
252
+ content_index=block_idx,
253
+ token_count=token_count,
254
+ is_cacheable=is_cacheable,
255
+ reason=reason,
256
+ )
257
+ )
258
 
259
  return sections
260
 
 
263
  ) -> tuple[bool, str]:
264
  """Assess whether a section is cacheable."""
265
  if token_count < self.config.min_cacheable_tokens:
266
+ return (
267
+ False,
268
+ f"Below minimum tokens ({token_count} < {self.config.min_cacheable_tokens})",
269
+ )
270
 
271
  if section_type == "system":
272
  return True, "System prompts are highly cacheable"
 
320
  def _estimate_tools_tokens(self, tools: Any) -> int:
321
  """Estimate token count for tool definitions."""
322
  import json
323
+
324
  try:
325
  return self._count_tokens_estimate(json.dumps(tools))
326
  except (TypeError, ValueError):
 
356
 
357
  return messages, transforms
358
 
359
+ def _stabilize_text(self, text: str, config: CacheConfig) -> tuple[str, list[str]]:
 
 
360
  """Stabilize a text string."""
361
  transforms: list[str] = []
362
  result = text
 
409
 
410
  for section in cacheable:
411
  if len(selected) >= config.max_breakpoints:
412
+ plan.warnings.append(f"Reached maximum breakpoints ({config.max_breakpoints})")
 
 
413
  break
414
 
415
  selected.append(section)
headroom/cache/base.py CHANGED
@@ -82,11 +82,13 @@ class CacheConfig:
82
  max_breakpoints: int = 4
83
 
84
  # Patterns to extract and move to dynamic section
85
- date_patterns: list[str] = field(default_factory=lambda: [
86
- r"Today is \w+ \d{1,2},? \d{4}\.?",
87
- r"Current date: \d{4}-\d{2}-\d{2}",
88
- r"The current time is .+\.",
89
- ])
 
 
90
 
91
  # Whether to normalize whitespace
92
  normalize_whitespace: bool = True
@@ -317,6 +319,7 @@ class BaseCacheOptimizer(ABC):
317
  def _compute_prefix_hash(self, content: str) -> str:
318
  """Compute a short hash of content."""
319
  import hashlib
 
320
  return hashlib.sha256(content.encode()).hexdigest()[:12]
321
 
322
  def _extract_system_content(self, messages: list[dict[str, Any]]) -> str:
 
82
  max_breakpoints: int = 4
83
 
84
  # Patterns to extract and move to dynamic section
85
+ date_patterns: list[str] = field(
86
+ default_factory=lambda: [
87
+ r"Today is \w+ \d{1,2},? \d{4}\.?",
88
+ r"Current date: \d{4}-\d{2}-\d{2}",
89
+ r"The current time is .+\.",
90
+ ]
91
+ )
92
 
93
  # Whether to normalize whitespace
94
  normalize_whitespace: bool = True
 
319
  def _compute_prefix_hash(self, content: str) -> str:
320
  """Compute a short hash of content."""
321
  import hashlib
322
+
323
  return hashlib.sha256(content.encode()).hexdigest()[:12]
324
 
325
  def _extract_system_content(self, messages: list[dict[str, Any]]) -> str:
headroom/cache/compression_feedback.py CHANGED
@@ -30,7 +30,6 @@ from __future__ import annotations
30
  import re
31
  import threading
32
  import time
33
- from collections import defaultdict
34
  from dataclasses import dataclass, field
35
  from typing import TYPE_CHECKING, Any
36
 
@@ -185,7 +184,9 @@ class CompressionFeedback:
185
  # Time-based tracking
186
  self._last_analysis: float = 0.0
187
  self._analysis_interval: float = analysis_interval
188
- self._last_event_timestamp: float = 0.0 # Track last processed event to avoid double-counting
 
 
189
 
190
  # Global statistics
191
  self._total_compressions: int = 0
@@ -196,6 +197,7 @@ class CompressionFeedback:
196
  """Get the compression store (lazy load global if not set)."""
197
  if self._store is None:
198
  from .compression_store import get_compression_store
 
199
  self._store = get_compression_store()
200
  return self._store
201
 
@@ -301,9 +303,7 @@ class CompressionFeedback:
301
  # Track query patterns
302
  if event.query:
303
  query_lower = event.query.lower()
304
- pattern.common_queries[query_lower] = (
305
- pattern.common_queries.get(query_lower, 0) + 1
306
- )
307
 
308
  # HIGH: Limit common_queries dict to prevent unbounded growth
309
  if len(pattern.common_queries) > 100:
@@ -325,32 +325,32 @@ class CompressionFeedback:
325
  from both dicts, then truncate both to the same key set.
326
  """
327
  # Get top 40 strategies from each dict (using 40 to allow union to stay under 50)
328
- top_compressions = set(
329
- k for k, _ in sorted(
 
330
  pattern.strategy_compressions.items(),
331
  key=lambda x: x[1],
332
  reverse=True,
333
  )[:40]
334
- )
335
- top_retrievals = set(
336
- k for k, _ in sorted(
 
337
  pattern.strategy_retrievals.items(),
338
  key=lambda x: x[1],
339
  reverse=True,
340
  )[:40]
341
- )
342
 
343
  # Keep union of top strategies from both
344
  keys_to_keep = top_compressions | top_retrievals
345
 
346
  # Truncate both dicts to same keys
347
  pattern.strategy_compressions = {
348
- k: v for k, v in pattern.strategy_compressions.items()
349
- if k in keys_to_keep
350
  }
351
  pattern.strategy_retrievals = {
352
- k: v for k, v in pattern.strategy_retrievals.items()
353
- if k in keys_to_keep
354
  }
355
 
356
  def _extract_field_hints(self, pattern: LocalToolPattern, query: str) -> None:
@@ -361,22 +361,30 @@ class CompressionFeedback:
361
  - JSON field names like "status", "error", "id"
362
  """
363
  # Look for field:value patterns
364
- field_patterns = re.findall(r'(\w+)[=:]', query)
365
- for field in field_patterns:
366
- pattern.queried_fields[field] = (
367
- pattern.queried_fields.get(field, 0) + 1
368
- )
369
 
370
  # Look for common JSON field names
371
  common_fields = [
372
- "id", "name", "status", "error", "message", "type",
373
- "code", "result", "value", "data", "items", "count",
 
 
 
 
 
 
 
 
 
 
374
  ]
375
  query_lower = query.lower()
376
- for field in common_fields:
377
- if field in query_lower:
378
- pattern.queried_fields[field] = (
379
- pattern.queried_fields.get(field, 0) + 1
380
  )
381
 
382
  # HIGH: Limit queried_fields dict to prevent unbounded growth
@@ -459,8 +467,7 @@ class CompressionFeedback:
459
  hints.suggested_items = 10
460
  hints.aggressiveness = 0.7
461
  hints.reason = (
462
- f"Low retrieval rate ({retrieval_rate:.0%}), "
463
- f"current compression is effective"
464
  )
465
 
466
  # Add field preservation hints based on common queries
@@ -488,6 +495,7 @@ class CompressionFeedback:
488
  HIGH FIX: Returns deep copies to prevent external mutation of internal state.
489
  """
490
  import copy as copy_module
 
491
  with self._lock:
492
  # Deep copy to prevent external code from modifying internal state
493
  return copy_module.deepcopy(self._tool_patterns)
@@ -504,7 +512,8 @@ class CompressionFeedback:
504
  "total_retrievals": self._total_retrievals,
505
  "global_retrieval_rate": (
506
  self._total_retrievals / self._total_compressions
507
- if self._total_compressions > 0 else 0.0
 
508
  ),
509
  "tools_tracked": len(self._tool_patterns),
510
  "tool_patterns": {
 
30
  import re
31
  import threading
32
  import time
 
33
  from dataclasses import dataclass, field
34
  from typing import TYPE_CHECKING, Any
35
 
 
184
  # Time-based tracking
185
  self._last_analysis: float = 0.0
186
  self._analysis_interval: float = analysis_interval
187
+ self._last_event_timestamp: float = (
188
+ 0.0 # Track last processed event to avoid double-counting
189
+ )
190
 
191
  # Global statistics
192
  self._total_compressions: int = 0
 
197
  """Get the compression store (lazy load global if not set)."""
198
  if self._store is None:
199
  from .compression_store import get_compression_store
200
+
201
  self._store = get_compression_store()
202
  return self._store
203
 
 
303
  # Track query patterns
304
  if event.query:
305
  query_lower = event.query.lower()
306
+ pattern.common_queries[query_lower] = pattern.common_queries.get(query_lower, 0) + 1
 
 
307
 
308
  # HIGH: Limit common_queries dict to prevent unbounded growth
309
  if len(pattern.common_queries) > 100:
 
325
  from both dicts, then truncate both to the same key set.
326
  """
327
  # Get top 40 strategies from each dict (using 40 to allow union to stay under 50)
328
+ top_compressions = {
329
+ k
330
+ for k, _ in sorted(
331
  pattern.strategy_compressions.items(),
332
  key=lambda x: x[1],
333
  reverse=True,
334
  )[:40]
335
+ }
336
+ top_retrievals = {
337
+ k
338
+ for k, _ in sorted(
339
  pattern.strategy_retrievals.items(),
340
  key=lambda x: x[1],
341
  reverse=True,
342
  )[:40]
343
+ }
344
 
345
  # Keep union of top strategies from both
346
  keys_to_keep = top_compressions | top_retrievals
347
 
348
  # Truncate both dicts to same keys
349
  pattern.strategy_compressions = {
350
+ k: v for k, v in pattern.strategy_compressions.items() if k in keys_to_keep
 
351
  }
352
  pattern.strategy_retrievals = {
353
+ k: v for k, v in pattern.strategy_retrievals.items() if k in keys_to_keep
 
354
  }
355
 
356
  def _extract_field_hints(self, pattern: LocalToolPattern, query: str) -> None:
 
361
  - JSON field names like "status", "error", "id"
362
  """
363
  # Look for field:value patterns
364
+ field_patterns = re.findall(r"(\w+)[=:]", query)
365
+ for field_name in field_patterns:
366
+ pattern.queried_fields[field_name] = pattern.queried_fields.get(field_name, 0) + 1
 
 
367
 
368
  # Look for common JSON field names
369
  common_fields = [
370
+ "id",
371
+ "name",
372
+ "status",
373
+ "error",
374
+ "message",
375
+ "type",
376
+ "code",
377
+ "result",
378
+ "value",
379
+ "data",
380
+ "items",
381
+ "count",
382
  ]
383
  query_lower = query.lower()
384
+ for common_field in common_fields:
385
+ if common_field in query_lower:
386
+ pattern.queried_fields[common_field] = (
387
+ pattern.queried_fields.get(common_field, 0) + 1
388
  )
389
 
390
  # HIGH: Limit queried_fields dict to prevent unbounded growth
 
467
  hints.suggested_items = 10
468
  hints.aggressiveness = 0.7
469
  hints.reason = (
470
+ f"Low retrieval rate ({retrieval_rate:.0%}), current compression is effective"
 
471
  )
472
 
473
  # Add field preservation hints based on common queries
 
495
  HIGH FIX: Returns deep copies to prevent external mutation of internal state.
496
  """
497
  import copy as copy_module
498
+
499
  with self._lock:
500
  # Deep copy to prevent external code from modifying internal state
501
  return copy_module.deepcopy(self._tool_patterns)
 
512
  "total_retrievals": self._total_retrievals,
513
  "global_retrieval_rate": (
514
  self._total_retrievals / self._total_compressions
515
+ if self._total_compressions > 0
516
+ else 0.0
517
  ),
518
  "tools_tracked": len(self._tool_patterns),
519
  "tool_patterns": {
headroom/cache/compression_store.py CHANGED
@@ -33,7 +33,6 @@ Usage:
33
 
34
  from __future__ import annotations
35
 
36
- import copy
37
  import hashlib
38
  import heapq
39
  import json
@@ -231,8 +230,7 @@ class CompressionStore:
231
  # True hash collision - different content, same hash
232
  # This is extremely rare with SHA256[:24] but should be logged
233
  logger.warning(
234
- "Hash collision detected: hash=%s tool=%s "
235
- "(existing_len=%d, new_len=%d)",
236
  hash_key,
237
  tool_name,
238
  len(existing.original_content),
@@ -437,15 +435,9 @@ class CompressionStore:
437
  # Clean expired entries
438
  self._clean_expired()
439
 
440
- total_original_tokens = sum(
441
- e.original_tokens for e in self._store.values()
442
- )
443
- total_compressed_tokens = sum(
444
- e.compressed_tokens for e in self._store.values()
445
- )
446
- total_retrievals = sum(
447
- e.retrieval_count for e in self._store.values()
448
- )
449
 
450
  return {
451
  "entry_count": len(self._store),
@@ -534,10 +526,7 @@ class CompressionStore:
534
 
535
  CRITICAL FIX: Track stale heap entries when deleting to prevent memory leak.
536
  """
537
- expired_keys = [
538
- key for key, entry in self._store.items()
539
- if entry.is_expired()
540
- ]
541
  for key in expired_keys:
542
  del self._store[key]
543
  # CRITICAL FIX: Increment stale counter - the heap still has an entry
@@ -552,8 +541,7 @@ class CompressionStore:
552
  """
553
  # Build new heap from current store entries only
554
  self._eviction_heap = [
555
- (entry.created_at, hash_key)
556
- for hash_key, entry in self._store.items()
557
  ]
558
  heapq.heapify(self._eviction_heap)
559
  # Reset stale counter - heap is now clean
@@ -630,7 +618,7 @@ class CompressionStore:
630
 
631
  # Keep only recent events
632
  if len(self._retrieval_events) > self._max_events:
633
- self._retrieval_events = self._retrieval_events[-self._max_events:]
634
 
635
  # Queue event for feedback processing (will be processed after lock release)
636
  # This is safe because process_pending_feedback() uses the lock to atomically
@@ -648,9 +636,9 @@ class CompressionStore:
648
  This is called automatically on each retrieval to ensure the
649
  feedback loop operates in real-time.
650
  """
651
- from .compression_feedback import get_compression_feedback
652
  from ..telemetry import get_telemetry_collector
653
  from ..telemetry.toin import get_toin
 
654
 
655
  # Get pending events and related entry data atomically
656
  with self._lock:
@@ -664,12 +652,14 @@ class CompressionStore:
664
  if entry:
665
  # Use the ACTUAL tool_signature_hash stored during compression
666
  # This MUST match the hash used by SmartCrusher
667
- event_data.append((
668
- event,
669
- entry.tool_name,
670
- entry.tool_signature_hash, # The correct hash!
671
- entry.compression_strategy,
672
- ))
 
 
673
  else:
674
  event_data.append((event, None, None, None))
675
 
@@ -679,7 +669,7 @@ class CompressionStore:
679
  telemetry = get_telemetry_collector()
680
  toin = get_toin()
681
 
682
- for event, tool_name, sig_hash, strategy in event_data:
683
  # Notify feedback system (pass strategy for success rate tracking)
684
  feedback.record_retrieval(event, strategy=strategy)
685
 
@@ -687,7 +677,7 @@ class CompressionStore:
687
  query_fields = None
688
  if event.query:
689
  # Extract field:value patterns
690
- query_fields = re.findall(r'(\w+)[=:]', event.query)
691
 
692
  # Notify telemetry for data flywheel
693
  try:
 
33
 
34
  from __future__ import annotations
35
 
 
36
  import hashlib
37
  import heapq
38
  import json
 
230
  # True hash collision - different content, same hash
231
  # This is extremely rare with SHA256[:24] but should be logged
232
  logger.warning(
233
+ "Hash collision detected: hash=%s tool=%s (existing_len=%d, new_len=%d)",
 
234
  hash_key,
235
  tool_name,
236
  len(existing.original_content),
 
435
  # Clean expired entries
436
  self._clean_expired()
437
 
438
+ total_original_tokens = sum(e.original_tokens for e in self._store.values())
439
+ total_compressed_tokens = sum(e.compressed_tokens for e in self._store.values())
440
+ total_retrievals = sum(e.retrieval_count for e in self._store.values())
 
 
 
 
 
 
441
 
442
  return {
443
  "entry_count": len(self._store),
 
526
 
527
  CRITICAL FIX: Track stale heap entries when deleting to prevent memory leak.
528
  """
529
+ expired_keys = [key for key, entry in self._store.items() if entry.is_expired()]
 
 
 
530
  for key in expired_keys:
531
  del self._store[key]
532
  # CRITICAL FIX: Increment stale counter - the heap still has an entry
 
541
  """
542
  # Build new heap from current store entries only
543
  self._eviction_heap = [
544
+ (entry.created_at, hash_key) for hash_key, entry in self._store.items()
 
545
  ]
546
  heapq.heapify(self._eviction_heap)
547
  # Reset stale counter - heap is now clean
 
618
 
619
  # Keep only recent events
620
  if len(self._retrieval_events) > self._max_events:
621
+ self._retrieval_events = self._retrieval_events[-self._max_events :]
622
 
623
  # Queue event for feedback processing (will be processed after lock release)
624
  # This is safe because process_pending_feedback() uses the lock to atomically
 
636
  This is called automatically on each retrieval to ensure the
637
  feedback loop operates in real-time.
638
  """
 
639
  from ..telemetry import get_telemetry_collector
640
  from ..telemetry.toin import get_toin
641
+ from .compression_feedback import get_compression_feedback
642
 
643
  # Get pending events and related entry data atomically
644
  with self._lock:
 
652
  if entry:
653
  # Use the ACTUAL tool_signature_hash stored during compression
654
  # This MUST match the hash used by SmartCrusher
655
+ event_data.append(
656
+ (
657
+ event,
658
+ entry.tool_name,
659
+ entry.tool_signature_hash, # The correct hash!
660
+ entry.compression_strategy,
661
+ )
662
+ )
663
  else:
664
  event_data.append((event, None, None, None))
665
 
 
669
  telemetry = get_telemetry_collector()
670
  toin = get_toin()
671
 
672
+ for event, _tool_name, sig_hash, strategy in event_data:
673
  # Notify feedback system (pass strategy for success rate tracking)
674
  feedback.record_retrieval(event, strategy=strategy)
675
 
 
677
  query_fields = None
678
  if event.query:
679
  # Extract field:value patterns
680
+ query_fields = re.findall(r"(\w+)[=:]", event.query)
681
 
682
  # Notify telemetry for data flywheel
683
  try:
headroom/cache/dynamic_detector.py CHANGED
@@ -44,13 +44,15 @@ _SENTENCE_TRANSFORMERS_AVAILABLE = False
44
 
45
  try:
46
  import spacy
 
47
  _SPACY_AVAILABLE = True
48
  except ImportError:
49
  spacy = None # type: ignore
50
 
51
  try:
52
- from sentence_transformers import SentenceTransformer
53
  import numpy as np
 
 
54
  _SENTENCE_TRANSFORMERS_AVAILABLE = True
55
  except ImportError:
56
  SentenceTransformer = None # type: ignore
@@ -138,35 +140,77 @@ class DetectorConfig:
138
  """Configuration for the dynamic content detector."""
139
 
140
  # Which tiers to enable (order matters - later tiers can use earlier results)
141
- tiers: list[Literal["regex", "ner", "semantic"]] = field(
142
- default_factory=lambda: ["regex"]
143
- )
144
 
145
  # Tier 1: Structural labels that indicate dynamic content
146
  # These are the KEY names that hint the VALUE is dynamic
147
  # Users can add domain-specific labels
148
- dynamic_labels: list[str] = field(default_factory=lambda: [
149
- # Time-related
150
- "date", "time", "timestamp", "datetime", "created", "updated",
151
- "modified", "expires", "last", "current", "today", "now",
152
- # Identifiers
153
- "id", "uuid", "guid", "session", "request", "trace", "span",
154
- "transaction", "correlation", "token", "key", "secret",
155
- # User-related
156
- "user", "username", "email", "name", "phone", "address",
157
- "customer", "client", "employee", "member",
158
- # System state
159
- "version", "build", "commit", "branch", "revision",
160
- "status", "state", "count", "total", "balance", "remaining",
161
- "load", "queue", "active", "pending",
162
- # Order/ticket related
163
- "order", "ticket", "case", "invoice", "reference",
164
- ])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  # Tier 1: Custom regex patterns (user-provided)
167
- custom_patterns: list[tuple[str, DynamicCategory]] = field(
168
- default_factory=list
169
- )
170
 
171
  # Entropy threshold for detecting random strings (0-1 scale normalized)
172
  # Higher = more selective (only very random strings)
@@ -237,48 +281,47 @@ class RegexDetector:
237
  # Universal patterns (these formats are language-agnostic)
238
  UNIVERSAL_PATTERNS = [
239
  # UUID - truly universal format
240
- (r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
241
- DynamicCategory.UUID, "uuid"),
242
-
 
 
243
  # ISO 8601 datetime (most universal date format)
244
- (r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?",
245
- DynamicCategory.DATETIME, "iso_datetime"),
246
-
 
 
247
  # ISO 8601 date only
248
- (r"\d{4}-\d{2}-\d{2}(?!\d)",
249
- DynamicCategory.DATE, "iso_date"),
250
-
251
  # Unix timestamps (10-13 digits, but NOT within longer numbers)
252
- (r"(?<![0-9])\d{10,13}(?![0-9])",
253
- DynamicCategory.TIMESTAMP, "unix_timestamp"),
254
-
255
  # 24-hour time HH:MM:SS or HH:MM
256
- (r"(?<![0-9])\d{1,2}:\d{2}(?::\d{2})?(?:\s*(?:AM|PM|am|pm))?(?![0-9])",
257
- DynamicCategory.TIME, "time"),
258
-
 
 
259
  # Version numbers with v prefix (unambiguous)
260
- (r"\bv\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?",
261
- DynamicCategory.VERSION, "version"),
262
-
263
  # API key/token patterns (prefix + random string)
264
- (r"\b(?:sk|pk|api|key|token|bearer|auth)[-_][a-zA-Z0-9]{16,}",
265
- DynamicCategory.REQUEST_ID, "api_key"),
266
-
 
 
267
  # Common prefixed IDs (req_, sess_, txn_, etc.)
268
- (r"\b[a-z]{2,6}_[a-zA-Z0-9]{8,}",
269
- DynamicCategory.REQUEST_ID, "prefixed_id"),
270
-
271
  # Hex strings of common ID lengths (32 = MD5, 40 = SHA1, 64 = SHA256)
272
- (r"\b[a-fA-F0-9]{32}\b",
273
- DynamicCategory.IDENTIFIER, "hex_32"),
274
- (r"\b[a-fA-F0-9]{40}\b",
275
- DynamicCategory.IDENTIFIER, "hex_40"),
276
- (r"\b[a-fA-F0-9]{64}\b",
277
- DynamicCategory.IDENTIFIER, "hex_64"),
278
-
279
  # JWT tokens (three base64 sections separated by dots)
280
- (r"eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+",
281
- DynamicCategory.REQUEST_ID, "jwt"),
 
 
 
282
  ]
283
 
284
  def __init__(self, config: DetectorConfig):
@@ -296,7 +339,7 @@ class RegexDetector:
296
  labels_pattern = "|".join(re.escape(label) for label in config.dynamic_labels)
297
  self._structural_pattern = re.compile(
298
  rf"(?P<label>(?:{labels_pattern}))(?P<sep>\s*[:=]\s*|\s+)(?P<value>[^\n,;]+)",
299
- re.IGNORECASE
300
  )
301
 
302
  # Compile custom patterns
@@ -319,15 +362,17 @@ class RegexDetector:
319
  if end - start < self.config.min_span_length:
320
  continue
321
 
322
- spans.append(DynamicSpan(
323
- text=match.group(),
324
- start=start,
325
- end=end,
326
- category=category,
327
- tier="regex",
328
- confidence=1.0,
329
- metadata={"pattern": pattern_name, "method": "universal"},
330
- ))
 
 
331
  seen_ranges.add((start, end))
332
 
333
  # 2. Structural detection: "Label: value" patterns
@@ -355,15 +400,17 @@ class RegexDetector:
355
  if not value.strip():
356
  continue
357
 
358
- spans.append(DynamicSpan(
359
- text=value,
360
- start=value_start,
361
- end=value_end,
362
- category=category,
363
- tier="regex",
364
- confidence=0.9,
365
- metadata={"pattern": "structural", "method": "structural", "label": label},
366
- ))
 
 
367
  seen_ranges.add((value_start, value_end))
368
 
369
  # 3. Entropy-based detection for remaining potential IDs
@@ -378,15 +425,17 @@ class RegexDetector:
378
  if end - start < self.config.min_span_length:
379
  continue
380
 
381
- spans.append(DynamicSpan(
382
- text=match.group(),
383
- start=start,
384
- end=end,
385
- category=category,
386
- tier="regex",
387
- confidence=0.8,
388
- metadata={"pattern": "custom", "method": "custom"},
389
- ))
 
 
390
  seen_ranges.add((start, end))
391
 
392
  return sorted(spans, key=lambda s: s.start)
@@ -432,15 +481,17 @@ class RegexDetector:
432
  entropy = calculate_entropy(text)
433
 
434
  if entropy >= self.config.entropy_threshold:
435
- spans.append(DynamicSpan(
436
- text=text,
437
- start=start,
438
- end=end,
439
- category=DynamicCategory.IDENTIFIER,
440
- tier="regex",
441
- confidence=entropy, # Use entropy as confidence
442
- metadata={"pattern": "entropy", "method": "entropy", "entropy": entropy},
443
- ))
 
 
444
  seen_ranges.add((start, end))
445
 
446
  return spans
@@ -452,10 +503,7 @@ class RegexDetector:
452
  seen_ranges: set[tuple[int, int]],
453
  ) -> bool:
454
  """Check if range overlaps with any existing range."""
455
- return any(
456
- not (end <= s or start >= e)
457
- for s, e in seen_ranges
458
- )
459
 
460
  def _categorize_label(self, label: str) -> DynamicCategory:
461
  """Categorize based on the label name."""
@@ -478,15 +526,35 @@ class RegexDetector:
478
  return DynamicCategory.REQUEST_ID
479
 
480
  # User-related
481
- if label in {"user", "username", "email", "name", "phone", "address",
482
- "customer", "client", "employee", "member"}:
 
 
 
 
 
 
 
 
 
 
483
  return DynamicCategory.USER_DATA
484
 
485
  # System state
486
  if label in {"version", "build", "commit", "branch", "revision"}:
487
  return DynamicCategory.VERSION
488
- if label in {"status", "state", "count", "total", "balance", "remaining",
489
- "load", "queue", "active", "pending"}:
 
 
 
 
 
 
 
 
 
 
490
  return DynamicCategory.VOLATILE
491
 
492
  # Order/ticket
@@ -570,8 +638,7 @@ class NERDetector:
570
 
571
  # Check for overlap with existing spans
572
  overlaps = any(
573
- not (ent.end_char <= s or ent.start_char >= e)
574
- for s, e in existing_ranges
575
  )
576
  if overlaps:
577
  continue
@@ -583,15 +650,17 @@ class NERDetector:
583
  if category == DynamicCategory.UNKNOWN:
584
  continue
585
 
586
- spans.append(DynamicSpan(
587
- text=ent.text,
588
- start=ent.start_char,
589
- end=ent.end_char,
590
- category=category,
591
- tier="ner",
592
- confidence=0.9,
593
- metadata={"entity_type": ent.label_},
594
- ))
 
 
595
  existing_ranges.add((ent.start_char, ent.end_char))
596
 
597
  return sorted(spans, key=lambda s: s.start), None
@@ -611,18 +680,15 @@ class SemanticDetector:
611
  "Real-time data",
612
  "Live prices",
613
  "Current stock price",
614
-
615
  # Session-specific
616
  "Your session ID",
617
  "Your account balance",
618
  "Your recent orders",
619
  "Your conversation history",
620
-
621
  # User-specific
622
  "Hello [user]",
623
  "Dear customer",
624
  "Your name is",
625
-
626
  # System state
627
  "Server status",
628
  "System load",
@@ -707,10 +773,7 @@ class SemanticDetector:
707
  continue
708
 
709
  # Check overlap with existing spans
710
- overlaps = any(
711
- not (end <= s or start >= e)
712
- for s, e in existing_ranges
713
- )
714
  if overlaps:
715
  continue
716
 
@@ -721,18 +784,20 @@ class SemanticDetector:
721
  # Determine category based on exemplar
722
  category = self._categorize_exemplar(best_exemplar)
723
 
724
- spans.append(DynamicSpan(
725
- text=text,
726
- start=start,
727
- end=end,
728
- category=category,
729
- tier="semantic",
730
- confidence=max_sim,
731
- metadata={
732
- "matched_exemplar": best_exemplar,
733
- "similarity": max_sim,
734
- },
735
- ))
 
 
736
  existing_ranges.add((start, end))
737
 
738
  return sorted(spans, key=lambda s: s.start), None
@@ -740,7 +805,7 @@ class SemanticDetector:
740
  def _split_sentences(self, content: str) -> list[tuple[str, int, int]]:
741
  """Split content into sentences with positions."""
742
  sentences: list[tuple[str, int, int]] = []
743
- pattern = r'[^.!?\n]+[.!?\n]?'
744
  for match in re.finditer(pattern, content):
745
  text = match.group().strip()
746
  if len(text) > 10:
@@ -820,6 +885,7 @@ class DynamicContentDetector:
820
  DetectionResult with spans, static/dynamic content split, etc.
821
  """
822
  import time
 
823
  start_time = time.perf_counter()
824
 
825
  all_spans: list[DynamicSpan] = []
@@ -881,7 +947,7 @@ class DynamicContentDetector:
881
 
882
  for span in reversed(spans):
883
  dynamic_parts.append(span.text)
884
- static = static[:span.start] + static[span.end:]
885
 
886
  static = self._clean_static_content(static)
887
  dynamic_parts.reverse()
 
44
 
45
  try:
46
  import spacy
47
+
48
  _SPACY_AVAILABLE = True
49
  except ImportError:
50
  spacy = None # type: ignore
51
 
52
  try:
 
53
  import numpy as np
54
+ from sentence_transformers import SentenceTransformer
55
+
56
  _SENTENCE_TRANSFORMERS_AVAILABLE = True
57
  except ImportError:
58
  SentenceTransformer = None # type: ignore
 
140
  """Configuration for the dynamic content detector."""
141
 
142
  # Which tiers to enable (order matters - later tiers can use earlier results)
143
+ tiers: list[Literal["regex", "ner", "semantic"]] = field(default_factory=lambda: ["regex"])
 
 
144
 
145
  # Tier 1: Structural labels that indicate dynamic content
146
  # These are the KEY names that hint the VALUE is dynamic
147
  # Users can add domain-specific labels
148
+ dynamic_labels: list[str] = field(
149
+ default_factory=lambda: [
150
+ # Time-related
151
+ "date",
152
+ "time",
153
+ "timestamp",
154
+ "datetime",
155
+ "created",
156
+ "updated",
157
+ "modified",
158
+ "expires",
159
+ "last",
160
+ "current",
161
+ "today",
162
+ "now",
163
+ # Identifiers
164
+ "id",
165
+ "uuid",
166
+ "guid",
167
+ "session",
168
+ "request",
169
+ "trace",
170
+ "span",
171
+ "transaction",
172
+ "correlation",
173
+ "token",
174
+ "key",
175
+ "secret",
176
+ # User-related
177
+ "user",
178
+ "username",
179
+ "email",
180
+ "name",
181
+ "phone",
182
+ "address",
183
+ "customer",
184
+ "client",
185
+ "employee",
186
+ "member",
187
+ # System state
188
+ "version",
189
+ "build",
190
+ "commit",
191
+ "branch",
192
+ "revision",
193
+ "status",
194
+ "state",
195
+ "count",
196
+ "total",
197
+ "balance",
198
+ "remaining",
199
+ "load",
200
+ "queue",
201
+ "active",
202
+ "pending",
203
+ # Order/ticket related
204
+ "order",
205
+ "ticket",
206
+ "case",
207
+ "invoice",
208
+ "reference",
209
+ ]
210
+ )
211
 
212
  # Tier 1: Custom regex patterns (user-provided)
213
+ custom_patterns: list[tuple[str, DynamicCategory]] = field(default_factory=list)
 
 
214
 
215
  # Entropy threshold for detecting random strings (0-1 scale normalized)
216
  # Higher = more selective (only very random strings)
 
281
  # Universal patterns (these formats are language-agnostic)
282
  UNIVERSAL_PATTERNS = [
283
  # UUID - truly universal format
284
+ (
285
+ r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
286
+ DynamicCategory.UUID,
287
+ "uuid",
288
+ ),
289
  # ISO 8601 datetime (most universal date format)
290
+ (
291
+ r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?",
292
+ DynamicCategory.DATETIME,
293
+ "iso_datetime",
294
+ ),
295
  # ISO 8601 date only
296
+ (r"\d{4}-\d{2}-\d{2}(?!\d)", DynamicCategory.DATE, "iso_date"),
 
 
297
  # Unix timestamps (10-13 digits, but NOT within longer numbers)
298
+ (r"(?<![0-9])\d{10,13}(?![0-9])", DynamicCategory.TIMESTAMP, "unix_timestamp"),
 
 
299
  # 24-hour time HH:MM:SS or HH:MM
300
+ (
301
+ r"(?<![0-9])\d{1,2}:\d{2}(?::\d{2})?(?:\s*(?:AM|PM|am|pm))?(?![0-9])",
302
+ DynamicCategory.TIME,
303
+ "time",
304
+ ),
305
  # Version numbers with v prefix (unambiguous)
306
+ (r"\bv\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?", DynamicCategory.VERSION, "version"),
 
 
307
  # API key/token patterns (prefix + random string)
308
+ (
309
+ r"\b(?:sk|pk|api|key|token|bearer|auth)[-_][a-zA-Z0-9]{16,}",
310
+ DynamicCategory.REQUEST_ID,
311
+ "api_key",
312
+ ),
313
  # Common prefixed IDs (req_, sess_, txn_, etc.)
314
+ (r"\b[a-z]{2,6}_[a-zA-Z0-9]{8,}", DynamicCategory.REQUEST_ID, "prefixed_id"),
 
 
315
  # Hex strings of common ID lengths (32 = MD5, 40 = SHA1, 64 = SHA256)
316
+ (r"\b[a-fA-F0-9]{32}\b", DynamicCategory.IDENTIFIER, "hex_32"),
317
+ (r"\b[a-fA-F0-9]{40}\b", DynamicCategory.IDENTIFIER, "hex_40"),
318
+ (r"\b[a-fA-F0-9]{64}\b", DynamicCategory.IDENTIFIER, "hex_64"),
 
 
 
 
319
  # JWT tokens (three base64 sections separated by dots)
320
+ (
321
+ r"eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+",
322
+ DynamicCategory.REQUEST_ID,
323
+ "jwt",
324
+ ),
325
  ]
326
 
327
  def __init__(self, config: DetectorConfig):
 
339
  labels_pattern = "|".join(re.escape(label) for label in config.dynamic_labels)
340
  self._structural_pattern = re.compile(
341
  rf"(?P<label>(?:{labels_pattern}))(?P<sep>\s*[:=]\s*|\s+)(?P<value>[^\n,;]+)",
342
+ re.IGNORECASE,
343
  )
344
 
345
  # Compile custom patterns
 
362
  if end - start < self.config.min_span_length:
363
  continue
364
 
365
+ spans.append(
366
+ DynamicSpan(
367
+ text=match.group(),
368
+ start=start,
369
+ end=end,
370
+ category=category,
371
+ tier="regex",
372
+ confidence=1.0,
373
+ metadata={"pattern": pattern_name, "method": "universal"},
374
+ )
375
+ )
376
  seen_ranges.add((start, end))
377
 
378
  # 2. Structural detection: "Label: value" patterns
 
400
  if not value.strip():
401
  continue
402
 
403
+ spans.append(
404
+ DynamicSpan(
405
+ text=value,
406
+ start=value_start,
407
+ end=value_end,
408
+ category=category,
409
+ tier="regex",
410
+ confidence=0.9,
411
+ metadata={"pattern": "structural", "method": "structural", "label": label},
412
+ )
413
+ )
414
  seen_ranges.add((value_start, value_end))
415
 
416
  # 3. Entropy-based detection for remaining potential IDs
 
425
  if end - start < self.config.min_span_length:
426
  continue
427
 
428
+ spans.append(
429
+ DynamicSpan(
430
+ text=match.group(),
431
+ start=start,
432
+ end=end,
433
+ category=category,
434
+ tier="regex",
435
+ confidence=0.8,
436
+ metadata={"pattern": "custom", "method": "custom"},
437
+ )
438
+ )
439
  seen_ranges.add((start, end))
440
 
441
  return sorted(spans, key=lambda s: s.start)
 
481
  entropy = calculate_entropy(text)
482
 
483
  if entropy >= self.config.entropy_threshold:
484
+ spans.append(
485
+ DynamicSpan(
486
+ text=text,
487
+ start=start,
488
+ end=end,
489
+ category=DynamicCategory.IDENTIFIER,
490
+ tier="regex",
491
+ confidence=entropy, # Use entropy as confidence
492
+ metadata={"pattern": "entropy", "method": "entropy", "entropy": entropy},
493
+ )
494
+ )
495
  seen_ranges.add((start, end))
496
 
497
  return spans
 
503
  seen_ranges: set[tuple[int, int]],
504
  ) -> bool:
505
  """Check if range overlaps with any existing range."""
506
+ return any(not (end <= s or start >= e) for s, e in seen_ranges)
 
 
 
507
 
508
  def _categorize_label(self, label: str) -> DynamicCategory:
509
  """Categorize based on the label name."""
 
526
  return DynamicCategory.REQUEST_ID
527
 
528
  # User-related
529
+ if label in {
530
+ "user",
531
+ "username",
532
+ "email",
533
+ "name",
534
+ "phone",
535
+ "address",
536
+ "customer",
537
+ "client",
538
+ "employee",
539
+ "member",
540
+ }:
541
  return DynamicCategory.USER_DATA
542
 
543
  # System state
544
  if label in {"version", "build", "commit", "branch", "revision"}:
545
  return DynamicCategory.VERSION
546
+ if label in {
547
+ "status",
548
+ "state",
549
+ "count",
550
+ "total",
551
+ "balance",
552
+ "remaining",
553
+ "load",
554
+ "queue",
555
+ "active",
556
+ "pending",
557
+ }:
558
  return DynamicCategory.VOLATILE
559
 
560
  # Order/ticket
 
638
 
639
  # Check for overlap with existing spans
640
  overlaps = any(
641
+ not (ent.end_char <= s or ent.start_char >= e) for s, e in existing_ranges
 
642
  )
643
  if overlaps:
644
  continue
 
650
  if category == DynamicCategory.UNKNOWN:
651
  continue
652
 
653
+ spans.append(
654
+ DynamicSpan(
655
+ text=ent.text,
656
+ start=ent.start_char,
657
+ end=ent.end_char,
658
+ category=category,
659
+ tier="ner",
660
+ confidence=0.9,
661
+ metadata={"entity_type": ent.label_},
662
+ )
663
+ )
664
  existing_ranges.add((ent.start_char, ent.end_char))
665
 
666
  return sorted(spans, key=lambda s: s.start), None
 
680
  "Real-time data",
681
  "Live prices",
682
  "Current stock price",
 
683
  # Session-specific
684
  "Your session ID",
685
  "Your account balance",
686
  "Your recent orders",
687
  "Your conversation history",
 
688
  # User-specific
689
  "Hello [user]",
690
  "Dear customer",
691
  "Your name is",
 
692
  # System state
693
  "Server status",
694
  "System load",
 
773
  continue
774
 
775
  # Check overlap with existing spans
776
+ overlaps = any(not (end <= s or start >= e) for s, e in existing_ranges)
 
 
 
777
  if overlaps:
778
  continue
779
 
 
784
  # Determine category based on exemplar
785
  category = self._categorize_exemplar(best_exemplar)
786
 
787
+ spans.append(
788
+ DynamicSpan(
789
+ text=text,
790
+ start=start,
791
+ end=end,
792
+ category=category,
793
+ tier="semantic",
794
+ confidence=max_sim,
795
+ metadata={
796
+ "matched_exemplar": best_exemplar,
797
+ "similarity": max_sim,
798
+ },
799
+ )
800
+ )
801
  existing_ranges.add((start, end))
802
 
803
  return sorted(spans, key=lambda s: s.start), None
 
805
  def _split_sentences(self, content: str) -> list[tuple[str, int, int]]:
806
  """Split content into sentences with positions."""
807
  sentences: list[tuple[str, int, int]] = []
808
+ pattern = r"[^.!?\n]+[.!?\n]?"
809
  for match in re.finditer(pattern, content):
810
  text = match.group().strip()
811
  if len(text) > 10:
 
885
  DetectionResult with spans, static/dynamic content split, etc.
886
  """
887
  import time
888
+
889
  start_time = time.perf_counter()
890
 
891
  all_spans: list[DynamicSpan] = []
 
947
 
948
  for span in reversed(spans):
949
  dynamic_parts.append(span.text)
950
+ static = static[: span.start] + static[span.end :]
951
 
952
  static = self._clean_static_content(static)
953
  dynamic_parts.reverse()
headroom/cache/google.py CHANGED
@@ -274,16 +274,13 @@ class GoogleCacheOptimizer(BaseCacheOptimizer):
274
  Returns:
275
  CacheResult with analysis and cache information
276
  """
277
- effective_config = config or self.config
278
 
279
  # Extract cacheable content (system messages + static context)
280
  cacheable_content = self._extract_cacheable_content(messages)
281
  content_hash = self._compute_prefix_hash(cacheable_content)
282
 
283
  # Estimate tokens
284
- total_tokens = self._count_tokens_estimate(
285
- self._messages_to_text(messages)
286
- )
287
  cacheable_tokens = self._count_tokens_estimate(cacheable_content)
288
 
289
  # Check for existing cache
@@ -371,9 +368,7 @@ class GoogleCacheOptimizer(BaseCacheOptimizer):
371
  cacheable_content = self._extract_cacheable_content(messages)
372
  content_hash = self._compute_prefix_hash(cacheable_content)
373
 
374
- total_tokens = self._count_tokens_estimate(
375
- self._messages_to_text(messages)
376
- )
377
  cacheable_tokens = self._count_tokens_estimate(cacheable_content)
378
 
379
  is_cacheable = cacheable_tokens >= GOOGLE_MIN_CACHE_TOKENS
@@ -384,33 +379,30 @@ class GoogleCacheOptimizer(BaseCacheOptimizer):
384
 
385
  if not is_cacheable:
386
  recommendations.append(
387
- f"Add {tokens_below_minimum:,} more tokens to static content "
388
- f"to enable caching"
389
  )
390
  recommendations.append(
391
  "Consider adding detailed examples or documentation to system prompt"
392
  )
393
  else:
394
  recommendations.append(
395
- f"Content is cacheable. Create cache with google-generativeai SDK"
396
  )
397
 
398
  # Storage cost estimation (rough - actual pricing varies)
399
  # Assuming ~$0.001 per 1000 tokens per hour (simplified)
400
  hourly_cost = (cacheable_tokens / 1000) * 0.001
401
- recommendations.append(
402
- f"Estimated storage cost: ~${hourly_cost:.4f}/hour"
403
- )
404
 
405
  # Break-even analysis
406
  if hourly_cost > 0:
407
  # Assuming $0.01 per 1000 input tokens base price
408
  base_cost_per_request = (cacheable_tokens / 1000) * 0.01
409
  savings_per_request = base_cost_per_request * GOOGLE_CACHE_DISCOUNT
410
- break_even_requests = hourly_cost / savings_per_request if savings_per_request > 0 else float('inf')
411
- recommendations.append(
412
- f"Break-even: ~{int(break_even_requests)} requests/hour"
413
  )
 
414
 
415
  return CacheabilityAnalysis(
416
  is_cacheable=is_cacheable,
@@ -580,9 +572,7 @@ class GoogleCacheOptimizer(BaseCacheOptimizer):
580
  old_expires = cache_info.expires_at
581
  cache_info.expires_at = new_expires_at
582
 
583
- logger.info(
584
- f"Extended cache {cache_id} TTL from {old_expires} to {new_expires_at}"
585
- )
586
 
587
  return cache_info
588
 
@@ -721,8 +711,7 @@ class GoogleCacheOptimizer(BaseCacheOptimizer):
721
 
722
  if not analysis.is_cacheable:
723
  logger.debug(
724
- f"Content not cacheable: {analysis.tokens_below_minimum} "
725
- f"tokens below minimum"
726
  )
727
  return None
728
 
@@ -764,8 +753,7 @@ class GoogleCacheOptimizer(BaseCacheOptimizer):
764
  "cached_content": cache_id,
765
  "contents": dynamic_messages,
766
  "_headroom_note": (
767
- "Use cached_content parameter with GenerativeModel "
768
- "to leverage the cache"
769
  ),
770
  }
771
 
 
274
  Returns:
275
  CacheResult with analysis and cache information
276
  """
 
277
 
278
  # Extract cacheable content (system messages + static context)
279
  cacheable_content = self._extract_cacheable_content(messages)
280
  content_hash = self._compute_prefix_hash(cacheable_content)
281
 
282
  # Estimate tokens
283
+ total_tokens = self._count_tokens_estimate(self._messages_to_text(messages))
 
 
284
  cacheable_tokens = self._count_tokens_estimate(cacheable_content)
285
 
286
  # Check for existing cache
 
368
  cacheable_content = self._extract_cacheable_content(messages)
369
  content_hash = self._compute_prefix_hash(cacheable_content)
370
 
371
+ total_tokens = self._count_tokens_estimate(self._messages_to_text(messages))
 
 
372
  cacheable_tokens = self._count_tokens_estimate(cacheable_content)
373
 
374
  is_cacheable = cacheable_tokens >= GOOGLE_MIN_CACHE_TOKENS
 
379
 
380
  if not is_cacheable:
381
  recommendations.append(
382
+ f"Add {tokens_below_minimum:,} more tokens to static content to enable caching"
 
383
  )
384
  recommendations.append(
385
  "Consider adding detailed examples or documentation to system prompt"
386
  )
387
  else:
388
  recommendations.append(
389
+ "Content is cacheable. Create cache with google-generativeai SDK"
390
  )
391
 
392
  # Storage cost estimation (rough - actual pricing varies)
393
  # Assuming ~$0.001 per 1000 tokens per hour (simplified)
394
  hourly_cost = (cacheable_tokens / 1000) * 0.001
395
+ recommendations.append(f"Estimated storage cost: ~${hourly_cost:.4f}/hour")
 
 
396
 
397
  # Break-even analysis
398
  if hourly_cost > 0:
399
  # Assuming $0.01 per 1000 input tokens base price
400
  base_cost_per_request = (cacheable_tokens / 1000) * 0.01
401
  savings_per_request = base_cost_per_request * GOOGLE_CACHE_DISCOUNT
402
+ break_even_requests = (
403
+ hourly_cost / savings_per_request if savings_per_request > 0 else float("inf")
 
404
  )
405
+ recommendations.append(f"Break-even: ~{int(break_even_requests)} requests/hour")
406
 
407
  return CacheabilityAnalysis(
408
  is_cacheable=is_cacheable,
 
572
  old_expires = cache_info.expires_at
573
  cache_info.expires_at = new_expires_at
574
 
575
+ logger.info(f"Extended cache {cache_id} TTL from {old_expires} to {new_expires_at}")
 
 
576
 
577
  return cache_info
578
 
 
711
 
712
  if not analysis.is_cacheable:
713
  logger.debug(
714
+ f"Content not cacheable: {analysis.tokens_below_minimum} tokens below minimum"
 
715
  )
716
  return None
717
 
 
753
  "cached_content": cache_id,
754
  "contents": dynamic_messages,
755
  "_headroom_note": (
756
+ "Use cached_content parameter with GenerativeModel to leverage the cache"
 
757
  ),
758
  }
759
 
headroom/cache/openai.py CHANGED
@@ -42,10 +42,9 @@ Usage:
42
 
43
  from __future__ import annotations
44
 
45
- import re
46
  from copy import deepcopy
47
  from dataclasses import dataclass, field
48
- from typing import Any, Literal
49
 
50
  from .base import (
51
  BaseCacheOptimizer,
@@ -233,12 +232,8 @@ class OpenAICacheOptimizer(BaseCacheOptimizer):
233
  warnings.extend(result.warnings)
234
 
235
  if result.spans:
236
- transforms_applied.append(
237
- f"extracted_{len(result.spans)}_dynamic_elements"
238
- )
239
- transforms_applied.extend(
240
- f"tier_{tier}" for tier in result.tiers_used
241
- )
242
 
243
  # Get static content with dynamic parts removed
244
  stabilized = result.static_content
@@ -382,8 +377,7 @@ class OpenAICacheOptimizer(BaseCacheOptimizer):
382
  # Check if prefix is stable
383
  current_hash = self._compute_prefix_hash(system_content)
384
  likely_hit = (
385
- self._previous_prefix_hash is not None
386
- and current_hash == self._previous_prefix_hash
387
  )
388
 
389
  if likely_hit:
@@ -427,7 +421,9 @@ class OpenAICacheOptimizer(BaseCacheOptimizer):
427
  leading = len(line) - len(line.lstrip())
428
  # Collapse multiple spaces in content (not indentation)
429
  content_part = " ".join(stripped.split())
430
- normalized_lines.append(" " * leading + content_part[leading:] if leading else content_part)
 
 
431
  else:
432
  normalized_lines.append("")
433
 
@@ -581,11 +577,8 @@ class OpenAICacheOptimizer(BaseCacheOptimizer):
581
  for block in content:
582
  if isinstance(block, dict):
583
  if block.get("type") == "text":
584
- total += self._count_tokens_estimate(
585
- block.get("text", "")
586
- )
587
  elif block.get("type") == "image_url":
588
  # Rough estimate for images
589
  total += 85 # Base cost
590
  return total
591
-
 
42
 
43
  from __future__ import annotations
44
 
 
45
  from copy import deepcopy
46
  from dataclasses import dataclass, field
47
+ from typing import Any
48
 
49
  from .base import (
50
  BaseCacheOptimizer,
 
232
  warnings.extend(result.warnings)
233
 
234
  if result.spans:
235
+ transforms_applied.append(f"extracted_{len(result.spans)}_dynamic_elements")
236
+ transforms_applied.extend(f"tier_{tier}" for tier in result.tiers_used)
 
 
 
 
237
 
238
  # Get static content with dynamic parts removed
239
  stabilized = result.static_content
 
377
  # Check if prefix is stable
378
  current_hash = self._compute_prefix_hash(system_content)
379
  likely_hit = (
380
+ self._previous_prefix_hash is not None and current_hash == self._previous_prefix_hash
 
381
  )
382
 
383
  if likely_hit:
 
421
  leading = len(line) - len(line.lstrip())
422
  # Collapse multiple spaces in content (not indentation)
423
  content_part = " ".join(stripped.split())
424
+ normalized_lines.append(
425
+ " " * leading + content_part[leading:] if leading else content_part
426
+ )
427
  else:
428
  normalized_lines.append("")
429
 
 
577
  for block in content:
578
  if isinstance(block, dict):
579
  if block.get("type") == "text":
580
+ total += self._count_tokens_estimate(block.get("text", ""))
 
 
581
  elif block.get("type") == "image_url":
582
  # Rough estimate for images
583
  total += 85 # Base cost
584
  return total
 
headroom/cache/registry.py CHANGED
@@ -7,9 +7,7 @@ This allows users to swap implementations and register custom optimizers.
7
 
8
  from __future__ import annotations
9
 
10
- from typing import Type
11
-
12
- from .base import CacheOptimizer, BaseCacheOptimizer, CacheConfig
13
 
14
 
15
  class CacheOptimizerRegistry:
@@ -32,14 +30,14 @@ class CacheOptimizerRegistry:
32
  CacheOptimizerRegistry.register("my-provider", MyOptimizer)
33
  """
34
 
35
- _optimizers: dict[str, Type[BaseCacheOptimizer]] = {}
36
  _instances: dict[str, BaseCacheOptimizer] = {}
37
 
38
  @classmethod
39
  def register(
40
  cls,
41
  name: str,
42
- optimizer_class: Type[BaseCacheOptimizer],
43
  *,
44
  override: bool = False,
45
  ) -> None:
@@ -56,8 +54,7 @@ class CacheOptimizerRegistry:
56
  """
57
  if name in cls._optimizers and not override:
58
  raise ValueError(
59
- f"Optimizer '{name}' already registered. "
60
- f"Use override=True to replace."
61
  )
62
  cls._optimizers[name] = optimizer_class
63
  # Clear cached instance if exists
@@ -109,10 +106,7 @@ class CacheOptimizerRegistry:
109
 
110
  if key not in cls._optimizers:
111
  available = list(cls._optimizers.keys())
112
- raise KeyError(
113
- f"No optimizer registered for '{key}'. "
114
- f"Available: {available}"
115
- )
116
 
117
  # Return cached instance if requested
118
  cache_key = f"{key}:{id(config)}" if config else key
@@ -165,8 +159,8 @@ def _register_defaults() -> None:
165
  """Register default optimizers."""
166
  # Import here to avoid circular imports
167
  from .anthropic import AnthropicCacheOptimizer
168
- from .openai import OpenAICacheOptimizer
169
  from .google import GoogleCacheOptimizer
 
170
 
171
  CacheOptimizerRegistry.register("anthropic", AnthropicCacheOptimizer)
172
  CacheOptimizerRegistry.register("openai", OpenAICacheOptimizer)
 
7
 
8
  from __future__ import annotations
9
 
10
+ from .base import BaseCacheOptimizer, CacheConfig
 
 
11
 
12
 
13
  class CacheOptimizerRegistry:
 
30
  CacheOptimizerRegistry.register("my-provider", MyOptimizer)
31
  """
32
 
33
+ _optimizers: dict[str, type[BaseCacheOptimizer]] = {}
34
  _instances: dict[str, BaseCacheOptimizer] = {}
35
 
36
  @classmethod
37
  def register(
38
  cls,
39
  name: str,
40
+ optimizer_class: type[BaseCacheOptimizer],
41
  *,
42
  override: bool = False,
43
  ) -> None:
 
54
  """
55
  if name in cls._optimizers and not override:
56
  raise ValueError(
57
+ f"Optimizer '{name}' already registered. Use override=True to replace."
 
58
  )
59
  cls._optimizers[name] = optimizer_class
60
  # Clear cached instance if exists
 
106
 
107
  if key not in cls._optimizers:
108
  available = list(cls._optimizers.keys())
109
+ raise KeyError(f"No optimizer registered for '{key}'. Available: {available}")
 
 
 
110
 
111
  # Return cached instance if requested
112
  cache_key = f"{key}:{id(config)}" if config else key
 
159
  """Register default optimizers."""
160
  # Import here to avoid circular imports
161
  from .anthropic import AnthropicCacheOptimizer
 
162
  from .google import GoogleCacheOptimizer
163
+ from .openai import OpenAICacheOptimizer
164
 
165
  CacheOptimizerRegistry.register("anthropic", AnthropicCacheOptimizer)
166
  CacheOptimizerRegistry.register("openai", OpenAICacheOptimizer)
headroom/cache/semantic.py CHANGED
@@ -38,8 +38,9 @@ from __future__ import annotations
38
  import hashlib
39
  import time
40
  from collections import OrderedDict
41
- from dataclasses import dataclass, field
42
- from typing import Any, Callable
 
43
 
44
  from .base import (
45
  BaseCacheOptimizer,
@@ -303,7 +304,8 @@ class SemanticCache:
303
 
304
  now = time.time()
305
  expired = [
306
- key for key, entry in self._cache.items()
 
307
  if now - entry.created_at > self.config.ttl_seconds
308
  ]
309
 
@@ -440,6 +442,7 @@ class SemanticCacheLayer:
440
  def _compute_messages_hash(self, messages: list[dict[str, Any]]) -> str:
441
  """Compute a hash of all messages."""
442
  import json
 
443
  try:
444
  content = json.dumps(messages, sort_keys=True)
445
  return hashlib.sha256(content.encode()).hexdigest()[:24]
 
38
  import hashlib
39
  import time
40
  from collections import OrderedDict
41
+ from collections.abc import Callable
42
+ from dataclasses import dataclass
43
+ from typing import Any
44
 
45
  from .base import (
46
  BaseCacheOptimizer,
 
304
 
305
  now = time.time()
306
  expired = [
307
+ key
308
+ for key, entry in self._cache.items()
309
  if now - entry.created_at > self.config.ttl_seconds
310
  ]
311
 
 
442
  def _compute_messages_hash(self, messages: list[dict[str, Any]]) -> str:
443
  """Compute a hash of all messages."""
444
  import json
445
+
446
  try:
447
  content = json.dumps(messages, sort_keys=True)
448
  return hashlib.sha256(content.encode()).hexdigest()[:24]
headroom/ccr/__init__.py CHANGED
@@ -21,6 +21,7 @@ from .tool_injection import (
21
  # MCP server is optional (requires mcp package)
22
  try:
23
  from .mcp_server import CCRMCPServer, create_ccr_mcp_server
 
24
  MCP_SERVER_AVAILABLE = True
25
  except ImportError:
26
  CCRMCPServer = None # type: ignore
 
21
  # MCP server is optional (requires mcp package)
22
  try:
23
  from .mcp_server import CCRMCPServer, create_ccr_mcp_server
24
+
25
  MCP_SERVER_AVAILABLE = True
26
  except ImportError:
27
  CCRMCPServer = None # type: ignore
headroom/ccr/mcp_server.py CHANGED
@@ -32,7 +32,6 @@ import asyncio
32
  import json
33
  import logging
34
  import os
35
- import sys
36
  from typing import Any
37
 
38
  # Try to import MCP SDK
@@ -40,6 +39,7 @@ try:
40
  from mcp.server import Server
41
  from mcp.server.stdio import stdio_server
42
  from mcp.types import TextContent, Tool
 
43
  MCP_AVAILABLE = True
44
  except ImportError:
45
  MCP_AVAILABLE = False
@@ -49,6 +49,7 @@ except ImportError:
49
  # Try to import httpx for proxy communication
50
  try:
51
  import httpx
 
52
  HTTPX_AVAILABLE = True
53
  except ImportError:
54
  HTTPX_AVAILABLE = False
@@ -88,14 +89,11 @@ class CCRMCPServer:
88
  self._http_client: httpx.AsyncClient | None = None
89
 
90
  if not MCP_AVAILABLE:
91
- raise ImportError(
92
- "MCP SDK not installed. Install with: pip install mcp"
93
- )
94
 
95
  if not direct_mode and not HTTPX_AVAILABLE:
96
  raise ImportError(
97
- "httpx not installed (required for HTTP mode). "
98
- "Install with: pip install httpx"
99
  )
100
 
101
  self.server = Server("headroom-ccr")
@@ -140,19 +138,23 @@ class CCRMCPServer:
140
  async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
141
  """Handle tool calls."""
142
  if name != CCR_TOOL_NAME:
143
- return [TextContent(
144
- type="text",
145
- text=json.dumps({"error": f"Unknown tool: {name}"}),
146
- )]
 
 
147
 
148
  hash_key = arguments.get("hash")
149
  query = arguments.get("query")
150
 
151
  if not hash_key:
152
- return [TextContent(
153
- type="text",
154
- text=json.dumps({"error": "hash parameter is required"}),
155
- )]
 
 
156
 
157
  # Retrieve content
158
  try:
@@ -161,16 +163,20 @@ class CCRMCPServer:
161
  else:
162
  result = await self._retrieve_via_proxy(hash_key, query)
163
 
164
- return [TextContent(
165
- type="text",
166
- text=json.dumps(result, indent=2),
167
- )]
 
 
168
  except Exception as e:
169
  logger.error(f"Retrieval failed: {e}")
170
- return [TextContent(
171
- type="text",
172
- text=json.dumps({"error": str(e)}),
173
- )]
 
 
174
 
175
  async def _retrieve_via_proxy(
176
  self,
 
32
  import json
33
  import logging
34
  import os
 
35
  from typing import Any
36
 
37
  # Try to import MCP SDK
 
39
  from mcp.server import Server
40
  from mcp.server.stdio import stdio_server
41
  from mcp.types import TextContent, Tool
42
+
43
  MCP_AVAILABLE = True
44
  except ImportError:
45
  MCP_AVAILABLE = False
 
49
  # Try to import httpx for proxy communication
50
  try:
51
  import httpx
52
+
53
  HTTPX_AVAILABLE = True
54
  except ImportError:
55
  HTTPX_AVAILABLE = False
 
89
  self._http_client: httpx.AsyncClient | None = None
90
 
91
  if not MCP_AVAILABLE:
92
+ raise ImportError("MCP SDK not installed. Install with: pip install mcp")
 
 
93
 
94
  if not direct_mode and not HTTPX_AVAILABLE:
95
  raise ImportError(
96
+ "httpx not installed (required for HTTP mode). Install with: pip install httpx"
 
97
  )
98
 
99
  self.server = Server("headroom-ccr")
 
138
  async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
139
  """Handle tool calls."""
140
  if name != CCR_TOOL_NAME:
141
+ return [
142
+ TextContent(
143
+ type="text",
144
+ text=json.dumps({"error": f"Unknown tool: {name}"}),
145
+ )
146
+ ]
147
 
148
  hash_key = arguments.get("hash")
149
  query = arguments.get("query")
150
 
151
  if not hash_key:
152
+ return [
153
+ TextContent(
154
+ type="text",
155
+ text=json.dumps({"error": "hash parameter is required"}),
156
+ )
157
+ ]
158
 
159
  # Retrieve content
160
  try:
 
163
  else:
164
  result = await self._retrieve_via_proxy(hash_key, query)
165
 
166
+ return [
167
+ TextContent(
168
+ type="text",
169
+ text=json.dumps(result, indent=2),
170
+ )
171
+ ]
172
  except Exception as e:
173
  logger.error(f"Retrieval failed: {e}")
174
+ return [
175
+ TextContent(
176
+ type="text",
177
+ text=json.dumps({"error": str(e)}),
178
+ )
179
+ ]
180
 
181
  async def _retrieve_via_proxy(
182
  self,
headroom/ccr/tool_injection.py CHANGED
@@ -321,10 +321,12 @@ class CCRToolInjector:
321
  else:
322
  # Append instructions
323
  if isinstance(content, str):
324
- updated_messages.append({
325
- **message,
326
- "content": content + instructions,
327
- })
 
 
328
  else:
329
  # Handle structured content
330
  updated_messages.append(message)
@@ -333,10 +335,13 @@ class CCRToolInjector:
333
 
334
  # If no system message, prepend one
335
  if not system_found:
336
- updated_messages.insert(0, {
337
- "role": "system",
338
- "content": instructions.strip(),
339
- })
 
 
 
340
 
341
  return updated_messages
342
 
 
321
  else:
322
  # Append instructions
323
  if isinstance(content, str):
324
+ updated_messages.append(
325
+ {
326
+ **message,
327
+ "content": content + instructions,
328
+ }
329
+ )
330
  else:
331
  # Handle structured content
332
  updated_messages.append(message)
 
335
 
336
  # If no system message, prepend one
337
  if not system_found:
338
+ updated_messages.insert(
339
+ 0,
340
+ {
341
+ "role": "system",
342
+ "content": instructions.strip(),
343
+ },
344
+ )
345
 
346
  return updated_messages
347
 
headroom/cli.py CHANGED
@@ -30,6 +30,7 @@ def get_version() -> str:
30
  """Get the current version."""
31
  try:
32
  from headroom import __version__
 
33
  return __version__
34
  except ImportError:
35
  return "unknown"
@@ -63,9 +64,9 @@ def cmd_proxy(args: argparse.Namespace) -> int:
63
  Starting proxy server...
64
 
65
  URL: http://{config.host}:{config.port}
66
- Optimization: {'ENABLED' if config.optimize else 'DISABLED'}
67
- Caching: {'ENABLED' if config.cache_enabled else 'DISABLED'}
68
- Rate Limit: {'ENABLED' if config.rate_limit_enabled else 'DISABLED'}
69
 
70
  Usage with Claude Code:
71
  ANTHROPIC_BASE_URL=http://{config.host}:{config.port} claude
@@ -119,7 +120,8 @@ Documentation: https://github.com/headroom-sdk/headroom
119
  )
120
 
121
  parser.add_argument(
122
- "--version", "-V",
 
123
  action="store_true",
124
  help="Show version and exit",
125
  )
@@ -138,7 +140,8 @@ Documentation: https://github.com/headroom-sdk/headroom
138
  help="Host to bind to (default: 127.0.0.1)",
139
  )
140
  proxy_parser.add_argument(
141
- "--port", "-p",
 
142
  type=int,
143
  default=8787,
144
  help="Port to bind to (default: 8787)",
 
30
  """Get the current version."""
31
  try:
32
  from headroom import __version__
33
+
34
  return __version__
35
  except ImportError:
36
  return "unknown"
 
64
  Starting proxy server...
65
 
66
  URL: http://{config.host}:{config.port}
67
+ Optimization: {"ENABLED" if config.optimize else "DISABLED"}
68
+ Caching: {"ENABLED" if config.cache_enabled else "DISABLED"}
69
+ Rate Limit: {"ENABLED" if config.rate_limit_enabled else "DISABLED"}
70
 
71
  Usage with Claude Code:
72
  ANTHROPIC_BASE_URL=http://{config.host}:{config.port} claude
 
120
  )
121
 
122
  parser.add_argument(
123
+ "--version",
124
+ "-V",
125
  action="store_true",
126
  help="Show version and exit",
127
  )
 
140
  help="Host to bind to (default: 127.0.0.1)",
141
  )
142
  proxy_parser.add_argument(
143
+ "--port",
144
+ "-p",
145
  type=int,
146
  default=8787,
147
  help="Port to bind to (default: 8787)",
headroom/client.py CHANGED
@@ -8,8 +8,8 @@ from typing import Any
8
 
9
  from .cache import (
10
  BaseCacheOptimizer,
11
- CacheOptimizerRegistry,
12
  CacheConfig,
 
13
  OptimizationContext,
14
  SemanticCacheLayer,
15
  )
@@ -19,12 +19,6 @@ from .config import (
19
  RequestMetrics,
20
  SimulationResult,
21
  )
22
- from .exceptions import (
23
- ConfigurationError,
24
- ProviderError,
25
- StorageError,
26
- ValidationError,
27
- )
28
  from .parser import parse_messages
29
  from .providers.base import Provider
30
  from .storage import create_storage
@@ -386,9 +380,7 @@ class HeadroomClient:
386
  tokenizer = self._get_tokenizer(model)
387
 
388
  # Analyze original messages
389
- blocks, block_breakdown, waste_signals = parse_messages(
390
- messages, tokenizer
391
- )
392
  tokens_before = tokenizer.count_messages(messages)
393
 
394
  # Compute cache alignment score
@@ -410,7 +402,9 @@ class HeadroomClient:
410
 
411
  # Apply transforms if in optimize mode
412
  if mode == HeadroomMode.OPTIMIZE:
413
- output_buffer = headroom_output_buffer_tokens or self._config.rolling_window.output_buffer_tokens
 
 
414
  model_limit = self._get_context_limit(model)
415
 
416
  result = self._pipeline.apply(
@@ -443,7 +437,9 @@ class HeadroomClient:
443
  cached_response = cache_result.cached_response
444
 
445
  # Update metrics from cache result
446
- cache_optimizer_used = cache_result.metrics.optimizer_name or self._cache_optimizer.name
 
 
447
  cache_optimizer_strategy = cache_result.metrics.strategy
448
  cacheable_tokens = cache_result.metrics.cacheable_tokens
449
  breakpoints_inserted = cache_result.metrics.breakpoints_inserted
@@ -456,9 +452,7 @@ class HeadroomClient:
456
 
457
  elif self._cache_optimizer is not None:
458
  # Direct cache optimizer (no semantic layer)
459
- cache_result = self._cache_optimizer.optimize(
460
- optimized_messages, cache_context
461
- )
462
  cache_optimizer_used = self._cache_optimizer.name
463
  cache_optimizer_strategy = self._cache_optimizer.strategy.value
464
  cacheable_tokens = cache_result.metrics.cacheable_tokens
@@ -625,8 +619,7 @@ class HeadroomClient:
625
  ) -> Iterator[Any]:
626
  """Wrap stream to pass through chunks and save metrics at end."""
627
  try:
628
- for chunk in stream:
629
- yield chunk
630
  finally:
631
  # Save metrics when stream completes
632
  # Note: output tokens unknown for streams
@@ -666,9 +659,7 @@ class HeadroomClient:
666
  # Extract response content for caching
667
  response_data = self._extract_response_content(response)
668
  if response_data:
669
- self._semantic_cache_layer.store_response(
670
- messages, response_data, cache_context
671
- )
672
 
673
  def _extract_response_content(self, response: Any) -> dict[str, Any] | None:
674
  """Extract cacheable content from API response."""
@@ -704,18 +695,18 @@ class HeadroomClient:
704
  tokenizer = self._get_tokenizer(model)
705
 
706
  # Analyze original
707
- blocks, block_breakdown, waste_signals = parse_messages(
708
- messages, tokenizer
709
- )
710
  tokens_before = tokenizer.count_messages(messages)
711
 
712
  # Compute original cache alignment
713
  aligner = CacheAligner(self._config.cache_aligner)
714
  cache_alignment_score = aligner.get_alignment_score(messages)
715
- stable_prefix_hash = compute_prefix_hash(messages)
716
 
717
  # Apply transforms
718
- output_buffer = headroom_output_buffer_tokens or self._config.rolling_window.output_buffer_tokens
 
 
719
  model_limit = self._get_context_limit(model)
720
 
721
  result = self._pipeline.simulate(
@@ -946,9 +937,7 @@ class HeadroomClient:
946
  "config": {
947
  "mode": self._default_mode.value,
948
  "provider": self._provider.name,
949
- "cache_optimizer": (
950
- self._cache_optimizer.name if self._cache_optimizer else None
951
- ),
952
  "semantic_cache": self._semantic_cache_layer is not None,
953
  },
954
  "transforms": {
 
8
 
9
  from .cache import (
10
  BaseCacheOptimizer,
 
11
  CacheConfig,
12
+ CacheOptimizerRegistry,
13
  OptimizationContext,
14
  SemanticCacheLayer,
15
  )
 
19
  RequestMetrics,
20
  SimulationResult,
21
  )
 
 
 
 
 
 
22
  from .parser import parse_messages
23
  from .providers.base import Provider
24
  from .storage import create_storage
 
380
  tokenizer = self._get_tokenizer(model)
381
 
382
  # Analyze original messages
383
+ blocks, block_breakdown, waste_signals = parse_messages(messages, tokenizer)
 
 
384
  tokens_before = tokenizer.count_messages(messages)
385
 
386
  # Compute cache alignment score
 
402
 
403
  # Apply transforms if in optimize mode
404
  if mode == HeadroomMode.OPTIMIZE:
405
+ output_buffer = (
406
+ headroom_output_buffer_tokens or self._config.rolling_window.output_buffer_tokens
407
+ )
408
  model_limit = self._get_context_limit(model)
409
 
410
  result = self._pipeline.apply(
 
437
  cached_response = cache_result.cached_response
438
 
439
  # Update metrics from cache result
440
+ cache_optimizer_used = (
441
+ cache_result.metrics.optimizer_name or self._cache_optimizer.name
442
+ )
443
  cache_optimizer_strategy = cache_result.metrics.strategy
444
  cacheable_tokens = cache_result.metrics.cacheable_tokens
445
  breakpoints_inserted = cache_result.metrics.breakpoints_inserted
 
452
 
453
  elif self._cache_optimizer is not None:
454
  # Direct cache optimizer (no semantic layer)
455
+ cache_result = self._cache_optimizer.optimize(optimized_messages, cache_context)
 
 
456
  cache_optimizer_used = self._cache_optimizer.name
457
  cache_optimizer_strategy = self._cache_optimizer.strategy.value
458
  cacheable_tokens = cache_result.metrics.cacheable_tokens
 
619
  ) -> Iterator[Any]:
620
  """Wrap stream to pass through chunks and save metrics at end."""
621
  try:
622
+ yield from stream
 
623
  finally:
624
  # Save metrics when stream completes
625
  # Note: output tokens unknown for streams
 
659
  # Extract response content for caching
660
  response_data = self._extract_response_content(response)
661
  if response_data:
662
+ self._semantic_cache_layer.store_response(messages, response_data, cache_context)
 
 
663
 
664
  def _extract_response_content(self, response: Any) -> dict[str, Any] | None:
665
  """Extract cacheable content from API response."""
 
695
  tokenizer = self._get_tokenizer(model)
696
 
697
  # Analyze original
698
+ blocks, block_breakdown, waste_signals = parse_messages(messages, tokenizer)
 
 
699
  tokens_before = tokenizer.count_messages(messages)
700
 
701
  # Compute original cache alignment
702
  aligner = CacheAligner(self._config.cache_aligner)
703
  cache_alignment_score = aligner.get_alignment_score(messages)
704
+ compute_prefix_hash(messages)
705
 
706
  # Apply transforms
707
+ output_buffer = (
708
+ headroom_output_buffer_tokens or self._config.rolling_window.output_buffer_tokens
709
+ )
710
  model_limit = self._get_context_limit(model)
711
 
712
  result = self._pipeline.simulate(
 
937
  "config": {
938
  "mode": self._default_mode.value,
939
  "provider": self._provider.name,
940
+ "cache_optimizer": (self._cache_optimizer.name if self._cache_optimizer else None),
 
 
941
  "semantic_cache": self._semantic_cache_layer is not None,
942
  },
943
  "transforms": {
headroom/config.py CHANGED
@@ -266,7 +266,9 @@ class CCRConfig:
266
 
267
  # Retrieval marker format
268
  # Inserted at end of compressed content to tell LLM how to get more
269
- marker_template: str = "\n[{original_count} items compressed to {compressed_count}. Retrieve more: hash={hash}]"
 
 
270
 
271
 
272
  @dataclass
 
266
 
267
  # Retrieval marker format
268
  # Inserted at end of compressed content to tell LLM how to get more
269
+ marker_template: str = (
270
+ "\n[{original_count} items compressed to {compressed_count}. Retrieve more: hash={hash}]"
271
+ )
272
 
273
 
274
  @dataclass
headroom/exceptions.py CHANGED
@@ -60,6 +60,7 @@ class ConfigurationError(HeadroomError):
60
  details={"valid_modes": ["audit", "optimize"]}
61
  )
62
  """
 
63
  pass
64
 
65
 
@@ -77,6 +78,7 @@ class ProviderError(HeadroomError):
77
  details={"provider": "foo", "known_providers": ["openai", "anthropic"]}
78
  )
79
  """
 
80
  pass
81
 
82
 
@@ -94,6 +96,7 @@ class StorageError(HeadroomError):
94
  details={"url": "sqlite:///foo.db", "error": "Permission denied"}
95
  )
96
  """
 
97
  pass
98
 
99
 
@@ -111,6 +114,7 @@ class CompressionError(HeadroomError):
111
  details={"tool_name": "search_api", "content_preview": "..."}
112
  )
113
  """
 
114
  pass
115
 
116
 
@@ -128,6 +132,7 @@ class TokenizationError(HeadroomError):
128
  details={"model": "gpt-99", "fallback_used": True}
129
  )
130
  """
 
131
  pass
132
 
133
 
@@ -145,6 +150,7 @@ class CacheError(HeadroomError):
145
  details={"hash": "abc123", "ttl": 300}
146
  )
147
  """
 
148
  pass
149
 
150
 
@@ -164,6 +170,7 @@ class ValidationError(HeadroomError):
164
  }
165
  )
166
  """
 
167
  pass
168
 
169
 
@@ -181,4 +188,5 @@ class TransformError(HeadroomError):
181
  details={"transform": "smart_crusher", "reason": "..."}
182
  )
183
  """
 
184
  pass
 
60
  details={"valid_modes": ["audit", "optimize"]}
61
  )
62
  """
63
+
64
  pass
65
 
66
 
 
78
  details={"provider": "foo", "known_providers": ["openai", "anthropic"]}
79
  )
80
  """
81
+
82
  pass
83
 
84
 
 
96
  details={"url": "sqlite:///foo.db", "error": "Permission denied"}
97
  )
98
  """
99
+
100
  pass
101
 
102
 
 
114
  details={"tool_name": "search_api", "content_preview": "..."}
115
  )
116
  """
117
+
118
  pass
119
 
120
 
 
132
  details={"model": "gpt-99", "fallback_used": True}
133
  )
134
  """
135
+
136
  pass
137
 
138
 
 
150
  details={"hash": "abc123", "ttl": 300}
151
  )
152
  """
153
+
154
  pass
155
 
156
 
 
170
  }
171
  )
172
  """
173
+
174
  pass
175
 
176
 
 
188
  details={"transform": "smart_crusher", "reason": "..."}
189
  )
190
  """
191
+
192
  pass
headroom/integrations/langchain.py CHANGED
@@ -142,6 +142,7 @@ class HeadroomChatModel(BaseChatModel):
142
 
143
  class Config:
144
  """Pydantic config for LangChain compatibility."""
 
145
  arbitrary_types_allowed = True
146
 
147
  def __init__(
@@ -206,9 +207,7 @@ class HeadroomChatModel(BaseChatModel):
206
  """History of optimization metrics."""
207
  return self._metrics_history.copy()
208
 
209
- def _convert_messages_to_openai(
210
- self, messages: list[BaseMessage]
211
- ) -> list[dict[str, Any]]:
212
  """Convert LangChain messages to OpenAI format for Headroom."""
213
  result = []
214
  for msg in messages:
@@ -232,22 +231,24 @@ class HeadroomChatModel(BaseChatModel):
232
  ]
233
  result.append(entry)
234
  elif isinstance(msg, ToolMessage):
235
- result.append({
236
- "role": "tool",
237
- "tool_call_id": msg.tool_call_id,
238
- "content": msg.content,
239
- })
 
 
240
  else:
241
  # Generic fallback
242
- result.append({
243
- "role": getattr(msg, "type", "user"),
244
- "content": msg.content,
245
- })
 
 
246
  return result
247
 
248
- def _convert_messages_from_openai(
249
- self, messages: list[dict[str, Any]]
250
- ) -> list[BaseMessage]:
251
  """Convert OpenAI format messages back to LangChain format."""
252
  result = []
253
  for msg in messages:
@@ -262,17 +263,21 @@ class HeadroomChatModel(BaseChatModel):
262
  tool_calls = []
263
  if "tool_calls" in msg:
264
  for tc in msg["tool_calls"]:
265
- tool_calls.append({
266
- "id": tc["id"],
267
- "name": tc["function"]["name"],
268
- "args": json.loads(tc["function"]["arguments"]),
269
- })
 
 
270
  result.append(AIMessage(content=content, tool_calls=tool_calls))
271
  elif role == "tool":
272
- result.append(ToolMessage(
273
- content=content,
274
- tool_call_id=msg.get("tool_call_id", ""),
275
- ))
 
 
276
  return result
277
 
278
  def _optimize_messages(
@@ -308,7 +313,8 @@ class HeadroomChatModel(BaseChatModel):
308
  tokens_saved=result.tokens_before - result.tokens_after,
309
  savings_percent=(
310
  (result.tokens_before - result.tokens_after) / result.tokens_before * 100
311
- if result.tokens_before > 0 else 0
 
312
  ),
313
  transforms_applied=result.transforms_applied,
314
  model=model,
@@ -400,9 +406,8 @@ class HeadroomChatModel(BaseChatModel):
400
  return {
401
  "total_requests": len(self._metrics_history),
402
  "total_tokens_saved": self._total_tokens_saved,
403
- "average_savings_percent": sum(
404
- m.savings_percent for m in self._metrics_history
405
- ) / len(self._metrics_history),
406
  "total_tokens_before": sum(m.tokens_before for m in self._metrics_history),
407
  "total_tokens_after": sum(m.tokens_after for m in self._metrics_history),
408
  }
@@ -530,7 +535,7 @@ class HeadroomCallbackHandler(BaseCallbackHandler):
530
  if self.log_level in ("DEBUG", "INFO"):
531
  logger.log(
532
  logging.DEBUG if self.log_level == "DEBUG" else logging.INFO,
533
- f"Chat model request: ~{estimated_tokens} input tokens"
534
  )
535
 
536
  def on_llm_end(self, response: Any, **kwargs) -> None:
@@ -565,7 +570,7 @@ class HeadroomCallbackHandler(BaseCallbackHandler):
565
  duration = f"{self._current_request['duration_ms']:.0f}ms"
566
  logger.log(
567
  logging.DEBUG if self.log_level == "DEBUG" else logging.INFO,
568
- f"LLM request completed: {tokens_info} in {duration}"
569
  )
570
 
571
  self._current_request = None
@@ -602,7 +607,8 @@ class HeadroomCallbackHandler(BaseCallbackHandler):
602
  "average_tokens": total_tokens / len(successful) if successful else 0,
603
  "average_duration_ms": (
604
  sum(r.get("duration_ms", 0) for r in successful) / len(successful)
605
- if successful else 0
 
606
  ),
607
  "errors": len(self._requests) - len(successful),
608
  "alerts": len(self._alerts),
@@ -670,11 +676,13 @@ class HeadroomRunnable:
670
  def __or__(self, other):
671
  """Support pipe operator for LCEL composition."""
672
  from langchain_core.runnables import RunnableSequence
 
673
  return RunnableSequence(first=self.as_runnable(), last=other)
674
 
675
  def __ror__(self, other):
676
  """Support reverse pipe operator."""
677
  from langchain_core.runnables import RunnableSequence
 
678
  return RunnableSequence(first=other, last=self.as_runnable())
679
 
680
  def as_runnable(self):
@@ -704,16 +712,20 @@ class HeadroomRunnable:
704
  elif isinstance(msg, AIMessage):
705
  openai_messages.append({"role": "assistant", "content": msg.content})
706
  elif isinstance(msg, ToolMessage):
707
- openai_messages.append({
708
- "role": "tool",
709
- "tool_call_id": msg.tool_call_id,
710
- "content": msg.content,
711
- })
 
 
712
  elif hasattr(msg, "type") and hasattr(msg, "content"):
713
- openai_messages.append({
714
- "role": msg.type,
715
- "content": msg.content,
716
- })
 
 
717
 
718
  # Get model context limit
719
  model = "gpt-4o" # Default model for estimation
@@ -735,7 +747,8 @@ class HeadroomRunnable:
735
  tokens_saved=result.tokens_before - result.tokens_after,
736
  savings_percent=(
737
  (result.tokens_before - result.tokens_after) / result.tokens_before * 100
738
- if result.tokens_before > 0 else 0
 
739
  ),
740
  transforms_applied=result.transforms_applied,
741
  model="gpt-4o",
@@ -755,10 +768,12 @@ class HeadroomRunnable:
755
  elif role == "assistant":
756
  output_messages.append(AIMessage(content=content))
757
  elif role == "tool":
758
- output_messages.append(ToolMessage(
759
- content=content,
760
- tool_call_id=msg.get("tool_call_id", ""),
761
- ))
 
 
762
 
763
  return output_messages
764
 
@@ -823,11 +838,13 @@ def optimize_messages(
823
  ]
824
  openai_messages.append(entry)
825
  elif isinstance(msg, ToolMessage):
826
- openai_messages.append({
827
- "role": "tool",
828
- "tool_call_id": msg.tool_call_id,
829
- "content": msg.content,
830
- })
 
 
831
 
832
  # Get model context limit
833
  model_limit = provider.get_context_limit(model)
@@ -853,17 +870,21 @@ def optimize_messages(
853
  tool_calls = []
854
  if "tool_calls" in msg:
855
  for tc in msg["tool_calls"]:
856
- tool_calls.append({
857
- "id": tc["id"],
858
- "name": tc["function"]["name"],
859
- "args": json.loads(tc["function"]["arguments"]),
860
- })
 
 
861
  output_messages.append(AIMessage(content=content, tool_calls=tool_calls))
862
  elif role == "tool":
863
- output_messages.append(ToolMessage(
864
- content=content,
865
- tool_call_id=msg.get("tool_call_id", ""),
866
- ))
 
 
867
 
868
  metrics = {
869
  "tokens_before": result.tokens_before,
@@ -871,7 +892,8 @@ def optimize_messages(
871
  "tokens_saved": result.tokens_before - result.tokens_after,
872
  "savings_percent": (
873
  (result.tokens_before - result.tokens_after) / result.tokens_before * 100
874
- if result.tokens_before > 0 else 0
 
875
  ),
876
  "transforms_applied": result.transforms_applied,
877
  }
 
142
 
143
  class Config:
144
  """Pydantic config for LangChain compatibility."""
145
+
146
  arbitrary_types_allowed = True
147
 
148
  def __init__(
 
207
  """History of optimization metrics."""
208
  return self._metrics_history.copy()
209
 
210
+ def _convert_messages_to_openai(self, messages: list[BaseMessage]) -> list[dict[str, Any]]:
 
 
211
  """Convert LangChain messages to OpenAI format for Headroom."""
212
  result = []
213
  for msg in messages:
 
231
  ]
232
  result.append(entry)
233
  elif isinstance(msg, ToolMessage):
234
+ result.append(
235
+ {
236
+ "role": "tool",
237
+ "tool_call_id": msg.tool_call_id,
238
+ "content": msg.content,
239
+ }
240
+ )
241
  else:
242
  # Generic fallback
243
+ result.append(
244
+ {
245
+ "role": getattr(msg, "type", "user"),
246
+ "content": msg.content,
247
+ }
248
+ )
249
  return result
250
 
251
+ def _convert_messages_from_openai(self, messages: list[dict[str, Any]]) -> list[BaseMessage]:
 
 
252
  """Convert OpenAI format messages back to LangChain format."""
253
  result = []
254
  for msg in messages:
 
263
  tool_calls = []
264
  if "tool_calls" in msg:
265
  for tc in msg["tool_calls"]:
266
+ tool_calls.append(
267
+ {
268
+ "id": tc["id"],
269
+ "name": tc["function"]["name"],
270
+ "args": json.loads(tc["function"]["arguments"]),
271
+ }
272
+ )
273
  result.append(AIMessage(content=content, tool_calls=tool_calls))
274
  elif role == "tool":
275
+ result.append(
276
+ ToolMessage(
277
+ content=content,
278
+ tool_call_id=msg.get("tool_call_id", ""),
279
+ )
280
+ )
281
  return result
282
 
283
  def _optimize_messages(
 
313
  tokens_saved=result.tokens_before - result.tokens_after,
314
  savings_percent=(
315
  (result.tokens_before - result.tokens_after) / result.tokens_before * 100
316
+ if result.tokens_before > 0
317
+ else 0
318
  ),
319
  transforms_applied=result.transforms_applied,
320
  model=model,
 
406
  return {
407
  "total_requests": len(self._metrics_history),
408
  "total_tokens_saved": self._total_tokens_saved,
409
+ "average_savings_percent": sum(m.savings_percent for m in self._metrics_history)
410
+ / len(self._metrics_history),
 
411
  "total_tokens_before": sum(m.tokens_before for m in self._metrics_history),
412
  "total_tokens_after": sum(m.tokens_after for m in self._metrics_history),
413
  }
 
535
  if self.log_level in ("DEBUG", "INFO"):
536
  logger.log(
537
  logging.DEBUG if self.log_level == "DEBUG" else logging.INFO,
538
+ f"Chat model request: ~{estimated_tokens} input tokens",
539
  )
540
 
541
  def on_llm_end(self, response: Any, **kwargs) -> None:
 
570
  duration = f"{self._current_request['duration_ms']:.0f}ms"
571
  logger.log(
572
  logging.DEBUG if self.log_level == "DEBUG" else logging.INFO,
573
+ f"LLM request completed: {tokens_info} in {duration}",
574
  )
575
 
576
  self._current_request = None
 
607
  "average_tokens": total_tokens / len(successful) if successful else 0,
608
  "average_duration_ms": (
609
  sum(r.get("duration_ms", 0) for r in successful) / len(successful)
610
+ if successful
611
+ else 0
612
  ),
613
  "errors": len(self._requests) - len(successful),
614
  "alerts": len(self._alerts),
 
676
  def __or__(self, other):
677
  """Support pipe operator for LCEL composition."""
678
  from langchain_core.runnables import RunnableSequence
679
+
680
  return RunnableSequence(first=self.as_runnable(), last=other)
681
 
682
  def __ror__(self, other):
683
  """Support reverse pipe operator."""
684
  from langchain_core.runnables import RunnableSequence
685
+
686
  return RunnableSequence(first=other, last=self.as_runnable())
687
 
688
  def as_runnable(self):
 
712
  elif isinstance(msg, AIMessage):
713
  openai_messages.append({"role": "assistant", "content": msg.content})
714
  elif isinstance(msg, ToolMessage):
715
+ openai_messages.append(
716
+ {
717
+ "role": "tool",
718
+ "tool_call_id": msg.tool_call_id,
719
+ "content": msg.content,
720
+ }
721
+ )
722
  elif hasattr(msg, "type") and hasattr(msg, "content"):
723
+ openai_messages.append(
724
+ {
725
+ "role": msg.type,
726
+ "content": msg.content,
727
+ }
728
+ )
729
 
730
  # Get model context limit
731
  model = "gpt-4o" # Default model for estimation
 
747
  tokens_saved=result.tokens_before - result.tokens_after,
748
  savings_percent=(
749
  (result.tokens_before - result.tokens_after) / result.tokens_before * 100
750
+ if result.tokens_before > 0
751
+ else 0
752
  ),
753
  transforms_applied=result.transforms_applied,
754
  model="gpt-4o",
 
768
  elif role == "assistant":
769
  output_messages.append(AIMessage(content=content))
770
  elif role == "tool":
771
+ output_messages.append(
772
+ ToolMessage(
773
+ content=content,
774
+ tool_call_id=msg.get("tool_call_id", ""),
775
+ )
776
+ )
777
 
778
  return output_messages
779
 
 
838
  ]
839
  openai_messages.append(entry)
840
  elif isinstance(msg, ToolMessage):
841
+ openai_messages.append(
842
+ {
843
+ "role": "tool",
844
+ "tool_call_id": msg.tool_call_id,
845
+ "content": msg.content,
846
+ }
847
+ )
848
 
849
  # Get model context limit
850
  model_limit = provider.get_context_limit(model)
 
870
  tool_calls = []
871
  if "tool_calls" in msg:
872
  for tc in msg["tool_calls"]:
873
+ tool_calls.append(
874
+ {
875
+ "id": tc["id"],
876
+ "name": tc["function"]["name"],
877
+ "args": json.loads(tc["function"]["arguments"]),
878
+ }
879
+ )
880
  output_messages.append(AIMessage(content=content, tool_calls=tool_calls))
881
  elif role == "tool":
882
+ output_messages.append(
883
+ ToolMessage(
884
+ content=content,
885
+ tool_call_id=msg.get("tool_call_id", ""),
886
+ )
887
+ )
888
 
889
  metrics = {
890
  "tokens_before": result.tokens_before,
 
892
  "tokens_saved": result.tokens_before - result.tokens_after,
893
  "savings_percent": (
894
  (result.tokens_before - result.tokens_after) / result.tokens_before * 100
895
+ if result.tokens_before > 0
896
+ else 0
897
  ),
898
  "transforms_applied": result.transforms_applied,
899
  }
headroom/integrations/mcp.py CHANGED
@@ -222,7 +222,7 @@ class HeadroomMCPCompressor:
222
 
223
  # Try to parse as JSON
224
  try:
225
- data = json.loads(content)
226
  except json.JSONDecodeError:
227
  # Not JSON, return as-is
228
  return MCPCompressionResult(
@@ -260,7 +260,12 @@ class HeadroomMCPCompressor:
260
  {
261
  "role": "assistant",
262
  "content": None,
263
- "tool_calls": [{"id": "call_1", "function": {"name": tool_name, "arguments": json.dumps(tool_args or {})}}]
 
 
 
 
 
264
  },
265
  {"role": "tool", "content": content, "tool_call_id": "call_1"},
266
  ]
@@ -287,7 +292,7 @@ class HeadroomMCPCompressor:
287
  compressed_content = result.messages[-1]["content"]
288
 
289
  # Remove any Headroom markers for clean output
290
- compressed_content = re.sub(r'\n<headroom:[^>]+>', '', compressed_content)
291
 
292
  # Count items and errors
293
  try:
@@ -295,13 +300,13 @@ class HeadroomMCPCompressor:
295
  compressed_data = json.loads(compressed_content)
296
 
297
  # Find the array in original
298
- for key, value in original_data.items():
299
  if isinstance(value, list):
300
  items_before = len(value)
301
  break
302
 
303
  # Find the array in compressed
304
- for key, value in compressed_data.items():
305
  if isinstance(value, list):
306
  items_after = len(value)
307
  # Count errors preserved
@@ -521,7 +526,7 @@ def create_headroom_mcp_proxy(
521
  ```
522
  """
523
  return {
524
- "upstream_servers": {name: server for name, server in upstream_servers},
525
  "compressor": HeadroomMCPCompressor(config=config),
526
  "config": config or HeadroomConfig(),
527
  }
 
222
 
223
  # Try to parse as JSON
224
  try:
225
+ json.loads(content)
226
  except json.JSONDecodeError:
227
  # Not JSON, return as-is
228
  return MCPCompressionResult(
 
260
  {
261
  "role": "assistant",
262
  "content": None,
263
+ "tool_calls": [
264
+ {
265
+ "id": "call_1",
266
+ "function": {"name": tool_name, "arguments": json.dumps(tool_args or {})},
267
+ }
268
+ ],
269
  },
270
  {"role": "tool", "content": content, "tool_call_id": "call_1"},
271
  ]
 
292
  compressed_content = result.messages[-1]["content"]
293
 
294
  # Remove any Headroom markers for clean output
295
+ compressed_content = re.sub(r"\n<headroom:[^>]+>", "", compressed_content)
296
 
297
  # Count items and errors
298
  try:
 
300
  compressed_data = json.loads(compressed_content)
301
 
302
  # Find the array in original
303
+ for _key, value in original_data.items():
304
  if isinstance(value, list):
305
  items_before = len(value)
306
  break
307
 
308
  # Find the array in compressed
309
+ for _key, value in compressed_data.items():
310
  if isinstance(value, list):
311
  items_after = len(value)
312
  # Count errors preserved
 
526
  ```
527
  """
528
  return {
529
+ "upstream_servers": dict(upstream_servers),
530
  "compressor": HeadroomMCPCompressor(config=config),
531
  "config": config or HeadroomConfig(),
532
  }
headroom/models/registry.py CHANGED
@@ -649,7 +649,7 @@ class ModelRegistry:
649
  Returns:
650
  List of provider names.
651
  """
652
- return list(set(info.provider for info in _MODELS.values()))
653
 
654
  @classmethod
655
  def get_context_limit(cls, model: str, default: int = 128000) -> int:
 
649
  Returns:
650
  List of provider names.
651
  """
652
+ return list({info.provider for info in _MODELS.values()})
653
 
654
  @classmethod
655
  def get_context_limit(cls, model: str, default: int = 128000) -> int:
headroom/pricing/registry.py CHANGED
@@ -10,6 +10,7 @@ class ModelPricing:
10
 
11
  All prices are in USD per 1 million tokens.
12
  """
 
13
  model: str
14
  provider: str
15
  input_per_1m: float
@@ -24,6 +25,7 @@ class ModelPricing:
24
  @dataclass
25
  class CostEstimate:
26
  """Result of a cost estimation calculation."""
 
27
  cost_usd: float
28
  breakdown: dict = field(default_factory=dict)
29
  pricing_date: date | None = None
 
10
 
11
  All prices are in USD per 1 million tokens.
12
  """
13
+
14
  model: str
15
  provider: str
16
  input_per_1m: float
 
25
  @dataclass
26
  class CostEstimate:
27
  """Result of a cost estimation calculation."""
28
+
29
  cost_usd: float
30
  breakdown: dict = field(default_factory=dict)
31
  pricing_date: date | None = None
headroom/providers/anthropic.py CHANGED
@@ -87,13 +87,14 @@ class AnthropicTokenCounter(TokenCounter):
87
  "For accurate counting, pass an Anthropic client: "
88
  "AnthropicProvider(client=Anthropic())",
89
  UserWarning,
90
- stacklevel=4
91
  )
92
  _FALLBACK_WARNING_SHOWN = True
93
 
94
  # Load tiktoken as fallback
95
  try:
96
  import tiktoken
 
97
  self._encoding = tiktoken.get_encoding("cl100k_base")
98
  except ImportError:
99
  if not self._use_api:
@@ -101,7 +102,7 @@ class AnthropicTokenCounter(TokenCounter):
101
  "tiktoken not installed - token counting will be very approximate. "
102
  "Install tiktoken or provide an Anthropic client.",
103
  UserWarning,
104
- stacklevel=4
105
  )
106
 
107
  def count_text(self, text: str) -> int:
@@ -184,11 +185,13 @@ class AnthropicTokenCounter(TokenCounter):
184
  # Tool results in OpenAI format
185
  return {
186
  "role": "user",
187
- "content": [{
188
- "type": "tool_result",
189
- "tool_use_id": message.get("tool_call_id", ""),
190
- "content": message.get("content", ""),
191
- }]
 
 
192
  }
193
 
194
  return {"role": role, "content": message.get("content", "")}
@@ -232,9 +235,7 @@ class AnthropicTokenCounter(TokenCounter):
232
  except Exception as e:
233
  # Fall back to estimation on API error
234
  warnings.warn(
235
- f"Token Count API failed ({e}), using estimation",
236
- UserWarning,
237
- stacklevel=3
238
  )
239
  return self._count_messages_estimated(messages)
240
 
@@ -318,8 +319,7 @@ class AnthropicProvider(Provider):
318
  return True
319
  # Check prefix matches
320
  return any(
321
- model.startswith(prefix)
322
- for prefix in ["claude-3", "claude-2", "claude-instant"]
323
  )
324
 
325
  def estimate_cost(
 
87
  "For accurate counting, pass an Anthropic client: "
88
  "AnthropicProvider(client=Anthropic())",
89
  UserWarning,
90
+ stacklevel=4,
91
  )
92
  _FALLBACK_WARNING_SHOWN = True
93
 
94
  # Load tiktoken as fallback
95
  try:
96
  import tiktoken
97
+
98
  self._encoding = tiktoken.get_encoding("cl100k_base")
99
  except ImportError:
100
  if not self._use_api:
 
102
  "tiktoken not installed - token counting will be very approximate. "
103
  "Install tiktoken or provide an Anthropic client.",
104
  UserWarning,
105
+ stacklevel=4,
106
  )
107
 
108
  def count_text(self, text: str) -> int:
 
185
  # Tool results in OpenAI format
186
  return {
187
  "role": "user",
188
+ "content": [
189
+ {
190
+ "type": "tool_result",
191
+ "tool_use_id": message.get("tool_call_id", ""),
192
+ "content": message.get("content", ""),
193
+ }
194
+ ],
195
  }
196
 
197
  return {"role": role, "content": message.get("content", "")}
 
235
  except Exception as e:
236
  # Fall back to estimation on API error
237
  warnings.warn(
238
+ f"Token Count API failed ({e}), using estimation", UserWarning, stacklevel=3
 
 
239
  )
240
  return self._count_messages_estimated(messages)
241
 
 
319
  return True
320
  # Check prefix matches
321
  return any(
322
+ model.startswith(prefix) for prefix in ["claude-3", "claude-2", "claude-instant"]
 
323
  )
324
 
325
  def estimate_cost(