alma-memory 0.5.0__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.
Files changed (111) hide show
  1. alma/__init__.py +296 -194
  2. alma/compression/__init__.py +33 -0
  3. alma/compression/pipeline.py +980 -0
  4. alma/confidence/__init__.py +47 -47
  5. alma/confidence/engine.py +540 -540
  6. alma/confidence/types.py +351 -351
  7. alma/config/loader.py +157 -157
  8. alma/consolidation/__init__.py +23 -23
  9. alma/consolidation/engine.py +678 -678
  10. alma/consolidation/prompts.py +84 -84
  11. alma/core.py +1189 -322
  12. alma/domains/__init__.py +30 -30
  13. alma/domains/factory.py +359 -359
  14. alma/domains/schemas.py +448 -448
  15. alma/domains/types.py +272 -272
  16. alma/events/__init__.py +75 -75
  17. alma/events/emitter.py +285 -284
  18. alma/events/storage_mixin.py +246 -246
  19. alma/events/types.py +126 -126
  20. alma/events/webhook.py +425 -425
  21. alma/exceptions.py +49 -49
  22. alma/extraction/__init__.py +31 -31
  23. alma/extraction/auto_learner.py +265 -264
  24. alma/extraction/extractor.py +420 -420
  25. alma/graph/__init__.py +106 -81
  26. alma/graph/backends/__init__.py +32 -18
  27. alma/graph/backends/kuzu.py +624 -0
  28. alma/graph/backends/memgraph.py +432 -0
  29. alma/graph/backends/memory.py +236 -236
  30. alma/graph/backends/neo4j.py +417 -417
  31. alma/graph/base.py +159 -159
  32. alma/graph/extraction.py +198 -198
  33. alma/graph/store.py +860 -860
  34. alma/harness/__init__.py +35 -35
  35. alma/harness/base.py +386 -386
  36. alma/harness/domains.py +705 -705
  37. alma/initializer/__init__.py +37 -37
  38. alma/initializer/initializer.py +418 -418
  39. alma/initializer/types.py +250 -250
  40. alma/integration/__init__.py +62 -62
  41. alma/integration/claude_agents.py +444 -432
  42. alma/integration/helena.py +423 -423
  43. alma/integration/victor.py +471 -471
  44. alma/learning/__init__.py +101 -86
  45. alma/learning/decay.py +878 -0
  46. alma/learning/forgetting.py +1446 -1446
  47. alma/learning/heuristic_extractor.py +390 -390
  48. alma/learning/protocols.py +374 -374
  49. alma/learning/validation.py +346 -346
  50. alma/mcp/__init__.py +123 -45
  51. alma/mcp/__main__.py +156 -156
  52. alma/mcp/resources.py +122 -122
  53. alma/mcp/server.py +955 -591
  54. alma/mcp/tools.py +3254 -511
  55. alma/observability/__init__.py +91 -0
  56. alma/observability/config.py +302 -0
  57. alma/observability/guidelines.py +170 -0
  58. alma/observability/logging.py +424 -0
  59. alma/observability/metrics.py +583 -0
  60. alma/observability/tracing.py +440 -0
  61. alma/progress/__init__.py +21 -21
  62. alma/progress/tracker.py +607 -607
  63. alma/progress/types.py +250 -250
  64. alma/retrieval/__init__.py +134 -53
  65. alma/retrieval/budget.py +525 -0
  66. alma/retrieval/cache.py +1304 -1061
  67. alma/retrieval/embeddings.py +202 -202
  68. alma/retrieval/engine.py +850 -366
  69. alma/retrieval/modes.py +365 -0
  70. alma/retrieval/progressive.py +560 -0
  71. alma/retrieval/scoring.py +344 -344
  72. alma/retrieval/trust_scoring.py +637 -0
  73. alma/retrieval/verification.py +797 -0
  74. alma/session/__init__.py +19 -19
  75. alma/session/manager.py +442 -399
  76. alma/session/types.py +288 -288
  77. alma/storage/__init__.py +101 -61
  78. alma/storage/archive.py +233 -0
  79. alma/storage/azure_cosmos.py +1259 -1048
  80. alma/storage/base.py +1083 -525
  81. alma/storage/chroma.py +1443 -1443
  82. alma/storage/constants.py +103 -0
  83. alma/storage/file_based.py +614 -619
  84. alma/storage/migrations/__init__.py +21 -0
  85. alma/storage/migrations/base.py +321 -0
  86. alma/storage/migrations/runner.py +323 -0
  87. alma/storage/migrations/version_stores.py +337 -0
  88. alma/storage/migrations/versions/__init__.py +11 -0
  89. alma/storage/migrations/versions/v1_0_0.py +373 -0
  90. alma/storage/migrations/versions/v1_1_0_workflow_context.py +551 -0
  91. alma/storage/pinecone.py +1080 -1080
  92. alma/storage/postgresql.py +1948 -1452
  93. alma/storage/qdrant.py +1306 -1306
  94. alma/storage/sqlite_local.py +3041 -1358
  95. alma/testing/__init__.py +46 -0
  96. alma/testing/factories.py +301 -0
  97. alma/testing/mocks.py +389 -0
  98. alma/types.py +292 -264
  99. alma/utils/__init__.py +19 -0
  100. alma/utils/tokenizer.py +521 -0
  101. alma/workflow/__init__.py +83 -0
  102. alma/workflow/artifacts.py +170 -0
  103. alma/workflow/checkpoint.py +311 -0
  104. alma/workflow/context.py +228 -0
  105. alma/workflow/outcomes.py +189 -0
  106. alma/workflow/reducers.py +393 -0
  107. {alma_memory-0.5.0.dist-info → alma_memory-0.7.0.dist-info}/METADATA +244 -72
  108. alma_memory-0.7.0.dist-info/RECORD +112 -0
  109. alma_memory-0.5.0.dist-info/RECORD +0 -76
  110. {alma_memory-0.5.0.dist-info → alma_memory-0.7.0.dist-info}/WHEEL +0 -0
  111. {alma_memory-0.5.0.dist-info → alma_memory-0.7.0.dist-info}/top_level.txt +0 -0
