aiecs 1.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. aiecs/__init__.py +72 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +469 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +363 -0
  7. aiecs/application/knowledge_graph/__init__.py +7 -0
  8. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
  11. aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
  12. aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
  13. aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
  14. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  15. aiecs/application/knowledge_graph/extractors/base.py +100 -0
  16. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
  17. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
  18. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
  19. aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
  20. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
  21. aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
  22. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
  23. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
  24. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  25. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
  26. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
  27. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  28. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
  29. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  30. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  31. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
  32. aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
  33. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
  34. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  35. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  36. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
  37. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
  38. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
  39. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
  40. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
  41. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
  42. aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
  43. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
  44. aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
  45. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
  46. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  47. aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
  48. aiecs/application/knowledge_graph/search/reranker.py +295 -0
  49. aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
  50. aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
  51. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  52. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
  53. aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
  54. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  55. aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
  56. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  57. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
  58. aiecs/common/__init__.py +9 -0
  59. aiecs/common/knowledge_graph/__init__.py +17 -0
  60. aiecs/common/knowledge_graph/runnable.py +484 -0
  61. aiecs/config/__init__.py +16 -0
  62. aiecs/config/config.py +498 -0
  63. aiecs/config/graph_config.py +137 -0
  64. aiecs/config/registry.py +23 -0
  65. aiecs/core/__init__.py +46 -0
  66. aiecs/core/interface/__init__.py +34 -0
  67. aiecs/core/interface/execution_interface.py +152 -0
  68. aiecs/core/interface/storage_interface.py +171 -0
  69. aiecs/domain/__init__.py +289 -0
  70. aiecs/domain/agent/__init__.py +189 -0
  71. aiecs/domain/agent/base_agent.py +697 -0
  72. aiecs/domain/agent/exceptions.py +103 -0
  73. aiecs/domain/agent/graph_aware_mixin.py +559 -0
  74. aiecs/domain/agent/hybrid_agent.py +490 -0
  75. aiecs/domain/agent/integration/__init__.py +26 -0
  76. aiecs/domain/agent/integration/context_compressor.py +222 -0
  77. aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
  78. aiecs/domain/agent/integration/retry_policy.py +219 -0
  79. aiecs/domain/agent/integration/role_config.py +213 -0
  80. aiecs/domain/agent/knowledge_aware_agent.py +646 -0
  81. aiecs/domain/agent/lifecycle.py +296 -0
  82. aiecs/domain/agent/llm_agent.py +300 -0
  83. aiecs/domain/agent/memory/__init__.py +12 -0
  84. aiecs/domain/agent/memory/conversation.py +197 -0
  85. aiecs/domain/agent/migration/__init__.py +14 -0
  86. aiecs/domain/agent/migration/conversion.py +160 -0
  87. aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
  88. aiecs/domain/agent/models.py +317 -0
  89. aiecs/domain/agent/observability.py +407 -0
  90. aiecs/domain/agent/persistence.py +289 -0
  91. aiecs/domain/agent/prompts/__init__.py +29 -0
  92. aiecs/domain/agent/prompts/builder.py +161 -0
  93. aiecs/domain/agent/prompts/formatters.py +189 -0
  94. aiecs/domain/agent/prompts/template.py +255 -0
  95. aiecs/domain/agent/registry.py +260 -0
  96. aiecs/domain/agent/tool_agent.py +257 -0
  97. aiecs/domain/agent/tools/__init__.py +12 -0
  98. aiecs/domain/agent/tools/schema_generator.py +221 -0
  99. aiecs/domain/community/__init__.py +155 -0
  100. aiecs/domain/community/agent_adapter.py +477 -0
  101. aiecs/domain/community/analytics.py +481 -0
  102. aiecs/domain/community/collaborative_workflow.py +642 -0
  103. aiecs/domain/community/communication_hub.py +645 -0
  104. aiecs/domain/community/community_builder.py +320 -0
  105. aiecs/domain/community/community_integration.py +800 -0
  106. aiecs/domain/community/community_manager.py +813 -0
  107. aiecs/domain/community/decision_engine.py +879 -0
  108. aiecs/domain/community/exceptions.py +225 -0
  109. aiecs/domain/community/models/__init__.py +33 -0
  110. aiecs/domain/community/models/community_models.py +268 -0
  111. aiecs/domain/community/resource_manager.py +457 -0
  112. aiecs/domain/community/shared_context_manager.py +603 -0
  113. aiecs/domain/context/__init__.py +58 -0
  114. aiecs/domain/context/context_engine.py +989 -0
  115. aiecs/domain/context/conversation_models.py +354 -0
  116. aiecs/domain/context/graph_memory.py +467 -0
  117. aiecs/domain/execution/__init__.py +12 -0
  118. aiecs/domain/execution/model.py +57 -0
  119. aiecs/domain/knowledge_graph/__init__.py +19 -0
  120. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  121. aiecs/domain/knowledge_graph/models/entity.py +130 -0
  122. aiecs/domain/knowledge_graph/models/evidence.py +194 -0
  123. aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
  124. aiecs/domain/knowledge_graph/models/path.py +179 -0
  125. aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
  126. aiecs/domain/knowledge_graph/models/query.py +272 -0
  127. aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
  128. aiecs/domain/knowledge_graph/models/relation.py +136 -0
  129. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  130. aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
  131. aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
  132. aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
  133. aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
  134. aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
  135. aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
  136. aiecs/domain/task/__init__.py +13 -0
  137. aiecs/domain/task/dsl_processor.py +613 -0
  138. aiecs/domain/task/model.py +62 -0
  139. aiecs/domain/task/task_context.py +268 -0
  140. aiecs/infrastructure/__init__.py +24 -0
  141. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  142. aiecs/infrastructure/graph_storage/base.py +601 -0
  143. aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
  144. aiecs/infrastructure/graph_storage/cache.py +429 -0
  145. aiecs/infrastructure/graph_storage/distributed.py +226 -0
  146. aiecs/infrastructure/graph_storage/error_handling.py +390 -0
  147. aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
  148. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  149. aiecs/infrastructure/graph_storage/in_memory.py +514 -0
  150. aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
  151. aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
  152. aiecs/infrastructure/graph_storage/metrics.py +357 -0
  153. aiecs/infrastructure/graph_storage/migration.py +413 -0
  154. aiecs/infrastructure/graph_storage/pagination.py +471 -0
  155. aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
  156. aiecs/infrastructure/graph_storage/postgres.py +871 -0
  157. aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
  158. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  159. aiecs/infrastructure/graph_storage/sqlite.py +623 -0
  160. aiecs/infrastructure/graph_storage/streaming.py +495 -0
  161. aiecs/infrastructure/messaging/__init__.py +13 -0
  162. aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
  163. aiecs/infrastructure/messaging/websocket_manager.py +298 -0
  164. aiecs/infrastructure/monitoring/__init__.py +34 -0
  165. aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
  166. aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
  167. aiecs/infrastructure/monitoring/structured_logger.py +48 -0
  168. aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
  169. aiecs/infrastructure/persistence/__init__.py +24 -0
  170. aiecs/infrastructure/persistence/context_engine_client.py +187 -0
  171. aiecs/infrastructure/persistence/database_manager.py +333 -0
  172. aiecs/infrastructure/persistence/file_storage.py +754 -0
  173. aiecs/infrastructure/persistence/redis_client.py +220 -0
  174. aiecs/llm/__init__.py +86 -0
  175. aiecs/llm/callbacks/__init__.py +11 -0
  176. aiecs/llm/callbacks/custom_callbacks.py +264 -0
  177. aiecs/llm/client_factory.py +420 -0
  178. aiecs/llm/clients/__init__.py +33 -0
  179. aiecs/llm/clients/base_client.py +193 -0
  180. aiecs/llm/clients/googleai_client.py +181 -0
  181. aiecs/llm/clients/openai_client.py +131 -0
  182. aiecs/llm/clients/vertex_client.py +437 -0
  183. aiecs/llm/clients/xai_client.py +184 -0
  184. aiecs/llm/config/__init__.py +51 -0
  185. aiecs/llm/config/config_loader.py +275 -0
  186. aiecs/llm/config/config_validator.py +236 -0
  187. aiecs/llm/config/model_config.py +151 -0
  188. aiecs/llm/utils/__init__.py +10 -0
  189. aiecs/llm/utils/validate_config.py +91 -0
  190. aiecs/main.py +363 -0
  191. aiecs/scripts/__init__.py +3 -0
  192. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  193. aiecs/scripts/aid/__init__.py +19 -0
  194. aiecs/scripts/aid/version_manager.py +215 -0
  195. aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
  196. aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
  197. aiecs/scripts/dependance_check/__init__.py +17 -0
  198. aiecs/scripts/dependance_check/dependency_checker.py +938 -0
  199. aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
  200. aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
  201. aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
  202. aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
  203. aiecs/scripts/dependance_patch/__init__.py +7 -0
  204. aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
  205. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  206. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
  207. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
  208. aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
  209. aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
  210. aiecs/scripts/tools_develop/README.md +449 -0
  211. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  212. aiecs/scripts/tools_develop/__init__.py +21 -0
  213. aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
  214. aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
  215. aiecs/scripts/tools_develop/verify_tools.py +356 -0
  216. aiecs/tasks/__init__.py +1 -0
  217. aiecs/tasks/worker.py +172 -0
  218. aiecs/tools/__init__.py +299 -0
  219. aiecs/tools/apisource/__init__.py +99 -0
  220. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  221. aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
  222. aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
  223. aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
  224. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  225. aiecs/tools/apisource/monitoring/metrics.py +303 -0
  226. aiecs/tools/apisource/providers/__init__.py +115 -0
  227. aiecs/tools/apisource/providers/base.py +664 -0
  228. aiecs/tools/apisource/providers/census.py +401 -0
  229. aiecs/tools/apisource/providers/fred.py +564 -0
  230. aiecs/tools/apisource/providers/newsapi.py +412 -0
  231. aiecs/tools/apisource/providers/worldbank.py +357 -0
  232. aiecs/tools/apisource/reliability/__init__.py +12 -0
  233. aiecs/tools/apisource/reliability/error_handler.py +375 -0
  234. aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
  235. aiecs/tools/apisource/tool.py +850 -0
  236. aiecs/tools/apisource/utils/__init__.py +9 -0
  237. aiecs/tools/apisource/utils/validators.py +338 -0
  238. aiecs/tools/base_tool.py +201 -0
  239. aiecs/tools/docs/__init__.py +121 -0
  240. aiecs/tools/docs/ai_document_orchestrator.py +599 -0
  241. aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
  242. aiecs/tools/docs/content_insertion_tool.py +1333 -0
  243. aiecs/tools/docs/document_creator_tool.py +1317 -0
  244. aiecs/tools/docs/document_layout_tool.py +1166 -0
  245. aiecs/tools/docs/document_parser_tool.py +994 -0
  246. aiecs/tools/docs/document_writer_tool.py +1818 -0
  247. aiecs/tools/knowledge_graph/__init__.py +17 -0
  248. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
  249. aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
  250. aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
  251. aiecs/tools/langchain_adapter.py +542 -0
  252. aiecs/tools/schema_generator.py +275 -0
  253. aiecs/tools/search_tool/__init__.py +100 -0
  254. aiecs/tools/search_tool/analyzers.py +589 -0
  255. aiecs/tools/search_tool/cache.py +260 -0
  256. aiecs/tools/search_tool/constants.py +128 -0
  257. aiecs/tools/search_tool/context.py +216 -0
  258. aiecs/tools/search_tool/core.py +749 -0
  259. aiecs/tools/search_tool/deduplicator.py +123 -0
  260. aiecs/tools/search_tool/error_handler.py +271 -0
  261. aiecs/tools/search_tool/metrics.py +371 -0
  262. aiecs/tools/search_tool/rate_limiter.py +178 -0
  263. aiecs/tools/search_tool/schemas.py +277 -0
  264. aiecs/tools/statistics/__init__.py +80 -0
  265. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
  266. aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
  267. aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
  268. aiecs/tools/statistics/data_loader_tool.py +564 -0
  269. aiecs/tools/statistics/data_profiler_tool.py +658 -0
  270. aiecs/tools/statistics/data_transformer_tool.py +573 -0
  271. aiecs/tools/statistics/data_visualizer_tool.py +495 -0
  272. aiecs/tools/statistics/model_trainer_tool.py +487 -0
  273. aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
  274. aiecs/tools/task_tools/__init__.py +86 -0
  275. aiecs/tools/task_tools/chart_tool.py +732 -0
  276. aiecs/tools/task_tools/classfire_tool.py +922 -0
  277. aiecs/tools/task_tools/image_tool.py +447 -0
  278. aiecs/tools/task_tools/office_tool.py +684 -0
  279. aiecs/tools/task_tools/pandas_tool.py +635 -0
  280. aiecs/tools/task_tools/report_tool.py +635 -0
  281. aiecs/tools/task_tools/research_tool.py +392 -0
  282. aiecs/tools/task_tools/scraper_tool.py +715 -0
  283. aiecs/tools/task_tools/stats_tool.py +688 -0
  284. aiecs/tools/temp_file_manager.py +130 -0
  285. aiecs/tools/tool_executor/__init__.py +37 -0
  286. aiecs/tools/tool_executor/tool_executor.py +881 -0
  287. aiecs/utils/LLM_output_structor.py +445 -0
  288. aiecs/utils/__init__.py +34 -0
  289. aiecs/utils/base_callback.py +47 -0
  290. aiecs/utils/cache_provider.py +695 -0
  291. aiecs/utils/execution_utils.py +184 -0
  292. aiecs/utils/logging.py +1 -0
  293. aiecs/utils/prompt_loader.py +14 -0
  294. aiecs/utils/token_usage_repository.py +323 -0
  295. aiecs/ws/__init__.py +0 -0
  296. aiecs/ws/socket_server.py +52 -0
  297. aiecs-1.5.1.dist-info/METADATA +608 -0
  298. aiecs-1.5.1.dist-info/RECORD +302 -0
  299. aiecs-1.5.1.dist-info/WHEEL +5 -0
  300. aiecs-1.5.1.dist-info/entry_points.txt +10 -0
  301. aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
  302. aiecs-1.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,329 @@
