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,390 @@
1
+ """
2
+ Comprehensive Error Handling and Logging for Graph Storage
3
+
4
+ Provides structured error handling, logging, and exception types
5
+ for production-ready graph storage operations.
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+ import traceback
11
+ from typing import Optional, Dict, Any, Callable
12
+ from enum import Enum
13
+ from dataclasses import dataclass
14
+ from datetime import datetime
15
+ import functools
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class GraphStoreError(Exception):
21
+ """Base exception for graph store errors"""
22
+
23
+
24
+ class GraphStoreConnectionError(GraphStoreError):
25
+ """Connection-related errors"""
26
+
27
+
28
+ class GraphStoreQueryError(GraphStoreError):
29
+ """Query execution errors"""
30
+
31
+
32
+ class GraphStoreValidationError(GraphStoreError):
33
+ """Data validation errors"""
34
+
35
+
36
+ class GraphStoreNotFoundError(GraphStoreError):
37
+ """Entity/relation not found errors"""
38
+
39
+
40
+ class GraphStoreConflictError(GraphStoreError):
41
+ """Conflict errors (duplicate IDs, etc.)"""
42
+
43
+
44
+ class GraphStoreTimeoutError(GraphStoreError):
45
+ """Operation timeout errors"""
46
+
47
+
48
+ class ErrorSeverity(str, Enum):
49
+ """Error severity levels"""
50
+
51
+ LOW = "low"
52
+ MEDIUM = "medium"
53
+ HIGH = "high"
54
+ CRITICAL = "critical"
55
+
56
+
57
+ @dataclass
58
+ class ErrorContext:
59
+ """Context information for error reporting"""
60
+
61
+ operation: str
62
+ entity_id: Optional[str] = None
63
+ relation_id: Optional[str] = None
64
+ query: Optional[str] = None
65
+ parameters: Optional[Dict[str, Any]] = None
66
+ timestamp: datetime = None
67
+ severity: ErrorSeverity = ErrorSeverity.MEDIUM
68
+
69
+ def __post_init__(self):
70
+ if self.timestamp is None:
71
+ self.timestamp = datetime.utcnow()
72
+
73
+ def to_dict(self) -> Dict[str, Any]:
74
+ """Convert to dictionary for logging"""
75
+ return {
76
+ "operation": self.operation,
77
+ "entity_id": self.entity_id,
78
+ "relation_id": self.relation_id,
79
+ "query": (
80
+ self.query[:100] + "..." if self.query and len(self.query) > 100 else self.query
81
+ ),
82
+ "parameters": self.parameters,
83
+ "timestamp": self.timestamp.isoformat(),
84
+ "severity": self.severity.value,
85
+ }
86
+
87
+
88
+ class ErrorHandler:
89
+ """
90
+ Centralized error handling and logging
91
+
92
+ Provides structured error handling with context and logging.
93
+
94
+ Example:
95
+ ```python
96
+ handler = ErrorHandler()
97
+
98
+ try:
99
+ await store.add_entity(entity)
100
+ except Exception as e:
101
+ handler.handle_error(
102
+ e,
103
+ ErrorContext(
104
+ operation="add_entity",
105
+ entity_id=entity.id,
106
+ severity=ErrorSeverity.HIGH
107
+ )
108
+ )
109
+ ```
110
+ """
111
+
112
+ def __init__(self, log_level: int = logging.ERROR):
113
+ """
114
+ Initialize error handler
115
+
116
+ Args:
117
+ log_level: Logging level for errors
118
+ """
119
+ self.log_level = log_level
120
+
121
+ def handle_error(self, error: Exception, context: ErrorContext, reraise: bool = True) -> None:
122
+ """
123
+ Handle and log an error with context
124
+
125
+ Args:
126
+ error: Exception that occurred
127
+ context: Error context information
128
+ reraise: Whether to re-raise the exception
129
+ """
130
+ # Map exception types
131
+ mapped_error = self._map_exception(error)
132
+
133
+ # Log error with context
134
+ self._log_error(mapped_error, context)
135
+
136
+ # Re-raise if requested
137
+ if reraise:
138
+ raise mapped_error from error
139
+
140
+ def _map_exception(self, error: Exception) -> Exception:
141
+ """
142
+ Map generic exceptions to specific graph store exceptions
143
+
144
+ Args:
145
+ error: Original exception
146
+
147
+ Returns:
148
+ Mapped exception
149
+ """
150
+ error_str = str(error).lower()
151
+ error_type = type(error).__name__
152
+
153
+ # Connection errors
154
+ if any(keyword in error_str for keyword in ["connection", "connect", "timeout", "network"]):
155
+ return GraphStoreConnectionError(str(error))
156
+
157
+ # Not found errors
158
+ if any(keyword in error_str for keyword in ["not found", "does not exist", "missing"]):
159
+ return GraphStoreNotFoundError(str(error))
160
+
161
+ # Conflict errors
162
+ if any(
163
+ keyword in error_str
164
+ for keyword in [
165
+ "duplicate",
166
+ "already exists",
167
+ "conflict",
168
+ "unique",
169
+ ]
170
+ ):
171
+ return GraphStoreConflictError(str(error))
172
+
173
+ # Validation errors
174
+ if any(
175
+ keyword in error_str for keyword in ["invalid", "validation", "required", "missing"]
176
+ ):
177
+ return GraphStoreValidationError(str(error))
178
+
179
+ # Timeout errors
180
+ if "timeout" in error_str or "TimeoutError" in error_type:
181
+ return GraphStoreTimeoutError(str(error))
182
+
183
+ # Query errors
184
+ if any(keyword in error_str for keyword in ["syntax", "query", "sql", "execute"]):
185
+ return GraphStoreQueryError(str(error))
186
+
187
+ # Return original if no mapping
188
+ return error
189
+
190
+ def _log_error(self, error: Exception, context: ErrorContext) -> None:
191
+ """
192
+ Log error with structured context
193
+
194
+ Args:
195
+ error: Exception to log
196
+ context: Error context
197
+ """
198
+ error_dict = {
199
+ "error_type": type(error).__name__,
200
+ "error_message": str(error),
201
+ "context": context.to_dict(),
202
+ "traceback": traceback.format_exc(),
203
+ }
204
+
205
+ # Log based on severity
206
+ if context.severity == ErrorSeverity.CRITICAL:
207
+ logger.critical(f"CRITICAL: {error_dict}")
208
+ elif context.severity == ErrorSeverity.HIGH:
209
+ logger.error(f"HIGH: {error_dict}")
210
+ elif context.severity == ErrorSeverity.MEDIUM:
211
+ logger.warning(f"MEDIUM: {error_dict}")
212
+ else:
213
+ logger.info(f"LOW: {error_dict}")
214
+
215
+
216
+ def error_handler(
217
+ operation: str,
218
+ severity: ErrorSeverity = ErrorSeverity.MEDIUM,
219
+ reraise: bool = True,
220
+ ):
221
+ """
222
+ Decorator for automatic error handling
223
+
224
+ Args:
225
+ operation: Operation name for context
226
+ severity: Error severity level
227
+ reraise: Whether to re-raise exceptions
228
+
229
+ Example:
230
+ ```python
231
+ @error_handler("add_entity", severity=ErrorSeverity.HIGH)
232
+ async def add_entity(self, entity: Entity):
233
+ # Implementation
234
+ pass
235
+ ```
236
+ """
237
+
238
+ def decorator(func: Callable) -> Callable:
239
+ @functools.wraps(func)
240
+ async def wrapper(self, *args, **kwargs):
241
+ handler = ErrorHandler()
242
+
243
+ # Extract context from arguments
244
+ entity_id = None
245
+ relation_id = None
246
+
247
+ if args and hasattr(args[0], "id"):
248
+ entity_id = args[0].id
249
+ elif "entity_id" in kwargs:
250
+ entity_id = kwargs["entity_id"]
251
+ elif "relation_id" in kwargs:
252
+ relation_id = kwargs["relation_id"]
253
+
254
+ context = ErrorContext(
255
+ operation=operation,
256
+ entity_id=entity_id,
257
+ relation_id=relation_id,
258
+ severity=severity,
259
+ )
260
+
261
+ try:
262
+ return await func(self, *args, **kwargs)
263
+ except Exception as e:
264
+ handler.handle_error(e, context, reraise=reraise)
265
+
266
+ return wrapper
267
+
268
+ return decorator
269
+
270
+
271
+ class RetryHandler:
272
+ """
273
+ Retry handler for transient errors
274
+
275
+ Implements exponential backoff retry logic for operations
276
+ that may fail due to transient issues.
277
+
278
+ Example:
279
+ ```python
280
+ retry = RetryHandler(max_retries=3, base_delay=1.0)
281
+
282
+ result = await retry.execute(
283
+ lambda: store.get_entity("entity_1"),
284
+ retry_on=[GraphStoreConnectionError]
285
+ )
286
+ ```
287
+ """
288
+
289
+ def __init__(
290
+ self,
291
+ max_retries: int = 3,
292
+ base_delay: float = 1.0,
293
+ max_delay: float = 60.0,
294
+ exponential_base: float = 2.0,
295
+ ):
296
+ """
297
+ Initialize retry handler
298
+
299
+ Args:
300
+ max_retries: Maximum number of retry attempts
301
+ base_delay: Base delay in seconds
302
+ max_delay: Maximum delay in seconds
303
+ exponential_base: Exponential backoff multiplier
304
+ """
305
+ self.max_retries = max_retries
306
+ self.base_delay = base_delay
307
+ self.max_delay = max_delay
308
+ self.exponential_base = exponential_base
309
+
310
+ async def execute(
311
+ self, func: Callable, retry_on: Optional[list] = None, *args, **kwargs
312
+ ) -> Any:
313
+ """
314
+ Execute function with retry logic
315
+
316
+ Args:
317
+ func: Async function to execute
318
+ retry_on: List of exception types to retry on (None = all)
319
+ *args: Function arguments
320
+ **kwargs: Function keyword arguments
321
+
322
+ Returns:
323
+ Function result
324
+
325
+ Raises:
326
+ Last exception if all retries fail
327
+ """
328
+ if retry_on is None:
329
+ retry_on = [Exception]
330
+
331
+ last_exception = None
332
+
333
+ for attempt in range(self.max_retries + 1):
334
+ try:
335
+ return await func(*args, **kwargs)
336
+ except Exception as e:
337
+ last_exception = e
338
+
339
+ # Check if we should retry
340
+ if not any(isinstance(e, exc_type) for exc_type in retry_on):
341
+ raise
342
+
343
+ # Don't retry on last attempt
344
+ if attempt >= self.max_retries:
345
+ break
346
+
347
+ # Calculate delay with exponential backoff
348
+ delay = min(
349
+ self.base_delay * (self.exponential_base**attempt),
350
+ self.max_delay,
351
+ )
352
+
353
+ logger.warning(
354
+ f"Retry attempt {attempt + 1}/{self.max_retries} after {delay:.1f}s: {e}"
355
+ )
356
+
357
+ await asyncio.sleep(delay)
358
+
359
+ # All retries exhausted
360
+ raise last_exception
361
+
362
+
363
+ # Configure logging for graph storage
364
+ def configure_graph_storage_logging(
365
+ level: int = logging.INFO, format_string: Optional[str] = None
366
+ ) -> None:
367
+ """
368
+ Configure logging for graph storage modules
369
+
370
+ Args:
371
+ level: Logging level
372
+ format_string: Custom format string
373
+ """
374
+ if format_string is None:
375
+ format_string = (
376
+ "%(asctime)s - %(name)s - %(levelname)s - " "%(message)s - [%(filename)s:%(lineno)d]"
377
+ )
378
+
379
+ handler = logging.StreamHandler()
380
+ handler.setFormatter(logging.Formatter(format_string))
381
+
382
+ # Configure graph storage logger
383
+ graph_logger = logging.getLogger("aiecs.infrastructure.graph_storage")
384
+ graph_logger.setLevel(level)
385
+ graph_logger.addHandler(handler)
386
+
387
+ logger.info(f"Graph storage logging configured at level {logging.getLevelName(level)}")
388
+
389
+
390
+ # Import asyncio for retry handler
@@ -0,0 +1,306 @@
1
+ """
2
+ Graceful Degradation for Graph Storage
3
+
4
+ Provides automatic fallback to in-memory storage when primary backend fails,
5
+ ensuring service continuity even during backend outages.
6
+ """
7
+
8
+ import logging
9
+ from typing import Optional, Any, Dict
10
+ from dataclasses import dataclass
11
+ from enum import Enum
12
+
13
+ from aiecs.infrastructure.graph_storage import InMemoryGraphStore, GraphStore
14
+ from aiecs.infrastructure.graph_storage.error_handling import (
15
+ GraphStoreConnectionError,
16
+ GraphStoreError,
17
+ ErrorHandler,
18
+ ErrorContext,
19
+ ErrorSeverity,
20
+ )
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class DegradationMode(str, Enum):
26
+ """Degradation mode"""
27
+
28
+ NORMAL = "normal" # Using primary backend
29
+ DEGRADED = "degraded" # Using fallback
30
+ FAILED = "failed" # Both backends failed
31
+
32
+
33
+ @dataclass
34
+ class DegradationStatus:
35
+ """Status of graceful degradation"""
36
+
37
+ mode: DegradationMode
38
+ primary_available: bool
39
+ fallback_available: bool
40
+ last_failure: Optional[str] = None
41
+ failure_count: int = 0
42
+
43
+ def to_dict(self) -> Dict[str, Any]:
44
+ """Convert to dictionary"""
45
+ return {
46
+ "mode": self.mode.value,
47
+ "primary_available": self.primary_available,
48
+ "fallback_available": self.fallback_available,
49
+ "last_failure": self.last_failure,
50
+ "failure_count": self.failure_count,
51
+ }
52
+
53
+
54
+ class GracefulDegradationStore:
55
+ """
56
+ Graph store with graceful degradation support
57
+
58
+ Automatically falls back to in-memory storage when primary backend fails.
59
+
60
+ Example:
61
+ ```python
62
+ primary = PostgresGraphStore(...)
63
+ store = GracefulDegradationStore(primary)
64
+ await store.initialize()
65
+
66
+ # If PostgreSQL fails, automatically uses in-memory fallback
67
+ entity = await store.get_entity("entity_1")
68
+ ```
69
+ """
70
+
71
+ def __init__(
72
+ self,
73
+ primary_store: GraphStore,
74
+ enable_fallback: bool = True,
75
+ max_failures: int = 3,
76
+ ):
77
+ """
78
+ Initialize graceful degradation store
79
+
80
+ Args:
81
+ primary_store: Primary graph store backend
82
+ enable_fallback: Enable automatic fallback
83
+ max_failures: Max failures before switching to fallback
84
+ """
85
+ self.primary_store = primary_store
86
+ self.enable_fallback = enable_fallback
87
+ self.max_failures = max_failures
88
+
89
+ self.fallback_store: Optional[InMemoryGraphStore] = None
90
+ self.status = DegradationStatus(
91
+ mode=DegradationMode.NORMAL,
92
+ primary_available=True,
93
+ fallback_available=False,
94
+ )
95
+ self.failure_count = 0
96
+ self.error_handler = ErrorHandler()
97
+
98
+ async def initialize(self) -> None:
99
+ """Initialize both primary and fallback stores"""
100
+ # Initialize primary
101
+ try:
102
+ await self.primary_store.initialize()
103
+ self.status.primary_available = True
104
+ logger.info("Primary store initialized successfully")
105
+ except Exception as e:
106
+ self.status.primary_available = False
107
+ self.status.last_failure = str(e)
108
+ logger.error(f"Primary store initialization failed: {e}")
109
+
110
+ if self.enable_fallback:
111
+ await self._initialize_fallback()
112
+
113
+ # Initialize fallback if enabled
114
+ if self.enable_fallback and not self.fallback_store:
115
+ await self._initialize_fallback()
116
+
117
+ async def _initialize_fallback(self) -> None:
118
+ """Initialize fallback in-memory store"""
119
+ try:
120
+ self.fallback_store = InMemoryGraphStore()
121
+ await self.fallback_store.initialize()
122
+ self.status.fallback_available = True
123
+ self.status.mode = DegradationMode.DEGRADED
124
+ logger.warning("Using fallback in-memory store")
125
+ except Exception as e:
126
+ self.status.fallback_available = False
127
+ self.status.mode = DegradationMode.FAILED
128
+ logger.critical(f"Fallback store initialization failed: {e}")
129
+
130
+ async def close(self) -> None:
131
+ """Close both stores"""
132
+ if self.primary_store:
133
+ try:
134
+ await self.primary_store.close()
135
+ except Exception as e:
136
+ logger.warning(f"Error closing primary store: {e}")
137
+
138
+ if self.fallback_store:
139
+ try:
140
+ await self.fallback_store.close()
141
+ except Exception as e:
142
+ logger.warning(f"Error closing fallback store: {e}")
143
+
144
+ def _get_active_store(self) -> GraphStore:
145
+ """
146
+ Get the currently active store (primary or fallback)
147
+
148
+ Returns:
149
+ Active graph store
150
+
151
+ Raises:
152
+ GraphStoreError: If no store is available
153
+ """
154
+ if self.status.primary_available:
155
+ return self.primary_store
156
+ elif self.status.fallback_available and self.fallback_store:
157
+ return self.fallback_store
158
+ else:
159
+ raise GraphStoreError("No graph store available (primary and fallback both failed)")
160
+
161
+ async def _execute_with_fallback(self, operation: str, func, *args, **kwargs):
162
+ """
163
+ Execute operation with automatic fallback
164
+
165
+ Args:
166
+ operation: Operation name for error context
167
+ func: Function to execute
168
+ *args: Function arguments
169
+ **kwargs: Function keyword arguments
170
+
171
+ Returns:
172
+ Function result
173
+ """
174
+ # Try primary first
175
+ if self.status.primary_available:
176
+ try:
177
+ result = await func(self.primary_store, *args, **kwargs)
178
+
179
+ # Reset failure count on success
180
+ if self.failure_count > 0:
181
+ logger.info("Primary store recovered, switching back")
182
+ self.failure_count = 0
183
+ self.status.primary_available = True
184
+ self.status.mode = DegradationMode.NORMAL
185
+
186
+ return result
187
+
188
+ except (GraphStoreConnectionError, Exception) as e:
189
+ self.failure_count += 1
190
+ self.status.last_failure = str(e)
191
+
192
+ # Log error
193
+ self.error_handler.handle_error(
194
+ e,
195
+ ErrorContext(operation=operation, severity=ErrorSeverity.HIGH),
196
+ reraise=False,
197
+ )
198
+
199
+ # Switch to fallback if threshold reached
200
+ if self.failure_count >= self.max_failures:
201
+ logger.warning(
202
+ f"Primary store failed {self.failure_count} times, "
203
+ f"switching to fallback"
204
+ )
205
+ self.status.primary_available = False
206
+ self.status.mode = DegradationMode.DEGRADED
207
+
208
+ # Try fallback if available
209
+ if self.status.fallback_available and self.fallback_store:
210
+ try:
211
+ return await func(self.fallback_store, *args, **kwargs)
212
+ except Exception as fallback_error:
213
+ logger.error(f"Fallback store also failed: {fallback_error}")
214
+ self.status.fallback_available = False
215
+ self.status.mode = DegradationMode.FAILED
216
+ raise GraphStoreError(
217
+ f"Both primary and fallback stores failed. "
218
+ f"Primary: {e}, Fallback: {fallback_error}"
219
+ )
220
+ else:
221
+ # No fallback available, raise original error
222
+ raise
223
+
224
+ # Use fallback if primary unavailable
225
+ elif self.status.fallback_available and self.fallback_store:
226
+ try:
227
+ return await func(self.fallback_store, *args, **kwargs)
228
+ except Exception as e:
229
+ logger.error(f"Fallback store failed: {e}")
230
+ self.status.fallback_available = False
231
+ self.status.mode = DegradationMode.FAILED
232
+ raise GraphStoreError(f"Fallback store failed: {e}")
233
+
234
+ else:
235
+ raise GraphStoreError("No graph store available")
236
+
237
+ # Delegate all GraphStore methods with fallback
238
+ async def add_entity(self, entity):
239
+ """Add entity with fallback"""
240
+ return await self._execute_with_fallback(
241
+ "add_entity", lambda store, e: store.add_entity(e), entity
242
+ )
243
+
244
+ async def get_entity(self, entity_id: str):
245
+ """Get entity with fallback"""
246
+ return await self._execute_with_fallback(
247
+ "get_entity", lambda store, eid: store.get_entity(eid), entity_id
248
+ )
249
+
250
+ async def add_relation(self, relation):
251
+ """Add relation with fallback"""
252
+ return await self._execute_with_fallback(
253
+ "add_relation", lambda store, r: store.add_relation(r), relation
254
+ )
255
+
256
+ async def get_relation(self, relation_id: str):
257
+ """Get relation with fallback"""
258
+ return await self._execute_with_fallback(
259
+ "get_relation",
260
+ lambda store, rid: store.get_relation(rid),
261
+ relation_id,
262
+ )
263
+
264
+ async def get_neighbors(self, entity_id: str, **kwargs):
265
+ """Get neighbors with fallback"""
266
+ return await self._execute_with_fallback(
267
+ "get_neighbors",
268
+ lambda store, eid, **kw: store.get_neighbors(eid, **kw),
269
+ entity_id,
270
+ **kwargs,
271
+ )
272
+
273
+ async def get_stats(self):
274
+ """Get stats with fallback"""
275
+ return await self._execute_with_fallback("get_stats", lambda store: store.get_stats())
276
+
277
+ def get_degradation_status(self) -> DegradationStatus:
278
+ """Get current degradation status"""
279
+ return self.status
280
+
281
+ async def try_recover_primary(self) -> bool:
282
+ """
283
+ Attempt to recover primary store
284
+
285
+ Returns:
286
+ True if recovery successful, False otherwise
287
+ """
288
+ if self.status.primary_available:
289
+ return True
290
+
291
+ try:
292
+ # Try to reinitialize primary
293
+ await self.primary_store.initialize()
294
+
295
+ # Test with a simple operation
296
+ await self.primary_store.get_stats()
297
+
298
+ self.status.primary_available = True
299
+ self.status.mode = DegradationMode.NORMAL
300
+ self.failure_count = 0
301
+ logger.info("Primary store recovered successfully")
302
+ return True
303
+
304
+ except Exception as e:
305
+ logger.warning(f"Primary store recovery failed: {e}")
306
+ return False