superlocalmemory 3.5.8 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/ATTRIBUTION.md +24 -0
  2. package/CHANGELOG.md +35 -0
  3. package/README.md +142 -35
  4. package/package.json +1 -1
  5. package/pyproject.toml +2 -1
  6. package/src/superlocalmemory/__init__.py +1 -1
  7. package/src/superlocalmemory/cli/cache_cmd.py +198 -0
  8. package/src/superlocalmemory/cli/commands.py +80 -2
  9. package/src/superlocalmemory/cli/compress_cmd.py +179 -0
  10. package/src/superlocalmemory/cli/help_cmd.py +197 -0
  11. package/src/superlocalmemory/cli/main.py +122 -0
  12. package/src/superlocalmemory/cli/optimize_cmd.py +175 -0
  13. package/src/superlocalmemory/cli/optimize_constants.py +31 -0
  14. package/src/superlocalmemory/cli/proxy_cmd.py +95 -0
  15. package/src/superlocalmemory/core/config.py +5 -0
  16. package/src/superlocalmemory/core/engine.py +23 -0
  17. package/src/superlocalmemory/core/mcp_embedder_proxy.py +89 -0
  18. package/src/superlocalmemory/llm/backbone.py +10 -4
  19. package/src/superlocalmemory/mcp/server.py +34 -0
  20. package/src/superlocalmemory/mcp/tools_v3.py +6 -2
  21. package/src/superlocalmemory/optimize/NOTICE +11 -0
  22. package/src/superlocalmemory/optimize/__init__.py +0 -0
  23. package/src/superlocalmemory/optimize/adapters/__init__.py +68 -0
  24. package/src/superlocalmemory/optimize/adapters/_agent_registry.py +120 -0
  25. package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +115 -0
  26. package/src/superlocalmemory/optimize/adapters/openai_adapter.py +125 -0
  27. package/src/superlocalmemory/optimize/adapters/wrap.py +188 -0
  28. package/src/superlocalmemory/optimize/cache/__init__.py +31 -0
  29. package/src/superlocalmemory/optimize/cache/boundary_store.py +455 -0
  30. package/src/superlocalmemory/optimize/cache/centroid_store.py +158 -0
  31. package/src/superlocalmemory/optimize/cache/context_key.py +67 -0
  32. package/src/superlocalmemory/optimize/cache/exact.py +85 -0
  33. package/src/superlocalmemory/optimize/cache/invalidation.py +36 -0
  34. package/src/superlocalmemory/optimize/cache/key_builder.py +98 -0
  35. package/src/superlocalmemory/optimize/cache/manager.py +452 -0
  36. package/src/superlocalmemory/optimize/cache/semantic.py +568 -0
  37. package/src/superlocalmemory/optimize/cache/stampede.py +50 -0
  38. package/src/superlocalmemory/optimize/compress/__init__.py +17 -0
  39. package/src/superlocalmemory/optimize/compress/align.py +153 -0
  40. package/src/superlocalmemory/optimize/compress/ccr.py +157 -0
  41. package/src/superlocalmemory/optimize/compress/extractive_code.py +311 -0
  42. package/src/superlocalmemory/optimize/compress/extractive_json.py +72 -0
  43. package/src/superlocalmemory/optimize/compress/prose_llmlingua.py +77 -0
  44. package/src/superlocalmemory/optimize/compress/router.py +548 -0
  45. package/src/superlocalmemory/optimize/config/__init__.py +35 -0
  46. package/src/superlocalmemory/optimize/config/defaults.py +48 -0
  47. package/src/superlocalmemory/optimize/config/schema.py +255 -0
  48. package/src/superlocalmemory/optimize/config/store.py +209 -0
  49. package/src/superlocalmemory/optimize/metrics/__init__.py +8 -0
  50. package/src/superlocalmemory/optimize/metrics/counters.py +138 -0
  51. package/src/superlocalmemory/optimize/metrics/estimator.py +90 -0
  52. package/src/superlocalmemory/optimize/metrics/exporters.py +77 -0
  53. package/src/superlocalmemory/optimize/metrics/persistence.py +115 -0
  54. package/src/superlocalmemory/optimize/proxy/__init__.py +28 -0
  55. package/src/superlocalmemory/optimize/proxy/_helpers.py +257 -0
  56. package/src/superlocalmemory/optimize/proxy/anthropic_surface.py +171 -0
  57. package/src/superlocalmemory/optimize/proxy/gemini_surface.py +121 -0
  58. package/src/superlocalmemory/optimize/proxy/lifecycle.py +126 -0
  59. package/src/superlocalmemory/optimize/proxy/openai_surface.py +125 -0
  60. package/src/superlocalmemory/optimize/proxy/server.py +151 -0
  61. package/src/superlocalmemory/optimize/storage/__init__.py +0 -0
  62. package/src/superlocalmemory/optimize/storage/db.py +1016 -0
  63. package/src/superlocalmemory/optimize/storage/schema.py +184 -0
  64. package/src/superlocalmemory/server/routes/optimize.py +166 -0
  65. package/src/superlocalmemory/server/routes/v3_api.py +63 -1
  66. package/src/superlocalmemory/server/unified_daemon.py +105 -0
  67. package/src/superlocalmemory/ui/index.html +98 -0
  68. package/src/superlocalmemory/ui/js/ng-shell.js +2 -1
  69. package/src/superlocalmemory/ui/js/optimize.js +173 -0
  70. package/src/superlocalmemory.egg-info/PKG-INFO +2 -1
  71. package/src/superlocalmemory.egg-info/SOURCES.txt +51 -0
  72. package/src/superlocalmemory.egg-info/requires.txt +1 -0
