engramma-memory 0.1.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.
@@ -0,0 +1,21 @@
1
+ """
2
+ Engramma Memory - A composable memory engine for AI systems.
3
+
4
+ Quick start:
5
+ >>> from engramma_memory import EngrammaMemory
6
+ >>> mem = EngrammaMemory(dim=256, backend="local")
7
+ >>> mem.store(key=embedding, value=data)
8
+ >>> result = mem.query(embedding, top_k=3)
9
+ >>> blend = mem.compose([key_a, key_b])
10
+
11
+ Async:
12
+ >>> from engramma_memory import EngrammaMemoryAsync
13
+ >>> mem = EngrammaMemoryAsync(dim=256, backend="cloud", api_key="nx_live_...")
14
+ >>> await mem.store(key=embedding, value=data)
15
+ """
16
+
17
+ from .async_core import EngrammaMemoryAsync
18
+ from .core import EngrammaMemory
19
+
20
+ __version__ = "0.1.0"
21
+ __all__ = ["EngrammaMemory", "EngrammaMemoryAsync"]
@@ -0,0 +1,418 @@
1
+ """
2
+ EngrammaMemory Async - Async-first interface for modern frameworks.
3
+
4
+ Full Phase 1-10 feature set with async/await.
5
+
6
+ Usage:
7
+ from engramma_memory import EngrammaMemoryAsync
8
+
9
+ async def main():
10
+ mem = EngrammaMemoryAsync(dim=256, backend="cloud", api_key="nx_live_...")
11
+ await mem.store(key=embedding, value=data)
12
+ results = await mem.query(embedding, top_k=5)
13
+
14
+ # Cloud-exclusive premium features
15
+ explanation = await mem.explain(query)
16
+ regime = await mem.get_current_regime()
17
+ await mem.consolidate()
18
+ """
19
+
20
+ from typing import Any, Dict, List, Optional, Union
21
+
22
+ import numpy as np
23
+ from numpy.typing import NDArray
24
+
25
+
26
+ class EngrammaMemoryAsync:
27
+ """
28
+ Async interface for Engramma Memory (cloud backend recommended).
29
+
30
+ Parameters
31
+ ----------
32
+ dim : int
33
+ Dimension of key/value vectors.
34
+ backend : str
35
+ "cloud" (recommended for async) or "local" (runs sync, no premium features).
36
+ api_key : str, optional
37
+ Required for cloud backend.
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ dim: int,
43
+ backend: str = "cloud",
44
+ api_key: Optional[str] = None,
45
+ max_patterns: int = 1000,
46
+ **kwargs,
47
+ ):
48
+ self.dim = dim
49
+ self.backend_name = backend
50
+ self._is_local = backend == "local"
51
+
52
+ if backend == "cloud":
53
+ if not api_key:
54
+ raise ValueError(
55
+ "Cloud backend requires an API key.\n"
56
+ "Get your free key: https://www.engramma-memory.com/signup"
57
+ )
58
+ from .backends.async_cloud import AsyncCloudBackend
59
+
60
+ self._backend = AsyncCloudBackend(dim=dim, api_key=api_key, **kwargs)
61
+ elif backend == "local":
62
+ from .backends.local import LocalBackend
63
+
64
+ self._backend = LocalBackend(dim=dim, max_patterns=max_patterns, **kwargs)
65
+ else:
66
+ raise ValueError(f"Unknown backend '{backend}'. Use 'local' or 'cloud'.")
67
+
68
+ def _require_cloud(self, method_name: str):
69
+ if self._is_local:
70
+ raise RuntimeError(
71
+ f"{method_name}() is a cloud-only feature.\n"
72
+ f"Switch to cloud: EngrammaMemoryAsync(backend='cloud', api_key='nx_...')"
73
+ )
74
+
75
+ # ═══════════════════════════════════════════════════════════════════
76
+ # CORE OPERATIONS
77
+ # ═══════════════════════════════════════════════════════════════════
78
+
79
+ async def store(
80
+ self,
81
+ key: Union[NDArray, List[float]],
82
+ value: Union[NDArray, List[float], Any],
83
+ metadata: Optional[Dict] = None,
84
+ ) -> None:
85
+ key = np.asarray(key, dtype=np.float32)
86
+ if self._is_local:
87
+ self._backend.store(key, value)
88
+ else:
89
+ await self._backend.store(key, value, metadata=metadata)
90
+
91
+ async def query(
92
+ self,
93
+ query: Union[NDArray, List[float]],
94
+ top_k: int = 1,
95
+ filters: Optional[Dict] = None,
96
+ use_phi_b: bool = False,
97
+ ) -> List[Dict[str, Any]]:
98
+ query = np.asarray(query, dtype=np.float32)
99
+ if self._is_local:
100
+ return self._backend.query(query, top_k=top_k)
101
+ return await self._backend.query(query, top_k=top_k, filters=filters, use_phi_b=use_phi_b)
102
+
103
+ async def retrieve(self, query: Union[NDArray, List[float]]) -> NDArray:
104
+ query = np.asarray(query, dtype=np.float32)
105
+ if self._is_local:
106
+ return self._backend.retrieve(query)
107
+ return await self._backend.retrieve(query)
108
+
109
+ async def compose(
110
+ self,
111
+ keys: List[Union[NDArray, List[float]]],
112
+ weights: Optional[List[float]] = None,
113
+ mode: str = "attention",
114
+ ) -> NDArray:
115
+ keys_arr = [np.asarray(k, dtype=np.float32) for k in keys]
116
+ if self._is_local:
117
+ return self._backend.compose(keys_arr, weights)
118
+ return await self._backend.compose(keys_arr, weights, mode=mode)
119
+
120
+ async def forget(self, key: Union[NDArray, List[float]], strategy: str = "decay") -> None:
121
+ key = np.asarray(key, dtype=np.float32)
122
+ if self._is_local:
123
+ self._backend.forget(key, strategy)
124
+ else:
125
+ await self._backend.forget(key, strategy)
126
+
127
+ # ═══════════════════════════════════════════════════════════════════
128
+ # PHASE 1 — NEUROMODULATION
129
+ # ═══════════════════════════════════════════════════════════════════
130
+
131
+ async def get_modulation_state(self) -> Dict[str, Any]:
132
+ """Get current neuromodulation state M(t). Cloud-only."""
133
+ self._require_cloud("get_modulation_state")
134
+ return await self._backend.get_modulation_state()
135
+
136
+ async def get_surprise_history(self, window: int = 100) -> Dict[str, Any]:
137
+ """Return surprise trajectory. Cloud-only."""
138
+ self._require_cloud("get_surprise_history")
139
+ return await self._backend.get_surprise_history(window)
140
+
141
+ async def configure_neuromodulation(
142
+ self, baseline: float = 0.5, sensitivity: float = 2.0, tau: float = 10.0
143
+ ) -> Dict[str, Any]:
144
+ """Fine-tune plasticity gate. Cloud-only."""
145
+ self._require_cloud("configure_neuromodulation")
146
+ return await self._backend.configure_neuromodulation(baseline, sensitivity, tau)
147
+
148
+ # ═══════════════════════════════════════════════════════════════════
149
+ # PHASE 2 — PHI_B ROUTING
150
+ # ═══════════════════════════════════════════════════════════════════
151
+
152
+ async def get_router_trace(self, query: Union[NDArray, List[float]]) -> Dict[str, Any]:
153
+ """Explain pathway selection for a query. Cloud-only."""
154
+ self._require_cloud("get_router_trace")
155
+ return await self._backend.get_router_trace(np.asarray(query, dtype=np.float32))
156
+
157
+ async def set_routing_geometry(self, geometry: str = "phi_b") -> Dict[str, Any]:
158
+ """Switch routing geometry. Cloud-only."""
159
+ self._require_cloud("set_routing_geometry")
160
+ return await self._backend.set_routing_geometry(geometry)
161
+
162
+ # ═══════════════════════════════════════════════════════════════════
163
+ # PHASE 3 — EFE STRATEGIC ROUTING
164
+ # ═══════════════════════════════════════════════════════════════════
165
+
166
+ async def query_with_epistemic_weight(
167
+ self,
168
+ query: Union[NDArray, List[float]],
169
+ epistemic_w: float = 1.0,
170
+ pragmatic_w: float = 0.5,
171
+ top_k: int = 1,
172
+ ) -> List[Dict[str, Any]]:
173
+ """Query with exploration/exploitation tradeoff. Cloud-only."""
174
+ self._require_cloud("query_with_epistemic_weight")
175
+ return await self._backend.query_with_epistemic_weight(
176
+ np.asarray(query, dtype=np.float32), epistemic_w, pragmatic_w, top_k
177
+ )
178
+
179
+ async def get_efe_scores(self, query: Union[NDArray, List[float]]) -> Dict[str, Any]:
180
+ """Get EFE scores per pathway. Cloud-only."""
181
+ self._require_cloud("get_efe_scores")
182
+ return await self._backend.get_efe_scores(np.asarray(query, dtype=np.float32))
183
+
184
+ async def set_pathway_strategy(self, strategy: str = "balanced") -> Dict[str, Any]:
185
+ """Set exploit/explore/balanced. Cloud-only."""
186
+ self._require_cloud("set_pathway_strategy")
187
+ return await self._backend.set_pathway_strategy(strategy)
188
+
189
+ # ═══════════════════════════════════════════════════════════════════
190
+ # PHASE 4 — STDP
191
+ # ═══════════════════════════════════════════════════════════════════
192
+
193
+ async def get_head_specialization(self) -> Dict[str, Any]:
194
+ """Which heads specialized for what. Cloud-only."""
195
+ self._require_cloud("get_head_specialization")
196
+ return await self._backend.get_head_specialization()
197
+
198
+ async def get_head_temperatures(self) -> Dict[str, Any]:
199
+ """Inspect head temperatures. Cloud-only."""
200
+ self._require_cloud("get_head_temperatures")
201
+ return await self._backend.get_head_temperatures()
202
+
203
+ async def enable_stdp_learning(
204
+ self, enabled: bool = True, eta: float = 0.01, tau: float = 5.0
205
+ ) -> Dict[str, Any]:
206
+ """Configure STDP plasticity. Cloud-only."""
207
+ self._require_cloud("enable_stdp_learning")
208
+ return await self._backend.enable_stdp_learning(enabled, eta, tau)
209
+
210
+ # ═══════════════════════════════════════════════════════════════════
211
+ # PHASE 6 — CAUSAL & ADVANCED COMPOSITION
212
+ # ═══════════════════════════════════════════════════════════════════
213
+
214
+ async def compose_fractional(
215
+ self,
216
+ key_a: Union[NDArray, List[float]],
217
+ key_b: Union[NDArray, List[float]],
218
+ alpha: float = 0.5,
219
+ ) -> NDArray:
220
+ """Continuous interpolation between patterns. Cloud-only."""
221
+ self._require_cloud("compose_fractional")
222
+ return await self._backend.compose_fractional(
223
+ np.asarray(key_a, dtype=np.float32), np.asarray(key_b, dtype=np.float32), alpha
224
+ )
225
+
226
+ async def get_causal_graph(self) -> Dict[str, Any]:
227
+ """Get discovered causal DAG. Cloud-only."""
228
+ self._require_cloud("get_causal_graph")
229
+ return await self._backend.get_causal_graph()
230
+
231
+ async def predict_causal_effect(
232
+ self, cause_key: Union[NDArray, List[float]], effect_key: Union[NDArray, List[float]]
233
+ ) -> Dict[str, Any]:
234
+ """Predict causal effect of perturbing a pattern. Cloud-only."""
235
+ self._require_cloud("predict_causal_effect")
236
+ return await self._backend.predict_causal_effect(
237
+ np.asarray(cause_key, dtype=np.float32),
238
+ np.asarray(effect_key, dtype=np.float32),
239
+ )
240
+
241
+ async def is_confounded(
242
+ self,
243
+ key_a: Union[NDArray, List[float]],
244
+ key_b: Union[NDArray, List[float]],
245
+ threshold: float = 0.5,
246
+ ) -> Dict[str, Any]:
247
+ """Detect hidden confounders. Cloud-only."""
248
+ self._require_cloud("is_confounded")
249
+ return await self._backend.is_confounded(
250
+ np.asarray(key_a, dtype=np.float32), np.asarray(key_b, dtype=np.float32), threshold
251
+ )
252
+
253
+ async def enable_active_exploration(self, enabled: bool = True) -> Dict[str, Any]:
254
+ """System autonomously probes uncertain relationships. Cloud-only."""
255
+ self._require_cloud("enable_active_exploration")
256
+ return await self._backend.enable_active_exploration(enabled)
257
+
258
+ # ═══════════════════════════════════════════════════════════════════
259
+ # PHASE 7 — STRUCTURE DISCOVERY
260
+ # ═══════════════════════════════════════════════════════════════════
261
+
262
+ async def get_skeleton_entropy(self) -> Dict[str, Any]:
263
+ """Quantify structural uncertainty. Cloud-only."""
264
+ self._require_cloud("get_skeleton_entropy")
265
+ return await self._backend.get_skeleton_entropy()
266
+
267
+ async def get_uncertain_pairs(self, n_top: int = 10) -> Dict[str, Any]:
268
+ """Which relationships need testing. Cloud-only."""
269
+ self._require_cloud("get_uncertain_pairs")
270
+ return await self._backend.get_uncertain_pairs(n_top)
271
+
272
+ async def auto_select_thresholds(self) -> Dict[str, Any]:
273
+ """Auto-calibrate thresholds via StARS. Cloud-only."""
274
+ self._require_cloud("auto_select_thresholds")
275
+ return await self._backend.auto_select_thresholds()
276
+
277
+ # ═══════════════════════════════════════════════════════════════════
278
+ # PHASE 8 — SAFETY & CONSOLIDATION
279
+ # ═══════════════════════════════════════════════════════════════════
280
+
281
+ async def get_current_regime(self) -> Dict[str, Any]:
282
+ """Get A/B/C regime status. Cloud-only."""
283
+ self._require_cloud("get_current_regime")
284
+ return await self._backend.get_current_regime()
285
+
286
+ async def enable_anomaly_protection(self, enabled: bool = True) -> Dict[str, Any]:
287
+ """Activate safety circuit. Cloud-only."""
288
+ self._require_cloud("enable_anomaly_protection")
289
+ return await self._backend.enable_anomaly_protection(enabled)
290
+
291
+ async def consolidate(self) -> Dict[str, Any]:
292
+ """Trigger Sleep/Wake consolidation. Cloud-only."""
293
+ self._require_cloud("consolidate")
294
+ return await self._backend.consolidate()
295
+
296
+ async def set_regime_thresholds(
297
+ self, theta_b: float = 1.0, theta_c: float = 2.0
298
+ ) -> Dict[str, Any]:
299
+ """Configure regime entry thresholds. Cloud-only."""
300
+ self._require_cloud("set_regime_thresholds")
301
+ return await self._backend.set_regime_thresholds(theta_b, theta_c)
302
+
303
+ # ═══════════════════════════════════════════════════════════════════
304
+ # PHASE 9 — TEMPORAL CAUSALITY
305
+ # ═══════════════════════════════════════════════════════════════════
306
+
307
+ async def test_granger_causality(
308
+ self,
309
+ key_a: Union[NDArray, List[float]],
310
+ key_b: Union[NDArray, List[float]],
311
+ max_lag: int = 10,
312
+ ) -> Dict[str, Any]:
313
+ """Test temporal Granger causality. Cloud-only."""
314
+ self._require_cloud("test_granger_causality")
315
+ return await self._backend.test_granger_causality(
316
+ np.asarray(key_a, dtype=np.float32), np.asarray(key_b, dtype=np.float32), max_lag
317
+ )
318
+
319
+ async def get_causal_predictions(
320
+ self, query: Union[NDArray, List[float]], n_predictions: int = 3
321
+ ) -> Dict[str, Any]:
322
+ """Predict next pattern accesses. Cloud-only."""
323
+ self._require_cloud("get_causal_predictions")
324
+ return await self._backend.get_causal_predictions(
325
+ np.asarray(query, dtype=np.float32), n_predictions
326
+ )
327
+
328
+ async def enable_prefetch(self, enabled: bool = True) -> Dict[str, Any]:
329
+ """Enable predictive prefetching. Cloud-only."""
330
+ self._require_cloud("enable_prefetch")
331
+ return await self._backend.enable_prefetch(enabled)
332
+
333
+ async def get_prefetch_hit_rate(self) -> Dict[str, Any]:
334
+ """Cache hit rate from predictions. Cloud-only."""
335
+ self._require_cloud("get_prefetch_hit_rate")
336
+ return await self._backend.get_prefetch_hit_rate()
337
+
338
+ # ═══════════════════════════════════════════════════════════════════
339
+ # PHASE 10 — TEXT, TIERS, XAI
340
+ # ═══════════════════════════════════════════════════════════════════
341
+
342
+ async def store_text(
343
+ self, text: str, value_embedding: Optional[NDArray] = None, metadata: Optional[Dict] = None
344
+ ) -> None:
345
+ """Store semantic text (HDC auto-encoding). Cloud-only."""
346
+ self._require_cloud("store_text")
347
+ await self._backend.store_text(text, value_embedding, metadata)
348
+
349
+ async def query_text(
350
+ self, query_text: str, top_k: int = 5, filters: Optional[Dict] = None
351
+ ) -> List[Dict[str, Any]]:
352
+ """Query by natural language. Cloud-only."""
353
+ self._require_cloud("query_text")
354
+ return await self._backend.query_text(query_text, top_k, filters)
355
+
356
+ async def compose_text(
357
+ self, texts: List[str], weights: Optional[List[float]] = None
358
+ ) -> Dict[str, Any]:
359
+ """Compose blends of text patterns. Cloud-only."""
360
+ self._require_cloud("compose_text")
361
+ return await self._backend.compose_text(texts, weights)
362
+
363
+ async def get_text_encoding(self, text: str) -> NDArray:
364
+ """Get HDC encoding of text. Cloud-only."""
365
+ self._require_cloud("get_text_encoding")
366
+ return await self._backend.get_text_encoding(text)
367
+
368
+ async def move_to_tier(
369
+ self, key: Union[NDArray, List[float]], tier: str = "warm"
370
+ ) -> Dict[str, Any]:
371
+ """Move pattern between hot/warm/cold tiers. Cloud-only."""
372
+ self._require_cloud("move_to_tier")
373
+ return await self._backend.move_to_tier(np.asarray(key, dtype=np.float32), tier)
374
+
375
+ async def get_tier_stats(self) -> Dict[str, Any]:
376
+ """Storage tier statistics. Cloud-only."""
377
+ self._require_cloud("get_tier_stats")
378
+ return await self._backend.get_tier_stats()
379
+
380
+ async def explain(self, query: Union[NDArray, List[float]]) -> Dict[str, Any]:
381
+ """Full XAI explanation of retrieval. Cloud-only."""
382
+ self._require_cloud("explain")
383
+ return await self._backend.explain(np.asarray(query, dtype=np.float32))
384
+
385
+ async def get_xai_dashboard(self) -> Dict[str, Any]:
386
+ """Full dashboard data for visualization. Cloud-only."""
387
+ self._require_cloud("get_xai_dashboard")
388
+ return await self._backend.get_xai_dashboard()
389
+
390
+ async def snapshot(self, name: Optional[str] = None) -> Dict[str, Any]:
391
+ """Create persistent memory snapshot. Cloud-only."""
392
+ self._require_cloud("snapshot")
393
+ return await self._backend.snapshot(name)
394
+
395
+ async def restore(self, snapshot_id: str) -> Dict[str, Any]:
396
+ """Restore from snapshot. Cloud-only."""
397
+ self._require_cloud("restore")
398
+ return await self._backend.restore(snapshot_id)
399
+
400
+ async def analytics(self, period: str = "24h") -> Dict[str, Any]:
401
+ """Usage analytics. Cloud-only."""
402
+ self._require_cloud("analytics")
403
+ return await self._backend.analytics(period)
404
+
405
+ # ═══════════════════════════════════════════════════════════════════
406
+ # LIFECYCLE
407
+ # ═══════════════════════════════════════════════════════════════════
408
+
409
+ async def close(self):
410
+ """Close the async HTTP client."""
411
+ if not self._is_local and hasattr(self._backend, "close"):
412
+ await self._backend.close()
413
+
414
+ async def __aenter__(self):
415
+ return self
416
+
417
+ async def __aexit__(self, *args):
418
+ await self.close()
@@ -0,0 +1,4 @@
1
+ from .cloud import CloudBackend
2
+ from .local import LocalBackend
3
+
4
+ __all__ = ["LocalBackend", "CloudBackend"]