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,630 @@
1
+ """
2
+ AST Node Definitions for Logic Query Parser
3
+
4
+ This module defines the Abstract Syntax Tree (AST) node hierarchy for the Logic Query DSL.
5
+ Each node is self-contained and responsible for its own validation and conversion to QueryPlan.
6
+
7
+ Design Principles:
8
+ 1. Self-Contained: Each node owns its complete structure (e.g., FindNode has filters)
9
+ 2. Polymorphic Conversion: Each node implements its own to_query_plan() method
10
+ 3. Immutable: Nodes are frozen dataclasses (cannot be modified after creation)
11
+ 4. Type-Safe: Full type hints for all fields and methods
12
+
13
+ Phase: 2.4 - Logic Query Parser
14
+ Version: 1.0
15
+ """
16
+
17
+ from abc import ABC, abstractmethod
18
+ from dataclasses import dataclass, field
19
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING
20
+ import uuid
21
+
22
+ if TYPE_CHECKING:
23
+ from .query_context import QueryContext
24
+
25
+ # Import QueryPlan models
26
+ try:
27
+ from aiecs.domain.knowledge_graph.models.query_plan import (
28
+ QueryPlan,
29
+ QueryStep,
30
+ QueryOperation,
31
+ )
32
+ from aiecs.domain.knowledge_graph.models.query import GraphQuery, QueryType
33
+
34
+ QUERY_PLAN_AVAILABLE = True
35
+ except ImportError:
36
+ QUERY_PLAN_AVAILABLE = False
37
+ QueryPlan = None
38
+ QueryStep = None
39
+ QueryOperation = None
40
+ GraphQuery = None
41
+ QueryType = None
42
+
43
+ # Placeholder for ValidationError (will be defined in error_handler.py)
44
+
45
+
46
+ @dataclass
47
+ class ValidationError:
48
+ """Validation error with location information"""
49
+
50
+ line: int
51
+ column: int
52
+ message: str
53
+ suggestion: Optional[str] = None
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class ASTNode(ABC):
58
+ """
59
+ Base class for all AST nodes
60
+
61
+ All AST nodes must:
62
+ 1. Store line/column metadata for error reporting
63
+ 2. Implement validate() for semantic validation
64
+ 3. Implement conversion to query plan (via to_query_plan or to_filter_dict)
65
+
66
+ Attributes:
67
+ line: Line number in source query (1-based)
68
+ column: Column number in source query (1-based)
69
+ """
70
+
71
+ line: int
72
+ column: int
73
+
74
+ @abstractmethod
75
+ def validate(self, schema: Any) -> List[ValidationError]:
76
+ """
77
+ Validate this node against the schema
78
+
79
+ Args:
80
+ schema: SchemaManager instance for validation
81
+
82
+ Returns:
83
+ List of validation errors (empty if valid)
84
+ """
85
+
86
+
87
+ @dataclass(frozen=True)
88
+ class QueryNode(ASTNode):
89
+ """
90
+ Top-level query node: Find + optional Traversals + optional WHERE
91
+
92
+ Represents a complete query with:
93
+ - Required: FindNode for entity selection
94
+ - Optional: List of TraversalNodes for graph navigation
95
+ - Optional: WHERE clause (embedded in FindNode.filters)
96
+
97
+ Example:
98
+ Find(Person) FOLLOWS AuthoredBy WHERE year > 2020
99
+
100
+ QueryNode(
101
+ find=FindNode(entity_type="Person", ...),
102
+ traversals=[TraversalNode(relation_type="AuthoredBy", ...)],
103
+ ...
104
+ )
105
+ """
106
+
107
+ find: "FindNode"
108
+ traversals: List["TraversalNode"] = field(default_factory=list)
109
+
110
+ def validate(self, schema: Any) -> List[ValidationError]:
111
+ """Validate all parts of the query"""
112
+ errors = []
113
+ errors.extend(self.find.validate(schema))
114
+ for traversal in self.traversals:
115
+ errors.extend(traversal.validate(schema))
116
+ return errors
117
+
118
+ def to_query_plan(self, context: "QueryContext", original_query: str = "") -> Any:
119
+ """
120
+ Convert QueryNode to QueryPlan
121
+
122
+ Creates a QueryPlan with multiple steps for complex queries with traversals.
123
+
124
+ Args:
125
+ context: Query context for variable resolution
126
+ original_query: Original query string for documentation
127
+
128
+ Returns:
129
+ QueryPlan with one or more QuerySteps
130
+ """
131
+ if not QUERY_PLAN_AVAILABLE:
132
+ raise ImportError("QueryPlan models not available")
133
+
134
+ # Generate plan ID
135
+ plan_id = f"plan_{uuid.uuid4().hex[:8]}"
136
+
137
+ # Convert to query steps
138
+ steps = self.to_query_steps(context)
139
+
140
+ # Create explanation
141
+ explanation = self._generate_explanation()
142
+
143
+ # Create QueryPlan
144
+ plan = QueryPlan(
145
+ plan_id=plan_id,
146
+ original_query=original_query or str(self),
147
+ steps=steps,
148
+ explanation=explanation,
149
+ optimized=False,
150
+ )
151
+
152
+ # Calculate total cost
153
+ plan.total_estimated_cost = plan.calculate_total_cost()
154
+
155
+ return plan
156
+
157
+ def to_query_steps(self, context: "QueryContext") -> List[Any]:
158
+ """
159
+ Convert QueryNode to list of QuerySteps
160
+
161
+ For simple queries (Find only), creates a single step.
162
+ For complex queries (Find + Traversals), creates multiple steps.
163
+
164
+ Args:
165
+ context: Query context for variable resolution
166
+
167
+ Returns:
168
+ List of QueryStep objects
169
+ """
170
+ if not QUERY_PLAN_AVAILABLE:
171
+ raise ImportError("QueryPlan models not available")
172
+
173
+ steps = []
174
+
175
+ # Step 1: Find entities (always present)
176
+ find_step = self.find.to_query_step(context, step_id="step_1")
177
+ steps.append(find_step)
178
+
179
+ # Steps 2+: Traversals (if any)
180
+ for i, traversal in enumerate(self.traversals, start=2):
181
+ step_id = f"step_{i}"
182
+ depends_on = [f"step_{i-1}"] # Each step depends on previous
183
+ traversal_step = traversal.to_query_step(
184
+ context, step_id=step_id, depends_on=depends_on
185
+ )
186
+ steps.append(traversal_step)
187
+
188
+ return steps
189
+
190
+ def _generate_explanation(self) -> str:
191
+ """Generate human-readable explanation of the query"""
192
+ parts = [f"Find {self.find.entity_type} entities"]
193
+
194
+ if self.find.entity_name:
195
+ parts.append(f"named '{self.find.entity_name}'")
196
+
197
+ if self.find.filters:
198
+ parts.append(f"with {len(self.find.filters)} filter(s)")
199
+
200
+ if self.traversals:
201
+ parts.append(f"then traverse {len(self.traversals)} relation(s)")
202
+
203
+ return " ".join(parts)
204
+
205
+ def __repr__(self) -> str:
206
+ """String representation for debugging"""
207
+ traversals_str = f", traversals={len(self.traversals)}" if self.traversals else ""
208
+ return f"QueryNode(find={self.find}{traversals_str})"
209
+
210
+
211
+ @dataclass(frozen=True)
212
+ class FindNode(ASTNode):
213
+ """
214
+ Entity selection node: Find(EntityType) or Find(EntityType[`Name`])
215
+
216
+ Represents entity selection with optional filters.
217
+ This node is self-contained and owns its filters.
218
+
219
+ Attributes:
220
+ entity_type: Type of entity to find (e.g., "Person", "Paper")
221
+ entity_name: Optional specific entity name (e.g., "Alice")
222
+ filters: List of filter nodes (from WHERE clause)
223
+
224
+ Example:
225
+ Find(Person[`Alice`]) WHERE age > 30
226
+
227
+ FindNode(
228
+ entity_type="Person",
229
+ entity_name="Alice",
230
+ filters=[PropertyFilterNode(property="age", operator=">", value=30)]
231
+ )
232
+ """
233
+
234
+ entity_type: str
235
+ entity_name: Optional[str] = None
236
+ filters: List["FilterNode"] = field(default_factory=list)
237
+
238
+ def validate(self, schema: Any) -> List[ValidationError]:
239
+ """Validate entity type and all filters"""
240
+ errors = []
241
+
242
+ # Validate entity type exists (if schema has this method)
243
+ if hasattr(schema, "has_entity_type"):
244
+ if not schema.has_entity_type(self.entity_type):
245
+ errors.append(
246
+ ValidationError(
247
+ self.line,
248
+ self.column,
249
+ f"Entity type '{self.entity_type}' not found",
250
+ suggestion=f"Available types: {', '.join(schema.get_entity_types())}",
251
+ )
252
+ )
253
+
254
+ # Validate all filters
255
+ for filter_node in self.filters:
256
+ errors.extend(filter_node.validate(schema))
257
+
258
+ return errors
259
+
260
+ def to_query_step(
261
+ self,
262
+ context: "QueryContext",
263
+ step_id: str = "step_1",
264
+ depends_on: List[str] = None,
265
+ ) -> Any:
266
+ """
267
+ Convert FindNode to QueryStep
268
+
269
+ Creates a QueryStep for entity lookup/filter operation.
270
+
271
+ Args:
272
+ context: Query context for variable resolution
273
+ step_id: Unique identifier for this step
274
+ depends_on: List of step IDs this step depends on
275
+
276
+ Returns:
277
+ QueryStep for entity lookup/filter
278
+ """
279
+ if not QUERY_PLAN_AVAILABLE:
280
+ raise ImportError("QueryPlan models not available")
281
+
282
+ # Build property filters
283
+ properties = {}
284
+ if self.filters:
285
+ # Combine all filters into a single filter dict
286
+ for filter_node in self.filters:
287
+ filter_dict = filter_node.to_filter_dict(context)
288
+ properties.update(filter_dict)
289
+
290
+ # Determine query type and operation
291
+ # If entity_name is provided, it's an entity lookup
292
+ # Otherwise, it's a filter operation
293
+ if self.entity_name:
294
+ query_type = QueryType.ENTITY_LOOKUP
295
+ operation = QueryOperation.ENTITY_LOOKUP
296
+ else:
297
+ # For filter operations, we use ENTITY_LOOKUP query type with
298
+ # filters
299
+ query_type = QueryType.ENTITY_LOOKUP
300
+ operation = QueryOperation.FILTER
301
+
302
+ # Create GraphQuery
303
+ query = GraphQuery(
304
+ query_type=query_type,
305
+ entity_type=self.entity_type,
306
+ entity_id=self.entity_name, # If specific entity name is provided
307
+ properties=properties,
308
+ max_results=100, # Default limit
309
+ )
310
+
311
+ # Create description
312
+ description = f"Find {self.entity_type} entities"
313
+ if self.entity_name:
314
+ description += f" named '{self.entity_name}'"
315
+ if self.filters:
316
+ description += f" with {len(self.filters)} filter(s)"
317
+
318
+ # Create QueryStep
319
+ step = QueryStep(
320
+ step_id=step_id,
321
+ operation=operation,
322
+ query=query,
323
+ depends_on=depends_on or [],
324
+ description=description,
325
+ estimated_cost=0.3, # Low cost for simple entity lookup
326
+ )
327
+
328
+ return step
329
+
330
+ def __repr__(self) -> str:
331
+ """String representation for debugging"""
332
+ name_str = f"[`{self.entity_name}`]" if self.entity_name else ""
333
+ filters_str = f", filters={len(self.filters)}" if self.filters else ""
334
+ return f"FindNode({self.entity_type}{name_str}{filters_str})"
335
+
336
+
337
+ @dataclass(frozen=True)
338
+ class TraversalNode(ASTNode):
339
+ """
340
+ Graph traversal node: FOLLOWS RelationType [direction]
341
+
342
+ Represents navigation along graph relationships.
343
+
344
+ Attributes:
345
+ relation_type: Type of relation to follow (e.g., "AuthoredBy")
346
+ direction: Direction of traversal ("outgoing", "incoming", or None for default)
347
+
348
+ Example:
349
+ FOLLOWS AuthoredBy INCOMING
350
+
351
+ TraversalNode(
352
+ relation_type="AuthoredBy",
353
+ direction="incoming"
354
+ )
355
+ """
356
+
357
+ relation_type: str
358
+ direction: Optional[str] = "outgoing" # "incoming" | "outgoing" | None
359
+
360
+ def validate(self, schema: Any) -> List[ValidationError]:
361
+ """Validate relation type exists"""
362
+ errors = []
363
+
364
+ # Validate relation type exists (if schema has this method)
365
+ if hasattr(schema, "has_relation_type"):
366
+ if not schema.has_relation_type(self.relation_type):
367
+ errors.append(
368
+ ValidationError(
369
+ self.line,
370
+ self.column,
371
+ f"Relation type '{self.relation_type}' not found",
372
+ )
373
+ )
374
+
375
+ # Validate direction
376
+ if self.direction and self.direction not in ["incoming", "outgoing"]:
377
+ errors.append(
378
+ ValidationError(
379
+ self.line,
380
+ self.column,
381
+ f"Invalid direction '{self.direction}'. Must be 'incoming' or 'outgoing'",
382
+ )
383
+ )
384
+
385
+ return errors
386
+
387
+ def to_query_step(self, context: "QueryContext", step_id: str, depends_on: List[str]) -> Any:
388
+ """
389
+ Convert TraversalNode to QueryStep
390
+
391
+ Creates a QueryStep for graph traversal operation.
392
+
393
+ Args:
394
+ context: Query context for variable resolution
395
+ step_id: Unique identifier for this step
396
+ depends_on: List of step IDs this step depends on
397
+
398
+ Returns:
399
+ QueryStep for graph traversal
400
+ """
401
+ if not QUERY_PLAN_AVAILABLE:
402
+ raise ImportError("QueryPlan models not available")
403
+
404
+ # Create GraphQuery for traversal
405
+ query = GraphQuery(
406
+ query_type=QueryType.TRAVERSAL,
407
+ relation_type=self.relation_type,
408
+ max_depth=1, # Single hop traversal
409
+ max_results=100, # Default limit
410
+ )
411
+
412
+ # Create description
413
+ direction_str = self.direction.upper() if self.direction else "OUTGOING"
414
+ description = f"Traverse {self.relation_type} relation ({direction_str})"
415
+
416
+ # Create QueryStep
417
+ step = QueryStep(
418
+ step_id=step_id,
419
+ operation=QueryOperation.TRAVERSAL,
420
+ query=query,
421
+ depends_on=depends_on,
422
+ description=description,
423
+ estimated_cost=0.5, # Medium cost for traversal
424
+ metadata={"direction": self.direction or "outgoing"},
425
+ )
426
+
427
+ return step
428
+
429
+ def __repr__(self) -> str:
430
+ """String representation for debugging"""
431
+ dir_str = f" {self.direction.upper()}" if self.direction else ""
432
+ return f"TraversalNode({self.relation_type}{dir_str})"
433
+
434
+
435
+ @dataclass(frozen=True)
436
+ class FilterNode(ASTNode):
437
+ """
438
+ Base class for filter nodes (WHERE conditions)
439
+
440
+ Filter nodes represent conditions in WHERE clauses.
441
+ They convert to filter dictionaries (MongoDB-style) for query execution.
442
+
443
+ Subclasses:
444
+ - PropertyFilterNode: property operator value (e.g., age > 30)
445
+ - BooleanFilterNode: AND/OR/NOT combinations
446
+ """
447
+
448
+ @abstractmethod
449
+ def to_filter_dict(self, context: "QueryContext") -> Dict[str, Any]:
450
+ """
451
+ Convert filter to MongoDB-style filter dictionary
452
+
453
+ Args:
454
+ context: Query context for variable resolution
455
+
456
+ Returns:
457
+ Filter dictionary (e.g., {"age": {"$gt": 30}})
458
+ """
459
+
460
+
461
+ @dataclass(frozen=True)
462
+ class PropertyFilterNode(FilterNode):
463
+ """
464
+ Property filter node: property operator value
465
+
466
+ Represents a comparison between a property and a value.
467
+
468
+ Attributes:
469
+ property_path: Property name or nested path (e.g., "age" or "address.city")
470
+ operator: Comparison operator (==, !=, >, <, >=, <=, IN, CONTAINS)
471
+ value: Value to compare against
472
+
473
+ Example:
474
+ age > 30
475
+
476
+ PropertyFilterNode(
477
+ property_path="age",
478
+ operator=">",
479
+ value=30
480
+ )
481
+ """
482
+
483
+ property_path: str # Can be nested: "address.city"
484
+ operator: str # "==", "!=", ">", "<", ">=", "<=", "IN", "CONTAINS"
485
+ value: Any
486
+
487
+ def to_filter_dict(self, context: "QueryContext") -> Dict[str, Any]:
488
+ """Convert to MongoDB-style filter dict"""
489
+ operator_map = {
490
+ "==": "$eq",
491
+ "!=": "$ne",
492
+ ">": "$gt",
493
+ "<": "$lt",
494
+ ">=": "$gte",
495
+ "<=": "$lte",
496
+ "IN": "$in",
497
+ "CONTAINS": "$regex",
498
+ }
499
+
500
+ mongo_op = operator_map.get(self.operator, "$eq")
501
+
502
+ # For CONTAINS, convert to regex pattern
503
+ if self.operator == "CONTAINS":
504
+ return {self.property_path: {mongo_op: self.value}}
505
+
506
+ return {self.property_path: {mongo_op: self.value}}
507
+
508
+ def validate(self, schema: Any) -> List[ValidationError]:
509
+ """Validate property exists and type matches"""
510
+ errors = []
511
+
512
+ # Validate operator is valid
513
+ valid_operators = ["==", "!=", ">", "<", ">=", "<=", "IN", "CONTAINS"]
514
+ if self.operator not in valid_operators:
515
+ errors.append(
516
+ ValidationError(
517
+ self.line,
518
+ self.column,
519
+ f"Invalid operator '{self.operator}'. Must be one of: {', '.join(valid_operators)}",
520
+ )
521
+ )
522
+
523
+ # Validate IN operator has list value
524
+ if self.operator == "IN" and not isinstance(self.value, list):
525
+ errors.append(
526
+ ValidationError(
527
+ self.line,
528
+ self.column,
529
+ f"IN operator requires a list value, got {type(self.value).__name__}",
530
+ )
531
+ )
532
+
533
+ # Validate CONTAINS operator has string value
534
+ if self.operator == "CONTAINS" and not isinstance(self.value, str):
535
+ errors.append(
536
+ ValidationError(
537
+ self.line,
538
+ self.column,
539
+ f"CONTAINS operator requires a string value, got {type(self.value).__name__}",
540
+ )
541
+ )
542
+
543
+ # TODO: Validate property exists in schema (requires entity context)
544
+
545
+ return errors
546
+
547
+ def __repr__(self) -> str:
548
+ """String representation for debugging"""
549
+ value_repr = f'"{self.value}"' if isinstance(self.value, str) else str(self.value)
550
+ return f"PropertyFilterNode({self.property_path} {self.operator} {value_repr})"
551
+
552
+
553
+ @dataclass(frozen=True)
554
+ class BooleanFilterNode(FilterNode):
555
+ """
556
+ Boolean filter node: AND/OR/NOT combinations
557
+
558
+ Represents boolean combinations of filters.
559
+
560
+ Attributes:
561
+ operator: Boolean operator ("AND", "OR", "NOT")
562
+ operands: List of filter nodes to combine
563
+
564
+ Example:
565
+ age > 30 AND status == "active"
566
+
567
+ BooleanFilterNode(
568
+ operator="AND",
569
+ operands=[
570
+ PropertyFilterNode(property_path="age", operator=">", value=30),
571
+ PropertyFilterNode(property_path="status", operator="==", value="active")
572
+ ]
573
+ )
574
+ """
575
+
576
+ operator: str # "AND", "OR", "NOT"
577
+ operands: List[FilterNode] = field(default_factory=list)
578
+
579
+ def to_filter_dict(self, context: "QueryContext") -> Dict[str, Any]:
580
+ """Convert to MongoDB-style boolean filter"""
581
+ op_map = {"AND": "$and", "OR": "$or", "NOT": "$not"}
582
+
583
+ mongo_op = op_map.get(self.operator, "$and")
584
+ operand_dicts = [op.to_filter_dict(context) for op in self.operands]
585
+
586
+ # NOT operator has special handling (single operand)
587
+ if self.operator == "NOT":
588
+ if len(operand_dicts) == 1:
589
+ return {mongo_op: operand_dicts[0]}
590
+ else:
591
+ # Multiple operands: NOT (a AND b AND c) = NOT {$and: [a, b,
592
+ # c]}
593
+ return {mongo_op: {"$and": operand_dicts}}
594
+
595
+ return {mongo_op: operand_dicts}
596
+
597
+ def validate(self, schema: Any) -> List[ValidationError]:
598
+ """Validate all operands"""
599
+ errors = []
600
+
601
+ # Validate operator is valid
602
+ valid_operators = ["AND", "OR", "NOT"]
603
+ if self.operator not in valid_operators:
604
+ errors.append(
605
+ ValidationError(
606
+ self.line,
607
+ self.column,
608
+ f"Invalid boolean operator '{self.operator}'. Must be one of: {', '.join(valid_operators)}",
609
+ )
610
+ )
611
+
612
+ # Validate operand count
613
+ if not self.operands:
614
+ errors.append(
615
+ ValidationError(
616
+ self.line,
617
+ self.column,
618
+ f"Boolean operator '{self.operator}' requires at least one operand",
619
+ )
620
+ )
621
+
622
+ # Validate all operands
623
+ for operand in self.operands:
624
+ errors.extend(operand.validate(schema))
625
+
626
+ return errors
627
+
628
+ def __repr__(self) -> str:
629
+ """String representation for debugging"""
630
+ return f"BooleanFilterNode({self.operator}, operands={len(self.operands)})"