alma-memory 0.2.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 +75 -0
- alma/config/__init__.py +5 -0
- alma/config/loader.py +156 -0
- alma/core.py +322 -0
- alma/harness/__init__.py +35 -0
- alma/harness/base.py +377 -0
- alma/harness/domains.py +689 -0
- alma/integration/__init__.py +62 -0
- alma/integration/claude_agents.py +432 -0
- alma/integration/helena.py +413 -0
- alma/integration/victor.py +447 -0
- alma/learning/__init__.py +86 -0
- alma/learning/forgetting.py +1396 -0
- alma/learning/heuristic_extractor.py +374 -0
- alma/learning/protocols.py +326 -0
- alma/learning/validation.py +341 -0
- alma/mcp/__init__.py +45 -0
- alma/mcp/__main__.py +155 -0
- alma/mcp/resources.py +121 -0
- alma/mcp/server.py +533 -0
- alma/mcp/tools.py +374 -0
- alma/retrieval/__init__.py +53 -0
- alma/retrieval/cache.py +1062 -0
- alma/retrieval/embeddings.py +202 -0
- alma/retrieval/engine.py +287 -0
- alma/retrieval/scoring.py +334 -0
- alma/storage/__init__.py +20 -0
- alma/storage/azure_cosmos.py +972 -0
- alma/storage/base.py +372 -0
- alma/storage/file_based.py +583 -0
- alma/storage/sqlite_local.py +912 -0
- alma/types.py +216 -0
- alma_memory-0.2.0.dist-info/METADATA +327 -0
- alma_memory-0.2.0.dist-info/RECORD +36 -0
- alma_memory-0.2.0.dist-info/WHEEL +5 -0
- alma_memory-0.2.0.dist-info/top_level.txt +1 -0
alma/mcp/tools.py
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ALMA MCP Tool Definitions.
|
|
3
|
+
|
|
4
|
+
Provides the tool functions that can be called via MCP protocol.
|
|
5
|
+
Each tool corresponds to an ALMA operation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Dict, Any, Optional, List
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from dataclasses import asdict
|
|
13
|
+
|
|
14
|
+
from alma import ALMA
|
|
15
|
+
from alma.types import MemorySlice
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _serialize_memory_slice(memory_slice: MemorySlice) -> Dict[str, Any]:
|
|
21
|
+
"""Convert MemorySlice to JSON-serializable dict."""
|
|
22
|
+
result = {
|
|
23
|
+
"heuristics": [],
|
|
24
|
+
"outcomes": [],
|
|
25
|
+
"domain_knowledge": [],
|
|
26
|
+
"anti_patterns": [],
|
|
27
|
+
"preferences": [],
|
|
28
|
+
"query": memory_slice.query,
|
|
29
|
+
"agent": memory_slice.agent,
|
|
30
|
+
"retrieval_time_ms": memory_slice.retrieval_time_ms,
|
|
31
|
+
"total_items": memory_slice.total_items,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for h in memory_slice.heuristics:
|
|
35
|
+
result["heuristics"].append({
|
|
36
|
+
"id": h.id,
|
|
37
|
+
"condition": h.condition,
|
|
38
|
+
"strategy": h.strategy,
|
|
39
|
+
"confidence": h.confidence,
|
|
40
|
+
"occurrence_count": h.occurrence_count,
|
|
41
|
+
"success_rate": h.success_rate,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
for o in memory_slice.outcomes:
|
|
45
|
+
result["outcomes"].append({
|
|
46
|
+
"id": o.id,
|
|
47
|
+
"task_type": o.task_type,
|
|
48
|
+
"task_description": o.task_description,
|
|
49
|
+
"success": o.success,
|
|
50
|
+
"strategy_used": o.strategy_used,
|
|
51
|
+
"duration_ms": o.duration_ms,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
for dk in memory_slice.domain_knowledge:
|
|
55
|
+
result["domain_knowledge"].append({
|
|
56
|
+
"id": dk.id,
|
|
57
|
+
"domain": dk.domain,
|
|
58
|
+
"fact": dk.fact,
|
|
59
|
+
"confidence": dk.confidence,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
for ap in memory_slice.anti_patterns:
|
|
63
|
+
result["anti_patterns"].append({
|
|
64
|
+
"id": ap.id,
|
|
65
|
+
"pattern": ap.pattern,
|
|
66
|
+
"why_bad": ap.why_bad,
|
|
67
|
+
"better_alternative": ap.better_alternative,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
for p in memory_slice.preferences:
|
|
71
|
+
result["preferences"].append({
|
|
72
|
+
"id": p.id,
|
|
73
|
+
"category": p.category,
|
|
74
|
+
"preference": p.preference,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def alma_retrieve(
|
|
81
|
+
alma: ALMA,
|
|
82
|
+
task: str,
|
|
83
|
+
agent: str,
|
|
84
|
+
user_id: Optional[str] = None,
|
|
85
|
+
top_k: int = 5,
|
|
86
|
+
) -> Dict[str, Any]:
|
|
87
|
+
"""
|
|
88
|
+
Retrieve relevant memories for a task.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
alma: ALMA instance
|
|
92
|
+
task: Description of the task to perform
|
|
93
|
+
agent: Name of the agent requesting memories
|
|
94
|
+
user_id: Optional user ID for preference retrieval
|
|
95
|
+
top_k: Maximum items per memory type
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Dict containing the memory slice with relevant memories
|
|
99
|
+
"""
|
|
100
|
+
try:
|
|
101
|
+
memories = alma.retrieve(
|
|
102
|
+
task=task,
|
|
103
|
+
agent=agent,
|
|
104
|
+
user_id=user_id,
|
|
105
|
+
top_k=top_k,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
"success": True,
|
|
110
|
+
"memories": _serialize_memory_slice(memories),
|
|
111
|
+
"prompt_injection": memories.to_prompt(),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.exception(f"Error in alma_retrieve: {e}")
|
|
116
|
+
return {
|
|
117
|
+
"success": False,
|
|
118
|
+
"error": str(e),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def alma_learn(
|
|
123
|
+
alma: ALMA,
|
|
124
|
+
agent: str,
|
|
125
|
+
task: str,
|
|
126
|
+
outcome: str,
|
|
127
|
+
strategy_used: str,
|
|
128
|
+
task_type: Optional[str] = None,
|
|
129
|
+
duration_ms: Optional[int] = None,
|
|
130
|
+
error_message: Optional[str] = None,
|
|
131
|
+
feedback: Optional[str] = None,
|
|
132
|
+
) -> Dict[str, Any]:
|
|
133
|
+
"""
|
|
134
|
+
Record a task outcome for learning.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
alma: ALMA instance
|
|
138
|
+
agent: Name of the agent that executed the task
|
|
139
|
+
task: Description of the task
|
|
140
|
+
outcome: "success" or "failure"
|
|
141
|
+
strategy_used: What approach was taken
|
|
142
|
+
task_type: Category of task (for grouping)
|
|
143
|
+
duration_ms: How long the task took
|
|
144
|
+
error_message: Error details if failed
|
|
145
|
+
feedback: User feedback if provided
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Dict with learning result
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
result = alma.learn(
|
|
152
|
+
agent=agent,
|
|
153
|
+
task=task,
|
|
154
|
+
outcome=outcome,
|
|
155
|
+
strategy_used=strategy_used,
|
|
156
|
+
task_type=task_type,
|
|
157
|
+
duration_ms=duration_ms,
|
|
158
|
+
error_message=error_message,
|
|
159
|
+
feedback=feedback,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
"success": True,
|
|
164
|
+
"learned": result,
|
|
165
|
+
"message": "Outcome recorded" if result else "Learning rejected (scope violation)",
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.exception(f"Error in alma_learn: {e}")
|
|
170
|
+
return {
|
|
171
|
+
"success": False,
|
|
172
|
+
"error": str(e),
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def alma_add_preference(
|
|
177
|
+
alma: ALMA,
|
|
178
|
+
user_id: str,
|
|
179
|
+
category: str,
|
|
180
|
+
preference: str,
|
|
181
|
+
source: str = "explicit_instruction",
|
|
182
|
+
) -> Dict[str, Any]:
|
|
183
|
+
"""
|
|
184
|
+
Add a user preference to memory.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
alma: ALMA instance
|
|
188
|
+
user_id: User identifier
|
|
189
|
+
category: Category (communication, code_style, workflow)
|
|
190
|
+
preference: The preference text
|
|
191
|
+
source: How this was learned
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Dict with the created preference
|
|
195
|
+
"""
|
|
196
|
+
try:
|
|
197
|
+
pref = alma.add_user_preference(
|
|
198
|
+
user_id=user_id,
|
|
199
|
+
category=category,
|
|
200
|
+
preference=preference,
|
|
201
|
+
source=source,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
"success": True,
|
|
206
|
+
"preference": {
|
|
207
|
+
"id": pref.id,
|
|
208
|
+
"user_id": pref.user_id,
|
|
209
|
+
"category": pref.category,
|
|
210
|
+
"preference": pref.preference,
|
|
211
|
+
"source": pref.source,
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.exception(f"Error in alma_add_preference: {e}")
|
|
217
|
+
return {
|
|
218
|
+
"success": False,
|
|
219
|
+
"error": str(e),
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def alma_add_knowledge(
|
|
224
|
+
alma: ALMA,
|
|
225
|
+
agent: str,
|
|
226
|
+
domain: str,
|
|
227
|
+
fact: str,
|
|
228
|
+
source: str = "user_stated",
|
|
229
|
+
) -> Dict[str, Any]:
|
|
230
|
+
"""
|
|
231
|
+
Add domain knowledge within agent's scope.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
alma: ALMA instance
|
|
235
|
+
agent: Agent this knowledge belongs to
|
|
236
|
+
domain: Knowledge domain
|
|
237
|
+
fact: The fact to remember
|
|
238
|
+
source: How this was learned
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
Dict with the created knowledge or rejection reason
|
|
242
|
+
"""
|
|
243
|
+
try:
|
|
244
|
+
knowledge = alma.add_domain_knowledge(
|
|
245
|
+
agent=agent,
|
|
246
|
+
domain=domain,
|
|
247
|
+
fact=fact,
|
|
248
|
+
source=source,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if knowledge is None:
|
|
252
|
+
return {
|
|
253
|
+
"success": False,
|
|
254
|
+
"error": f"Agent '{agent}' not allowed to learn in domain '{domain}'",
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
"success": True,
|
|
259
|
+
"knowledge": {
|
|
260
|
+
"id": knowledge.id,
|
|
261
|
+
"agent": knowledge.agent,
|
|
262
|
+
"domain": knowledge.domain,
|
|
263
|
+
"fact": knowledge.fact,
|
|
264
|
+
"source": knowledge.source,
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
except Exception as e:
|
|
269
|
+
logger.exception(f"Error in alma_add_knowledge: {e}")
|
|
270
|
+
return {
|
|
271
|
+
"success": False,
|
|
272
|
+
"error": str(e),
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def alma_forget(
|
|
277
|
+
alma: ALMA,
|
|
278
|
+
agent: Optional[str] = None,
|
|
279
|
+
older_than_days: int = 90,
|
|
280
|
+
below_confidence: float = 0.3,
|
|
281
|
+
) -> Dict[str, Any]:
|
|
282
|
+
"""
|
|
283
|
+
Prune stale or low-confidence memories.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
alma: ALMA instance
|
|
287
|
+
agent: Specific agent to prune, or None for all
|
|
288
|
+
older_than_days: Remove outcomes older than this
|
|
289
|
+
below_confidence: Remove heuristics below this confidence
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Dict with number of items pruned
|
|
293
|
+
"""
|
|
294
|
+
try:
|
|
295
|
+
count = alma.forget(
|
|
296
|
+
agent=agent,
|
|
297
|
+
older_than_days=older_than_days,
|
|
298
|
+
below_confidence=below_confidence,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
"success": True,
|
|
303
|
+
"pruned_count": count,
|
|
304
|
+
"message": f"Pruned {count} stale or low-confidence memories",
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.exception(f"Error in alma_forget: {e}")
|
|
309
|
+
return {
|
|
310
|
+
"success": False,
|
|
311
|
+
"error": str(e),
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def alma_stats(
|
|
316
|
+
alma: ALMA,
|
|
317
|
+
agent: Optional[str] = None,
|
|
318
|
+
) -> Dict[str, Any]:
|
|
319
|
+
"""
|
|
320
|
+
Get memory statistics.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
alma: ALMA instance
|
|
324
|
+
agent: Specific agent or None for all
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
Dict with memory statistics
|
|
328
|
+
"""
|
|
329
|
+
try:
|
|
330
|
+
stats = alma.get_stats(agent=agent)
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
"success": True,
|
|
334
|
+
"stats": stats,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
except Exception as e:
|
|
338
|
+
logger.exception(f"Error in alma_stats: {e}")
|
|
339
|
+
return {
|
|
340
|
+
"success": False,
|
|
341
|
+
"error": str(e),
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def alma_health(alma: ALMA) -> Dict[str, Any]:
|
|
346
|
+
"""
|
|
347
|
+
Health check for ALMA server.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
alma: ALMA instance
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
Dict with health status
|
|
354
|
+
"""
|
|
355
|
+
try:
|
|
356
|
+
# Basic health checks
|
|
357
|
+
stats = alma.get_stats()
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
"success": True,
|
|
361
|
+
"status": "healthy",
|
|
362
|
+
"project_id": alma.project_id,
|
|
363
|
+
"total_memories": stats.get("total_count", 0),
|
|
364
|
+
"registered_agents": list(alma.scopes.keys()),
|
|
365
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
except Exception as e:
|
|
369
|
+
logger.exception(f"Error in alma_health: {e}")
|
|
370
|
+
return {
|
|
371
|
+
"success": False,
|
|
372
|
+
"status": "unhealthy",
|
|
373
|
+
"error": str(e),
|
|
374
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ALMA Retrieval Engine.
|
|
3
|
+
|
|
4
|
+
Provides semantic search, scoring, and caching for memory retrieval.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from alma.retrieval.engine import RetrievalEngine
|
|
8
|
+
from alma.retrieval.scoring import (
|
|
9
|
+
MemoryScorer,
|
|
10
|
+
ScoringWeights,
|
|
11
|
+
ScoredItem,
|
|
12
|
+
compute_composite_score,
|
|
13
|
+
)
|
|
14
|
+
from alma.retrieval.cache import (
|
|
15
|
+
CacheBackend,
|
|
16
|
+
RetrievalCache,
|
|
17
|
+
RedisCache,
|
|
18
|
+
NullCache,
|
|
19
|
+
CacheEntry,
|
|
20
|
+
CacheStats,
|
|
21
|
+
PerformanceMetrics,
|
|
22
|
+
create_cache,
|
|
23
|
+
)
|
|
24
|
+
from alma.retrieval.embeddings import (
|
|
25
|
+
EmbeddingProvider,
|
|
26
|
+
LocalEmbedder,
|
|
27
|
+
AzureEmbedder,
|
|
28
|
+
MockEmbedder,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
# Engine
|
|
33
|
+
"RetrievalEngine",
|
|
34
|
+
# Scoring
|
|
35
|
+
"MemoryScorer",
|
|
36
|
+
"ScoringWeights",
|
|
37
|
+
"ScoredItem",
|
|
38
|
+
"compute_composite_score",
|
|
39
|
+
# Cache
|
|
40
|
+
"CacheBackend",
|
|
41
|
+
"RetrievalCache",
|
|
42
|
+
"RedisCache",
|
|
43
|
+
"NullCache",
|
|
44
|
+
"CacheEntry",
|
|
45
|
+
"CacheStats",
|
|
46
|
+
"PerformanceMetrics",
|
|
47
|
+
"create_cache",
|
|
48
|
+
# Embeddings
|
|
49
|
+
"EmbeddingProvider",
|
|
50
|
+
"LocalEmbedder",
|
|
51
|
+
"AzureEmbedder",
|
|
52
|
+
"MockEmbedder",
|
|
53
|
+
]
|