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,337 @@
|
|
|
1
|
+
"""Query parser for decomposing queries into activation signals."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
|
|
9
|
+
from neural_memory.extraction.entities import Entity, EntityExtractor, extract_keywords
|
|
10
|
+
from neural_memory.extraction.temporal import TemporalExtractor, TimeHint
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class QueryIntent(StrEnum):
|
|
14
|
+
"""The intent/purpose of a query."""
|
|
15
|
+
|
|
16
|
+
ASK_WHAT = "ask_what" # What happened?
|
|
17
|
+
ASK_WHERE = "ask_where" # Where did it happen?
|
|
18
|
+
ASK_WHEN = "ask_when" # When did it happen?
|
|
19
|
+
ASK_WHO = "ask_who" # Who was involved?
|
|
20
|
+
ASK_WHY = "ask_why" # Why did it happen?
|
|
21
|
+
ASK_HOW = "ask_how" # How did it happen?
|
|
22
|
+
ASK_FEELING = "ask_feeling" # How did I feel?
|
|
23
|
+
ASK_PATTERN = "ask_pattern" # What's the pattern?
|
|
24
|
+
CONFIRM = "confirm" # Did X happen?
|
|
25
|
+
COMPARE = "compare" # Compare X and Y
|
|
26
|
+
RECALL = "recall" # General recall
|
|
27
|
+
UNKNOWN = "unknown"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Perspective(StrEnum):
|
|
31
|
+
"""The perspective/framing of the query."""
|
|
32
|
+
|
|
33
|
+
RECALL = "recall" # Remember something
|
|
34
|
+
CONFIRM = "confirm" # Verify something
|
|
35
|
+
COMPARE = "compare" # Compare things
|
|
36
|
+
ANALYZE = "analyze" # Analyze/understand
|
|
37
|
+
SUMMARIZE = "summarize" # Get summary
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class Stimulus:
|
|
42
|
+
"""
|
|
43
|
+
Decomposed query signals for activation.
|
|
44
|
+
|
|
45
|
+
A Stimulus represents all the extracted signals from a query
|
|
46
|
+
that will be used to activate relevant neurons.
|
|
47
|
+
|
|
48
|
+
Attributes:
|
|
49
|
+
time_hints: Extracted time references
|
|
50
|
+
keywords: Important keywords from the query
|
|
51
|
+
entities: Named entities found
|
|
52
|
+
intent: What the query is asking for
|
|
53
|
+
perspective: How the query frames the request
|
|
54
|
+
raw_query: The original query text
|
|
55
|
+
language: Detected or specified language
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
time_hints: list[TimeHint]
|
|
59
|
+
keywords: list[str]
|
|
60
|
+
entities: list[Entity]
|
|
61
|
+
intent: QueryIntent
|
|
62
|
+
perspective: Perspective
|
|
63
|
+
raw_query: str
|
|
64
|
+
language: str = "auto"
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def has_time_context(self) -> bool:
|
|
68
|
+
"""Check if query has temporal constraints."""
|
|
69
|
+
return len(self.time_hints) > 0
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def has_entities(self) -> bool:
|
|
73
|
+
"""Check if query mentions specific entities."""
|
|
74
|
+
return len(self.entities) > 0
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def anchor_count(self) -> int:
|
|
78
|
+
"""Count of potential anchor points for activation."""
|
|
79
|
+
return len(self.time_hints) + len(self.entities) + len(self.keywords)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class QueryParser:
|
|
83
|
+
"""
|
|
84
|
+
Parser for decomposing queries into activation signals.
|
|
85
|
+
|
|
86
|
+
The parser extracts:
|
|
87
|
+
- Temporal references (time hints)
|
|
88
|
+
- Named entities (people, places, etc.)
|
|
89
|
+
- Keywords (important words)
|
|
90
|
+
- Intent (what the query is asking)
|
|
91
|
+
- Perspective (how the query frames the request)
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
# Intent detection patterns
|
|
95
|
+
INTENT_PATTERNS: dict[QueryIntent, list[str]] = {
|
|
96
|
+
QueryIntent.ASK_WHAT: [
|
|
97
|
+
# English
|
|
98
|
+
r"what",
|
|
99
|
+
r"which",
|
|
100
|
+
r"tell me about",
|
|
101
|
+
# Vietnamese
|
|
102
|
+
r"gì",
|
|
103
|
+
r"cái gì",
|
|
104
|
+
r"điều gì",
|
|
105
|
+
r"chuyện gì",
|
|
106
|
+
r"việc gì",
|
|
107
|
+
],
|
|
108
|
+
QueryIntent.ASK_WHERE: [
|
|
109
|
+
# English
|
|
110
|
+
r"where",
|
|
111
|
+
r"location",
|
|
112
|
+
r"place",
|
|
113
|
+
# Vietnamese
|
|
114
|
+
r"ở đâu",
|
|
115
|
+
r"đâu",
|
|
116
|
+
r"chỗ nào",
|
|
117
|
+
r"nơi nào",
|
|
118
|
+
],
|
|
119
|
+
QueryIntent.ASK_WHEN: [
|
|
120
|
+
# English
|
|
121
|
+
r"when",
|
|
122
|
+
r"what time",
|
|
123
|
+
# Vietnamese
|
|
124
|
+
r"khi nào",
|
|
125
|
+
r"lúc nào",
|
|
126
|
+
r"bao giờ",
|
|
127
|
+
r"mấy giờ",
|
|
128
|
+
],
|
|
129
|
+
QueryIntent.ASK_WHO: [
|
|
130
|
+
# English
|
|
131
|
+
r"who",
|
|
132
|
+
r"whom",
|
|
133
|
+
# Vietnamese
|
|
134
|
+
r"ai",
|
|
135
|
+
r"người nào",
|
|
136
|
+
r"với ai",
|
|
137
|
+
],
|
|
138
|
+
QueryIntent.ASK_WHY: [
|
|
139
|
+
# English
|
|
140
|
+
r"why",
|
|
141
|
+
r"reason",
|
|
142
|
+
r"cause",
|
|
143
|
+
# Vietnamese
|
|
144
|
+
r"tại sao",
|
|
145
|
+
r"vì sao",
|
|
146
|
+
r"lý do",
|
|
147
|
+
],
|
|
148
|
+
QueryIntent.ASK_HOW: [
|
|
149
|
+
# English
|
|
150
|
+
r"how did",
|
|
151
|
+
r"how was",
|
|
152
|
+
r"how to",
|
|
153
|
+
# Vietnamese
|
|
154
|
+
r"như thế nào",
|
|
155
|
+
r"làm sao",
|
|
156
|
+
r"thế nào",
|
|
157
|
+
r"ra sao",
|
|
158
|
+
],
|
|
159
|
+
QueryIntent.ASK_FEELING: [
|
|
160
|
+
# English
|
|
161
|
+
r"how (?:did|do) (?:i|you) feel",
|
|
162
|
+
r"feeling",
|
|
163
|
+
r"emotion",
|
|
164
|
+
# Vietnamese
|
|
165
|
+
r"cảm thấy",
|
|
166
|
+
r"cảm xúc",
|
|
167
|
+
r"tâm trạng",
|
|
168
|
+
r"vui không",
|
|
169
|
+
r"buồn không",
|
|
170
|
+
],
|
|
171
|
+
QueryIntent.ASK_PATTERN: [
|
|
172
|
+
# English
|
|
173
|
+
r"usually",
|
|
174
|
+
r"typically",
|
|
175
|
+
r"pattern",
|
|
176
|
+
r"often",
|
|
177
|
+
r"always",
|
|
178
|
+
# Vietnamese
|
|
179
|
+
r"thường",
|
|
180
|
+
r"hay",
|
|
181
|
+
r"luôn",
|
|
182
|
+
r"mỗi khi",
|
|
183
|
+
],
|
|
184
|
+
QueryIntent.CONFIRM: [
|
|
185
|
+
# English
|
|
186
|
+
r"did (?:i|we|you)",
|
|
187
|
+
r"was there",
|
|
188
|
+
r"have (?:i|we|you)",
|
|
189
|
+
r"is it true",
|
|
190
|
+
# Vietnamese
|
|
191
|
+
r"có phải",
|
|
192
|
+
r"đúng không",
|
|
193
|
+
r"phải không",
|
|
194
|
+
],
|
|
195
|
+
QueryIntent.COMPARE: [
|
|
196
|
+
# English
|
|
197
|
+
r"compare",
|
|
198
|
+
r"difference",
|
|
199
|
+
r"versus",
|
|
200
|
+
r"vs",
|
|
201
|
+
r"better",
|
|
202
|
+
r"worse",
|
|
203
|
+
# Vietnamese
|
|
204
|
+
r"so sánh",
|
|
205
|
+
r"khác nhau",
|
|
206
|
+
r"giống nhau",
|
|
207
|
+
r"hơn",
|
|
208
|
+
],
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
def __init__(
|
|
212
|
+
self,
|
|
213
|
+
temporal_extractor: TemporalExtractor | None = None,
|
|
214
|
+
entity_extractor: EntityExtractor | None = None,
|
|
215
|
+
) -> None:
|
|
216
|
+
"""
|
|
217
|
+
Initialize the parser.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
temporal_extractor: Custom temporal extractor (creates default if None)
|
|
221
|
+
entity_extractor: Custom entity extractor (creates default if None)
|
|
222
|
+
"""
|
|
223
|
+
self._temporal = temporal_extractor or TemporalExtractor()
|
|
224
|
+
self._entity = entity_extractor or EntityExtractor()
|
|
225
|
+
|
|
226
|
+
# Compile intent patterns
|
|
227
|
+
import re
|
|
228
|
+
|
|
229
|
+
self._intent_compiled: dict[QueryIntent, list[re.Pattern[str]]] = {}
|
|
230
|
+
for intent, patterns in self.INTENT_PATTERNS.items():
|
|
231
|
+
self._intent_compiled[intent] = [re.compile(p, re.IGNORECASE) for p in patterns]
|
|
232
|
+
|
|
233
|
+
def parse(
|
|
234
|
+
self,
|
|
235
|
+
query: str,
|
|
236
|
+
reference_time: datetime | None = None,
|
|
237
|
+
language: str = "auto",
|
|
238
|
+
) -> Stimulus:
|
|
239
|
+
"""
|
|
240
|
+
Parse a query into a Stimulus.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
query: The query text
|
|
244
|
+
reference_time: Reference time for temporal parsing
|
|
245
|
+
language: "vi", "en", or "auto"
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Stimulus containing all extracted signals
|
|
249
|
+
"""
|
|
250
|
+
if reference_time is None:
|
|
251
|
+
reference_time = datetime.now()
|
|
252
|
+
|
|
253
|
+
# Detect language if auto
|
|
254
|
+
if language == "auto":
|
|
255
|
+
language = self._detect_language(query)
|
|
256
|
+
|
|
257
|
+
# Extract components
|
|
258
|
+
time_hints = self._temporal.extract(query, reference_time, language)
|
|
259
|
+
entities = self._entity.extract(query, language)
|
|
260
|
+
keywords = extract_keywords(query)
|
|
261
|
+
|
|
262
|
+
# Detect intent
|
|
263
|
+
intent = self._detect_intent(query)
|
|
264
|
+
|
|
265
|
+
# Detect perspective
|
|
266
|
+
perspective = self._detect_perspective(query, intent)
|
|
267
|
+
|
|
268
|
+
return Stimulus(
|
|
269
|
+
time_hints=time_hints,
|
|
270
|
+
keywords=keywords,
|
|
271
|
+
entities=entities,
|
|
272
|
+
intent=intent,
|
|
273
|
+
perspective=perspective,
|
|
274
|
+
raw_query=query,
|
|
275
|
+
language=language,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
def _detect_language(self, text: str) -> str:
|
|
279
|
+
"""Simple language detection."""
|
|
280
|
+
# Vietnamese-specific characters
|
|
281
|
+
vi_chars = set("àáảãạăằắẳẵặâầấẩẫậèéẻẽẹêềếểễệìíỉĩịòóỏõọôồốổỗộơờớởỡợùúủũụưừứửữựỳýỷỹỵđ")
|
|
282
|
+
|
|
283
|
+
text_lower = text.lower()
|
|
284
|
+
vi_count = sum(1 for c in text_lower if c in vi_chars)
|
|
285
|
+
|
|
286
|
+
# If significant Vietnamese characters, it's Vietnamese
|
|
287
|
+
if vi_count > len(text) * 0.05:
|
|
288
|
+
return "vi"
|
|
289
|
+
|
|
290
|
+
# Check for Vietnamese words
|
|
291
|
+
vi_words = {"của", "và", "là", "có", "được", "cho", "với", "này", "trong", "để", "các"}
|
|
292
|
+
words = set(text_lower.split())
|
|
293
|
+
if words & vi_words:
|
|
294
|
+
return "vi"
|
|
295
|
+
|
|
296
|
+
return "en"
|
|
297
|
+
|
|
298
|
+
def _detect_intent(self, query: str) -> QueryIntent:
|
|
299
|
+
"""Detect the query intent."""
|
|
300
|
+
query_lower = query.lower()
|
|
301
|
+
|
|
302
|
+
for intent, patterns in self._intent_compiled.items():
|
|
303
|
+
for pattern in patterns:
|
|
304
|
+
if pattern.search(query_lower):
|
|
305
|
+
return intent
|
|
306
|
+
|
|
307
|
+
return QueryIntent.RECALL
|
|
308
|
+
|
|
309
|
+
def _detect_perspective(
|
|
310
|
+
self,
|
|
311
|
+
query: str,
|
|
312
|
+
intent: QueryIntent,
|
|
313
|
+
) -> Perspective:
|
|
314
|
+
"""Detect the query perspective."""
|
|
315
|
+
query_lower = query.lower()
|
|
316
|
+
|
|
317
|
+
# Check for confirmation patterns
|
|
318
|
+
if intent == QueryIntent.CONFIRM:
|
|
319
|
+
return Perspective.CONFIRM
|
|
320
|
+
|
|
321
|
+
# Check for comparison patterns
|
|
322
|
+
if intent == QueryIntent.COMPARE:
|
|
323
|
+
return Perspective.COMPARE
|
|
324
|
+
|
|
325
|
+
# Check for summary patterns
|
|
326
|
+
summary_patterns = ["summary", "summarize", "tóm tắt", "overview", "tổng kết"]
|
|
327
|
+
for pattern in summary_patterns:
|
|
328
|
+
if pattern in query_lower:
|
|
329
|
+
return Perspective.SUMMARIZE
|
|
330
|
+
|
|
331
|
+
# Check for analysis patterns
|
|
332
|
+
analysis_patterns = ["analyze", "understand", "explain", "phân tích", "giải thích"]
|
|
333
|
+
for pattern in analysis_patterns:
|
|
334
|
+
if pattern in query_lower:
|
|
335
|
+
return Perspective.ANALYZE
|
|
336
|
+
|
|
337
|
+
return Perspective.RECALL
|