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,396 @@
1
+ """Query router for intelligent query routing.
2
+
3
+ MemoCore-style query routing that determines the optimal retrieval strategy
4
+ based on query analysis. Routes queries to:
5
+ - Semantic search (RAG/embeddings) for conceptual queries
6
+ - Temporal traversal for time-based queries
7
+ - Causal traversal for why/how queries
8
+ - Direct lookup for exact recall
9
+ - Pattern matching for habit/frequency queries
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from dataclasses import dataclass
15
+ from enum import IntEnum, StrEnum
16
+ from typing import Any
17
+
18
+ from neural_memory.extraction.parser import QueryIntent, Stimulus
19
+
20
+
21
+ class QueryType(StrEnum):
22
+ """Types of queries for routing purposes."""
23
+
24
+ SEMANTIC = "semantic" # Conceptual, meaning-based: "What do I know about auth?"
25
+ TEMPORAL = "temporal" # Time-based: "What did I do yesterday?"
26
+ CAUSAL = "causal" # Cause/effect: "Why did the build fail?"
27
+ DIRECT = "direct" # Exact recall: "What's Alice's email?"
28
+ PATTERN = "pattern" # Habit/frequency: "What do I usually do on Mondays?"
29
+ COMPARATIVE = "comparative" # Comparison: "How is X different from Y?"
30
+
31
+
32
+ class RouteConfidence(IntEnum):
33
+ """Confidence level in route selection."""
34
+
35
+ LOW = 1 # Guessing, might be wrong
36
+ MEDIUM = 2 # Reasonable guess
37
+ HIGH = 3 # Strong signals
38
+ CERTAIN = 4 # Very clear query type
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class RouteDecision:
43
+ """Decision about how to route a query.
44
+
45
+ Attributes:
46
+ primary: Primary query type to use
47
+ secondary: Optional fallback query type
48
+ confidence: How confident we are in this routing
49
+ signals: What signals led to this decision
50
+ suggested_depth: Suggested traversal depth (0-3)
51
+ use_embeddings: Whether to use vector search
52
+ time_weighted: Whether to weight by recency
53
+ metadata: Additional routing hints
54
+ """
55
+
56
+ primary: QueryType
57
+ secondary: QueryType | None = None
58
+ confidence: RouteConfidence = RouteConfidence.MEDIUM
59
+ signals: tuple[str, ...] = ()
60
+ suggested_depth: int = 1
61
+ use_embeddings: bool = False
62
+ time_weighted: bool = True
63
+ metadata: dict[str, Any] | None = None
64
+
65
+ @property
66
+ def should_fallback(self) -> bool:
67
+ """Whether to try secondary route if primary fails."""
68
+ return self.secondary is not None and self.confidence <= RouteConfidence.MEDIUM
69
+
70
+
71
+ class QueryRouter:
72
+ """Routes queries to optimal retrieval strategies.
73
+
74
+ The router analyzes a Stimulus (parsed query) and determines:
75
+ 1. What type of query this is
76
+ 2. What retrieval strategy to use
77
+ 3. How deep to search
78
+ 4. Whether to use semantic search
79
+ """
80
+
81
+ # Keywords that strongly indicate query types
82
+ SEMANTIC_SIGNALS = frozenset(
83
+ [
84
+ # English
85
+ "about",
86
+ "related",
87
+ "concept",
88
+ "idea",
89
+ "understand",
90
+ "explain",
91
+ "knowledge",
92
+ "information",
93
+ "details",
94
+ "overview",
95
+ # Vietnamese
96
+ "về",
97
+ "liên quan",
98
+ "khái niệm",
99
+ "hiểu",
100
+ "giải thích",
101
+ "thông tin",
102
+ ]
103
+ )
104
+
105
+ TEMPORAL_SIGNALS = frozenset(
106
+ [
107
+ # English
108
+ "when",
109
+ "yesterday",
110
+ "today",
111
+ "last week",
112
+ "ago",
113
+ "before",
114
+ "after",
115
+ "morning",
116
+ "afternoon",
117
+ "evening",
118
+ "night",
119
+ "recently",
120
+ "earlier",
121
+ # Vietnamese
122
+ "khi nào",
123
+ "hôm qua",
124
+ "hôm nay",
125
+ "tuần trước",
126
+ "trước",
127
+ "sau",
128
+ "sáng",
129
+ "chiều",
130
+ "tối",
131
+ "gần đây",
132
+ "lúc",
133
+ ]
134
+ )
135
+
136
+ CAUSAL_SIGNALS = frozenset(
137
+ [
138
+ # English
139
+ "why",
140
+ "because",
141
+ "cause",
142
+ "reason",
143
+ "result",
144
+ "led to",
145
+ "caused",
146
+ "effect",
147
+ "consequence",
148
+ "how come",
149
+ # Vietnamese
150
+ "tại sao",
151
+ "vì sao",
152
+ "lý do",
153
+ "nguyên nhân",
154
+ "kết quả",
155
+ "dẫn đến",
156
+ ]
157
+ )
158
+
159
+ DIRECT_SIGNALS = frozenset(
160
+ [
161
+ # English
162
+ "what is",
163
+ "what's",
164
+ "exact",
165
+ "specific",
166
+ "precisely",
167
+ "tell me the",
168
+ "give me",
169
+ "show me",
170
+ "find",
171
+ # Vietnamese
172
+ "là gì",
173
+ "chính xác",
174
+ "cụ thể",
175
+ "cho tôi",
176
+ "tìm",
177
+ ]
178
+ )
179
+
180
+ PATTERN_SIGNALS = frozenset(
181
+ [
182
+ # English
183
+ "usually",
184
+ "typically",
185
+ "always",
186
+ "often",
187
+ "habit",
188
+ "routine",
189
+ "every",
190
+ "pattern",
191
+ "tend to",
192
+ "normally",
193
+ # Vietnamese
194
+ "thường",
195
+ "hay",
196
+ "luôn",
197
+ "thói quen",
198
+ "mỗi",
199
+ "xu hướng",
200
+ ]
201
+ )
202
+
203
+ COMPARATIVE_SIGNALS = frozenset(
204
+ [
205
+ # English
206
+ "compare",
207
+ "versus",
208
+ "vs",
209
+ "difference",
210
+ "different",
211
+ "similar",
212
+ "better",
213
+ "worse",
214
+ "same",
215
+ "like",
216
+ # Vietnamese
217
+ "so sánh",
218
+ "khác",
219
+ "giống",
220
+ "hơn",
221
+ "như",
222
+ ]
223
+ )
224
+
225
+ def route(self, stimulus: Stimulus) -> RouteDecision:
226
+ """Determine the optimal route for a query.
227
+
228
+ Args:
229
+ stimulus: Parsed query stimulus
230
+
231
+ Returns:
232
+ RouteDecision with routing strategy
233
+ """
234
+ query_lower = stimulus.raw_query.lower()
235
+ signals: list[str] = []
236
+
237
+ # Score each query type
238
+ scores: dict[QueryType, float] = {
239
+ QueryType.SEMANTIC: 0.0,
240
+ QueryType.TEMPORAL: 0.0,
241
+ QueryType.CAUSAL: 0.0,
242
+ QueryType.DIRECT: 0.0,
243
+ QueryType.PATTERN: 0.0,
244
+ QueryType.COMPARATIVE: 0.0,
245
+ }
246
+
247
+ # Check for signal keywords
248
+ for signal in self.TEMPORAL_SIGNALS:
249
+ if signal in query_lower:
250
+ scores[QueryType.TEMPORAL] += 2.0
251
+ signals.append(f"temporal:{signal}")
252
+
253
+ for signal in self.CAUSAL_SIGNALS:
254
+ if signal in query_lower:
255
+ scores[QueryType.CAUSAL] += 2.0
256
+ signals.append(f"causal:{signal}")
257
+
258
+ for signal in self.DIRECT_SIGNALS:
259
+ if signal in query_lower:
260
+ scores[QueryType.DIRECT] += 1.5
261
+ signals.append(f"direct:{signal}")
262
+
263
+ for signal in self.PATTERN_SIGNALS:
264
+ if signal in query_lower:
265
+ scores[QueryType.PATTERN] += 2.0
266
+ signals.append(f"pattern:{signal}")
267
+
268
+ for signal in self.COMPARATIVE_SIGNALS:
269
+ if signal in query_lower:
270
+ scores[QueryType.COMPARATIVE] += 2.0
271
+ signals.append(f"comparative:{signal}")
272
+
273
+ for signal in self.SEMANTIC_SIGNALS:
274
+ if signal in query_lower:
275
+ scores[QueryType.SEMANTIC] += 1.0
276
+ signals.append(f"semantic:{signal}")
277
+
278
+ # Use parsed intent to boost scores
279
+ intent_boosts = self._get_intent_boosts(stimulus.intent)
280
+ for query_type, boost in intent_boosts.items():
281
+ scores[query_type] += boost
282
+
283
+ # Time hints strongly indicate temporal
284
+ if stimulus.has_time_context:
285
+ scores[QueryType.TEMPORAL] += 3.0
286
+ signals.append("has_time_hints")
287
+
288
+ # Entities without time often indicate direct lookup
289
+ if stimulus.has_entities and not stimulus.has_time_context:
290
+ scores[QueryType.DIRECT] += 1.0
291
+ signals.append("entities_no_time")
292
+
293
+ # Find primary and secondary types
294
+ sorted_types = sorted(scores.items(), key=lambda x: x[1], reverse=True)
295
+ primary = sorted_types[0][0]
296
+ primary_score = sorted_types[0][1]
297
+ secondary = sorted_types[1][0] if sorted_types[1][1] > 0 else None
298
+
299
+ # Determine confidence
300
+ confidence = self._determine_confidence(primary_score, scores)
301
+
302
+ # Determine depth based on query type
303
+ suggested_depth = self._suggest_depth(primary, stimulus)
304
+
305
+ # Determine if embeddings should be used
306
+ use_embeddings = primary in (QueryType.SEMANTIC, QueryType.COMPARATIVE)
307
+
308
+ # Time weighting for temporal and pattern queries
309
+ time_weighted = primary in (QueryType.TEMPORAL, QueryType.PATTERN)
310
+
311
+ return RouteDecision(
312
+ primary=primary,
313
+ secondary=secondary,
314
+ confidence=confidence,
315
+ signals=tuple(signals),
316
+ suggested_depth=suggested_depth,
317
+ use_embeddings=use_embeddings,
318
+ time_weighted=time_weighted,
319
+ )
320
+
321
+ def _get_intent_boosts(self, intent: QueryIntent) -> dict[QueryType, float]:
322
+ """Get score boosts based on query intent."""
323
+ boosts: dict[QueryType, float] = {}
324
+
325
+ intent_mapping = {
326
+ QueryIntent.ASK_WHEN: {QueryType.TEMPORAL: 2.0},
327
+ QueryIntent.ASK_WHY: {QueryType.CAUSAL: 2.0},
328
+ QueryIntent.ASK_HOW: {QueryType.CAUSAL: 1.5, QueryType.SEMANTIC: 0.5},
329
+ QueryIntent.ASK_WHAT: {QueryType.SEMANTIC: 1.0, QueryType.DIRECT: 1.0},
330
+ QueryIntent.ASK_WHO: {QueryType.DIRECT: 1.5},
331
+ QueryIntent.ASK_WHERE: {QueryType.DIRECT: 1.5, QueryType.TEMPORAL: 0.5},
332
+ QueryIntent.ASK_PATTERN: {QueryType.PATTERN: 3.0},
333
+ QueryIntent.ASK_FEELING: {QueryType.TEMPORAL: 1.0, QueryType.SEMANTIC: 1.0},
334
+ QueryIntent.CONFIRM: {QueryType.DIRECT: 2.0},
335
+ QueryIntent.COMPARE: {QueryType.COMPARATIVE: 3.0},
336
+ }
337
+
338
+ return intent_mapping.get(intent, boosts)
339
+
340
+ def _determine_confidence(
341
+ self, primary_score: float, all_scores: dict[QueryType, float]
342
+ ) -> RouteConfidence:
343
+ """Determine confidence in the routing decision."""
344
+ if primary_score >= 5.0:
345
+ return RouteConfidence.CERTAIN
346
+ elif primary_score >= 3.0:
347
+ return RouteConfidence.HIGH
348
+ elif primary_score >= 1.5:
349
+ return RouteConfidence.MEDIUM
350
+ else:
351
+ return RouteConfidence.LOW
352
+
353
+ def _suggest_depth(self, query_type: QueryType, stimulus: Stimulus) -> int:
354
+ """Suggest traversal depth based on query type."""
355
+ depth_defaults = {
356
+ QueryType.DIRECT: 0, # Instant lookup
357
+ QueryType.TEMPORAL: 1, # Context level
358
+ QueryType.SEMANTIC: 2, # Habit level (broader search)
359
+ QueryType.CAUSAL: 2, # Need to traverse causes
360
+ QueryType.PATTERN: 2, # Cross-time patterns
361
+ QueryType.COMPARATIVE: 2, # Need multiple memories
362
+ }
363
+
364
+ base_depth = depth_defaults.get(query_type, 1)
365
+
366
+ # Increase depth for complex queries
367
+ if stimulus.anchor_count > 3:
368
+ base_depth = min(3, base_depth + 1)
369
+
370
+ return base_depth
371
+
372
+
373
+ def route_query(stimulus: Stimulus) -> RouteDecision:
374
+ """Convenience function to route a query.
375
+
376
+ Args:
377
+ stimulus: Parsed query stimulus
378
+
379
+ Returns:
380
+ RouteDecision with routing strategy
381
+ """
382
+ router = QueryRouter()
383
+ return router.route(stimulus)
384
+
385
+
386
+ def get_query_type_description(query_type: QueryType) -> str:
387
+ """Get human-readable description of a query type."""
388
+ descriptions = {
389
+ QueryType.SEMANTIC: "Conceptual search - finding related ideas and knowledge",
390
+ QueryType.TEMPORAL: "Time-based search - finding memories from specific times",
391
+ QueryType.CAUSAL: "Causal search - understanding why/how things happened",
392
+ QueryType.DIRECT: "Direct lookup - finding specific facts or details",
393
+ QueryType.PATTERN: "Pattern search - finding habits and recurring themes",
394
+ QueryType.COMPARATIVE: "Comparative search - comparing different memories",
395
+ }
396
+ return descriptions.get(query_type, "Unknown query type")