prismcortex 0.2.1__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.
@@ -0,0 +1,40 @@
1
+ """PrismCortex — deterministic, auditable, self-consolidating memory for AI agents.
2
+
3
+ from prismcortex import reference_memory
4
+ mem = reference_memory() # reference adapters + real Gemini
5
+ mem.digest("My deploy budget is $40k.")
6
+ mem.recall("What's my deploy budget?").answer
7
+
8
+ Swap the reference adapters for the real Insight ITS packages (prismlang, prismrag,
9
+ prismresonance, prismlib, Chorus) one port at a time — the engine never changes.
10
+ """
11
+ from .engine import Memory
12
+ from .factory import reference_memory
13
+ from .models import (
14
+ Band,
15
+ DigestOutcome,
16
+ DigestResult,
17
+ Edge,
18
+ GraphVersion,
19
+ Node,
20
+ RecallResult,
21
+ StateDelta,
22
+ Subgraph,
23
+ )
24
+
25
+ __version__ = "0.2.1"
26
+
27
+ __all__ = [
28
+ "Memory",
29
+ "reference_memory",
30
+ "Band",
31
+ "DigestOutcome",
32
+ "DigestResult",
33
+ "RecallResult",
34
+ "Node",
35
+ "Edge",
36
+ "Subgraph",
37
+ "StateDelta",
38
+ "GraphVersion",
39
+ "__version__",
40
+ ]
@@ -0,0 +1,20 @@
1
+ """Adapters that satisfy the PrismCortex ports."""
2
+ from .reference import (
3
+ DurableCache,
4
+ HashingProjector,
5
+ InMemoryGraphStore,
6
+ InProcessMesh,
7
+ InProcessResonance,
8
+ ListStaging,
9
+ LocalBlobStore,
10
+ )
11
+
12
+ __all__ = [
13
+ "DurableCache",
14
+ "HashingProjector",
15
+ "InMemoryGraphStore",
16
+ "InProcessMesh",
17
+ "InProcessResonance",
18
+ "ListStaging",
19
+ "LocalBlobStore",
20
+ ]
@@ -0,0 +1,104 @@
1
+ """IVF-style ANN retrieval for graphs beyond ~10k nodes (numpy-only, no extra deps)."""
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ from typing import Optional
6
+
7
+ import numpy as np
8
+
9
+ from .reference import InMemoryGraphStore
10
+
11
+
12
+ class AnnGraphStore(InMemoryGraphStore):
13
+ """In-memory bitemporal store with inverted-file ANN when node count exceeds threshold."""
14
+
15
+ def __init__(self, *, tenant_id: str = "default", ivf_threshold: Optional[int] = None, nlist: int = 256, nprobe: int = 16) -> None:
16
+ super().__init__()
17
+ self.tenant_id = tenant_id
18
+ self._ivf_threshold = ivf_threshold or int(os.environ.get("PRISMCORTEX_ANN_THRESHOLD", "5000"))
19
+ self._nlist = nlist
20
+ self._nprobe = nprobe
21
+ self._centroids: Optional[np.ndarray] = None
22
+ self._inverted: list[list[str]] = []
23
+ self._ivf_dirty = True
24
+
25
+ def _rebuild_ivf(self) -> None:
26
+ self._ensure_matrix()
27
+ if self._emb_unit is None or len(self._emb_ids) < self._ivf_threshold:
28
+ self._centroids = None
29
+ self._inverted = []
30
+ self._ivf_dirty = False
31
+ return
32
+ n, d = self._emb_unit.shape
33
+ k = min(self._nlist, max(8, n // 40))
34
+ rng = np.random.default_rng(42)
35
+ idx = rng.choice(n, size=k, replace=False)
36
+ self._centroids = self._emb_unit[idx].copy()
37
+ # Lloyd-lite: 3 iterations
38
+ for _ in range(3):
39
+ sims = self._emb_unit @ self._centroids.T
40
+ assign = np.argmax(sims, axis=1)
41
+ for c in range(k):
42
+ mask = assign == c
43
+ if mask.any():
44
+ self._centroids[c] = self._emb_unit[mask].mean(axis=0)
45
+ cn = np.linalg.norm(self._centroids[c]) or 1.0
46
+ self._centroids[c] /= cn
47
+ self._inverted = [[] for _ in range(k)]
48
+ sims = self._emb_unit @ self._centroids.T
49
+ assign = np.argmax(sims, axis=1)
50
+ for i, c in enumerate(assign):
51
+ self._inverted[int(c)].append(self._emb_ids[i])
52
+ self._ivf_dirty = False
53
+
54
+ def _ensure_matrix(self) -> None:
55
+ super()._ensure_matrix()
56
+ if self._matrix_dirty:
57
+ self._ivf_dirty = True
58
+
59
+ def apply(self, delta):
60
+ v = super().apply(delta)
61
+ self._ivf_dirty = True
62
+ return v
63
+
64
+ def retrieve(self, embedding: list[float], k: int = 8):
65
+ if not self._nodes:
66
+ return super().retrieve(embedding, k)
67
+ self._ensure_matrix()
68
+ if self._emb_unit is None:
69
+ return super().retrieve(embedding, k)
70
+ if len(self._emb_ids) < self._ivf_threshold:
71
+ return super().retrieve(embedding, k)
72
+ if self._ivf_dirty:
73
+ self._rebuild_ivf()
74
+ if self._centroids is None:
75
+ return super().retrieve(embedding, k)
76
+
77
+ q = np.asarray(embedding, dtype=np.float32)
78
+ qn = float(np.linalg.norm(q)) or 1.0
79
+ q = q / qn
80
+ csim = self._centroids @ q
81
+ n = len(self._emb_ids)
82
+ # Scale probe depth with graph size (more clusters searched at 50k+ nodes).
83
+ nprobe = min(len(csim), max(self._nprobe, n // 2000))
84
+ probe = np.argsort(-csim, kind="stable")[:nprobe]
85
+ candidates: set[str] = set()
86
+ id_to_row = {nid: i for i, nid in enumerate(self._emb_ids)}
87
+ for c in probe:
88
+ for nid in self._inverted[int(c)]:
89
+ candidates.add(nid)
90
+ if not candidates:
91
+ return super().retrieve(embedding, k)
92
+
93
+ rows = [id_to_row[nid] for nid in candidates if nid in id_to_row]
94
+ sims = self._emb_unit[rows] @ q
95
+ order = np.argsort(-sims, kind="stable")[: min(k, len(rows))]
96
+ chosen = {self._emb_ids[rows[int(i)]] for i in order}
97
+
98
+ edges = [e for e in self._edges.values() if e.is_current and (e.src in chosen or e.dst in chosen)]
99
+ for e in edges:
100
+ chosen.add(e.src)
101
+ chosen.add(e.dst)
102
+ nodes = [self._nodes[n] for n in chosen if n in self._nodes]
103
+ from ..models import Subgraph
104
+ return Subgraph(nodes=nodes, edges=edges)
@@ -0,0 +1,174 @@
1
+ """Production adapters — the real Insight ITS packages behind the ports.
2
+
3
+ Import-guarded so the OSS core stays installable without them (`pip install
4
+ prismcortex[prism]`). Each class wraps exactly one package:
5
+
6
+ - PrismLangProjector → prismlang.PrismProjector (deterministic, CPU-stable vectors)
7
+ - PrismResonanceAdapter → prismresonance.PrismResonance (wavepacket weight + sleep)
8
+ - PrismLibCache → prismlib SQLiteStore (durable cache-as-failover)
9
+
10
+ The bitemporal GraphStore stays PrismCortex-owned (the reference store *is* the design
11
+ — prismrag-patch is a retrieval governor, not a bitemporal database, so it slots in as
12
+ a recall-time filter rather than the store itself). The Chorus/prismlib cluster mesh is
13
+ the remaining seam; InProcessMesh stands in until it's wired.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import os
18
+ import time
19
+ from typing import Optional
20
+
21
+ import numpy as np
22
+
23
+
24
+ class PrismLangProjector:
25
+ """GistProjector via prismlang.PrismProjector.
26
+
27
+ project() returns (category_slug, vector, trace). The vector is all-MiniLM-L6-v2
28
+ JL-reduced with a seed derived from tenant_id — deterministic given a fixed tenant,
29
+ which is exactly the CPU-stable projection the read-path determinism contract needs.
30
+ """
31
+
32
+ def __init__(self, taxonomy=None, tenant_id: str = "prismcortex", k: int = 128) -> None:
33
+ # k = projection dimension. PrismLang defaults to 64, which crowds at scale
34
+ # (retrieval recall drops to ~0.86 at 3k facts); 128 restores it to ~0.97 with no
35
+ # latency cost. See benchmarks/scale_bench.py.
36
+ from prismlang import Category, PrismProjector, TaxonomyConfig
37
+
38
+ if taxonomy is None:
39
+ taxonomy = TaxonomyConfig(categories=[
40
+ Category("general", "General", ["the", "a", "is", "of", "to"]),
41
+ Category("preference", "Preference", ["like", "prefer", "favorite", "want", "hate"]),
42
+ Category("fact", "Fact", ["is", "are", "has", "budget", "name", "id", "number"]),
43
+ Category("tech", "Tech", ["deploy", "database", "server", "region", "api", "model"]),
44
+ ])
45
+ self._p = PrismProjector(taxonomy, tenant_id=tenant_id, k=k)
46
+ _, probe, _ = self._p.project("dimension probe")
47
+ self.dim = len(probe)
48
+
49
+ def embed(self, text: str) -> list[float]:
50
+ _, vec, _ = self._p.project(text)
51
+ return [float(x) for x in vec]
52
+
53
+ def classify(self, text: str) -> str:
54
+ cat, _, _ = self._p.project(text)
55
+ return cat
56
+
57
+
58
+ class PrismResonanceAdapter:
59
+ """ResonanceEngine via prismresonance.PrismResonance (compiles an ONNX graph on
60
+ first construction; writes a small state db)."""
61
+
62
+ def __init__(self, embedding_dim: int, state_path: str = "resonance_state.db", onnx_path: str = "resonance_engine.onnx") -> None:
63
+ from prismresonance import PrismResonance
64
+ from prismresonance.frequency import FrequencyFamily
65
+
66
+ self._FF = FrequencyFamily
67
+ self._r = PrismResonance.create(embedding_dim=embedding_dim, state_path=state_path, onnx_path=onnx_path)
68
+ self._amps: dict[str, np.ndarray] = {} # kept so reinforce can re-ingest
69
+
70
+ def _freq(self, band: str):
71
+ return getattr(self._FF, band, self._FF.NEUTRAL)
72
+
73
+ def ingest(self, chunk_id: str, amplitude: list[float], band: str) -> None:
74
+ amp = np.asarray(amplitude, dtype=np.float32)
75
+ if amp.size == 0:
76
+ return
77
+ self._amps[chunk_id] = amp
78
+ self._r.ingest(chunk_id, amp, self._freq(band))
79
+
80
+ def reinforce(self, chunk_id: str) -> None:
81
+ amp = self._amps.get(chunk_id)
82
+ if amp is not None: # re-ingest at higher salience ≈ LTP
83
+ self._r.ingest(chunk_id, amp, self._FF.ALERT)
84
+
85
+ def rank(self, candidate_ids: list[str]) -> list[str]:
86
+ return list(candidate_ids) # resonance ordering is not on the determinism path
87
+
88
+ def consolidate(self) -> None:
89
+ self._r.sleep()
90
+
91
+ def shutdown(self) -> None:
92
+ try:
93
+ self._r.shutdown()
94
+ except Exception:
95
+ pass
96
+
97
+
98
+ class PrismLibCache:
99
+ """ResponseCache via prismlib SQLiteStore — exact-key, durable (cache-as-failover).
100
+
101
+ packet_id carries our content-address; response carries the frozen answer. Durable
102
+ by file path so a frozen answer survives restart and cache eviction.
103
+ """
104
+
105
+ _TEN_YEARS = 10 * 365 * 24 * 3600
106
+
107
+ def __init__(self, db_path: str = ".prismcortex_cache/prismlib.db") -> None:
108
+ from prism.cache import CacheEntry, SQLiteStore
109
+
110
+ os.makedirs(os.path.dirname(db_path) or ".", exist_ok=True)
111
+ self._CacheEntry = CacheEntry
112
+ self._db_path = db_path
113
+ self._store = SQLiteStore(db_path=db_path)
114
+
115
+ def get(self, key: str) -> Optional[str]:
116
+ entry = self._store.load(key)
117
+ return entry.response if entry else None
118
+
119
+ def has(self, key: str) -> bool:
120
+ return self._store.load(key) is not None
121
+
122
+ def put(self, key: str, value: str) -> None:
123
+ now = time.time()
124
+ self._store.save(self._CacheEntry(
125
+ packet_id=key, query_text="", response=value,
126
+ created_at=now, expires_at=now + self._TEN_YEARS,
127
+ ))
128
+
129
+ def clear(self) -> None:
130
+ """Drop all cached answers (recreate the store) — used on erasure."""
131
+ from prism.cache import SQLiteStore
132
+
133
+ try:
134
+ self._store.close()
135
+ except Exception: # noqa: BLE001
136
+ pass
137
+ if self._db_path != ":memory:" and os.path.exists(self._db_path):
138
+ os.remove(self._db_path)
139
+ self._store = SQLiteStore(db_path=self._db_path)
140
+
141
+
142
+ def prism_memory(
143
+ *,
144
+ model: Optional[str] = None,
145
+ cache_db: str = ".prismcortex_cache/prismlib.db",
146
+ tenant_id: str = "prismcortex",
147
+ resonance_state: str = ".prismcortex_cache/resonance_state.db",
148
+ resonance_onnx: str = ".prismcortex_cache/resonance_engine.onnx",
149
+ k: int = 8,
150
+ ):
151
+ """Production-wired Memory: real PrismLang + PrismResonance + PrismLib + Gemini.
152
+
153
+ GraphStore + Mesh remain the reference (PrismCortex-owned bitemporal store; mesh is
154
+ the open Chorus seam). Needs prismcortex[prism], prismcortex[gemini], and a key.
155
+ """
156
+ from ..engine import Memory
157
+ from ..llm.gemini import GeminiClient
158
+ from .reference import InMemoryGraphStore, InProcessMesh, ListStaging
159
+
160
+ projector = PrismLangProjector(tenant_id=tenant_id)
161
+ resonance = PrismResonanceAdapter(embedding_dim=projector.dim, state_path=resonance_state, onnx_path=resonance_onnx)
162
+ cache = PrismLibCache(db_path=cache_db)
163
+ gemini = GeminiClient(model=model)
164
+ return Memory(
165
+ projector=projector,
166
+ extractor=gemini,
167
+ renderer=gemini,
168
+ store=InMemoryGraphStore(),
169
+ resonance=resonance,
170
+ cache=cache,
171
+ mesh=InProcessMesh(),
172
+ staging=ListStaging(),
173
+ k=k,
174
+ )