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,466 @@
1
+ """
2
+ Performance Monitoring for Graph Storage
3
+
4
+ Provides query performance monitoring, query plan analysis, and optimization suggestions.
5
+ """
6
+
7
+ import time
8
+ import logging
9
+ import asyncpg
10
+ from typing import Dict, List, Optional, Any
11
+ from dataclasses import dataclass, field
12
+ from collections import defaultdict
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @dataclass
18
+ class QueryStats:
19
+ """Statistics for a single query"""
20
+
21
+ query_type: str
22
+ query_text: str
23
+ execution_count: int = 0
24
+ total_time_ms: float = 0.0
25
+ min_time_ms: float = float("inf")
26
+ max_time_ms: float = 0.0
27
+ avg_time_ms: float = 0.0
28
+ execution_times: List[float] = field(default_factory=list)
29
+
30
+ def add_execution(self, duration_ms: float) -> None:
31
+ """Add a query execution time"""
32
+ self.execution_count += 1
33
+ self.total_time_ms += duration_ms
34
+ self.min_time_ms = min(self.min_time_ms, duration_ms)
35
+ self.max_time_ms = max(self.max_time_ms, duration_ms)
36
+ self.execution_times.append(duration_ms)
37
+
38
+ # Keep only last 100 executions for percentile calculations
39
+ if len(self.execution_times) > 100:
40
+ self.execution_times.pop(0)
41
+
42
+ self.avg_time_ms = self.total_time_ms / self.execution_count
43
+
44
+ def get_percentile(self, percentile: float) -> float:
45
+ """Get percentile execution time"""
46
+ if not self.execution_times:
47
+ return 0.0
48
+ sorted_times = sorted(self.execution_times)
49
+ index = int(len(sorted_times) * percentile / 100)
50
+ return sorted_times[min(index, len(sorted_times) - 1)]
51
+
52
+ def to_dict(self) -> Dict[str, Any]:
53
+ """Convert to dictionary"""
54
+ return {
55
+ "query_type": self.query_type,
56
+ "query_text": (
57
+ self.query_text[:100] + "..." if len(self.query_text) > 100 else self.query_text
58
+ ),
59
+ "execution_count": self.execution_count,
60
+ "total_time_ms": round(self.total_time_ms, 2),
61
+ "avg_time_ms": round(self.avg_time_ms, 2),
62
+ "min_time_ms": round(self.min_time_ms, 2),
63
+ "max_time_ms": round(self.max_time_ms, 2),
64
+ "p50_ms": round(self.get_percentile(50), 2),
65
+ "p95_ms": round(self.get_percentile(95), 2),
66
+ "p99_ms": round(self.get_percentile(99), 2),
67
+ }
68
+
69
+
70
+ @dataclass
71
+ class QueryPlan:
72
+ """PostgreSQL query execution plan"""
73
+
74
+ query: str
75
+ plan: Dict[str, Any]
76
+ total_cost: float
77
+ execution_time_ms: Optional[float] = None
78
+
79
+ def get_warnings(self) -> List[str]:
80
+ """Get performance warnings from query plan"""
81
+ warnings = []
82
+
83
+ # Check for sequential scans
84
+ if self._has_sequential_scan(self.plan):
85
+ warnings.append("Sequential scan detected - consider adding index")
86
+
87
+ # Check for nested loops with large datasets
88
+ if self._has_inefficient_nested_loop(self.plan):
89
+ warnings.append("Inefficient nested loop - consider optimizing join")
90
+
91
+ # Check for high cost
92
+ if self.total_cost > 10000:
93
+ warnings.append(f"High query cost ({self.total_cost:.0f}) - consider optimization")
94
+
95
+ return warnings
96
+
97
+ def _has_sequential_scan(self, node: Dict[str, Any]) -> bool:
98
+ """Check if plan has sequential scan"""
99
+ if node.get("Node Type") == "Seq Scan":
100
+ return True
101
+ for child in node.get("Plans", []):
102
+ if self._has_sequential_scan(child):
103
+ return True
104
+ return False
105
+
106
+ def _has_inefficient_nested_loop(self, node: Dict[str, Any]) -> bool:
107
+ """Check if plan has inefficient nested loop"""
108
+ if node.get("Node Type") == "Nested Loop":
109
+ # Check if estimated rows is large
110
+ if node.get("Plan Rows", 0) > 1000:
111
+ return True
112
+ for child in node.get("Plans", []):
113
+ if self._has_inefficient_nested_loop(child):
114
+ return True
115
+ return False
116
+
117
+
118
+ class PerformanceMonitor:
119
+ """
120
+ Monitor query performance and provide optimization suggestions
121
+
122
+ Tracks query execution times, analyzes query plans, and provides
123
+ recommendations for improving query performance.
124
+
125
+ Example:
126
+ ```python
127
+ monitor = PerformanceMonitor()
128
+
129
+ # Track query execution
130
+ async with monitor.track_query("get_entity", "SELECT * FROM entities WHERE id = $1"):
131
+ result = await conn.fetch(query, entity_id)
132
+
133
+ # Get performance report
134
+ report = monitor.get_performance_report()
135
+ print(report["slow_queries"])
136
+ ```
137
+ """
138
+
139
+ def __init__(
140
+ self,
141
+ enabled: bool = True,
142
+ slow_query_threshold_ms: float = 100.0,
143
+ log_slow_queries: bool = True,
144
+ ):
145
+ """
146
+ Initialize performance monitor
147
+
148
+ Args:
149
+ enabled: Enable/disable monitoring
150
+ slow_query_threshold_ms: Threshold for slow query logging (ms)
151
+ log_slow_queries: Log slow queries to logger
152
+ """
153
+ self.enabled = enabled
154
+ self.slow_query_threshold_ms = slow_query_threshold_ms
155
+ self.log_slow_queries = log_slow_queries
156
+
157
+ self.query_stats: Dict[str, QueryStats] = {}
158
+ self.slow_queries: List[Dict[str, Any]] = []
159
+ self._lock = None
160
+
161
+ async def initialize(self) -> None:
162
+ """Initialize monitor (create locks, etc.)"""
163
+ import asyncio
164
+
165
+ self._lock = asyncio.Lock()
166
+
167
+ def track_query(self, query_type: str, query_text: str):
168
+ """
169
+ Context manager for tracking query execution
170
+
171
+ Args:
172
+ query_type: Type of query (e.g., "get_entity", "find_paths")
173
+ query_text: SQL query text
174
+
175
+ Returns:
176
+ Context manager
177
+
178
+ Example:
179
+ ```python
180
+ async with monitor.track_query("get_entity", query):
181
+ result = await conn.fetch(query)
182
+ ```
183
+ """
184
+ return QueryTracker(self, query_type, query_text)
185
+
186
+ async def record_query(
187
+ self,
188
+ query_type: str,
189
+ query_text: str,
190
+ duration_ms: float,
191
+ row_count: Optional[int] = None,
192
+ ) -> None:
193
+ """
194
+ Record a query execution
195
+
196
+ Args:
197
+ query_type: Type of query
198
+ query_text: SQL query text
199
+ duration_ms: Execution time in milliseconds
200
+ row_count: Number of rows returned/affected
201
+ """
202
+ if not self.enabled:
203
+ return
204
+
205
+ # Update stats
206
+ key = f"{query_type}:{query_text[:50]}"
207
+ if key not in self.query_stats:
208
+ self.query_stats[key] = QueryStats(query_type, query_text)
209
+
210
+ self.query_stats[key].add_execution(duration_ms)
211
+
212
+ # Check if slow query
213
+ if duration_ms >= self.slow_query_threshold_ms:
214
+ slow_query = {
215
+ "query_type": query_type,
216
+ "query_text": query_text[:200],
217
+ "duration_ms": duration_ms,
218
+ "row_count": row_count,
219
+ "timestamp": time.time(),
220
+ }
221
+ self.slow_queries.append(slow_query)
222
+
223
+ # Keep only last 100 slow queries
224
+ if len(self.slow_queries) > 100:
225
+ self.slow_queries.pop(0)
226
+
227
+ if self.log_slow_queries:
228
+ logger.warning(
229
+ f"Slow query detected: {query_type} took {duration_ms:.2f}ms "
230
+ f"(rows: {row_count})"
231
+ )
232
+
233
+ async def analyze_query_plan(
234
+ self, conn: asyncpg.Connection, query: str, params: tuple = ()
235
+ ) -> QueryPlan:
236
+ """
237
+ Analyze query execution plan
238
+
239
+ Args:
240
+ conn: Database connection
241
+ query: SQL query to analyze
242
+ params: Query parameters
243
+
244
+ Returns:
245
+ QueryPlan with analysis results
246
+
247
+ Example:
248
+ ```python
249
+ plan = await monitor.analyze_query_plan(
250
+ conn,
251
+ "SELECT * FROM entities WHERE entity_type = $1",
252
+ ("Person",)
253
+ )
254
+ print(plan.get_warnings())
255
+ ```
256
+ """
257
+ if not self.enabled:
258
+ raise RuntimeError("Performance monitoring is disabled")
259
+
260
+ # Get query plan with EXPLAIN (ANALYZE, FORMAT JSON)
261
+ explain_query = f"EXPLAIN (ANALYZE, FORMAT JSON) {query}"
262
+
263
+ try:
264
+ start = time.time()
265
+ result = await conn.fetch(explain_query, *params)
266
+ duration_ms = (time.time() - start) * 1000
267
+
268
+ if not result:
269
+ raise ValueError("No query plan returned")
270
+
271
+ # Parse JSON plan
272
+ plan_json = result[0]["QUERY PLAN"]
273
+ if isinstance(plan_json, str):
274
+ import json
275
+
276
+ plan_json = json.loads(plan_json)
277
+
278
+ # Extract plan details
279
+ plan_data = plan_json[0] if isinstance(plan_json, list) else plan_json
280
+ total_cost = plan_data.get("Plan", {}).get("Total Cost", 0)
281
+
282
+ return QueryPlan(
283
+ query=query,
284
+ plan=plan_data,
285
+ total_cost=total_cost,
286
+ execution_time_ms=duration_ms,
287
+ )
288
+
289
+ except Exception as e:
290
+ logger.error(f"Failed to analyze query plan: {e}")
291
+ raise
292
+
293
+ def get_performance_report(self) -> Dict[str, Any]:
294
+ """
295
+ Get comprehensive performance report
296
+
297
+ Returns:
298
+ Dictionary with performance statistics
299
+
300
+ Example:
301
+ ```python
302
+ report = monitor.get_performance_report()
303
+ print(f"Total queries: {report['total_queries']}")
304
+ print(f"Slow queries: {len(report['slow_queries'])}")
305
+ ```
306
+ """
307
+ if not self.enabled:
308
+ return {"enabled": False}
309
+
310
+ # Calculate aggregate stats
311
+ total_queries = sum(stats.execution_count for stats in self.query_stats.values())
312
+ total_time_ms = sum(stats.total_time_ms for stats in self.query_stats.values())
313
+
314
+ # Get top slow queries
315
+ sorted_stats = sorted(
316
+ self.query_stats.values(),
317
+ key=lambda s: s.avg_time_ms,
318
+ reverse=True,
319
+ )
320
+ top_slow = [stats.to_dict() for stats in sorted_stats[:10]]
321
+
322
+ # Get most frequent queries
323
+ sorted_by_count = sorted(
324
+ self.query_stats.values(),
325
+ key=lambda s: s.execution_count,
326
+ reverse=True,
327
+ )
328
+ most_frequent = [stats.to_dict() for stats in sorted_by_count[:10]]
329
+
330
+ return {
331
+ "enabled": True,
332
+ "total_queries": total_queries,
333
+ "total_time_ms": round(total_time_ms, 2),
334
+ "avg_query_time_ms": (
335
+ round(total_time_ms / total_queries, 2) if total_queries > 0 else 0
336
+ ),
337
+ "unique_queries": len(self.query_stats),
338
+ "slow_query_count": len(self.slow_queries),
339
+ "slow_query_threshold_ms": self.slow_query_threshold_ms,
340
+ "top_slow_queries": top_slow,
341
+ "most_frequent_queries": most_frequent,
342
+ "recent_slow_queries": self.slow_queries[-10:],
343
+ }
344
+
345
+ def get_query_stats(self, query_type: Optional[str] = None) -> List[Dict[str, Any]]:
346
+ """
347
+ Get statistics for specific query type or all queries
348
+
349
+ Args:
350
+ query_type: Filter by query type (None for all)
351
+
352
+ Returns:
353
+ List of query statistics
354
+ """
355
+ if not self.enabled:
356
+ return []
357
+
358
+ stats = self.query_stats.values()
359
+ if query_type:
360
+ stats = [s for s in stats if s.query_type == query_type]
361
+
362
+ return [s.to_dict() for s in stats]
363
+
364
+ def reset_stats(self) -> None:
365
+ """Reset all performance statistics"""
366
+ self.query_stats.clear()
367
+ self.slow_queries.clear()
368
+ logger.info("Performance statistics reset")
369
+
370
+
371
+ class QueryTracker:
372
+ """Context manager for tracking query execution time"""
373
+
374
+ def __init__(self, monitor: PerformanceMonitor, query_type: str, query_text: str):
375
+ """
376
+ Initialize query tracker
377
+
378
+ Args:
379
+ monitor: Performance monitor instance
380
+ query_type: Type of query
381
+ query_text: SQL query text
382
+ """
383
+ self.monitor = monitor
384
+ self.query_type = query_type
385
+ self.query_text = query_text
386
+ self.start_time = 0.0
387
+
388
+ async def __aenter__(self):
389
+ """Start timing"""
390
+ self.start_time = time.time()
391
+ return self
392
+
393
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
394
+ """End timing and record"""
395
+ if not self.monitor.enabled:
396
+ return
397
+
398
+ duration_ms = (time.time() - self.start_time) * 1000
399
+ await self.monitor.record_query(self.query_type, self.query_text, duration_ms)
400
+
401
+
402
+ class PreparedStatementCache:
403
+ """
404
+ Cache for prepared statements
405
+
406
+ Provides caching of prepared statements to reduce query planning overhead.
407
+
408
+ Example:
409
+ ```python
410
+ cache = PreparedStatementCache(max_size=100)
411
+
412
+ # Get or create prepared statement
413
+ stmt = await cache.get_or_prepare(
414
+ conn,
415
+ "get_entity",
416
+ "SELECT * FROM entities WHERE id = $1"
417
+ )
418
+ result = await conn.fetch(stmt, entity_id)
419
+ ```
420
+ """
421
+
422
+ def __init__(self, max_size: int = 100):
423
+ """
424
+ Initialize prepared statement cache
425
+
426
+ Args:
427
+ max_size: Maximum number of cached statements
428
+ """
429
+ self.max_size = max_size
430
+ self.cache: Dict[str, Any] = {}
431
+ self.access_count: Dict[str, int] = defaultdict(int)
432
+
433
+ async def get_or_prepare(self, conn: asyncpg.Connection, name: str, query: str) -> Any:
434
+ """
435
+ Get cached prepared statement or create new one
436
+
437
+ Args:
438
+ conn: Database connection
439
+ name: Statement name (unique identifier)
440
+ query: SQL query text
441
+
442
+ Returns:
443
+ Prepared statement
444
+ """
445
+ # Check cache
446
+ if name in self.cache:
447
+ self.access_count[name] += 1
448
+ return self.cache[name]
449
+
450
+ # Evict least used if cache full
451
+ if len(self.cache) >= self.max_size:
452
+ least_used = min(self.access_count.items(), key=lambda x: x[1])[0]
453
+ del self.cache[least_used]
454
+ del self.access_count[least_used]
455
+
456
+ # Prepare statement
457
+ stmt = await conn.prepare(query)
458
+ self.cache[name] = stmt
459
+ self.access_count[name] = 1
460
+
461
+ return stmt
462
+
463
+ def clear(self) -> None:
464
+ """Clear all cached statements"""
465
+ self.cache.clear()
466
+ self.access_count.clear()