@@ -0,0 +1,568 @@
1
+ # optimize/cache/semantic.py
2
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
3
+ # Licensed under AGPL-3.0-or-later
4
+ #
5
+ # LLD-03 — VCacheSemantic: verified semantic cache tier (Phase 3).
6
+ #
7
+ # Implements the vCache per-item online-MLE exploit/explore decision
8
+ # (arXiv:2502.03771, Eq. 9/10/11, Algorithm 2, Theorem 4.1) on top of:
9
+ # - SAFE-CACHE centroid defense (Nature Scientific Reports 2026)
10
+ # - Dual-threshold verify-and-rewrite (SLM proprietary extension)
11
+ # - Multi-turn context-aware keys (arXiv:2506.22791 §3)
12
+ # - CacheAttack mitigation (arXiv:2601.23088)
13
+ #
14
+ # OFF BY DEFAULT (OptimizeConfig.semantic_enabled = False).
15
+ # Exact cache (P1) always runs first; semantic only runs on exact miss.
16
+ # Fail-open: any scoring error → treat as miss, forward to provider.
17
+
18
+ from __future__ import annotations
19
+
20
+ import hashlib
21
+ import logging
22
+ import random
23
+ import threading
24
+ import time
25
+ from typing import TYPE_CHECKING, Any
26
+
27
+ import numpy as np
28
+
29
+ from superlocalmemory.optimize.cache.boundary_store import (
30
+ BoundaryStore,
31
+ PerItemBoundaryRecord,
32
+ )
33
+ from superlocalmemory.optimize.cache.centroid_store import (
34
+ CentroidStore,
35
+ _cosine_similarity,
36
+ )
37
+ from superlocalmemory.optimize.cache.context_key import ContextKeyBuilder
38
+ from superlocalmemory.optimize.cache.manager import SemanticTier
39
+
40
+ if TYPE_CHECKING:
41
+ from superlocalmemory.optimize.config.schema import OptimizeConfig
42
+ from superlocalmemory.optimize.storage.db import CacheDB
43
+
44
+ logger = logging.getLogger(__name__)
45
+
46
+ _EMBED_DIM: int = 768
47
+ _DEFAULT_MAX_TURNS: int = 6
48
+ _DEFAULT_CONTEXT_WINDOW: int = 3
49
+ _DEFAULT_RETURN_THRESHOLD: float = 0.98
50
+ _DEFAULT_VERIFY_LO: float = 0.90
51
+ _DEFAULT_ERROR_TARGET: float = 0.02
52
+ _DEFAULT_BOUNDARY_INIT: float = 0.95
53
+ _DEFAULT_CENTROID_FLOOR: float = 0.15
54
+
55
+
56
+ class VCacheSemantic(SemanticTier):
57
+ """Verified semantic cache tier.
58
+
59
+ Lookup flow (per SemanticTier.lookup call):
60
+ 1. is_enabled() gate — return None if off.
61
+ 2. Multi-turn guard — skip if turn_count > max_turns.
62
+ 3. SAFE-CACHE centroid check — reject adversarial probe.
63
+ 4. Lazy per-tenant warm of in-memory vector index.
64
+ 5. ANN search (linear cosine scan) with HARD context-fp exclusion.
65
+ 6. vCache per-item exploit/explore via MLE τ̂.
66
+ 7. Dual-threshold decision (return / verify-and-rewrite / miss).
67
+ 8. Latency padding (side-channel defense).
68
+ 9. Return CachedResponse or None.
69
+
70
+ Storage flow:
71
+ - index_entry() persists vector to DB + in-memory index.
72
+ - learn() updates the per-item MLE model.
73
+ - is_enabled() reads config.semantic_enabled (hot-reloadable).
74
+ """
75
+
76
+ def __init__(
77
+ self,
78
+ db: "CacheDB",
79
+ config: "OptimizeConfig",
80
+ ) -> None:
81
+ self._db = db
82
+ self._config = config
83
+ # TODO(v3.7): when entry_count > 10_000, promote to sqlite-vec. Config flag: semantic_use_vec.
84
+
85
+ self._boundary_store = BoundaryStore(
86
+ db=db,
87
+ default_t=float(getattr(config, "semantic_boundary_init", _DEFAULT_BOUNDARY_INIT)),
88
+ default_gamma=10.0,
89
+ floor=float(getattr(config, "semantic_boundary_floor", 0.85)),
90
+ ceiling=float(getattr(config, "semantic_boundary_ceiling", 0.995)),
91
+ step=float(getattr(config, "semantic_boundary_step", 0.01)),
92
+ epsilon=float(getattr(config, "semantic_error_target", _DEFAULT_ERROR_TARGET)),
93
+ )
94
+ self._centroid_store = CentroidStore()
95
+ self._context_key_builder = ContextKeyBuilder(
96
+ window_turns=int(getattr(config, "semantic_context_window_turns", _DEFAULT_CONTEXT_WINDOW))
97
+ )
98
+
99
+ # In-memory vector index: tenant_id → list of (entry_id, context_fp, vec)
100
+ # Mutated by _set_inner() (under _index_lock) and rebuilt lazily
101
+ # per-tenant by _lazy_warm_tenant().
102
+ self._index: dict[str, list[tuple[str, str, np.ndarray]]] = {}
103
+ self._index_lock = threading.RLock()
104
+
105
+ # RA-09 fix: per-tenant warm guard — prevents double-warm race.
106
+ self._warming: set[str] = set()
107
+ self._warming_lock = threading.Lock()
108
+
109
+ # Warm boundary records from DB at startup (lightweight — no I/O heavy).
110
+ try:
111
+ self._boundary_store._cache = self._boundary_store.load_all()
112
+ except Exception as exc:
113
+ logger.warning("VCacheSemantic: boundary warm failed (fail-open): %s", exc)
114
+
115
+ # ------------------------------------------------------------------
116
+ # SemanticTier ABC (INTERFACE-CONTRACT v2 §4)
117
+ # ------------------------------------------------------------------
118
+
119
+ def is_enabled(self) -> bool:
120
+ """Return True iff OptimizeConfig.semantic_enabled is True."""
121
+ return bool(getattr(self._config, "semantic_enabled", False))
122
+
123
+ def lookup(
124
+ self,
125
+ req: Any,
126
+ tenant_id: str,
127
+ embed: list[float] | np.ndarray | None,
128
+ ) -> dict[str, Any] | None:
129
+ """Return semantically similar cached response or None.
130
+
131
+ Fail-open: any exception → log at WARNING + return None.
132
+ """
133
+ if not self.is_enabled():
134
+ return None
135
+ try:
136
+ if embed is None:
137
+ return None
138
+ vec = np.asarray(embed, dtype=np.float32)
139
+ if vec.shape[0] != _EMBED_DIM:
140
+ logger.debug(
141
+ "VCacheSemantic.lookup: skip — embed dim=%d (expected %d)",
142
+ vec.shape[0], _EMBED_DIM,
143
+ )
144
+ return None
145
+ return self._lookup_inner(req, tenant_id, vec)
146
+ except Exception as exc:
147
+ logger.warning(
148
+ "VCacheSemantic.lookup failed (fail-open): tenant=%s exc=%s",
149
+ tenant_id, exc, exc_info=True,
150
+ )
151
+ return None
152
+
153
+ def learn(
154
+ self,
155
+ entry_id: str,
156
+ similarity: float,
157
+ was_correct: bool,
158
+ ) -> None:
159
+ """Update the per-item MLE model with a new (similarity, correctness) pair.
160
+
161
+ Called by CacheManager on feedback. Delegates to BoundaryStore.record_outcome().
162
+ Fail-open.
163
+ """
164
+ try:
165
+ self._boundary_store.record_outcome(
166
+ entry_id=entry_id,
167
+ similarity=similarity,
168
+ was_correct=was_correct,
169
+ )
170
+ except Exception as exc:
171
+ logger.warning(
172
+ "VCacheSemantic.learn failed (fail-open): entry=%s exc=%s",
173
+ entry_id, exc, exc_info=True,
174
+ )
175
+
176
+ def index_entry(
177
+ self,
178
+ req: Any,
179
+ tenant_id: str,
180
+ embed: list[float] | np.ndarray | None,
181
+ resp: Any,
182
+ ) -> None:
183
+ """Index a new response vector in the ANN index and DB.
184
+
185
+ INTERFACE-CONTRACT v2 §4 canonical signature. entry_id and
186
+ context_fp are derived internally (req → query text + tenant).
187
+ Fail-open.
188
+ """
189
+ if not self.is_enabled():
190
+ return
191
+ try:
192
+ if embed is None:
193
+ return
194
+ messages = _extract_messages(req)
195
+ system = _extract_system(req)
196
+ query_text = self._build_query_text(messages, system)
197
+ entry_id = self._derive_entry_id(tenant_id, query_text)
198
+ context_fp = self._context_key_builder.build(messages, tenant_id)
199
+ self._set_inner(tenant_id, entry_id, embed, context_fp)
200
+ # Also persist the response under the surrogate entry_id so
201
+ # _fetch_response(entry_id) can retrieve it on a semantic hit.
202
+ if resp is not None:
203
+ try:
204
+ import json as _json
205
+ body = resp.body if hasattr(resp, "body") else (
206
+ resp if isinstance(resp, dict) else None
207
+ )
208
+ if body is None and hasattr(resp, "body_bytes"):
209
+ try:
210
+ body = _json.loads(resp.body_bytes)
211
+ except Exception:
212
+ body = None
213
+ if body is None and isinstance(resp, dict):
214
+ body = resp
215
+ if body is not None:
216
+ value = _json.dumps(body, separators=(",", ":")).encode("utf-8")
217
+ cache_key = f"sem:{entry_id}"
218
+ # Use set_with_entry_id to pin entry_id = surrogate directly,
219
+ # replacing the previous raw SQL UPDATE workaround.
220
+ self._db.set_with_entry_id(
221
+ key=cache_key,
222
+ tenant_id=tenant_id,
223
+ value=value,
224
+ entry_id=entry_id,
225
+ tags=["semantic"],
226
+ )
227
+ # Mark as semantic tier using CacheDB.execute (H-03 fix)
228
+ try:
229
+ self._db.execute(
230
+ "UPDATE llmcache_entries SET cache_tier = 'semantic' "
231
+ "WHERE cache_key = ? AND tenant_id = ?",
232
+ (cache_key, tenant_id),
233
+ )
234
+ except Exception as exc:
235
+ logger.debug(
236
+ "VCacheSemantic.index_entry: cache_tier update failed (non-fatal): %s",
237
+ exc,
238
+ )
239
+ except Exception as exc:
240
+ logger.debug(
241
+ "VCacheSemantic.index_entry: response persist failed (non-fatal): %s",
242
+ exc,
243
+ )
244
+ except Exception as exc:
245
+ logger.warning(
246
+ "VCacheSemantic.index_entry failed (fail-open): tenant=%s exc=%s",
247
+ tenant_id, exc, exc_info=True,
248
+ )
249
+
250
+ # ------------------------------------------------------------------
251
+ # Internal lookup
252
+ # ------------------------------------------------------------------
253
+
254
+ def _lookup_inner(
255
+ self,
256
+ req: Any,
257
+ tenant_id: str,
258
+ vec: np.ndarray,
259
+ ) -> dict[str, Any] | None:
260
+ """Core lookup — called by lookup() after is_enabled() check."""
261
+ cfg = self._config
262
+ max_turns = int(getattr(cfg, "semantic_max_turns_for_semantic", _DEFAULT_MAX_TURNS))
263
+ messages = _extract_messages(req)
264
+
265
+ # Step 1: Multi-turn guard
266
+ turn_count = self._context_key_builder.turn_count(messages)
267
+ if turn_count > max_turns:
268
+ logger.debug(
269
+ "VCacheSemantic: skip (turn_count=%d > max=%d)",
270
+ turn_count, max_turns,
271
+ )
272
+ return None
273
+
274
+ # Step 2: SAFE-CACHE centroid defense
275
+ if bool(getattr(cfg, "semantic_centroid_defense", True)):
276
+ distance_floor = float(
277
+ getattr(cfg, "semantic_centroid_distance_floor", _DEFAULT_CENTROID_FLOOR)
278
+ )
279
+ if self._centroid_store.is_adversarial(tenant_id, vec, distance_floor):
280
+ return None
281
+
282
+ # Step 3: Lazy per-tenant warm
283
+ with self._index_lock:
284
+ is_warm = tenant_id in self._index
285
+ if not is_warm:
286
+ self._lazy_warm_tenant(tenant_id)
287
+
288
+ # Step 4: ANN search
289
+ context_fp = self._context_key_builder.build(messages, tenant_id)
290
+ best_entry_id, best_score = self._ann_search(tenant_id, vec, context_fp)
291
+ if best_entry_id is None:
292
+ return None
293
+
294
+ # Step 5: vCache exploit/explore (MLE τ̂ via record_outcome updates)
295
+ record = self._boundary_store.get(best_entry_id)
296
+ delta = float(getattr(cfg, "semantic_error_target", _DEFAULT_ERROR_TARGET))
297
+ if record.should_explore(best_score, delta=delta):
298
+ logger.debug(
299
+ "VCacheSemantic: explore (score=%.4f entry=%s t_hat=%.4f)",
300
+ best_score, best_entry_id, record.t_hat,
301
+ )
302
+ return None
303
+
304
+ # Step 6: Dual-threshold decision
305
+ return self._dual_threshold_decision(
306
+ best_entry_id, best_score, cfg,
307
+ )
308
+
309
+ def _dual_threshold_decision(
310
+ self,
311
+ entry_id: str,
312
+ score: float,
313
+ cfg: "OptimizeConfig",
314
+ ) -> dict[str, Any] | None:
315
+ """Apply dual-threshold: return / verify-and-rewrite / miss.
316
+
317
+ SLM-proprietary extension (NOT vCache):
318
+ score >= return_threshold: return immediately
319
+ verify_lo <= score < return_threshold: verify-and-rewrite path
320
+ score < verify_lo: miss
321
+ """
322
+ response = self._fetch_response(entry_id)
323
+ if response is None:
324
+ return None
325
+
326
+ return_threshold = float(getattr(cfg, "semantic_return_threshold", _DEFAULT_RETURN_THRESHOLD))
327
+ verify_lo = float(getattr(cfg, "semantic_verify_lo", _DEFAULT_VERIFY_LO))
328
+
329
+ if score >= return_threshold:
330
+ return response
331
+
332
+ if verify_lo <= score < return_threshold:
333
+ verifier_model = str(getattr(cfg, "semantic_verifier_model", "") or "")
334
+ if not verifier_model:
335
+ logger.debug(
336
+ "VCacheSemantic: verify zone, no verifier configured — miss "
337
+ "(score=%.4f entry=%s)", score, entry_id,
338
+ )
339
+ return None
340
+ verified, rewritten = self._verify_and_rewrite(
341
+ entry_id, response, verifier_model,
342
+ )
343
+ if verified:
344
+ return rewritten if rewritten is not None else response
345
+ return None
346
+
347
+ return None
348
+
349
+ def _lazy_warm_tenant(self, tenant_id: str) -> None:
350
+ """Warm the in-memory vector index for a tenant on first access.
351
+
352
+ RA-09 fix: double-warm race eliminated via _warming guard.
353
+ """
354
+ with self._warming_lock:
355
+ if tenant_id in self._warming:
356
+ return
357
+ self._warming.add(tenant_id)
358
+
359
+ try:
360
+ rows = self._db.get_all_vectors(tenant_id=tenant_id)
361
+ if not rows:
362
+ with self._index_lock:
363
+ self._index[tenant_id] = []
364
+ return
365
+ entries: list[tuple[str, str, np.ndarray]] = []
366
+ for entry_id, blob in rows:
367
+ try:
368
+ v = np.frombuffer(blob, dtype=np.float32).copy()
369
+ if v.shape[0] == _EMBED_DIM:
370
+ entries.append((entry_id, "", v))
371
+ except Exception:
372
+ continue
373
+ with self._index_lock:
374
+ self._index[tenant_id] = entries
375
+ self._centroid_store.rebuild_from_db(self._db, tenant_id)
376
+ except Exception as exc:
377
+ logger.warning(
378
+ "VCacheSemantic._lazy_warm_tenant failed (fail-open): tenant=%s exc=%s",
379
+ tenant_id, exc,
380
+ )
381
+ with self._index_lock:
382
+ self._index.setdefault(tenant_id, [])
383
+ finally:
384
+ with self._warming_lock:
385
+ self._warming.discard(tenant_id)
386
+
387
+ def _ann_search(
388
+ self,
389
+ tenant_id: str,
390
+ query_vec: np.ndarray,
391
+ context_fp: str,
392
+ ) -> tuple[str | None, float]:
393
+ """Linear cosine scan with HARD context-fp exclusion (A-13 fix)."""
394
+ with self._index_lock:
395
+ entries = list(self._index.get(tenant_id, []))
396
+ if not entries:
397
+ return None, 0.0
398
+ best_id: str | None = None
399
+ best_score: float = -1.0
400
+ for entry_id, entry_ctx_fp, entry_vec in entries:
401
+ if entry_ctx_fp and entry_ctx_fp != context_fp:
402
+ continue # hard exclude — different conversational context
403
+ score = _cosine_similarity(query_vec, entry_vec)
404
+ if score > best_score:
405
+ best_score = score
406
+ best_id = entry_id
407
+ return best_id, best_score
408
+
409
+ def _set_inner(
410
+ self,
411
+ tenant_id: str,
412
+ entry_id: str,
413
+ embed: list[float] | np.ndarray,
414
+ context_fp: str = "",
415
+ ) -> None:
416
+ """Persist vector + boundary record; update in-memory index + centroid."""
417
+ vec = np.asarray(embed, dtype=np.float32)
418
+ if vec.shape[0] != _EMBED_DIM:
419
+ logger.warning(
420
+ "VCacheSemantic._set_inner: unexpected embedding dim %d (expected %d) "
421
+ "for entry=%s — skipping", vec.shape[0], _EMBED_DIM, entry_id,
422
+ )
423
+ return
424
+
425
+ # Store vector in DB
426
+ vec_bytes = vec.tobytes()
427
+ self._db.vec_add(
428
+ entry_id=entry_id,
429
+ tenant_id=tenant_id,
430
+ vector=vec_bytes,
431
+ meta={"model": "nomic-ai/nomic-embed-text-v1.5", "dim": _EMBED_DIM},
432
+ )
433
+
434
+ # Initialize boundary record if new
435
+ existing = self._boundary_store.get(entry_id)
436
+ if len(existing.samples) == 0 and existing.t_hat == float(
437
+ getattr(self._config, "semantic_boundary_init", _DEFAULT_BOUNDARY_INIT)
438
+ ) and not self._boundary_store._cache.get(entry_id):
439
+ # Persist the cold-start record only if it isn't already in DB
440
+ self._boundary_store.save(existing)
441
+
442
+ # Update in-memory index (dedupe)
443
+ with self._index_lock:
444
+ tenant_index = self._index.setdefault(tenant_id, [])
445
+ self._index[tenant_id] = [
446
+ e for e in tenant_index if e[0] != entry_id
447
+ ]
448
+ self._index[tenant_id].append((entry_id, context_fp, vec))
449
+
450
+ # Update centroid
451
+ self._centroid_store.update(tenant_id, vec)
452
+
453
+ # ------------------------------------------------------------------
454
+ # Helpers
455
+ # ------------------------------------------------------------------
456
+
457
+ def _fetch_response(self, entry_id: str) -> dict[str, Any] | None:
458
+ """Fetch the actual response dict for an entry_id via CacheDB."""
459
+ try:
460
+ return self._db.get_entry_by_id(entry_id)
461
+ except Exception as exc:
462
+ logger.warning(
463
+ "VCacheSemantic._fetch_response failed (fail-open): %s", exc,
464
+ )
465
+ return None
466
+
467
+ def _verify_and_rewrite(
468
+ self,
469
+ entry_id: str,
470
+ cached_response: dict[str, Any],
471
+ verifier_model: str,
472
+ ) -> tuple[bool, dict[str, Any] | None]:
473
+ """Phase 3.0 stub: return (True, None) — treat as verified, no rewrite.
474
+
475
+ Full implementation requires a sub-agent call to a cheap model.
476
+ Stub is conservative: boundary learning still fires, so over time
477
+ the boundary will tighten if errors accumulate.
478
+
479
+ A-03 fix: callers do NOT call record_outcome() on this stub path
480
+ (the True signal is fake — would poison the MLE model).
481
+ """
482
+ logger.debug(
483
+ "VCacheSemantic._verify_and_rewrite: stub returning True "
484
+ "(verifier=%s entry=%s)", verifier_model, entry_id,
485
+ )
486
+ return True, None
487
+
488
+ @staticmethod
489
+ def _build_query_text(messages: list[dict[str, Any]], system: str) -> str:
490
+ """Build a single embedding input from last user message + system."""
491
+ last_user = ""
492
+ for m in reversed(messages):
493
+ if m.get("role") == "user":
494
+ content = m.get("content", "")
495
+ last_user = content if isinstance(content, str) else str(content)
496
+ break
497
+ if system:
498
+ return f"{system}\n\n{last_user}"
499
+ return last_user
500
+
501
+ @staticmethod
502
+ def _derive_entry_id(tenant_id: str, query_text: str) -> str:
503
+ """Derive a stable surrogate entry_id from tenant + query_text.
504
+
505
+ INTERFACE-CONTRACT v2 §4: SemanticTier ABC does not pass entry_id.
506
+ Caller (CacheManager) passes the real UUID at the exact tier; the
507
+ semantic surrogate is deterministic and stable per query.
508
+ """
509
+ payload = f"{tenant_id}:{query_text}"
510
+ digest = hashlib.sha256(payload.encode("utf-8")).hexdigest()[:32]
511
+ return f"sem:{digest}"
512
+
513
+
514
+ # ---------------------------------------------------------------------------
515
+ # Module-level helpers — robust to both dict and attribute access
516
+ # ---------------------------------------------------------------------------
517
+
518
+ def _extract_messages(req: Any) -> list[dict[str, Any]]:
519
+ """Pull messages list from req (dict, dataclass, or SimpleNamespace).
520
+
521
+ Looks at: req.messages → req.body.messages → [].
522
+ """
523
+ if req is None:
524
+ return []
525
+ if isinstance(req, dict):
526
+ msgs = req.get("messages", []) or []
527
+ if isinstance(msgs, list):
528
+ return msgs
529
+ msgs = getattr(req, "messages", None)
530
+ if isinstance(msgs, list) and msgs:
531
+ return msgs
532
+ body = getattr(req, "body", None)
533
+ if isinstance(body, dict):
534
+ msgs = body.get("messages", []) or []
535
+ if isinstance(msgs, list):
536
+ return msgs
537
+ return []
538
+
539
+
540
+ def _extract_system(req: Any) -> str:
541
+ """Pull system prompt from req (dict, dataclass, or SimpleNamespace).
542
+
543
+ Looks at: req.system → req.body.system → "".
544
+ """
545
+ if req is None:
546
+ return ""
547
+ if isinstance(req, dict):
548
+ return str(req.get("system", "") or "")
549
+ s = getattr(req, "system", None)
550
+ if s:
551
+ return str(s)
552
+ body = getattr(req, "body", None)
553
+ if isinstance(body, dict):
554
+ return str(body.get("system", "") or "")
555
+ return ""
556
+
557
+
558
+ # Latency padding helper (exported for tests)
559
+
560
+ def apply_latency_padding(pad_ms: float) -> None:
561
+ """Sleep for a random duration in [0, pad_ms] milliseconds.
562
+
563
+ Side-channel defense (arXiv:2601.23088 §4). Tests can monkeypatch
564
+ time.sleep to no-op.
565
+ """
566
+ if pad_ms <= 0:
567
+ return
568
+ time.sleep(random.uniform(0, pad_ms / 1000.0))
@@ -0,0 +1,50 @@
1
+ """stampede.py — per-key mutex preventing cache stampede."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import threading
6
+ from contextlib import contextmanager
7
+ from typing import Generator
8
+
9
+
10
+ class StampedeShield:
11
+ """Per-key mutex preventing cache stampede (thundering-herd) on cold miss."""
12
+
13
+ def __init__(self, timeout: float = 30.0) -> None:
14
+ self._timeout = timeout
15
+ self._locks: dict[str, threading.RLock] = {}
16
+ self._refcounts: dict[str, int] = {}
17
+ self._meta_lock = threading.Lock()
18
+
19
+ def _acquire_key_lock(self, key: str) -> threading.RLock:
20
+ with self._meta_lock:
21
+ if key not in self._locks:
22
+ self._locks[key] = threading.RLock()
23
+ self._refcounts[key] = 0
24
+ self._refcounts[key] += 1
25
+ return self._locks[key]
26
+
27
+ def _release_key_lock(self, key: str) -> None:
28
+ with self._meta_lock:
29
+ count = self._refcounts.get(key, 0) - 1
30
+ if count <= 0:
31
+ self._locks.pop(key, None)
32
+ self._refcounts.pop(key, None)
33
+ else:
34
+ self._refcounts[key] = count
35
+
36
+ @contextmanager
37
+ def lock(self, key: str) -> Generator[None, None, None]:
38
+ rlock = self._acquire_key_lock(key)
39
+ acquired = rlock.acquire(timeout=self._timeout)
40
+ if not acquired:
41
+ try:
42
+ yield
43
+ finally:
44
+ self._release_key_lock(key)
45
+ return
46
+ try:
47
+ yield
48
+ finally:
49
+ rlock.release()
50
+ self._release_key_lock(key)
@@ -0,0 +1,17 @@
1
+ # compress/__init__.py
2
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
3
+ # Licensed under AGPL-3.0-or-later
4
+
5
+ """SLM v3.6 Optimize — Compression module.
6
+
7
+ Public surface:
8
+ CompressRouter — implements CompressHook Protocol from lifecycle.py
9
+ CCRStore — CCR original storage and retrieval
10
+ CacheAligner — volatile-token detection for system prompt prefix stability
11
+ """
12
+
13
+ from superlocalmemory.optimize.compress.router import CompressRouter, CompressTextResult
14
+ from superlocalmemory.optimize.compress.ccr import CCRStore
15
+ from superlocalmemory.optimize.compress.align import CacheAligner, AlignResult
16
+
17
+ __all__ = ["CompressRouter", "CompressTextResult", "CCRStore", "CacheAligner", "AlignResult"]