alma/domains/types.py CHANGED
@@ -1,272 +1,272 @@
1
- """
2
- Domain Memory Types.
3
-
4
- Data models for domain-specific memory schemas.
5
- """
6
-
7
- import uuid
8
- from dataclasses import dataclass, field
9
- from datetime import datetime, timezone
10
- from typing import Any, Dict, List, Optional
11
-
12
-
13
- @dataclass
14
- class EntityType:
15
- """
16
- A type of entity in a domain.
17
-
18
- Entities are the "things" that agents work with in a domain.
19
- For example: features, bugs, tests (coding), papers, hypotheses (research).
20
- """
21
-
22
- name: str # "feature", "test", "paper", "lead"
23
- description: str
24
- attributes: List[str] = field(
25
- default_factory=list
26
- ) # ["status", "priority", "owner"]
27
-
28
- # Optional schema validation
29
- required_attributes: List[str] = field(default_factory=list)
30
- attribute_types: Dict[str, str] = field(
31
- default_factory=dict
32
- ) # attr -> "str", "int", "bool"
33
-
34
- def validate_entity(self, entity: Dict[str, Any]) -> List[str]:
35
- """Validate an entity instance against this type."""
36
- errors = []
37
- for attr in self.required_attributes:
38
- if attr not in entity:
39
- errors.append(f"Missing required attribute: {attr}")
40
- return errors
41
-
42
-
43
- @dataclass
44
- class RelationshipType:
45
- """
46
- A relationship between entities in a domain.
47
-
48
- Relationships connect entities (e.g., "feature implements spec").
49
- """
50
-
51
- name: str # "implements", "blocks", "supports", "cites"
52
- description: str
53
- source_type: str # Entity type name
54
- target_type: str # Entity type name
55
-
56
- # Cardinality
57
- many_to_many: bool = True
58
- required: bool = False
59
-
60
-
61
- @dataclass
62
- class DomainSchema:
63
- """
64
- Defines memory structure for a specific domain.
65
-
66
- A schema describes what entities exist in a domain, how they relate,
67
- and what learning categories agents can use.
68
- """
69
-
70
- id: str
71
- name: str # "coding", "research", "sales"
72
- description: str
73
-
74
- # What entities exist in this domain
75
- entity_types: List[EntityType] = field(default_factory=list)
76
-
77
- # What relationships between entities
78
- relationship_types: List[RelationshipType] = field(default_factory=list)
79
-
80
- # Learning categories (replaces hardcoded HELENA_CATEGORIES)
81
- learning_categories: List[str] = field(default_factory=list)
82
-
83
- # What can agents in this domain NOT learn (scoping)
84
- excluded_categories: List[str] = field(default_factory=list)
85
-
86
- # Domain-specific settings
87
- min_occurrences_for_heuristic: int = 3
88
- confidence_decay_days: float = 30.0
89
-
90
- # Timestamps
91
- created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
92
- updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
93
-
94
- # Extensible metadata
95
- metadata: Dict[str, Any] = field(default_factory=dict)
96
-
97
- @classmethod
98
- def create(
99
- cls,
100
- name: str,
101
- description: str,
102
- learning_categories: Optional[List[str]] = None,
103
- **kwargs,
104
- ) -> "DomainSchema":
105
- """Factory method to create a new domain schema."""
106
- return cls(
107
- id=str(uuid.uuid4()),
108
- name=name,
109
- description=description,
110
- learning_categories=learning_categories or [],
111
- **kwargs,
112
- )
113
-
114
- def add_entity_type(
115
- self,
116
- name: str,
117
- description: str,
118
- attributes: Optional[List[str]] = None,
119
- ) -> EntityType:
120
- """Add an entity type to this schema."""
121
- entity = EntityType(
122
- name=name,
123
- description=description,
124
- attributes=attributes or [],
125
- )
126
- self.entity_types.append(entity)
127
- self.updated_at = datetime.now(timezone.utc)
128
- return entity
129
-
130
- def add_relationship_type(
131
- self,
132
- name: str,
133
- description: str,
134
- source_type: str,
135
- target_type: str,
136
- ) -> RelationshipType:
137
- """Add a relationship type to this schema."""
138
- rel = RelationshipType(
139
- name=name,
140
- description=description,
141
- source_type=source_type,
142
- target_type=target_type,
143
- )
144
- self.relationship_types.append(rel)
145
- self.updated_at = datetime.now(timezone.utc)
146
- return rel
147
-
148
- def add_learning_category(self, category: str) -> None:
149
- """Add a learning category."""
150
- if category not in self.learning_categories:
151
- self.learning_categories.append(category)
152
- self.updated_at = datetime.now(timezone.utc)
153
-
154
- def add_excluded_category(self, category: str) -> None:
155
- """Add an excluded category (agent cannot learn from this)."""
156
- if category not in self.excluded_categories:
157
- self.excluded_categories.append(category)
158
- self.updated_at = datetime.now(timezone.utc)
159
-
160
- def get_entity_type(self, name: str) -> Optional[EntityType]:
161
- """Get entity type by name."""
162
- for entity in self.entity_types:
163
- if entity.name == name:
164
- return entity
165
- return None
166
-
167
- def get_relationship_type(self, name: str) -> Optional[RelationshipType]:
168
- """Get relationship type by name."""
169
- for rel in self.relationship_types:
170
- if rel.name == name:
171
- return rel
172
- return None
173
-
174
- def is_category_allowed(self, category: str) -> bool:
175
- """Check if a learning category is allowed in this domain."""
176
- if self.learning_categories and category not in self.learning_categories:
177
- return False
178
- if category in self.excluded_categories:
179
- return False
180
- return True
181
-
182
- def validate(self) -> List[str]:
183
- """Validate the schema for consistency."""
184
- errors = []
185
-
186
- # Check relationship source/target types exist
187
- entity_names = {e.name for e in self.entity_types}
188
- for rel in self.relationship_types:
189
- if rel.source_type not in entity_names:
190
- errors.append(
191
- f"Relationship '{rel.name}' references unknown source type: {rel.source_type}"
192
- )
193
- if rel.target_type not in entity_names:
194
- errors.append(
195
- f"Relationship '{rel.name}' references unknown target type: {rel.target_type}"
196
- )
197
-
198
- # Check for duplicate entity names
199
- seen_names = set()
200
- for entity in self.entity_types:
201
- if entity.name in seen_names:
202
- errors.append(f"Duplicate entity type name: {entity.name}")
203
- seen_names.add(entity.name)
204
-
205
- return errors
206
-
207
- def to_dict(self) -> Dict[str, Any]:
208
- """Convert schema to dictionary for serialization."""
209
- return {
210
- "id": self.id,
211
- "name": self.name,
212
- "description": self.description,
213
- "entity_types": [
214
- {
215
- "name": e.name,
216
- "description": e.description,
217
- "attributes": e.attributes,
218
- }
219
- for e in self.entity_types
220
- ],
221
- "relationship_types": [
222
- {
223
- "name": r.name,
224
- "description": r.description,
225
- "source_type": r.source_type,
226
- "target_type": r.target_type,
227
- }
228
- for r in self.relationship_types
229
- ],
230
- "learning_categories": self.learning_categories,
231
- "excluded_categories": self.excluded_categories,
232
- "min_occurrences_for_heuristic": self.min_occurrences_for_heuristic,
233
- "confidence_decay_days": self.confidence_decay_days,
234
- "created_at": self.created_at.isoformat(),
235
- "updated_at": self.updated_at.isoformat(),
236
- "metadata": self.metadata,
237
- }
238
-
239
- @classmethod
240
- def from_dict(cls, data: Dict[str, Any]) -> "DomainSchema":
241
- """Create schema from dictionary."""
242
- entity_types = [
243
- EntityType(
244
- name=e["name"],
245
- description=e["description"],
246
- attributes=e.get("attributes", []),
247
- )
248
- for e in data.get("entity_types", [])
249
- ]
250
-
251
- relationship_types = [
252
- RelationshipType(
253
- name=r["name"],
254
- description=r["description"],
255
- source_type=r["source_type"],
256
- target_type=r["target_type"],
257
- )
258
- for r in data.get("relationship_types", [])
259
- ]
260
-
261
- return cls(
262
- id=data.get("id", str(uuid.uuid4())),
263
- name=data["name"],
264
- description=data["description"],
265
- entity_types=entity_types,
266
- relationship_types=relationship_types,
267
- learning_categories=data.get("learning_categories", []),
268
- excluded_categories=data.get("excluded_categories", []),
269
- min_occurrences_for_heuristic=data.get("min_occurrences_for_heuristic", 3),
270
- confidence_decay_days=data.get("confidence_decay_days", 30.0),
271
- metadata=data.get("metadata", {}),
272
- )
1
+ """
2
+ Domain Memory Types.
3
+
4
+ Data models for domain-specific memory schemas.
5
+ """
6
+
7
+ import uuid
8
+ from dataclasses import dataclass, field
9
+ from datetime import datetime, timezone
10
+ from typing import Any, Dict, List, Optional
11
+
12
+
13
+ @dataclass
14
+ class EntityType:
15
+ """
16
+ A type of entity in a domain.
17
+
18
+ Entities are the "things" that agents work with in a domain.
19
+ For example: features, bugs, tests (coding), papers, hypotheses (research).
20
+ """
21
+
22
+ name: str # "feature", "test", "paper", "lead"
23
+ description: str
24
+ attributes: List[str] = field(
25
+ default_factory=list
26
+ ) # ["status", "priority", "owner"]
27
+
28
+ # Optional schema validation
29
+ required_attributes: List[str] = field(default_factory=list)
30
+ attribute_types: Dict[str, str] = field(
31
+ default_factory=dict
32
+ ) # attr -> "str", "int", "bool"
33
+
34
+ def validate_entity(self, entity: Dict[str, Any]) -> List[str]:
35
+ """Validate an entity instance against this type."""
36
+ errors = []
37
+ for attr in self.required_attributes:
38
+ if attr not in entity:
39
+ errors.append(f"Missing required attribute: {attr}")
40
+ return errors
41
+
42
+
43
+ @dataclass
44
+ class RelationshipType:
45
+ """
46
+ A relationship between entities in a domain.
47
+
48
+ Relationships connect entities (e.g., "feature implements spec").
49
+ """
50
+
51
+ name: str # "implements", "blocks", "supports", "cites"
52
+ description: str
53
+ source_type: str # Entity type name
54
+ target_type: str # Entity type name
55
+
56
+ # Cardinality
57
+ many_to_many: bool = True
58
+ required: bool = False
59
+
60
+
61
+ @dataclass
62
+ class DomainSchema:
63
+ """
64
+ Defines memory structure for a specific domain.
65
+
66
+ A schema describes what entities exist in a domain, how they relate,
67
+ and what learning categories agents can use.
68
+ """
69
+
70
+ id: str
71
+ name: str # "coding", "research", "sales"
72
+ description: str
73
+
74
+ # What entities exist in this domain
75
+ entity_types: List[EntityType] = field(default_factory=list)
76
+
77
+ # What relationships between entities
78
+ relationship_types: List[RelationshipType] = field(default_factory=list)
79
+
80
+ # Learning categories (replaces hardcoded HELENA_CATEGORIES)
81
+ learning_categories: List[str] = field(default_factory=list)
82
+
83
+ # What can agents in this domain NOT learn (scoping)
84
+ excluded_categories: List[str] = field(default_factory=list)
85
+
86
+ # Domain-specific settings
87
+ min_occurrences_for_heuristic: int = 3
88
+ confidence_decay_days: float = 30.0
89
+
90
+ # Timestamps
91
+ created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
92
+ updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
93
+
94
+ # Extensible metadata
95
+ metadata: Dict[str, Any] = field(default_factory=dict)
96
+
97
+ @classmethod
98
+ def create(
99
+ cls,
100
+ name: str,
101
+ description: str,
102
+ learning_categories: Optional[List[str]] = None,
103
+ **kwargs,
104
+ ) -> "DomainSchema":
105
+ """Factory method to create a new domain schema."""
106
+ return cls(
107
+ id=str(uuid.uuid4()),
108
+ name=name,
109
+ description=description,
110
+ learning_categories=learning_categories or [],
111
+ **kwargs,
112
+ )
113
+
114
+ def add_entity_type(
115
+ self,
116
+ name: str,
117
+ description: str,
118
+ attributes: Optional[List[str]] = None,
119
+ ) -> EntityType:
120
+ """Add an entity type to this schema."""
121
+ entity = EntityType(
122
+ name=name,
123
+ description=description,
124
+ attributes=attributes or [],
125
+ )
126
+ self.entity_types.append(entity)
127
+ self.updated_at = datetime.now(timezone.utc)
128
+ return entity
129
+
130
+ def add_relationship_type(
131
+ self,
132
+ name: str,
133
+ description: str,
134
+ source_type: str,
135
+ target_type: str,
136
+ ) -> RelationshipType:
137
+ """Add a relationship type to this schema."""
138
+ rel = RelationshipType(
139
+ name=name,
140
+ description=description,
141
+ source_type=source_type,
142
+ target_type=target_type,
143
+ )
144
+ self.relationship_types.append(rel)
145
+ self.updated_at = datetime.now(timezone.utc)
146
+ return rel
147
+
148
+ def add_learning_category(self, category: str) -> None:
149
+ """Add a learning category."""
150
+ if category not in self.learning_categories:
151
+ self.learning_categories.append(category)
152
+ self.updated_at = datetime.now(timezone.utc)
153
+
154
+ def add_excluded_category(self, category: str) -> None:
155
+ """Add an excluded category (agent cannot learn from this)."""
156
+ if category not in self.excluded_categories:
157
+ self.excluded_categories.append(category)
158
+ self.updated_at = datetime.now(timezone.utc)
159
+
160
+ def get_entity_type(self, name: str) -> Optional[EntityType]:
161
+ """Get entity type by name."""
162
+ for entity in self.entity_types:
163
+ if entity.name == name:
164
+ return entity
165
+ return None
166
+
167
+ def get_relationship_type(self, name: str) -> Optional[RelationshipType]:
168
+ """Get relationship type by name."""
169
+ for rel in self.relationship_types:
170
+ if rel.name == name:
171
+ return rel
172
+ return None
173
+
174
+ def is_category_allowed(self, category: str) -> bool:
175
+ """Check if a learning category is allowed in this domain."""
176
+ if self.learning_categories and category not in self.learning_categories:
177
+ return False
178
+ if category in self.excluded_categories:
179
+ return False
180
+ return True
181
+
182
+ def validate(self) -> List[str]:
183
+ """Validate the schema for consistency."""
184
+ errors = []
185
+
186
+ # Check relationship source/target types exist
187
+ entity_names = {e.name for e in self.entity_types}
188
+ for rel in self.relationship_types:
189
+ if rel.source_type not in entity_names:
190
+ errors.append(
191
+ f"Relationship '{rel.name}' references unknown source type: {rel.source_type}"
192
+ )
193
+ if rel.target_type not in entity_names:
194
+ errors.append(
195
+ f"Relationship '{rel.name}' references unknown target type: {rel.target_type}"
196
+ )
197
+
198
+ # Check for duplicate entity names
199
+ seen_names = set()
200
+ for entity in self.entity_types:
201
+ if entity.name in seen_names:
202
+ errors.append(f"Duplicate entity type name: {entity.name}")
203
+ seen_names.add(entity.name)
204
+
205
+ return errors
206
+
207
+ def to_dict(self) -> Dict[str, Any]:
208
+ """Convert schema to dictionary for serialization."""
209
+ return {
210
+ "id": self.id,
211
+ "name": self.name,
212
+ "description": self.description,
213
+ "entity_types": [
214
+ {
215
+ "name": e.name,
216
+ "description": e.description,
217
+ "attributes": e.attributes,
218
+ }
219
+ for e in self.entity_types
220
+ ],
221
+ "relationship_types": [
222
+ {
223
+ "name": r.name,
224
+ "description": r.description,
225
+ "source_type": r.source_type,
226
+ "target_type": r.target_type,
227
+ }
228
+ for r in self.relationship_types
229
+ ],
230
+ "learning_categories": self.learning_categories,
231
+ "excluded_categories": self.excluded_categories,
232
+ "min_occurrences_for_heuristic": self.min_occurrences_for_heuristic,
233
+ "confidence_decay_days": self.confidence_decay_days,
234
+ "created_at": self.created_at.isoformat(),
235
+ "updated_at": self.updated_at.isoformat(),
236
+ "metadata": self.metadata,
237
+ }
238
+
239
+ @classmethod
240
+ def from_dict(cls, data: Dict[str, Any]) -> "DomainSchema":
241
+ """Create schema from dictionary."""
242
+ entity_types = [
243
+ EntityType(
244
+ name=e["name"],
245
+ description=e["description"],
246
+ attributes=e.get("attributes", []),
247
+ )
248
+ for e in data.get("entity_types", [])
249
+ ]
250
+
251
+ relationship_types = [
252
+ RelationshipType(
253
+ name=r["name"],
254
+ description=r["description"],
255
+ source_type=r["source_type"],
256
+ target_type=r["target_type"],
257
+ )
258
+ for r in data.get("relationship_types", [])
259
+ ]
260
+
261
+ return cls(
262
+ id=data.get("id", str(uuid.uuid4())),
263
+ name=data["name"],
264
+ description=data["description"],
265
+ entity_types=entity_types,
266
+ relationship_types=relationship_types,
267
+ learning_categories=data.get("learning_categories", []),
268
+ excluded_categories=data.get("excluded_categories", []),
269
+ min_occurrences_for_heuristic=data.get("min_occurrences_for_heuristic", 3),
270
+ confidence_decay_days=data.get("confidence_decay_days", 30.0),
271
+ metadata=data.get("metadata", {}),
272
+ )