neural-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.
- neural_memory/__init__.py +38 -0
- neural_memory/cli/__init__.py +15 -0
- neural_memory/cli/__main__.py +6 -0
- neural_memory/cli/config.py +176 -0
- neural_memory/cli/main.py +2702 -0
- neural_memory/cli/storage.py +169 -0
- neural_memory/cli/tui.py +471 -0
- neural_memory/core/__init__.py +52 -0
- neural_memory/core/brain.py +301 -0
- neural_memory/core/brain_mode.py +273 -0
- neural_memory/core/fiber.py +236 -0
- neural_memory/core/memory_types.py +331 -0
- neural_memory/core/neuron.py +168 -0
- neural_memory/core/project.py +257 -0
- neural_memory/core/synapse.py +215 -0
- neural_memory/engine/__init__.py +15 -0
- neural_memory/engine/activation.py +335 -0
- neural_memory/engine/encoder.py +391 -0
- neural_memory/engine/retrieval.py +440 -0
- neural_memory/extraction/__init__.py +42 -0
- neural_memory/extraction/entities.py +547 -0
- neural_memory/extraction/parser.py +337 -0
- neural_memory/extraction/router.py +396 -0
- neural_memory/extraction/temporal.py +428 -0
- neural_memory/mcp/__init__.py +9 -0
- neural_memory/mcp/__main__.py +6 -0
- neural_memory/mcp/server.py +621 -0
- neural_memory/py.typed +0 -0
- neural_memory/safety/__init__.py +31 -0
- neural_memory/safety/freshness.py +238 -0
- neural_memory/safety/sensitive.py +304 -0
- neural_memory/server/__init__.py +5 -0
- neural_memory/server/app.py +99 -0
- neural_memory/server/dependencies.py +33 -0
- neural_memory/server/models.py +138 -0
- neural_memory/server/routes/__init__.py +7 -0
- neural_memory/server/routes/brain.py +221 -0
- neural_memory/server/routes/memory.py +169 -0
- neural_memory/server/routes/sync.py +387 -0
- neural_memory/storage/__init__.py +17 -0
- neural_memory/storage/base.py +441 -0
- neural_memory/storage/factory.py +329 -0
- neural_memory/storage/memory_store.py +896 -0
- neural_memory/storage/shared_store.py +650 -0
- neural_memory/storage/sqlite_store.py +1613 -0
- neural_memory/sync/__init__.py +5 -0
- neural_memory/sync/client.py +435 -0
- neural_memory/unified_config.py +315 -0
- neural_memory/utils/__init__.py +5 -0
- neural_memory/utils/config.py +98 -0
- neural_memory-0.1.0.dist-info/METADATA +314 -0
- neural_memory-0.1.0.dist-info/RECORD +55 -0
- neural_memory-0.1.0.dist-info/WHEEL +4 -0
- neural_memory-0.1.0.dist-info/entry_points.txt +4 -0
- neural_memory-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""Storage factory for creating storage based on configuration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from neural_memory.core.brain_mode import BrainMode, BrainModeConfig
|
|
8
|
+
from neural_memory.storage.memory_store import InMemoryStorage
|
|
9
|
+
from neural_memory.storage.shared_store import SharedStorage
|
|
10
|
+
from neural_memory.storage.sqlite_store import SQLiteStorage
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from neural_memory.storage.base import NeuralStorage
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def create_storage(
|
|
17
|
+
config: BrainModeConfig,
|
|
18
|
+
brain_id: str,
|
|
19
|
+
*,
|
|
20
|
+
local_path: str | None = None,
|
|
21
|
+
) -> NeuralStorage:
|
|
22
|
+
"""
|
|
23
|
+
Create a storage instance based on configuration.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
config: Brain mode configuration
|
|
27
|
+
brain_id: ID of the brain to connect to
|
|
28
|
+
local_path: Path for local SQLite storage (used in LOCAL mode)
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Configured storage instance
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
# Local mode with SQLite
|
|
35
|
+
config = BrainModeConfig.local()
|
|
36
|
+
storage = await create_storage(config, "brain-1", local_path="./brain.db")
|
|
37
|
+
|
|
38
|
+
# Shared mode
|
|
39
|
+
config = BrainModeConfig.shared_mode("http://localhost:8000")
|
|
40
|
+
storage = await create_storage(config, "brain-1")
|
|
41
|
+
|
|
42
|
+
# Hybrid mode
|
|
43
|
+
config = BrainModeConfig.hybrid_mode("./local.db", "http://localhost:8000")
|
|
44
|
+
storage = await create_storage(config, "brain-1")
|
|
45
|
+
"""
|
|
46
|
+
if config.mode == BrainMode.LOCAL:
|
|
47
|
+
if local_path:
|
|
48
|
+
storage = SQLiteStorage(local_path)
|
|
49
|
+
await storage.initialize()
|
|
50
|
+
storage.set_brain(brain_id)
|
|
51
|
+
return storage
|
|
52
|
+
else:
|
|
53
|
+
storage = InMemoryStorage()
|
|
54
|
+
storage.set_brain(brain_id)
|
|
55
|
+
return storage
|
|
56
|
+
|
|
57
|
+
elif config.mode == BrainMode.SHARED:
|
|
58
|
+
if not config.shared:
|
|
59
|
+
raise ValueError("SharedConfig required for SHARED mode")
|
|
60
|
+
|
|
61
|
+
storage = SharedStorage(
|
|
62
|
+
server_url=config.shared.server_url,
|
|
63
|
+
brain_id=brain_id,
|
|
64
|
+
timeout=config.shared.timeout,
|
|
65
|
+
api_key=config.shared.api_key,
|
|
66
|
+
)
|
|
67
|
+
await storage.connect()
|
|
68
|
+
return storage
|
|
69
|
+
|
|
70
|
+
elif config.mode == BrainMode.HYBRID:
|
|
71
|
+
if not config.hybrid:
|
|
72
|
+
raise ValueError("HybridConfig required for HYBRID mode")
|
|
73
|
+
|
|
74
|
+
# For hybrid mode, return a HybridStorage that wraps both local and remote
|
|
75
|
+
storage = await HybridStorage.create(
|
|
76
|
+
local_path=config.hybrid.local_path,
|
|
77
|
+
server_url=config.hybrid.server_url,
|
|
78
|
+
brain_id=brain_id,
|
|
79
|
+
api_key=config.hybrid.api_key,
|
|
80
|
+
sync_strategy=config.hybrid.sync_strategy,
|
|
81
|
+
auto_sync_on_encode=config.hybrid.auto_sync_on_encode,
|
|
82
|
+
)
|
|
83
|
+
return storage
|
|
84
|
+
|
|
85
|
+
else:
|
|
86
|
+
raise ValueError(f"Unknown brain mode: {config.mode}")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class HybridStorage:
|
|
90
|
+
"""
|
|
91
|
+
Hybrid storage that combines local SQLite with remote sync.
|
|
92
|
+
|
|
93
|
+
Provides offline-first capability with optional sync to server.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
local: SQLiteStorage,
|
|
99
|
+
remote: SharedStorage,
|
|
100
|
+
*,
|
|
101
|
+
auto_sync_on_encode: bool = True,
|
|
102
|
+
) -> None:
|
|
103
|
+
self._local = local
|
|
104
|
+
self._remote = remote
|
|
105
|
+
self._auto_sync = auto_sync_on_encode
|
|
106
|
+
self._brain_id: str | None = None
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
async def create(
|
|
110
|
+
cls,
|
|
111
|
+
local_path: str,
|
|
112
|
+
server_url: str,
|
|
113
|
+
brain_id: str,
|
|
114
|
+
*,
|
|
115
|
+
api_key: str | None = None,
|
|
116
|
+
sync_strategy: str = "bidirectional",
|
|
117
|
+
auto_sync_on_encode: bool = True,
|
|
118
|
+
) -> HybridStorage:
|
|
119
|
+
"""Create and initialize hybrid storage."""
|
|
120
|
+
local = SQLiteStorage(local_path)
|
|
121
|
+
await local.initialize()
|
|
122
|
+
local.set_brain(brain_id)
|
|
123
|
+
|
|
124
|
+
remote = SharedStorage(
|
|
125
|
+
server_url=server_url,
|
|
126
|
+
brain_id=brain_id,
|
|
127
|
+
api_key=api_key,
|
|
128
|
+
)
|
|
129
|
+
# Don't connect remote immediately - connect on demand
|
|
130
|
+
|
|
131
|
+
storage = cls(
|
|
132
|
+
local=local,
|
|
133
|
+
remote=remote,
|
|
134
|
+
auto_sync_on_encode=auto_sync_on_encode,
|
|
135
|
+
)
|
|
136
|
+
storage._brain_id = brain_id
|
|
137
|
+
return storage
|
|
138
|
+
|
|
139
|
+
def set_brain(self, brain_id: str) -> None:
|
|
140
|
+
"""Set the current brain context."""
|
|
141
|
+
self._brain_id = brain_id
|
|
142
|
+
self._local.set_brain(brain_id)
|
|
143
|
+
self._remote.set_brain(brain_id)
|
|
144
|
+
|
|
145
|
+
# Delegate all NeuralStorage methods to local storage
|
|
146
|
+
# Sync to remote when appropriate
|
|
147
|
+
|
|
148
|
+
async def add_neuron(self, neuron):
|
|
149
|
+
"""Add neuron locally, optionally sync."""
|
|
150
|
+
result = await self._local.add_neuron(neuron)
|
|
151
|
+
if self._auto_sync:
|
|
152
|
+
try:
|
|
153
|
+
await self._ensure_connected()
|
|
154
|
+
await self._remote.add_neuron(neuron)
|
|
155
|
+
except Exception:
|
|
156
|
+
pass # Offline, will sync later
|
|
157
|
+
return result
|
|
158
|
+
|
|
159
|
+
async def get_neuron(self, neuron_id):
|
|
160
|
+
"""Get neuron from local storage."""
|
|
161
|
+
return await self._local.get_neuron(neuron_id)
|
|
162
|
+
|
|
163
|
+
async def find_neurons(self, **kwargs):
|
|
164
|
+
"""Find neurons in local storage."""
|
|
165
|
+
return await self._local.find_neurons(**kwargs)
|
|
166
|
+
|
|
167
|
+
async def update_neuron(self, neuron):
|
|
168
|
+
"""Update neuron locally, optionally sync."""
|
|
169
|
+
await self._local.update_neuron(neuron)
|
|
170
|
+
if self._auto_sync:
|
|
171
|
+
try:
|
|
172
|
+
await self._ensure_connected()
|
|
173
|
+
await self._remote.update_neuron(neuron)
|
|
174
|
+
except Exception:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
async def delete_neuron(self, neuron_id):
|
|
178
|
+
"""Delete neuron locally, optionally sync."""
|
|
179
|
+
result = await self._local.delete_neuron(neuron_id)
|
|
180
|
+
if self._auto_sync:
|
|
181
|
+
try:
|
|
182
|
+
await self._ensure_connected()
|
|
183
|
+
await self._remote.delete_neuron(neuron_id)
|
|
184
|
+
except Exception:
|
|
185
|
+
pass
|
|
186
|
+
return result
|
|
187
|
+
|
|
188
|
+
async def get_neuron_state(self, neuron_id):
|
|
189
|
+
return await self._local.get_neuron_state(neuron_id)
|
|
190
|
+
|
|
191
|
+
async def update_neuron_state(self, state):
|
|
192
|
+
await self._local.update_neuron_state(state)
|
|
193
|
+
|
|
194
|
+
async def add_synapse(self, synapse):
|
|
195
|
+
result = await self._local.add_synapse(synapse)
|
|
196
|
+
if self._auto_sync:
|
|
197
|
+
try:
|
|
198
|
+
await self._ensure_connected()
|
|
199
|
+
await self._remote.add_synapse(synapse)
|
|
200
|
+
except Exception:
|
|
201
|
+
pass
|
|
202
|
+
return result
|
|
203
|
+
|
|
204
|
+
async def get_synapse(self, synapse_id):
|
|
205
|
+
return await self._local.get_synapse(synapse_id)
|
|
206
|
+
|
|
207
|
+
async def get_synapses(self, **kwargs):
|
|
208
|
+
return await self._local.get_synapses(**kwargs)
|
|
209
|
+
|
|
210
|
+
async def update_synapse(self, synapse):
|
|
211
|
+
await self._local.update_synapse(synapse)
|
|
212
|
+
if self._auto_sync:
|
|
213
|
+
try:
|
|
214
|
+
await self._ensure_connected()
|
|
215
|
+
await self._remote.update_synapse(synapse)
|
|
216
|
+
except Exception:
|
|
217
|
+
pass
|
|
218
|
+
|
|
219
|
+
async def delete_synapse(self, synapse_id):
|
|
220
|
+
result = await self._local.delete_synapse(synapse_id)
|
|
221
|
+
if self._auto_sync:
|
|
222
|
+
try:
|
|
223
|
+
await self._ensure_connected()
|
|
224
|
+
await self._remote.delete_synapse(synapse_id)
|
|
225
|
+
except Exception:
|
|
226
|
+
pass
|
|
227
|
+
return result
|
|
228
|
+
|
|
229
|
+
async def get_neighbors(self, neuron_id, **kwargs):
|
|
230
|
+
return await self._local.get_neighbors(neuron_id, **kwargs)
|
|
231
|
+
|
|
232
|
+
async def get_path(self, source_id, target_id, max_hops=4):
|
|
233
|
+
return await self._local.get_path(source_id, target_id, max_hops)
|
|
234
|
+
|
|
235
|
+
async def add_fiber(self, fiber):
|
|
236
|
+
result = await self._local.add_fiber(fiber)
|
|
237
|
+
if self._auto_sync:
|
|
238
|
+
try:
|
|
239
|
+
await self._ensure_connected()
|
|
240
|
+
await self._remote.add_fiber(fiber)
|
|
241
|
+
except Exception:
|
|
242
|
+
pass
|
|
243
|
+
return result
|
|
244
|
+
|
|
245
|
+
async def get_fiber(self, fiber_id):
|
|
246
|
+
return await self._local.get_fiber(fiber_id)
|
|
247
|
+
|
|
248
|
+
async def find_fibers(self, **kwargs):
|
|
249
|
+
return await self._local.find_fibers(**kwargs)
|
|
250
|
+
|
|
251
|
+
async def update_fiber(self, fiber):
|
|
252
|
+
await self._local.update_fiber(fiber)
|
|
253
|
+
if self._auto_sync:
|
|
254
|
+
try:
|
|
255
|
+
await self._ensure_connected()
|
|
256
|
+
await self._remote.update_fiber(fiber)
|
|
257
|
+
except Exception:
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
async def delete_fiber(self, fiber_id):
|
|
261
|
+
result = await self._local.delete_fiber(fiber_id)
|
|
262
|
+
if self._auto_sync:
|
|
263
|
+
try:
|
|
264
|
+
await self._ensure_connected()
|
|
265
|
+
await self._remote.delete_fiber(fiber_id)
|
|
266
|
+
except Exception:
|
|
267
|
+
pass
|
|
268
|
+
return result
|
|
269
|
+
|
|
270
|
+
async def get_fibers(self, **kwargs):
|
|
271
|
+
return await self._local.get_fibers(**kwargs)
|
|
272
|
+
|
|
273
|
+
async def save_brain(self, brain):
|
|
274
|
+
await self._local.save_brain(brain)
|
|
275
|
+
|
|
276
|
+
async def get_brain(self, brain_id):
|
|
277
|
+
return await self._local.get_brain(brain_id)
|
|
278
|
+
|
|
279
|
+
async def export_brain(self, brain_id):
|
|
280
|
+
return await self._local.export_brain(brain_id)
|
|
281
|
+
|
|
282
|
+
async def import_brain(self, snapshot, target_brain_id=None):
|
|
283
|
+
return await self._local.import_brain(snapshot, target_brain_id)
|
|
284
|
+
|
|
285
|
+
async def get_stats(self, brain_id):
|
|
286
|
+
return await self._local.get_stats(brain_id)
|
|
287
|
+
|
|
288
|
+
async def clear(self, brain_id):
|
|
289
|
+
await self._local.clear(brain_id)
|
|
290
|
+
|
|
291
|
+
# Sync operations
|
|
292
|
+
|
|
293
|
+
async def sync(self) -> dict:
|
|
294
|
+
"""
|
|
295
|
+
Manually trigger a full sync with remote server.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
Sync statistics
|
|
299
|
+
"""
|
|
300
|
+
await self._ensure_connected()
|
|
301
|
+
|
|
302
|
+
# Export local brain
|
|
303
|
+
if not self._brain_id:
|
|
304
|
+
raise ValueError("No brain set")
|
|
305
|
+
|
|
306
|
+
local_snapshot = await self._local.export_brain(self._brain_id)
|
|
307
|
+
|
|
308
|
+
# Get remote snapshot
|
|
309
|
+
try:
|
|
310
|
+
_remote_snapshot = await self._remote.export_brain(self._brain_id)
|
|
311
|
+
except Exception:
|
|
312
|
+
# Brain doesn't exist on remote, push our version
|
|
313
|
+
await self._remote.import_brain(local_snapshot, self._brain_id)
|
|
314
|
+
return {"pushed": True, "pulled": False}
|
|
315
|
+
|
|
316
|
+
# TODO: Implement proper merge logic using _remote_snapshot
|
|
317
|
+
# For now, just push local to remote
|
|
318
|
+
await self._remote.import_brain(local_snapshot, self._brain_id)
|
|
319
|
+
|
|
320
|
+
return {"pushed": True, "pulled": False}
|
|
321
|
+
|
|
322
|
+
async def _ensure_connected(self) -> None:
|
|
323
|
+
"""Ensure remote storage is connected."""
|
|
324
|
+
if not self._remote.is_connected:
|
|
325
|
+
await self._remote.connect()
|
|
326
|
+
|
|
327
|
+
async def close(self) -> None:
|
|
328
|
+
"""Close all connections."""
|
|
329
|
+
await self._remote.disconnect()
|