alma-memory 0.4.0__py3-none-any.whl → 0.5.1__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 +121 -45
- alma/confidence/__init__.py +1 -1
- alma/confidence/engine.py +92 -58
- alma/confidence/types.py +34 -14
- alma/config/loader.py +3 -2
- alma/consolidation/__init__.py +23 -0
- alma/consolidation/engine.py +678 -0
- alma/consolidation/prompts.py +84 -0
- alma/core.py +136 -28
- alma/domains/__init__.py +6 -6
- alma/domains/factory.py +12 -9
- alma/domains/schemas.py +17 -3
- alma/domains/types.py +8 -4
- alma/events/__init__.py +75 -0
- alma/events/emitter.py +284 -0
- alma/events/storage_mixin.py +246 -0
- alma/events/types.py +126 -0
- alma/events/webhook.py +425 -0
- alma/exceptions.py +49 -0
- alma/extraction/__init__.py +31 -0
- alma/extraction/auto_learner.py +265 -0
- alma/extraction/extractor.py +420 -0
- alma/graph/__init__.py +106 -0
- alma/graph/backends/__init__.py +32 -0
- alma/graph/backends/kuzu.py +624 -0
- alma/graph/backends/memgraph.py +432 -0
- alma/graph/backends/memory.py +236 -0
- alma/graph/backends/neo4j.py +417 -0
- alma/graph/base.py +159 -0
- alma/graph/extraction.py +198 -0
- alma/graph/store.py +860 -0
- alma/harness/__init__.py +4 -4
- alma/harness/base.py +18 -9
- alma/harness/domains.py +27 -11
- alma/initializer/__init__.py +1 -1
- alma/initializer/initializer.py +51 -43
- alma/initializer/types.py +25 -17
- alma/integration/__init__.py +9 -9
- alma/integration/claude_agents.py +32 -20
- alma/integration/helena.py +32 -22
- alma/integration/victor.py +57 -33
- alma/learning/__init__.py +27 -27
- alma/learning/forgetting.py +198 -148
- alma/learning/heuristic_extractor.py +40 -24
- alma/learning/protocols.py +65 -17
- alma/learning/validation.py +7 -2
- alma/mcp/__init__.py +4 -4
- alma/mcp/__main__.py +2 -1
- alma/mcp/resources.py +17 -16
- alma/mcp/server.py +102 -44
- alma/mcp/tools.py +180 -45
- alma/observability/__init__.py +84 -0
- alma/observability/config.py +302 -0
- alma/observability/logging.py +424 -0
- alma/observability/metrics.py +583 -0
- alma/observability/tracing.py +440 -0
- alma/progress/__init__.py +3 -3
- alma/progress/tracker.py +26 -20
- alma/progress/types.py +8 -12
- alma/py.typed +0 -0
- alma/retrieval/__init__.py +11 -11
- alma/retrieval/cache.py +20 -21
- alma/retrieval/embeddings.py +4 -4
- alma/retrieval/engine.py +179 -39
- alma/retrieval/scoring.py +73 -63
- alma/session/__init__.py +2 -2
- alma/session/manager.py +5 -5
- alma/session/types.py +5 -4
- alma/storage/__init__.py +70 -0
- alma/storage/azure_cosmos.py +414 -133
- alma/storage/base.py +215 -4
- alma/storage/chroma.py +1443 -0
- alma/storage/constants.py +103 -0
- alma/storage/file_based.py +59 -28
- alma/storage/migrations/__init__.py +21 -0
- alma/storage/migrations/base.py +321 -0
- alma/storage/migrations/runner.py +323 -0
- alma/storage/migrations/version_stores.py +337 -0
- alma/storage/migrations/versions/__init__.py +11 -0
- alma/storage/migrations/versions/v1_0_0.py +373 -0
- alma/storage/pinecone.py +1080 -0
- alma/storage/postgresql.py +1559 -0
- alma/storage/qdrant.py +1306 -0
- alma/storage/sqlite_local.py +504 -60
- alma/testing/__init__.py +46 -0
- alma/testing/factories.py +301 -0
- alma/testing/mocks.py +389 -0
- alma/types.py +62 -14
- alma_memory-0.5.1.dist-info/METADATA +939 -0
- alma_memory-0.5.1.dist-info/RECORD +93 -0
- {alma_memory-0.4.0.dist-info → alma_memory-0.5.1.dist-info}/WHEEL +1 -1
- alma_memory-0.4.0.dist-info/METADATA +0 -488
- alma_memory-0.4.0.dist-info/RECORD +0 -52
- {alma_memory-0.4.0.dist-info → alma_memory-0.5.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ALMA Consolidation Prompts.
|
|
3
|
+
|
|
4
|
+
LLM prompts for intelligently merging similar memories.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Prompt for merging multiple similar heuristics into one
|
|
8
|
+
MERGE_HEURISTICS_PROMPT = """You are a memory consolidation agent. Given these similar heuristics that have been identified as near-duplicates based on semantic similarity, create a single consolidated heuristic that captures the essence of all.
|
|
9
|
+
|
|
10
|
+
Similar Heuristics:
|
|
11
|
+
{heuristics}
|
|
12
|
+
|
|
13
|
+
Create a consolidated heuristic that:
|
|
14
|
+
1. Generalizes the condition to cover all cases
|
|
15
|
+
2. Combines the strategies into a comprehensive approach
|
|
16
|
+
3. Preserves any unique insights from individual heuristics
|
|
17
|
+
|
|
18
|
+
Output a JSON object with exactly these fields:
|
|
19
|
+
{{
|
|
20
|
+
"condition": "The generalized condition that triggers this heuristic",
|
|
21
|
+
"strategy": "The merged strategy combining all approaches",
|
|
22
|
+
"confidence": <average confidence as a float between 0 and 1>
|
|
23
|
+
}}
|
|
24
|
+
|
|
25
|
+
Only output the JSON object, no other text."""
|
|
26
|
+
|
|
27
|
+
# Prompt for merging similar domain knowledge
|
|
28
|
+
MERGE_DOMAIN_KNOWLEDGE_PROMPT = """You are a memory consolidation agent. Given these similar domain knowledge facts that have been identified as near-duplicates, create a single consolidated fact that captures all the information.
|
|
29
|
+
|
|
30
|
+
Similar Domain Knowledge:
|
|
31
|
+
{knowledge_items}
|
|
32
|
+
|
|
33
|
+
Create a consolidated fact that:
|
|
34
|
+
1. Combines all unique information
|
|
35
|
+
2. Removes redundancy
|
|
36
|
+
3. Maintains accuracy
|
|
37
|
+
|
|
38
|
+
Output a JSON object with exactly these fields:
|
|
39
|
+
{{
|
|
40
|
+
"fact": "The consolidated fact combining all information",
|
|
41
|
+
"confidence": <average confidence as a float between 0 and 1>
|
|
42
|
+
}}
|
|
43
|
+
|
|
44
|
+
Only output the JSON object, no other text."""
|
|
45
|
+
|
|
46
|
+
# Prompt for merging anti-patterns
|
|
47
|
+
MERGE_ANTI_PATTERNS_PROMPT = """You are a memory consolidation agent. Given these similar anti-patterns that have been identified as near-duplicates, create a single consolidated anti-pattern.
|
|
48
|
+
|
|
49
|
+
Similar Anti-Patterns:
|
|
50
|
+
{anti_patterns}
|
|
51
|
+
|
|
52
|
+
Create a consolidated anti-pattern that:
|
|
53
|
+
1. Generalizes the pattern description
|
|
54
|
+
2. Combines all reasons why it's bad
|
|
55
|
+
3. Provides a comprehensive alternative
|
|
56
|
+
|
|
57
|
+
Output a JSON object with exactly these fields:
|
|
58
|
+
{{
|
|
59
|
+
"pattern": "The generalized pattern to avoid",
|
|
60
|
+
"why_bad": "Combined explanation of why this pattern is problematic",
|
|
61
|
+
"better_alternative": "The recommended alternative approach"
|
|
62
|
+
}}
|
|
63
|
+
|
|
64
|
+
Only output the JSON object, no other text."""
|
|
65
|
+
|
|
66
|
+
# Prompt for merging outcomes (typically used for summarization rather than true merge)
|
|
67
|
+
MERGE_OUTCOMES_PROMPT = """You are a memory consolidation agent. Given these similar task outcomes, create a summary that captures the key learnings.
|
|
68
|
+
|
|
69
|
+
Similar Outcomes:
|
|
70
|
+
{outcomes}
|
|
71
|
+
|
|
72
|
+
Create a summary that:
|
|
73
|
+
1. Identifies the common task type
|
|
74
|
+
2. Notes the overall success/failure pattern
|
|
75
|
+
3. Highlights effective strategies
|
|
76
|
+
|
|
77
|
+
Output a JSON object with exactly these fields:
|
|
78
|
+
{{
|
|
79
|
+
"task_type": "The common task type",
|
|
80
|
+
"summary": "Summary of the outcomes and learnings",
|
|
81
|
+
"recommended_strategy": "The most effective strategy based on the outcomes"
|
|
82
|
+
}}
|
|
83
|
+
|
|
84
|
+
Only output the JSON object, no other text."""
|
alma/core.py
CHANGED
|
@@ -1,27 +1,38 @@
|
|
|
1
1
|
"""
|
|
2
2
|
ALMA Core - Main interface for the Agent Learning Memory Architecture.
|
|
3
|
+
|
|
4
|
+
API Return Type Conventions:
|
|
5
|
+
- Create operations: Return created object or raise exception
|
|
6
|
+
- Update operations: Return updated object or raise exception
|
|
7
|
+
- Delete operations: Return bool (success) or int (count), raise on failure
|
|
8
|
+
- Query operations: Return list (empty if none) or object
|
|
9
|
+
|
|
10
|
+
All scope violations raise ScopeViolationError for consistent error handling.
|
|
3
11
|
"""
|
|
4
12
|
|
|
5
|
-
from typing import Optional, Dict, Any, List
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
import yaml
|
|
8
13
|
import logging
|
|
14
|
+
import time
|
|
15
|
+
from typing import Any, Dict, Optional
|
|
9
16
|
|
|
17
|
+
from alma.config.loader import ConfigLoader
|
|
18
|
+
from alma.exceptions import ScopeViolationError
|
|
19
|
+
from alma.learning.protocols import LearningProtocol
|
|
20
|
+
from alma.observability.logging import get_logger
|
|
21
|
+
from alma.observability.metrics import get_metrics
|
|
22
|
+
from alma.observability.tracing import SpanKind, get_tracer, trace_method
|
|
23
|
+
from alma.retrieval.engine import RetrievalEngine
|
|
24
|
+
from alma.storage.base import StorageBackend
|
|
10
25
|
from alma.types import (
|
|
11
|
-
|
|
26
|
+
DomainKnowledge,
|
|
12
27
|
MemoryScope,
|
|
13
|
-
|
|
28
|
+
MemorySlice,
|
|
14
29
|
Outcome,
|
|
15
30
|
UserPreference,
|
|
16
|
-
DomainKnowledge,
|
|
17
|
-
AntiPattern,
|
|
18
31
|
)
|
|
19
|
-
from alma.storage.base import StorageBackend
|
|
20
|
-
from alma.retrieval.engine import RetrievalEngine
|
|
21
|
-
from alma.learning.protocols import LearningProtocol
|
|
22
|
-
from alma.config.loader import ConfigLoader
|
|
23
32
|
|
|
24
33
|
logger = logging.getLogger(__name__)
|
|
34
|
+
structured_logger = get_logger(__name__)
|
|
35
|
+
tracer = get_tracer(__name__)
|
|
25
36
|
|
|
26
37
|
|
|
27
38
|
class ALMA:
|
|
@@ -114,14 +125,22 @@ class ALMA:
|
|
|
114
125
|
|
|
115
126
|
if storage_type == "azure":
|
|
116
127
|
from alma.storage.azure_cosmos import AzureCosmosStorage
|
|
128
|
+
|
|
117
129
|
return AzureCosmosStorage.from_config(config)
|
|
130
|
+
elif storage_type == "postgres":
|
|
131
|
+
from alma.storage.postgresql import PostgreSQLStorage
|
|
132
|
+
|
|
133
|
+
return PostgreSQLStorage.from_config(config)
|
|
118
134
|
elif storage_type == "sqlite":
|
|
119
135
|
from alma.storage.sqlite_local import SQLiteStorage
|
|
136
|
+
|
|
120
137
|
return SQLiteStorage.from_config(config)
|
|
121
138
|
else:
|
|
122
139
|
from alma.storage.file_based import FileBasedStorage
|
|
140
|
+
|
|
123
141
|
return FileBasedStorage.from_config(config)
|
|
124
142
|
|
|
143
|
+
@trace_method(name="ALMA.retrieve", kind=SpanKind.INTERNAL)
|
|
125
144
|
def retrieve(
|
|
126
145
|
self,
|
|
127
146
|
task: str,
|
|
@@ -141,11 +160,19 @@ class ALMA:
|
|
|
141
160
|
Returns:
|
|
142
161
|
MemorySlice with relevant memories for context injection
|
|
143
162
|
"""
|
|
163
|
+
start_time = time.time()
|
|
164
|
+
metrics = get_metrics()
|
|
165
|
+
|
|
144
166
|
# Validate agent has a defined scope
|
|
145
167
|
if agent not in self.scopes:
|
|
146
168
|
logger.warning(f"Agent '{agent}' has no defined scope, using defaults")
|
|
169
|
+
structured_logger.warning(
|
|
170
|
+
"Agent has no defined scope, using defaults",
|
|
171
|
+
agent=agent,
|
|
172
|
+
project_id=self.project_id,
|
|
173
|
+
)
|
|
147
174
|
|
|
148
|
-
|
|
175
|
+
result = self.retrieval.retrieve(
|
|
149
176
|
query=task,
|
|
150
177
|
agent=agent,
|
|
151
178
|
project_id=self.project_id,
|
|
@@ -154,6 +181,30 @@ class ALMA:
|
|
|
154
181
|
scope=self.scopes.get(agent),
|
|
155
182
|
)
|
|
156
183
|
|
|
184
|
+
# Record metrics
|
|
185
|
+
duration_ms = (time.time() - start_time) * 1000
|
|
186
|
+
cache_hit = result.retrieval_time_ms < 10 # Approximate cache hit detection
|
|
187
|
+
metrics.record_retrieve_latency(
|
|
188
|
+
duration_ms=duration_ms,
|
|
189
|
+
agent=agent,
|
|
190
|
+
project_id=self.project_id,
|
|
191
|
+
cache_hit=cache_hit,
|
|
192
|
+
items_returned=result.total_items,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
structured_logger.info(
|
|
196
|
+
"Memory retrieval completed",
|
|
197
|
+
agent=agent,
|
|
198
|
+
project_id=self.project_id,
|
|
199
|
+
task_preview=task[:50] if task else "",
|
|
200
|
+
items_returned=result.total_items,
|
|
201
|
+
duration_ms=duration_ms,
|
|
202
|
+
cache_hit=cache_hit,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
return result
|
|
206
|
+
|
|
207
|
+
@trace_method(name="ALMA.learn", kind=SpanKind.INTERNAL)
|
|
157
208
|
def learn(
|
|
158
209
|
self,
|
|
159
210
|
agent: str,
|
|
@@ -164,7 +215,7 @@ class ALMA:
|
|
|
164
215
|
duration_ms: Optional[int] = None,
|
|
165
216
|
error_message: Optional[str] = None,
|
|
166
217
|
feedback: Optional[str] = None,
|
|
167
|
-
) ->
|
|
218
|
+
) -> Outcome:
|
|
168
219
|
"""
|
|
169
220
|
Learn from a task outcome.
|
|
170
221
|
|
|
@@ -182,9 +233,15 @@ class ALMA:
|
|
|
182
233
|
feedback: User feedback if provided
|
|
183
234
|
|
|
184
235
|
Returns:
|
|
185
|
-
|
|
236
|
+
The created Outcome record
|
|
237
|
+
|
|
238
|
+
Raises:
|
|
239
|
+
ScopeViolationError: If learning is outside agent's scope
|
|
186
240
|
"""
|
|
187
|
-
|
|
241
|
+
start_time = time.time()
|
|
242
|
+
metrics = get_metrics()
|
|
243
|
+
|
|
244
|
+
outcome_record = self.learning.learn(
|
|
188
245
|
agent=agent,
|
|
189
246
|
project_id=self.project_id,
|
|
190
247
|
task=task,
|
|
@@ -197,10 +254,28 @@ class ALMA:
|
|
|
197
254
|
)
|
|
198
255
|
|
|
199
256
|
# Invalidate cache for this agent/project after learning
|
|
200
|
-
|
|
201
|
-
self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
|
|
257
|
+
self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
|
|
202
258
|
|
|
203
|
-
|
|
259
|
+
# Record metrics
|
|
260
|
+
learn_duration_ms = (time.time() - start_time) * 1000
|
|
261
|
+
metrics.record_learn_operation(
|
|
262
|
+
duration_ms=learn_duration_ms,
|
|
263
|
+
agent=agent,
|
|
264
|
+
project_id=self.project_id,
|
|
265
|
+
memory_type="outcome",
|
|
266
|
+
success=True,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
structured_logger.info(
|
|
270
|
+
"Learning operation completed",
|
|
271
|
+
agent=agent,
|
|
272
|
+
project_id=self.project_id,
|
|
273
|
+
task_type=task_type,
|
|
274
|
+
outcome=outcome,
|
|
275
|
+
duration_ms=learn_duration_ms,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return outcome_record
|
|
204
279
|
|
|
205
280
|
def add_user_preference(
|
|
206
281
|
self,
|
|
@@ -239,7 +314,7 @@ class ALMA:
|
|
|
239
314
|
domain: str,
|
|
240
315
|
fact: str,
|
|
241
316
|
source: str = "user_stated",
|
|
242
|
-
) ->
|
|
317
|
+
) -> DomainKnowledge:
|
|
243
318
|
"""
|
|
244
319
|
Add domain knowledge within agent's scope.
|
|
245
320
|
|
|
@@ -250,15 +325,17 @@ class ALMA:
|
|
|
250
325
|
source: How this was learned
|
|
251
326
|
|
|
252
327
|
Returns:
|
|
253
|
-
The created DomainKnowledge
|
|
328
|
+
The created DomainKnowledge
|
|
329
|
+
|
|
330
|
+
Raises:
|
|
331
|
+
ScopeViolationError: If agent is not allowed to learn in this domain
|
|
254
332
|
"""
|
|
255
333
|
# Check scope
|
|
256
334
|
scope = self.scopes.get(agent)
|
|
257
335
|
if scope and not scope.is_allowed(domain):
|
|
258
|
-
|
|
259
|
-
f"Agent '{agent}' not allowed to learn in domain '{domain}'"
|
|
336
|
+
raise ScopeViolationError(
|
|
337
|
+
f"Agent '{agent}' is not allowed to learn in domain '{domain}'"
|
|
260
338
|
)
|
|
261
|
-
return None
|
|
262
339
|
|
|
263
340
|
result = self.learning.add_domain_knowledge(
|
|
264
341
|
agent=agent,
|
|
@@ -269,11 +346,11 @@ class ALMA:
|
|
|
269
346
|
)
|
|
270
347
|
|
|
271
348
|
# Invalidate cache for this agent/project after adding knowledge
|
|
272
|
-
|
|
273
|
-
self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
|
|
349
|
+
self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
|
|
274
350
|
|
|
275
351
|
return result
|
|
276
352
|
|
|
353
|
+
@trace_method(name="ALMA.forget", kind=SpanKind.INTERNAL)
|
|
277
354
|
def forget(
|
|
278
355
|
self,
|
|
279
356
|
agent: Optional[str] = None,
|
|
@@ -283,7 +360,8 @@ class ALMA:
|
|
|
283
360
|
"""
|
|
284
361
|
Prune stale or low-confidence memories.
|
|
285
362
|
|
|
286
|
-
|
|
363
|
+
This is a delete operation that invalidates cache after pruning
|
|
364
|
+
to ensure fresh retrieval results.
|
|
287
365
|
|
|
288
366
|
Args:
|
|
289
367
|
agent: Specific agent to prune, or None for all
|
|
@@ -291,8 +369,14 @@ class ALMA:
|
|
|
291
369
|
below_confidence: Remove heuristics below this confidence
|
|
292
370
|
|
|
293
371
|
Returns:
|
|
294
|
-
Number of items pruned
|
|
372
|
+
Number of items pruned (0 if nothing was pruned)
|
|
373
|
+
|
|
374
|
+
Raises:
|
|
375
|
+
StorageError: If the delete operation fails
|
|
295
376
|
"""
|
|
377
|
+
start_time = time.time()
|
|
378
|
+
metrics = get_metrics()
|
|
379
|
+
|
|
296
380
|
count = self.learning.forget(
|
|
297
381
|
project_id=self.project_id,
|
|
298
382
|
agent=agent,
|
|
@@ -304,17 +388,41 @@ class ALMA:
|
|
|
304
388
|
if count > 0:
|
|
305
389
|
self.retrieval.invalidate_cache(agent=agent, project_id=self.project_id)
|
|
306
390
|
|
|
391
|
+
# Record metrics
|
|
392
|
+
duration_ms = (time.time() - start_time) * 1000
|
|
393
|
+
metrics.record_forget_operation(
|
|
394
|
+
duration_ms=duration_ms,
|
|
395
|
+
agent=agent,
|
|
396
|
+
project_id=self.project_id,
|
|
397
|
+
items_removed=count,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
structured_logger.info(
|
|
401
|
+
"Forget operation completed",
|
|
402
|
+
agent=agent or "all",
|
|
403
|
+
project_id=self.project_id,
|
|
404
|
+
items_removed=count,
|
|
405
|
+
older_than_days=older_than_days,
|
|
406
|
+
below_confidence=below_confidence,
|
|
407
|
+
duration_ms=duration_ms,
|
|
408
|
+
)
|
|
409
|
+
|
|
307
410
|
return count
|
|
308
411
|
|
|
309
412
|
def get_stats(self, agent: Optional[str] = None) -> Dict[str, Any]:
|
|
310
413
|
"""
|
|
311
414
|
Get memory statistics.
|
|
312
415
|
|
|
416
|
+
This is a query operation that returns statistics about stored memories.
|
|
417
|
+
|
|
313
418
|
Args:
|
|
314
419
|
agent: Specific agent or None for all
|
|
315
420
|
|
|
316
421
|
Returns:
|
|
317
|
-
Dict with counts and metadata
|
|
422
|
+
Dict with counts and metadata (always returns a dict, may be empty)
|
|
423
|
+
|
|
424
|
+
Raises:
|
|
425
|
+
StorageError: If the query operation fails
|
|
318
426
|
"""
|
|
319
427
|
return self.storage.get_stats(
|
|
320
428
|
project_id=self.project_id,
|
alma/domains/__init__.py
CHANGED
|
@@ -5,17 +5,17 @@ Provides domain-agnostic memory schemas and factory pattern
|
|
|
5
5
|
for creating domain-specific ALMA instances.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from alma.domains.types import (
|
|
9
|
-
DomainSchema,
|
|
10
|
-
EntityType,
|
|
11
|
-
RelationshipType,
|
|
12
|
-
)
|
|
13
8
|
from alma.domains.factory import DomainMemoryFactory
|
|
14
9
|
from alma.domains.schemas import (
|
|
15
10
|
get_coding_schema,
|
|
11
|
+
get_general_schema,
|
|
16
12
|
get_research_schema,
|
|
17
13
|
get_sales_schema,
|
|
18
|
-
|
|
14
|
+
)
|
|
15
|
+
from alma.domains.types import (
|
|
16
|
+
DomainSchema,
|
|
17
|
+
EntityType,
|
|
18
|
+
RelationshipType,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
__all__ = [
|
alma/domains/factory.py
CHANGED
|
@@ -4,18 +4,18 @@ Domain Memory Factory.
|
|
|
4
4
|
Factory pattern for creating domain-specific ALMA instances.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Dict, Any, Optional, List, Type
|
|
8
7
|
import logging
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
9
|
|
|
10
|
-
from alma.domains.types import DomainSchema, EntityType, RelationshipType
|
|
11
10
|
from alma.domains.schemas import (
|
|
12
11
|
get_coding_schema,
|
|
12
|
+
get_content_creation_schema,
|
|
13
|
+
get_customer_support_schema,
|
|
14
|
+
get_general_schema,
|
|
13
15
|
get_research_schema,
|
|
14
16
|
get_sales_schema,
|
|
15
|
-
get_general_schema,
|
|
16
|
-
get_customer_support_schema,
|
|
17
|
-
get_content_creation_schema,
|
|
18
17
|
)
|
|
18
|
+
from alma.domains.types import DomainSchema
|
|
19
19
|
|
|
20
20
|
logger = logging.getLogger(__name__)
|
|
21
21
|
|
|
@@ -124,7 +124,9 @@ class DomainMemoryFactory:
|
|
|
124
124
|
description=config.get("description", f"Custom schema: {name}"),
|
|
125
125
|
learning_categories=config.get("learning_categories", []),
|
|
126
126
|
excluded_categories=config.get("excluded_categories", []),
|
|
127
|
-
min_occurrences_for_heuristic=config.get(
|
|
127
|
+
min_occurrences_for_heuristic=config.get(
|
|
128
|
+
"min_occurrences_for_heuristic", 3
|
|
129
|
+
),
|
|
128
130
|
confidence_decay_days=config.get("confidence_decay_days", 30.0),
|
|
129
131
|
)
|
|
130
132
|
|
|
@@ -200,16 +202,17 @@ class DomainMemoryFactory:
|
|
|
200
202
|
- Initialize domain-specific entity tracking
|
|
201
203
|
"""
|
|
202
204
|
# Import here to avoid circular dependency
|
|
203
|
-
from alma
|
|
204
|
-
from alma.retrieval import RetrievalEngine
|
|
205
|
+
from alma import ALMA
|
|
205
206
|
from alma.learning import LearningProtocol
|
|
207
|
+
from alma.retrieval import RetrievalEngine
|
|
208
|
+
from alma.storage.file_based import FileBasedStorage
|
|
206
209
|
from alma.types import MemoryScope
|
|
207
|
-
from alma import ALMA
|
|
208
210
|
|
|
209
211
|
# Create storage if not provided
|
|
210
212
|
if storage is None:
|
|
211
213
|
import tempfile
|
|
212
214
|
from pathlib import Path
|
|
215
|
+
|
|
213
216
|
storage_dir = Path(tempfile.mkdtemp()) / ".alma" / project_id
|
|
214
217
|
storage = FileBasedStorage(storage_dir)
|
|
215
218
|
|
alma/domains/schemas.py
CHANGED
|
@@ -4,7 +4,7 @@ Pre-built Domain Schemas.
|
|
|
4
4
|
Standard domain schemas for common use cases.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from alma.domains.types import DomainSchema
|
|
7
|
+
from alma.domains.types import DomainSchema
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def get_coding_schema() -> DomainSchema:
|
|
@@ -110,12 +110,26 @@ def get_research_schema() -> DomainSchema:
|
|
|
110
110
|
schema.add_entity_type(
|
|
111
111
|
name="paper",
|
|
112
112
|
description="An academic paper or article",
|
|
113
|
-
attributes=[
|
|
113
|
+
attributes=[
|
|
114
|
+
"title",
|
|
115
|
+
"authors",
|
|
116
|
+
"year",
|
|
117
|
+
"citations",
|
|
118
|
+
"abstract",
|
|
119
|
+
"venue",
|
|
120
|
+
"doi",
|
|
121
|
+
],
|
|
114
122
|
)
|
|
115
123
|
schema.add_entity_type(
|
|
116
124
|
name="hypothesis",
|
|
117
125
|
description="A research hypothesis",
|
|
118
|
-
attributes=[
|
|
126
|
+
attributes=[
|
|
127
|
+
"statement",
|
|
128
|
+
"confidence",
|
|
129
|
+
"evidence_for",
|
|
130
|
+
"evidence_against",
|
|
131
|
+
"status",
|
|
132
|
+
],
|
|
119
133
|
)
|
|
120
134
|
schema.add_entity_type(
|
|
121
135
|
name="experiment",
|
alma/domains/types.py
CHANGED
|
@@ -4,10 +4,10 @@ Domain Memory Types.
|
|
|
4
4
|
Data models for domain-specific memory schemas.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import uuid
|
|
7
8
|
from dataclasses import dataclass, field
|
|
8
9
|
from datetime import datetime, timezone
|
|
9
|
-
from typing import
|
|
10
|
-
import uuid
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@dataclass
|
|
@@ -21,11 +21,15 @@ class EntityType:
|
|
|
21
21
|
|
|
22
22
|
name: str # "feature", "test", "paper", "lead"
|
|
23
23
|
description: str
|
|
24
|
-
attributes: List[str] = field(
|
|
24
|
+
attributes: List[str] = field(
|
|
25
|
+
default_factory=list
|
|
26
|
+
) # ["status", "priority", "owner"]
|
|
25
27
|
|
|
26
28
|
# Optional schema validation
|
|
27
29
|
required_attributes: List[str] = field(default_factory=list)
|
|
28
|
-
attribute_types: Dict[str, str] = field(
|
|
30
|
+
attribute_types: Dict[str, str] = field(
|
|
31
|
+
default_factory=dict
|
|
32
|
+
) # attr -> "str", "int", "bool"
|
|
29
33
|
|
|
30
34
|
def validate_entity(self, entity: Dict[str, Any]) -> List[str]:
|
|
31
35
|
"""Validate an entity instance against this type."""
|
alma/events/__init__.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ALMA Event System.
|
|
3
|
+
|
|
4
|
+
Provides event emission and webhook delivery for memory operations.
|
|
5
|
+
|
|
6
|
+
The event system allows external systems to react to memory changes through:
|
|
7
|
+
1. In-process callbacks (subscribe to event types)
|
|
8
|
+
2. Webhooks (HTTP delivery with signatures)
|
|
9
|
+
|
|
10
|
+
Example - In-process subscription:
|
|
11
|
+
```python
|
|
12
|
+
from alma.events import get_emitter, MemoryEventType
|
|
13
|
+
|
|
14
|
+
def on_memory_created(event):
|
|
15
|
+
print(f"Memory created: {event.memory_id}")
|
|
16
|
+
|
|
17
|
+
emitter = get_emitter()
|
|
18
|
+
emitter.subscribe(MemoryEventType.CREATED, on_memory_created)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Example - Webhook delivery:
|
|
22
|
+
```python
|
|
23
|
+
from alma.events import WebhookConfig, WebhookManager, get_emitter
|
|
24
|
+
|
|
25
|
+
manager = WebhookManager()
|
|
26
|
+
manager.add_webhook(WebhookConfig(
|
|
27
|
+
url="https://example.com/webhook",
|
|
28
|
+
events=[MemoryEventType.CREATED, MemoryEventType.UPDATED],
|
|
29
|
+
secret="my-webhook-secret"
|
|
30
|
+
))
|
|
31
|
+
manager.start(get_emitter())
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from alma.events.emitter import (
|
|
36
|
+
EventEmitter,
|
|
37
|
+
get_emitter,
|
|
38
|
+
reset_emitter,
|
|
39
|
+
)
|
|
40
|
+
from alma.events.storage_mixin import (
|
|
41
|
+
EventAwareStorageMixin,
|
|
42
|
+
emit_on_save,
|
|
43
|
+
)
|
|
44
|
+
from alma.events.types import (
|
|
45
|
+
MemoryEvent,
|
|
46
|
+
MemoryEventType,
|
|
47
|
+
create_memory_event,
|
|
48
|
+
)
|
|
49
|
+
from alma.events.webhook import (
|
|
50
|
+
WebhookConfig,
|
|
51
|
+
WebhookDelivery,
|
|
52
|
+
WebhookDeliveryResult,
|
|
53
|
+
WebhookDeliveryStatus,
|
|
54
|
+
WebhookManager,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
# Types
|
|
59
|
+
"MemoryEvent",
|
|
60
|
+
"MemoryEventType",
|
|
61
|
+
"create_memory_event",
|
|
62
|
+
# Emitter
|
|
63
|
+
"EventEmitter",
|
|
64
|
+
"get_emitter",
|
|
65
|
+
"reset_emitter",
|
|
66
|
+
# Webhook
|
|
67
|
+
"WebhookConfig",
|
|
68
|
+
"WebhookDelivery",
|
|
69
|
+
"WebhookDeliveryResult",
|
|
70
|
+
"WebhookDeliveryStatus",
|
|
71
|
+
"WebhookManager",
|
|
72
|
+
# Storage Mixin
|
|
73
|
+
"EventAwareStorageMixin",
|
|
74
|
+
"emit_on_save",
|
|
75
|
+
]
|