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/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
+ ]