1
+ """
2
+ Enhanced Graph Traversal
3
+
4
+ Provides advanced traversal capabilities with PathPattern support,
5
+ cycle detection, and sophisticated path filtering.
6
+ """
7
+
8
+ from typing import List, Optional
9
+ from collections import deque
10
+ from aiecs.domain.knowledge_graph.models.relation import Relation
11
+ from aiecs.domain.knowledge_graph.models.path import Path
12
+ from aiecs.domain.knowledge_graph.models.path_pattern import (
13
+ PathPattern,
14
+ TraversalDirection,
15
+ )
16
+ from aiecs.infrastructure.graph_storage.base import GraphStore
17
+
18
+
19
+ class EnhancedTraversal:
20
+ """
21
+ Enhanced Graph Traversal Service
22
+
23
+ Provides advanced traversal capabilities beyond basic BFS:
24
+ - PathPattern-based traversal
25
+ - Cycle detection and handling
26
+ - Depth-limited traversal with constraints
27
+ - Path filtering by pattern
28
+
29
+ Example:
30
+ ```python
31
+ traversal = EnhancedTraversal(graph_store)
32
+
33
+ # Define pattern
34
+ pattern = PathPattern(
35
+ relation_types=["WORKS_FOR", "LOCATED_IN"],
36
+ max_depth=2,
37
+ allow_cycles=False
38
+ )
39
+
40
+ # Traverse with pattern
41
+ paths = await traversal.traverse_with_pattern(
42
+ start_entity_id="person_1",
43
+ pattern=pattern,
44
+ max_results=10
45
+ )
46
+ ```
47
+ """
48
+
49
+ def __init__(self, graph_store: GraphStore):
50
+ """
51
+ Initialize enhanced traversal service
52
+
53
+ Args:
54
+ graph_store: Graph storage backend to use
55
+ """
56
+ self.graph_store = graph_store
57
+
58
+ async def traverse_with_pattern(
59
+ self,
60
+ start_entity_id: str,
61
+ pattern: PathPattern,
62
+ max_results: int = 100,
63
+ ) -> List[Path]:
64
+ """
65
+ Traverse graph following a path pattern
66
+
67
+ Args:
68
+ start_entity_id: Starting entity ID
69
+ pattern: Path pattern to follow
70
+ max_results: Maximum number of paths to return
71
+
72
+ Returns:
73
+ List of paths matching the pattern
74
+ """
75
+ start_entity = await self.graph_store.get_entity(start_entity_id)
76
+ if start_entity is None:
77
+ return []
78
+
79
+ # Check if start entity is allowed
80
+ if not pattern.is_entity_allowed(start_entity.id, start_entity.entity_type):
81
+ return []
82
+
83
+ paths: List[Path] = []
84
+ # visited_in_path: Set[str] = set() if not pattern.allow_cycles else
85
+ # None # Reserved for future use
86
+
87
+ # BFS with pattern matching
88
+ queue: deque = deque()
89
+ queue.append(
90
+ {
91
+ "entity": start_entity,
92
+ "path_entities": [start_entity],
93
+ "path_edges": [],
94
+ "depth": 0,
95
+ "visited": ({start_entity.id} if not pattern.allow_cycles else set()),
96
+ }
97
+ )
98
+
99
+ while queue and len(paths) < max_results:
100
+ current = queue.popleft()
101
+ current_entity = current["entity"]
102
+ current_depth = current["depth"]
103
+ path_entities = current["path_entities"]
104
+ path_edges = current["path_edges"]
105
+ visited_nodes = current["visited"]
106
+
107
+ # Add path if it meets length requirements
108
+ if pattern.is_valid_path_length(len(path_edges)):
109
+ path = Path(nodes=path_entities, edges=path_edges)
110
+ paths.append(path)
111
+
112
+ # Continue traversal if not at max depth
113
+ if not pattern.should_continue_traversal(current_depth):
114
+ continue
115
+
116
+ # Get neighbors based on pattern direction
117
+ # pattern.direction is already a string due to use_enum_values=True
118
+ direction_str = (
119
+ pattern.direction if isinstance(pattern.direction, str) else pattern.direction.value
120
+ )
121
+ neighbors = await self.graph_store.get_neighbors(
122
+ current_entity.id,
123
+ relation_type=None, # We'll filter by pattern
124
+ direction=direction_str,
125
+ )
126
+
127
+ for neighbor in neighbors:
128
+ # Check if entity is allowed
129
+ if not pattern.is_entity_allowed(neighbor.id, neighbor.entity_type):
130
+ continue
131
+
132
+ # Check for cycles
133
+ if not pattern.allow_cycles and neighbor.id in visited_nodes:
134
+ continue
135
+
136
+ # Get the relation between current and neighbor
137
+ # We need to find the actual relation
138
+ relation = await self._find_relation(
139
+ current_entity.id, neighbor.id, pattern.direction
140
+ )
141
+
142
+ if relation is None:
143
+ continue
144
+
145
+ # Check if relation is allowed at this depth
146
+ if not pattern.is_relation_allowed(relation.relation_type, current_depth):
147
+ continue
148
+
149
+ # For incoming direction, we need to reverse the relation for path construction
150
+ # because paths expect edges[i].source_id == nodes[i].id
151
+ direction_str = (
152
+ pattern.direction
153
+ if isinstance(pattern.direction, str)
154
+ else pattern.direction.value
155
+ )
156
+ if direction_str == "incoming":
157
+ # Reverse the relation: if we have e1->e2 and we're going from e2 to e1,
158
+ # the path needs e2->e1 (source=current, target=neighbor)
159
+ path_relation = Relation(
160
+ id=f"{relation.id}_reversed",
161
+ relation_type=relation.relation_type,
162
+ source_id=current_entity.id,
163
+ target_id=neighbor.id,
164
+ weight=relation.weight,
165
+ )
166
+ else:
167
+ path_relation = relation
168
+
169
+ # Create new path state
170
+ new_path_entities = path_entities + [neighbor]
171
+ new_path_edges = path_edges + [path_relation]
172
+ new_visited = (
173
+ visited_nodes | {neighbor.id} if not pattern.allow_cycles else visited_nodes
174
+ )
175
+
176
+ queue.append(
177
+ {
178
+ "entity": neighbor,
179
+ "path_entities": new_path_entities,
180
+ "path_edges": new_path_edges,
181
+ "depth": current_depth + 1,
182
+ "visited": new_visited,
183
+ }
184
+ )
185
+
186
+ return paths
187
+
188
+ async def _find_relation(
189
+ self, source_id: str, target_id: str, direction: TraversalDirection
190
+ ) -> Optional[Relation]:
191
+ """
192
+ Find the relation between two entities
193
+
194
+ Args:
195
+ source_id: Source entity ID
196
+ target_id: Target entity ID
197
+ direction: Traversal direction (can be enum or string)
198
+
199
+ Returns:
200
+ Relation if found, None otherwise
201
+ """
202
+ # Try to find the actual relation in the graph store
203
+ # This works with both InMemoryGraphStore and SQLiteGraphStore
204
+
205
+ # Handle both enum and string directions
206
+ direction_str = direction if isinstance(direction, str) else direction.value
207
+ direction_enum = (
208
+ TraversalDirection(direction_str) if isinstance(direction, str) else direction
209
+ )
210
+
211
+ if (
212
+ direction_enum == TraversalDirection.OUTGOING
213
+ or direction_enum == TraversalDirection.BOTH
214
+ ):
215
+ # Look for outgoing relations from source
216
+ neighbors = await self.graph_store.get_neighbors(
217
+ source_id, relation_type=None, direction="outgoing"
218
+ )
219
+ for neighbor in neighbors:
220
+ if neighbor.id == target_id:
221
+ # Found the neighbor, now get the relation
222
+ # This is a workaround - ideally get_neighbors would return relations too
223
+ # For now, check if the store exposes relations
224
+ from aiecs.infrastructure.graph_storage.in_memory import (
225
+ InMemoryGraphStore,
226
+ )
227
+
228
+ if isinstance(self.graph_store, InMemoryGraphStore):
229
+ for rel in self.graph_store.relations.values():
230
+ if rel.source_id == source_id and rel.target_id == target_id:
231
+ return rel
232
+ else:
233
+ # For SQLite or other stores, try to get the relation
234
+ # This is a placeholder - real implementation would
235
+ # query the DB
236
+ return Relation(
237
+ id=f"rel_{source_id}_{target_id}",
238
+ relation_type="CONNECTED_TO",
239
+ source_id=source_id,
240
+ target_id=target_id,
241
+ )
242
+
243
+ if (
244
+ direction_enum == TraversalDirection.INCOMING
245
+ or direction_enum == TraversalDirection.BOTH
246
+ ):
247
+ # Look for incoming relations to source (i.e., outgoing from
248
+ # target)
249
+ neighbors = await self.graph_store.get_neighbors(
250
+ target_id, relation_type=None, direction="outgoing"
251
+ )
252
+ for neighbor in neighbors:
253
+ if neighbor.id == source_id:
254
+ from aiecs.infrastructure.graph_storage.in_memory import (
255
+ InMemoryGraphStore,
256
+ )
257
+
258
+ if isinstance(self.graph_store, InMemoryGraphStore):
259
+ for rel in self.graph_store.relations.values():
260
+ if rel.source_id == target_id and rel.target_id == source_id:
261
+ return rel
262
+ else:
263
+ return Relation(
264
+ id=f"rel_{target_id}_{source_id}",
265
+ relation_type="CONNECTED_TO",
266
+ source_id=target_id,
267
+ target_id=source_id,
268
+ )
269
+
270
+ return None
271
+
272
+ def detect_cycles(self, path: Path) -> bool:
273
+ """
274
+ Detect if a path contains cycles (repeated nodes)
275
+
276
+ Args:
277
+ path: Path to check
278
+
279
+ Returns:
280
+ True if path contains cycles
281
+ """
282
+ entity_ids = path.get_entity_ids()
283
+ return len(entity_ids) != len(set(entity_ids))
284
+
285
+ def filter_paths_without_cycles(self, paths: List[Path]) -> List[Path]:
286
+ """
287
+ Filter out paths that contain cycles
288
+
289
+ Args:
290
+ paths: List of paths to filter
291
+
292
+ Returns:
293
+ List of paths without cycles
294
+ """
295
+ return [path for path in paths if not self.detect_cycles(path)]
296
+
297
+ async def find_all_paths_between(
298
+ self,
299
+ source_id: str,
300
+ target_id: str,
301
+ pattern: Optional[PathPattern] = None,
302
+ max_paths: int = 10,
303
+ ) -> List[Path]:
304
+ """
305
+ Find all paths between two entities matching a pattern
306
+
307
+ Args:
308
+ source_id: Source entity ID
309
+ target_id: Target entity ID
310
+ pattern: Optional path pattern to follow
311
+ max_paths: Maximum number of paths to return
312
+
313
+ Returns:
314
+ List of paths from source to target
315
+ """
316
+ if pattern is None:
317
+ pattern = PathPattern(max_depth=5, allow_cycles=False)
318
+
319
+ # Traverse from source
320
+ all_paths = await self.traverse_with_pattern(
321
+ start_entity_id=source_id,
322
+ pattern=pattern,
323
+ max_results=max_paths * 10, # Get more paths for filtering
324
+ )
325
+
326
+ # Filter paths that end at target
327
+ target_paths = [path for path in all_paths if path.end_entity.id == target_id]
328
+
329
+ return target_paths[:max_paths]
@@ -0,0 +1,269 @@
1
+ """
2
+ Path Scoring and Ranking
3
+
4
+ Provides utilities for scoring and ranking graph paths based on various criteria.
5
+ """
6
+
7
+ from typing import List, Callable, Optional
8
+ from aiecs.domain.knowledge_graph.models.path import Path
9
+
10
+
11
+ class PathScorer:
12
+ """
13
+ Path Scoring Utility
14
+
15
+ Scores and ranks paths based on configurable criteria:
16
+ - Path length (shorter/longer)
17
+ - Relation types (preferred types)
18
+ - Entity types (preferred types)
19
+ - Relation weights (accumulated)
20
+ - Custom scoring functions
21
+
22
+ Example:
23
+ ```python
24
+ scorer = PathScorer()
25
+
26
+ # Score by path length (shorter is better)
27
+ scored_paths = scorer.score_by_length(paths, prefer_shorter=True)
28
+
29
+ # Score by relation weights
30
+ scored_paths = scorer.score_by_weights(paths)
31
+
32
+ # Custom scoring
33
+ scored_paths = scorer.score_custom(paths, lambda p: compute_score(p))
34
+
35
+ # Rank and return top paths
36
+ top_paths = scorer.rank_paths(scored_paths, top_k=10)
37
+ ```
38
+ """
39
+
40
+ def score_by_length(
41
+ self,
42
+ paths: List[Path],
43
+ prefer_shorter: bool = True,
44
+ normalize: bool = True,
45
+ ) -> List[Path]:
46
+ """
47
+ Score paths by their length
48
+
49
+ Args:
50
+ paths: List of paths to score
51
+ prefer_shorter: If True, shorter paths get higher scores
52
+ normalize: If True, normalize scores to 0-1 range
53
+
54
+ Returns:
55
+ List of paths with scores assigned
56
+ """
57
+ if not paths:
58
+ return []
59
+
60
+ # Get length range
61
+ lengths = [p.length for p in paths]
62
+ min_len = min(lengths)
63
+ max_len = max(lengths)
64
+
65
+ scored_paths = []
66
+ for path in paths:
67
+ if max_len == min_len:
68
+ # All paths same length
69
+ score = 1.0
70
+ elif prefer_shorter:
71
+ # Shorter paths get higher scores
72
+ if normalize:
73
+ score = 1.0 - ((path.length - min_len) / (max_len - min_len))
74
+ else:
75
+ score = 1.0 / (path.length + 1)
76
+ else:
77
+ # Longer paths get higher scores
78
+ if normalize:
79
+ score = (path.length - min_len) / (max_len - min_len)
80
+ else:
81
+ score = path.length / (max_len + 1)
82
+
83
+ # Create new path with score
84
+ scored_path = Path(nodes=path.nodes, edges=path.edges, score=score)
85
+ scored_paths.append(scored_path)
86
+
87
+ return scored_paths
88
+
89
+ def score_by_weights(self, paths: List[Path], aggregation: str = "mean") -> List[Path]:
90
+ """
91
+ Score paths by relation weights
92
+
93
+ Args:
94
+ paths: List of paths to score
95
+ aggregation: How to aggregate weights ("mean", "sum", "min", "max")
96
+
97
+ Returns:
98
+ List of paths with scores based on relation weights
99
+ """
100
+ scored_paths = []
101
+
102
+ for path in paths:
103
+ if not path.edges:
104
+ score = 1.0
105
+ else:
106
+ weights = [edge.weight for edge in path.edges]
107
+
108
+ if aggregation == "mean":
109
+ score = sum(weights) / len(weights)
110
+ elif aggregation == "sum":
111
+ score = sum(weights) / len(weights) # Normalized by length
112
+ elif aggregation == "min":
113
+ score = min(weights)
114
+ elif aggregation == "max":
115
+ score = max(weights)
116
+ else:
117
+ score = sum(weights) / len(weights) # Default to mean
118
+
119
+ scored_path = Path(nodes=path.nodes, edges=path.edges, score=score)
120
+ scored_paths.append(scored_path)
121
+
122
+ return scored_paths
123
+
124
+ def score_by_relation_types(
125
+ self,
126
+ paths: List[Path],
127
+ preferred_types: List[str],
128
+ penalty: float = 0.5,
129
+ ) -> List[Path]:
130
+ """
131
+ Score paths by preferred relation types
132
+
133
+ Args:
134
+ paths: List of paths to score
135
+ preferred_types: List of preferred relation types
136
+ penalty: Score multiplier for non-preferred relations
137
+
138
+ Returns:
139
+ List of paths scored by relation type preferences
140
+ """
141
+ scored_paths = []
142
+
143
+ for path in paths:
144
+ if not path.edges:
145
+ score = 1.0
146
+ else:
147
+ # Calculate score based on preferred types
148
+ type_scores = []
149
+ for edge in path.edges:
150
+ if edge.relation_type in preferred_types:
151
+ type_scores.append(1.0)
152
+ else:
153
+ type_scores.append(penalty)
154
+
155
+ score = sum(type_scores) / len(type_scores)
156
+
157
+ scored_path = Path(nodes=path.nodes, edges=path.edges, score=score)
158
+ scored_paths.append(scored_path)
159
+
160
+ return scored_paths
161
+
162
+ def score_custom(self, paths: List[Path], scoring_fn: Callable[[Path], float]) -> List[Path]:
163
+ """
164
+ Score paths using a custom scoring function
165
+
166
+ Args:
167
+ paths: List of paths to score
168
+ scoring_fn: Function that takes a Path and returns a score (0.0-1.0)
169
+
170
+ Returns:
171
+ List of paths with custom scores
172
+ """
173
+ scored_paths = []
174
+
175
+ for path in paths:
176
+ score = scoring_fn(path)
177
+ # Clamp score to valid range
178
+ score = max(0.0, min(1.0, score))
179
+
180
+ scored_path = Path(nodes=path.nodes, edges=path.edges, score=score)
181
+ scored_paths.append(scored_path)
182
+
183
+ return scored_paths
184
+
185
+ def combine_scores(
186
+ self,
187
+ paths_lists: List[List[Path]],
188
+ weights: Optional[List[float]] = None,
189
+ ) -> List[Path]:
190
+ """
191
+ Combine scores from multiple scoring methods
192
+
193
+ Args:
194
+ paths_lists: List of path lists (each with different scores)
195
+ weights: Optional weights for each scoring method
196
+
197
+ Returns:
198
+ List of paths with combined scores
199
+ """
200
+ if not paths_lists:
201
+ return []
202
+
203
+ if weights is None:
204
+ weights = [1.0] * len(paths_lists)
205
+
206
+ # Normalize weights
207
+ total_weight = sum(weights)
208
+ weights = [w / total_weight for w in weights]
209
+
210
+ # Build path ID to combined score mapping
211
+ path_scores = {}
212
+
213
+ for paths, weight in zip(paths_lists, weights):
214
+ for path in paths:
215
+ # Use path node IDs as key
216
+ path_key = tuple(p.id for p in path.nodes)
217
+
218
+ if path_key not in path_scores:
219
+ path_scores[path_key] = {"path": path, "score": 0.0}
220
+
221
+ # Add weighted score
222
+ if path.score is not None:
223
+ path_scores[path_key]["score"] += path.score * weight
224
+
225
+ # Create scored paths
226
+ combined_paths = []
227
+ for data in path_scores.values():
228
+ scored_path = Path(
229
+ nodes=data["path"].nodes,
230
+ edges=data["path"].edges,
231
+ score=data["score"],
232
+ )
233
+ combined_paths.append(scored_path)
234
+
235
+ return combined_paths
236
+
237
+ def rank_paths(
238
+ self,
239
+ paths: List[Path],
240
+ top_k: Optional[int] = None,
241
+ min_score: Optional[float] = None,
242
+ ) -> List[Path]:
243
+ """
244
+ Rank paths by score and return top results
245
+
246
+ Args:
247
+ paths: List of paths to rank
248
+ top_k: Number of top paths to return (None = all)
249
+ min_score: Minimum score threshold (None = no threshold)
250
+
251
+ Returns:
252
+ Sorted list of paths (highest score first)
253
+ """
254
+ # Filter by minimum score if specified
255
+ if min_score is not None:
256
+ paths = [p for p in paths if p.score is not None and p.score >= min_score]
257
+
258
+ # Sort by score (descending)
259
+ sorted_paths = sorted(
260
+ paths,
261
+ key=lambda p: p.score if p.score is not None else 0.0,
262
+ reverse=True,
263
+ )
264
+
265
+ # Return top k if specified
266
+ if top_k is not None:
267
+ return sorted_paths[:top_k]
268
+
269
+ return sorted_paths
@@ -0,0 +1,13 @@
1
+ """
2
+ Knowledge Graph Validators
3
+
4
+ Validators for entities and relations against schema.
5
+ """
6
+
7
+ from aiecs.application.knowledge_graph.validators.relation_validator import (
8
+ RelationValidator,
9
+ )
10
+
11
+ __all__ = [
12
+ "RelationValidator",
13
+ ]