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,504 @@
1
+ """
2
+ Inference Engine
3
+
4
+ Rule-based logical inference over knowledge graphs.
5
+ """
6
+
7
+ import uuid
8
+ import time
9
+ from typing import List, Optional, Dict, Any, Set, Tuple
10
+ from collections import defaultdict
11
+ from aiecs.infrastructure.graph_storage.base import GraphStore
12
+ from aiecs.domain.knowledge_graph.models.relation import Relation
13
+ from aiecs.domain.knowledge_graph.models.inference_rule import (
14
+ InferenceRule,
15
+ InferenceStep,
16
+ InferenceResult,
17
+ RuleType,
18
+ )
19
+ from aiecs.domain.knowledge_graph.schema.relation_type import RelationType
20
+
21
+
22
+ class InferenceCache:
23
+ """
24
+ Cache for inference results
25
+
26
+ Stores previously computed inference results to avoid recomputation.
27
+ """
28
+
29
+ def __init__(self, max_size: int = 1000, ttl_seconds: Optional[float] = None):
30
+ """
31
+ Initialize inference cache
32
+
33
+ Args:
34
+ max_size: Maximum number of cached entries
35
+ ttl_seconds: Time-to-live in seconds (None = no expiration)
36
+ """
37
+ self.max_size = max_size
38
+ self.ttl_seconds = ttl_seconds
39
+ self._cache: Dict[str, Tuple[InferenceResult, float]] = {}
40
+ self._access_times: Dict[str, float] = {}
41
+
42
+ def _make_key(
43
+ self,
44
+ relation_type: str,
45
+ source_id: Optional[str] = None,
46
+ target_id: Optional[str] = None,
47
+ ) -> str:
48
+ """Create cache key"""
49
+ if source_id and target_id:
50
+ return f"{relation_type}:{source_id}:{target_id}"
51
+ elif source_id:
52
+ return f"{relation_type}:{source_id}:*"
53
+ elif target_id:
54
+ return f"{relation_type}:*:{target_id}"
55
+ else:
56
+ return f"{relation_type}:*:*"
57
+
58
+ def get(
59
+ self,
60
+ relation_type: str,
61
+ source_id: Optional[str] = None,
62
+ target_id: Optional[str] = None,
63
+ ) -> Optional[InferenceResult]:
64
+ """
65
+ Get cached inference result
66
+
67
+ Args:
68
+ relation_type: Relation type
69
+ source_id: Source entity ID
70
+ target_id: Target entity ID
71
+
72
+ Returns:
73
+ Cached result or None
74
+ """
75
+ key = self._make_key(relation_type, source_id, target_id)
76
+
77
+ if key not in self._cache:
78
+ return None
79
+
80
+ result, cached_time = self._cache[key]
81
+
82
+ # Check TTL
83
+ if self.ttl_seconds and (time.time() - cached_time) > self.ttl_seconds:
84
+ del self._cache[key]
85
+ if key in self._access_times:
86
+ del self._access_times[key]
87
+ return None
88
+
89
+ # Update access time
90
+ self._access_times[key] = time.time()
91
+ return result
92
+
93
+ def put(
94
+ self,
95
+ relation_type: str,
96
+ result: InferenceResult,
97
+ source_id: Optional[str] = None,
98
+ target_id: Optional[str] = None,
99
+ ) -> None:
100
+ """
101
+ Cache inference result
102
+
103
+ Args:
104
+ relation_type: Relation type
105
+ result: Inference result to cache
106
+ source_id: Source entity ID
107
+ target_id: Target entity ID
108
+ """
109
+ key = self._make_key(relation_type, source_id, target_id)
110
+
111
+ # Evict if cache is full (LRU)
112
+ if len(self._cache) >= self.max_size and key not in self._cache:
113
+ # Remove least recently used
114
+ lru_key = min(self._access_times.items(), key=lambda x: x[1])[0]
115
+ del self._cache[lru_key]
116
+ del self._access_times[lru_key]
117
+
118
+ self._cache[key] = (result, time.time())
119
+ self._access_times[key] = time.time()
120
+
121
+ def clear(self) -> None:
122
+ """Clear all cached results"""
123
+ self._cache.clear()
124
+ self._access_times.clear()
125
+
126
+ def get_stats(self) -> Dict[str, Any]:
127
+ """Get cache statistics"""
128
+ return {
129
+ "size": len(self._cache),
130
+ "max_size": self.max_size,
131
+ "ttl_seconds": self.ttl_seconds,
132
+ }
133
+
134
+
135
+ class InferenceEngine:
136
+ """
137
+ Rule-Based Inference Engine
138
+
139
+ Applies logical inference rules to infer new relations from existing ones.
140
+
141
+ Features:
142
+ - Transitive inference (A->B, B->C => A->C)
143
+ - Symmetric inference (A->B => B->A)
144
+ - Custom inference rules
145
+ - Result caching
146
+ - Explainability (trace inference steps)
147
+
148
+ Example:
149
+ ```python
150
+ engine = InferenceEngine(graph_store)
151
+
152
+ # Add rules
153
+ engine.add_rule(InferenceRule(
154
+ rule_id="transitive_works_for",
155
+ rule_type=RuleType.TRANSITIVE,
156
+ relation_type="WORKS_FOR"
157
+ ))
158
+
159
+ # Infer relations
160
+ result = await engine.infer_relations(
161
+ relation_type="WORKS_FOR",
162
+ max_steps=3
163
+ )
164
+
165
+ print(f"Inferred {len(result.inferred_relations)} relations")
166
+ print(result.get_explanation_string())
167
+ ```
168
+ """
169
+
170
+ def __init__(self, graph_store: GraphStore, cache: Optional[InferenceCache] = None):
171
+ """
172
+ Initialize inference engine
173
+
174
+ Args:
175
+ graph_store: Graph storage backend
176
+ cache: Optional inference cache (creates one if not provided)
177
+ """
178
+ self.graph_store = graph_store
179
+ self.cache = cache or InferenceCache()
180
+ self.rules: Dict[str, InferenceRule] = {}
181
+ self.relation_type_schemas: Dict[str, RelationType] = {}
182
+
183
+ def add_rule(self, rule: InferenceRule) -> None:
184
+ """
185
+ Add an inference rule
186
+
187
+ Args:
188
+ rule: Inference rule to add
189
+ """
190
+ self.rules[rule.rule_id] = rule
191
+
192
+ def remove_rule(self, rule_id: str) -> None:
193
+ """
194
+ Remove an inference rule
195
+
196
+ Args:
197
+ rule_id: ID of rule to remove
198
+ """
199
+ if rule_id in self.rules:
200
+ del self.rules[rule_id]
201
+
202
+ def get_rules(self, relation_type: Optional[str] = None) -> List[InferenceRule]:
203
+ """
204
+ Get inference rules
205
+
206
+ Args:
207
+ relation_type: Filter by relation type (None = all)
208
+
209
+ Returns:
210
+ List of inference rules
211
+ """
212
+ rules = list(self.rules.values())
213
+ if relation_type:
214
+ rules = [r for r in rules if r.relation_type == relation_type]
215
+ return rules
216
+
217
+ async def infer_relations(
218
+ self,
219
+ relation_type: str,
220
+ max_steps: int = 10,
221
+ source_id: Optional[str] = None,
222
+ target_id: Optional[str] = None,
223
+ use_cache: bool = True,
224
+ ) -> InferenceResult:
225
+ """
226
+ Infer relations using enabled rules
227
+
228
+ Args:
229
+ relation_type: Relation type to infer
230
+ max_steps: Maximum number of inference steps
231
+ source_id: Optional source entity ID filter
232
+ target_id: Optional target entity ID filter
233
+ use_cache: Whether to use cache
234
+
235
+ Returns:
236
+ Inference result with inferred relations and steps
237
+ """
238
+ # Check cache
239
+ if use_cache:
240
+ cached = self.cache.get(relation_type, source_id, target_id)
241
+ if cached:
242
+ return cached
243
+
244
+ time.time()
245
+ inferred_relations: List[Relation] = []
246
+ inference_steps: List[InferenceStep] = []
247
+ # Track inferred relation IDs to avoid duplicates
248
+ visited: Set[str] = set()
249
+
250
+ # Get applicable rules
251
+ applicable_rules = [rule for rule in self.get_rules(relation_type) if rule.enabled]
252
+
253
+ if not applicable_rules:
254
+ result = InferenceResult(
255
+ inferred_relations=[],
256
+ inference_steps=[],
257
+ total_steps=0,
258
+ confidence=0.0,
259
+ explanation=f"No inference rules enabled for relation type: {relation_type}",
260
+ )
261
+ return result
262
+
263
+ # Get existing relations by traversing the graph
264
+ # We'll collect relations as we discover them through inference
265
+ # Start with relations we can find through get_neighbors
266
+ existing_relations: List[Relation] = []
267
+
268
+ # For inference, we'll discover relations as we traverse
269
+ # This is a limitation of the current GraphStore interface
270
+ # In practice, we'd query for all relations of a type directly
271
+
272
+ # Apply rules iteratively
273
+ current_relations = existing_relations.copy()
274
+ step_count = 0
275
+
276
+ while step_count < max_steps:
277
+ new_relations = []
278
+
279
+ for rule in applicable_rules:
280
+ if rule.rule_type == RuleType.TRANSITIVE:
281
+ inferred = await self._apply_transitive_rule(rule, current_relations, visited)
282
+ new_relations.extend(inferred)
283
+ elif rule.rule_type == RuleType.SYMMETRIC:
284
+ inferred = await self._apply_symmetric_rule(rule, current_relations, visited)
285
+ new_relations.extend(inferred)
286
+
287
+ if not new_relations:
288
+ break # No new relations inferred
289
+
290
+ # Add new relations
291
+ for rel, step in new_relations:
292
+ if rel.id not in visited:
293
+ inferred_relations.append(rel)
294
+ inference_steps.append(step)
295
+ visited.add(rel.id)
296
+ current_relations.append(rel)
297
+
298
+ step_count += 1
299
+
300
+ # Calculate overall confidence
301
+ if inference_steps:
302
+ confidence = sum(step.confidence for step in inference_steps) / len(inference_steps)
303
+ else:
304
+ confidence = 0.0
305
+
306
+ # Create result
307
+ result = InferenceResult(
308
+ inferred_relations=inferred_relations,
309
+ inference_steps=inference_steps,
310
+ total_steps=step_count,
311
+ confidence=confidence,
312
+ explanation=f"Inferred {len(inferred_relations)} relations using {len(applicable_rules)} rules in {step_count} steps",
313
+ )
314
+
315
+ # Cache result
316
+ if use_cache:
317
+ self.cache.put(relation_type, result, source_id, target_id)
318
+
319
+ return result
320
+
321
+ async def _get_relations(self, relation_type: str) -> List[Relation]:
322
+ """
323
+ Get all relations of a given type
324
+
325
+ Note: This is a simplified implementation that uses traversal.
326
+ In production, GraphStore should have a get_relations_by_type method.
327
+ """
328
+ relations: List[Relation] = []
329
+ # visited_entities: Set[str] = set() # Reserved for future use
330
+
331
+ # Get all entities (we'll need to traverse to find them)
332
+ # For now, we'll collect relations as we traverse
333
+ # This is inefficient but works for the current interface
334
+
335
+ # Try to get relations from paths
336
+ # We'll use a simple approach: traverse from a few entities
337
+ # In practice, this should be optimized in GraphStore
338
+
339
+ return relations
340
+
341
+ async def _apply_transitive_rule(
342
+ self, rule: InferenceRule, relations: List[Relation], visited: Set[str]
343
+ ) -> List[Tuple[Relation, InferenceStep]]:
344
+ """
345
+ Apply transitive rule: A->B, B->C => A->C
346
+
347
+ Args:
348
+ rule: Transitive rule to apply
349
+ relations: Existing relations
350
+ visited: Set of already inferred relation IDs
351
+
352
+ Returns:
353
+ List of (inferred_relation, inference_step) tuples
354
+ """
355
+ inferred = []
356
+
357
+ # Build index: source -> target
358
+ source_to_targets: Dict[str, List[Relation]] = defaultdict(list)
359
+ for rel in relations:
360
+ if rel.relation_type == rule.relation_type:
361
+ source_to_targets[rel.source_id].append(rel)
362
+
363
+ # Find transitive chains
364
+ for rel1 in relations:
365
+ if rel1.relation_type != rule.relation_type:
366
+ continue
367
+
368
+ # rel1: A -> B
369
+ # Find relations where B is source: B -> C
370
+ for rel2 in source_to_targets.get(rel1.target_id, []):
371
+ # Check if A -> C already exists
372
+ inferred_id = f"inf_{rel1.source_id}_{rel2.target_id}_{rule.relation_type}"
373
+
374
+ if inferred_id in visited:
375
+ continue
376
+
377
+ # Check if relation already exists
378
+ existing = await self.graph_store.get_relation(inferred_id)
379
+ if existing:
380
+ continue
381
+
382
+ # Create inferred relation
383
+ # Confidence decays with each step
384
+ confidence = min(rel1.weight, rel2.weight) * (1.0 - rule.confidence_decay)
385
+
386
+ inferred_rel = Relation(
387
+ id=inferred_id,
388
+ relation_type=rule.relation_type,
389
+ source_id=rel1.source_id,
390
+ target_id=rel2.target_id,
391
+ weight=confidence,
392
+ properties={
393
+ "inferred": True,
394
+ "source_relations": [rel1.id, rel2.id],
395
+ "rule_id": rule.rule_id,
396
+ },
397
+ )
398
+
399
+ # Create inference step
400
+ step = InferenceStep(
401
+ step_id=f"step_{uuid.uuid4().hex[:8]}",
402
+ inferred_relation=inferred_rel,
403
+ source_relations=[rel1, rel2],
404
+ rule=rule,
405
+ confidence=confidence,
406
+ explanation=f"Transitive: {rel1.source_id} -> {rel1.target_id} -> {rel2.target_id} => {rel1.source_id} -> {rel2.target_id}",
407
+ )
408
+
409
+ inferred.append((inferred_rel, step))
410
+
411
+ return inferred
412
+
413
+ async def _apply_symmetric_rule(
414
+ self, rule: InferenceRule, relations: List[Relation], visited: Set[str]
415
+ ) -> List[Tuple[Relation, InferenceStep]]:
416
+ """
417
+ Apply symmetric rule: A->B => B->A
418
+
419
+ Args:
420
+ rule: Symmetric rule to apply
421
+ relations: Existing relations
422
+ visited: Set of already inferred relation IDs
423
+
424
+ Returns:
425
+ List of (inferred_relation, inference_step) tuples
426
+ """
427
+ inferred = []
428
+
429
+ # Build set of existing relations (source, target) pairs
430
+ existing_pairs = set()
431
+ for rel in relations:
432
+ if rel.relation_type == rule.relation_type:
433
+ existing_pairs.add((rel.source_id, rel.target_id))
434
+
435
+ # Find relations that need symmetric inference
436
+ for rel in relations:
437
+ if rel.relation_type != rule.relation_type:
438
+ continue
439
+
440
+ # Check if reverse already exists
441
+ reverse_pair = (rel.target_id, rel.source_id)
442
+ if reverse_pair in existing_pairs:
443
+ continue
444
+
445
+ # Check if already inferred
446
+ inferred_id = f"inf_{rel.target_id}_{rel.source_id}_{rule.relation_type}"
447
+ if inferred_id in visited:
448
+ continue
449
+
450
+ # Check if relation already exists
451
+ existing = await self.graph_store.get_relation(inferred_id)
452
+ if existing:
453
+ continue
454
+
455
+ # Create inferred relation
456
+ # Confidence slightly lower than original
457
+ confidence = rel.weight * (1.0 - rule.confidence_decay)
458
+
459
+ inferred_rel = Relation(
460
+ id=inferred_id,
461
+ relation_type=rule.relation_type,
462
+ source_id=rel.target_id,
463
+ target_id=rel.source_id,
464
+ weight=confidence,
465
+ properties={
466
+ "inferred": True,
467
+ "source_relations": [rel.id],
468
+ "rule_id": rule.rule_id,
469
+ },
470
+ )
471
+
472
+ # Create inference step
473
+ step = InferenceStep(
474
+ step_id=f"step_{uuid.uuid4().hex[:8]}",
475
+ inferred_relation=inferred_rel,
476
+ source_relations=[rel],
477
+ rule=rule,
478
+ confidence=confidence,
479
+ explanation=f"Symmetric: {rel.source_id} -> {rel.target_id} => {rel.target_id} -> {rel.source_id}",
480
+ )
481
+
482
+ inferred.append((inferred_rel, step))
483
+
484
+ return inferred
485
+
486
+ def get_inference_trace(self, result: InferenceResult) -> List[str]:
487
+ """
488
+ Get human-readable trace of inference steps
489
+
490
+ Args:
491
+ result: Inference result
492
+
493
+ Returns:
494
+ List of trace strings
495
+ """
496
+ trace = []
497
+ trace.append(f"Inference trace for {result.total_steps} steps:")
498
+
499
+ for i, step in enumerate(result.inference_steps, 1):
500
+ trace.append(f" Step {i}: {step.explanation}")
501
+ trace.append(f" Confidence: {step.confidence:.2f}")
502
+ trace.append(f" Rule: {step.rule.rule_id} ({step.rule.rule_type})")
503
+
504
+ return trace
@@ -0,0 +1,167 @@
1
+ """
2
+ Logic Form Parser
3
+
4
+ Wrapper around LogicQueryParser for converting natural language queries
5
+ to structured logical forms.
6
+ """
7
+
8
+ from typing import Dict, Any, List, Optional
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum
11
+
12
+ from aiecs.application.knowledge_graph.reasoning.logic_parser import (
13
+ LogicQueryParser,
14
+ )
15
+ from aiecs.domain.knowledge_graph.schema.schema_manager import SchemaManager
16
+
17
+
18
+ class QueryType(str, Enum):
19
+ """Query type enumeration"""
20
+
21
+ SELECT = "SELECT"
22
+ ASK = "ASK"
23
+ COUNT = "COUNT"
24
+ FIND = "FIND"
25
+
26
+
27
+ @dataclass
28
+ class Variable:
29
+ """Query variable"""
30
+
31
+ name: str
32
+ type: Optional[str] = None
33
+
34
+
35
+ @dataclass
36
+ class Predicate:
37
+ """Logical predicate"""
38
+
39
+ name: str
40
+ arguments: List[Any] = field(default_factory=list)
41
+
42
+
43
+ @dataclass
44
+ class Constraint:
45
+ """Query constraint"""
46
+
47
+ constraint_type: str
48
+ variable: Variable
49
+ value: Any
50
+
51
+
52
+ @dataclass
53
+ class LogicalQuery:
54
+ """
55
+ Structured logical query representation
56
+
57
+ Represents a parsed natural language query as a logical form
58
+ with variables, predicates, and constraints.
59
+ """
60
+
61
+ query_type: QueryType
62
+ variables: List[Variable] = field(default_factory=list)
63
+ predicates: List[Predicate] = field(default_factory=list)
64
+ constraints: List[Constraint] = field(default_factory=list)
65
+ raw_query: str = ""
66
+
67
+ def to_dict(self) -> Dict[str, Any]:
68
+ """Convert to dictionary representation"""
69
+ return {
70
+ "query_type": self.query_type.value,
71
+ "variables": [{"name": v.name, "type": v.type} for v in self.variables],
72
+ "predicates": [
73
+ {
74
+ "name": p.name,
75
+ "arguments": [
76
+ arg.name if hasattr(arg, "name") else str(arg) for arg in p.arguments
77
+ ],
78
+ }
79
+ for p in self.predicates
80
+ ],
81
+ "constraints": [
82
+ {
83
+ "type": c.constraint_type,
84
+ "variable": c.variable.name,
85
+ "value": c.value,
86
+ }
87
+ for c in self.constraints
88
+ ],
89
+ "raw_query": self.raw_query,
90
+ }
91
+
92
+
93
+ class LogicFormParser:
94
+ """
95
+ Logic Form Parser
96
+
97
+ Converts natural language queries into structured logical forms.
98
+ Uses LogicQueryParser internally for parsing.
99
+
100
+ Example:
101
+ ```python
102
+ parser = LogicFormParser()
103
+ logical_query = parser.parse("Find all people who work for companies in San Francisco")
104
+
105
+ print(f"Query type: {logical_query.query_type}")
106
+ print(f"Variables: {[v.name for v in logical_query.variables]}")
107
+ print(f"Predicates: {[p.name for p in logical_query.predicates]}")
108
+ ```
109
+ """
110
+
111
+ def __init__(self, schema_manager: Optional[SchemaManager] = None):
112
+ """
113
+ Initialize logic form parser
114
+
115
+ Args:
116
+ schema_manager: Optional schema manager for type validation
117
+ """
118
+ self.schema_manager = schema_manager
119
+ if schema_manager:
120
+ self.query_parser = LogicQueryParser(schema_manager)
121
+ else:
122
+ # Create a minimal schema manager if none provided
123
+ self.query_parser = None
124
+
125
+ def parse(self, query: str) -> LogicalQuery:
126
+ """
127
+ Parse natural language query to logical form
128
+
129
+ Args:
130
+ query: Natural language query string
131
+
132
+ Returns:
133
+ LogicalQuery with structured representation
134
+ """
135
+ # Simple heuristic-based parsing for now
136
+ # In a full implementation, this would use NLP or the LogicQueryParser
137
+
138
+ logical_query = LogicalQuery(query_type=QueryType.FIND, raw_query=query)
139
+
140
+ # Extract variables (simple pattern matching)
141
+ if "people" in query.lower() or "person" in query.lower():
142
+ logical_query.variables.append(Variable(name="?person", type="Person"))
143
+ if "companies" in query.lower() or "company" in query.lower():
144
+ logical_query.variables.append(Variable(name="?company", type="Company"))
145
+
146
+ # Extract predicates
147
+ if "work for" in query.lower() or "works for" in query.lower():
148
+ logical_query.predicates.append(
149
+ Predicate(name="WORKS_FOR", arguments=["?person", "?company"])
150
+ )
151
+ if "located in" in query.lower():
152
+ logical_query.predicates.append(
153
+ Predicate(name="LOCATED_IN", arguments=["?company", "San Francisco"])
154
+ )
155
+
156
+ # Extract constraints
157
+ if "in San Francisco" in query or "in san francisco" in query.lower():
158
+ if logical_query.variables:
159
+ logical_query.constraints.append(
160
+ Constraint(
161
+ constraint_type="property_equals",
162
+ variable=logical_query.variables[-1],
163
+ value="San Francisco",
164
+ )
165
+ )
166
+
167
+ return logical_query