alma-memory 0.2.0__py3-none-any.whl → 0.3.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.
@@ -0,0 +1,434 @@
1
+ """
2
+ Pre-built Domain Schemas.
3
+
4
+ Standard domain schemas for common use cases.
5
+ """
6
+
7
+ from alma.domains.types import DomainSchema, EntityType, RelationshipType
8
+
9
+
10
+ def get_coding_schema() -> DomainSchema:
11
+ """
12
+ Pre-built schema for coding workflows.
13
+
14
+ This is the formalized version of ALMA's original Helena/Victor schema.
15
+ Suitable for: Frontend testing, backend testing, general development.
16
+ """
17
+ schema = DomainSchema.create(
18
+ name="coding",
19
+ description="Memory schema for software development workflows",
20
+ learning_categories=[
21
+ "testing_strategies",
22
+ "selector_patterns",
23
+ "api_design_patterns",
24
+ "error_handling",
25
+ "performance_optimization",
26
+ "debugging_techniques",
27
+ "code_review_patterns",
28
+ "refactoring_strategies",
29
+ ],
30
+ )
31
+
32
+ # Entity types
33
+ schema.add_entity_type(
34
+ name="feature",
35
+ description="A software feature or capability",
36
+ attributes=["status", "tests", "files", "priority", "owner"],
37
+ )
38
+ schema.add_entity_type(
39
+ name="bug",
40
+ description="A software defect or issue",
41
+ attributes=["severity", "reproduction_steps", "fix", "status", "root_cause"],
42
+ )
43
+ schema.add_entity_type(
44
+ name="test",
45
+ description="A test case or test suite",
46
+ attributes=["type", "status", "coverage", "flaky", "last_run"],
47
+ )
48
+ schema.add_entity_type(
49
+ name="component",
50
+ description="A code component or module",
51
+ attributes=["path", "type", "dependencies", "tests"],
52
+ )
53
+ schema.add_entity_type(
54
+ name="api_endpoint",
55
+ description="An API endpoint",
56
+ attributes=["method", "path", "request_schema", "response_schema", "auth"],
57
+ )
58
+
59
+ # Relationships
60
+ schema.add_relationship_type(
61
+ name="tests",
62
+ description="Test covers a feature or component",
63
+ source_type="test",
64
+ target_type="feature",
65
+ )
66
+ schema.add_relationship_type(
67
+ name="fixes",
68
+ description="Commit or change fixes a bug",
69
+ source_type="feature",
70
+ target_type="bug",
71
+ )
72
+ schema.add_relationship_type(
73
+ name="depends_on",
74
+ description="Component depends on another component",
75
+ source_type="component",
76
+ target_type="component",
77
+ )
78
+ schema.add_relationship_type(
79
+ name="implements",
80
+ description="Component implements an API endpoint",
81
+ source_type="component",
82
+ target_type="api_endpoint",
83
+ )
84
+
85
+ return schema
86
+
87
+
88
+ def get_research_schema() -> DomainSchema:
89
+ """
90
+ Pre-built schema for research workflows.
91
+
92
+ Suitable for: Literature review, hypothesis testing, academic research.
93
+ """
94
+ schema = DomainSchema.create(
95
+ name="research",
96
+ description="Memory schema for research and academic workflows",
97
+ learning_categories=[
98
+ "literature_review_patterns",
99
+ "methodology_selection",
100
+ "data_analysis_strategies",
101
+ "citation_patterns",
102
+ "hypothesis_formulation",
103
+ "experiment_design",
104
+ "peer_review_patterns",
105
+ "synthesis_techniques",
106
+ ],
107
+ )
108
+
109
+ # Entity types
110
+ schema.add_entity_type(
111
+ name="paper",
112
+ description="An academic paper or article",
113
+ attributes=["title", "authors", "year", "citations", "abstract", "venue", "doi"],
114
+ )
115
+ schema.add_entity_type(
116
+ name="hypothesis",
117
+ description="A research hypothesis",
118
+ attributes=["statement", "confidence", "evidence_for", "evidence_against", "status"],
119
+ )
120
+ schema.add_entity_type(
121
+ name="experiment",
122
+ description="An experiment or study",
123
+ attributes=["method", "results", "conclusions", "status", "sample_size"],
124
+ )
125
+ schema.add_entity_type(
126
+ name="dataset",
127
+ description="A dataset used in research",
128
+ attributes=["name", "size", "format", "source", "license"],
129
+ )
130
+ schema.add_entity_type(
131
+ name="finding",
132
+ description="A research finding or insight",
133
+ attributes=["summary", "significance", "confidence", "supporting_evidence"],
134
+ )
135
+
136
+ # Relationships
137
+ schema.add_relationship_type(
138
+ name="cites",
139
+ description="Paper cites another paper",
140
+ source_type="paper",
141
+ target_type="paper",
142
+ )
143
+ schema.add_relationship_type(
144
+ name="tests",
145
+ description="Experiment tests a hypothesis",
146
+ source_type="experiment",
147
+ target_type="hypothesis",
148
+ )
149
+ schema.add_relationship_type(
150
+ name="uses",
151
+ description="Experiment uses a dataset",
152
+ source_type="experiment",
153
+ target_type="dataset",
154
+ )
155
+ schema.add_relationship_type(
156
+ name="supports",
157
+ description="Finding supports a hypothesis",
158
+ source_type="finding",
159
+ target_type="hypothesis",
160
+ )
161
+
162
+ return schema
163
+
164
+
165
+ def get_sales_schema() -> DomainSchema:
166
+ """
167
+ Pre-built schema for sales workflows.
168
+
169
+ Suitable for: Lead management, customer conversations, deal tracking.
170
+ """
171
+ schema = DomainSchema.create(
172
+ name="sales",
173
+ description="Memory schema for sales and customer engagement workflows",
174
+ learning_categories=[
175
+ "objection_handling",
176
+ "closing_techniques",
177
+ "qualification_patterns",
178
+ "follow_up_timing",
179
+ "value_proposition",
180
+ "discovery_questions",
181
+ "relationship_building",
182
+ "negotiation_strategies",
183
+ ],
184
+ )
185
+
186
+ # Entity types
187
+ schema.add_entity_type(
188
+ name="lead",
189
+ description="A potential customer or prospect",
190
+ attributes=["stage", "value", "next_action", "source", "company", "title"],
191
+ )
192
+ schema.add_entity_type(
193
+ name="objection",
194
+ description="A customer objection or concern",
195
+ attributes=["type", "response", "outcome", "context"],
196
+ )
197
+ schema.add_entity_type(
198
+ name="conversation",
199
+ description="A customer interaction",
200
+ attributes=["channel", "sentiment", "result", "summary", "follow_up"],
201
+ )
202
+ schema.add_entity_type(
203
+ name="deal",
204
+ description="A sales deal or opportunity",
205
+ attributes=["stage", "value", "close_date", "probability", "stakeholders"],
206
+ )
207
+ schema.add_entity_type(
208
+ name="product",
209
+ description="A product or service being sold",
210
+ attributes=["name", "price", "features", "competitors"],
211
+ )
212
+
213
+ # Relationships
214
+ schema.add_relationship_type(
215
+ name="converts_to",
216
+ description="Lead converts to a deal",
217
+ source_type="lead",
218
+ target_type="deal",
219
+ )
220
+ schema.add_relationship_type(
221
+ name="raised",
222
+ description="Lead raised an objection",
223
+ source_type="lead",
224
+ target_type="objection",
225
+ )
226
+ schema.add_relationship_type(
227
+ name="had",
228
+ description="Lead had a conversation",
229
+ source_type="lead",
230
+ target_type="conversation",
231
+ )
232
+ schema.add_relationship_type(
233
+ name="interested_in",
234
+ description="Lead is interested in a product",
235
+ source_type="lead",
236
+ target_type="product",
237
+ )
238
+
239
+ return schema
240
+
241
+
242
+ def get_general_schema() -> DomainSchema:
243
+ """
244
+ Minimal schema for general-purpose agents.
245
+
246
+ This is a flexible schema that can be extended for any domain.
247
+ Suitable for: General assistants, tool-using agents, custom workflows.
248
+ """
249
+ schema = DomainSchema.create(
250
+ name="general",
251
+ description="Minimal, flexible schema for general-purpose agents",
252
+ learning_categories=[
253
+ "task_patterns",
254
+ "error_recovery",
255
+ "tool_usage",
256
+ "efficiency_patterns",
257
+ "user_preferences",
258
+ "context_switching",
259
+ ],
260
+ )
261
+
262
+ # Entity types (minimal but extensible)
263
+ schema.add_entity_type(
264
+ name="task",
265
+ description="A unit of work to be completed",
266
+ attributes=["title", "status", "priority", "category"],
267
+ )
268
+ schema.add_entity_type(
269
+ name="resource",
270
+ description="A resource used or created",
271
+ attributes=["type", "path", "status", "metadata"],
272
+ )
273
+ schema.add_entity_type(
274
+ name="goal",
275
+ description="An objective or target",
276
+ attributes=["description", "status", "deadline", "progress"],
277
+ )
278
+ schema.add_entity_type(
279
+ name="context",
280
+ description="A context or environment state",
281
+ attributes=["name", "state", "active"],
282
+ )
283
+
284
+ # Relationships (minimal)
285
+ schema.add_relationship_type(
286
+ name="achieves",
287
+ description="Task contributes to a goal",
288
+ source_type="task",
289
+ target_type="goal",
290
+ )
291
+ schema.add_relationship_type(
292
+ name="uses",
293
+ description="Task uses a resource",
294
+ source_type="task",
295
+ target_type="resource",
296
+ )
297
+ schema.add_relationship_type(
298
+ name="requires",
299
+ description="Task requires a context",
300
+ source_type="task",
301
+ target_type="context",
302
+ )
303
+
304
+ return schema
305
+
306
+
307
+ def get_customer_support_schema() -> DomainSchema:
308
+ """
309
+ Pre-built schema for customer support workflows.
310
+
311
+ Suitable for: Ticket handling, escalation, knowledge base management.
312
+ """
313
+ schema = DomainSchema.create(
314
+ name="customer_support",
315
+ description="Memory schema for customer support workflows",
316
+ learning_categories=[
317
+ "issue_classification",
318
+ "resolution_patterns",
319
+ "escalation_criteria",
320
+ "customer_sentiment",
321
+ "knowledge_retrieval",
322
+ "follow_up_patterns",
323
+ "edge_case_handling",
324
+ ],
325
+ )
326
+
327
+ # Entity types
328
+ schema.add_entity_type(
329
+ name="ticket",
330
+ description="A customer support ticket",
331
+ attributes=["status", "priority", "category", "customer_id", "resolution"],
332
+ )
333
+ schema.add_entity_type(
334
+ name="article",
335
+ description="A knowledge base article",
336
+ attributes=["title", "content", "category", "views", "helpful_votes"],
337
+ )
338
+ schema.add_entity_type(
339
+ name="customer",
340
+ description="A customer profile",
341
+ attributes=["tier", "history", "sentiment", "preferences"],
342
+ )
343
+ schema.add_entity_type(
344
+ name="issue",
345
+ description="A known issue or problem",
346
+ attributes=["description", "status", "workaround", "affected_customers"],
347
+ )
348
+
349
+ # Relationships
350
+ schema.add_relationship_type(
351
+ name="resolves",
352
+ description="Article resolves a ticket",
353
+ source_type="article",
354
+ target_type="ticket",
355
+ )
356
+ schema.add_relationship_type(
357
+ name="submitted_by",
358
+ description="Ticket submitted by customer",
359
+ source_type="ticket",
360
+ target_type="customer",
361
+ )
362
+ schema.add_relationship_type(
363
+ name="related_to",
364
+ description="Ticket related to a known issue",
365
+ source_type="ticket",
366
+ target_type="issue",
367
+ )
368
+
369
+ return schema
370
+
371
+
372
+ def get_content_creation_schema() -> DomainSchema:
373
+ """
374
+ Pre-built schema for content creation workflows.
375
+
376
+ Suitable for: Blog writing, social media, marketing content.
377
+ """
378
+ schema = DomainSchema.create(
379
+ name="content_creation",
380
+ description="Memory schema for content creation workflows",
381
+ learning_categories=[
382
+ "writing_patterns",
383
+ "engagement_optimization",
384
+ "audience_targeting",
385
+ "seo_strategies",
386
+ "content_formatting",
387
+ "voice_and_tone",
388
+ "visual_content_patterns",
389
+ ],
390
+ )
391
+
392
+ # Entity types
393
+ schema.add_entity_type(
394
+ name="content",
395
+ description="A piece of content",
396
+ attributes=["type", "title", "status", "platform", "performance_metrics"],
397
+ )
398
+ schema.add_entity_type(
399
+ name="audience",
400
+ description="A target audience segment",
401
+ attributes=["name", "demographics", "interests", "pain_points"],
402
+ )
403
+ schema.add_entity_type(
404
+ name="campaign",
405
+ description="A content campaign",
406
+ attributes=["name", "goal", "start_date", "end_date", "budget"],
407
+ )
408
+ schema.add_entity_type(
409
+ name="template",
410
+ description="A content template",
411
+ attributes=["type", "structure", "usage_count", "effectiveness"],
412
+ )
413
+
414
+ # Relationships
415
+ schema.add_relationship_type(
416
+ name="targets",
417
+ description="Content targets an audience",
418
+ source_type="content",
419
+ target_type="audience",
420
+ )
421
+ schema.add_relationship_type(
422
+ name="part_of",
423
+ description="Content is part of a campaign",
424
+ source_type="content",
425
+ target_type="campaign",
426
+ )
427
+ schema.add_relationship_type(
428
+ name="uses",
429
+ description="Content uses a template",
430
+ source_type="content",
431
+ target_type="template",
432
+ )
433
+
434
+ return schema
alma/domains/types.py ADDED
@@ -0,0 +1,268 @@
1
+ """
2
+ Domain Memory Types.
3
+
4
+ Data models for domain-specific memory schemas.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from datetime import datetime, timezone
9
+ from typing import List, Dict, Any, Optional
10
+ import uuid
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(default_factory=list) # ["status", "priority", "owner"]
25
+
26
+ # Optional schema validation
27
+ required_attributes: List[str] = field(default_factory=list)
28
+ attribute_types: Dict[str, str] = field(default_factory=dict) # attr -> "str", "int", "bool"
29
+
30
+ def validate_entity(self, entity: Dict[str, Any]) -> List[str]:
31
+ """Validate an entity instance against this type."""
32
+ errors = []
33
+ for attr in self.required_attributes:
34
+ if attr not in entity:
35
+ errors.append(f"Missing required attribute: {attr}")
36
+ return errors
37
+
38
+
39
+ @dataclass
40
+ class RelationshipType:
41
+ """
42
+ A relationship between entities in a domain.
43
+
44
+ Relationships connect entities (e.g., "feature implements spec").
45
+ """
46
+
47
+ name: str # "implements", "blocks", "supports", "cites"
48
+ description: str
49
+ source_type: str # Entity type name
50
+ target_type: str # Entity type name
51
+
52
+ # Cardinality
53
+ many_to_many: bool = True
54
+ required: bool = False
55
+
56
+
57
+ @dataclass
58
+ class DomainSchema:
59
+ """
60
+ Defines memory structure for a specific domain.
61
+
62
+ A schema describes what entities exist in a domain, how they relate,
63
+ and what learning categories agents can use.
64
+ """
65
+
66
+ id: str
67
+ name: str # "coding", "research", "sales"
68
+ description: str
69
+
70
+ # What entities exist in this domain
71
+ entity_types: List[EntityType] = field(default_factory=list)
72
+
73
+ # What relationships between entities
74
+ relationship_types: List[RelationshipType] = field(default_factory=list)
75
+
76
+ # Learning categories (replaces hardcoded HELENA_CATEGORIES)
77
+ learning_categories: List[str] = field(default_factory=list)
78
+
79
+ # What can agents in this domain NOT learn (scoping)
80
+ excluded_categories: List[str] = field(default_factory=list)
81
+
82
+ # Domain-specific settings
83
+ min_occurrences_for_heuristic: int = 3
84
+ confidence_decay_days: float = 30.0
85
+
86
+ # Timestamps
87
+ created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
88
+ updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
89
+
90
+ # Extensible metadata
91
+ metadata: Dict[str, Any] = field(default_factory=dict)
92
+
93
+ @classmethod
94
+ def create(
95
+ cls,
96
+ name: str,
97
+ description: str,
98
+ learning_categories: Optional[List[str]] = None,
99
+ **kwargs,
100
+ ) -> "DomainSchema":
101
+ """Factory method to create a new domain schema."""
102
+ return cls(
103
+ id=str(uuid.uuid4()),
104
+ name=name,
105
+ description=description,
106
+ learning_categories=learning_categories or [],
107
+ **kwargs,
108
+ )
109
+
110
+ def add_entity_type(
111
+ self,
112
+ name: str,
113
+ description: str,
114
+ attributes: Optional[List[str]] = None,
115
+ ) -> EntityType:
116
+ """Add an entity type to this schema."""
117
+ entity = EntityType(
118
+ name=name,
119
+ description=description,
120
+ attributes=attributes or [],
121
+ )
122
+ self.entity_types.append(entity)
123
+ self.updated_at = datetime.now(timezone.utc)
124
+ return entity
125
+
126
+ def add_relationship_type(
127
+ self,
128
+ name: str,
129
+ description: str,
130
+ source_type: str,
131
+ target_type: str,
132
+ ) -> RelationshipType:
133
+ """Add a relationship type to this schema."""
134
+ rel = RelationshipType(
135
+ name=name,
136
+ description=description,
137
+ source_type=source_type,
138
+ target_type=target_type,
139
+ )
140
+ self.relationship_types.append(rel)
141
+ self.updated_at = datetime.now(timezone.utc)
142
+ return rel
143
+
144
+ def add_learning_category(self, category: str) -> None:
145
+ """Add a learning category."""
146
+ if category not in self.learning_categories:
147
+ self.learning_categories.append(category)
148
+ self.updated_at = datetime.now(timezone.utc)
149
+
150
+ def add_excluded_category(self, category: str) -> None:
151
+ """Add an excluded category (agent cannot learn from this)."""
152
+ if category not in self.excluded_categories:
153
+ self.excluded_categories.append(category)
154
+ self.updated_at = datetime.now(timezone.utc)
155
+
156
+ def get_entity_type(self, name: str) -> Optional[EntityType]:
157
+ """Get entity type by name."""
158
+ for entity in self.entity_types:
159
+ if entity.name == name:
160
+ return entity
161
+ return None
162
+
163
+ def get_relationship_type(self, name: str) -> Optional[RelationshipType]:
164
+ """Get relationship type by name."""
165
+ for rel in self.relationship_types:
166
+ if rel.name == name:
167
+ return rel
168
+ return None
169
+
170
+ def is_category_allowed(self, category: str) -> bool:
171
+ """Check if a learning category is allowed in this domain."""
172
+ if self.learning_categories and category not in self.learning_categories:
173
+ return False
174
+ if category in self.excluded_categories:
175
+ return False
176
+ return True
177
+
178
+ def validate(self) -> List[str]:
179
+ """Validate the schema for consistency."""
180
+ errors = []
181
+
182
+ # Check relationship source/target types exist
183
+ entity_names = {e.name for e in self.entity_types}
184
+ for rel in self.relationship_types:
185
+ if rel.source_type not in entity_names:
186
+ errors.append(
187
+ f"Relationship '{rel.name}' references unknown source type: {rel.source_type}"
188
+ )
189
+ if rel.target_type not in entity_names:
190
+ errors.append(
191
+ f"Relationship '{rel.name}' references unknown target type: {rel.target_type}"
192
+ )
193
+
194
+ # Check for duplicate entity names
195
+ seen_names = set()
196
+ for entity in self.entity_types:
197
+ if entity.name in seen_names:
198
+ errors.append(f"Duplicate entity type name: {entity.name}")
199
+ seen_names.add(entity.name)
200
+
201
+ return errors
202
+
203
+ def to_dict(self) -> Dict[str, Any]:
204
+ """Convert schema to dictionary for serialization."""
205
+ return {
206
+ "id": self.id,
207
+ "name": self.name,
208
+ "description": self.description,
209
+ "entity_types": [
210
+ {
211
+ "name": e.name,
212
+ "description": e.description,
213
+ "attributes": e.attributes,
214
+ }
215
+ for e in self.entity_types
216
+ ],
217
+ "relationship_types": [
218
+ {
219
+ "name": r.name,
220
+ "description": r.description,
221
+ "source_type": r.source_type,
222
+ "target_type": r.target_type,
223
+ }
224
+ for r in self.relationship_types
225
+ ],
226
+ "learning_categories": self.learning_categories,
227
+ "excluded_categories": self.excluded_categories,
228
+ "min_occurrences_for_heuristic": self.min_occurrences_for_heuristic,
229
+ "confidence_decay_days": self.confidence_decay_days,
230
+ "created_at": self.created_at.isoformat(),
231
+ "updated_at": self.updated_at.isoformat(),
232
+ "metadata": self.metadata,
233
+ }
234
+
235
+ @classmethod
236
+ def from_dict(cls, data: Dict[str, Any]) -> "DomainSchema":
237
+ """Create schema from dictionary."""
238
+ entity_types = [
239
+ EntityType(
240
+ name=e["name"],
241
+ description=e["description"],
242
+ attributes=e.get("attributes", []),
243
+ )
244
+ for e in data.get("entity_types", [])
245
+ ]
246
+
247
+ relationship_types = [
248
+ RelationshipType(
249
+ name=r["name"],
250
+ description=r["description"],
251
+ source_type=r["source_type"],
252
+ target_type=r["target_type"],
253
+ )
254
+ for r in data.get("relationship_types", [])
255
+ ]
256
+
257
+ return cls(
258
+ id=data.get("id", str(uuid.uuid4())),
259
+ name=data["name"],
260
+ description=data["description"],
261
+ entity_types=entity_types,
262
+ relationship_types=relationship_types,
263
+ learning_categories=data.get("learning_categories", []),
264
+ excluded_categories=data.get("excluded_categories", []),
265
+ min_occurrences_for_heuristic=data.get("min_occurrences_for_heuristic", 3),
266
+ confidence_decay_days=data.get("confidence_decay_days", 30.0),
267
+ metadata=data.get("metadata", {}),
268
+ )