chopratejas commited on
Commit
313fe01
·
1 Parent(s): dd832fe

Fix ruff formatting

Browse files
devto-article.md ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # I Was Wasting 85% of My LLM Tokens on JSON Boilerplate
2
+
3
+ I recently built an agent to handle some SRE tasks—fetching logs, querying databases, searching code. It worked, but when I looked at the traces, I was annoyed.
4
+
5
+ It wasn't just that it was expensive (though the bill was climbing). It was the sheer **inefficiency**.
6
+
7
+ I looked at a single tool output—a search for Python files. It was 40,000 tokens.
8
+ About 35,000 of those tokens were just `"type": "file"` and `"language": "python"` repeated 2,000 times.
9
+
10
+ We are paying premium compute prices to force state-of-the-art models to read standard JSON boilerplate.
11
+
12
+ I couldn't find a tool that solved this without breaking the agent, so I wrote one. It's called **Headroom**. It's a context optimization layer that sits between your app and your LLM. It compresses context by ~85% without losing semantic meaning.
13
+
14
+ It's open source (Apache-2.0). If you just want the code:
15
+ **[github.com/chopratejas/headroom](https://github.com/chopratejas/headroom)**
16
+
17
+ ---
18
+
19
+ ## Why Truncation and Summarization Don't Work
20
+
21
+ When your context window fills up, the standard industry solution is **truncation** (chopping off the oldest messages or the middle of the document).
22
+
23
+ But for an agent, truncation is dangerous.
24
+
25
+ * If you chop the middle of a log file, you might lose the one error line that explains the crash.
26
+ * If you chop a file list, you might lose the exact config file the user asked for.
27
+
28
+ I tried **summarization** (using a cheaper model to summarize the data first), but that introduced hallucination. I had a summarizer tell me a deployment "looked fine" because it ignored specific error codes in the raw log.
29
+
30
+ I needed a third option: **Lossless compression.** Or at least, "intent-lossless."
31
+
32
+ ---
33
+
34
+ ## The Core Idea: Statistical Analysis, Not Blind Truncation
35
+
36
+ I realized that 90% of the data in a tool output is just schema scaffolding. The LLM doesn't need to see `status: active` repeated a thousand times. It needs the **anomalies**.
37
+
38
+ Headroom's SmartCrusher runs statistical analysis before touching your data:
39
+
40
+ **1. Constant Factoring**
41
+ If every item in an array has `"type": "file"`, it doesn't repeat that 2,000 times. It extracts constants once.
42
+
43
+ **2. Outlier Detection**
44
+ It calculates standard deviation of numerical fields. It preserves the spikes—the values that are >2σ from the mean. Those are usually what matters.
45
+
46
+ **3. Error Preservation**
47
+ Hard rule: never discard strings that look like stack traces, error messages, or failures. Errors are sacred.
48
+
49
+ **4. Relevance Scoring**
50
+ If you searched for "auth", items containing "auth" get preserved. Uses BM25 + semantic embeddings (hybrid scoring) to match items against the user's query context.
51
+
52
+ **5. First/Last Retention**
53
+ Always keeps first few and last few items. The LLM expects to see some examples, and recency matters.
54
+
55
+ The result: 40,000 tokens → 4,000 tokens. Same information density. No hallucination risk.
56
+
57
+ ---
58
+
59
+ ## CCR: Making Compression Reversible
60
+
61
+ Here's the insight that changed everything: **compression should be reversible**.
62
+
63
+ I call the architecture **CCR** (Compress-Cache-Retrieve):
64
+
65
+ ### 1. Compress
66
+ SmartCrusher compresses the tool output from 2,000 items to 20.
67
+
68
+ ### 2. Cache
69
+ The original 2,000 items are cached locally (5-minute TTL, LRU eviction).
70
+
71
+ ### 3. Retrieve
72
+ Headroom injects a tool called `headroom_retrieve()` into the LLM's context. If the model looks at the compressed summary and decides it needs more data—maybe the user asked a follow-up question—it can call that tool. Headroom fetches from the cache and returns the relevant items.
73
+
74
+ This changes the risk calculus. You can compress aggressively (90%+) because **nothing is ever truly lost**. The model can always "unzip" what it needs.
75
+
76
+ I've had conversations like this:
77
+
78
+ ```
79
+ Turn 1: "Search for all Python files"
80
+ → 1000 files returned, compressed to 15
81
+
82
+ Turn 5: "Actually, what was that file handling JWT tokens?"
83
+ → LLM calls headroom_retrieve("jwt")
84
+ → Returns jwt_handler.py from cached data
85
+ ```
86
+
87
+ No extra API calls. No "sorry, I don't have that information anymore."
88
+
89
+ ---
90
+
91
+ ## TOIN: The Network Effect
92
+
93
+ Here's where it gets interesting. Headroom learns from compression patterns.
94
+
95
+ **TOIN** (Tool Output Intelligence Network) tracks—anonymously—what happens after compression:
96
+ - Which fields get retrieved most often?
97
+ - Which tool types have high retrieval rates?
98
+ - What query patterns trigger retrievals?
99
+
100
+ This data feeds back into compression recommendations. If TOIN learns that users frequently retrieve `error_code` fields after compression, it tells SmartCrusher to preserve `error_code` more aggressively next time.
101
+
102
+ Privacy is built in:
103
+ - No actual data values stored
104
+ - Tool names are structure hashes
105
+ - Field names are SHA256[:8] hashes
106
+ - No user identifiers
107
+
108
+ The network effect: more users → more compression events → better recommendations for everyone.
109
+
110
+ ---
111
+
112
+ ## Memory: Cross-Conversation Learning
113
+
114
+ Agents often need to remember things across conversations. "I prefer dark mode." "My timezone is PST." "I'm working on the auth refactor."
115
+
116
+ Headroom has a memory system that extracts and stores these facts automatically.
117
+
118
+ Two approaches:
119
+
120
+ **Fast Memory (Recommended)**
121
+ Zero extra latency. The LLM outputs a `<memory>` block inline with its response. Headroom parses it out and stores the memory.
122
+
123
+ ```python
124
+ from headroom.memory import with_fast_memory
125
+ client = with_fast_memory(OpenAI(), user_id="alice")
126
+
127
+ # Memories extracted automatically from responses
128
+ # Injected automatically into future requests
129
+ ```
130
+
131
+ **Background Memory**
132
+ Separate LLM call extracts memories asynchronously. More accurate but adds latency.
133
+
134
+ ```python
135
+ from headroom import with_memory
136
+ client = with_memory(OpenAI(), user_id="alice")
137
+ ```
138
+
139
+ Memories are stored locally (SQLite) and injected into future conversations. The model remembers that Alice prefers dark mode without you managing state.
140
+
141
+ ---
142
+
143
+ ## The Transform Pipeline
144
+
145
+ Headroom runs four transforms on each request:
146
+
147
+ ### 1. CacheAligner
148
+ LLM providers offer cached token pricing (Anthropic: 90% off, OpenAI: 50% off). But caching only works if your prompt prefix is stable.
149
+
150
+ Problem: your system prompt probably has a timestamp. `Current time: 2024-01-15 10:32:45`. That breaks caching.
151
+
152
+ CacheAligner extracts dynamic content and moves it to the end, stabilizing the prefix. Same information, better cache hits.
153
+
154
+ ### 2. SmartCrusher
155
+ The statistical compression engine. Analyzes arrays, detects patterns, preserves anomalies, factors constants.
156
+
157
+ ### 3. ContentRouter
158
+ Different content needs different compression. Code isn't JSON isn't logs isn't prose.
159
+
160
+ ContentRouter uses ML-based content detection to route data to specialized compressors:
161
+ - **Code** → AST-aware compression (tree-sitter)
162
+ - **JSON** → SmartCrusher
163
+ - **Logs** → LogCompressor (clusters similar messages)
164
+ - **Text** → Optional LLMLingua integration (20x compression, adds latency)
165
+
166
+ ### 4. RollingWindow
167
+ When context exceeds the model limit, something has to go. RollingWindow drops oldest tool calls + responses together (never orphans data), preserves system prompt and recent turns.
168
+
169
+ ---
170
+
171
+ ## Three Ways to Use It
172
+
173
+ ### Option 1: Proxy Server (Zero Code Changes)
174
+
175
+ ```bash
176
+ pip install headroom-ai
177
+ headroom proxy --port 8787
178
+ ```
179
+
180
+ Point your OpenAI client to `http://localhost:8787/v1`. Done.
181
+
182
+ ```python
183
+ from openai import OpenAI
184
+ client = OpenAI(base_url="http://localhost:8787/v1")
185
+ # No other changes
186
+ ```
187
+
188
+ Works with Claude Code, Cursor, any OpenAI-compatible client.
189
+
190
+ ### Option 2: SDK Wrapper
191
+
192
+ ```python
193
+ from headroom import HeadroomClient
194
+ from openai import OpenAI
195
+
196
+ client = HeadroomClient(OpenAI())
197
+
198
+ response = client.chat.completions.create(
199
+ model="gpt-4o",
200
+ messages=[...],
201
+ headroom_mode="optimize" # or "audit" or "simulate"
202
+ )
203
+ ```
204
+
205
+ Three modes:
206
+ - **audit**: Observe only. Logs what would be optimized, doesn't change anything.
207
+ - **optimize**: Apply compression. This is what saves tokens.
208
+ - **simulate**: Dry run. Returns the optimized messages without calling the API.
209
+
210
+ Start with `audit` to see potential savings, then flip to `optimize` when you're confident.
211
+
212
+ ### Option 3: Framework Integrations
213
+
214
+ **LangChain:**
215
+ ```python
216
+ from langchain_openai import ChatOpenAI
217
+ from headroom.integrations.langchain import HeadroomChatModel
218
+
219
+ base_model = ChatOpenAI(model="gpt-4o")
220
+ model = HeadroomChatModel(base_model, mode="optimize")
221
+
222
+ # Use in any chain or agent
223
+ chain = prompt | model | parser
224
+ ```
225
+
226
+ **Agno:**
227
+ ```python
228
+ from agno.agent import Agent
229
+ from headroom.integrations.agno import HeadroomAgnoModel
230
+
231
+ model = HeadroomAgnoModel(original_model, mode="optimize")
232
+ agent = Agent(model=model, tools=[...])
233
+ ```
234
+
235
+ **MCP (Model Context Protocol):**
236
+ ```python
237
+ from headroom.integrations.mcp import compress_tool_result
238
+
239
+ # Compress any tool result before returning to LLM
240
+ compressed = compress_tool_result(tool_name, result_data)
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Real Numbers
246
+
247
+ I've been running this in production for months. Here's what the token reduction looks like:
248
+
249
+ | Workload | Before | After | Savings |
250
+ |----------|--------|-------|---------|
251
+ | Log Analysis | 22,000 | 3,300 | 85% |
252
+ | Code Search | 45,000 | 4,500 | 90% |
253
+ | Database Queries | 18,000 | 2,700 | 85% |
254
+ | Long Conversations | 80,000 | 32,000 | 60% |
255
+
256
+ Latency overhead: 3-5ms per request. No extra LLM calls.
257
+
258
+ ---
259
+
260
+ ## What's Coming Next
261
+
262
+ This is actively maintained. On the roadmap:
263
+
264
+ **More Frameworks**
265
+ - CrewAI integration
266
+ - AutoGen integration
267
+ - Semantic Kernel integration
268
+
269
+ **Managed Storage**
270
+ - Cloud-hosted TOIN backend (opt-in)
271
+ - Cross-device memory sync
272
+ - Team-shared compression patterns
273
+
274
+ **Better Compression**
275
+ - Domain-specific profiles (SRE, coding, data analysis)
276
+ - Custom compressor plugins
277
+ - Streaming compression for real-time tools
278
+
279
+ ---
280
+
281
+ ## Why I Built This
282
+
283
+ I'm a believer that we're in the "optimization phase" of the AI hype cycle. Getting things to work is table stakes; getting them to work cheaply and reliably is the actual engineering work.
284
+
285
+ Headroom is my attempt to fix the "context bloat" problem properly. Not with heuristics or truncation, but with statistical analysis and reversible compression.
286
+
287
+ It runs entirely locally. No data leaves your machine (except to OpenAI/Anthropic as usual). Apache-2.0 licensed.
288
+
289
+ **Repo:** [github.com/chopratejas/headroom](https://github.com/chopratejas/headroom)
290
+
291
+ If you find bugs or have ideas, open an issue. I'm actively maintaining this.
292
+
293
+ ---
294
+
295
+ *Tags: #llm #ai #python #openai #anthropic #agents #optimization*
headroom/cache/compression_store.py CHANGED
@@ -741,16 +741,13 @@ class CompressionStore:
741
  # Handle both direct arrays and wrapped arrays
742
  if isinstance(parsed, list):
743
  # Filter to dicts only (field learning needs dict items)
744
- retrieved_items = [
745
- item for item in parsed if isinstance(item, dict)
746
- ]
747
  elif isinstance(parsed, dict):
748
  # Check for common wrapper patterns: {"items": [...], "results": [...]}
749
  for key in ("items", "results", "data", "records"):
750
  if key in parsed and isinstance(parsed[key], list):
751
  retrieved_items = [
752
- item for item in parsed[key]
753
- if isinstance(item, dict)
754
  ]
755
  break
756
  except (json.JSONDecodeError, TypeError):
 
741
  # Handle both direct arrays and wrapped arrays
742
  if isinstance(parsed, list):
743
  # Filter to dicts only (field learning needs dict items)
744
+ retrieved_items = [item for item in parsed if isinstance(item, dict)]
 
 
745
  elif isinstance(parsed, dict):
746
  # Check for common wrapper patterns: {"items": [...], "results": [...]}
747
  for key in ("items", "results", "data", "records"):
748
  if key in parsed and isinstance(parsed[key], list):
749
  retrieved_items = [
750
+ item for item in parsed[key] if isinstance(item, dict)
 
751
  ]
752
  break
753
  except (json.JSONDecodeError, TypeError):
headroom/telemetry/models.py CHANGED
@@ -436,7 +436,7 @@ class FieldSemantics:
436
  "field_hash": self.field_hash,
437
  "inferred_type": self.inferred_type,
438
  "confidence": self.confidence,
439
- "important_value_hashes": self.important_value_hashes[:self.MAX_IMPORTANT_VALUES],
440
  "default_value_hash": self.default_value_hash,
441
  "value_retrieval_frequency": dict(
442
  sorted(
@@ -514,9 +514,7 @@ class FieldSemantics:
514
  )[: self.MAX_IMPORTANT_VALUES]
515
 
516
  # Track query operators
517
- self.query_operator_frequency[operator] = (
518
- self.query_operator_frequency.get(operator, 0) + 1
519
- )
520
 
521
  def record_compression_stats(
522
  self,
@@ -543,9 +541,7 @@ class FieldSemantics:
543
  self.total_unique_values_seen = int(
544
  (self.total_unique_values_seen * (n - 1) + unique_values) / n
545
  )
546
- self.total_values_seen = int(
547
- (self.total_values_seen * (n - 1) + total_values) / n
548
- )
549
  self.most_common_value_frequency = (
550
  self.most_common_value_frequency * (n - 1) + most_common_frequency
551
  ) / n
@@ -569,9 +565,7 @@ class FieldSemantics:
569
  return
570
 
571
  # Calculate metrics
572
- uniqueness_ratio = (
573
- self.total_unique_values_seen / max(1, self.total_values_seen)
574
- )
575
  has_dominant_default = self.most_common_value_frequency > 0.7
576
  retrieval_diversity = len(self.value_retrieval_frequency) / max(1, self.retrieval_count)
577
 
@@ -598,15 +592,15 @@ class FieldSemantics:
598
  # ERROR_INDICATOR: Has dominant default + retrievals are for non-default values
599
  elif has_dominant_default and self.default_value_hash:
600
  # Check if retrieved values are different from default
601
- default_retrieval_count = self.value_retrieval_frequency.get(
602
- self.default_value_hash, 0
603
- )
604
  non_default_retrieval_ratio = 1 - (
605
  default_retrieval_count / max(1, self.retrieval_count)
606
  )
607
  if non_default_retrieval_ratio > 0.7:
608
  inferred = "error_indicator"
609
- confidence = min(0.9, non_default_retrieval_ratio * self.most_common_value_frequency)
 
 
610
 
611
  # STATUS: Low uniqueness + specific values retrieved
612
  elif uniqueness_ratio < 0.2 and retrieval_diversity < 0.5:
 
436
  "field_hash": self.field_hash,
437
  "inferred_type": self.inferred_type,
438
  "confidence": self.confidence,
439
+ "important_value_hashes": self.important_value_hashes[: self.MAX_IMPORTANT_VALUES],
440
  "default_value_hash": self.default_value_hash,
441
  "value_retrieval_frequency": dict(
442
  sorted(
 
514
  )[: self.MAX_IMPORTANT_VALUES]
515
 
516
  # Track query operators
517
+ self.query_operator_frequency[operator] = self.query_operator_frequency.get(operator, 0) + 1
 
 
518
 
519
  def record_compression_stats(
520
  self,
 
541
  self.total_unique_values_seen = int(
542
  (self.total_unique_values_seen * (n - 1) + unique_values) / n
543
  )
544
+ self.total_values_seen = int((self.total_values_seen * (n - 1) + total_values) / n)
 
 
545
  self.most_common_value_frequency = (
546
  self.most_common_value_frequency * (n - 1) + most_common_frequency
547
  ) / n
 
565
  return
566
 
567
  # Calculate metrics
568
+ uniqueness_ratio = self.total_unique_values_seen / max(1, self.total_values_seen)
 
 
569
  has_dominant_default = self.most_common_value_frequency > 0.7
570
  retrieval_diversity = len(self.value_retrieval_frequency) / max(1, self.retrieval_count)
571
 
 
592
  # ERROR_INDICATOR: Has dominant default + retrievals are for non-default values
593
  elif has_dominant_default and self.default_value_hash:
594
  # Check if retrieved values are different from default
595
+ default_retrieval_count = self.value_retrieval_frequency.get(self.default_value_hash, 0)
 
 
596
  non_default_retrieval_ratio = 1 - (
597
  default_retrieval_count / max(1, self.retrieval_count)
598
  )
599
  if non_default_retrieval_ratio > 0.7:
600
  inferred = "error_indicator"
601
+ confidence = min(
602
+ 0.9, non_default_retrieval_ratio * self.most_common_value_frequency
603
+ )
604
 
605
  # STATUS: Low uniqueness + specific values retrieved
606
  elif uniqueness_ratio < 0.2 and retrieval_diversity < 0.5:
headroom/telemetry/toin.py CHANGED
@@ -169,9 +169,7 @@ class ToolPattern:
169
  "skip_compression_recommended": self.skip_compression_recommended,
170
  "preserve_fields": self.preserve_fields,
171
  # Field-level semantics (TOIN Evolution)
172
- "field_semantics": {
173
- k: v.to_dict() for k, v in self.field_semantics.items()
174
- },
175
  "sample_size": self.sample_size,
176
  "user_count": self.user_count,
177
  "confidence": self.confidence,
 
169
  "skip_compression_recommended": self.skip_compression_recommended,
170
  "preserve_fields": self.preserve_fields,
171
  # Field-level semantics (TOIN Evolution)
172
+ "field_semantics": {k: v.to_dict() for k, v in self.field_semantics.items()},
 
 
173
  "sample_size": self.sample_size,
174
  "user_count": self.user_count,
175
  "confidence": self.confidence,
headroom/transforms/smart_crusher.py CHANGED
@@ -645,7 +645,8 @@ def _detect_items_by_learned_semantics(
645
  # Build a quick lookup for field_hash -> FieldSemantics
646
  # Pre-filter to fields with sufficient confidence
647
  confident_semantics = {
648
- fh: fs for fh, fs in field_semantics.items()
 
649
  if fs.confidence >= 0.3 and fs.inferred_type != "unknown"
650
  }
651
 
@@ -1599,7 +1600,9 @@ class SmartCrusher(Transform):
1599
  return keep_indices
1600
 
1601
  # Use provided field_semantics or fall back to instance variable (set by crush())
1602
- effective_field_semantics = field_semantics or getattr(self, "_current_field_semantics", None)
 
 
1603
 
1604
  # Identify error items using KEYWORD detection (preservation guarantee)
1605
  # This ensures ALL error items are kept, regardless of frequency
@@ -2024,7 +2027,9 @@ class SmartCrusher(Transform):
2024
  # === TOIN Evolution: Extract field semantics for signal detection ===
2025
  # Store temporarily on instance for use in _prioritize_indices
2026
  # This enables learned signal detection without changing all method signatures
2027
- self._current_field_semantics = toin_hint.field_semantics if toin_hint.field_semantics else None
 
 
2028
 
2029
  # Local feedback hints (if TOIN didn't apply)
2030
  if not toin_hint_applied and self.config.use_feedback_hints and tool_name:
 
645
  # Build a quick lookup for field_hash -> FieldSemantics
646
  # Pre-filter to fields with sufficient confidence
647
  confident_semantics = {
648
+ fh: fs
649
+ for fh, fs in field_semantics.items()
650
  if fs.confidence >= 0.3 and fs.inferred_type != "unknown"
651
  }
652
 
 
1600
  return keep_indices
1601
 
1602
  # Use provided field_semantics or fall back to instance variable (set by crush())
1603
+ effective_field_semantics = field_semantics or getattr(
1604
+ self, "_current_field_semantics", None
1605
+ )
1606
 
1607
  # Identify error items using KEYWORD detection (preservation guarantee)
1608
  # This ensures ALL error items are kept, regardless of frequency
 
2027
  # === TOIN Evolution: Extract field semantics for signal detection ===
2028
  # Store temporarily on instance for use in _prioritize_indices
2029
  # This enables learned signal detection without changing all method signatures
2030
+ self._current_field_semantics = (
2031
+ toin_hint.field_semantics if toin_hint.field_semantics else None
2032
+ )
2033
 
2034
  # Local feedback hints (if TOIN didn't apply)
2035
  if not toin_hint_applied and self.config.use_feedback_hints and tool_name:
tests/test_cache/test_client_integration.py CHANGED
@@ -388,9 +388,9 @@ class TestCacheOptimizerInvocation:
388
  headroom_meta = result.headroom
389
 
390
  # Check that cache optimizer was reported
391
- assert headroom_meta.cache_optimizer_used is not None or \
392
- any("cache_optimizer" in t for t in (headroom_meta.transforms_applied or [])), \
393
- "Cache optimizer usage should be reported in metadata"
394
 
395
  @patch("headroom.storage.sqlite.SQLiteStorage.save")
396
  def test_optimizer_not_called_in_audit_mode(self, mock_save, temp_db):
@@ -430,9 +430,7 @@ class TestCacheOptimizerInvocation:
430
  )
431
 
432
  # Optimizer should NOT be called in AUDIT mode
433
- assert not spy_optimize.called, (
434
- "Cache optimizer should NOT be called in AUDIT mode"
435
- )
436
 
437
 
438
  class TestSemanticCacheIntegration:
@@ -444,9 +442,7 @@ class TestSemanticCacheIntegration:
444
  """
445
 
446
  @patch("headroom.storage.sqlite.SQLiteStorage.save")
447
- def test_semantic_cache_hit_returns_cached_response_without_api_call(
448
- self, mock_save, temp_db
449
- ):
450
  """CRITICAL: Verify semantic cache hit returns cached response without API call.
451
 
452
  This test catches the gap where semantic cache is enabled but cached
@@ -565,8 +561,7 @@ class TestSessionStatsTracking:
565
  after_requests = after_stats["session"]["requests_total"]
566
 
567
  assert after_requests == initial_requests + 1, (
568
- f"requests_total should increment. "
569
- f"Before: {initial_requests}, After: {after_requests}"
570
  )
571
  assert after_stats["session"]["requests_audit"] >= 1, (
572
  "requests_audit should be at least 1 after AUDIT mode request"
 
388
  headroom_meta = result.headroom
389
 
390
  # Check that cache optimizer was reported
391
+ assert headroom_meta.cache_optimizer_used is not None or any(
392
+ "cache_optimizer" in t for t in (headroom_meta.transforms_applied or [])
393
+ ), "Cache optimizer usage should be reported in metadata"
394
 
395
  @patch("headroom.storage.sqlite.SQLiteStorage.save")
396
  def test_optimizer_not_called_in_audit_mode(self, mock_save, temp_db):
 
430
  )
431
 
432
  # Optimizer should NOT be called in AUDIT mode
433
+ assert not spy_optimize.called, "Cache optimizer should NOT be called in AUDIT mode"
 
 
434
 
435
 
436
  class TestSemanticCacheIntegration:
 
442
  """
443
 
444
  @patch("headroom.storage.sqlite.SQLiteStorage.save")
445
+ def test_semantic_cache_hit_returns_cached_response_without_api_call(self, mock_save, temp_db):
 
 
446
  """CRITICAL: Verify semantic cache hit returns cached response without API call.
447
 
448
  This test catches the gap where semantic cache is enabled but cached
 
561
  after_requests = after_stats["session"]["requests_total"]
562
 
563
  assert after_requests == initial_requests + 1, (
564
+ f"requests_total should increment. Before: {initial_requests}, After: {after_requests}"
 
565
  )
566
  assert after_stats["session"]["requests_audit"] >= 1, (
567
  "requests_audit should be at least 1 after AUDIT mode request"
tests/test_proxy_ccr.py CHANGED
@@ -386,7 +386,9 @@ class TestEndToEndTOINIntegration:
386
  yield client
387
  reset_compression_store()
388
 
389
- def test_pipeline_compresses_tool_output_and_records_toin(self, fresh_toin, client_with_optimization):
 
 
390
  """CRITICAL: Verify SmartCrusher compression records events in TOIN.
391
 
392
  This tests the production code path:
@@ -467,8 +469,7 @@ class TestEndToEndTOINIntegration:
467
 
468
  # Verify SmartCrusher was invoked (transform name starts with smart_crush)
469
  smart_crush_applied = any(
470
- t.startswith("smart_crush") or t.startswith("smart:")
471
- for t in result.transforms_applied
472
  )
473
  assert smart_crush_applied, (
474
  f"SmartCrusher should be in transforms: {result.transforms_applied}"
 
386
  yield client
387
  reset_compression_store()
388
 
389
+ def test_pipeline_compresses_tool_output_and_records_toin(
390
+ self, fresh_toin, client_with_optimization
391
+ ):
392
  """CRITICAL: Verify SmartCrusher compression records events in TOIN.
393
 
394
  This tests the production code path:
 
469
 
470
  # Verify SmartCrusher was invoked (transform name starts with smart_crush)
471
  smart_crush_applied = any(
472
+ t.startswith("smart_crush") or t.startswith("smart:") for t in result.transforms_applied
 
473
  )
474
  assert smart_crush_applied, (
475
  f"SmartCrusher should be in transforms: {result.transforms_applied}"
tests/test_toin_field_learning.py CHANGED
@@ -245,7 +245,9 @@ class TestFieldSemanticsLearning:
245
 
246
  # VERIFY
247
  assert fs.is_value_important(pending_hash), "Retrieved value should be important"
248
- assert not fs.is_value_important(unknown_hash), "Never-retrieved value should NOT be important"
 
 
249
 
250
 
251
  class TestTOINFieldLearningIntegration:
@@ -275,8 +277,7 @@ class TestTOINFieldLearningIntegration:
275
  pattern = toin._patterns.get(sig.structure_hash)
276
  assert pattern is not None, "Pattern should exist"
277
  assert len(pattern.field_semantics) > 0, (
278
- f"field_semantics should be populated after retrieval. "
279
- f"Got: {pattern.field_semantics}"
280
  )
281
 
282
  # Check that field hashes match expected fields
@@ -327,7 +328,9 @@ class TestTOINFieldLearningIntegration:
327
 
328
  status_sem = pattern.field_semantics[status_hash]
329
  # The type should be inferred (not unknown) after enough data
330
- assert status_sem.retrieval_count >= 6, f"Should have 6+ retrievals, got {status_sem.retrieval_count}"
 
 
331
 
332
  def test_get_recommendation_includes_field_semantics(self):
333
  """PROVES: get_recommendation returns learned field_semantics."""
@@ -513,9 +516,10 @@ class TestEndToEndFieldLearning:
513
  # Error value should be tracked
514
  error_hash = _hash_value("error")
515
  failed_hash = _hash_value("failed")
516
- assert error_hash in status_sem.important_value_hashes or \
517
- failed_hash in status_sem.important_value_hashes, \
518
- "Error/failed values should be marked as important"
 
519
 
520
  def test_recommendation_hint_includes_learned_semantics(self):
521
  """PROVES: TOIN recommendation includes learned field semantics for SmartCrusher."""
@@ -549,8 +553,7 @@ class TestEndToEndFieldLearning:
549
  # VERIFY
550
  assert hint is not None
551
  assert len(hint.field_semantics) > 0, (
552
- f"Recommendation should include field_semantics. "
553
- f"Got: {hint.field_semantics}"
554
  )
555
  assert status_hash in hint.field_semantics
556
 
 
245
 
246
  # VERIFY
247
  assert fs.is_value_important(pending_hash), "Retrieved value should be important"
248
+ assert not fs.is_value_important(unknown_hash), (
249
+ "Never-retrieved value should NOT be important"
250
+ )
251
 
252
 
253
  class TestTOINFieldLearningIntegration:
 
277
  pattern = toin._patterns.get(sig.structure_hash)
278
  assert pattern is not None, "Pattern should exist"
279
  assert len(pattern.field_semantics) > 0, (
280
+ f"field_semantics should be populated after retrieval. Got: {pattern.field_semantics}"
 
281
  )
282
 
283
  # Check that field hashes match expected fields
 
328
 
329
  status_sem = pattern.field_semantics[status_hash]
330
  # The type should be inferred (not unknown) after enough data
331
+ assert status_sem.retrieval_count >= 6, (
332
+ f"Should have 6+ retrievals, got {status_sem.retrieval_count}"
333
+ )
334
 
335
  def test_get_recommendation_includes_field_semantics(self):
336
  """PROVES: get_recommendation returns learned field_semantics."""
 
516
  # Error value should be tracked
517
  error_hash = _hash_value("error")
518
  failed_hash = _hash_value("failed")
519
+ assert (
520
+ error_hash in status_sem.important_value_hashes
521
+ or failed_hash in status_sem.important_value_hashes
522
+ ), "Error/failed values should be marked as important"
523
 
524
  def test_recommendation_hint_includes_learned_semantics(self):
525
  """PROVES: TOIN recommendation includes learned field semantics for SmartCrusher."""
 
553
  # VERIFY
554
  assert hint is not None
555
  assert len(hint.field_semantics) > 0, (
556
+ f"Recommendation should include field_semantics. Got: {hint.field_semantics}"
 
557
  )
558
  assert status_hash in hint.field_semantics
559