alma-memory 0.4.0__py3-none-any.whl → 0.5.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.
Files changed (77) hide show
  1. alma/__init__.py +88 -44
  2. alma/confidence/__init__.py +1 -1
  3. alma/confidence/engine.py +92 -58
  4. alma/confidence/types.py +34 -14
  5. alma/config/loader.py +3 -2
  6. alma/consolidation/__init__.py +23 -0
  7. alma/consolidation/engine.py +678 -0
  8. alma/consolidation/prompts.py +84 -0
  9. alma/core.py +15 -15
  10. alma/domains/__init__.py +6 -6
  11. alma/domains/factory.py +12 -9
  12. alma/domains/schemas.py +17 -3
  13. alma/domains/types.py +8 -4
  14. alma/events/__init__.py +75 -0
  15. alma/events/emitter.py +284 -0
  16. alma/events/storage_mixin.py +246 -0
  17. alma/events/types.py +126 -0
  18. alma/events/webhook.py +425 -0
  19. alma/exceptions.py +49 -0
  20. alma/extraction/__init__.py +31 -0
  21. alma/extraction/auto_learner.py +264 -0
  22. alma/extraction/extractor.py +420 -0
  23. alma/graph/__init__.py +81 -0
  24. alma/graph/backends/__init__.py +18 -0
  25. alma/graph/backends/memory.py +236 -0
  26. alma/graph/backends/neo4j.py +417 -0
  27. alma/graph/base.py +159 -0
  28. alma/graph/extraction.py +198 -0
  29. alma/graph/store.py +860 -0
  30. alma/harness/__init__.py +4 -4
  31. alma/harness/base.py +18 -9
  32. alma/harness/domains.py +27 -11
  33. alma/initializer/__init__.py +1 -1
  34. alma/initializer/initializer.py +51 -43
  35. alma/initializer/types.py +25 -17
  36. alma/integration/__init__.py +9 -9
  37. alma/integration/claude_agents.py +10 -10
  38. alma/integration/helena.py +32 -22
  39. alma/integration/victor.py +57 -33
  40. alma/learning/__init__.py +27 -27
  41. alma/learning/forgetting.py +198 -148
  42. alma/learning/heuristic_extractor.py +40 -24
  43. alma/learning/protocols.py +62 -14
  44. alma/learning/validation.py +7 -2
  45. alma/mcp/__init__.py +4 -4
  46. alma/mcp/__main__.py +2 -1
  47. alma/mcp/resources.py +17 -16
  48. alma/mcp/server.py +102 -44
  49. alma/mcp/tools.py +174 -37
  50. alma/progress/__init__.py +3 -3
  51. alma/progress/tracker.py +26 -20
  52. alma/progress/types.py +8 -12
  53. alma/py.typed +0 -0
  54. alma/retrieval/__init__.py +11 -11
  55. alma/retrieval/cache.py +20 -21
  56. alma/retrieval/embeddings.py +4 -4
  57. alma/retrieval/engine.py +114 -35
  58. alma/retrieval/scoring.py +73 -63
  59. alma/session/__init__.py +2 -2
  60. alma/session/manager.py +5 -5
  61. alma/session/types.py +5 -4
  62. alma/storage/__init__.py +41 -0
  63. alma/storage/azure_cosmos.py +101 -31
  64. alma/storage/base.py +157 -4
  65. alma/storage/chroma.py +1443 -0
  66. alma/storage/file_based.py +56 -20
  67. alma/storage/pinecone.py +1080 -0
  68. alma/storage/postgresql.py +1452 -0
  69. alma/storage/qdrant.py +1306 -0
  70. alma/storage/sqlite_local.py +376 -31
  71. alma/types.py +62 -14
  72. alma_memory-0.5.0.dist-info/METADATA +905 -0
  73. alma_memory-0.5.0.dist-info/RECORD +76 -0
  74. {alma_memory-0.4.0.dist-info → alma_memory-0.5.0.dist-info}/WHEEL +1 -1
  75. alma_memory-0.4.0.dist-info/METADATA +0 -488
  76. alma_memory-0.4.0.dist-info/RECORD +0 -52
  77. {alma_memory-0.4.0.dist-info → alma_memory-0.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,246 @@
1
+ """
2
+ ALMA Event-Aware Storage Mixin.
3
+
4
+ Provides event emission capabilities for storage backends.
5
+ This is a mixin class that can be used by any storage implementation
6
+ to emit events when memory operations occur.
7
+ """
8
+
9
+ import logging
10
+ from dataclasses import asdict
11
+ from typing import Any, Dict, Optional
12
+
13
+ from alma.events.emitter import EventEmitter, get_emitter
14
+ from alma.events.types import MemoryEventType, create_memory_event
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class EventAwareStorageMixin:
20
+ """
21
+ Mixin that adds event emission to storage backends.
22
+
23
+ This mixin provides helper methods to emit events for various
24
+ storage operations. Events are only emitted if there are subscribers,
25
+ making the overhead negligible when events are not used.
26
+
27
+ Usage:
28
+ class MyStorage(StorageBackend, EventAwareStorageMixin):
29
+ def save_heuristic(self, heuristic):
30
+ result_id = super().save_heuristic(heuristic)
31
+ self._emit_memory_event(
32
+ event_type=MemoryEventType.CREATED,
33
+ agent=heuristic.agent,
34
+ project_id=heuristic.project_id,
35
+ memory_type="heuristics",
36
+ memory_id=result_id,
37
+ payload=self._heuristic_to_dict(heuristic),
38
+ )
39
+ return result_id
40
+ """
41
+
42
+ _events_enabled: bool = True
43
+ _custom_emitter: Optional[EventEmitter] = None
44
+
45
+ def enable_events(self) -> None:
46
+ """Enable event emission for this storage instance."""
47
+ self._events_enabled = True
48
+
49
+ def disable_events(self) -> None:
50
+ """Disable event emission for this storage instance."""
51
+ self._events_enabled = False
52
+
53
+ def set_emitter(self, emitter: EventEmitter) -> None:
54
+ """
55
+ Set a custom event emitter for this storage instance.
56
+
57
+ Args:
58
+ emitter: The event emitter to use
59
+ """
60
+ self._custom_emitter = emitter
61
+
62
+ def _get_emitter(self) -> EventEmitter:
63
+ """Get the event emitter to use."""
64
+ if self._custom_emitter is not None:
65
+ return self._custom_emitter
66
+ return get_emitter()
67
+
68
+ def _should_emit(self, event_type: MemoryEventType) -> bool:
69
+ """
70
+ Check if events should be emitted.
71
+
72
+ Returns False if:
73
+ - Events are disabled for this storage instance
74
+ - There are no subscribers for this event type
75
+
76
+ This optimization ensures event creation overhead is avoided
77
+ when no one is listening.
78
+
79
+ Args:
80
+ event_type: The type of event being considered
81
+
82
+ Returns:
83
+ True if an event should be emitted
84
+ """
85
+ if not self._events_enabled:
86
+ return False
87
+
88
+ emitter = self._get_emitter()
89
+ return emitter.has_subscribers(event_type)
90
+
91
+ def _emit_memory_event(
92
+ self,
93
+ event_type: MemoryEventType,
94
+ agent: str,
95
+ project_id: str,
96
+ memory_type: str,
97
+ memory_id: str,
98
+ payload: Dict[str, Any],
99
+ metadata: Optional[Dict[str, Any]] = None,
100
+ ) -> None:
101
+ """
102
+ Emit a memory event if there are subscribers.
103
+
104
+ This is a convenience method that checks for subscribers before
105
+ creating and emitting the event.
106
+
107
+ Args:
108
+ event_type: Type of event
109
+ agent: Agent name
110
+ project_id: Project identifier
111
+ memory_type: Type of memory
112
+ memory_id: Memory identifier
113
+ payload: Event-specific data
114
+ metadata: Optional additional context
115
+ """
116
+ if not self._should_emit(event_type):
117
+ return
118
+
119
+ event = create_memory_event(
120
+ event_type=event_type,
121
+ agent=agent,
122
+ project_id=project_id,
123
+ memory_type=memory_type,
124
+ memory_id=memory_id,
125
+ payload=payload,
126
+ metadata=metadata,
127
+ )
128
+
129
+ emitter = self._get_emitter()
130
+ emitter.emit(event)
131
+
132
+ logger.debug(f"Emitted {event_type.value} for {memory_type}/{memory_id}")
133
+
134
+ def _emit_created_event(
135
+ self,
136
+ agent: str,
137
+ project_id: str,
138
+ memory_type: str,
139
+ memory_id: str,
140
+ payload: Dict[str, Any],
141
+ ) -> None:
142
+ """Emit a CREATED event."""
143
+ self._emit_memory_event(
144
+ event_type=MemoryEventType.CREATED,
145
+ agent=agent,
146
+ project_id=project_id,
147
+ memory_type=memory_type,
148
+ memory_id=memory_id,
149
+ payload=payload,
150
+ )
151
+
152
+ def _emit_updated_event(
153
+ self,
154
+ agent: str,
155
+ project_id: str,
156
+ memory_type: str,
157
+ memory_id: str,
158
+ payload: Dict[str, Any],
159
+ ) -> None:
160
+ """Emit an UPDATED event."""
161
+ self._emit_memory_event(
162
+ event_type=MemoryEventType.UPDATED,
163
+ agent=agent,
164
+ project_id=project_id,
165
+ memory_type=memory_type,
166
+ memory_id=memory_id,
167
+ payload=payload,
168
+ )
169
+
170
+ def _emit_deleted_event(
171
+ self,
172
+ agent: str,
173
+ project_id: str,
174
+ memory_type: str,
175
+ memory_id: str,
176
+ ) -> None:
177
+ """Emit a DELETED event."""
178
+ self._emit_memory_event(
179
+ event_type=MemoryEventType.DELETED,
180
+ agent=agent,
181
+ project_id=project_id,
182
+ memory_type=memory_type,
183
+ memory_id=memory_id,
184
+ payload={"deleted_id": memory_id},
185
+ )
186
+
187
+
188
+ def emit_on_save(
189
+ memory_type: str, event_type: MemoryEventType = MemoryEventType.CREATED
190
+ ):
191
+ """
192
+ Decorator to emit events when save methods are called.
193
+
194
+ This decorator can be used on storage methods that save memories.
195
+ It will emit an event after the save completes successfully.
196
+
197
+ Args:
198
+ memory_type: The type of memory being saved (e.g., "heuristics")
199
+ event_type: The event type to emit (default: CREATED)
200
+
201
+ Example:
202
+ @emit_on_save("heuristics")
203
+ def save_heuristic(self, heuristic: Heuristic) -> str:
204
+ # Original implementation
205
+ ...
206
+ """
207
+
208
+ def decorator(func):
209
+ def wrapper(self, memory_item):
210
+ # Call the original method
211
+ result_id = func(self, memory_item)
212
+
213
+ # Emit event if storage supports events
214
+ if hasattr(self, "_emit_memory_event") and hasattr(self, "_should_emit"):
215
+ if self._should_emit(event_type):
216
+ # Extract common fields
217
+ agent = getattr(memory_item, "agent", "unknown")
218
+ project_id = getattr(memory_item, "project_id", "unknown")
219
+
220
+ # Convert to dict, handling dataclasses
221
+ try:
222
+ if hasattr(memory_item, "__dataclass_fields__"):
223
+ payload = {
224
+ k: v
225
+ for k, v in asdict(memory_item).items()
226
+ if k != "embedding" # Exclude large embedding vectors
227
+ }
228
+ else:
229
+ payload = {"id": result_id}
230
+ except Exception:
231
+ payload = {"id": result_id}
232
+
233
+ self._emit_memory_event(
234
+ event_type=event_type,
235
+ agent=agent,
236
+ project_id=project_id,
237
+ memory_type=memory_type,
238
+ memory_id=result_id,
239
+ payload=payload,
240
+ )
241
+
242
+ return result_id
243
+
244
+ return wrapper
245
+
246
+ return decorator
alma/events/types.py ADDED
@@ -0,0 +1,126 @@
1
+ """
2
+ ALMA Event Types.
3
+
4
+ Defines event types and the MemoryEvent dataclass for the event system.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from datetime import datetime, timezone
9
+ from enum import Enum
10
+ from typing import Any, Dict, Optional
11
+
12
+
13
+ class MemoryEventType(Enum):
14
+ """Types of events that can be emitted by the memory system."""
15
+
16
+ # Core memory operations
17
+ CREATED = "memory.created"
18
+ UPDATED = "memory.updated"
19
+ DELETED = "memory.deleted"
20
+ CONSOLIDATED = "memory.consolidated"
21
+
22
+ # Learning-specific events
23
+ HEURISTIC_FORMED = "heuristic.formed"
24
+ ANTIPATTERN_DETECTED = "antipattern.detected"
25
+ PREFERENCE_ADDED = "preference.added"
26
+ KNOWLEDGE_ADDED = "knowledge.added"
27
+
28
+ # Confidence events
29
+ CONFIDENCE_UPDATED = "confidence.updated"
30
+ CONFIDENCE_DECAYED = "confidence.decayed"
31
+
32
+
33
+ @dataclass
34
+ class MemoryEvent:
35
+ """
36
+ Represents an event in the memory system.
37
+
38
+ Events are emitted when memory operations occur, allowing external
39
+ systems to react to changes through callbacks or webhooks.
40
+
41
+ Attributes:
42
+ event_type: The type of event that occurred
43
+ agent: Name of the agent associated with the event
44
+ project_id: Project identifier
45
+ memory_type: Type of memory (heuristics, outcomes, etc.)
46
+ memory_id: Unique identifier of the affected memory
47
+ timestamp: When the event occurred
48
+ payload: Event-specific data (e.g., the created memory)
49
+ metadata: Optional additional context
50
+ """
51
+
52
+ event_type: MemoryEventType
53
+ agent: str
54
+ project_id: str
55
+ memory_type: (
56
+ str # heuristics, outcomes, preferences, domain_knowledge, anti_patterns
57
+ )
58
+ memory_id: str
59
+ timestamp: datetime
60
+ payload: Dict[str, Any]
61
+ metadata: Optional[Dict[str, Any]] = field(default_factory=dict)
62
+
63
+ def to_dict(self) -> Dict[str, Any]:
64
+ """Convert event to dictionary for serialization."""
65
+ return {
66
+ "event_type": self.event_type.value,
67
+ "agent": self.agent,
68
+ "project_id": self.project_id,
69
+ "memory_type": self.memory_type,
70
+ "memory_id": self.memory_id,
71
+ "timestamp": self.timestamp.isoformat(),
72
+ "payload": self.payload,
73
+ "metadata": self.metadata or {},
74
+ }
75
+
76
+ @classmethod
77
+ def from_dict(cls, data: Dict[str, Any]) -> "MemoryEvent":
78
+ """Create event from dictionary."""
79
+ return cls(
80
+ event_type=MemoryEventType(data["event_type"]),
81
+ agent=data["agent"],
82
+ project_id=data["project_id"],
83
+ memory_type=data["memory_type"],
84
+ memory_id=data["memory_id"],
85
+ timestamp=datetime.fromisoformat(data["timestamp"]),
86
+ payload=data["payload"],
87
+ metadata=data.get("metadata"),
88
+ )
89
+
90
+
91
+ def create_memory_event(
92
+ event_type: MemoryEventType,
93
+ agent: str,
94
+ project_id: str,
95
+ memory_type: str,
96
+ memory_id: str,
97
+ payload: Dict[str, Any],
98
+ metadata: Optional[Dict[str, Any]] = None,
99
+ *,
100
+ default_metadata: bool = True,
101
+ ) -> MemoryEvent:
102
+ """
103
+ Factory function to create a MemoryEvent with current timestamp.
104
+
105
+ Args:
106
+ event_type: Type of event
107
+ agent: Agent name
108
+ project_id: Project identifier
109
+ memory_type: Type of memory
110
+ memory_id: Memory identifier
111
+ payload: Event-specific data
112
+ metadata: Optional additional context
113
+
114
+ Returns:
115
+ A new MemoryEvent instance
116
+ """
117
+ return MemoryEvent(
118
+ event_type=event_type,
119
+ agent=agent,
120
+ project_id=project_id,
121
+ memory_type=memory_type,
122
+ memory_id=memory_id,
123
+ timestamp=datetime.now(timezone.utc),
124
+ payload=payload,
125
+ metadata=metadata if metadata is not None else {},
126
+ )