grai-build 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.
- grai/__init__.py +11 -0
- grai/cli/__init__.py +5 -0
- grai/cli/main.py +2546 -0
- grai/core/__init__.py +1 -0
- grai/core/cache/__init__.py +33 -0
- grai/core/cache/build_cache.py +352 -0
- grai/core/compiler/__init__.py +23 -0
- grai/core/compiler/cypher_compiler.py +426 -0
- grai/core/exporter/__init__.py +13 -0
- grai/core/exporter/ir_exporter.py +343 -0
- grai/core/lineage/__init__.py +42 -0
- grai/core/lineage/lineage_tracker.py +685 -0
- grai/core/loader/__init__.py +21 -0
- grai/core/loader/neo4j_loader.py +514 -0
- grai/core/models.py +344 -0
- grai/core/parser/__init__.py +25 -0
- grai/core/parser/yaml_parser.py +375 -0
- grai/core/validator/__init__.py +25 -0
- grai/core/validator/validator.py +475 -0
- grai/core/visualizer/__init__.py +650 -0
- grai/core/visualizer/visualizer.py +15 -0
- grai/templates/__init__.py +1 -0
- grai_build-0.3.0.dist-info/METADATA +374 -0
- grai_build-0.3.0.dist-info/RECORD +28 -0
- grai_build-0.3.0.dist-info/WHEEL +5 -0
- grai_build-0.3.0.dist-info/entry_points.txt +2 -0
- grai_build-0.3.0.dist-info/licenses/LICENSE +21 -0
- grai_build-0.3.0.dist-info/top_level.txt +1 -0
grai/core/models.py
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core Pydantic models for grai.build.
|
|
3
|
+
|
|
4
|
+
This module defines the data structures for entities, relations, and properties
|
|
5
|
+
that form the foundation of the declarative knowledge graph modeling.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Any, Dict, List, Optional, Union
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PropertyType(str, Enum):
|
|
15
|
+
"""Supported property types for entity and relation attributes."""
|
|
16
|
+
|
|
17
|
+
STRING = "string"
|
|
18
|
+
INTEGER = "integer"
|
|
19
|
+
FLOAT = "float"
|
|
20
|
+
BOOLEAN = "boolean"
|
|
21
|
+
DATE = "date"
|
|
22
|
+
DATETIME = "datetime"
|
|
23
|
+
JSON = "json"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SourceType(str, Enum):
|
|
27
|
+
"""Supported source types for entities and relations."""
|
|
28
|
+
|
|
29
|
+
DATABASE = "database"
|
|
30
|
+
TABLE = "table"
|
|
31
|
+
CSV = "csv"
|
|
32
|
+
JSON = "json"
|
|
33
|
+
PARQUET = "parquet"
|
|
34
|
+
API = "api"
|
|
35
|
+
STREAM = "stream"
|
|
36
|
+
OTHER = "other"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SourceConfig(BaseModel):
|
|
40
|
+
"""
|
|
41
|
+
Configuration for entity/relation data sources.
|
|
42
|
+
|
|
43
|
+
Supports both simple string format (backward compatible) and detailed config.
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
name: Source identifier (e.g., table name, file path, API endpoint).
|
|
47
|
+
type: Type of data source.
|
|
48
|
+
connection: Optional connection string or identifier.
|
|
49
|
+
schema: Optional database schema name.
|
|
50
|
+
database: Optional database name.
|
|
51
|
+
format: Optional data format details.
|
|
52
|
+
metadata: Optional additional source metadata.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
name: str = Field(..., min_length=1, description="Source identifier")
|
|
56
|
+
type: Optional[SourceType] = Field(default=None, description="Source type")
|
|
57
|
+
connection: Optional[str] = Field(default=None, description="Connection identifier")
|
|
58
|
+
db_schema: Optional[str] = Field(default=None, description="Database schema")
|
|
59
|
+
database: Optional[str] = Field(default=None, description="Database name")
|
|
60
|
+
format: Optional[str] = Field(default=None, description="Data format details")
|
|
61
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def from_string(cls, source: str) -> "SourceConfig":
|
|
65
|
+
"""
|
|
66
|
+
Create a SourceConfig from a simple string.
|
|
67
|
+
|
|
68
|
+
Maintains backward compatibility with existing entity definitions.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
source: Simple source string (e.g., "analytics.customers")
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
SourceConfig with inferred type if possible.
|
|
75
|
+
"""
|
|
76
|
+
# Try to infer type from string
|
|
77
|
+
source_type = None
|
|
78
|
+
if "." in source:
|
|
79
|
+
# Likely a database table (schema.table)
|
|
80
|
+
source_type = SourceType.TABLE
|
|
81
|
+
elif source.endswith(".csv"):
|
|
82
|
+
source_type = SourceType.CSV
|
|
83
|
+
elif source.endswith(".json"):
|
|
84
|
+
source_type = SourceType.JSON
|
|
85
|
+
elif source.endswith(".parquet"):
|
|
86
|
+
source_type = SourceType.PARQUET
|
|
87
|
+
elif source.startswith("http://") or source.startswith("https://"):
|
|
88
|
+
source_type = SourceType.API
|
|
89
|
+
|
|
90
|
+
return cls(name=source, type=source_type)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Property(BaseModel):
|
|
94
|
+
"""
|
|
95
|
+
Represents a property (attribute) of an entity or relation.
|
|
96
|
+
|
|
97
|
+
Attributes:
|
|
98
|
+
name: The property name.
|
|
99
|
+
type: The data type of the property.
|
|
100
|
+
required: Whether this property must have a value.
|
|
101
|
+
description: Optional description of the property.
|
|
102
|
+
default: Optional default value for the property.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
name: str = Field(..., min_length=1, description="Property name")
|
|
106
|
+
type: PropertyType = Field(..., description="Property data type")
|
|
107
|
+
required: bool = Field(default=False, description="Whether the property is required")
|
|
108
|
+
description: Optional[str] = Field(default=None, description="Property description")
|
|
109
|
+
default: Optional[Any] = Field(default=None, description="Default value")
|
|
110
|
+
|
|
111
|
+
@field_validator("name")
|
|
112
|
+
@classmethod
|
|
113
|
+
def validate_name(cls, v: str) -> str:
|
|
114
|
+
"""Validate that property name is a valid identifier."""
|
|
115
|
+
if not v.replace("_", "").isalnum():
|
|
116
|
+
raise ValueError(f"Property name must be alphanumeric with underscores: {v}")
|
|
117
|
+
return v
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class Entity(BaseModel):
|
|
121
|
+
"""
|
|
122
|
+
Represents a node/entity in the knowledge graph.
|
|
123
|
+
|
|
124
|
+
Attributes:
|
|
125
|
+
entity: The entity type name (becomes the node label in Neo4j).
|
|
126
|
+
source: The data source - can be a string or SourceConfig object.
|
|
127
|
+
keys: List of property names that uniquely identify this entity.
|
|
128
|
+
properties: List of properties/attributes for this entity.
|
|
129
|
+
description: Optional description of the entity.
|
|
130
|
+
metadata: Optional additional metadata.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
entity: str = Field(..., min_length=1, description="Entity type name")
|
|
134
|
+
source: Union[str, SourceConfig] = Field(..., description="Data source identifier or config")
|
|
135
|
+
keys: List[str] = Field(..., min_length=1, description="Key properties for uniqueness")
|
|
136
|
+
properties: List[Property] = Field(
|
|
137
|
+
default_factory=list, description="Entity properties/attributes"
|
|
138
|
+
)
|
|
139
|
+
description: Optional[str] = Field(default=None, description="Entity description")
|
|
140
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
141
|
+
|
|
142
|
+
@field_validator("source")
|
|
143
|
+
@classmethod
|
|
144
|
+
def validate_source(cls, v: Union[str, SourceConfig]) -> SourceConfig:
|
|
145
|
+
"""Convert string source to SourceConfig for consistency."""
|
|
146
|
+
if isinstance(v, str):
|
|
147
|
+
return SourceConfig.from_string(v)
|
|
148
|
+
return v
|
|
149
|
+
|
|
150
|
+
@field_validator("entity")
|
|
151
|
+
@classmethod
|
|
152
|
+
def validate_entity_name(cls, v: str) -> str:
|
|
153
|
+
"""Validate that entity name is a valid identifier."""
|
|
154
|
+
if not v.replace("_", "").isalnum():
|
|
155
|
+
raise ValueError(f"Entity name must be alphanumeric with underscores: {v}")
|
|
156
|
+
return v
|
|
157
|
+
|
|
158
|
+
@field_validator("keys")
|
|
159
|
+
@classmethod
|
|
160
|
+
def validate_keys(cls, v: List[str]) -> List[str]:
|
|
161
|
+
"""Validate that all keys are non-empty."""
|
|
162
|
+
if not all(k.strip() for k in v):
|
|
163
|
+
raise ValueError("All keys must be non-empty strings")
|
|
164
|
+
return v
|
|
165
|
+
|
|
166
|
+
def get_property(self, name: str) -> Optional[Property]:
|
|
167
|
+
"""
|
|
168
|
+
Get a property by name.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
name: The property name to look up.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
The Property object if found, None otherwise.
|
|
175
|
+
"""
|
|
176
|
+
return next((p for p in self.properties if p.name == name), None)
|
|
177
|
+
|
|
178
|
+
def get_key_properties(self) -> List[Property]:
|
|
179
|
+
"""
|
|
180
|
+
Get all properties that are designated as keys.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
List of Property objects that are keys.
|
|
184
|
+
"""
|
|
185
|
+
return [p for p in self.properties if p.name in self.keys]
|
|
186
|
+
|
|
187
|
+
def get_source_name(self) -> str:
|
|
188
|
+
"""
|
|
189
|
+
Get the source name as a string.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Source name string.
|
|
193
|
+
"""
|
|
194
|
+
if isinstance(self.source, SourceConfig):
|
|
195
|
+
return self.source.name
|
|
196
|
+
return str(self.source)
|
|
197
|
+
|
|
198
|
+
def get_source_config(self) -> SourceConfig:
|
|
199
|
+
"""
|
|
200
|
+
Get the full source configuration.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
SourceConfig object.
|
|
204
|
+
"""
|
|
205
|
+
if isinstance(self.source, SourceConfig):
|
|
206
|
+
return self.source
|
|
207
|
+
return SourceConfig.from_string(str(self.source))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class RelationMapping(BaseModel):
|
|
211
|
+
"""
|
|
212
|
+
Defines how entities are connected in a relation.
|
|
213
|
+
|
|
214
|
+
Attributes:
|
|
215
|
+
from_key: The key property name on the source entity.
|
|
216
|
+
to_key: The key property name on the target entity.
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
from_key: str = Field(..., min_length=1, description="Source entity key property")
|
|
220
|
+
to_key: str = Field(..., min_length=1, description="Target entity key property")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class Relation(BaseModel):
|
|
224
|
+
"""
|
|
225
|
+
Represents an edge/relation in the knowledge graph.
|
|
226
|
+
|
|
227
|
+
Attributes:
|
|
228
|
+
relation: The relation type name (becomes the edge label in Neo4j).
|
|
229
|
+
from_entity: The source entity type.
|
|
230
|
+
to_entity: The target entity type.
|
|
231
|
+
source: The data source - can be a string or SourceConfig object.
|
|
232
|
+
mappings: How source and target entities connect via keys.
|
|
233
|
+
properties: List of properties/attributes for this relation.
|
|
234
|
+
description: Optional description of the relation.
|
|
235
|
+
metadata: Optional additional metadata.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
relation: str = Field(..., min_length=1, description="Relation type name")
|
|
239
|
+
from_entity: str = Field(..., min_length=1, alias="from", description="Source entity type")
|
|
240
|
+
to_entity: str = Field(..., min_length=1, alias="to", description="Target entity type")
|
|
241
|
+
source: Union[str, SourceConfig] = Field(..., description="Data source identifier or config")
|
|
242
|
+
mappings: RelationMapping = Field(..., description="Key mappings between entities")
|
|
243
|
+
properties: List[Property] = Field(
|
|
244
|
+
default_factory=list, description="Relation properties/attributes"
|
|
245
|
+
)
|
|
246
|
+
description: Optional[str] = Field(default=None, description="Relation description")
|
|
247
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
248
|
+
|
|
249
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
250
|
+
|
|
251
|
+
@field_validator("source")
|
|
252
|
+
@classmethod
|
|
253
|
+
def validate_source(cls, v: Union[str, SourceConfig]) -> SourceConfig:
|
|
254
|
+
"""Convert string source to SourceConfig for consistency."""
|
|
255
|
+
if isinstance(v, str):
|
|
256
|
+
return SourceConfig.from_string(v)
|
|
257
|
+
return v
|
|
258
|
+
|
|
259
|
+
@field_validator("relation")
|
|
260
|
+
@classmethod
|
|
261
|
+
def validate_relation_name(cls, v: str) -> str:
|
|
262
|
+
"""Validate that relation name is uppercase and valid."""
|
|
263
|
+
if not v.isupper():
|
|
264
|
+
raise ValueError(f"Relation name should be uppercase: {v}")
|
|
265
|
+
if not v.replace("_", "").isalnum():
|
|
266
|
+
raise ValueError(f"Relation name must be alphanumeric with underscores: {v}")
|
|
267
|
+
return v
|
|
268
|
+
|
|
269
|
+
def get_property(self, name: str) -> Optional[Property]:
|
|
270
|
+
"""
|
|
271
|
+
Get a property by name.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
name: The property name to look up.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
The Property object if found, None otherwise.
|
|
278
|
+
"""
|
|
279
|
+
return next((p for p in self.properties if p.name == name), None)
|
|
280
|
+
|
|
281
|
+
def get_source_name(self) -> str:
|
|
282
|
+
"""
|
|
283
|
+
Get the source name as a string.
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
Source name string.
|
|
287
|
+
"""
|
|
288
|
+
if isinstance(self.source, SourceConfig):
|
|
289
|
+
return self.source.name
|
|
290
|
+
return str(self.source)
|
|
291
|
+
|
|
292
|
+
def get_source_config(self) -> SourceConfig:
|
|
293
|
+
"""
|
|
294
|
+
Get the full source configuration.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
SourceConfig object.
|
|
298
|
+
"""
|
|
299
|
+
if isinstance(self.source, SourceConfig):
|
|
300
|
+
return self.source
|
|
301
|
+
return SourceConfig.from_string(str(self.source))
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class Project(BaseModel):
|
|
305
|
+
"""
|
|
306
|
+
Represents a complete grai.build project configuration.
|
|
307
|
+
|
|
308
|
+
Attributes:
|
|
309
|
+
name: The project name.
|
|
310
|
+
version: The project version.
|
|
311
|
+
entities: List of entity definitions in the project.
|
|
312
|
+
relations: List of relation definitions in the project.
|
|
313
|
+
config: Optional project-level configuration.
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
name: str = Field(..., min_length=1, description="Project name")
|
|
317
|
+
version: str = Field(default="1.0.0", description="Project version")
|
|
318
|
+
entities: List[Entity] = Field(default_factory=list, description="Entity definitions")
|
|
319
|
+
relations: List[Relation] = Field(default_factory=list, description="Relation definitions")
|
|
320
|
+
config: Dict[str, Any] = Field(default_factory=dict, description="Project configuration")
|
|
321
|
+
|
|
322
|
+
def get_entity(self, name: str) -> Optional[Entity]:
|
|
323
|
+
"""
|
|
324
|
+
Get an entity by name.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
name: The entity name to look up.
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
The Entity object if found, None otherwise.
|
|
331
|
+
"""
|
|
332
|
+
return next((e for e in self.entities if e.entity == name), None)
|
|
333
|
+
|
|
334
|
+
def get_relation(self, name: str) -> Optional[Relation]:
|
|
335
|
+
"""
|
|
336
|
+
Get a relation by name.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
name: The relation name to look up.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
The Relation object if found, None otherwise.
|
|
343
|
+
"""
|
|
344
|
+
return next((r for r in self.relations if r.relation == name), None)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Parser module for loading YAML definitions into Pydantic models."""
|
|
2
|
+
|
|
3
|
+
from grai.core.parser.yaml_parser import (
|
|
4
|
+
ParserError,
|
|
5
|
+
ValidationParserError,
|
|
6
|
+
YAMLParseError,
|
|
7
|
+
load_entities_from_directory,
|
|
8
|
+
load_project,
|
|
9
|
+
load_project_manifest,
|
|
10
|
+
load_relations_from_directory,
|
|
11
|
+
parse_entity_file,
|
|
12
|
+
parse_relation_file,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"ParserError",
|
|
17
|
+
"YAMLParseError",
|
|
18
|
+
"ValidationParserError",
|
|
19
|
+
"parse_entity_file",
|
|
20
|
+
"parse_relation_file",
|
|
21
|
+
"load_entities_from_directory",
|
|
22
|
+
"load_relations_from_directory",
|
|
23
|
+
"load_project_manifest",
|
|
24
|
+
"load_project",
|
|
25
|
+
]
|