alma-memory 0.5.1__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 -226
  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 -430
  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 -265
  24. alma/extraction/extractor.py +420 -420
  25. alma/graph/__init__.py +106 -106
  26. alma/graph/backends/__init__.py +32 -32
  27. alma/graph/backends/kuzu.py +624 -624
  28. alma/graph/backends/memgraph.py +432 -432
  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 -444
  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 -509
  55. alma/observability/__init__.py +91 -84
  56. alma/observability/config.py +302 -302
  57. alma/observability/guidelines.py +170 -0
  58. alma/observability/logging.py +424 -424
  59. alma/observability/metrics.py +583 -583
  60. alma/observability/tracing.py +440 -440
  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 -427
  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 -90
  78. alma/storage/archive.py +233 -0
  79. alma/storage/azure_cosmos.py +1259 -1259
  80. alma/storage/base.py +1083 -583
  81. alma/storage/chroma.py +1443 -1443
  82. alma/storage/constants.py +103 -103
  83. alma/storage/file_based.py +614 -614
  84. alma/storage/migrations/__init__.py +21 -21
  85. alma/storage/migrations/base.py +321 -321
  86. alma/storage/migrations/runner.py +323 -323
  87. alma/storage/migrations/version_stores.py +337 -337
  88. alma/storage/migrations/versions/__init__.py +11 -11
  89. alma/storage/migrations/versions/v1_0_0.py +373 -373
  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 -1559
  93. alma/storage/qdrant.py +1306 -1306
  94. alma/storage/sqlite_local.py +3041 -1457
  95. alma/testing/__init__.py +46 -46
  96. alma/testing/factories.py +301 -301
  97. alma/testing/mocks.py +389 -389
  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.1.dist-info → alma_memory-0.7.0.dist-info}/METADATA +210 -72
  108. alma_memory-0.7.0.dist-info/RECORD +112 -0
  109. alma_memory-0.5.1.dist-info/RECORD +0 -93
  110. {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/WHEEL +0 -0
  111. {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/top_level.txt +0 -0
alma/domains/factory.py CHANGED
@@ -1,359 +1,359 @@
1
- """
2
- Domain Memory Factory.
3
-
4
- Factory pattern for creating domain-specific ALMA instances.
5
- """
6
-
7
- import logging
8
- from typing import Any, Dict, List, Optional
9
-
10
- from alma.domains.schemas import (
11
- get_coding_schema,
12
- get_content_creation_schema,
13
- get_customer_support_schema,
14
- get_general_schema,
15
- get_research_schema,
16
- get_sales_schema,
17
- )
18
- from alma.domains.types import DomainSchema
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
-
23
- class DomainMemoryFactory:
24
- """
25
- Factory for creating domain-specific ALMA instances.
26
-
27
- Provides:
28
- - Pre-built schemas for common domains
29
- - Custom schema creation from config
30
- - ALMA instance creation with domain-specific settings
31
-
32
- Usage:
33
- # Use pre-built schema
34
- factory = DomainMemoryFactory()
35
- schema = factory.get_schema("coding")
36
- alma = factory.create_alma(schema, "my-project", storage)
37
-
38
- # Create custom schema
39
- schema = factory.create_schema("my-domain", {
40
- "description": "Custom domain",
41
- "entity_types": [...],
42
- "learning_categories": [...]
43
- })
44
- """
45
-
46
- # Registry of pre-built schemas
47
- _builtin_schemas: Dict[str, callable] = {
48
- "coding": get_coding_schema,
49
- "research": get_research_schema,
50
- "sales": get_sales_schema,
51
- "general": get_general_schema,
52
- "customer_support": get_customer_support_schema,
53
- "content_creation": get_content_creation_schema,
54
- }
55
-
56
- # Custom schema registry (for user-defined schemas)
57
- _custom_schemas: Dict[str, DomainSchema] = {}
58
-
59
- def __init__(self):
60
- """Initialize the factory."""
61
- pass
62
-
63
- @classmethod
64
- def list_schemas(cls) -> List[str]:
65
- """List all available schema names (built-in and custom)."""
66
- return list(cls._builtin_schemas.keys()) + list(cls._custom_schemas.keys())
67
-
68
- @classmethod
69
- def get_schema(cls, name: str) -> Optional[DomainSchema]:
70
- """
71
- Get a schema by name.
72
-
73
- Args:
74
- name: Schema name (e.g., "coding", "research")
75
-
76
- Returns:
77
- DomainSchema or None if not found
78
- """
79
- # Check custom schemas first
80
- if name in cls._custom_schemas:
81
- return cls._custom_schemas[name]
82
-
83
- # Check built-in schemas
84
- if name in cls._builtin_schemas:
85
- return cls._builtin_schemas[name]()
86
-
87
- return None
88
-
89
- @classmethod
90
- def create_schema(
91
- cls,
92
- name: str,
93
- config: Dict[str, Any],
94
- register: bool = True,
95
- ) -> DomainSchema:
96
- """
97
- Create a new domain schema from configuration.
98
-
99
- Args:
100
- name: Schema name
101
- config: Schema configuration dictionary
102
- register: If True, register schema for later retrieval
103
-
104
- Config format:
105
- {
106
- "description": "Schema description",
107
- "entity_types": [
108
- {"name": "entity1", "description": "...", "attributes": ["a", "b"]}
109
- ],
110
- "relationship_types": [
111
- {"name": "rel1", "description": "...", "source": "e1", "target": "e2"}
112
- ],
113
- "learning_categories": ["cat1", "cat2"],
114
- "excluded_categories": ["cat3"],
115
- "min_occurrences_for_heuristic": 3,
116
- "confidence_decay_days": 30.0
117
- }
118
-
119
- Returns:
120
- Created DomainSchema
121
- """
122
- schema = DomainSchema.create(
123
- name=name,
124
- description=config.get("description", f"Custom schema: {name}"),
125
- learning_categories=config.get("learning_categories", []),
126
- excluded_categories=config.get("excluded_categories", []),
127
- min_occurrences_for_heuristic=config.get(
128
- "min_occurrences_for_heuristic", 3
129
- ),
130
- confidence_decay_days=config.get("confidence_decay_days", 30.0),
131
- )
132
-
133
- # Add entity types
134
- for entity_config in config.get("entity_types", []):
135
- schema.add_entity_type(
136
- name=entity_config["name"],
137
- description=entity_config.get("description", ""),
138
- attributes=entity_config.get("attributes", []),
139
- )
140
-
141
- # Add relationship types
142
- for rel_config in config.get("relationship_types", []):
143
- schema.add_relationship_type(
144
- name=rel_config["name"],
145
- description=rel_config.get("description", ""),
146
- source_type=rel_config.get("source_type", rel_config.get("source", "")),
147
- target_type=rel_config.get("target_type", rel_config.get("target", "")),
148
- )
149
-
150
- # Validate
151
- errors = schema.validate()
152
- if errors:
153
- logger.warning(f"Schema validation warnings for '{name}': {errors}")
154
-
155
- # Register if requested
156
- if register:
157
- cls._custom_schemas[name] = schema
158
- logger.info(f"Registered custom schema: {name}")
159
-
160
- return schema
161
-
162
- @classmethod
163
- def register_schema(cls, schema: DomainSchema) -> None:
164
- """Register an existing schema for later retrieval."""
165
- cls._custom_schemas[schema.name] = schema
166
- logger.info(f"Registered schema: {schema.name}")
167
-
168
- @classmethod
169
- def unregister_schema(cls, name: str) -> bool:
170
- """Unregister a custom schema."""
171
- if name in cls._custom_schemas:
172
- del cls._custom_schemas[name]
173
- return True
174
- return False
175
-
176
- def create_alma(
177
- self,
178
- schema: DomainSchema,
179
- project_id: str,
180
- storage: Optional[Any] = None,
181
- embedding_provider: str = "mock",
182
- **config,
183
- ) -> Any:
184
- """
185
- Create ALMA instance configured for a domain.
186
-
187
- Args:
188
- schema: Domain schema to use
189
- project_id: Project identifier
190
- storage: Optional storage backend (FileBasedStorage created if None)
191
- embedding_provider: Embedding provider ("mock", "local", "openai")
192
- **config: Additional ALMA configuration
193
-
194
- Returns:
195
- Configured ALMA instance
196
-
197
- Note:
198
- This integrates with the core ALMA class.
199
- The schema is used to:
200
- - Configure allowed learning categories
201
- - Set heuristic thresholds
202
- - Initialize domain-specific entity tracking
203
- """
204
- # Import here to avoid circular dependency
205
- from alma import ALMA
206
- from alma.learning import LearningProtocol
207
- from alma.retrieval import RetrievalEngine
208
- from alma.storage.file_based import FileBasedStorage
209
- from alma.types import MemoryScope
210
-
211
- # Create storage if not provided
212
- if storage is None:
213
- import tempfile
214
- from pathlib import Path
215
-
216
- storage_dir = Path(tempfile.mkdtemp()) / ".alma" / project_id
217
- storage = FileBasedStorage(storage_dir)
218
-
219
- # Create retrieval engine
220
- retrieval = RetrievalEngine(
221
- storage=storage,
222
- embedding_provider=embedding_provider,
223
- )
224
-
225
- # Create scope based on schema
226
- default_agent = config.get("default_agent", "agent")
227
- scopes = {
228
- default_agent: MemoryScope(
229
- agent_name=default_agent,
230
- can_learn=schema.learning_categories,
231
- cannot_learn=schema.excluded_categories,
232
- min_occurrences_for_heuristic=schema.min_occurrences_for_heuristic,
233
- )
234
- }
235
-
236
- # Create learning protocol
237
- learning = LearningProtocol(
238
- storage=storage,
239
- scopes=scopes,
240
- )
241
-
242
- # Create ALMA instance
243
- alma = ALMA(
244
- storage=storage,
245
- retrieval_engine=retrieval,
246
- learning_protocol=learning,
247
- scopes=scopes,
248
- project_id=project_id,
249
- )
250
-
251
- # Store schema reference for domain-aware operations
252
- alma._domain_schema = schema
253
-
254
- logger.info(
255
- f"Created ALMA instance for project '{project_id}' "
256
- f"with domain schema '{schema.name}'"
257
- )
258
-
259
- return alma
260
-
261
- def create_alma_for_agent(
262
- self,
263
- schema_name: str,
264
- agent: str,
265
- project_id: str,
266
- storage: Optional[Any] = None,
267
- scope_restrictions: Optional[List[str]] = None,
268
- **config,
269
- ) -> Any:
270
- """
271
- Create ALMA instance for a specific agent.
272
-
273
- This is a convenience method that:
274
- 1. Gets the appropriate schema
275
- 2. Creates ALMA instance
276
- 3. Configures agent-specific settings
277
-
278
- Args:
279
- schema_name: Name of schema to use
280
- agent: Agent identifier
281
- project_id: Project identifier
282
- storage: Optional storage backend
283
- scope_restrictions: Categories agent cannot learn from
284
- **config: Additional ALMA configuration
285
-
286
- Returns:
287
- Configured ALMA instance for the agent
288
- """
289
- schema = self.get_schema(schema_name)
290
- if not schema:
291
- raise ValueError(f"Unknown schema: {schema_name}")
292
-
293
- # Apply scope restrictions
294
- if scope_restrictions:
295
- for cat in scope_restrictions:
296
- schema.add_excluded_category(cat)
297
-
298
- # Create ALMA with agent config
299
- alma = self.create_alma(
300
- schema=schema,
301
- project_id=project_id,
302
- storage=storage,
303
- default_agent=agent,
304
- **config,
305
- )
306
-
307
- return alma
308
-
309
-
310
- # Convenience functions for common patterns
311
- def create_coding_alma(
312
- project_id: str,
313
- agent: str = "developer",
314
- storage: Optional[Any] = None,
315
- **config,
316
- ) -> Any:
317
- """Create ALMA configured for coding workflows."""
318
- factory = DomainMemoryFactory()
319
- return factory.create_alma_for_agent(
320
- schema_name="coding",
321
- agent=agent,
322
- project_id=project_id,
323
- storage=storage,
324
- **config,
325
- )
326
-
327
-
328
- def create_research_alma(
329
- project_id: str,
330
- agent: str = "researcher",
331
- storage: Optional[Any] = None,
332
- **config,
333
- ) -> Any:
334
- """Create ALMA configured for research workflows."""
335
- factory = DomainMemoryFactory()
336
- return factory.create_alma_for_agent(
337
- schema_name="research",
338
- agent=agent,
339
- project_id=project_id,
340
- storage=storage,
341
- **config,
342
- )
343
-
344
-
345
- def create_general_alma(
346
- project_id: str,
347
- agent: str = "assistant",
348
- storage: Optional[Any] = None,
349
- **config,
350
- ) -> Any:
351
- """Create ALMA configured for general-purpose agents."""
352
- factory = DomainMemoryFactory()
353
- return factory.create_alma_for_agent(
354
- schema_name="general",
355
- agent=agent,
356
- project_id=project_id,
357
- storage=storage,
358
- **config,
359
- )
1
+ """
2
+ Domain Memory Factory.
3
+
4
+ Factory pattern for creating domain-specific ALMA instances.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ from alma.domains.schemas import (
11
+ get_coding_schema,
12
+ get_content_creation_schema,
13
+ get_customer_support_schema,
14
+ get_general_schema,
15
+ get_research_schema,
16
+ get_sales_schema,
17
+ )
18
+ from alma.domains.types import DomainSchema
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class DomainMemoryFactory:
24
+ """
25
+ Factory for creating domain-specific ALMA instances.
26
+
27
+ Provides:
28
+ - Pre-built schemas for common domains
29
+ - Custom schema creation from config
30
+ - ALMA instance creation with domain-specific settings
31
+
32
+ Usage:
33
+ # Use pre-built schema
34
+ factory = DomainMemoryFactory()
35
+ schema = factory.get_schema("coding")
36
+ alma = factory.create_alma(schema, "my-project", storage)
37
+
38
+ # Create custom schema
39
+ schema = factory.create_schema("my-domain", {
40
+ "description": "Custom domain",
41
+ "entity_types": [...],
42
+ "learning_categories": [...]
43
+ })
44
+ """
45
+
46
+ # Registry of pre-built schemas
47
+ _builtin_schemas: Dict[str, callable] = {
48
+ "coding": get_coding_schema,
49
+ "research": get_research_schema,
50
+ "sales": get_sales_schema,
51
+ "general": get_general_schema,
52
+ "customer_support": get_customer_support_schema,
53
+ "content_creation": get_content_creation_schema,
54
+ }
55
+
56
+ # Custom schema registry (for user-defined schemas)
57
+ _custom_schemas: Dict[str, DomainSchema] = {}
58
+
59
+ def __init__(self):
60
+ """Initialize the factory."""
61
+ pass
62
+
63
+ @classmethod
64
+ def list_schemas(cls) -> List[str]:
65
+ """List all available schema names (built-in and custom)."""
66
+ return list(cls._builtin_schemas.keys()) + list(cls._custom_schemas.keys())
67
+
68
+ @classmethod
69
+ def get_schema(cls, name: str) -> Optional[DomainSchema]:
70
+ """
71
+ Get a schema by name.
72
+
73
+ Args:
74
+ name: Schema name (e.g., "coding", "research")
75
+
76
+ Returns:
77
+ DomainSchema or None if not found
78
+ """
79
+ # Check custom schemas first
80
+ if name in cls._custom_schemas:
81
+ return cls._custom_schemas[name]
82
+
83
+ # Check built-in schemas
84
+ if name in cls._builtin_schemas:
85
+ return cls._builtin_schemas[name]()
86
+
87
+ return None
88
+
89
+ @classmethod
90
+ def create_schema(
91
+ cls,
92
+ name: str,
93
+ config: Dict[str, Any],
94
+ register: bool = True,
95
+ ) -> DomainSchema:
96
+ """
97
+ Create a new domain schema from configuration.
98
+
99
+ Args:
100
+ name: Schema name
101
+ config: Schema configuration dictionary
102
+ register: If True, register schema for later retrieval
103
+
104
+ Config format:
105
+ {
106
+ "description": "Schema description",
107
+ "entity_types": [
108
+ {"name": "entity1", "description": "...", "attributes": ["a", "b"]}
109
+ ],
110
+ "relationship_types": [
111
+ {"name": "rel1", "description": "...", "source": "e1", "target": "e2"}
112
+ ],
113
+ "learning_categories": ["cat1", "cat2"],
114
+ "excluded_categories": ["cat3"],
115
+ "min_occurrences_for_heuristic": 3,
116
+ "confidence_decay_days": 30.0
117
+ }
118
+
119
+ Returns:
120
+ Created DomainSchema
121
+ """
122
+ schema = DomainSchema.create(
123
+ name=name,
124
+ description=config.get("description", f"Custom schema: {name}"),
125
+ learning_categories=config.get("learning_categories", []),
126
+ excluded_categories=config.get("excluded_categories", []),
127
+ min_occurrences_for_heuristic=config.get(
128
+ "min_occurrences_for_heuristic", 3
129
+ ),
130
+ confidence_decay_days=config.get("confidence_decay_days", 30.0),
131
+ )
132
+
133
+ # Add entity types
134
+ for entity_config in config.get("entity_types", []):
135
+ schema.add_entity_type(
136
+ name=entity_config["name"],
137
+ description=entity_config.get("description", ""),
138
+ attributes=entity_config.get("attributes", []),
139
+ )
140
+
141
+ # Add relationship types
142
+ for rel_config in config.get("relationship_types", []):
143
+ schema.add_relationship_type(
144
+ name=rel_config["name"],
145
+ description=rel_config.get("description", ""),
146
+ source_type=rel_config.get("source_type", rel_config.get("source", "")),
147
+ target_type=rel_config.get("target_type", rel_config.get("target", "")),
148
+ )
149
+
150
+ # Validate
151
+ errors = schema.validate()
152
+ if errors:
153
+ logger.warning(f"Schema validation warnings for '{name}': {errors}")
154
+
155
+ # Register if requested
156
+ if register:
157
+ cls._custom_schemas[name] = schema
158
+ logger.info(f"Registered custom schema: {name}")
159
+
160
+ return schema
161
+
162
+ @classmethod
163
+ def register_schema(cls, schema: DomainSchema) -> None:
164
+ """Register an existing schema for later retrieval."""
165
+ cls._custom_schemas[schema.name] = schema
166
+ logger.info(f"Registered schema: {schema.name}")
167
+
168
+ @classmethod
169
+ def unregister_schema(cls, name: str) -> bool:
170
+ """Unregister a custom schema."""
171
+ if name in cls._custom_schemas:
172
+ del cls._custom_schemas[name]
173
+ return True
174
+ return False
175
+
176
+ def create_alma(
177
+ self,
178
+ schema: DomainSchema,
179
+ project_id: str,
180
+ storage: Optional[Any] = None,
181
+ embedding_provider: str = "mock",
182
+ **config,
183
+ ) -> Any:
184
+ """
185
+ Create ALMA instance configured for a domain.
186
+
187
+ Args:
188
+ schema: Domain schema to use
189
+ project_id: Project identifier
190
+ storage: Optional storage backend (FileBasedStorage created if None)
191
+ embedding_provider: Embedding provider ("mock", "local", "openai")
192
+ **config: Additional ALMA configuration
193
+
194
+ Returns:
195
+ Configured ALMA instance
196
+
197
+ Note:
198
+ This integrates with the core ALMA class.
199
+ The schema is used to:
200
+ - Configure allowed learning categories
201
+ - Set heuristic thresholds
202
+ - Initialize domain-specific entity tracking
203
+ """
204
+ # Import here to avoid circular dependency
205
+ from alma import ALMA
206
+ from alma.learning import LearningProtocol
207
+ from alma.retrieval import RetrievalEngine
208
+ from alma.storage.file_based import FileBasedStorage
209
+ from alma.types import MemoryScope
210
+
211
+ # Create storage if not provided
212
+ if storage is None:
213
+ import tempfile
214
+ from pathlib import Path
215
+
216
+ storage_dir = Path(tempfile.mkdtemp()) / ".alma" / project_id
217
+ storage = FileBasedStorage(storage_dir)
218
+
219
+ # Create retrieval engine
220
+ retrieval = RetrievalEngine(
221
+ storage=storage,
222
+ embedding_provider=embedding_provider,
223
+ )
224
+
225
+ # Create scope based on schema
226
+ default_agent = config.get("default_agent", "agent")
227
+ scopes = {
228
+ default_agent: MemoryScope(
229
+ agent_name=default_agent,
230
+ can_learn=schema.learning_categories,
231
+ cannot_learn=schema.excluded_categories,
232
+ min_occurrences_for_heuristic=schema.min_occurrences_for_heuristic,
233
+ )
234
+ }
235
+
236
+ # Create learning protocol
237
+ learning = LearningProtocol(
238
+ storage=storage,
239
+ scopes=scopes,
240
+ )
241
+
242
+ # Create ALMA instance
243
+ alma = ALMA(
244
+ storage=storage,
245
+ retrieval_engine=retrieval,
246
+ learning_protocol=learning,
247
+ scopes=scopes,
248
+ project_id=project_id,
249
+ )
250
+
251
+ # Store schema reference for domain-aware operations
252
+ alma._domain_schema = schema
253
+
254
+ logger.info(
255
+ f"Created ALMA instance for project '{project_id}' "
256
+ f"with domain schema '{schema.name}'"
257
+ )
258
+
259
+ return alma
260
+
261
+ def create_alma_for_agent(
262
+ self,
263
+ schema_name: str,
264
+ agent: str,
265
+ project_id: str,
266
+ storage: Optional[Any] = None,
267
+ scope_restrictions: Optional[List[str]] = None,
268
+ **config,
269
+ ) -> Any:
270
+ """
271
+ Create ALMA instance for a specific agent.
272
+
273
+ This is a convenience method that:
274
+ 1. Gets the appropriate schema
275
+ 2. Creates ALMA instance
276
+ 3. Configures agent-specific settings
277
+
278
+ Args:
279
+ schema_name: Name of schema to use
280
+ agent: Agent identifier
281
+ project_id: Project identifier
282
+ storage: Optional storage backend
283
+ scope_restrictions: Categories agent cannot learn from
284
+ **config: Additional ALMA configuration
285
+
286
+ Returns:
287
+ Configured ALMA instance for the agent
288
+ """
289
+ schema = self.get_schema(schema_name)
290
+ if not schema:
291
+ raise ValueError(f"Unknown schema: {schema_name}")
292
+
293
+ # Apply scope restrictions
294
+ if scope_restrictions:
295
+ for cat in scope_restrictions:
296
+ schema.add_excluded_category(cat)
297
+
298
+ # Create ALMA with agent config
299
+ alma = self.create_alma(
300
+ schema=schema,
301
+ project_id=project_id,
302
+ storage=storage,
303
+ default_agent=agent,
304
+ **config,
305
+ )
306
+
307
+ return alma
308
+
309
+
310
+ # Convenience functions for common patterns
311
+ def create_coding_alma(
312
+ project_id: str,
313
+ agent: str = "developer",
314
+ storage: Optional[Any] = None,
315
+ **config,
316
+ ) -> Any:
317
+ """Create ALMA configured for coding workflows."""
318
+ factory = DomainMemoryFactory()
319
+ return factory.create_alma_for_agent(
320
+ schema_name="coding",
321
+ agent=agent,
322
+ project_id=project_id,
323
+ storage=storage,
324
+ **config,
325
+ )
326
+
327
+
328
+ def create_research_alma(
329
+ project_id: str,
330
+ agent: str = "researcher",
331
+ storage: Optional[Any] = None,
332
+ **config,
333
+ ) -> Any:
334
+ """Create ALMA configured for research workflows."""
335
+ factory = DomainMemoryFactory()
336
+ return factory.create_alma_for_agent(
337
+ schema_name="research",
338
+ agent=agent,
339
+ project_id=project_id,
340
+ storage=storage,
341
+ **config,
342
+ )
343
+
344
+
345
+ def create_general_alma(
346
+ project_id: str,
347
+ agent: str = "assistant",
348
+ storage: Optional[Any] = None,
349
+ **config,
350
+ ) -> Any:
351
+ """Create ALMA configured for general-purpose agents."""
352
+ factory = DomainMemoryFactory()
353
+ return factory.create_alma_for_agent(
354
+ schema_name="general",
355
+ agent=agent,
356
+ project_id=project_id,
357
+ storage=storage,
358
+ **config,
359
+ )