cortexm 0.3.0__py3-none-any.whl

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 (120) hide show
  1. context_m.py +17 -0
  2. cortexm/__init__.py +45 -0
  3. cortexm/accel.py +403 -0
  4. cortexm/api/__init__.py +0 -0
  5. cortexm/api/chaos.py +118 -0
  6. cortexm/api/memory.py +635 -0
  7. cortexm/bench/__init__.py +0 -0
  8. cortexm/bench/abilities.py +311 -0
  9. cortexm/bench/baselines.py +89 -0
  10. cortexm/bench/beam_loader.py +317 -0
  11. cortexm/bench/generator.py +376 -0
  12. cortexm/bench/harness.py +211 -0
  13. cortexm/bench/messy.py +218 -0
  14. cortexm/bench/micro.py +251 -0
  15. cortexm/bench/ood.py +443 -0
  16. cortexm/bench/run.py +137 -0
  17. cortexm/bridge/__init__.py +0 -0
  18. cortexm/bridge/dates.py +178 -0
  19. cortexm/bridge/decoders.py +204 -0
  20. cortexm/bridge/enrich.py +255 -0
  21. cortexm/bridge/extractor.py +316 -0
  22. cortexm/bridge/fallback.py +332 -0
  23. cortexm/bridge/onnx_runtime.py +158 -0
  24. cortexm/bridge/patterns.py +760 -0
  25. cortexm/bridge/ppr.py +104 -0
  26. cortexm/bridge/prefilter.py +188 -0
  27. cortexm/bridge/query_extract.py +420 -0
  28. cortexm/bridge/reader.py +1174 -0
  29. cortexm/bridge/rerank.py +204 -0
  30. cortexm/bridge/writer.py +492 -0
  31. cortexm/cli.py +295 -0
  32. cortexm/cognition/__init__.py +53 -0
  33. cortexm/cognition/abstraction.py +192 -0
  34. cortexm/cognition/analogy.py +159 -0
  35. cortexm/cognition/engine.py +204 -0
  36. cortexm/cognition/gaps.py +365 -0
  37. cortexm/cognition/scanner.py +204 -0
  38. cortexm/config.py +375 -0
  39. cortexm/cortexm.py +8 -0
  40. cortexm/enterprise/__init__.py +0 -0
  41. cortexm/enterprise/audit.py +178 -0
  42. cortexm/enterprise/governance.py +239 -0
  43. cortexm/errors.py +35 -0
  44. cortexm/features/__init__.py +0 -0
  45. cortexm/features/git.py +204 -0
  46. cortexm/features/prefetch.py +88 -0
  47. cortexm/features/zk.py +105 -0
  48. cortexm/federation/__init__.py +39 -0
  49. cortexm/federation/crdt.py +275 -0
  50. cortexm/federation/fabric.py +109 -0
  51. cortexm/federation/hlc.py +80 -0
  52. cortexm/federation/node.py +145 -0
  53. cortexm/federation/schema_report.py +73 -0
  54. cortexm/federation/transport.py +164 -0
  55. cortexm/index/__init__.py +19 -0
  56. cortexm/index/nsg.py +386 -0
  57. cortexm/mcp/__init__.py +0 -0
  58. cortexm/mcp/server.py +985 -0
  59. cortexm/metrics.py +62 -0
  60. cortexm/migrate/__init__.py +0 -0
  61. cortexm/migrate/importers.py +192 -0
  62. cortexm/provenance/__init__.py +78 -0
  63. cortexm/provenance/agent.py +214 -0
  64. cortexm/provenance/cose.py +201 -0
  65. cortexm/provenance/scitt.py +258 -0
  66. cortexm/provenance/vc.py +250 -0
  67. cortexm/security/__init__.py +0 -0
  68. cortexm/security/crypto.py +162 -0
  69. cortexm/security/hashes.py +140 -0
  70. cortexm/security/injection.py +149 -0
  71. cortexm/security/mind.py +154 -0
  72. cortexm/security/pii.py +265 -0
  73. cortexm/security/rbac.py +169 -0
  74. cortexm/security/sandbox.py +131 -0
  75. cortexm/security/zk_hamming.py +142 -0
  76. cortexm/security/zk_sql.py +485 -0
  77. cortexm/server/__init__.py +0 -0
  78. cortexm/server/metrics.py +88 -0
  79. cortexm/server/rest.py +936 -0
  80. cortexm/server/sparql.py +984 -0
  81. cortexm/text/__init__.py +0 -0
  82. cortexm/text/dissim.py +252 -0
  83. cortexm/text/embedder.py +155 -0
  84. cortexm/text/fuzzy.py +218 -0
  85. cortexm/text/idiolect.py +253 -0
  86. cortexm/text/labse.py +374 -0
  87. cortexm/text/tokenizer.py +79 -0
  88. cortexm/trace/__init__.py +0 -0
  89. cortexm/trace/blob_arena.py +277 -0
  90. cortexm/trace/consolidate.py +337 -0
  91. cortexm/trace/contradictions.py +69 -0
  92. cortexm/trace/dedup.py +114 -0
  93. cortexm/trace/edges.py +214 -0
  94. cortexm/trace/fact.py +121 -0
  95. cortexm/trace/fade.py +245 -0
  96. cortexm/trace/lifecycle.py +112 -0
  97. cortexm/trace/rebuild.py +173 -0
  98. cortexm/trace/rules.py +171 -0
  99. cortexm/trace/store.py +680 -0
  100. cortexm/trace/structural.py +183 -0
  101. cortexm/trace/tmt.py +335 -0
  102. cortexm/util.py +148 -0
  103. cortexm/vsa/__init__.py +0 -0
  104. cortexm/vsa/attribution.py +149 -0
  105. cortexm/vsa/cleanup.py +161 -0
  106. cortexm/vsa/codecs.py +397 -0
  107. cortexm/vsa/hologram_overlay.py +139 -0
  108. cortexm/vsa/index.py +163 -0
  109. cortexm/vsa/ops.py +149 -0
  110. cortexm/vsa/palace.py +446 -0
  111. cortexm/vsa/role_vectors.py +236 -0
  112. cortexm/vsa/slb.py +78 -0
  113. cortexm/vsa/tlsh_trie.py +137 -0
  114. cortexm/vsa/working_memory.py +249 -0
  115. cortexm-0.3.0.dist-info/METADATA +482 -0
  116. cortexm-0.3.0.dist-info/RECORD +120 -0
  117. cortexm-0.3.0.dist-info/WHEEL +5 -0
  118. cortexm-0.3.0.dist-info/entry_points.txt +2 -0
  119. cortexm-0.3.0.dist-info/licenses/LICENSE +190 -0
  120. cortexm-0.3.0.dist-info/top_level.txt +2 -0
