alma-memory 0.5.1__py3-none-any.whl → 0.7.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.
- alma/__init__.py +296 -226
- alma/compression/__init__.py +33 -0
- alma/compression/pipeline.py +980 -0
- alma/confidence/__init__.py +47 -47
- alma/confidence/engine.py +540 -540
- alma/confidence/types.py +351 -351
- alma/config/loader.py +157 -157
- alma/consolidation/__init__.py +23 -23
- alma/consolidation/engine.py +678 -678
- alma/consolidation/prompts.py +84 -84
- alma/core.py +1189 -430
- alma/domains/__init__.py +30 -30
- alma/domains/factory.py +359 -359
- alma/domains/schemas.py +448 -448
- alma/domains/types.py +272 -272
- alma/events/__init__.py +75 -75
- alma/events/emitter.py +285 -284
- alma/events/storage_mixin.py +246 -246
- alma/events/types.py +126 -126
- alma/events/webhook.py +425 -425
- alma/exceptions.py +49 -49
- alma/extraction/__init__.py +31 -31
- alma/extraction/auto_learner.py +265 -265
- alma/extraction/extractor.py +420 -420
- alma/graph/__init__.py +106 -106
- alma/graph/backends/__init__.py +32 -32
- alma/graph/backends/kuzu.py +624 -624
- alma/graph/backends/memgraph.py +432 -432
- alma/graph/backends/memory.py +236 -236
- alma/graph/backends/neo4j.py +417 -417
- alma/graph/base.py +159 -159
- alma/graph/extraction.py +198 -198
- alma/graph/store.py +860 -860
- alma/harness/__init__.py +35 -35
- alma/harness/base.py +386 -386
- alma/harness/domains.py +705 -705
- alma/initializer/__init__.py +37 -37
- alma/initializer/initializer.py +418 -418
- alma/initializer/types.py +250 -250
- alma/integration/__init__.py +62 -62
- alma/integration/claude_agents.py +444 -444
- alma/integration/helena.py +423 -423
- alma/integration/victor.py +471 -471
- alma/learning/__init__.py +101 -86
- alma/learning/decay.py +878 -0
- alma/learning/forgetting.py +1446 -1446
- alma/learning/heuristic_extractor.py +390 -390
- alma/learning/protocols.py +374 -374
- alma/learning/validation.py +346 -346
- alma/mcp/__init__.py +123 -45
- alma/mcp/__main__.py +156 -156
- alma/mcp/resources.py +122 -122
- alma/mcp/server.py +955 -591
- alma/mcp/tools.py +3254 -509
- alma/observability/__init__.py +91 -84
- alma/observability/config.py +302 -302
- alma/observability/guidelines.py +170 -0
- alma/observability/logging.py +424 -424
- alma/observability/metrics.py +583 -583
- alma/observability/tracing.py +440 -440
- alma/progress/__init__.py +21 -21
- alma/progress/tracker.py +607 -607
- alma/progress/types.py +250 -250
- alma/retrieval/__init__.py +134 -53
- alma/retrieval/budget.py +525 -0
- alma/retrieval/cache.py +1304 -1061
- alma/retrieval/embeddings.py +202 -202
- alma/retrieval/engine.py +850 -427
- alma/retrieval/modes.py +365 -0
- alma/retrieval/progressive.py +560 -0
- alma/retrieval/scoring.py +344 -344
- alma/retrieval/trust_scoring.py +637 -0
- alma/retrieval/verification.py +797 -0
- alma/session/__init__.py +19 -19
- alma/session/manager.py +442 -399
- alma/session/types.py +288 -288
- alma/storage/__init__.py +101 -90
- alma/storage/archive.py +233 -0
- alma/storage/azure_cosmos.py +1259 -1259
- alma/storage/base.py +1083 -583
- alma/storage/chroma.py +1443 -1443
- alma/storage/constants.py +103 -103
- alma/storage/file_based.py +614 -614
- alma/storage/migrations/__init__.py +21 -21
- alma/storage/migrations/base.py +321 -321
- alma/storage/migrations/runner.py +323 -323
- alma/storage/migrations/version_stores.py +337 -337
- alma/storage/migrations/versions/__init__.py +11 -11
- alma/storage/migrations/versions/v1_0_0.py +373 -373
- alma/storage/migrations/versions/v1_1_0_workflow_context.py +551 -0
- alma/storage/pinecone.py +1080 -1080
- alma/storage/postgresql.py +1948 -1559
- alma/storage/qdrant.py +1306 -1306
- alma/storage/sqlite_local.py +3041 -1457
- alma/testing/__init__.py +46 -46
- alma/testing/factories.py +301 -301
- alma/testing/mocks.py +389 -389
- alma/types.py +292 -264
- alma/utils/__init__.py +19 -0
- alma/utils/tokenizer.py +521 -0
- alma/workflow/__init__.py +83 -0
- alma/workflow/artifacts.py +170 -0
- alma/workflow/checkpoint.py +311 -0
- alma/workflow/context.py +228 -0
- alma/workflow/outcomes.py +189 -0
- alma/workflow/reducers.py +393 -0
- {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/METADATA +210 -72
- alma_memory-0.7.0.dist-info/RECORD +112 -0
- alma_memory-0.5.1.dist-info/RECORD +0 -93
- {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/WHEEL +0 -0
- {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/top_level.txt +0 -0
alma/retrieval/modes.py
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ALMA Retrieval Modes.
|
|
3
|
+
|
|
4
|
+
Provides mode-aware retrieval strategies that adapt to different cognitive tasks.
|
|
5
|
+
Based on Memory Wall principles: "Planning needs breadth. Execution needs precision."
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Dict, List
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RetrievalMode(Enum):
|
|
14
|
+
"""
|
|
15
|
+
Cognitive modes for retrieval strategy selection.
|
|
16
|
+
|
|
17
|
+
Different tasks require fundamentally different retrieval approaches:
|
|
18
|
+
- BROAD: For planning, brainstorming - needs diverse, exploratory results
|
|
19
|
+
- PRECISE: For execution, implementation - needs high-confidence matches
|
|
20
|
+
- DIAGNOSTIC: For debugging, troubleshooting - needs anti-patterns and failures
|
|
21
|
+
- LEARNING: For pattern finding, consolidation - needs similar memories to merge
|
|
22
|
+
- RECALL: For exact memory lookup - prioritizes exact matches
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
BROAD = "broad"
|
|
26
|
+
PRECISE = "precise"
|
|
27
|
+
DIAGNOSTIC = "diagnostic"
|
|
28
|
+
LEARNING = "learning"
|
|
29
|
+
RECALL = "recall"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ModeConfig:
|
|
34
|
+
"""
|
|
35
|
+
Configuration for a retrieval mode.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
top_k: Default number of results to return
|
|
39
|
+
min_confidence: Minimum confidence threshold for results
|
|
40
|
+
weights: Scoring weights (similarity, recency, success, confidence)
|
|
41
|
+
include_anti_patterns: Whether to include anti-patterns in results
|
|
42
|
+
diversity_factor: 0.0 = pure relevance, 1.0 = maximum diversity (MMR)
|
|
43
|
+
prioritize_failures: Boost failed outcomes (for debugging)
|
|
44
|
+
cluster_similar: Group similar results together (for learning)
|
|
45
|
+
exact_match_boost: Multiplier for exact/high-similarity matches
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
top_k: int
|
|
49
|
+
min_confidence: float
|
|
50
|
+
weights: Dict[str, float] = field(default_factory=dict)
|
|
51
|
+
include_anti_patterns: bool = True
|
|
52
|
+
diversity_factor: float = 0.0
|
|
53
|
+
prioritize_failures: bool = False
|
|
54
|
+
cluster_similar: bool = False
|
|
55
|
+
exact_match_boost: float = 1.0
|
|
56
|
+
|
|
57
|
+
def __post_init__(self):
|
|
58
|
+
"""Validate and normalize weights."""
|
|
59
|
+
if self.weights:
|
|
60
|
+
total = sum(self.weights.values())
|
|
61
|
+
if total > 0 and not (0.99 <= total <= 1.01):
|
|
62
|
+
# Normalize weights to sum to 1.0
|
|
63
|
+
self.weights = {k: v / total for k, v in self.weights.items()}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# Default configurations for each mode
|
|
67
|
+
MODE_CONFIGS: Dict[RetrievalMode, ModeConfig] = {
|
|
68
|
+
RetrievalMode.BROAD: ModeConfig(
|
|
69
|
+
top_k=15,
|
|
70
|
+
min_confidence=0.3,
|
|
71
|
+
weights={
|
|
72
|
+
"similarity": 0.70,
|
|
73
|
+
"recency": 0.10,
|
|
74
|
+
"success_rate": 0.10,
|
|
75
|
+
"confidence": 0.10,
|
|
76
|
+
},
|
|
77
|
+
include_anti_patterns=False, # Exploring, don't want negatives
|
|
78
|
+
diversity_factor=0.8, # High diversity for exploration
|
|
79
|
+
exact_match_boost=1.0,
|
|
80
|
+
),
|
|
81
|
+
RetrievalMode.PRECISE: ModeConfig(
|
|
82
|
+
top_k=5,
|
|
83
|
+
min_confidence=0.7,
|
|
84
|
+
weights={
|
|
85
|
+
"similarity": 0.30,
|
|
86
|
+
"recency": 0.10,
|
|
87
|
+
"success_rate": 0.40, # Proven strategies matter
|
|
88
|
+
"confidence": 0.20,
|
|
89
|
+
},
|
|
90
|
+
include_anti_patterns=True, # Know what to avoid
|
|
91
|
+
diversity_factor=0.2, # Focused results
|
|
92
|
+
exact_match_boost=2.0, # Boost exact matches
|
|
93
|
+
),
|
|
94
|
+
RetrievalMode.DIAGNOSTIC: ModeConfig(
|
|
95
|
+
top_k=10,
|
|
96
|
+
min_confidence=0.4,
|
|
97
|
+
weights={
|
|
98
|
+
"similarity": 0.40,
|
|
99
|
+
"recency": 0.30, # Recent issues more relevant
|
|
100
|
+
"success_rate": 0.00, # Don't penalize failures
|
|
101
|
+
"confidence": 0.30,
|
|
102
|
+
},
|
|
103
|
+
include_anti_patterns=True, # Critical for debugging
|
|
104
|
+
diversity_factor=0.5,
|
|
105
|
+
prioritize_failures=True, # Failures are valuable here
|
|
106
|
+
exact_match_boost=1.5,
|
|
107
|
+
),
|
|
108
|
+
RetrievalMode.LEARNING: ModeConfig(
|
|
109
|
+
top_k=20,
|
|
110
|
+
min_confidence=0.2, # Low threshold to find patterns
|
|
111
|
+
weights={
|
|
112
|
+
"similarity": 0.90, # Similarity is key for consolidation
|
|
113
|
+
"recency": 0.00,
|
|
114
|
+
"success_rate": 0.05,
|
|
115
|
+
"confidence": 0.05,
|
|
116
|
+
},
|
|
117
|
+
include_anti_patterns=True,
|
|
118
|
+
diversity_factor=0.3, # Some diversity but group similar
|
|
119
|
+
cluster_similar=True,
|
|
120
|
+
exact_match_boost=1.0,
|
|
121
|
+
),
|
|
122
|
+
RetrievalMode.RECALL: ModeConfig(
|
|
123
|
+
top_k=3,
|
|
124
|
+
min_confidence=0.5,
|
|
125
|
+
weights={
|
|
126
|
+
"similarity": 0.95, # Almost pure similarity
|
|
127
|
+
"recency": 0.00,
|
|
128
|
+
"success_rate": 0.00,
|
|
129
|
+
"confidence": 0.05,
|
|
130
|
+
},
|
|
131
|
+
include_anti_patterns=False,
|
|
132
|
+
diversity_factor=0.0, # No diversity, exact match
|
|
133
|
+
exact_match_boost=3.0, # Strong boost for exact matches
|
|
134
|
+
),
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# Keywords for mode inference
|
|
139
|
+
_DIAGNOSTIC_TERMS = frozenset(
|
|
140
|
+
[
|
|
141
|
+
"error",
|
|
142
|
+
"bug",
|
|
143
|
+
"fail",
|
|
144
|
+
"failed",
|
|
145
|
+
"failing",
|
|
146
|
+
"broken",
|
|
147
|
+
"issue",
|
|
148
|
+
"problem",
|
|
149
|
+
"debug",
|
|
150
|
+
"fix",
|
|
151
|
+
"wrong",
|
|
152
|
+
"crash",
|
|
153
|
+
"exception",
|
|
154
|
+
"traceback",
|
|
155
|
+
"not working",
|
|
156
|
+
"doesn't work",
|
|
157
|
+
"won't work",
|
|
158
|
+
"can't",
|
|
159
|
+
]
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
_BROAD_TERMS = frozenset(
|
|
163
|
+
[
|
|
164
|
+
"how should",
|
|
165
|
+
"what approach",
|
|
166
|
+
"options for",
|
|
167
|
+
"ways to",
|
|
168
|
+
"plan",
|
|
169
|
+
"design",
|
|
170
|
+
"architect",
|
|
171
|
+
"strategy",
|
|
172
|
+
"alternative",
|
|
173
|
+
"consider",
|
|
174
|
+
"brainstorm",
|
|
175
|
+
"explore",
|
|
176
|
+
"ideas for",
|
|
177
|
+
"possibilities",
|
|
178
|
+
]
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
_RECALL_TERMS = frozenset(
|
|
182
|
+
[
|
|
183
|
+
"what was",
|
|
184
|
+
"when did",
|
|
185
|
+
"remember when",
|
|
186
|
+
"last time",
|
|
187
|
+
"previously",
|
|
188
|
+
"before",
|
|
189
|
+
"earlier",
|
|
190
|
+
"what did we",
|
|
191
|
+
"history of",
|
|
192
|
+
"past",
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
_LEARNING_TERMS = frozenset(
|
|
197
|
+
[
|
|
198
|
+
"pattern",
|
|
199
|
+
"similar",
|
|
200
|
+
"consolidate",
|
|
201
|
+
"common",
|
|
202
|
+
"recurring",
|
|
203
|
+
"repeated",
|
|
204
|
+
"frequent",
|
|
205
|
+
"trend",
|
|
206
|
+
"consistent",
|
|
207
|
+
"like before",
|
|
208
|
+
]
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def infer_mode_from_query(query: str) -> RetrievalMode:
|
|
213
|
+
"""
|
|
214
|
+
Heuristically infer the best retrieval mode from query text.
|
|
215
|
+
|
|
216
|
+
Uses keyword matching to detect the cognitive task type.
|
|
217
|
+
Falls back to PRECISE mode for general queries.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
query: The search query or task description
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
The inferred RetrievalMode
|
|
224
|
+
"""
|
|
225
|
+
query_lower = query.lower()
|
|
226
|
+
|
|
227
|
+
# Check for diagnostic terms (errors, bugs, fixes)
|
|
228
|
+
if any(term in query_lower for term in _DIAGNOSTIC_TERMS):
|
|
229
|
+
return RetrievalMode.DIAGNOSTIC
|
|
230
|
+
|
|
231
|
+
# Check for broad/planning terms
|
|
232
|
+
if any(term in query_lower for term in _BROAD_TERMS):
|
|
233
|
+
return RetrievalMode.BROAD
|
|
234
|
+
|
|
235
|
+
# Check for recall terms (historical lookup)
|
|
236
|
+
if any(term in query_lower for term in _RECALL_TERMS):
|
|
237
|
+
return RetrievalMode.RECALL
|
|
238
|
+
|
|
239
|
+
# Check for learning/pattern terms
|
|
240
|
+
if any(term in query_lower for term in _LEARNING_TERMS):
|
|
241
|
+
return RetrievalMode.LEARNING
|
|
242
|
+
|
|
243
|
+
# Default to PRECISE for execution/implementation
|
|
244
|
+
return RetrievalMode.PRECISE
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def get_mode_config(mode: RetrievalMode) -> ModeConfig:
|
|
248
|
+
"""
|
|
249
|
+
Get the configuration for a specific mode.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
mode: The retrieval mode
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
ModeConfig for the specified mode
|
|
256
|
+
"""
|
|
257
|
+
return MODE_CONFIGS[mode]
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def get_mode_reason(query: str, mode: RetrievalMode) -> str:
|
|
261
|
+
"""
|
|
262
|
+
Explain why a particular mode was inferred.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
query: The original query
|
|
266
|
+
mode: The inferred mode
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Human-readable explanation
|
|
270
|
+
"""
|
|
271
|
+
query_lower = query.lower()
|
|
272
|
+
|
|
273
|
+
if mode == RetrievalMode.DIAGNOSTIC:
|
|
274
|
+
matched = [t for t in _DIAGNOSTIC_TERMS if t in query_lower]
|
|
275
|
+
if matched:
|
|
276
|
+
return f"Query contains diagnostic terms: {', '.join(matched[:3])}"
|
|
277
|
+
return "Query appears to be about debugging or troubleshooting"
|
|
278
|
+
|
|
279
|
+
if mode == RetrievalMode.BROAD:
|
|
280
|
+
matched = [t for t in _BROAD_TERMS if t in query_lower]
|
|
281
|
+
if matched:
|
|
282
|
+
return (
|
|
283
|
+
f"Query contains planning/exploration terms: {', '.join(matched[:3])}"
|
|
284
|
+
)
|
|
285
|
+
return "Query appears to be exploratory or planning-related"
|
|
286
|
+
|
|
287
|
+
if mode == RetrievalMode.RECALL:
|
|
288
|
+
matched = [t for t in _RECALL_TERMS if t in query_lower]
|
|
289
|
+
if matched:
|
|
290
|
+
return f"Query contains recall terms: {', '.join(matched[:3])}"
|
|
291
|
+
return "Query appears to be looking for past events or decisions"
|
|
292
|
+
|
|
293
|
+
if mode == RetrievalMode.LEARNING:
|
|
294
|
+
matched = [t for t in _LEARNING_TERMS if t in query_lower]
|
|
295
|
+
if matched:
|
|
296
|
+
return f"Query contains pattern terms: {', '.join(matched[:3])}"
|
|
297
|
+
return "Query appears to be looking for patterns or similarities"
|
|
298
|
+
|
|
299
|
+
return "Default mode for implementation/execution queries"
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def create_custom_mode(base_mode: RetrievalMode, **overrides) -> ModeConfig:
|
|
303
|
+
"""
|
|
304
|
+
Create a custom mode configuration based on an existing mode.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
base_mode: The mode to use as a base
|
|
308
|
+
**overrides: Fields to override (top_k, min_confidence, etc.)
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
New ModeConfig with overrides applied
|
|
312
|
+
"""
|
|
313
|
+
base = get_mode_config(base_mode)
|
|
314
|
+
|
|
315
|
+
return ModeConfig(
|
|
316
|
+
top_k=overrides.get("top_k", base.top_k),
|
|
317
|
+
min_confidence=overrides.get("min_confidence", base.min_confidence),
|
|
318
|
+
weights=overrides.get("weights", base.weights.copy()),
|
|
319
|
+
include_anti_patterns=overrides.get(
|
|
320
|
+
"include_anti_patterns", base.include_anti_patterns
|
|
321
|
+
),
|
|
322
|
+
diversity_factor=overrides.get("diversity_factor", base.diversity_factor),
|
|
323
|
+
prioritize_failures=overrides.get(
|
|
324
|
+
"prioritize_failures", base.prioritize_failures
|
|
325
|
+
),
|
|
326
|
+
cluster_similar=overrides.get("cluster_similar", base.cluster_similar),
|
|
327
|
+
exact_match_boost=overrides.get("exact_match_boost", base.exact_match_boost),
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def validate_mode_config(config: ModeConfig) -> List[str]:
|
|
332
|
+
"""
|
|
333
|
+
Validate a mode configuration.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
config: The configuration to validate
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
List of validation errors (empty if valid)
|
|
340
|
+
"""
|
|
341
|
+
errors = []
|
|
342
|
+
|
|
343
|
+
if config.top_k < 1:
|
|
344
|
+
errors.append("top_k must be at least 1")
|
|
345
|
+
|
|
346
|
+
if not (0.0 <= config.min_confidence <= 1.0):
|
|
347
|
+
errors.append("min_confidence must be between 0.0 and 1.0")
|
|
348
|
+
|
|
349
|
+
if not (0.0 <= config.diversity_factor <= 1.0):
|
|
350
|
+
errors.append("diversity_factor must be between 0.0 and 1.0")
|
|
351
|
+
|
|
352
|
+
if config.exact_match_boost < 0:
|
|
353
|
+
errors.append("exact_match_boost must be non-negative")
|
|
354
|
+
|
|
355
|
+
if config.weights:
|
|
356
|
+
total = sum(config.weights.values())
|
|
357
|
+
if not (0.99 <= total <= 1.01):
|
|
358
|
+
errors.append(f"weights must sum to 1.0 (got {total:.2f})")
|
|
359
|
+
|
|
360
|
+
required_keys = {"similarity", "recency", "success_rate", "confidence"}
|
|
361
|
+
missing = required_keys - set(config.weights.keys())
|
|
362
|
+
if missing:
|
|
363
|
+
errors.append(f"weights missing keys: {missing}")
|
|
364
|
+
|
|
365
|
+
return errors
|