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.
Files changed (55) hide show
  1. neural_memory/__init__.py +38 -0
  2. neural_memory/cli/__init__.py +15 -0
  3. neural_memory/cli/__main__.py +6 -0
  4. neural_memory/cli/config.py +176 -0
  5. neural_memory/cli/main.py +2702 -0
  6. neural_memory/cli/storage.py +169 -0
  7. neural_memory/cli/tui.py +471 -0
  8. neural_memory/core/__init__.py +52 -0
  9. neural_memory/core/brain.py +301 -0
  10. neural_memory/core/brain_mode.py +273 -0
  11. neural_memory/core/fiber.py +236 -0
  12. neural_memory/core/memory_types.py +331 -0
  13. neural_memory/core/neuron.py +168 -0
  14. neural_memory/core/project.py +257 -0
  15. neural_memory/core/synapse.py +215 -0
  16. neural_memory/engine/__init__.py +15 -0
  17. neural_memory/engine/activation.py +335 -0
  18. neural_memory/engine/encoder.py +391 -0
  19. neural_memory/engine/retrieval.py +440 -0
  20. neural_memory/extraction/__init__.py +42 -0
  21. neural_memory/extraction/entities.py +547 -0
  22. neural_memory/extraction/parser.py +337 -0
  23. neural_memory/extraction/router.py +396 -0
  24. neural_memory/extraction/temporal.py +428 -0
  25. neural_memory/mcp/__init__.py +9 -0
  26. neural_memory/mcp/__main__.py +6 -0
  27. neural_memory/mcp/server.py +621 -0
  28. neural_memory/py.typed +0 -0
  29. neural_memory/safety/__init__.py +31 -0
  30. neural_memory/safety/freshness.py +238 -0
  31. neural_memory/safety/sensitive.py +304 -0
  32. neural_memory/server/__init__.py +5 -0
  33. neural_memory/server/app.py +99 -0
  34. neural_memory/server/dependencies.py +33 -0
  35. neural_memory/server/models.py +138 -0
  36. neural_memory/server/routes/__init__.py +7 -0
  37. neural_memory/server/routes/brain.py +221 -0
  38. neural_memory/server/routes/memory.py +169 -0
  39. neural_memory/server/routes/sync.py +387 -0
  40. neural_memory/storage/__init__.py +17 -0
  41. neural_memory/storage/base.py +441 -0
  42. neural_memory/storage/factory.py +329 -0
  43. neural_memory/storage/memory_store.py +896 -0
  44. neural_memory/storage/shared_store.py +650 -0
  45. neural_memory/storage/sqlite_store.py +1613 -0
  46. neural_memory/sync/__init__.py +5 -0
  47. neural_memory/sync/client.py +435 -0
  48. neural_memory/unified_config.py +315 -0
  49. neural_memory/utils/__init__.py +5 -0
  50. neural_memory/utils/config.py +98 -0
  51. neural_memory-0.1.0.dist-info/METADATA +314 -0
  52. neural_memory-0.1.0.dist-info/RECORD +55 -0
  53. neural_memory-0.1.0.dist-info/WHEEL +4 -0
  54. neural_memory-0.1.0.dist-info/entry_points.txt +4 -0
  55. neural_memory-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,391 @@