context_m.py ADDED
@@ -0,0 +1,17 @@
1
+ """Backward-compat shim. The canonical module is now ``cortexm``.
2
+
3
+ This file exists so existing scripts that did::
4
+
5
+ from context_m import Memory
6
+
7
+ keep working after `pip install cortexm`. New code should use::
8
+
9
+ from cortexm import Memory
10
+
11
+ The shim will be removed in a future major release; migrate at your
12
+ leisure. The shim imports lazily so it adds ~0ms to cold-start when
13
+ nobody uses the old name.
14
+ """
15
+ from cortexm import Memory, Config, __version__
16
+
17
+ __all__ = ["Memory", "Config", "__version__"]
cortexm/__init__.py ADDED
@@ -0,0 +1,45 @@
1
+ """Context-M — The Universal Neuro-Symbolic Memory Fabric.
2
+
3
+ Layer 1 Symbolic Trace : bi-temporal fact graph with contradiction
4
+ resolution, temporal edges, Datalog-lite rules.
5
+ Layer 2 VSA Memory Palace: holographic reduced representations (HRR) with
6
+ INT8 / Binary-HRR / RaBitQ / PQ codecs, a
7
+ page-clustered tree index and a semantic
8
+ lookaside buffer (SLB).
9
+ Bridge : μ=0 deterministic ingest (zero LLM calls), neuro-symbolic read
10
+ path with cryptographic provenance on every retrieval.
11
+
12
+ Mem0-compatible surface: ``from cortexm import Memory``
13
+ Alias (plan naming): ``from cortexm import Memory``
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ __version__ = "0.1.0"
19
+
20
+ # μ=0 protocol counter: number of LLM invocations used by this process.
21
+ # The BEAM-honest protocol requires this to stay 0 during ingest & retrieval.
22
+ LLM_CALLS = 0
23
+
24
+
25
+ def _lazy_memory():
26
+ from cortexm.api.memory import Memory
27
+
28
+ return Memory
29
+
30
+
31
+ def __getattr__(name: str):
32
+ if name == "Memory":
33
+ return _lazy_memory()
34
+ if name == "Config":
35
+ from cortexm.config import Config
36
+
37
+ return Config
38
+ if name == "LLM_CALLS":
39
+ from cortexm import metrics
40
+
41
+ return metrics.llm_calls()
42
+ raise AttributeError(name)
43
+
44
+
45
+ __all__ = ["Memory", "Config", "LLM_CALLS", "__version__"]
cortexm/accel.py ADDED
@@ -0,0 +1,403 @@
1
+ """Opportunistic Rust acceleration.
2
+
3
+ The Python/NumPy implementation is the REFERENCE and always works. If the
4
+ compiled wheels are installed (`pip install ./rust/cortexm-core` and
5
+ `pip install ./rust/quadrant`), the hot paths route through them:
6
+
7
+ * ``hashing.h64`` — keyed BLAKE2b (byte-exact parity, tested)
8
+ * ``vsa.bind/unbind`` — permutation gather with injected perms
9
+ * ``vsa.encode_fact`` — fused bind×3 + bundle + lexical mix + norm
10
+ * ``slb`` — L1-resident lookaside buffer
11
+ * ``quadrant`` — page-clustered log-depth vector index
12
+
13
+ Design rule: the Rust side never GENERATES randomness — permutations and
14
+ role vectors are injected from Python's deterministic VSA state, so a
15
+ mixed Python/Rust deployment produces bit-identical holograms. Every
16
+ accelerated path has a NumPy fallback and a parity test
17
+ (``tests/test_rust_accel.py``).
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import os
23
+ from typing import Any
24
+
25
+ # --- optional wheels -------------------------------------------------------
26
+ try: # pragma: no cover
27
+ import cortexm_core as _core # type: ignore
28
+ except Exception: # pragma: no cover
29
+ _core = None
30
+
31
+ try: # pragma: no cover
32
+ import quadrant as _quadrant # type: ignore
33
+ except Exception: # pragma: no cover
34
+ _quadrant = None
35
+
36
+ RUST_AVAILABLE = _core is not None
37
+ QUADRANT_AVAILABLE = _quadrant is not None
38
+ _env = os.environ.get("CONTEXTM_RUST", "auto")
39
+ RUST_ENABLED = (_env == "1") or (_env == "auto" and RUST_AVAILABLE)
40
+ QUADRANT_ENABLED = (_env == "1") or (_env == "auto" and QUADRANT_AVAILABLE)
41
+
42
+
43
+ def rust_status() -> dict[str, Any]:
44
+ return {
45
+ "cortexm_core": bool(RUST_AVAILABLE and RUST_ENABLED),
46
+ "quadrant": bool(QUADRANT_AVAILABLE and QUADRANT_ENABLED),
47
+ "env": _env,
48
+ "hint": ("" if RUST_AVAILABLE else
49
+ "build with: pip install ./rust/cortexm-core "
50
+ "./rust/quadrant (requires cargo + maturin)"),
51
+ }
52
+
53
+
54
+ # --- hashing ---------------------------------------------------------------
55
+ if RUST_AVAILABLE and RUST_ENABLED:
56
+ def h64(feature: str, seed: int = 0) -> int:
57
+ return _core.h64(feature, seed)
58
+ else: # pragma: no cover
59
+ from cortexm.util import h64 # noqa: F401 (NumPy reference)
60
+
61
+
62
+ # --- VSA acceleration wrapper ----------------------------------------------
63
+ class RustVSA:
64
+ """Binds a Python VSA to compiled bind/unbind/encode_fact.
65
+
66
+ The authoritative perms live in the Python VSA; Rust receives them
67
+ once via set_perm and accelerates the per-fact gathers afterwards.
68
+ """
69
+
70
+ def __init__(self, vsa) -> None:
71
+ if not (RUST_AVAILABLE and RUST_ENABLED):
72
+ raise RuntimeError("rust acceleration disabled or unavailable")
73
+ self.vsa = vsa
74
+ self._pb = _core.PermBindings(vsa.dims)
75
+ self._injected: set[str] = set()
76
+
77
+ def _inject(self, role: str) -> None:
78
+ if role not in self._injected:
79
+ self._pb.set_perm(role, self.vsa.perm(role).tolist())
80
+ self._injected.add(role)
81
+
82
+ def bind(self, role: str, filler):
83
+ import numpy as np
84
+ self._inject(role)
85
+ f = np.ascontiguousarray(filler, dtype=np.float32)
86
+ return self._pb.bind(role, f)
87
+
88
+ def unbind(self, role: str, h):
89
+ import numpy as np
90
+ self._inject(role)
91
+ hv = np.ascontiguousarray(h, dtype=np.float32)
92
+ return self._pb.unbind(role, hv)
93
+
94
+ def encode_fact(self, s_vec, r_vec, v_vec):
95
+ import numpy as np
96
+ for role in ("S", "R", "V"):
97
+ self._inject(role)
98
+ return self._pb.encode_fact(
99
+ np.ascontiguousarray(s_vec, dtype=np.float32),
100
+ np.ascontiguousarray(r_vec, dtype=np.float32),
101
+ np.ascontiguousarray(v_vec, dtype=np.float32),
102
+ self.vsa.lam)
103
+
104
+
105
+ # --- quadrant index wrapper --------------------------------------------------
106
+ class QuadrantANN:
107
+ """Page-clustered log-depth index over holograms (approximate top-k).
108
+
109
+ Falls back to exact numpy when the wheel is absent — callers get the
110
+ same interface either way.
111
+ """
112
+
113
+ def __init__(self, vectors, page_capacity: int = 64) -> None:
114
+ import numpy as np
115
+ self.vectors = np.ascontiguousarray(vectors, dtype=np.float32)
116
+ if QUADRANT_AVAILABLE and QUADRANT_ENABLED:
117
+ self._idx = _quadrant.QuadrantIndex.build(
118
+ self.vectors, page_capacity)
119
+ self._exact = False
120
+ else: # pragma: no cover
121
+ self._idx = None
122
+ self._exact = True
123
+
124
+ def search(self, query, k: int = 10, max_leaves: int = 16):
125
+ import numpy as np
126
+ q = np.ascontiguousarray(query, dtype=np.float32)
127
+ if self._exact: # pragma: no cover
128
+ sims = self.vectors @ q
129
+ part = np.argpartition(-sims, k)[:k]
130
+ order = part[np.argsort(-sims[part])]
131
+ return order.tolist(), sims[order].tolist()
132
+ ids, scores = self._idx.search(q, k, max_leaves)
133
+ return list(ids), list(scores)
134
+
135
+ def stats(self) -> dict:
136
+ if self._exact: # pragma: no cover
137
+ n = self.vectors.shape[0]
138
+ return {"n_vectors": n, "mode": "exact-numpy",
139
+ "note": "quadrant wheel not installed"}
140
+ return {"mode": "quadrant", "detail": self._idx.stats()}
141
+
142
+
143
+ # --- explicit binary/FP32 tiering ------------------------------------------
144
+ # User concern (architectural fix #1): "Binary HRR + FFT doesn't work →
145
+ # Separate tiers: binary for edge, FP32 for cloud."
146
+ # The codec stack already supports both (binary/RaBitQ for edge, PQ/INT8
147
+ # for cloud); this makes the routing EXPLICIT based on deployment tier.
148
+
149
+ EDGE_CODECS = ("binary", "rabitq") # 96 B/v — fits on Raspberry Pi 5
150
+ CLOUD_CODECS = ("pq", "int8") # 8 B/v or 770 B/v — bandwidth-dense
151
+
152
+
153
+ def detect_tier() -> str:
154
+ """Auto-detect deployment tier from environment signals.
155
+
156
+ Returns 'edge' or 'cloud'. Override via CONTEXTM_TIER env var.
157
+ """
158
+ explicit = os.environ.get("CONTEXTM_TIER", "").lower()
159
+ if explicit in ("edge", "cloud"):
160
+ return explicit
161
+ # heuristic: cloud = multi-core + >4GB RAM + has scipy
162
+ try:
163
+ import multiprocessing
164
+ if multiprocessing.cpu_count() >= 4:
165
+ import os as _os
166
+ mem = _os.sysconf("SC_PAGE_SIZE") * _os.sysconf("SC_PHYS_PAGES")
167
+ if mem >= 4 * 1024**3:
168
+ return "cloud"
169
+ except Exception:
170
+ pass
171
+ return "edge"
172
+
173
+
174
+ def recommend_codec(tier: str | None = None, tmr: bool = True) -> str:
175
+ """Recommend a codec for the deployment tier.
176
+
177
+ Edge: binary (with TMR if self-healing required) — 96 B/v.
178
+ Cloud: pq for bandwidth-dense, int8 for accuracy-critical.
179
+ """
180
+ t = tier or detect_tier()
181
+ if t == "edge":
182
+ return "binary" if tmr else "rabitq"
183
+ return "pq"
184
+
185
+
186
+ def tier_status() -> dict:
187
+ """Report current tier + recommended + active codec info."""
188
+ tier = detect_tier()
189
+ rec = recommend_codec(tier)
190
+ return {
191
+ "tier": tier,
192
+ "recommended_codec": rec,
193
+ "edge_codecs": list(EDGE_CODECS),
194
+ "cloud_codecs": list(CLOUD_CODECS),
195
+ "rust_enabled": RUST_ENABLED,
196
+ "quadrant_enabled": QUADRANT_ENABLED,
197
+ "note": (f"Auto-detected {tier} tier; recommend '{rec}' codec. "
198
+ f"Override with CONTEXTM_TIER=edge|cloud"),
199
+ }
200
+
201
+
202
+ # --- SIMD kernel wrappers (Task 6-simd) ------------------------------------
203
+ # The Rust crate exposes `dot / dot_i8_f32 / cosine / l2_sq /
204
+ # batch_dot / batch_dot_i8 / topk / argmax` — each is a thin pyo3
205
+ # wrapper around the runtime-dispatched kernels in `rust/cortexm-core/
206
+ # src/simd.rs` (AVX-512 → AVX2+FMA → NEON → scalar). When the wheel is
207
+ # absent we fall back to numpy so callers always get a working answer.
208
+ #
209
+ # Design rule (mirrors RustVSA): the kernels NEVER allocate randomness
210
+ # and produce bit-compatible results across the Rust / NumPy paths (≤1e-5
211
+ # FP32 noise, asserted by `tests/test_rust_accel.py::TestSimdKernels`).
212
+ # Use the free-function form (`accel.cosine(a, b)`) for one-off queries;
213
+ # use the class form (`accel.SimdKernels().batch_dot(...)`) when you
214
+ # want to gate behaviour on `RUST_ENABLED` without re-checking globals.
215
+
216
+
217
+ def _as_f32(x) -> "np.ndarray":
218
+ import numpy as np
219
+ return np.ascontiguousarray(x, dtype=np.float32)
220
+
221
+
222
+ def dot(a, b) -> float:
223
+ """SIMD dot product. NumPy fallback: `np.dot(a, b)`."""
224
+ if RUST_AVAILABLE and RUST_ENABLED:
225
+ import numpy as np
226
+ return float(_core.dot(np.ascontiguousarray(a, dtype=np.float32),
227
+ np.ascontiguousarray(b, dtype=np.float32)))
228
+ import numpy as np
229
+ return float(np.dot(_as_f32(a), _as_f32(b)))
230
+
231
+
232
+ def dot_i8_f32(q8, q) -> float:
233
+ """INT8 × f32 dot product. NumPy fallback casts int8 → f32 first."""
234
+ if RUST_AVAILABLE and RUST_ENABLED:
235
+ import numpy as np
236
+ return float(_core.dot_i8_f32(
237
+ np.ascontiguousarray(q8, dtype=np.int8),
238
+ np.ascontiguousarray(q, dtype=np.float32)))
239
+ import numpy as np
240
+ q8 = np.ascontiguousarray(q8, dtype=np.int8)
241
+ return float((q8.astype(np.float32) @ _as_f32(q)))
242
+
243
+
244
+ def cosine(a, b) -> float:
245
+ """Cosine similarity via SIMD-accelerated dot + L2 norms.
246
+
247
+ Identical vectors return exactly 1.0 (bit-exact through the
248
+ SIMD self-dot path); distinct vectors agree with numpy's
249
+ `a @ b / (|a|·|b|)` to within 1e-5 (FP32 lane-reduction noise).
250
+ """
251
+ if RUST_AVAILABLE and RUST_ENABLED:
252
+ import numpy as np
253
+ return float(_core.cosine(
254
+ np.ascontiguousarray(a, dtype=np.float32),
255
+ np.ascontiguousarray(b, dtype=np.float32)))
256
+ import numpy as np
257
+ a = _as_f32(a)
258
+ b = _as_f32(b)
259
+ return float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-12))
260
+
261
+
262
+ def l2_sq(a, b) -> float:
263
+ """Squared L2 distance `sum((a-b)**2)`. 0.0 for identical vectors."""
264
+ if RUST_AVAILABLE and RUST_ENABLED:
265
+ import numpy as np
266
+ return float(_core.l2_sq(
267
+ np.ascontiguousarray(a, dtype=np.float32),
268
+ np.ascontiguousarray(b, dtype=np.float32)))
269
+ import numpy as np
270
+ d = _as_f32(a) - _as_f32(b)
271
+ return float(d @ d)
272
+
273
+
274
+ def batch_dot(rows, q, n_rows: int, dims: int) -> list[float]:
275
+ """Matrix-vector product over a flat `[n_rows × dims]` f32 slice.
276
+
277
+ Far more cache-friendly than calling `dot()` per row from Python —
278
+ one boundary crossing for the whole batch. Returns a plain list so
279
+ callers can `np.asarray(...)` if they want a contiguous array.
280
+ """
281
+ if RUST_AVAILABLE and RUST_ENABLED:
282
+ import numpy as np
283
+ r = np.ascontiguousarray(rows, dtype=np.float32).reshape(-1)
284
+ q = np.ascontiguousarray(q, dtype=np.float32)
285
+ return list(_core.batch_dot(r, q, n_rows, dims))
286
+ import numpy as np
287
+ r = np.asarray(rows, dtype=np.float32).reshape(n_rows, dims)
288
+ q = _as_f32(q)
289
+ return (r @ q).tolist()
290
+
291
+
292
+ def batch_dot_i8(packed, q, n_rows: int, dims: int) -> list[float]:
293
+ """INT8-packed rows × f32 query. Raw int8·f32 dot products — callers
294
+ apply per-row scales (the codec's aux array) themselves."""
295
+ if RUST_AVAILABLE and RUST_ENABLED:
296
+ import numpy as np
297
+ p = np.ascontiguousarray(packed, dtype=np.int8).reshape(-1)
298
+ q = np.ascontiguousarray(q, dtype=np.float32)
299
+ return list(_core.batch_dot_i8(p, q, n_rows, dims))
300
+ import numpy as np
301
+ p = np.asarray(packed, dtype=np.int8).reshape(n_rows, dims)
302
+ q = _as_f32(q)
303
+ return (p.astype(np.float32) @ q).tolist()
304
+
305
+
306
+ def topk(scores, k: int) -> list[tuple[int, float]]:
307
+ """Top-`k` (idx, score) tuples in descending order. O(N) via
308
+ `select_nth_unstable`, then O(k log k) for the prefix sort."""
309
+ if RUST_AVAILABLE and RUST_ENABLED:
310
+ import numpy as np
311
+ s = np.ascontiguousarray(scores, dtype=np.float32)
312
+ return [(int(i), float(v)) for i, v in _core.topk(s, k)]
313
+ import numpy as np
314
+ s = np.asarray(scores, dtype=np.float32)
315
+ k = max(0, min(k, s.size))
316
+ if k == 0:
317
+ return []
318
+ part = np.argpartition(-s, k - 1)[:k]
319
+ part = part[np.argsort(-s[part])]
320
+ return [(int(i), float(s[i])) for i in part]
321
+
322
+
323
+ def argmax(scores) -> tuple[int, float]:
324
+ """Return (idx, value) of the max element. Ties → first occurrence."""
325
+ if RUST_AVAILABLE and RUST_ENABLED:
326
+ import numpy as np
327
+ s = np.ascontiguousarray(scores, dtype=np.float32)
328
+ i, v = _core.argmax(s)
329
+ return (int(i), float(v))
330
+ import numpy as np
331
+ s = np.asarray(scores, dtype=np.float32)
332
+ if s.size == 0:
333
+ return (0, 0.0)
334
+ i = int(np.argmax(s))
335
+ return (i, float(s[i]))
336
+
337
+
338
+ class SimdKernels:
339
+ """Container class that mirrors the Rust kernel surface so callers
340
+ can wire `accel.SimdKernels()` once and dispatch uniformly. The
341
+ NumPy fallbacks produce bit-identical results within 1e-5 (FP32
342
+ lane-reduction noise, asserted by parity tests).
343
+
344
+ Mirrors the existing `RustVSA` pattern: the constructor raises
345
+ `RuntimeError` if Rust is explicitly disabled (`CONTEXTM_RUST=0`)
346
+ — when Rust is *available* but the user opted out, callers should
347
+ use the free functions above instead.
348
+ """
349
+
350
+ def __init__(self) -> None:
351
+ if not (RUST_AVAILABLE and RUST_ENABLED):
352
+ raise RuntimeError(
353
+ "SimdKernels requires Rust acceleration enabled "
354
+ "(CONTEXTM_RUST=auto or =1, and the cortexm_core wheel built)")
355
+
356
+ def dot(self, a, b) -> float:
357
+ import numpy as np
358
+ return float(_core.dot(
359
+ np.ascontiguousarray(a, dtype=np.float32),
360
+ np.ascontiguousarray(b, dtype=np.float32)))
361
+
362
+ def dot_i8_f32(self, q8, q) -> float:
363
+ import numpy as np
364
+ return float(_core.dot_i8_f32(
365
+ np.ascontiguousarray(q8, dtype=np.int8),
366
+ np.ascontiguousarray(q, dtype=np.float32)))
367
+
368
+ def cosine(self, a, b) -> float:
369
+ import numpy as np
370
+ return float(_core.cosine(
371
+ np.ascontiguousarray(a, dtype=np.float32),
372
+ np.ascontiguousarray(b, dtype=np.float32)))
373
+
374
+ def l2_sq(self, a, b) -> float:
375
+ import numpy as np
376
+ return float(_core.l2_sq(
377
+ np.ascontiguousarray(a, dtype=np.float32),
378
+ np.ascontiguousarray(b, dtype=np.float32)))
379
+
380
+ def batch_dot(self, rows, q, n_rows: int, dims: int) -> list[float]:
381
+ import numpy as np
382
+ r = np.ascontiguousarray(rows, dtype=np.float32).reshape(-1)
383
+ q = np.ascontiguousarray(q, dtype=np.float32)
384
+ return list(_core.batch_dot(r, q, n_rows, dims))
385
+
386
+ def batch_dot_i8(self, packed, q, n_rows: int,
387
+ dims: int) -> list[float]:
388
+ import numpy as np
389
+ p = np.ascontiguousarray(packed, dtype=np.int8).reshape(-1)
390
+ q = np.ascontiguousarray(q, dtype=np.float32)
391
+ return list(_core.batch_dot_i8(p, q, n_rows, dims))
392
+
393
+ def topk(self, scores, k: int) -> list[tuple[int, float]]:
394
+ import numpy as np
395
+ s = np.ascontiguousarray(scores, dtype=np.float32)
396
+ return [(int(i), float(v)) for i, v in _core.topk(s, k)]
397
+
398
+ def argmax(self, scores) -> tuple[int, float]:
399
+ import numpy as np
400
+ s = np.ascontiguousarray(scores, dtype=np.float32)
401
+ i, v = _core.argmax(s)
402
+ return (int(i), float(v))
403
+
File without changes
cortexm/api/chaos.py ADDED
@@ -0,0 +1,118 @@
1
+ """Chaos mode — EAM-inspired zero-config auto-ingest.
2
+
3
+ arXiv insight (EAM / HeatherDB): the UX is "dump text in, intelligence
4
+ emerges." EAM conflates storage with inference — we reject that as
5
+ a black box. But the UX itself is the right default: most users
6
+ shouldn't have to tune patterns, configure idiolect dictionaries,
7
+ or wire bridges. They should just dump text in.
8
+
9
+ This module provides a one-call auto-ingest path:
10
+
11
+ chaos_ingest(mem, list_of_texts, user_id="default")
12
+
13
+ That runs the FULL pipeline with sensible defaults:
14
+ - per-user idiolect normalizer (built-in text-speak escape hatch)
15
+ - DisSim recursive simplification (splits compound sentences so
16
+ the pattern extractor sees one fact per clause)
17
+ - pattern extractor (μ=0, deterministic)
18
+ - lifecycle / contradiction / palace encoding / edges
19
+
20
+ The user can LATER opt into deterministic mode (call mem.add()
21
+ directly) when they want explicit control over the pipeline. This
22
+ matches EAM's "magic" UX without inheriting EAM's opacity — every
23
+ fact has full provenance, the Memory Git ancestry is preserved,
24
+ and the audit log records which sub-module extracted what.
25
+
26
+ Why the name: "chaos mode" because raw chaotic text → structured
27
+ facts, no user configuration required. Let EAM sell the magic;
28
+ Context-M keeps the receipts.
29
+ """
30
+ from __future__ import annotations
31
+
32
+ from typing import Iterable
33
+
34
+ from cortexm.config import Config
35
+ from cortexm.text.dissim import DisSimSplitter
36
+ from cortexm.text.embedder import HashingEmbedder
37
+ from cortexm.text.idiolect import PerUserIdiolectNormalizer
38
+
39
+
40
+ # Module-level singletons per Memory instance — idiolect + dissim are
41
+ # stateful (idiolect accumulates per-user slang observations; dissim
42
+ # is stateless but expensive to construct because it compiles regex).
43
+ # We cache one of each per Memory so the host doesn't pay construction
44
+ # cost on every chaos_ingest call.
45
+ _CACHE_ATTR = "_chaos_cache"
46
+
47
+
48
+ def _get_cache(mem):
49
+ """Lazy-init the chaos-mode cache on a Memory instance."""
50
+ if not hasattr(mem, _CACHE_ATTR):
51
+ embedder = HashingEmbedder(mem.palace.dims, mem.palace.cfg.seed)
52
+ setattr(mem, _CACHE_ATTR, {
53
+ "idiolect": PerUserIdiolectNormalizer(embedder),
54
+ "dissim": DisSimSplitter(max_depth=2),
55
+ "embedder": embedder,
56
+ })
57
+ return getattr(mem, _CACHE_ATTR)
58
+
59
+
60
+ def chaos_ingest(mem, texts, *, user_id: str = "default",
61
+ agent_id: str | None = None,
62
+ run_id: str | None = None) -> dict:
63
+ """Auto-ingest raw text via the full unmess + dissim + writer pipeline.
64
+
65
+ For each text:
66
+ 1. observe idiolect (per-user slang dictionary accumulates)
67
+ 2. normalize via idiolect (text-speak + kNN slang → canonical)
68
+ 3. split compound sentences via DisSim (recursive syntactic)
69
+ 4. for each clause: writer.add() — chunk insert + pattern
70
+ extract + quarantine + lifecycle + palace encoding + edges
71
+
72
+ Returns a stats dict matching Memory.add()'s shape (so callers
73
+ can drop chaos_ingest in place of mem.add() without changes).
74
+ """
75
+ cache = _get_cache(mem)
76
+ idiolect = cache["idiolect"]
77
+ dissim = cache["dissim"]
78
+
79
+ if isinstance(texts, str):
80
+ texts = [texts]
81
+
82
+ total_inserted = 0
83
+ all_results: list = []
84
+ total_tokens = 0
85
+ for text in texts:
86
+ if not text or not text.strip():
87
+ continue
88
+ # 1. observe idiolect
89
+ idiolect.observe(user_id, text)
90
+ # 2. normalize
91
+ norm = idiolect.normalize(user_id, text)
92
+ # 3. split into clauses
93
+ clauses = [c.text for c in (dissim.simplify_text(norm) or [norm])]
94
+ # 4. ingest each clause through the standard writer pipeline
95
+ for clause in clauses:
96
+ if not clause or not clause.strip():
97
+ continue
98
+ out = mem.add(
99
+ [{"role": "user", "content": clause}],
100
+ user_id=user_id, agent_id=agent_id, run_id=run_id)
101
+ total_inserted += out.get("stats", {}).get("facts_inserted", 0)
102
+ total_tokens += out.get("stats", {}).get("tokens", 0)
103
+ all_results.extend(out.get("results", []))
104
+
105
+ return {
106
+ "event": "CHAOS_INGEST",
107
+ "results": all_results,
108
+ "commit": None, # multiple commits per call
109
+ "stats": {
110
+ "messages": len(texts),
111
+ "tokens": total_tokens,
112
+ "facts_inserted": total_inserted,
113
+ "llm_calls": 0,
114
+ },
115
+ }
116
+
117
+
118
+ __all__ = ["chaos_ingest"]