aiecs 1.0.1__py3-none-any.whl → 1.7.6__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.

Potentially problematic release.


This version of aiecs might be problematic. Click here for more details.

Files changed (340) hide show
  1. aiecs/__init__.py +13 -16
  2. aiecs/__main__.py +7 -7
  3. aiecs/aiecs_client.py +269 -75
  4. aiecs/application/executors/operation_executor.py +79 -54
  5. aiecs/application/knowledge_graph/__init__.py +7 -0
  6. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  7. aiecs/application/knowledge_graph/builder/data_quality.py +302 -0
  8. aiecs/application/knowledge_graph/builder/data_reshaping.py +293 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +369 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +490 -0
  11. aiecs/application/knowledge_graph/builder/import_optimizer.py +396 -0
  12. aiecs/application/knowledge_graph/builder/schema_inference.py +462 -0
  13. aiecs/application/knowledge_graph/builder/schema_mapping.py +563 -0
  14. aiecs/application/knowledge_graph/builder/structured_pipeline.py +1384 -0
  15. aiecs/application/knowledge_graph/builder/text_chunker.py +317 -0
  16. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  17. aiecs/application/knowledge_graph/extractors/base.py +98 -0
  18. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +422 -0
  19. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +347 -0
  20. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +241 -0
  21. aiecs/application/knowledge_graph/fusion/__init__.py +78 -0
  22. aiecs/application/knowledge_graph/fusion/ab_testing.py +395 -0
  23. aiecs/application/knowledge_graph/fusion/abbreviation_expander.py +327 -0
  24. aiecs/application/knowledge_graph/fusion/alias_index.py +597 -0
  25. aiecs/application/knowledge_graph/fusion/alias_matcher.py +384 -0
  26. aiecs/application/knowledge_graph/fusion/cache_coordinator.py +343 -0
  27. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +433 -0
  28. aiecs/application/knowledge_graph/fusion/entity_linker.py +511 -0
  29. aiecs/application/knowledge_graph/fusion/evaluation_dataset.py +240 -0
  30. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +632 -0
  31. aiecs/application/knowledge_graph/fusion/matching_config.py +489 -0
  32. aiecs/application/knowledge_graph/fusion/name_normalizer.py +352 -0
  33. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +183 -0
  34. aiecs/application/knowledge_graph/fusion/semantic_name_matcher.py +464 -0
  35. aiecs/application/knowledge_graph/fusion/similarity_pipeline.py +534 -0
  36. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  37. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +342 -0
  38. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +366 -0
  39. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  40. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +195 -0
  41. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  42. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  43. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +341 -0
  44. aiecs/application/knowledge_graph/reasoning/inference_engine.py +500 -0
  45. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +163 -0
  46. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  47. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  48. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +913 -0
  49. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +866 -0
  50. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +475 -0
  51. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +396 -0
  52. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +208 -0
  53. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +170 -0
  54. aiecs/application/knowledge_graph/reasoning/query_planner.py +855 -0
  55. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +518 -0
  56. aiecs/application/knowledge_graph/retrieval/__init__.py +27 -0
  57. aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +211 -0
  58. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +592 -0
  59. aiecs/application/knowledge_graph/retrieval/strategy_types.py +23 -0
  60. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  61. aiecs/application/knowledge_graph/search/hybrid_search.py +457 -0
  62. aiecs/application/knowledge_graph/search/reranker.py +293 -0
  63. aiecs/application/knowledge_graph/search/reranker_strategies.py +535 -0
  64. aiecs/application/knowledge_graph/search/text_similarity.py +392 -0
  65. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  66. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +305 -0
  67. aiecs/application/knowledge_graph/traversal/path_scorer.py +271 -0
  68. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  69. aiecs/application/knowledge_graph/validators/relation_validator.py +239 -0
  70. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  71. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +313 -0
  72. aiecs/common/__init__.py +9 -0
  73. aiecs/common/knowledge_graph/__init__.py +17 -0
  74. aiecs/common/knowledge_graph/runnable.py +471 -0
  75. aiecs/config/__init__.py +20 -5
  76. aiecs/config/config.py +762 -31
  77. aiecs/config/graph_config.py +131 -0
  78. aiecs/config/tool_config.py +399 -0
  79. aiecs/core/__init__.py +29 -13
  80. aiecs/core/interface/__init__.py +2 -2
  81. aiecs/core/interface/execution_interface.py +22 -22
  82. aiecs/core/interface/storage_interface.py +37 -88
  83. aiecs/core/registry/__init__.py +31 -0
  84. aiecs/core/registry/service_registry.py +92 -0
  85. aiecs/domain/__init__.py +270 -1
  86. aiecs/domain/agent/__init__.py +191 -0
  87. aiecs/domain/agent/base_agent.py +3870 -0
  88. aiecs/domain/agent/exceptions.py +99 -0
  89. aiecs/domain/agent/graph_aware_mixin.py +569 -0
  90. aiecs/domain/agent/hybrid_agent.py +1435 -0
  91. aiecs/domain/agent/integration/__init__.py +29 -0
  92. aiecs/domain/agent/integration/context_compressor.py +216 -0
  93. aiecs/domain/agent/integration/context_engine_adapter.py +587 -0
  94. aiecs/domain/agent/integration/protocols.py +281 -0
  95. aiecs/domain/agent/integration/retry_policy.py +218 -0
  96. aiecs/domain/agent/integration/role_config.py +213 -0
  97. aiecs/domain/agent/knowledge_aware_agent.py +1892 -0
  98. aiecs/domain/agent/lifecycle.py +291 -0
  99. aiecs/domain/agent/llm_agent.py +692 -0
  100. aiecs/domain/agent/memory/__init__.py +12 -0
  101. aiecs/domain/agent/memory/conversation.py +1124 -0
  102. aiecs/domain/agent/migration/__init__.py +14 -0
  103. aiecs/domain/agent/migration/conversion.py +163 -0
  104. aiecs/domain/agent/migration/legacy_wrapper.py +86 -0
  105. aiecs/domain/agent/models.py +884 -0
  106. aiecs/domain/agent/observability.py +479 -0
  107. aiecs/domain/agent/persistence.py +449 -0
  108. aiecs/domain/agent/prompts/__init__.py +29 -0
  109. aiecs/domain/agent/prompts/builder.py +159 -0
  110. aiecs/domain/agent/prompts/formatters.py +187 -0
  111. aiecs/domain/agent/prompts/template.py +255 -0
  112. aiecs/domain/agent/registry.py +253 -0
  113. aiecs/domain/agent/tool_agent.py +444 -0
  114. aiecs/domain/agent/tools/__init__.py +15 -0
  115. aiecs/domain/agent/tools/schema_generator.py +364 -0
  116. aiecs/domain/community/__init__.py +155 -0
  117. aiecs/domain/community/agent_adapter.py +469 -0
  118. aiecs/domain/community/analytics.py +432 -0
  119. aiecs/domain/community/collaborative_workflow.py +648 -0
  120. aiecs/domain/community/communication_hub.py +634 -0
  121. aiecs/domain/community/community_builder.py +320 -0
  122. aiecs/domain/community/community_integration.py +796 -0
  123. aiecs/domain/community/community_manager.py +803 -0
  124. aiecs/domain/community/decision_engine.py +849 -0
  125. aiecs/domain/community/exceptions.py +231 -0
  126. aiecs/domain/community/models/__init__.py +33 -0
  127. aiecs/domain/community/models/community_models.py +234 -0
  128. aiecs/domain/community/resource_manager.py +461 -0
  129. aiecs/domain/community/shared_context_manager.py +589 -0
  130. aiecs/domain/context/__init__.py +40 -10
  131. aiecs/domain/context/context_engine.py +1910 -0
  132. aiecs/domain/context/conversation_models.py +87 -53
  133. aiecs/domain/context/graph_memory.py +582 -0
  134. aiecs/domain/execution/model.py +12 -4
  135. aiecs/domain/knowledge_graph/__init__.py +19 -0
  136. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  137. aiecs/domain/knowledge_graph/models/entity.py +148 -0
  138. aiecs/domain/knowledge_graph/models/evidence.py +178 -0
  139. aiecs/domain/knowledge_graph/models/inference_rule.py +184 -0
  140. aiecs/domain/knowledge_graph/models/path.py +171 -0
  141. aiecs/domain/knowledge_graph/models/path_pattern.py +171 -0
  142. aiecs/domain/knowledge_graph/models/query.py +261 -0
  143. aiecs/domain/knowledge_graph/models/query_plan.py +181 -0
  144. aiecs/domain/knowledge_graph/models/relation.py +202 -0
  145. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  146. aiecs/domain/knowledge_graph/schema/entity_type.py +131 -0
  147. aiecs/domain/knowledge_graph/schema/graph_schema.py +253 -0
  148. aiecs/domain/knowledge_graph/schema/property_schema.py +143 -0
  149. aiecs/domain/knowledge_graph/schema/relation_type.py +163 -0
  150. aiecs/domain/knowledge_graph/schema/schema_manager.py +691 -0
  151. aiecs/domain/knowledge_graph/schema/type_enums.py +209 -0
  152. aiecs/domain/task/dsl_processor.py +172 -56
  153. aiecs/domain/task/model.py +20 -8
  154. aiecs/domain/task/task_context.py +27 -24
  155. aiecs/infrastructure/__init__.py +0 -2
  156. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  157. aiecs/infrastructure/graph_storage/base.py +837 -0
  158. aiecs/infrastructure/graph_storage/batch_operations.py +458 -0
  159. aiecs/infrastructure/graph_storage/cache.py +424 -0
  160. aiecs/infrastructure/graph_storage/distributed.py +223 -0
  161. aiecs/infrastructure/graph_storage/error_handling.py +380 -0
  162. aiecs/infrastructure/graph_storage/graceful_degradation.py +294 -0
  163. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  164. aiecs/infrastructure/graph_storage/in_memory.py +1197 -0
  165. aiecs/infrastructure/graph_storage/index_optimization.py +446 -0
  166. aiecs/infrastructure/graph_storage/lazy_loading.py +431 -0
  167. aiecs/infrastructure/graph_storage/metrics.py +344 -0
  168. aiecs/infrastructure/graph_storage/migration.py +400 -0
  169. aiecs/infrastructure/graph_storage/pagination.py +483 -0
  170. aiecs/infrastructure/graph_storage/performance_monitoring.py +456 -0
  171. aiecs/infrastructure/graph_storage/postgres.py +1563 -0
  172. aiecs/infrastructure/graph_storage/property_storage.py +353 -0
  173. aiecs/infrastructure/graph_storage/protocols.py +76 -0
  174. aiecs/infrastructure/graph_storage/query_optimizer.py +642 -0
  175. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  176. aiecs/infrastructure/graph_storage/sqlite.py +1373 -0
  177. aiecs/infrastructure/graph_storage/streaming.py +487 -0
  178. aiecs/infrastructure/graph_storage/tenant.py +412 -0
  179. aiecs/infrastructure/messaging/celery_task_manager.py +92 -54
  180. aiecs/infrastructure/messaging/websocket_manager.py +51 -35
  181. aiecs/infrastructure/monitoring/__init__.py +22 -0
  182. aiecs/infrastructure/monitoring/executor_metrics.py +45 -11
  183. aiecs/infrastructure/monitoring/global_metrics_manager.py +212 -0
  184. aiecs/infrastructure/monitoring/structured_logger.py +3 -7
  185. aiecs/infrastructure/monitoring/tracing_manager.py +63 -35
  186. aiecs/infrastructure/persistence/__init__.py +14 -1
  187. aiecs/infrastructure/persistence/context_engine_client.py +184 -0
  188. aiecs/infrastructure/persistence/database_manager.py +67 -43
  189. aiecs/infrastructure/persistence/file_storage.py +180 -103
  190. aiecs/infrastructure/persistence/redis_client.py +74 -21
  191. aiecs/llm/__init__.py +73 -25
  192. aiecs/llm/callbacks/__init__.py +11 -0
  193. aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +26 -19
  194. aiecs/llm/client_factory.py +224 -36
  195. aiecs/llm/client_resolver.py +155 -0
  196. aiecs/llm/clients/__init__.py +38 -0
  197. aiecs/llm/clients/base_client.py +324 -0
  198. aiecs/llm/clients/google_function_calling_mixin.py +457 -0
  199. aiecs/llm/clients/googleai_client.py +241 -0
  200. aiecs/llm/clients/openai_client.py +158 -0
  201. aiecs/llm/clients/openai_compatible_mixin.py +367 -0
  202. aiecs/llm/clients/vertex_client.py +897 -0
  203. aiecs/llm/clients/xai_client.py +201 -0
  204. aiecs/llm/config/__init__.py +51 -0
  205. aiecs/llm/config/config_loader.py +272 -0
  206. aiecs/llm/config/config_validator.py +206 -0
  207. aiecs/llm/config/model_config.py +143 -0
  208. aiecs/llm/protocols.py +149 -0
  209. aiecs/llm/utils/__init__.py +10 -0
  210. aiecs/llm/utils/validate_config.py +89 -0
  211. aiecs/main.py +140 -121
  212. aiecs/scripts/aid/VERSION_MANAGEMENT.md +138 -0
  213. aiecs/scripts/aid/__init__.py +19 -0
  214. aiecs/scripts/aid/module_checker.py +499 -0
  215. aiecs/scripts/aid/version_manager.py +235 -0
  216. aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +1 -0
  217. aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +1 -0
  218. aiecs/scripts/dependance_check/__init__.py +15 -0
  219. aiecs/scripts/dependance_check/dependency_checker.py +1835 -0
  220. aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +192 -90
  221. aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +203 -71
  222. aiecs/scripts/dependance_patch/__init__.py +7 -0
  223. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  224. aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +21 -14
  225. aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +1 -1
  226. aiecs/scripts/knowledge_graph/__init__.py +3 -0
  227. aiecs/scripts/knowledge_graph/run_threshold_experiments.py +212 -0
  228. aiecs/scripts/migrations/multi_tenancy/README.md +142 -0
  229. aiecs/scripts/tools_develop/README.md +671 -0
  230. aiecs/scripts/tools_develop/README_CONFIG_CHECKER.md +273 -0
  231. aiecs/scripts/tools_develop/TOOLS_CONFIG_GUIDE.md +1287 -0
  232. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  233. aiecs/scripts/tools_develop/__init__.py +21 -0
  234. aiecs/scripts/tools_develop/check_all_tools_config.py +548 -0
  235. aiecs/scripts/tools_develop/check_type_annotations.py +257 -0
  236. aiecs/scripts/tools_develop/pre-commit-schema-coverage.sh +66 -0
  237. aiecs/scripts/tools_develop/schema_coverage.py +511 -0
  238. aiecs/scripts/tools_develop/validate_tool_schemas.py +475 -0
  239. aiecs/scripts/tools_develop/verify_executor_config_fix.py +98 -0
  240. aiecs/scripts/tools_develop/verify_tools.py +352 -0
  241. aiecs/tasks/__init__.py +0 -1
  242. aiecs/tasks/worker.py +115 -47
  243. aiecs/tools/__init__.py +194 -72
  244. aiecs/tools/apisource/__init__.py +99 -0
  245. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  246. aiecs/tools/apisource/intelligence/data_fusion.py +632 -0
  247. aiecs/tools/apisource/intelligence/query_analyzer.py +417 -0
  248. aiecs/tools/apisource/intelligence/search_enhancer.py +385 -0
  249. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  250. aiecs/tools/apisource/monitoring/metrics.py +330 -0
  251. aiecs/tools/apisource/providers/__init__.py +112 -0
  252. aiecs/tools/apisource/providers/base.py +671 -0
  253. aiecs/tools/apisource/providers/census.py +397 -0
  254. aiecs/tools/apisource/providers/fred.py +535 -0
  255. aiecs/tools/apisource/providers/newsapi.py +409 -0
  256. aiecs/tools/apisource/providers/worldbank.py +352 -0
  257. aiecs/tools/apisource/reliability/__init__.py +12 -0
  258. aiecs/tools/apisource/reliability/error_handler.py +363 -0
  259. aiecs/tools/apisource/reliability/fallback_strategy.py +376 -0
  260. aiecs/tools/apisource/tool.py +832 -0
  261. aiecs/tools/apisource/utils/__init__.py +9 -0
  262. aiecs/tools/apisource/utils/validators.py +334 -0
  263. aiecs/tools/base_tool.py +415 -21
  264. aiecs/tools/docs/__init__.py +121 -0
  265. aiecs/tools/docs/ai_document_orchestrator.py +607 -0
  266. aiecs/tools/docs/ai_document_writer_orchestrator.py +2350 -0
  267. aiecs/tools/docs/content_insertion_tool.py +1320 -0
  268. aiecs/tools/docs/document_creator_tool.py +1323 -0
  269. aiecs/tools/docs/document_layout_tool.py +1160 -0
  270. aiecs/tools/docs/document_parser_tool.py +1011 -0
  271. aiecs/tools/docs/document_writer_tool.py +1829 -0
  272. aiecs/tools/knowledge_graph/__init__.py +17 -0
  273. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +807 -0
  274. aiecs/tools/knowledge_graph/graph_search_tool.py +944 -0
  275. aiecs/tools/knowledge_graph/kg_builder_tool.py +524 -0
  276. aiecs/tools/langchain_adapter.py +300 -138
  277. aiecs/tools/schema_generator.py +455 -0
  278. aiecs/tools/search_tool/__init__.py +100 -0
  279. aiecs/tools/search_tool/analyzers.py +581 -0
  280. aiecs/tools/search_tool/cache.py +264 -0
  281. aiecs/tools/search_tool/constants.py +128 -0
  282. aiecs/tools/search_tool/context.py +224 -0
  283. aiecs/tools/search_tool/core.py +778 -0
  284. aiecs/tools/search_tool/deduplicator.py +119 -0
  285. aiecs/tools/search_tool/error_handler.py +242 -0
  286. aiecs/tools/search_tool/metrics.py +343 -0
  287. aiecs/tools/search_tool/rate_limiter.py +172 -0
  288. aiecs/tools/search_tool/schemas.py +275 -0
  289. aiecs/tools/statistics/__init__.py +80 -0
  290. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +646 -0
  291. aiecs/tools/statistics/ai_insight_generator_tool.py +508 -0
  292. aiecs/tools/statistics/ai_report_orchestrator_tool.py +684 -0
  293. aiecs/tools/statistics/data_loader_tool.py +555 -0
  294. aiecs/tools/statistics/data_profiler_tool.py +638 -0
  295. aiecs/tools/statistics/data_transformer_tool.py +580 -0
  296. aiecs/tools/statistics/data_visualizer_tool.py +498 -0
  297. aiecs/tools/statistics/model_trainer_tool.py +507 -0
  298. aiecs/tools/statistics/statistical_analyzer_tool.py +472 -0
  299. aiecs/tools/task_tools/__init__.py +49 -36
  300. aiecs/tools/task_tools/chart_tool.py +200 -184
  301. aiecs/tools/task_tools/classfire_tool.py +268 -267
  302. aiecs/tools/task_tools/image_tool.py +175 -131
  303. aiecs/tools/task_tools/office_tool.py +226 -146
  304. aiecs/tools/task_tools/pandas_tool.py +477 -121
  305. aiecs/tools/task_tools/report_tool.py +390 -142
  306. aiecs/tools/task_tools/research_tool.py +149 -79
  307. aiecs/tools/task_tools/scraper_tool.py +339 -145
  308. aiecs/tools/task_tools/stats_tool.py +448 -209
  309. aiecs/tools/temp_file_manager.py +26 -24
  310. aiecs/tools/tool_executor/__init__.py +18 -16
  311. aiecs/tools/tool_executor/tool_executor.py +364 -52
  312. aiecs/utils/LLM_output_structor.py +74 -48
  313. aiecs/utils/__init__.py +14 -3
  314. aiecs/utils/base_callback.py +0 -3
  315. aiecs/utils/cache_provider.py +696 -0
  316. aiecs/utils/execution_utils.py +50 -31
  317. aiecs/utils/prompt_loader.py +1 -0
  318. aiecs/utils/token_usage_repository.py +37 -11
  319. aiecs/ws/socket_server.py +14 -4
  320. {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/METADATA +52 -15
  321. aiecs-1.7.6.dist-info/RECORD +337 -0
  322. aiecs-1.7.6.dist-info/entry_points.txt +13 -0
  323. aiecs/config/registry.py +0 -19
  324. aiecs/domain/context/content_engine.py +0 -982
  325. aiecs/llm/base_client.py +0 -99
  326. aiecs/llm/openai_client.py +0 -125
  327. aiecs/llm/vertex_client.py +0 -186
  328. aiecs/llm/xai_client.py +0 -184
  329. aiecs/scripts/dependency_checker.py +0 -857
  330. aiecs/scripts/quick_dependency_check.py +0 -269
  331. aiecs/tools/task_tools/search_api.py +0 -7
  332. aiecs-1.0.1.dist-info/RECORD +0 -90
  333. aiecs-1.0.1.dist-info/entry_points.txt +0 -7
  334. /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
  335. /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
  336. /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
  337. /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
  338. {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/WHEEL +0 -0
  339. {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/licenses/LICENSE +0 -0
  340. {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,855 @@
1
+ """
2
+ Query Planner
3
+
4
+ Translates natural language queries to structured graph query plans.
5
+ Decomposes complex queries into executable steps and optimizes execution order.
6
+ """
7
+
8
+ import uuid
9
+ import re
10
+ from typing import Optional, List, Dict, Any, Set, Union
11
+ from aiecs.infrastructure.graph_storage.base import GraphStore
12
+ from aiecs.domain.knowledge_graph.models.query import GraphQuery, QueryType
13
+ from aiecs.domain.knowledge_graph.models.query_plan import (
14
+ QueryPlan,
15
+ QueryStep,
16
+ QueryOperation,
17
+ OptimizationStrategy,
18
+ )
19
+ from aiecs.infrastructure.graph_storage.query_optimizer import (
20
+ QueryOptimizer,
21
+ QueryStatisticsCollector,
22
+ )
23
+
24
+ # Import LogicQueryParser for DSL support
25
+ try:
26
+ from aiecs.application.knowledge_graph.reasoning.logic_parser import (
27
+ LogicQueryParser,
28
+ ParserError,
29
+ )
30
+
31
+ LOGIC_PARSER_AVAILABLE = True
32
+ except ImportError:
33
+ LOGIC_PARSER_AVAILABLE = False
34
+ from typing import Any, TYPE_CHECKING
35
+ if TYPE_CHECKING:
36
+ LogicQueryParser: Any # type: ignore[assignment,no-redef]
37
+ ParserError: Any # type: ignore[assignment,no-redef]
38
+ else:
39
+ LogicQueryParser = None # type: ignore[assignment]
40
+ ParserError = None # type: ignore[assignment]
41
+
42
+
43
+ class QueryPlanner:
44
+ """
45
+ Query Planning Engine
46
+
47
+ Translates natural language queries into structured, optimized execution plans.
48
+
49
+ Features:
50
+ - Natural language to graph query translation
51
+ - Query decomposition (complex queries → multiple steps)
52
+ - Query optimization (reorder operations for efficiency)
53
+ - Cost estimation
54
+
55
+ Example::
56
+
57
+ planner = QueryPlanner(graph_store)
58
+
59
+ # Plan a complex query
60
+ plan = planner.plan_query(
61
+ "Who works at companies that Alice knows people at?"
62
+ )
63
+
64
+ # Optimize the plan
65
+ optimized_plan = planner.optimize_plan(
66
+ plan,
67
+ strategy=OptimizationStrategy.MINIMIZE_COST
68
+ )
69
+ """
70
+
71
+ def __init__(
72
+ self,
73
+ graph_store: GraphStore,
74
+ enable_advanced_optimization: bool = True,
75
+ schema: Optional[Any] = None,
76
+ ):
77
+ """
78
+ Initialize query planner
79
+
80
+ Args:
81
+ graph_store: Graph storage backend for queries
82
+ enable_advanced_optimization: Enable advanced query optimization (default: True)
83
+ schema: Optional schema manager for logic query validation
84
+ """
85
+ self.graph_store = graph_store
86
+ self.schema = schema
87
+
88
+ # Pattern templates for query understanding
89
+ self.query_patterns = self._initialize_query_patterns()
90
+
91
+ # Advanced query optimizer
92
+ self._enable_advanced_optimization = enable_advanced_optimization
93
+ # Type annotations for optional attributes
94
+ self._optimizer: Optional[QueryOptimizer]
95
+ self._statistics_collector: Optional[QueryStatisticsCollector]
96
+ self._logic_parser: Optional[LogicQueryParser]
97
+
98
+ if enable_advanced_optimization:
99
+ # Collect statistics from graph store
100
+ collector = QueryStatisticsCollector()
101
+ statistics = collector.collect_from_graph_store(graph_store)
102
+
103
+ # Initialize optimizer
104
+ self._optimizer = QueryOptimizer(statistics=statistics)
105
+ self._statistics_collector = collector
106
+ else:
107
+ self._optimizer = None
108
+ self._statistics_collector = None
109
+
110
+ # Logic query parser (if available)
111
+ if LOGIC_PARSER_AVAILABLE and schema is not None:
112
+ self._logic_parser = LogicQueryParser(schema=schema) # type: ignore[assignment]
113
+ else:
114
+ self._logic_parser = None
115
+
116
+ def _initialize_query_patterns(self) -> List[Dict[str, Any]]:
117
+ """Initialize query pattern matchers"""
118
+ return [
119
+ {
120
+ "pattern": r"find (.*?) with (.*?) = (['\"]?.+?['\"]?)",
121
+ "type": "entity_lookup_by_property",
122
+ "operations": ["filter"],
123
+ },
124
+ {
125
+ "pattern": r"who (works at|is employed by) (.*?)",
126
+ "type": "relation_traversal",
127
+ "operations": ["entity_lookup", "traversal"],
128
+ },
129
+ {
130
+ "pattern": r"what (companies|organizations) does (.*?) know people at",
131
+ "type": "multi_hop_query",
132
+ "operations": ["entity_lookup", "traversal", "traversal"],
133
+ },
134
+ {
135
+ "pattern": r"(similar|related) to (.*?)",
136
+ "type": "vector_search",
137
+ "operations": ["vector_search"],
138
+ },
139
+ {
140
+ "pattern": r"path from (.*?) to (.*?)",
141
+ "type": "path_finding",
142
+ "operations": ["path_finding"],
143
+ },
144
+ {
145
+ "pattern": r"neighbors of (.*?)",
146
+ "type": "neighbor_query",
147
+ "operations": ["entity_lookup", "traversal"],
148
+ },
149
+ ]
150
+
151
+ def plan_query(
152
+ self,
153
+ natural_language_query: str,
154
+ context: Optional[Dict[str, Any]] = None,
155
+ ) -> QueryPlan:
156
+ """
157
+ Create an execution plan from natural language query
158
+
159
+ Args:
160
+ natural_language_query: Natural language query string
161
+ context: Optional context (e.g., embeddings, entity IDs)
162
+
163
+ Returns:
164
+ Query execution plan
165
+
166
+ Example::
167
+
168
+ plan = planner.plan_query(
169
+ "Find papers similar to 'Deep Learning' and their authors"
170
+ )
171
+ """
172
+ context = context or {}
173
+ plan_id = f"plan_{uuid.uuid4().hex[:8]}"
174
+
175
+ # Analyze query structure
176
+ query_info = self._analyze_query(natural_language_query)
177
+
178
+ # Decompose into steps
179
+ steps = self._decompose_query(natural_language_query, query_info, context)
180
+
181
+ # Create plan
182
+ plan = QueryPlan(
183
+ plan_id=plan_id,
184
+ original_query=natural_language_query,
185
+ steps=steps,
186
+ explanation=self._generate_explanation(steps),
187
+ metadata={"query_info": query_info},
188
+ )
189
+
190
+ # Calculate total cost
191
+ plan.total_estimated_cost = plan.calculate_total_cost()
192
+
193
+ return plan
194
+
195
+ def _analyze_query(self, query: str) -> Dict[str, Any]:
196
+ """
197
+ Analyze query to determine type and complexity
198
+
199
+ Args:
200
+ query: Natural language query
201
+
202
+ Returns:
203
+ Query analysis information
204
+ """
205
+ query_lower = query.lower()
206
+
207
+ # Match against known patterns
208
+ matched_pattern = None
209
+ for pattern_info in self.query_patterns:
210
+ if re.search(pattern_info["pattern"], query_lower):
211
+ matched_pattern = pattern_info
212
+ break
213
+
214
+ # Determine complexity
215
+ is_multi_hop = any(
216
+ keyword in query_lower
217
+ for keyword in [
218
+ "who works at",
219
+ "people at",
220
+ "friends of",
221
+ "colleagues",
222
+ "through",
223
+ "connected to",
224
+ "related through",
225
+ ]
226
+ )
227
+
228
+ has_vector_search = any(keyword in query_lower for keyword in ["similar", "related", "like", "semantically"])
229
+
230
+ has_path_finding = any(keyword in query_lower for keyword in ["path", "route", "connection", "how to get"])
231
+
232
+ return {
233
+ "matched_pattern": matched_pattern,
234
+ "is_multi_hop": is_multi_hop,
235
+ "has_vector_search": has_vector_search,
236
+ "has_path_finding": has_path_finding,
237
+ "complexity": self._estimate_complexity(query_lower),
238
+ }
239
+
240
+ def _estimate_complexity(self, query: str) -> str:
241
+ """Estimate query complexity"""
242
+ hop_indicators = query.count("who") + query.count("what") + query.count("which")
243
+
244
+ if hop_indicators > 2 or "through" in query:
245
+ return "high"
246
+ elif hop_indicators > 0 or any(k in query for k in ["find", "get", "show"]):
247
+ return "medium"
248
+ else:
249
+ return "low"
250
+
251
+ def _decompose_query(self, query: str, query_info: Dict[str, Any], context: Dict[str, Any]) -> List[QueryStep]:
252
+ """
253
+ Decompose query into executable steps
254
+
255
+ Args:
256
+ query: Natural language query
257
+ query_info: Query analysis information
258
+ context: Query context
259
+
260
+ Returns:
261
+ List of query steps
262
+ """
263
+ steps = []
264
+
265
+ # Use matched pattern if available
266
+ if query_info["matched_pattern"]:
267
+ steps = self._create_steps_from_pattern(query, query_info["matched_pattern"], context)
268
+ else:
269
+ # Fall back to generic decomposition
270
+ steps = self._create_generic_steps(query, query_info, context)
271
+
272
+ return steps
273
+
274
+ def _create_steps_from_pattern(self, query: str, pattern_info: Dict[str, Any], context: Dict[str, Any]) -> List[QueryStep]:
275
+ """Create steps based on matched pattern"""
276
+ steps = []
277
+ query_type = pattern_info["type"]
278
+
279
+ if query_type == "entity_lookup_by_property":
280
+ # Single step: filter entities by property
281
+ steps.append(
282
+ QueryStep(
283
+ step_id="step_1",
284
+ operation=QueryOperation.FILTER,
285
+ query=GraphQuery(
286
+ query_type=QueryType.CUSTOM,
287
+ properties=context.get("properties", {}),
288
+ max_results=context.get("max_results", 10),
289
+ ),
290
+ description="Filter entities by properties",
291
+ estimated_cost=0.3,
292
+ )
293
+ )
294
+
295
+ elif query_type == "relation_traversal":
296
+ # Two steps: lookup entity, traverse relations
297
+ steps.append(
298
+ QueryStep(
299
+ step_id="step_1",
300
+ operation=QueryOperation.ENTITY_LOOKUP,
301
+ query=GraphQuery(
302
+ query_type=QueryType.ENTITY_LOOKUP,
303
+ entity_id=context.get("entity_id"),
304
+ max_results=1,
305
+ ),
306
+ description="Look up starting entity",
307
+ estimated_cost=0.2,
308
+ )
309
+ )
310
+
311
+ steps.append(
312
+ QueryStep(
313
+ step_id="step_2",
314
+ operation=QueryOperation.TRAVERSAL,
315
+ query=GraphQuery(
316
+ query_type=QueryType.TRAVERSAL,
317
+ relation_type=context.get("relation_type"),
318
+ max_depth=context.get("max_depth", 1),
319
+ max_results=context.get("max_results", 10),
320
+ ),
321
+ depends_on=["step_1"],
322
+ description="Traverse relations from starting entity",
323
+ estimated_cost=0.5,
324
+ )
325
+ )
326
+
327
+ elif query_type == "multi_hop_query":
328
+ # Multiple hops
329
+ steps = self._create_multi_hop_steps(query, context)
330
+
331
+ elif query_type == "vector_search":
332
+ # Single step: vector similarity search
333
+ steps.append(
334
+ QueryStep(
335
+ step_id="step_1",
336
+ operation=QueryOperation.VECTOR_SEARCH,
337
+ query=GraphQuery(
338
+ query_type=QueryType.VECTOR_SEARCH,
339
+ embedding=context.get("query_embedding"),
340
+ entity_type=context.get("entity_type"),
341
+ max_results=context.get("max_results", 10),
342
+ score_threshold=context.get("score_threshold", 0.7),
343
+ ),
344
+ description="Find semantically similar entities",
345
+ estimated_cost=0.4,
346
+ )
347
+ )
348
+
349
+ elif query_type == "path_finding":
350
+ # Single step: find path between entities
351
+ steps.append(
352
+ QueryStep(
353
+ step_id="step_1",
354
+ operation=QueryOperation.TRAVERSAL,
355
+ query=GraphQuery(
356
+ query_type=QueryType.PATH_FINDING,
357
+ source_entity_id=context.get("source_id"),
358
+ target_entity_id=context.get("target_id"),
359
+ max_depth=context.get("max_depth", 5),
360
+ max_results=context.get("max_results", 10),
361
+ ),
362
+ description="Find paths between entities",
363
+ estimated_cost=0.7,
364
+ )
365
+ )
366
+
367
+ elif query_type == "neighbor_query":
368
+ # Two steps: lookup + get neighbors
369
+ steps.append(
370
+ QueryStep(
371
+ step_id="step_1",
372
+ operation=QueryOperation.ENTITY_LOOKUP,
373
+ query=GraphQuery(
374
+ query_type=QueryType.ENTITY_LOOKUP,
375
+ entity_id=context.get("entity_id"),
376
+ max_results=1,
377
+ ),
378
+ description="Look up central entity",
379
+ estimated_cost=0.2,
380
+ )
381
+ )
382
+
383
+ steps.append(
384
+ QueryStep(
385
+ step_id="step_2",
386
+ operation=QueryOperation.TRAVERSAL,
387
+ query=GraphQuery(
388
+ query_type=QueryType.TRAVERSAL,
389
+ max_depth=1,
390
+ max_results=context.get("max_results", 20),
391
+ ),
392
+ depends_on=["step_1"],
393
+ description="Get neighboring entities",
394
+ estimated_cost=0.4,
395
+ )
396
+ )
397
+
398
+ return steps
399
+
400
+ def _create_multi_hop_steps(self, query: str, context: Dict[str, Any]) -> List[QueryStep]:
401
+ """Create steps for multi-hop query"""
402
+ steps = []
403
+ num_hops = context.get("num_hops", 2)
404
+
405
+ # Step 1: Find starting entity
406
+ steps.append(
407
+ QueryStep(
408
+ step_id="step_1",
409
+ operation=QueryOperation.ENTITY_LOOKUP,
410
+ query=GraphQuery(
411
+ query_type=QueryType.ENTITY_LOOKUP,
412
+ entity_id=context.get("start_entity_id"),
413
+ max_results=1,
414
+ ),
415
+ description="Find starting entity",
416
+ estimated_cost=0.2,
417
+ )
418
+ )
419
+
420
+ # Create hop steps
421
+ for i in range(num_hops):
422
+ hop_num = i + 1
423
+ step_id = f"step_{hop_num + 1}"
424
+ depends_on = [f"step_{hop_num}"]
425
+
426
+ steps.append(
427
+ QueryStep(
428
+ step_id=step_id,
429
+ operation=QueryOperation.TRAVERSAL,
430
+ query=GraphQuery(
431
+ query_type=QueryType.TRAVERSAL,
432
+ relation_type=context.get(f"hop{hop_num}_relation"),
433
+ max_depth=1,
434
+ max_results=context.get("max_results", 20),
435
+ ),
436
+ depends_on=depends_on,
437
+ description=f"Hop {hop_num}: Traverse to next level",
438
+ estimated_cost=0.4 + (0.1 * i), # Cost increases with depth
439
+ )
440
+ )
441
+
442
+ return steps
443
+
444
+ def _create_generic_steps(self, query: str, query_info: Dict[str, Any], context: Dict[str, Any]) -> List[QueryStep]:
445
+ """Create generic steps when no pattern matches"""
446
+ steps = []
447
+
448
+ # Priority 1: If start_entity_id is provided, use traversal
449
+ if context.get("start_entity_id"):
450
+ # Step 1: Lookup starting entity
451
+ steps.append(
452
+ QueryStep(
453
+ step_id="step_1",
454
+ operation=QueryOperation.ENTITY_LOOKUP,
455
+ query=GraphQuery(
456
+ query_type=QueryType.ENTITY_LOOKUP,
457
+ entity_id=context.get("start_entity_id"),
458
+ max_results=1,
459
+ ),
460
+ description="Look up starting entity",
461
+ estimated_cost=0.2,
462
+ )
463
+ )
464
+
465
+ # Step 2: Traverse from starting entity
466
+ target_id = context.get("target_entity_id")
467
+ if target_id:
468
+ # Path finding if target is specified
469
+ steps.append(
470
+ QueryStep(
471
+ step_id="step_2",
472
+ operation=QueryOperation.TRAVERSAL,
473
+ query=GraphQuery(
474
+ query_type=QueryType.PATH_FINDING,
475
+ source_entity_id=context.get("start_entity_id"),
476
+ target_entity_id=target_id,
477
+ max_depth=context.get("max_hops", 3),
478
+ max_results=context.get("max_results", 10),
479
+ ),
480
+ depends_on=["step_1"],
481
+ description="Find paths from start to target entity",
482
+ estimated_cost=0.6,
483
+ )
484
+ )
485
+ else:
486
+ # General traversal if no target
487
+ steps.append(
488
+ QueryStep(
489
+ step_id="step_2",
490
+ operation=QueryOperation.TRAVERSAL,
491
+ query=GraphQuery(
492
+ query_type=QueryType.TRAVERSAL,
493
+ entity_id=context.get("start_entity_id"),
494
+ relation_type=(context.get("relation_types", [None])[0] if context.get("relation_types") else None),
495
+ max_depth=context.get("max_hops", 3),
496
+ max_results=context.get("max_results", 10),
497
+ ),
498
+ depends_on=["step_1"],
499
+ description="Traverse from starting entity",
500
+ estimated_cost=0.5,
501
+ )
502
+ )
503
+
504
+ # Priority 2: If query_embedding is provided, use vector search
505
+ elif context.get("query_embedding"):
506
+ steps.append(
507
+ QueryStep(
508
+ step_id="step_1",
509
+ operation=QueryOperation.VECTOR_SEARCH,
510
+ query=GraphQuery(
511
+ query_type=QueryType.VECTOR_SEARCH,
512
+ embedding=context.get("query_embedding"),
513
+ entity_type=context.get("entity_type"),
514
+ max_results=context.get("max_results", 10),
515
+ score_threshold=context.get("score_threshold", 0.5),
516
+ ),
517
+ description="Search for relevant entities using vector similarity",
518
+ estimated_cost=0.5,
519
+ )
520
+ )
521
+
522
+ # Priority 3: Default fallback - entity lookup by type if entity_type
523
+ # is provided
524
+ elif context.get("entity_type"):
525
+ steps.append(
526
+ QueryStep(
527
+ step_id="step_1",
528
+ operation=QueryOperation.FILTER,
529
+ query=GraphQuery(
530
+ query_type=QueryType.ENTITY_LOOKUP,
531
+ entity_type=context.get("entity_type"),
532
+ max_results=context.get("max_results", 10),
533
+ ),
534
+ description=f"Filter entities by type: {context.get('entity_type')}",
535
+ estimated_cost=0.3,
536
+ )
537
+ )
538
+
539
+ # Priority 4: Last resort - simple vector search (may not work without
540
+ # embeddings)
541
+ else:
542
+ steps.append(
543
+ QueryStep(
544
+ step_id="step_1",
545
+ operation=QueryOperation.VECTOR_SEARCH,
546
+ query=GraphQuery(
547
+ query_type=QueryType.VECTOR_SEARCH,
548
+ embedding=None, # Will need to be generated
549
+ max_results=context.get("max_results", 10),
550
+ score_threshold=0.5,
551
+ ),
552
+ description="Search for relevant entities (fallback - may not work without embeddings)",
553
+ estimated_cost=0.5,
554
+ )
555
+ )
556
+
557
+ return steps
558
+
559
+ def _generate_explanation(self, steps: List[QueryStep]) -> str:
560
+ """Generate human-readable explanation of plan"""
561
+ if not steps:
562
+ return "No steps in plan"
563
+
564
+ if len(steps) == 1:
565
+ return f"Single-step query: {steps[0].description}"
566
+
567
+ parts = [f"Multi-step query with {len(steps)} steps:"]
568
+ for i, step in enumerate(steps, 1):
569
+ parts.append(f"{i}. {step.description}")
570
+
571
+ return "\n".join(parts)
572
+
573
+ def optimize_plan(
574
+ self,
575
+ plan: QueryPlan,
576
+ strategy: OptimizationStrategy = OptimizationStrategy.BALANCED,
577
+ ) -> QueryPlan:
578
+ """
579
+ Optimize query execution plan
580
+
581
+ Args:
582
+ plan: Original query plan
583
+ strategy: Optimization strategy
584
+
585
+ Returns:
586
+ Optimized query plan
587
+
588
+ Example::
589
+
590
+ optimized = planner.optimize_plan(
591
+ plan,
592
+ strategy=OptimizationStrategy.MINIMIZE_COST
593
+ )
594
+ """
595
+ if plan.optimized:
596
+ return plan # Already optimized
597
+
598
+ # Use advanced optimizer if enabled
599
+ if self._enable_advanced_optimization and self._optimizer:
600
+ result = self._optimizer.optimize(plan)
601
+ return result.optimized_plan
602
+
603
+ # Fall back to basic optimization
604
+ optimized_steps = list(plan.steps)
605
+
606
+ if strategy == OptimizationStrategy.MINIMIZE_COST:
607
+ optimized_steps = self._optimize_for_cost(optimized_steps)
608
+ elif strategy == OptimizationStrategy.MINIMIZE_LATENCY:
609
+ optimized_steps = self._optimize_for_latency(optimized_steps)
610
+ else: # BALANCED
611
+ optimized_steps = self._optimize_balanced(optimized_steps)
612
+
613
+ # Create optimized plan
614
+ optimized_plan = QueryPlan(
615
+ plan_id=plan.plan_id + "_opt",
616
+ original_query=plan.original_query,
617
+ steps=optimized_steps,
618
+ optimized=True,
619
+ explanation=plan.explanation + "\n(Optimized)",
620
+ metadata=plan.metadata,
621
+ )
622
+
623
+ optimized_plan.total_estimated_cost = optimized_plan.calculate_total_cost()
624
+
625
+ return optimized_plan
626
+
627
+ def _optimize_for_cost(self, steps: List[QueryStep]) -> List[QueryStep]:
628
+ """
629
+ Optimize to minimize total cost
630
+
631
+ Strategy: Execute cheaper operations first when possible
632
+ """
633
+ # Group steps by dependency level
634
+ levels = self._get_dependency_levels(steps)
635
+
636
+ optimized = []
637
+ for level_steps in levels:
638
+ # Sort by cost (ascending) within each level
639
+ sorted_level = sorted(level_steps, key=lambda s: s.estimated_cost)
640
+ optimized.extend(sorted_level)
641
+
642
+ return optimized
643
+
644
+ def _optimize_for_latency(self, steps: List[QueryStep]) -> List[QueryStep]:
645
+ """
646
+ Optimize to minimize latency
647
+
648
+ Strategy: Maximize parallelization
649
+ """
650
+ # Already maximized in get_execution_order()
651
+ # Just return original order
652
+ return steps
653
+
654
+ def _optimize_balanced(self, steps: List[QueryStep]) -> List[QueryStep]:
655
+ """
656
+ Balanced optimization
657
+
658
+ Strategy: Balance cost and latency
659
+ """
660
+ levels = self._get_dependency_levels(steps)
661
+
662
+ optimized = []
663
+ for level_steps in levels:
664
+ # Sort by cost but not too aggressively
665
+ # Keep expensive operations that can run in parallel
666
+ sorted_level = sorted(
667
+ level_steps,
668
+ key=lambda s: (s.estimated_cost > 0.7, s.estimated_cost),
669
+ )
670
+ optimized.extend(sorted_level)
671
+
672
+ return optimized
673
+
674
+ def _get_dependency_levels(self, steps: List[QueryStep]) -> List[List[QueryStep]]:
675
+ """
676
+ Group steps by dependency level
677
+
678
+ Returns:
679
+ List of lists, each containing steps at the same dependency level
680
+ """
681
+ # step_map = {step.step_id: step for step in steps} # Reserved for
682
+ # future use
683
+ levels: List[List[QueryStep]] = []
684
+ processed: Set[str] = set()
685
+
686
+ while len(processed) < len(steps):
687
+ current_level = []
688
+ for step in steps:
689
+ if step.step_id in processed:
690
+ continue
691
+ # Check if all dependencies are processed
692
+ if all(dep in processed for dep in step.depends_on):
693
+ current_level.append(step)
694
+
695
+ if not current_level:
696
+ break # Should not happen with valid dependencies
697
+
698
+ levels.append(current_level)
699
+ processed.update(step.step_id for step in current_level)
700
+
701
+ return levels
702
+
703
+ def translate_to_graph_query(
704
+ self,
705
+ natural_language_query: str,
706
+ context: Optional[Dict[str, Any]] = None,
707
+ ) -> GraphQuery:
708
+ """
709
+ Translate natural language to a single graph query
710
+
711
+ For simple queries that don't need decomposition.
712
+
713
+ Args:
714
+ natural_language_query: Natural language query
715
+ context: Query context (embeddings, entity IDs, etc.)
716
+
717
+ Returns:
718
+ Single graph query
719
+
720
+ Example::
721
+
722
+ query = planner.translate_to_graph_query(
723
+ "Find entities similar to X",
724
+ context={"query_embedding": [0.1, 0.2, ...]}
725
+ )
726
+ """
727
+ context = context or {}
728
+ query_lower = natural_language_query.lower()
729
+
730
+ # Determine query type
731
+ if "similar" in query_lower or "related" in query_lower:
732
+ return GraphQuery(
733
+ query_type=QueryType.VECTOR_SEARCH,
734
+ embedding=context.get("query_embedding"),
735
+ entity_type=context.get("entity_type"),
736
+ max_results=context.get("max_results", 10),
737
+ score_threshold=context.get("score_threshold", 0.7),
738
+ )
739
+
740
+ elif "path" in query_lower:
741
+ return GraphQuery(
742
+ query_type=QueryType.PATH_FINDING,
743
+ source_entity_id=context.get("source_id"),
744
+ target_entity_id=context.get("target_id"),
745
+ max_depth=context.get("max_depth", 5),
746
+ max_results=context.get("max_results", 10),
747
+ )
748
+
749
+ elif "neighbor" in query_lower or "connected to" in query_lower:
750
+ return GraphQuery(
751
+ query_type=QueryType.TRAVERSAL,
752
+ entity_id=context.get("entity_id"),
753
+ relation_type=context.get("relation_type"),
754
+ max_depth=1,
755
+ max_results=context.get("max_results", 20),
756
+ )
757
+
758
+ else:
759
+ # Default to entity lookup
760
+ return GraphQuery(
761
+ query_type=QueryType.ENTITY_LOOKUP,
762
+ entity_id=context.get("entity_id"),
763
+ entity_type=context.get("entity_type"),
764
+ properties=context.get("properties", {}),
765
+ max_results=context.get("max_results", 10),
766
+ )
767
+
768
+ # Advanced Optimization Methods
769
+
770
+ def update_statistics(self) -> None:
771
+ """
772
+ Update query statistics from graph store
773
+
774
+ Call this periodically to keep optimizer statistics up-to-date
775
+ """
776
+ if self._enable_advanced_optimization and self._statistics_collector and self._optimizer:
777
+ statistics = self._statistics_collector.collect_from_graph_store(self.graph_store)
778
+ self._optimizer.update_statistics(statistics)
779
+
780
+ def record_execution_time(self, execution_time_ms: float) -> None:
781
+ """
782
+ Record query execution time for statistics
783
+
784
+ Args:
785
+ execution_time_ms: Execution time in milliseconds
786
+ """
787
+ if self._statistics_collector:
788
+ self._statistics_collector.record_execution_time(execution_time_ms)
789
+
790
+ def get_optimizer_stats(self) -> Dict[str, Any]:
791
+ """
792
+ Get optimizer statistics
793
+
794
+ Returns:
795
+ Dictionary with optimizer statistics
796
+ """
797
+ if not self._enable_advanced_optimization or not self._optimizer:
798
+ return {"enabled": False}
799
+
800
+ return {
801
+ "enabled": True,
802
+ "optimizations_performed": self._optimizer.get_optimization_count(),
803
+ "avg_execution_time_ms": (self._statistics_collector.get_average_execution_time() if self._statistics_collector else 0.0),
804
+ "p95_execution_time_ms": (self._statistics_collector.get_execution_percentile(0.95) if self._statistics_collector else 0.0),
805
+ "entity_count": self._optimizer.statistics.entity_count,
806
+ "relation_count": self._optimizer.statistics.relation_count,
807
+ "avg_degree": self._optimizer.statistics.avg_degree,
808
+ }
809
+
810
+ # ========================================================================
811
+ # Logic Query Support
812
+ # ========================================================================
813
+
814
+ def plan_logic_query(self, logic_query: str) -> Union[QueryPlan, List[Any]]:
815
+ """
816
+ Create execution plan from logic query DSL
817
+
818
+ This method parses a logic query (e.g., "Find(Person) WHERE age > 30")
819
+ and converts it directly to a QueryPlan.
820
+
821
+ Args:
822
+ logic_query: Logic query string in DSL format
823
+
824
+ Returns:
825
+ QueryPlan if successful, List[ParserError] if errors occurred
826
+
827
+ Example::
828
+
829
+ plan = planner.plan_logic_query("Find(Person) WHERE age > 30")
830
+
831
+ if isinstance(plan, list):
832
+ # Parsing errors
833
+ for error in plan:
834
+ print(f"Error at line {error.line}: {error.message}")
835
+ else:
836
+ # Success - execute the plan
837
+ result = await graph_store.execute_plan(plan)
838
+ """
839
+ if not LOGIC_PARSER_AVAILABLE:
840
+ raise ImportError("Logic parser not available. Install lark-parser.")
841
+
842
+ if self._logic_parser is None:
843
+ raise ValueError("Logic parser not initialized. Provide schema to QueryPlanner.")
844
+
845
+ # Parse logic query to QueryPlan
846
+ return self._logic_parser.parse_to_query_plan(logic_query)
847
+
848
+ def supports_logic_queries(self) -> bool:
849
+ """
850
+ Check if logic query support is available
851
+
852
+ Returns:
853
+ True if logic queries are supported, False otherwise
854
+ """
855
+ return LOGIC_PARSER_AVAILABLE and self._logic_parser is not None