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.
- alma/__init__.py +296 -226
- alma/compression/__init__.py +33 -0
- alma/compression/pipeline.py +980 -0
- alma/confidence/__init__.py +47 -47
- alma/confidence/engine.py +540 -540
- alma/confidence/types.py +351 -351
- alma/config/loader.py +157 -157
- alma/consolidation/__init__.py +23 -23
- alma/consolidation/engine.py +678 -678
- alma/consolidation/prompts.py +84 -84
- alma/core.py +1189 -430
- alma/domains/__init__.py +30 -30
- alma/domains/factory.py +359 -359
- alma/domains/schemas.py +448 -448
- alma/domains/types.py +272 -272
- alma/events/__init__.py +75 -75
- alma/events/emitter.py +285 -284
- alma/events/storage_mixin.py +246 -246
- alma/events/types.py +126 -126
- alma/events/webhook.py +425 -425
- alma/exceptions.py +49 -49
- alma/extraction/__init__.py +31 -31
- alma/extraction/auto_learner.py +265 -265
- alma/extraction/extractor.py +420 -420
- alma/graph/__init__.py +106 -106
- alma/graph/backends/__init__.py +32 -32
- alma/graph/backends/kuzu.py +624 -624
- alma/graph/backends/memgraph.py +432 -432
- alma/graph/backends/memory.py +236 -236
- alma/graph/backends/neo4j.py +417 -417
- alma/graph/base.py +159 -159
- alma/graph/extraction.py +198 -198
- alma/graph/store.py +860 -860
- alma/harness/__init__.py +35 -35
- alma/harness/base.py +386 -386
- alma/harness/domains.py +705 -705
- alma/initializer/__init__.py +37 -37
- alma/initializer/initializer.py +418 -418
- alma/initializer/types.py +250 -250
- alma/integration/__init__.py +62 -62
- alma/integration/claude_agents.py +444 -444
- alma/integration/helena.py +423 -423
- alma/integration/victor.py +471 -471
- alma/learning/__init__.py +101 -86
- alma/learning/decay.py +878 -0
- alma/learning/forgetting.py +1446 -1446
- alma/learning/heuristic_extractor.py +390 -390
- alma/learning/protocols.py +374 -374
- alma/learning/validation.py +346 -346
- alma/mcp/__init__.py +123 -45
- alma/mcp/__main__.py +156 -156
- alma/mcp/resources.py +122 -122
- alma/mcp/server.py +955 -591
- alma/mcp/tools.py +3254 -509
- alma/observability/__init__.py +91 -84
- alma/observability/config.py +302 -302
- alma/observability/guidelines.py +170 -0
- alma/observability/logging.py +424 -424
- alma/observability/metrics.py +583 -583
- alma/observability/tracing.py +440 -440
- alma/progress/__init__.py +21 -21
- alma/progress/tracker.py +607 -607
- alma/progress/types.py +250 -250
- alma/retrieval/__init__.py +134 -53
- alma/retrieval/budget.py +525 -0
- alma/retrieval/cache.py +1304 -1061
- alma/retrieval/embeddings.py +202 -202
- alma/retrieval/engine.py +850 -427
- alma/retrieval/modes.py +365 -0
- alma/retrieval/progressive.py +560 -0
- alma/retrieval/scoring.py +344 -344
- alma/retrieval/trust_scoring.py +637 -0
- alma/retrieval/verification.py +797 -0
- alma/session/__init__.py +19 -19
- alma/session/manager.py +442 -399
- alma/session/types.py +288 -288
- alma/storage/__init__.py +101 -90
- alma/storage/archive.py +233 -0
- alma/storage/azure_cosmos.py +1259 -1259
- alma/storage/base.py +1083 -583
- alma/storage/chroma.py +1443 -1443
- alma/storage/constants.py +103 -103
- alma/storage/file_based.py +614 -614
- alma/storage/migrations/__init__.py +21 -21
- alma/storage/migrations/base.py +321 -321
- alma/storage/migrations/runner.py +323 -323
- alma/storage/migrations/version_stores.py +337 -337
- alma/storage/migrations/versions/__init__.py +11 -11
- alma/storage/migrations/versions/v1_0_0.py +373 -373
- alma/storage/migrations/versions/v1_1_0_workflow_context.py +551 -0
- alma/storage/pinecone.py +1080 -1080
- alma/storage/postgresql.py +1948 -1559
- alma/storage/qdrant.py +1306 -1306
- alma/storage/sqlite_local.py +3041 -1457
- alma/testing/__init__.py +46 -46
- alma/testing/factories.py +301 -301
- alma/testing/mocks.py +389 -389
- alma/types.py +292 -264
- alma/utils/__init__.py +19 -0
- alma/utils/tokenizer.py +521 -0
- alma/workflow/__init__.py +83 -0
- alma/workflow/artifacts.py +170 -0
- alma/workflow/checkpoint.py +311 -0
- alma/workflow/context.py +228 -0
- alma/workflow/outcomes.py +189 -0
- alma/workflow/reducers.py +393 -0
- {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/METADATA +210 -72
- alma_memory-0.7.0.dist-info/RECORD +112 -0
- alma_memory-0.5.1.dist-info/RECORD +0 -93
- {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/WHEEL +0 -0
- {alma_memory-0.5.1.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
|
+
)
|