1
+ """Memory encoder for converting experiences into neural structures."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from datetime import datetime
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from neural_memory.core.fiber import Fiber
10
+ from neural_memory.core.neuron import Neuron, NeuronType
11
+ from neural_memory.core.synapse import Synapse, SynapseType
12
+ from neural_memory.extraction.entities import EntityExtractor, EntityType, extract_keywords
13
+ from neural_memory.extraction.temporal import TemporalExtractor
14
+
15
+ if TYPE_CHECKING:
16
+ from neural_memory.core.brain import BrainConfig
17
+ from neural_memory.storage.base import NeuralStorage
18
+
19
+
20
+ @dataclass
21
+ class EncodingResult:
22
+ """
23
+ Result of encoding a memory.
24
+
25
+ Attributes:
26
+ fiber: The created memory fiber
27
+ neurons_created: List of newly created neurons
28
+ neurons_linked: List of existing neuron IDs that were linked
29
+ synapses_created: List of newly created synapses
30
+ """
31
+
32
+ fiber: Fiber
33
+ neurons_created: list[Neuron]
34
+ neurons_linked: list[str]
35
+ synapses_created: list[Synapse]
36
+
37
+
38
+ class MemoryEncoder:
39
+ """
40
+ Encoder for converting experiences into neural structures.
41
+
42
+ The encoder:
43
+ 1. Extracts neurons from content (time, entities, actions, concepts)
44
+ 2. Finds existing similar neurons for de-duplication
45
+ 3. Creates synapses based on relationships
46
+ 4. Bundles everything into a Fiber
47
+ 5. Auto-links with nearby temporal neurons
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ storage: NeuralStorage,
53
+ config: BrainConfig,
54
+ temporal_extractor: TemporalExtractor | None = None,
55
+ entity_extractor: EntityExtractor | None = None,
56
+ ) -> None:
57
+ """
58
+ Initialize the encoder.
59
+
60
+ Args:
61
+ storage: Storage backend
62
+ config: Brain configuration
63
+ temporal_extractor: Custom temporal extractor
64
+ entity_extractor: Custom entity extractor
65
+ """
66
+ self._storage = storage
67
+ self._config = config
68
+ self._temporal = temporal_extractor or TemporalExtractor()
69
+ self._entity = entity_extractor or EntityExtractor()
70
+
71
+ async def encode(
72
+ self,
73
+ content: str,
74
+ timestamp: datetime | None = None,
75
+ metadata: dict[str, Any] | None = None,
76
+ tags: set[str] | None = None,
77
+ ) -> EncodingResult:
78
+ """
79
+ Encode content into neural structures.
80
+
81
+ Args:
82
+ content: The text content to encode
83
+ timestamp: When this memory occurred (default: now)
84
+ metadata: Additional metadata to attach
85
+ tags: Optional tags for the fiber
86
+
87
+ Returns:
88
+ EncodingResult with created structures
89
+ """
90
+ if timestamp is None:
91
+ timestamp = datetime.utcnow()
92
+
93
+ neurons_created: list[Neuron] = []
94
+ neurons_linked: list[str] = []
95
+ synapses_created: list[Synapse] = []
96
+
97
+ # 1. Extract time neurons
98
+ time_neurons = await self._extract_time_neurons(content, timestamp)
99
+ neurons_created.extend(time_neurons)
100
+
101
+ # 2. Extract entity neurons
102
+ entity_neurons = await self._extract_entity_neurons(content)
103
+ neurons_created.extend(entity_neurons)
104
+
105
+ # 3. Extract concept/keyword neurons
106
+ concept_neurons = await self._extract_concept_neurons(content)
107
+ neurons_created.extend(concept_neurons)
108
+
109
+ # 4. Create the anchor neuron (main content)
110
+ anchor_neuron = Neuron.create(
111
+ type=NeuronType.CONCEPT,
112
+ content=content,
113
+ metadata={
114
+ "is_anchor": True,
115
+ "timestamp": timestamp.isoformat(),
116
+ **(metadata or {}),
117
+ },
118
+ )
119
+ await self._storage.add_neuron(anchor_neuron)
120
+ neurons_created.append(anchor_neuron)
121
+
122
+ # 5. Create synapses between neurons
123
+ all_neurons = neurons_created
124
+
125
+ # Connect anchor to time neurons
126
+ for time_neuron in time_neurons:
127
+ synapse = Synapse.create(
128
+ source_id=anchor_neuron.id,
129
+ target_id=time_neuron.id,
130
+ type=SynapseType.HAPPENED_AT,
131
+ weight=0.9,
132
+ )
133
+ await self._storage.add_synapse(synapse)
134
+ synapses_created.append(synapse)
135
+
136
+ # Connect anchor to entity neurons
137
+ for entity_neuron in entity_neurons:
138
+ synapse = Synapse.create(
139
+ source_id=anchor_neuron.id,
140
+ target_id=entity_neuron.id,
141
+ type=SynapseType.INVOLVES,
142
+ weight=0.8,
143
+ )
144
+ await self._storage.add_synapse(synapse)
145
+ synapses_created.append(synapse)
146
+
147
+ # Connect anchor to concept neurons
148
+ for concept_neuron in concept_neurons:
149
+ synapse = Synapse.create(
150
+ source_id=anchor_neuron.id,
151
+ target_id=concept_neuron.id,
152
+ type=SynapseType.RELATED_TO,
153
+ weight=0.6,
154
+ )
155
+ await self._storage.add_synapse(synapse)
156
+ synapses_created.append(synapse)
157
+
158
+ # Connect entities that co-occur
159
+ for i, neuron_a in enumerate(entity_neurons):
160
+ for neuron_b in entity_neurons[i + 1 :]:
161
+ synapse = Synapse.create(
162
+ source_id=neuron_a.id,
163
+ target_id=neuron_b.id,
164
+ type=SynapseType.CO_OCCURS,
165
+ weight=0.5,
166
+ )
167
+ await self._storage.add_synapse(synapse)
168
+ synapses_created.append(synapse)
169
+
170
+ # 6. Link to nearby temporal memories
171
+ linked = await self._link_temporal_neighbors(anchor_neuron, timestamp)
172
+ neurons_linked.extend(linked)
173
+
174
+ # 7. Create fiber
175
+ neuron_ids = {n.id for n in all_neurons}
176
+ synapse_ids = {s.id for s in synapses_created}
177
+
178
+ fiber = Fiber.create(
179
+ neuron_ids=neuron_ids,
180
+ synapse_ids=synapse_ids,
181
+ anchor_neuron_id=anchor_neuron.id,
182
+ time_start=timestamp,
183
+ time_end=timestamp,
184
+ tags=tags,
185
+ metadata=metadata,
186
+ )
187
+
188
+ # Calculate coherence (simple: edges / possible edges)
189
+ possible_edges = len(neuron_ids) * (len(neuron_ids) - 1) / 2
190
+ coherence = len(synapse_ids) / max(1, possible_edges)
191
+ fiber = fiber.with_salience(min(1.0, coherence + 0.3))
192
+
193
+ await self._storage.add_fiber(fiber)
194
+
195
+ return EncodingResult(
196
+ fiber=fiber,
197
+ neurons_created=neurons_created,
198
+ neurons_linked=neurons_linked,
199
+ synapses_created=synapses_created,
200
+ )
201
+
202
+ async def _extract_time_neurons(
203
+ self,
204
+ content: str,
205
+ reference_time: datetime,
206
+ ) -> list[Neuron]:
207
+ """Extract and create time neurons from content."""
208
+ neurons: list[Neuron] = []
209
+
210
+ # Extract time hints from content
211
+ time_hints = self._temporal.extract(content, reference_time)
212
+
213
+ for hint in time_hints:
214
+ # Check for existing similar time neuron
215
+ existing = await self._find_similar_time_neuron(hint.midpoint)
216
+ if existing:
217
+ # Use existing neuron
218
+ continue
219
+
220
+ neuron = Neuron.create(
221
+ type=NeuronType.TIME,
222
+ content=hint.original,
223
+ metadata={
224
+ "absolute_start": hint.absolute_start.isoformat(),
225
+ "absolute_end": hint.absolute_end.isoformat(),
226
+ "granularity": hint.granularity.value,
227
+ },
228
+ )
229
+ await self._storage.add_neuron(neuron)
230
+ neurons.append(neuron)
231
+
232
+ # Always create a time neuron for the reference timestamp
233
+ timestamp_neuron = Neuron.create(
234
+ type=NeuronType.TIME,
235
+ content=reference_time.strftime("%Y-%m-%d %H:%M"),
236
+ metadata={
237
+ "absolute_start": reference_time.isoformat(),
238
+ "absolute_end": reference_time.isoformat(),
239
+ "granularity": "minute",
240
+ },
241
+ )
242
+ await self._storage.add_neuron(timestamp_neuron)
243
+ neurons.append(timestamp_neuron)
244
+
245
+ return neurons
246
+
247
+ async def _find_similar_time_neuron(
248
+ self,
249
+ timestamp: datetime,
250
+ ) -> Neuron | None:
251
+ """Find existing time neuron close to given timestamp."""
252
+ from datetime import timedelta
253
+
254
+ # Look for time neurons within 1 hour
255
+ start = timestamp - timedelta(hours=1)
256
+ end = timestamp + timedelta(hours=1)
257
+
258
+ existing = await self._storage.find_neurons(
259
+ type=NeuronType.TIME,
260
+ time_range=(start, end),
261
+ limit=1,
262
+ )
263
+
264
+ return existing[0] if existing else None
265
+
266
+ async def _extract_entity_neurons(
267
+ self,
268
+ content: str,
269
+ ) -> list[Neuron]:
270
+ """Extract and create entity neurons from content."""
271
+ neurons: list[Neuron] = []
272
+
273
+ entities = self._entity.extract(content)
274
+
275
+ for entity in entities:
276
+ # Map entity type to neuron type
277
+ neuron_type = self._entity_type_to_neuron_type(entity.type)
278
+
279
+ # Check for existing similar entity
280
+ existing = await self._find_similar_entity(entity.text)
281
+ if existing:
282
+ continue
283
+
284
+ neuron = Neuron.create(
285
+ type=neuron_type,
286
+ content=entity.text,
287
+ metadata={
288
+ "entity_type": entity.type.value,
289
+ "confidence": entity.confidence,
290
+ },
291
+ )
292
+ await self._storage.add_neuron(neuron)
293
+ neurons.append(neuron)
294
+
295
+ return neurons
296
+
297
+ def _entity_type_to_neuron_type(self, entity_type: EntityType) -> NeuronType:
298
+ """Map entity type to neuron type."""
299
+ mapping = {
300
+ EntityType.PERSON: NeuronType.ENTITY,
301
+ EntityType.LOCATION: NeuronType.SPATIAL,
302
+ EntityType.ORGANIZATION: NeuronType.ENTITY,
303
+ EntityType.PRODUCT: NeuronType.ENTITY,
304
+ EntityType.EVENT: NeuronType.ACTION,
305
+ EntityType.UNKNOWN: NeuronType.CONCEPT,
306
+ }
307
+ return mapping.get(entity_type, NeuronType.CONCEPT)
308
+
309
+ async def _find_similar_entity(
310
+ self,
311
+ text: str,
312
+ ) -> Neuron | None:
313
+ """Find existing entity neuron with similar content."""
314
+ existing = await self._storage.find_neurons(
315
+ content_exact=text,
316
+ limit=1,
317
+ )
318
+ return existing[0] if existing else None
319
+
320
+ async def _extract_concept_neurons(
321
+ self,
322
+ content: str,
323
+ ) -> list[Neuron]:
324
+ """Extract and create concept neurons from keywords."""
325
+ neurons: list[Neuron] = []
326
+
327
+ keywords = extract_keywords(content)
328
+
329
+ # Only create neurons for significant keywords
330
+ for keyword in keywords[:10]: # Limit to top 10
331
+ if len(keyword) < 3:
332
+ continue
333
+
334
+ # Check for existing
335
+ existing = await self._storage.find_neurons(
336
+ type=NeuronType.CONCEPT,
337
+ content_exact=keyword,
338
+ limit=1,
339
+ )
340
+ if existing:
341
+ continue
342
+
343
+ neuron = Neuron.create(
344
+ type=NeuronType.CONCEPT,
345
+ content=keyword,
346
+ )
347
+ await self._storage.add_neuron(neuron)
348
+ neurons.append(neuron)
349
+
350
+ return neurons
351
+
352
+ async def _link_temporal_neighbors(
353
+ self,
354
+ anchor: Neuron,
355
+ timestamp: datetime,
356
+ ) -> list[str]:
357
+ """Link to temporally nearby memories."""
358
+ from datetime import timedelta
359
+
360
+ linked: list[str] = []
361
+
362
+ # Find fibers in nearby time window
363
+ start = timestamp - timedelta(hours=24)
364
+ end = timestamp + timedelta(hours=24)
365
+
366
+ nearby_fibers = await self._storage.find_fibers(
367
+ time_overlaps=(start, end),
368
+ limit=5,
369
+ )
370
+
371
+ for fiber in nearby_fibers:
372
+ if fiber.anchor_neuron_id == anchor.id:
373
+ continue
374
+
375
+ # Create temporal synapse
376
+ synapse = Synapse.create(
377
+ source_id=anchor.id,
378
+ target_id=fiber.anchor_neuron_id,
379
+ type=SynapseType.RELATED_TO,
380
+ weight=0.3,
381
+ metadata={"temporal_link": True},
382
+ )
383
+
384
+ try:
385
+ await self._storage.add_synapse(synapse)
386
+ linked.append(fiber.anchor_neuron_id)
387
+ except ValueError:
388
+ # Synapse might already exist
389
+ pass
390
+
391
+ return linked