aiecs 1.5.1__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 (302) hide show
  1. aiecs/__init__.py +72 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +469 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +363 -0
  7. aiecs/application/knowledge_graph/__init__.py +7 -0
  8. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
  11. aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
  12. aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
  13. aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
  14. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  15. aiecs/application/knowledge_graph/extractors/base.py +100 -0
  16. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
  17. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
  18. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
  19. aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
  20. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
  21. aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
  22. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
  23. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
  24. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  25. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
  26. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
  27. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  28. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
  29. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  30. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  31. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
  32. aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
  33. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
  34. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  35. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  36. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
  37. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
  38. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
  39. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
  40. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
  41. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
  42. aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
  43. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
  44. aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
  45. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
  46. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  47. aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
  48. aiecs/application/knowledge_graph/search/reranker.py +295 -0
  49. aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
  50. aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
  51. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  52. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
  53. aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
  54. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  55. aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
  56. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  57. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
  58. aiecs/common/__init__.py +9 -0
  59. aiecs/common/knowledge_graph/__init__.py +17 -0
  60. aiecs/common/knowledge_graph/runnable.py +484 -0
  61. aiecs/config/__init__.py +16 -0
  62. aiecs/config/config.py +498 -0
  63. aiecs/config/graph_config.py +137 -0
  64. aiecs/config/registry.py +23 -0
  65. aiecs/core/__init__.py +46 -0
  66. aiecs/core/interface/__init__.py +34 -0
  67. aiecs/core/interface/execution_interface.py +152 -0
  68. aiecs/core/interface/storage_interface.py +171 -0
  69. aiecs/domain/__init__.py +289 -0
  70. aiecs/domain/agent/__init__.py +189 -0
  71. aiecs/domain/agent/base_agent.py +697 -0
  72. aiecs/domain/agent/exceptions.py +103 -0
  73. aiecs/domain/agent/graph_aware_mixin.py +559 -0
  74. aiecs/domain/agent/hybrid_agent.py +490 -0
  75. aiecs/domain/agent/integration/__init__.py +26 -0
  76. aiecs/domain/agent/integration/context_compressor.py +222 -0
  77. aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
  78. aiecs/domain/agent/integration/retry_policy.py +219 -0
  79. aiecs/domain/agent/integration/role_config.py +213 -0
  80. aiecs/domain/agent/knowledge_aware_agent.py +646 -0
  81. aiecs/domain/agent/lifecycle.py +296 -0
  82. aiecs/domain/agent/llm_agent.py +300 -0
  83. aiecs/domain/agent/memory/__init__.py +12 -0
  84. aiecs/domain/agent/memory/conversation.py +197 -0
  85. aiecs/domain/agent/migration/__init__.py +14 -0
  86. aiecs/domain/agent/migration/conversion.py +160 -0
  87. aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
  88. aiecs/domain/agent/models.py +317 -0
  89. aiecs/domain/agent/observability.py +407 -0
  90. aiecs/domain/agent/persistence.py +289 -0
  91. aiecs/domain/agent/prompts/__init__.py +29 -0
  92. aiecs/domain/agent/prompts/builder.py +161 -0
  93. aiecs/domain/agent/prompts/formatters.py +189 -0
  94. aiecs/domain/agent/prompts/template.py +255 -0
  95. aiecs/domain/agent/registry.py +260 -0
  96. aiecs/domain/agent/tool_agent.py +257 -0
  97. aiecs/domain/agent/tools/__init__.py +12 -0
  98. aiecs/domain/agent/tools/schema_generator.py +221 -0
  99. aiecs/domain/community/__init__.py +155 -0
  100. aiecs/domain/community/agent_adapter.py +477 -0
  101. aiecs/domain/community/analytics.py +481 -0
  102. aiecs/domain/community/collaborative_workflow.py +642 -0
  103. aiecs/domain/community/communication_hub.py +645 -0
  104. aiecs/domain/community/community_builder.py +320 -0
  105. aiecs/domain/community/community_integration.py +800 -0
  106. aiecs/domain/community/community_manager.py +813 -0
  107. aiecs/domain/community/decision_engine.py +879 -0
  108. aiecs/domain/community/exceptions.py +225 -0
  109. aiecs/domain/community/models/__init__.py +33 -0
  110. aiecs/domain/community/models/community_models.py +268 -0
  111. aiecs/domain/community/resource_manager.py +457 -0
  112. aiecs/domain/community/shared_context_manager.py +603 -0
  113. aiecs/domain/context/__init__.py +58 -0
  114. aiecs/domain/context/context_engine.py +989 -0
  115. aiecs/domain/context/conversation_models.py +354 -0
  116. aiecs/domain/context/graph_memory.py +467 -0
  117. aiecs/domain/execution/__init__.py +12 -0
  118. aiecs/domain/execution/model.py +57 -0
  119. aiecs/domain/knowledge_graph/__init__.py +19 -0
  120. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  121. aiecs/domain/knowledge_graph/models/entity.py +130 -0
  122. aiecs/domain/knowledge_graph/models/evidence.py +194 -0
  123. aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
  124. aiecs/domain/knowledge_graph/models/path.py +179 -0
  125. aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
  126. aiecs/domain/knowledge_graph/models/query.py +272 -0
  127. aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
  128. aiecs/domain/knowledge_graph/models/relation.py +136 -0
  129. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  130. aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
  131. aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
  132. aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
  133. aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
  134. aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
  135. aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
  136. aiecs/domain/task/__init__.py +13 -0
  137. aiecs/domain/task/dsl_processor.py +613 -0
  138. aiecs/domain/task/model.py +62 -0
  139. aiecs/domain/task/task_context.py +268 -0
  140. aiecs/infrastructure/__init__.py +24 -0
  141. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  142. aiecs/infrastructure/graph_storage/base.py +601 -0
  143. aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
  144. aiecs/infrastructure/graph_storage/cache.py +429 -0
  145. aiecs/infrastructure/graph_storage/distributed.py +226 -0
  146. aiecs/infrastructure/graph_storage/error_handling.py +390 -0
  147. aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
  148. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  149. aiecs/infrastructure/graph_storage/in_memory.py +514 -0
  150. aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
  151. aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
  152. aiecs/infrastructure/graph_storage/metrics.py +357 -0
  153. aiecs/infrastructure/graph_storage/migration.py +413 -0
  154. aiecs/infrastructure/graph_storage/pagination.py +471 -0
  155. aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
  156. aiecs/infrastructure/graph_storage/postgres.py +871 -0
  157. aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
  158. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  159. aiecs/infrastructure/graph_storage/sqlite.py +623 -0
  160. aiecs/infrastructure/graph_storage/streaming.py +495 -0
  161. aiecs/infrastructure/messaging/__init__.py +13 -0
  162. aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
  163. aiecs/infrastructure/messaging/websocket_manager.py +298 -0
  164. aiecs/infrastructure/monitoring/__init__.py +34 -0
  165. aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
  166. aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
  167. aiecs/infrastructure/monitoring/structured_logger.py +48 -0
  168. aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
  169. aiecs/infrastructure/persistence/__init__.py +24 -0
  170. aiecs/infrastructure/persistence/context_engine_client.py +187 -0
  171. aiecs/infrastructure/persistence/database_manager.py +333 -0
  172. aiecs/infrastructure/persistence/file_storage.py +754 -0
  173. aiecs/infrastructure/persistence/redis_client.py +220 -0
  174. aiecs/llm/__init__.py +86 -0
  175. aiecs/llm/callbacks/__init__.py +11 -0
  176. aiecs/llm/callbacks/custom_callbacks.py +264 -0
  177. aiecs/llm/client_factory.py +420 -0
  178. aiecs/llm/clients/__init__.py +33 -0
  179. aiecs/llm/clients/base_client.py +193 -0
  180. aiecs/llm/clients/googleai_client.py +181 -0
  181. aiecs/llm/clients/openai_client.py +131 -0
  182. aiecs/llm/clients/vertex_client.py +437 -0
  183. aiecs/llm/clients/xai_client.py +184 -0
  184. aiecs/llm/config/__init__.py +51 -0
  185. aiecs/llm/config/config_loader.py +275 -0
  186. aiecs/llm/config/config_validator.py +236 -0
  187. aiecs/llm/config/model_config.py +151 -0
  188. aiecs/llm/utils/__init__.py +10 -0
  189. aiecs/llm/utils/validate_config.py +91 -0
  190. aiecs/main.py +363 -0
  191. aiecs/scripts/__init__.py +3 -0
  192. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  193. aiecs/scripts/aid/__init__.py +19 -0
  194. aiecs/scripts/aid/version_manager.py +215 -0
  195. aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
  196. aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
  197. aiecs/scripts/dependance_check/__init__.py +17 -0
  198. aiecs/scripts/dependance_check/dependency_checker.py +938 -0
  199. aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
  200. aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
  201. aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
  202. aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
  203. aiecs/scripts/dependance_patch/__init__.py +7 -0
  204. aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
  205. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  206. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
  207. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
  208. aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
  209. aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
  210. aiecs/scripts/tools_develop/README.md +449 -0
  211. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  212. aiecs/scripts/tools_develop/__init__.py +21 -0
  213. aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
  214. aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
  215. aiecs/scripts/tools_develop/verify_tools.py +356 -0
  216. aiecs/tasks/__init__.py +1 -0
  217. aiecs/tasks/worker.py +172 -0
  218. aiecs/tools/__init__.py +299 -0
  219. aiecs/tools/apisource/__init__.py +99 -0
  220. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  221. aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
  222. aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
  223. aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
  224. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  225. aiecs/tools/apisource/monitoring/metrics.py +303 -0
  226. aiecs/tools/apisource/providers/__init__.py +115 -0
  227. aiecs/tools/apisource/providers/base.py +664 -0
  228. aiecs/tools/apisource/providers/census.py +401 -0
  229. aiecs/tools/apisource/providers/fred.py +564 -0
  230. aiecs/tools/apisource/providers/newsapi.py +412 -0
  231. aiecs/tools/apisource/providers/worldbank.py +357 -0
  232. aiecs/tools/apisource/reliability/__init__.py +12 -0
  233. aiecs/tools/apisource/reliability/error_handler.py +375 -0
  234. aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
  235. aiecs/tools/apisource/tool.py +850 -0
  236. aiecs/tools/apisource/utils/__init__.py +9 -0
  237. aiecs/tools/apisource/utils/validators.py +338 -0
  238. aiecs/tools/base_tool.py +201 -0
  239. aiecs/tools/docs/__init__.py +121 -0
  240. aiecs/tools/docs/ai_document_orchestrator.py +599 -0
  241. aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
  242. aiecs/tools/docs/content_insertion_tool.py +1333 -0
  243. aiecs/tools/docs/document_creator_tool.py +1317 -0
  244. aiecs/tools/docs/document_layout_tool.py +1166 -0
  245. aiecs/tools/docs/document_parser_tool.py +994 -0
  246. aiecs/tools/docs/document_writer_tool.py +1818 -0
  247. aiecs/tools/knowledge_graph/__init__.py +17 -0
  248. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
  249. aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
  250. aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
  251. aiecs/tools/langchain_adapter.py +542 -0
  252. aiecs/tools/schema_generator.py +275 -0
  253. aiecs/tools/search_tool/__init__.py +100 -0
  254. aiecs/tools/search_tool/analyzers.py +589 -0
  255. aiecs/tools/search_tool/cache.py +260 -0
  256. aiecs/tools/search_tool/constants.py +128 -0
  257. aiecs/tools/search_tool/context.py +216 -0
  258. aiecs/tools/search_tool/core.py +749 -0
  259. aiecs/tools/search_tool/deduplicator.py +123 -0
  260. aiecs/tools/search_tool/error_handler.py +271 -0
  261. aiecs/tools/search_tool/metrics.py +371 -0
  262. aiecs/tools/search_tool/rate_limiter.py +178 -0
  263. aiecs/tools/search_tool/schemas.py +277 -0
  264. aiecs/tools/statistics/__init__.py +80 -0
  265. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
  266. aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
  267. aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
  268. aiecs/tools/statistics/data_loader_tool.py +564 -0
  269. aiecs/tools/statistics/data_profiler_tool.py +658 -0
  270. aiecs/tools/statistics/data_transformer_tool.py +573 -0
  271. aiecs/tools/statistics/data_visualizer_tool.py +495 -0
  272. aiecs/tools/statistics/model_trainer_tool.py +487 -0
  273. aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
  274. aiecs/tools/task_tools/__init__.py +86 -0
  275. aiecs/tools/task_tools/chart_tool.py +732 -0
  276. aiecs/tools/task_tools/classfire_tool.py +922 -0
  277. aiecs/tools/task_tools/image_tool.py +447 -0
  278. aiecs/tools/task_tools/office_tool.py +684 -0
  279. aiecs/tools/task_tools/pandas_tool.py +635 -0
  280. aiecs/tools/task_tools/report_tool.py +635 -0
  281. aiecs/tools/task_tools/research_tool.py +392 -0
  282. aiecs/tools/task_tools/scraper_tool.py +715 -0
  283. aiecs/tools/task_tools/stats_tool.py +688 -0
  284. aiecs/tools/temp_file_manager.py +130 -0
  285. aiecs/tools/tool_executor/__init__.py +37 -0
  286. aiecs/tools/tool_executor/tool_executor.py +881 -0
  287. aiecs/utils/LLM_output_structor.py +445 -0
  288. aiecs/utils/__init__.py +34 -0
  289. aiecs/utils/base_callback.py +47 -0
  290. aiecs/utils/cache_provider.py +695 -0
  291. aiecs/utils/execution_utils.py +184 -0
  292. aiecs/utils/logging.py +1 -0
  293. aiecs/utils/prompt_loader.py +14 -0
  294. aiecs/utils/token_usage_repository.py +323 -0
  295. aiecs/ws/__init__.py +0 -0
  296. aiecs/ws/socket_server.py +52 -0
  297. aiecs-1.5.1.dist-info/METADATA +608 -0
  298. aiecs-1.5.1.dist-info/RECORD +302 -0
  299. aiecs-1.5.1.dist-info/WHEEL +5 -0
  300. aiecs-1.5.1.dist-info/entry_points.txt +10 -0
  301. aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
  302. aiecs-1.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,654 @@
