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,429 @@
1
+ """
2
+ Graph Store Caching Layer
3
+
4
+ Provides caching capabilities for graph storage backends using Redis.
5
+ Supports TTL-based expiration, invalidation, and cache warming.
6
+ """
7
+
8
+ import json
9
+ import hashlib
10
+ import logging
11
+ from typing import Any, Dict, Optional, Callable
12
+ from functools import wraps
13
+ import asyncio
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class GraphStoreCacheConfig:
19
+ """Configuration for graph store caching"""
20
+
21
+ def __init__(
22
+ self,
23
+ enabled: bool = True,
24
+ ttl: int = 300, # 5 minutes default
25
+ max_cache_size_mb: int = 100,
26
+ redis_url: Optional[str] = None,
27
+ key_prefix: str = "graph:",
28
+ ):
29
+ """
30
+ Initialize cache configuration
31
+
32
+ Args:
33
+ enabled: Enable/disable caching
34
+ ttl: Time-to-live for cache entries in seconds
35
+ max_cache_size_mb: Maximum cache size in MB (for in-memory fallback)
36
+ redis_url: Redis connection URL (e.g., "redis://localhost:6379/0")
37
+ key_prefix: Prefix for all cache keys
38
+ """
39
+ self.enabled = enabled
40
+ self.ttl = ttl
41
+ self.max_cache_size_mb = max_cache_size_mb
42
+ self.redis_url = redis_url
43
+ self.key_prefix = key_prefix
44
+
45
+
46
+ class CacheBackend:
47
+ """Abstract cache backend interface"""
48
+
49
+ async def get(self, key: str) -> Optional[str]:
50
+ """Get value from cache"""
51
+ raise NotImplementedError
52
+
53
+ async def set(self, key: str, value: str, ttl: int) -> None:
54
+ """Set value in cache with TTL"""
55
+ raise NotImplementedError
56
+
57
+ async def delete(self, key: str) -> None:
58
+ """Delete key from cache"""
59
+ raise NotImplementedError
60
+
61
+ async def delete_pattern(self, pattern: str) -> None:
62
+ """Delete all keys matching pattern"""
63
+ raise NotImplementedError
64
+
65
+ async def clear(self) -> None:
66
+ """Clear all cache entries"""
67
+ raise NotImplementedError
68
+
69
+ async def close(self) -> None:
70
+ """Close cache connection"""
71
+
72
+
73
+ class InMemoryCacheBackend(CacheBackend):
74
+ """In-memory LRU cache backend (fallback when Redis unavailable)"""
75
+
76
+ def __init__(self, max_size_mb: int = 100):
77
+ """
78
+ Initialize in-memory cache
79
+
80
+ Args:
81
+ max_size_mb: Maximum cache size in MB
82
+ """
83
+ self.cache: Dict[str, tuple[str, float]] = {} # key -> (value, expiry_time)
84
+ self.max_size_bytes = max_size_mb * 1024 * 1024
85
+ self.current_size = 0
86
+ self._lock = asyncio.Lock()
87
+
88
+ async def get(self, key: str) -> Optional[str]:
89
+ """Get value from cache if not expired"""
90
+ async with self._lock:
91
+ if key in self.cache:
92
+ value, expiry = self.cache[key]
93
+ if expiry > asyncio.get_event_loop().time():
94
+ return value
95
+ else:
96
+ # Expired, remove
97
+ del self.cache[key]
98
+ self.current_size -= len(value)
99
+ return None
100
+
101
+ async def set(self, key: str, value: str, ttl: int) -> None:
102
+ """Set value in cache with TTL"""
103
+ async with self._lock:
104
+ # Check if we need to evict
105
+ value_size = len(value)
106
+ while self.current_size + value_size > self.max_size_bytes and self.cache:
107
+ # Evict oldest entry (simplified LRU)
108
+ oldest_key = next(iter(self.cache))
109
+ oldest_value, _ = self.cache[oldest_key]
110
+ del self.cache[oldest_key]
111
+ self.current_size -= len(oldest_value)
112
+
113
+ # Add/update entry
114
+ expiry_time = asyncio.get_event_loop().time() + ttl
115
+ if key in self.cache:
116
+ old_value, _ = self.cache[key]
117
+ self.current_size -= len(old_value)
118
+
119
+ self.cache[key] = (value, expiry_time)
120
+ self.current_size += value_size
121
+
122
+ async def delete(self, key: str) -> None:
123
+ """Delete key from cache"""
124
+ async with self._lock:
125
+ if key in self.cache:
126
+ value, _ = self.cache[key]
127
+ del self.cache[key]
128
+ self.current_size -= len(value)
129
+
130
+ async def delete_pattern(self, pattern: str) -> None:
131
+ """Delete all keys matching pattern (simple prefix match)"""
132
+ async with self._lock:
133
+ keys_to_delete = [
134
+ k for k in self.cache.keys() if k.startswith(pattern.replace("*", ""))
135
+ ]
136
+ for key in keys_to_delete:
137
+ value, _ = self.cache[key]
138
+ del self.cache[key]
139
+ self.current_size -= len(value)
140
+
141
+ async def clear(self) -> None:
142
+ """Clear all cache entries"""
143
+ async with self._lock:
144
+ self.cache.clear()
145
+ self.current_size = 0
146
+
147
+
148
+ class RedisCacheBackend(CacheBackend):
149
+ """Redis cache backend"""
150
+
151
+ def __init__(self, redis_url: str):
152
+ """
153
+ Initialize Redis cache
154
+
155
+ Args:
156
+ redis_url: Redis connection URL
157
+ """
158
+ self.redis_url = redis_url
159
+ self.redis = None
160
+ self._initialized = False
161
+
162
+ async def initialize(self) -> bool:
163
+ """Initialize Redis connection"""
164
+ try:
165
+ import redis.asyncio as aioredis
166
+
167
+ self.redis = await aioredis.from_url(
168
+ self.redis_url, encoding="utf-8", decode_responses=True
169
+ )
170
+ # Test connection
171
+ await self.redis.ping()
172
+ self._initialized = True
173
+ logger.info(f"Redis cache initialized: {self.redis_url}")
174
+ return True
175
+ except Exception as e:
176
+ logger.warning(f"Failed to initialize Redis cache: {e}")
177
+ self.redis = None
178
+ self._initialized = False
179
+ return False
180
+
181
+ async def get(self, key: str) -> Optional[str]:
182
+ """Get value from Redis"""
183
+ if not self._initialized or not self.redis:
184
+ return None
185
+
186
+ try:
187
+ return await self.redis.get(key)
188
+ except Exception as e:
189
+ logger.warning(f"Redis get error: {e}")
190
+ return None
191
+
192
+ async def set(self, key: str, value: str, ttl: int) -> None:
193
+ """Set value in Redis with TTL"""
194
+ if not self._initialized or not self.redis:
195
+ return
196
+
197
+ try:
198
+ await self.redis.setex(key, ttl, value)
199
+ except Exception as e:
200
+ logger.warning(f"Redis set error: {e}")
201
+
202
+ async def delete(self, key: str) -> None:
203
+ """Delete key from Redis"""
204
+ if not self._initialized or not self.redis:
205
+ return
206
+
207
+ try:
208
+ await self.redis.delete(key)
209
+ except Exception as e:
210
+ logger.warning(f"Redis delete error: {e}")
211
+
212
+ async def delete_pattern(self, pattern: str) -> None:
213
+ """Delete all keys matching pattern"""
214
+ if not self._initialized or not self.redis:
215
+ return
216
+
217
+ try:
218
+ cursor = 0
219
+ while True:
220
+ cursor, keys = await self.redis.scan(cursor, match=pattern, count=100)
221
+ if keys:
222
+ await self.redis.delete(*keys)
223
+ if cursor == 0:
224
+ break
225
+ except Exception as e:
226
+ logger.warning(f"Redis delete_pattern error: {e}")
227
+
228
+ async def clear(self) -> None:
229
+ """Clear all cache entries (dangerous in production!)"""
230
+ if not self._initialized or not self.redis:
231
+ return
232
+
233
+ try:
234
+ await self.redis.flushdb()
235
+ except Exception as e:
236
+ logger.warning(f"Redis clear error: {e}")
237
+
238
+ async def close(self) -> None:
239
+ """Close Redis connection"""
240
+ if self.redis:
241
+ await self.redis.close()
242
+ self._initialized = False
243
+
244
+
245
+ class GraphStoreCache:
246
+ """
247
+ Cache layer for graph store operations
248
+
249
+ Provides transparent caching with automatic invalidation.
250
+ Falls back to in-memory cache if Redis is unavailable.
251
+
252
+ Example:
253
+ ```python
254
+ cache = GraphStoreCache(GraphStoreCacheConfig(
255
+ redis_url="redis://localhost:6379/0"
256
+ ))
257
+ await cache.initialize()
258
+
259
+ # Cache a query result
260
+ result = await cache.get_or_set(
261
+ "entity:person_1",
262
+ lambda: store.get_entity("person_1"),
263
+ ttl=300
264
+ )
265
+
266
+ # Invalidate cache
267
+ await cache.invalidate_entity("person_1")
268
+ ```
269
+ """
270
+
271
+ def __init__(self, config: GraphStoreCacheConfig):
272
+ """
273
+ Initialize cache
274
+
275
+ Args:
276
+ config: Cache configuration
277
+ """
278
+ self.config = config
279
+ self.backend: Optional[CacheBackend] = None
280
+ self._initialized = False
281
+
282
+ async def initialize(self) -> None:
283
+ """Initialize cache backend (Redis or in-memory fallback)"""
284
+ if not self.config.enabled:
285
+ logger.info("Graph store cache disabled")
286
+ return
287
+
288
+ # Try Redis first
289
+ if self.config.redis_url:
290
+ redis_backend = RedisCacheBackend(self.config.redis_url)
291
+ if await redis_backend.initialize():
292
+ self.backend = redis_backend
293
+ self._initialized = True
294
+ logger.info("Using Redis cache backend")
295
+ return
296
+
297
+ # Fallback to in-memory
298
+ self.backend = InMemoryCacheBackend(self.config.max_cache_size_mb)
299
+ self._initialized = True
300
+ logger.info("Using in-memory cache backend (fallback)")
301
+
302
+ async def close(self) -> None:
303
+ """Close cache backend"""
304
+ if self.backend:
305
+ await self.backend.close()
306
+ self._initialized = False
307
+
308
+ def _make_key(self, operation: str, *args) -> str:
309
+ """
310
+ Create cache key from operation and arguments
311
+
312
+ Args:
313
+ operation: Operation name (e.g., "entity", "relation", "neighbors")
314
+ *args: Operation arguments
315
+
316
+ Returns:
317
+ Cache key string
318
+ """
319
+ # Create deterministic key from args
320
+ args_str = json.dumps(args, sort_keys=True)
321
+ args_hash = hashlib.md5(args_str.encode()).hexdigest()[:8]
322
+ return f"{self.config.key_prefix}{operation}:{args_hash}"
323
+
324
+ async def get_or_set(self, key: str, fetch_func: Callable, ttl: Optional[int] = None) -> Any:
325
+ """
326
+ Get value from cache or fetch and cache it
327
+
328
+ Args:
329
+ key: Cache key
330
+ fetch_func: Async function to fetch value if not cached
331
+ ttl: TTL override (uses config.ttl if None)
332
+
333
+ Returns:
334
+ Cached or fetched value
335
+ """
336
+ if not self._initialized or not self.backend:
337
+ return await fetch_func()
338
+
339
+ # Try to get from cache
340
+ cached = await self.backend.get(key)
341
+ if cached is not None:
342
+ try:
343
+ return json.loads(cached)
344
+ except json.JSONDecodeError:
345
+ logger.warning(f"Failed to decode cached value for key: {key}")
346
+
347
+ # Fetch and cache
348
+ value = await fetch_func()
349
+ if value is not None:
350
+ try:
351
+ cached_value = json.dumps(value)
352
+ await self.backend.set(key, cached_value, ttl or self.config.ttl)
353
+ except (TypeError, json.JSONEncodeError) as e:
354
+ logger.warning(f"Failed to cache value for key {key}: {e}")
355
+
356
+ return value
357
+
358
+ async def invalidate_entity(self, entity_id: str) -> None:
359
+ """
360
+ Invalidate all cache entries related to an entity
361
+
362
+ Args:
363
+ entity_id: Entity ID to invalidate
364
+ """
365
+ if not self._initialized or not self.backend:
366
+ return
367
+
368
+ # Invalidate entity and related queries
369
+ await self.backend.delete(f"{self.config.key_prefix}entity:{entity_id}")
370
+ await self.backend.delete_pattern(f"{self.config.key_prefix}neighbors:{entity_id}:*")
371
+ await self.backend.delete_pattern(f"{self.config.key_prefix}paths:*:{entity_id}:*")
372
+ await self.backend.delete_pattern(f"{self.config.key_prefix}traverse:{entity_id}:*")
373
+
374
+ async def invalidate_relation(self, relation_id: str) -> None:
375
+ """
376
+ Invalidate all cache entries related to a relation
377
+
378
+ Args:
379
+ relation_id: Relation ID to invalidate
380
+ """
381
+ if not self._initialized or not self.backend:
382
+ return
383
+
384
+ await self.backend.delete(f"{self.config.key_prefix}relation:{relation_id}")
385
+ # Relations affect neighbors and paths, so invalidate broadly
386
+ await self.backend.delete_pattern(f"{self.config.key_prefix}neighbors:*")
387
+ await self.backend.delete_pattern(f"{self.config.key_prefix}paths:*")
388
+
389
+ async def clear(self) -> None:
390
+ """Clear all cache entries"""
391
+ if self._initialized and self.backend:
392
+ await self.backend.clear()
393
+
394
+
395
+ def cached_method(cache_key_func: Callable[[Any, ...], str], ttl: Optional[int] = None):
396
+ """
397
+ Decorator for caching graph store methods
398
+
399
+ Args:
400
+ cache_key_func: Function to generate cache key from method args
401
+ ttl: Cache TTL (uses cache config if None)
402
+
403
+ Example:
404
+ ```python
405
+ @cached_method(lambda self, entity_id: f"entity:{entity_id}")
406
+ async def get_entity(self, entity_id: str) -> Optional[Entity]:
407
+ # Implementation
408
+ pass
409
+ ```
410
+ """
411
+
412
+ def decorator(func: Callable) -> Callable:
413
+ @wraps(func)
414
+ async def wrapper(self, *args, **kwargs):
415
+ # Check if caching is available
416
+ if not hasattr(self, "cache") or not self.cache or not self.cache._initialized:
417
+ return await func(self, *args, **kwargs)
418
+
419
+ # Generate cache key
420
+ cache_key = cache_key_func(self, *args, **kwargs)
421
+
422
+ # Try to get from cache or fetch
423
+ return await self.cache.get_or_set(
424
+ cache_key, lambda: func(self, *args, **kwargs), ttl=ttl
425
+ )
426
+
427
+ return wrapper
428
+
429
+ return decorator
@@ -0,0 +1,226 @@
1
+ """
2
+ Distributed Graph Operations (Optional/Future)
3
+
4
+ Provides foundation for distributed graph processing across multiple nodes.
5
+ This module defines interfaces and utilities for future distributed implementations.
6
+
7
+ Note: Current implementation focuses on single-node optimizations.
8
+ Distributed features are placeholders for future scaling needs.
9
+ """
10
+
11
+ import logging
12
+ from typing import Optional, List, Dict, Any
13
+ from enum import Enum
14
+ from dataclasses import dataclass
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class PartitionStrategy(str, Enum):
20
+ """Graph partitioning strategy"""
21
+
22
+ HASH = "hash" # Hash-based partitioning
23
+ RANGE = "range" # Range-based partitioning
24
+ COMMUNITY = "community" # Community detection-based
25
+ CUSTOM = "custom" # Custom partitioning function
26
+
27
+
28
+ @dataclass
29
+ class GraphPartition:
30
+ """
31
+ Graph partition metadata
32
+
33
+ Describes a partition of the graph for distributed processing.
34
+ """
35
+
36
+ partition_id: int
37
+ node_count: int
38
+ edge_count: int
39
+ node_ids: Optional[List[str]] = None
40
+
41
+ def to_dict(self) -> Dict[str, Any]:
42
+ """Convert to dictionary"""
43
+ return {
44
+ "partition_id": self.partition_id,
45
+ "node_count": self.node_count,
46
+ "edge_count": self.edge_count,
47
+ "has_node_list": self.node_ids is not None,
48
+ }
49
+
50
+
51
+ class DistributedGraphMixin:
52
+ """
53
+ Mixin for distributed graph operations (Future Enhancement)
54
+
55
+ Provides interfaces for partitioning and distributed query execution.
56
+ Current implementation is a placeholder for future distributed features.
57
+
58
+ Example (Future Use):
59
+ ```python
60
+ class DistributedGraphStore(PostgresGraphStore, DistributedGraphMixin):
61
+ pass
62
+
63
+ store = DistributedGraphStore()
64
+
65
+ # Partition graph for distributed processing
66
+ partitions = await store.partition_graph(num_partitions=4)
67
+
68
+ # Execute distributed query
69
+ result = await store.distributed_query(query, partitions)
70
+ ```
71
+ """
72
+
73
+ async def partition_graph(
74
+ self,
75
+ num_partitions: int,
76
+ strategy: PartitionStrategy = PartitionStrategy.HASH,
77
+ ) -> List[GraphPartition]:
78
+ """
79
+ Partition graph for distributed processing
80
+
81
+ Args:
82
+ num_partitions: Number of partitions to create
83
+ strategy: Partitioning strategy
84
+
85
+ Returns:
86
+ List of partition metadata
87
+
88
+ Note:
89
+ This is a placeholder for future implementation.
90
+ Current version returns conceptual partitions.
91
+ """
92
+ logger.info("Graph partitioning requested but not yet implemented")
93
+ logger.info("For production distributed graphs, consider:")
94
+ logger.info(" - Neo4j Fabric for distributed queries")
95
+ logger.info(" - TigerGraph for native distributed processing")
96
+ logger.info(" - Amazon Neptune with read replicas")
97
+
98
+ # Placeholder: Return empty partitions
99
+ return [
100
+ GraphPartition(partition_id=i, node_count=0, edge_count=0)
101
+ for i in range(num_partitions)
102
+ ]
103
+
104
+ async def get_partition_info(self, partition_id: int) -> Optional[GraphPartition]:
105
+ """
106
+ Get information about a specific partition
107
+
108
+ Args:
109
+ partition_id: Partition ID
110
+
111
+ Returns:
112
+ Partition metadata or None
113
+ """
114
+ # Placeholder
115
+ logger.warning("Partition info not available in current implementation")
116
+ return None
117
+
118
+ async def distributed_query(self, query: str, partitions: Optional[List[int]] = None) -> Any:
119
+ """
120
+ Execute query across distributed partitions
121
+
122
+ Args:
123
+ query: Query to execute
124
+ partitions: Specific partitions to query (None for all)
125
+
126
+ Returns:
127
+ Aggregated query results
128
+
129
+ Note:
130
+ This is a placeholder. Current implementation executes locally.
131
+ """
132
+ logger.warning("Distributed query not implemented, executing locally")
133
+ # Fall back to local execution
134
+ return None
135
+
136
+
137
+ # Utility functions for future distributed implementations
138
+
139
+
140
+ def hash_partition_key(entity_id: str, num_partitions: int) -> int:
141
+ """
142
+ Compute partition ID using hash function
143
+
144
+ Args:
145
+ entity_id: Entity ID to partition
146
+ num_partitions: Total number of partitions
147
+
148
+ Returns:
149
+ Partition ID (0 to num_partitions-1)
150
+ """
151
+ return hash(entity_id) % num_partitions
152
+
153
+
154
+ def range_partition_key(entity_id: str, ranges: List[tuple[str, str]]) -> int:
155
+ """
156
+ Compute partition ID using range-based partitioning
157
+
158
+ Args:
159
+ entity_id: Entity ID to partition
160
+ ranges: List of (start, end) ranges for each partition
161
+
162
+ Returns:
163
+ Partition ID
164
+ """
165
+ for i, (start, end) in enumerate(ranges):
166
+ if start <= entity_id < end:
167
+ return i
168
+ return len(ranges) - 1 # Default to last partition
169
+
170
+
171
+ # Documentation for distributed graph deployment
172
+
173
+ DISTRIBUTED_DEPLOYMENT_NOTES = """
174
+ ## Distributed Graph Deployment Options
175
+
176
+ For large-scale distributed graphs (>100M nodes), consider:
177
+
178
+ ### 1. Neo4j Fabric
179
+ - Federated queries across multiple Neo4j databases
180
+ - Sharding support
181
+ - CYPHER-based distributed queries
182
+
183
+ ### 2. TigerGraph
184
+ - Native distributed graph database
185
+ - Horizontal scaling
186
+ - GSQL for distributed queries
187
+
188
+ ### 3. Amazon Neptune
189
+ - Managed graph database service
190
+ - Read replicas for scale-out reads
191
+ - Integration with AWS ecosystem
192
+
193
+ ### 4. JanusGraph
194
+ - Distributed graph database
195
+ - Backend-agnostic (Cassandra, HBase, etc.)
196
+ - Gremlin query language
197
+
198
+ ### 5. PostgreSQL with Citus
199
+ - Distributed PostgreSQL
200
+ - Can be used with current PostgresGraphStore
201
+ - Horizontal sharding of graph tables
202
+
203
+ ### Current AIECS Architecture
204
+
205
+ For most use cases (< 100M nodes), single-node PostgreSQL is sufficient:
206
+ - Vertical scaling up to 1TB RAM
207
+ - Connection pooling for concurrent access
208
+ - Read replicas for read scaling
209
+ - Batch operations for bulk loading
210
+
211
+ When to consider distributed:
212
+ - > 100M nodes
213
+ - > 1B edges
214
+ - Multiple geographic regions
215
+ - Extreme write throughput requirements (>100K writes/sec)
216
+
217
+ ### Migration Path
218
+
219
+ 1. Start with single-node PostgreSQL (current)
220
+ 2. Add read replicas for read scaling
221
+ 3. Implement connection routing for replicas
222
+ 4. If needed, migrate to distributed backend:
223
+ - Implement custom GraphStore for chosen backend
224
+ - Use GraphStorageMigrator for data migration
225
+ - Test with compatibility suite
226
+ """