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,866 @@
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: List[ValidationError] = []
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: List[ValidationError] = []
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: List[ValidationError] = []
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: List[ValidationError] = []
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: List[ValidationError] = []
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(self, entity_type: str, line: int, column: int) -> List[ValidationError]:
337
+ """
338
+ Validate that an entity type exists in the schema
339
+
340
+ Args:
341
+ entity_type: Entity type name to validate
342
+ line: Line number for error reporting
343
+ column: Column number for error reporting
344
+
345
+ Returns:
346
+ List of validation errors
347
+ """
348
+ errors: List[ValidationError] = []
349
+
350
+ # Check if schema has the method
351
+ if not hasattr(self.schema, "get_entity_type"):
352
+ return errors
353
+
354
+ # Check if entity type exists
355
+ entity_schema = self.schema.get_entity_type(entity_type)
356
+ if entity_schema is None:
357
+ # Get available types for suggestion
358
+ available_types = []
359
+ if hasattr(self.schema, "list_entity_types"):
360
+ available_types = self.schema.list_entity_types()
361
+
362
+ suggestion = None
363
+ if available_types:
364
+ suggestion = f"Available entity types: {', '.join(available_types[:5])}"
365
+ if len(available_types) > 5:
366
+ suggestion += f" (and {len(available_types) - 5} more)"
367
+
368
+ errors.append(
369
+ ValidationError(
370
+ line=line,
371
+ column=column,
372
+ message=f"Entity type '{entity_type}' not found in schema",
373
+ suggestion=suggestion,
374
+ )
375
+ )
376
+
377
+ return errors
378
+
379
+ def validate_relation_type(self, relation_type: str, line: int, column: int) -> List[ValidationError]:
380
+ """
381
+ Validate that a relation type exists in the schema
382
+
383
+ Args:
384
+ relation_type: Relation type name to validate
385
+ line: Line number for error reporting
386
+ column: Column number for error reporting
387
+
388
+ Returns:
389
+ List of validation errors
390
+ """
391
+ errors: List[ValidationError] = []
392
+
393
+ # Check if schema has the method
394
+ if not hasattr(self.schema, "get_relation_type"):
395
+ return errors
396
+
397
+ # Check if relation type exists
398
+ relation_schema = self.schema.get_relation_type(relation_type)
399
+ if relation_schema is None:
400
+ # Get available types for suggestion
401
+ available_types = []
402
+ if hasattr(self.schema, "list_relation_types"):
403
+ available_types = self.schema.list_relation_types()
404
+
405
+ suggestion = None
406
+ if available_types:
407
+ suggestion = f"Available relation types: {', '.join(available_types[:5])}"
408
+ if len(available_types) > 5:
409
+ suggestion += f" (and {len(available_types) - 5} more)"
410
+
411
+ errors.append(
412
+ ValidationError(
413
+ line=line,
414
+ column=column,
415
+ message=f"Relation type '{relation_type}' not found in schema",
416
+ suggestion=suggestion,
417
+ )
418
+ )
419
+
420
+ return errors
421
+
422
+ def validate_property(self, entity_type: str, property_path: str, line: int, column: int) -> List[ValidationError]:
423
+ """
424
+ Validate that a property exists in an entity type
425
+
426
+ Supports nested property paths (e.g., "address.city") by recursively
427
+ validating each part of the path.
428
+
429
+ Args:
430
+ entity_type: Entity type name
431
+ property_path: Property path (may be nested like "address.city")
432
+ line: Line number for error reporting
433
+ column: Column number for error reporting
434
+
435
+ Returns:
436
+ List of validation errors
437
+ """
438
+ errors: List[ValidationError] = []
439
+
440
+ # Get entity type schema
441
+ if not hasattr(self.schema, "get_entity_type"):
442
+ return errors
443
+
444
+ entity_schema = self.schema.get_entity_type(entity_type)
445
+ if entity_schema is None:
446
+ return errors # Entity type error already reported
447
+
448
+ # Validate nested property path recursively
449
+ errors.extend(
450
+ self._validate_nested_property_path(
451
+ entity_schema=entity_schema,
452
+ property_path=property_path,
453
+ entity_type=entity_type,
454
+ line=line,
455
+ column=column,
456
+ )
457
+ )
458
+
459
+ return errors
460
+
461
+ def _validate_nested_property_path(
462
+ self,
463
+ entity_schema: Any,
464
+ property_path: str,
465
+ entity_type: str,
466
+ line: int,
467
+ column: int,
468
+ current_path: str = "",
469
+ ) -> List[ValidationError]:
470
+ """
471
+ Recursively validate a nested property path
472
+
473
+ Args:
474
+ entity_schema: Current entity schema (may be nested)
475
+ property_path: Remaining property path to validate
476
+ entity_type: Root entity type name
477
+ line: Line number for error reporting
478
+ column: Column number for error reporting
479
+ current_path: Accumulated path so far (for error messages)
480
+
481
+ Returns:
482
+ List of validation errors
483
+ """
484
+ errors: List[ValidationError] = []
485
+
486
+ # Split property path into parts
487
+ property_parts = property_path.split(".")
488
+ current_property = property_parts[0]
489
+ remaining_path = ".".join(property_parts[1:]) if len(property_parts) > 1 else None
490
+
491
+ # Build full path for error messages
492
+ full_path = f"{current_path}.{current_property}" if current_path else current_property
493
+
494
+ # Check if entity schema has get_property method
495
+ if not hasattr(entity_schema, "get_property"):
496
+ return errors
497
+
498
+ # Get property schema
499
+ property_schema = entity_schema.get_property(current_property)
500
+ if property_schema is None:
501
+ # Property doesn't exist - get available properties for suggestion
502
+ available_props = []
503
+ if hasattr(entity_schema, "properties"):
504
+ available_props = list(entity_schema.properties.keys())
505
+ elif hasattr(entity_schema, "get_property_names"):
506
+ available_props = entity_schema.get_property_names()
507
+
508
+ suggestion = None
509
+ if available_props:
510
+ context = f"{entity_type}.{current_path}" if current_path else entity_type
511
+ suggestion = f"Available properties for {context}: {', '.join(available_props[:5])}"
512
+ if len(available_props) > 5:
513
+ suggestion += f" (and {len(available_props) - 5} more)"
514
+
515
+ errors.append(
516
+ ValidationError(
517
+ line=line,
518
+ column=column,
519
+ message=f"Property '{full_path}' not found in {entity_type if not current_path else current_path}",
520
+ suggestion=suggestion,
521
+ )
522
+ )
523
+ return errors # Can't continue validation if property doesn't exist
524
+
525
+ # Check if there's more nesting to validate
526
+ if remaining_path:
527
+ # Check if current property is DICT type (supports nesting)
528
+ if not hasattr(property_schema, "property_type"):
529
+ # Can't determine if nesting is supported
530
+ errors.append(
531
+ ValidationError(
532
+ line=line,
533
+ column=column,
534
+ message=f"Cannot validate nested path '{full_path}.{remaining_path}': "
535
+ f"property '{current_property}' type unknown",
536
+ suggestion="Ensure property schema defines property_type",
537
+ )
538
+ )
539
+ return errors
540
+
541
+ property_type = property_schema.property_type
542
+
543
+ # Check if property type supports nesting
544
+ # DICT type supports nesting, but we need nested schema
545
+ if hasattr(property_type, "value"):
546
+ type_value = property_type.value
547
+ elif hasattr(property_type, "name"):
548
+ type_value = property_type.name
549
+ else:
550
+ type_value = str(property_type)
551
+
552
+ # Import PropertyType to check if it's DICT
553
+ from aiecs.domain.knowledge_graph.schema.property_schema import PropertyType
554
+
555
+ if type_value == PropertyType.DICT.value or type_value == "dict":
556
+ # Property is DICT type - check for nested schema
557
+ nested_schema = self._get_nested_schema(property_schema)
558
+ if nested_schema is None:
559
+ # No nested schema defined - can't validate deeper nesting
560
+ errors.append(
561
+ ValidationError(
562
+ line=line,
563
+ column=column,
564
+ message=f"Cannot validate nested path '{full_path}.{remaining_path}': "
565
+ f"property '{current_property}' is DICT type but nested schema not defined",
566
+ suggestion=f"Define nested schema for '{current_property}' or use flat property path",
567
+ )
568
+ )
569
+ return errors
570
+
571
+ # Recursively validate remaining path
572
+ errors.extend(
573
+ self._validate_nested_property_path(
574
+ entity_schema=nested_schema,
575
+ property_path=remaining_path,
576
+ entity_type=entity_type,
577
+ line=line,
578
+ column=column,
579
+ current_path=full_path,
580
+ )
581
+ )
582
+ else:
583
+ # Property is not DICT type - can't nest further
584
+ errors.append(
585
+ ValidationError(
586
+ line=line,
587
+ column=column,
588
+ message=f"Cannot access nested path '{full_path}.{remaining_path}': "
589
+ f"property '{current_property}' is {type_value} type, not DICT",
590
+ suggestion=f"Use '{full_path}' directly or change property type to DICT",
591
+ )
592
+ )
593
+
594
+ return errors
595
+
596
+ def _get_nested_schema(self, property_schema: Any) -> Optional[Any]:
597
+ """
598
+ Get nested schema for a DICT property
599
+
600
+ Checks for nested schema in multiple ways:
601
+ 1. property_schema.nested_schema attribute
602
+ 2. property_schema.schema attribute (if not a callable method)
603
+ 3. property_schema.properties attribute (treat as EntityType-like)
604
+
605
+ Args:
606
+ property_schema: Property schema to get nested schema from
607
+
608
+ Returns:
609
+ Nested schema object or None if not found
610
+ """
611
+ # Check for explicit nested_schema attribute
612
+ if hasattr(property_schema, "nested_schema"):
613
+ nested_schema = getattr(property_schema, "nested_schema", None)
614
+ if nested_schema is not None:
615
+ return nested_schema
616
+
617
+ # Check for schema attribute (but not if it's a callable method)
618
+ if hasattr(property_schema, "schema"):
619
+ schema_attr = getattr(property_schema, "schema", None)
620
+ # Only use if it's not callable (Pydantic models have schema() method)
621
+ if schema_attr is not None and not callable(schema_attr):
622
+ return schema_attr
623
+
624
+ # Check if property_schema has properties attribute (treat as EntityType-like)
625
+ if hasattr(property_schema, "properties"):
626
+ properties = getattr(property_schema, "properties", None)
627
+ # Only use if it's a dict-like structure (not a Pydantic method)
628
+ if properties and isinstance(properties, dict) and len(properties) > 0:
629
+ # Create a mock entity schema-like object
630
+ class NestedSchema:
631
+ def __init__(self, properties):
632
+ self.properties = properties
633
+
634
+ def get_property(self, property_name: str):
635
+ if isinstance(self.properties, dict):
636
+ return self.properties.get(property_name)
637
+ return None
638
+
639
+ def get_property_names(self):
640
+ if isinstance(self.properties, dict):
641
+ return list(self.properties.keys())
642
+ return []
643
+
644
+ return NestedSchema(properties)
645
+
646
+ return None
647
+
648
+ def validate_property_value_type(
649
+ self,
650
+ entity_type: str,
651
+ property_path: str,
652
+ value: Any,
653
+ operator: str,
654
+ line: int,
655
+ column: int,
656
+ ) -> List[ValidationError]:
657
+ """
658
+ Validate that a property value matches the expected type
659
+
660
+ Supports nested property paths by recursively finding the final property schema.
661
+
662
+ Args:
663
+ entity_type: Entity type name
664
+ property_path: Property path (may be nested like "address.city")
665
+ value: Value to validate
666
+ operator: Operator being used
667
+ line: Line number for error reporting
668
+ column: Column number for error reporting
669
+
670
+ Returns:
671
+ List of validation errors
672
+ """
673
+ errors: List[ValidationError] = []
674
+
675
+ # Get entity type schema
676
+ if not hasattr(self.schema, "get_entity_type"):
677
+ return errors
678
+
679
+ entity_schema = self.schema.get_entity_type(entity_type)
680
+ if entity_schema is None:
681
+ return errors
682
+
683
+ # Get property schema for nested path
684
+ property_schema = self._get_property_schema_for_path(entity_schema, property_path)
685
+ if property_schema is None:
686
+ return errors # Property error already reported
687
+
688
+ # Get property type
689
+ if not hasattr(property_schema, "property_type"):
690
+ return errors
691
+
692
+ property_type = property_schema.property_type
693
+
694
+ # Map property types to Python types
695
+ type_map = {
696
+ "STRING": str,
697
+ "INTEGER": int,
698
+ "FLOAT": float,
699
+ "BOOLEAN": bool,
700
+ "DATE": str, # Dates are typically strings in queries
701
+ "DATETIME": str,
702
+ }
703
+
704
+ # Get expected Python type
705
+ expected_type = None
706
+ if hasattr(property_type, "value"):
707
+ # Enum type
708
+ expected_type = type_map.get(property_type.value)
709
+ elif hasattr(property_type, "name"):
710
+ # String type name
711
+ expected_type = type_map.get(property_type.name)
712
+ elif isinstance(property_type, str):
713
+ # Direct string type
714
+ expected_type = type_map.get(property_type)
715
+
716
+ if expected_type is None:
717
+ return errors # Unknown type, skip validation
718
+
719
+ # For IN operator, check list elements
720
+ if operator == "IN":
721
+ if isinstance(value, list):
722
+ for item in value:
723
+ if not isinstance(item, expected_type):
724
+ errors.append(
725
+ ValidationError(
726
+ line=line,
727
+ column=column,
728
+ message=f"Property '{property_path}' expects {expected_type.__name__} values, "
729
+ f"but list contains {type(item).__name__}",
730
+ suggestion=f"Ensure all list values are {expected_type.__name__}",
731
+ )
732
+ )
733
+ break # Only report once
734
+ return errors
735
+
736
+ # Check value type
737
+ if not isinstance(value, expected_type):
738
+ errors.append(
739
+ ValidationError(
740
+ line=line,
741
+ column=column,
742
+ message=f"Property '{property_path}' expects {expected_type.__name__} value, "
743
+ f"got {type(value).__name__}",
744
+ suggestion=f"Use a {expected_type.__name__} value",
745
+ )
746
+ )
747
+
748
+ return errors
749
+
750
+ def _get_property_schema_for_path(self, entity_schema: Any, property_path: str) -> Optional[Any]:
751
+ """
752
+ Get property schema for a nested property path
753
+
754
+ Args:
755
+ entity_schema: Entity schema to start from
756
+ property_path: Property path (may be nested like "address.city")
757
+
758
+ Returns:
759
+ Property schema for the final property in the path, or None if not found
760
+ """
761
+ property_parts = property_path.split(".")
762
+ current_schema = entity_schema
763
+
764
+ for i, part in enumerate(property_parts):
765
+ if not hasattr(current_schema, "get_property"):
766
+ return None
767
+
768
+ property_schema = current_schema.get_property(part)
769
+ if property_schema is None:
770
+ return None
771
+
772
+ # If this is the last part, return the property schema
773
+ if i == len(property_parts) - 1:
774
+ return property_schema
775
+
776
+ # Otherwise, check if this property supports nesting
777
+ if not hasattr(property_schema, "property_type"):
778
+ return None
779
+
780
+ property_type = property_schema.property_type
781
+ type_value = None
782
+ if hasattr(property_type, "value"):
783
+ type_value = property_type.value
784
+ elif hasattr(property_type, "name"):
785
+ type_value = property_type.name
786
+ else:
787
+ type_value = str(property_type)
788
+
789
+ # Import PropertyType to check if it's DICT
790
+ from aiecs.domain.knowledge_graph.schema.property_schema import PropertyType
791
+
792
+ if type_value == PropertyType.DICT.value or type_value == "dict":
793
+ # Get nested schema for next iteration
794
+ nested_schema = self._get_nested_schema(property_schema)
795
+ if nested_schema is None:
796
+ return None
797
+ current_schema = nested_schema
798
+ else:
799
+ # Can't nest further
800
+ return None
801
+
802
+ return None
803
+
804
+ def validate_relation_endpoints(
805
+ self,
806
+ relation_type: str,
807
+ current_entity_type: str,
808
+ direction: str,
809
+ line: int,
810
+ column: int,
811
+ ) -> List[ValidationError]:
812
+ """
813
+ Validate that relation endpoints match entity types
814
+
815
+ Args:
816
+ relation_type: Relation type name
817
+ current_entity_type: Current entity type in the query
818
+ direction: Direction of traversal ("incoming" or "outgoing")
819
+ line: Line number for error reporting
820
+ column: Column number for error reporting
821
+
822
+ Returns:
823
+ List of validation errors
824
+ """
825
+ errors: List[ValidationError] = []
826
+
827
+ # Get relation type schema
828
+ if not hasattr(self.schema, "get_relation_type"):
829
+ return errors
830
+
831
+ relation_schema = self.schema.get_relation_type(relation_type)
832
+ if relation_schema is None:
833
+ return errors # Relation type error already reported
834
+
835
+ # Check if relation has endpoint constraints
836
+ if not hasattr(relation_schema, "source_entity_types") or not hasattr(relation_schema, "target_entity_types"):
837
+ return errors
838
+
839
+ source_types = relation_schema.source_entity_types
840
+ target_types = relation_schema.target_entity_types
841
+
842
+ # Validate based on direction
843
+ if direction == "outgoing":
844
+ # Current entity is source, check if it's allowed
845
+ if source_types and current_entity_type not in source_types:
846
+ errors.append(
847
+ ValidationError(
848
+ line=line,
849
+ column=column,
850
+ message=f"Entity type '{current_entity_type}' cannot be source of relation '{relation_type}'",
851
+ suggestion=f"Allowed source types: {', '.join(source_types)}",
852
+ )
853
+ )
854
+ elif direction == "incoming":
855
+ # Current entity is target, check if it's allowed
856
+ if target_types and current_entity_type not in target_types:
857
+ errors.append(
858
+ ValidationError(
859
+ line=line,
860
+ column=column,
861
+ message=f"Entity type '{current_entity_type}' cannot be target of relation '{relation_type}'",
862
+ suggestion=f"Allowed target types: {', '.join(target_types)}",
863
+ )
864
+ )
865
+
866
+ return errors