1
+ """
2
+ AST Validator for Logic Query Parser
3
+
4
+ This module provides comprehensive validation of AST nodes against the knowledge graph schema.
5
+ It validates entity types, properties, relation types, property types, and variable references.
6
+
7
+ Design Principles:
8
+ 1. Schema-aware validation (entity types, properties, relations)
9
+ 2. Error accumulation (collect all errors, don't stop at first)
10
+ 3. Helpful error messages with suggestions
11
+ 4. Type checking (property values match expected types)
12
+
13
+ Phase: 2.4 - Logic Query Parser
14
+ Task: 3.1 - Implement AST Validator
15
+ Version: 1.0
16
+ """
17
+
18
+ from typing import Any, List, Optional, Set
19
+ from .ast_nodes import (
20
+ ValidationError,
21
+ ASTNode,
22
+ QueryNode,
23
+ FindNode,
24
+ TraversalNode,
25
+ PropertyFilterNode,
26
+ BooleanFilterNode,
27
+ )
28
+
29
+
30
+ class ASTValidator:
31
+ """
32
+ AST Validator with schema integration
33
+
34
+ This class provides comprehensive validation of AST nodes against the
35
+ knowledge graph schema. It validates:
36
+ - Entity types exist in schema
37
+ - Properties exist in entity schema
38
+ - Relation types exist in schema
39
+ - Property values match expected types
40
+ - Relation endpoints match entity types
41
+ - Variable references are defined before use
42
+
43
+ The validator accumulates all errors instead of stopping at the first error.
44
+
45
+ Example:
46
+ ```python
47
+ from aiecs.domain.knowledge_graph.schema import SchemaManager
48
+
49
+ schema = SchemaManager.load("schema.json")
50
+ validator = ASTValidator(schema)
51
+
52
+ errors = validator.validate(ast_node)
53
+ if errors:
54
+ for error in errors:
55
+ print(f"Line {error.line}: {error.message}")
56
+ ```
57
+ """
58
+
59
+ def __init__(self, schema: Any):
60
+ """
61
+ Initialize AST validator
62
+
63
+ Args:
64
+ schema: SchemaManager instance for validation
65
+ """
66
+ self.schema = schema
67
+ self.current_entity_type: Optional[str] = None
68
+ self.defined_variables: Set[str] = set()
69
+
70
+ # ========================================================================
71
+ # Main Validation Entry Point
72
+ # ========================================================================
73
+
74
+ def validate(self, node: ASTNode) -> List[ValidationError]:
75
+ """
76
+ Validate an AST node and all its children
77
+
78
+ This is the main entry point for validation. It accumulates all
79
+ errors from the node and its children.
80
+
81
+ Args:
82
+ node: AST node to validate
83
+
84
+ Returns:
85
+ List of validation errors (empty if valid)
86
+ """
87
+ errors = []
88
+
89
+ # Dispatch to specific validation method based on node type
90
+ if isinstance(node, QueryNode):
91
+ errors.extend(self.validate_query_node(node))
92
+ elif isinstance(node, FindNode):
93
+ errors.extend(self.validate_find_node(node))
94
+ elif isinstance(node, TraversalNode):
95
+ errors.extend(self.validate_traversal_node(node))
96
+ elif isinstance(node, PropertyFilterNode):
97
+ errors.extend(self.validate_property_filter_node(node))
98
+ elif isinstance(node, BooleanFilterNode):
99
+ errors.extend(self.validate_boolean_filter_node(node))
100
+ else:
101
+ # Fallback to node's own validate method
102
+ errors.extend(node.validate(self.schema))
103
+
104
+ return errors
105
+
106
+ # ========================================================================
107
+ # Node-Specific Validation Methods
108
+ # ========================================================================
109
+
110
+ def validate_query_node(self, node: QueryNode) -> List[ValidationError]:
111
+ """
112
+ Validate QueryNode
113
+
114
+ Validates:
115
+ - FindNode
116
+ - All TraversalNodes
117
+ - Entity type consistency across traversals
118
+
119
+ Args:
120
+ node: QueryNode to validate
121
+
122
+ Returns:
123
+ List of validation errors
124
+ """
125
+ errors = []
126
+
127
+ # Validate FindNode
128
+ errors.extend(self.validate_find_node(node.find))
129
+
130
+ # Track current entity type for traversal validation
131
+ self.current_entity_type = node.find.entity_type
132
+
133
+ # Validate all traversals
134
+ for traversal in node.traversals:
135
+ errors.extend(self.validate_traversal_node(traversal))
136
+
137
+ return errors
138
+
139
+ def validate_find_node(self, node: FindNode) -> List[ValidationError]:
140
+ """
141
+ Validate FindNode
142
+
143
+ Validates:
144
+ - Entity type exists in schema
145
+ - All filters reference valid properties
146
+
147
+ Args:
148
+ node: FindNode to validate
149
+
150
+ Returns:
151
+ List of validation errors
152
+ """
153
+ errors = []
154
+
155
+ # Validate entity type exists
156
+ errors.extend(self.validate_entity_type(node.entity_type, node.line, node.column))
157
+
158
+ # Set current entity type for property validation
159
+ self.current_entity_type = node.entity_type
160
+
161
+ # Validate all filters
162
+ for filter_node in node.filters:
163
+ errors.extend(self.validate(filter_node))
164
+
165
+ return errors
166
+
167
+ def validate_traversal_node(self, node: TraversalNode) -> List[ValidationError]:
168
+ """
169
+ Validate TraversalNode
170
+
171
+ Validates:
172
+ - Relation type exists in schema
173
+ - Relation endpoints match current entity type
174
+ - Direction is valid
175
+
176
+ Args:
177
+ node: TraversalNode to validate
178
+
179
+ Returns:
180
+ List of validation errors
181
+ """
182
+ errors = []
183
+
184
+ # Validate relation type exists
185
+ errors.extend(self.validate_relation_type(node.relation_type, node.line, node.column))
186
+
187
+ # Validate relation endpoints if we have a current entity type
188
+ if self.current_entity_type:
189
+ errors.extend(
190
+ self.validate_relation_endpoints(
191
+ node.relation_type,
192
+ self.current_entity_type,
193
+ node.direction or "outgoing",
194
+ node.line,
195
+ node.column,
196
+ )
197
+ )
198
+
199
+ # Validate direction
200
+ if node.direction and node.direction not in ["incoming", "outgoing"]:
201
+ errors.append(
202
+ ValidationError(
203
+ line=node.line,
204
+ column=node.column,
205
+ message=f"Invalid direction '{node.direction}'. Must be 'incoming' or 'outgoing'",
206
+ suggestion="Use 'INCOMING' or 'OUTGOING'",
207
+ )
208
+ )
209
+
210
+ return errors
211
+
212
+ def validate_property_filter_node(self, node: PropertyFilterNode) -> List[ValidationError]:
213
+ """
214
+ Validate PropertyFilterNode
215
+
216
+ Validates:
217
+ - Property exists in current entity type
218
+ - Property value type matches schema
219
+ - Operator is valid for property type
220
+
221
+ Args:
222
+ node: PropertyFilterNode to validate
223
+
224
+ Returns:
225
+ List of validation errors
226
+ """
227
+ errors = []
228
+
229
+ # Validate operator
230
+ valid_operators = ["==", "!=", ">", "<", ">=", "<=", "IN", "CONTAINS"]
231
+ if node.operator not in valid_operators:
232
+ errors.append(
233
+ ValidationError(
234
+ line=node.line,
235
+ column=node.column,
236
+ message=f"Invalid operator '{node.operator}'",
237
+ suggestion=f"Valid operators: {', '.join(valid_operators)}",
238
+ )
239
+ )
240
+
241
+ # Validate property exists in current entity type
242
+ if self.current_entity_type:
243
+ errors.extend(
244
+ self.validate_property(
245
+ self.current_entity_type,
246
+ node.property_path,
247
+ node.line,
248
+ node.column,
249
+ )
250
+ )
251
+
252
+ # Validate property value type
253
+ errors.extend(
254
+ self.validate_property_value_type(
255
+ self.current_entity_type,
256
+ node.property_path,
257
+ node.value,
258
+ node.operator,
259
+ node.line,
260
+ node.column,
261
+ )
262
+ )
263
+
264
+ # Validate operator-specific constraints
265
+ if node.operator == "IN" and not isinstance(node.value, list):
266
+ errors.append(
267
+ ValidationError(
268
+ line=node.line,
269
+ column=node.column,
270
+ message=f"IN operator requires a list value, got {type(node.value).__name__}",
271
+ suggestion="Use a list like ['value1', 'value2']",
272
+ )
273
+ )
274
+
275
+ if node.operator == "CONTAINS" and not isinstance(node.value, str):
276
+ errors.append(
277
+ ValidationError(
278
+ line=node.line,
279
+ column=node.column,
280
+ message=f"CONTAINS operator requires a string value, got {type(node.value).__name__}",
281
+ suggestion="Use a string value",
282
+ )
283
+ )
284
+
285
+ return errors
286
+
287
+ def validate_boolean_filter_node(self, node: BooleanFilterNode) -> List[ValidationError]:
288
+ """
289
+ Validate BooleanFilterNode
290
+
291
+ Validates:
292
+ - Operator is valid (AND, OR, NOT)
293
+ - Has at least one operand
294
+ - All operands are valid
295
+
296
+ Args:
297
+ node: BooleanFilterNode to validate
298
+
299
+ Returns:
300
+ List of validation errors
301
+ """
302
+ errors = []
303
+
304
+ # Validate operator
305
+ valid_operators = ["AND", "OR", "NOT"]
306
+ if node.operator not in valid_operators:
307
+ errors.append(
308
+ ValidationError(
309
+ line=node.line,
310
+ column=node.column,
311
+ message=f"Invalid boolean operator '{node.operator}'",
312
+ suggestion=f"Valid operators: {', '.join(valid_operators)}",
313
+ )
314
+ )
315
+
316
+ # Validate operand count
317
+ if not node.operands:
318
+ errors.append(
319
+ ValidationError(
320
+ line=node.line,
321
+ column=node.column,
322
+ message=f"Boolean operator '{node.operator}' requires at least one operand",
323
+ )
324
+ )
325
+
326
+ # Validate all operands
327
+ for operand in node.operands:
328
+ errors.extend(self.validate(operand))
329
+
330
+ return errors
331
+
332
+ # ========================================================================
333
+ # Helper Validation Methods
334
+ # ========================================================================
335
+
336
+ def validate_entity_type(
337
+ self, entity_type: str, line: int, column: int
338
+ ) -> List[ValidationError]:
339
+ """
340
+ Validate that an entity type exists in the schema
341
+
342
+ Args:
343
+ entity_type: Entity type name to validate
344
+ line: Line number for error reporting
345
+ column: Column number for error reporting
346
+
347
+ Returns:
348
+ List of validation errors
349
+ """
350
+ errors = []
351
+
352
+ # Check if schema has the method
353
+ if not hasattr(self.schema, "get_entity_type"):
354
+ return errors
355
+
356
+ # Check if entity type exists
357
+ entity_schema = self.schema.get_entity_type(entity_type)
358
+ if entity_schema is None:
359
+ # Get available types for suggestion
360
+ available_types = []
361
+ if hasattr(self.schema, "list_entity_types"):
362
+ available_types = self.schema.list_entity_types()
363
+
364
+ suggestion = None
365
+ if available_types:
366
+ suggestion = f"Available entity types: {', '.join(available_types[:5])}"
367
+ if len(available_types) > 5:
368
+ suggestion += f" (and {len(available_types) - 5} more)"
369
+
370
+ errors.append(
371
+ ValidationError(
372
+ line=line,
373
+ column=column,
374
+ message=f"Entity type '{entity_type}' not found in schema",
375
+ suggestion=suggestion,
376
+ )
377
+ )
378
+
379
+ return errors
380
+
381
+ def validate_relation_type(
382
+ self, relation_type: str, line: int, column: int
383
+ ) -> List[ValidationError]:
384
+ """
385
+ Validate that a relation type exists in the schema
386
+
387
+ Args:
388
+ relation_type: Relation type name to validate
389
+ line: Line number for error reporting
390
+ column: Column number for error reporting
391
+
392
+ Returns:
393
+ List of validation errors
394
+ """
395
+ errors = []
396
+
397
+ # Check if schema has the method
398
+ if not hasattr(self.schema, "get_relation_type"):
399
+ return errors
400
+
401
+ # Check if relation type exists
402
+ relation_schema = self.schema.get_relation_type(relation_type)
403
+ if relation_schema is None:
404
+ # Get available types for suggestion
405
+ available_types = []
406
+ if hasattr(self.schema, "list_relation_types"):
407
+ available_types = self.schema.list_relation_types()
408
+
409
+ suggestion = None
410
+ if available_types:
411
+ suggestion = f"Available relation types: {', '.join(available_types[:5])}"
412
+ if len(available_types) > 5:
413
+ suggestion += f" (and {len(available_types) - 5} more)"
414
+
415
+ errors.append(
416
+ ValidationError(
417
+ line=line,
418
+ column=column,
419
+ message=f"Relation type '{relation_type}' not found in schema",
420
+ suggestion=suggestion,
421
+ )
422
+ )
423
+
424
+ return errors
425
+
426
+ def validate_property(
427
+ self, entity_type: str, property_path: str, line: int, column: int
428
+ ) -> List[ValidationError]:
429
+ """
430
+ Validate that a property exists in an entity type
431
+
432
+ Args:
433
+ entity_type: Entity type name
434
+ property_path: Property path (may be nested like "address.city")
435
+ line: Line number for error reporting
436
+ column: Column number for error reporting
437
+
438
+ Returns:
439
+ List of validation errors
440
+ """
441
+ errors = []
442
+
443
+ # Get entity type schema
444
+ if not hasattr(self.schema, "get_entity_type"):
445
+ return errors
446
+
447
+ entity_schema = self.schema.get_entity_type(entity_type)
448
+ if entity_schema is None:
449
+ return errors # Entity type error already reported
450
+
451
+ # Handle nested properties (e.g., "address.city")
452
+ property_parts = property_path.split(".")
453
+ current_property = property_parts[0]
454
+
455
+ # Check if property exists
456
+ if not hasattr(entity_schema, "get_property"):
457
+ return errors
458
+
459
+ property_schema = entity_schema.get_property(current_property)
460
+ if property_schema is None:
461
+ # Get available properties for suggestion
462
+ available_props = []
463
+ if hasattr(entity_schema, "properties"):
464
+ available_props = list(entity_schema.properties.keys())
465
+
466
+ suggestion = None
467
+ if available_props:
468
+ suggestion = (
469
+ f"Available properties for {entity_type}: {', '.join(available_props[:5])}"
470
+ )
471
+ if len(available_props) > 5:
472
+ suggestion += f" (and {len(available_props) - 5} more)"
473
+
474
+ errors.append(
475
+ ValidationError(
476
+ line=line,
477
+ column=column,
478
+ message=f"Property '{current_property}' not found in entity type '{entity_type}'",
479
+ suggestion=suggestion,
480
+ )
481
+ )
482
+
483
+ # TODO: Validate nested properties (requires nested schema support)
484
+
485
+ return errors
486
+
487
+ def validate_property_value_type(
488
+ self,
489
+ entity_type: str,
490
+ property_path: str,
491
+ value: Any,
492
+ operator: str,
493
+ line: int,
494
+ column: int,
495
+ ) -> List[ValidationError]:
496
+ """
497
+ Validate that a property value matches the expected type
498
+
499
+ Args:
500
+ entity_type: Entity type name
501
+ property_path: Property path
502
+ value: Value to validate
503
+ operator: Operator being used
504
+ line: Line number for error reporting
505
+ column: Column number for error reporting
506
+
507
+ Returns:
508
+ List of validation errors
509
+ """
510
+ errors = []
511
+
512
+ # Get entity type schema
513
+ if not hasattr(self.schema, "get_entity_type"):
514
+ return errors
515
+
516
+ entity_schema = self.schema.get_entity_type(entity_type)
517
+ if entity_schema is None:
518
+ return errors
519
+
520
+ # Get property schema
521
+ property_parts = property_path.split(".")
522
+ current_property = property_parts[0]
523
+
524
+ if not hasattr(entity_schema, "get_property"):
525
+ return errors
526
+
527
+ property_schema = entity_schema.get_property(current_property)
528
+ if property_schema is None:
529
+ return errors # Property error already reported
530
+
531
+ # Get property type
532
+ if not hasattr(property_schema, "property_type"):
533
+ return errors
534
+
535
+ property_type = property_schema.property_type
536
+
537
+ # Map property types to Python types
538
+ type_map = {
539
+ "STRING": str,
540
+ "INTEGER": int,
541
+ "FLOAT": float,
542
+ "BOOLEAN": bool,
543
+ "DATE": str, # Dates are typically strings in queries
544
+ "DATETIME": str,
545
+ }
546
+
547
+ # Get expected Python type
548
+ expected_type = None
549
+ if hasattr(property_type, "value"):
550
+ # Enum type
551
+ expected_type = type_map.get(property_type.value)
552
+ elif hasattr(property_type, "name"):
553
+ # String type name
554
+ expected_type = type_map.get(property_type.name)
555
+
556
+ if expected_type is None:
557
+ return errors # Unknown type, skip validation
558
+
559
+ # For IN operator, check list elements
560
+ if operator == "IN":
561
+ if isinstance(value, list):
562
+ for item in value:
563
+ if not isinstance(item, expected_type):
564
+ errors.append(
565
+ ValidationError(
566
+ line=line,
567
+ column=column,
568
+ message=f"Property '{property_path}' expects {expected_type.__name__} values, "
569
+ f"but list contains {type(item).__name__}",
570
+ suggestion=f"Ensure all list values are {expected_type.__name__}",
571
+ )
572
+ )
573
+ break # Only report once
574
+ return errors
575
+
576
+ # Check value type
577
+ if not isinstance(value, expected_type):
578
+ errors.append(
579
+ ValidationError(
580
+ line=line,
581
+ column=column,
582
+ message=f"Property '{property_path}' expects {expected_type.__name__} value, "
583
+ f"got {type(value).__name__}",
584
+ suggestion=f"Use a {expected_type.__name__} value",
585
+ )
586
+ )
587
+
588
+ return errors
589
+
590
+ def validate_relation_endpoints(
591
+ self,
592
+ relation_type: str,
593
+ current_entity_type: str,
594
+ direction: str,
595
+ line: int,
596
+ column: int,
597
+ ) -> List[ValidationError]:
598
+ """
599
+ Validate that relation endpoints match entity types
600
+
601
+ Args:
602
+ relation_type: Relation type name
603
+ current_entity_type: Current entity type in the query
604
+ direction: Direction of traversal ("incoming" or "outgoing")
605
+ line: Line number for error reporting
606
+ column: Column number for error reporting
607
+
608
+ Returns:
609
+ List of validation errors
610
+ """
611
+ errors = []
612
+
613
+ # Get relation type schema
614
+ if not hasattr(self.schema, "get_relation_type"):
615
+ return errors
616
+
617
+ relation_schema = self.schema.get_relation_type(relation_type)
618
+ if relation_schema is None:
619
+ return errors # Relation type error already reported
620
+
621
+ # Check if relation has endpoint constraints
622
+ if not hasattr(relation_schema, "source_entity_types") or not hasattr(
623
+ relation_schema, "target_entity_types"
624
+ ):
625
+ return errors
626
+
627
+ source_types = relation_schema.source_entity_types
628
+ target_types = relation_schema.target_entity_types
629
+
630
+ # Validate based on direction
631
+ if direction == "outgoing":
632
+ # Current entity is source, check if it's allowed
633
+ if source_types and current_entity_type not in source_types:
634
+ errors.append(
635
+ ValidationError(
636
+ line=line,
637
+ column=column,
638
+ message=f"Entity type '{current_entity_type}' cannot be source of relation '{relation_type}'",
639
+ suggestion=f"Allowed source types: {', '.join(source_types)}",
640
+ )
641
+ )
642
+ elif direction == "incoming":
643
+ # Current entity is target, check if it's allowed
644
+ if target_types and current_entity_type not in target_types:
645
+ errors.append(
646
+ ValidationError(
647
+ line=line,
648
+ column=column,
649
+ message=f"Entity type '{current_entity_type}' cannot be target of relation '{relation_type}'",
650
+ suggestion=f"Allowed target types: {', '.join(target_types)}",
651
+ )
652
+ )
653
+
654
+ return errors