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,483 @@
1
+ """
2
+ PostgreSQL Index Optimization
3
+
4
+ Provides index analysis, recommendations, and automated index creation
5
+ based on query patterns and workload analysis.
6
+ """
7
+
8
+ import logging
9
+ import asyncpg
10
+ from typing import List, Dict, Any, Optional
11
+ from dataclasses import dataclass
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ @dataclass
17
+ class IndexRecommendation:
18
+ """Index creation recommendation"""
19
+
20
+ table_name: str
21
+ columns: List[str]
22
+ index_type: str # btree, gin, gist, ivfflat
23
+ reason: str
24
+ estimated_benefit: str # "high", "medium", "low"
25
+ create_sql: str
26
+
27
+ def to_dict(self) -> Dict[str, Any]:
28
+ """Convert to dictionary"""
29
+ return {
30
+ "table": self.table_name,
31
+ "columns": self.columns,
32
+ "type": self.index_type,
33
+ "reason": self.reason,
34
+ "benefit": self.estimated_benefit,
35
+ "sql": self.create_sql,
36
+ }
37
+
38
+
39
+ @dataclass
40
+ class IndexInfo:
41
+ """Information about an existing index"""
42
+
43
+ index_name: str
44
+ table_name: str
45
+ columns: List[str]
46
+ index_type: str
47
+ is_unique: bool
48
+ size_bytes: int
49
+ usage_count: int = 0
50
+
51
+ def to_dict(self) -> Dict[str, Any]:
52
+ """Convert to dictionary"""
53
+ return {
54
+ "name": self.index_name,
55
+ "table": self.table_name,
56
+ "columns": self.columns,
57
+ "type": self.index_type,
58
+ "unique": self.is_unique,
59
+ "size_mb": round(self.size_bytes / (1024 * 1024), 2),
60
+ "usage_count": self.usage_count,
61
+ }
62
+
63
+
64
+ class IndexOptimizer:
65
+ """
66
+ Analyze and optimize PostgreSQL indexes for graph storage
67
+
68
+ Provides:
69
+ - Index analysis and usage statistics
70
+ - Query pattern analysis
71
+ - Index recommendations
72
+ - Automated index creation
73
+
74
+ Example:
75
+ ```python
76
+ optimizer = IndexOptimizer(conn)
77
+
78
+ # Analyze existing indexes
79
+ indexes = await optimizer.analyze_indexes()
80
+
81
+ # Get recommendations
82
+ recommendations = await optimizer.get_recommendations()
83
+ for rec in recommendations:
84
+ print(rec.create_sql)
85
+
86
+ # Apply recommendations
87
+ await optimizer.apply_recommendations(recommendations)
88
+ ```
89
+ """
90
+
91
+ def __init__(self, pool: asyncpg.Pool):
92
+ """
93
+ Initialize index optimizer
94
+
95
+ Args:
96
+ pool: PostgreSQL connection pool
97
+ """
98
+ self.pool = pool
99
+
100
+ async def analyze_indexes(self) -> List[IndexInfo]:
101
+ """
102
+ Analyze all indexes on graph tables
103
+
104
+ Returns:
105
+ List of IndexInfo with usage statistics
106
+ """
107
+ async with self.pool.acquire() as conn:
108
+ # Query index information
109
+ query = """
110
+ SELECT
111
+ i.indexrelid::regclass AS index_name,
112
+ i.indrelid::regclass AS table_name,
113
+ array_agg(a.attname ORDER BY x.n) AS columns,
114
+ am.amname AS index_type,
115
+ i.indisunique AS is_unique,
116
+ pg_relation_size(i.indexrelid) AS size_bytes,
117
+ COALESCE(s.idx_scan, 0) AS usage_count
118
+ FROM
119
+ pg_index i
120
+ JOIN pg_class c ON c.oid = i.indrelid
121
+ JOIN pg_am am ON am.oid = c.relam
122
+ JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
123
+ CROSS JOIN LATERAL unnest(i.indkey) WITH ORDINALITY AS x(attnum, n)
124
+ LEFT JOIN pg_stat_user_indexes s ON s.indexrelid = i.indexrelid
125
+ WHERE
126
+ c.relname IN ('graph_entities', 'graph_relations')
127
+ AND a.attnum = x.attnum
128
+ GROUP BY
129
+ i.indexrelid, i.indrelid, am.amname, i.indisunique, s.idx_scan
130
+ ORDER BY
131
+ table_name, index_name
132
+ """
133
+
134
+ rows = await conn.fetch(query)
135
+
136
+ indexes = []
137
+ for row in rows:
138
+ indexes.append(
139
+ IndexInfo(
140
+ index_name=str(row["index_name"]),
141
+ table_name=str(row["table_name"]),
142
+ columns=list(row["columns"]),
143
+ index_type=row["index_type"],
144
+ is_unique=row["is_unique"],
145
+ size_bytes=row["size_bytes"],
146
+ usage_count=row["usage_count"],
147
+ )
148
+ )
149
+
150
+ return indexes
151
+
152
+ async def get_unused_indexes(self, min_usage_threshold: int = 10) -> List[IndexInfo]:
153
+ """
154
+ Find indexes that are rarely or never used
155
+
156
+ Args:
157
+ min_usage_threshold: Minimum usage count to consider index used
158
+
159
+ Returns:
160
+ List of unused indexes
161
+ """
162
+ indexes = await self.analyze_indexes()
163
+ unused = [idx for idx in indexes if idx.usage_count < min_usage_threshold]
164
+ return unused
165
+
166
+ async def get_missing_index_recommendations(
167
+ self,
168
+ ) -> List[IndexRecommendation]:
169
+ """
170
+ Analyze query patterns and recommend missing indexes
171
+
172
+ Returns:
173
+ List of index recommendations
174
+ """
175
+ recommendations = []
176
+
177
+ async with self.pool.acquire() as conn:
178
+ # Check for missing indexes based on common query patterns
179
+
180
+ # 1. Check if composite index on (entity_type, properties) would be beneficial
181
+ # This is useful for queries filtering by type and JSONB properties
182
+ entity_type_props_exists = await self._index_exists(
183
+ conn, "graph_entities", ["entity_type", "properties"]
184
+ )
185
+ if not entity_type_props_exists:
186
+ recommendations.append(
187
+ IndexRecommendation(
188
+ table_name="graph_entities",
189
+ columns=["entity_type", "properties"],
190
+ index_type="gin",
191
+ reason="Queries often filter by entity_type and search properties",
192
+ estimated_benefit="medium",
193
+ create_sql=(
194
+ "CREATE INDEX CONCURRENTLY idx_graph_entities_type_props "
195
+ "ON graph_entities (entity_type, properties jsonb_path_ops)"
196
+ ),
197
+ )
198
+ )
199
+
200
+ # 2. Check for relation composite indexes
201
+ source_type_exists = await self._index_exists(
202
+ conn, "graph_relations", ["source_id", "relation_type"]
203
+ )
204
+ if not source_type_exists:
205
+ recommendations.append(
206
+ IndexRecommendation(
207
+ table_name="graph_relations",
208
+ columns=["source_id", "relation_type"],
209
+ index_type="btree",
210
+ reason="Queries often filter relations by source and type",
211
+ estimated_benefit="high",
212
+ create_sql=(
213
+ "CREATE INDEX CONCURRENTLY idx_graph_relations_source_type "
214
+ "ON graph_relations (source_id, relation_type)"
215
+ ),
216
+ )
217
+ )
218
+
219
+ target_type_exists = await self._index_exists(
220
+ conn, "graph_relations", ["target_id", "relation_type"]
221
+ )
222
+ if not target_type_exists:
223
+ recommendations.append(
224
+ IndexRecommendation(
225
+ table_name="graph_relations",
226
+ columns=["target_id", "relation_type"],
227
+ index_type="btree",
228
+ reason="Queries often filter incoming relations by target and type",
229
+ estimated_benefit="high",
230
+ create_sql=(
231
+ "CREATE INDEX CONCURRENTLY idx_graph_relations_target_type "
232
+ "ON graph_relations (target_id, relation_type)"
233
+ ),
234
+ )
235
+ )
236
+
237
+ # 3. Check for weight index (useful for weighted path finding)
238
+ weight_exists = await self._index_exists(conn, "graph_relations", ["weight"])
239
+ if not weight_exists:
240
+ recommendations.append(
241
+ IndexRecommendation(
242
+ table_name="graph_relations",
243
+ columns=["weight"],
244
+ index_type="btree",
245
+ reason="Weight-based path finding and sorting",
246
+ estimated_benefit="low",
247
+ create_sql=(
248
+ "CREATE INDEX CONCURRENTLY idx_graph_relations_weight "
249
+ "ON graph_relations (weight)"
250
+ ),
251
+ )
252
+ )
253
+
254
+ # 4. Check for timestamp indexes (useful for temporal queries)
255
+ entity_created_exists = await self._index_exists(conn, "graph_entities", ["created_at"])
256
+ if not entity_created_exists:
257
+ recommendations.append(
258
+ IndexRecommendation(
259
+ table_name="graph_entities",
260
+ columns=["created_at"],
261
+ index_type="btree",
262
+ reason="Temporal queries (recently created entities)",
263
+ estimated_benefit="low",
264
+ create_sql=(
265
+ "CREATE INDEX CONCURRENTLY idx_graph_entities_created "
266
+ "ON graph_entities (created_at)"
267
+ ),
268
+ )
269
+ )
270
+
271
+ return recommendations
272
+
273
+ async def _index_exists(
274
+ self, conn: asyncpg.Connection, table_name: str, columns: List[str]
275
+ ) -> bool:
276
+ """Check if an index exists on specified columns"""
277
+ query = """
278
+ SELECT EXISTS (
279
+ SELECT 1
280
+ FROM pg_index i
281
+ JOIN pg_class c ON c.oid = i.indrelid
282
+ JOIN pg_attribute a ON a.attrelid = i.indrelid
283
+ WHERE c.relname = $1
284
+ AND array_agg(a.attname) @> $2::text[]
285
+ GROUP BY i.indexrelid
286
+ )
287
+ """
288
+ result = await conn.fetchval(query, table_name, columns)
289
+ return result or False
290
+
291
+ async def apply_recommendations(
292
+ self, recommendations: List[IndexRecommendation], dry_run: bool = False
293
+ ) -> Dict[str, Any]:
294
+ """
295
+ Apply index recommendations
296
+
297
+ Args:
298
+ recommendations: List of recommendations to apply
299
+ dry_run: If True, only show what would be done
300
+
301
+ Returns:
302
+ Dictionary with results
303
+ """
304
+ results: Dict[str, List[IndexRecommendation]] = {
305
+ "applied": [],
306
+ "failed": [],
307
+ "skipped": [],
308
+ }
309
+
310
+ for rec in recommendations:
311
+ try:
312
+ if dry_run:
313
+ logger.info(f"[DRY RUN] Would create index: {rec.create_sql}")
314
+ results["skipped"].append(
315
+ {"recommendation": rec.to_dict(), "reason": "dry_run"}
316
+ )
317
+ continue
318
+
319
+ # Create index
320
+ async with self.pool.acquire() as conn:
321
+ logger.info(f"Creating index: {rec.create_sql}")
322
+ await conn.execute(rec.create_sql)
323
+ results["applied"].append(rec.to_dict())
324
+ logger.info(
325
+ f"Successfully created index on {rec.table_name}({', '.join(rec.columns)})"
326
+ )
327
+
328
+ except Exception as e:
329
+ logger.error(f"Failed to create index: {e}")
330
+ results["failed"].append({"recommendation": rec.to_dict(), "error": str(e)})
331
+
332
+ return results
333
+
334
+ async def analyze_table_stats(self) -> Dict[str, Any]:
335
+ """
336
+ Get table statistics for optimization decisions
337
+
338
+ Returns:
339
+ Dictionary with table statistics
340
+ """
341
+ async with self.pool.acquire() as conn:
342
+ stats_query = """
343
+ SELECT
344
+ schemaname,
345
+ tablename,
346
+ pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size,
347
+ pg_stat_get_live_tuples(schemaname||'.'||tablename::regclass) AS row_count,
348
+ pg_stat_get_dead_tuples(schemaname||'.'||tablename::regclass) AS dead_tuples,
349
+ last_vacuum,
350
+ last_autovacuum,
351
+ last_analyze,
352
+ last_autoanalyze
353
+ FROM pg_stat_user_tables
354
+ WHERE tablename IN ('graph_entities', 'graph_relations')
355
+ """
356
+
357
+ rows = await conn.fetch(stats_query)
358
+
359
+ stats = {}
360
+ for row in rows:
361
+ stats[row["tablename"]] = {
362
+ "total_size": row["total_size"],
363
+ "row_count": row["row_count"],
364
+ "dead_tuples": row["dead_tuples"],
365
+ "last_vacuum": (row["last_vacuum"].isoformat() if row["last_vacuum"] else None),
366
+ "last_autovacuum": (
367
+ row["last_autovacuum"].isoformat() if row["last_autovacuum"] else None
368
+ ),
369
+ "last_analyze": (
370
+ row["last_analyze"].isoformat() if row["last_analyze"] else None
371
+ ),
372
+ "last_autoanalyze": (
373
+ row["last_autoanalyze"].isoformat() if row["last_autoanalyze"] else None
374
+ ),
375
+ }
376
+
377
+ return stats
378
+
379
+ async def vacuum_analyze(self, table_name: Optional[str] = None) -> None:
380
+ """
381
+ Run VACUUM ANALYZE on graph tables
382
+
383
+ Args:
384
+ table_name: Specific table to analyze (None for all graph tables)
385
+ """
386
+ tables = [table_name] if table_name else ["graph_entities", "graph_relations"]
387
+
388
+ async with self.pool.acquire() as conn:
389
+ for table in tables:
390
+ logger.info(f"Running VACUUM ANALYZE on {table}")
391
+ await conn.execute(f"VACUUM ANALYZE {table}")
392
+ logger.info(f"Completed VACUUM ANALYZE on {table}")
393
+
394
+ async def get_optimization_report(self) -> Dict[str, Any]:
395
+ """
396
+ Generate comprehensive optimization report
397
+
398
+ Returns:
399
+ Dictionary with optimization recommendations and statistics
400
+ """
401
+ # Get all information
402
+ indexes = await self.analyze_indexes()
403
+ unused_indexes = await self.get_unused_indexes()
404
+ recommendations = await self.get_missing_index_recommendations()
405
+ table_stats = await self.analyze_table_stats()
406
+
407
+ # Calculate totals
408
+ total_index_size = sum(idx.size_bytes for idx in indexes)
409
+ unused_index_size = sum(idx.size_bytes for idx in unused_indexes)
410
+
411
+ report = {
412
+ "indexes": {
413
+ "total_count": len(indexes),
414
+ "total_size_mb": round(total_index_size / (1024 * 1024), 2),
415
+ "unused_count": len(unused_indexes),
416
+ "unused_size_mb": round(unused_index_size / (1024 * 1024), 2),
417
+ "details": [idx.to_dict() for idx in indexes],
418
+ },
419
+ "unused_indexes": [idx.to_dict() for idx in unused_indexes],
420
+ "recommendations": [rec.to_dict() for rec in recommendations],
421
+ "table_stats": table_stats,
422
+ "summary": {
423
+ "total_recommendations": len(recommendations),
424
+ "high_priority": len([r for r in recommendations if r.estimated_benefit == "high"]),
425
+ "medium_priority": len(
426
+ [r for r in recommendations if r.estimated_benefit == "medium"]
427
+ ),
428
+ "low_priority": len([r for r in recommendations if r.estimated_benefit == "low"]),
429
+ "potential_space_savings_mb": round(unused_index_size / (1024 * 1024), 2),
430
+ },
431
+ }
432
+
433
+ return report
434
+
435
+
436
+ # Pre-defined optimal index set for graph storage
437
+ OPTIMAL_INDEXES = [
438
+ {
439
+ "name": "idx_graph_entities_type",
440
+ "table": "graph_entities",
441
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_entities_type ON graph_entities(entity_type)",
442
+ },
443
+ {
444
+ "name": "idx_graph_entities_properties",
445
+ "table": "graph_entities",
446
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_entities_properties ON graph_entities USING GIN(properties)",
447
+ },
448
+ {
449
+ "name": "idx_graph_relations_type",
450
+ "table": "graph_relations",
451
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_relations_type ON graph_relations(relation_type)",
452
+ },
453
+ {
454
+ "name": "idx_graph_relations_source",
455
+ "table": "graph_relations",
456
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_relations_source ON graph_relations(source_id)",
457
+ },
458
+ {
459
+ "name": "idx_graph_relations_target",
460
+ "table": "graph_relations",
461
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_relations_target ON graph_relations(target_id)",
462
+ },
463
+ {
464
+ "name": "idx_graph_relations_source_target",
465
+ "table": "graph_relations",
466
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_relations_source_target ON graph_relations(source_id, target_id)",
467
+ },
468
+ {
469
+ "name": "idx_graph_relations_properties",
470
+ "table": "graph_relations",
471
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_relations_properties ON graph_relations USING GIN(properties)",
472
+ },
473
+ {
474
+ "name": "idx_graph_relations_source_type",
475
+ "table": "graph_relations",
476
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_relations_source_type ON graph_relations(source_id, relation_type)",
477
+ },
478
+ {
479
+ "name": "idx_graph_relations_target_type",
480
+ "table": "graph_relations",
481
+ "sql": "CREATE INDEX IF NOT EXISTS idx_graph_relations_target_type ON graph_relations(target_id, relation_type)",
482
+ },
483
+ ]