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,333 @@
1
+ import asyncpg
2
+ import json
3
+ import logging
4
+ from datetime import datetime
5
+ from typing import Dict, List, Any, Optional
6
+ from aiecs.domain.execution.model import TaskStatus, TaskStepResult
7
+ from aiecs.config.config import get_settings
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class DatabaseManager:
13
+ """
14
+ Specialized handler for database connections, operations, and task history management
15
+ """
16
+
17
+ def __init__(self, db_config: Optional[Dict[str, Any]] = None):
18
+ if db_config is None:
19
+ settings = get_settings()
20
+ self.db_config = settings.database_config
21
+ else:
22
+ self.db_config = db_config
23
+ self.connection_pool = None
24
+ self._initialized = False
25
+
26
+ async def connect(self, min_size: int = 10, max_size: int = 20):
27
+ """Connect to database and initialize connection pool"""
28
+ if self._initialized:
29
+ logger.info("Database already connected")
30
+ return
31
+
32
+ try:
33
+ await self.init_connection_pool(min_size, max_size)
34
+ await self.init_database_schema()
35
+ self._initialized = True
36
+ logger.info("Database connection and schema initialization completed")
37
+ except Exception as e:
38
+ logger.error(f"Failed to connect to database: {e}")
39
+ raise
40
+
41
+ async def init_connection_pool(self, min_size: int = 10, max_size: int = 20):
42
+ """Initialize database connection pool"""
43
+ try:
44
+ self.connection_pool = await asyncpg.create_pool(
45
+ **self.db_config, min_size=min_size, max_size=max_size
46
+ )
47
+ logger.info("Database connection pool initialized successfully")
48
+ except Exception as e:
49
+ logger.error(f"Failed to initialize database connection pool: {e}")
50
+ raise
51
+
52
+ async def _get_connection(self):
53
+ """Get database connection"""
54
+ if self.connection_pool:
55
+ return self.connection_pool.acquire()
56
+ else:
57
+ return asyncpg.connect(**self.db_config)
58
+
59
+ async def init_database_schema(self):
60
+ """Initialize database table structure"""
61
+ try:
62
+ if self.connection_pool:
63
+ async with self.connection_pool.acquire() as conn:
64
+ await self._create_tables(conn)
65
+ else:
66
+ conn = await asyncpg.connect(**self.db_config)
67
+ try:
68
+ await self._create_tables(conn)
69
+ finally:
70
+ await conn.close()
71
+
72
+ self._initialized = True
73
+ logger.info("Database schema initialized successfully")
74
+ return True
75
+ except Exception as e:
76
+ logger.error(f"Database initialization error: {e}")
77
+ return False
78
+
79
+ async def _create_tables(self, conn):
80
+ """Create database tables"""
81
+ await conn.execute(
82
+ """
83
+ CREATE TABLE IF NOT EXISTS task_history (
84
+ id SERIAL PRIMARY KEY,
85
+ user_id TEXT NOT NULL,
86
+ task_id TEXT NOT NULL,
87
+ step INTEGER NOT NULL,
88
+ result JSONB NOT NULL,
89
+ timestamp TIMESTAMP NOT NULL,
90
+ status TEXT NOT NULL DEFAULT 'pending'
91
+ );
92
+ CREATE INDEX IF NOT EXISTS idx_task_history_user_id ON task_history (user_id);
93
+ CREATE INDEX IF NOT EXISTS idx_task_history_task_id ON task_history (task_id);
94
+ CREATE INDEX IF NOT EXISTS idx_task_history_status ON task_history (status);
95
+ CREATE INDEX IF NOT EXISTS idx_task_history_timestamp ON task_history (timestamp);
96
+ """
97
+ )
98
+
99
+ async def save_task_history(
100
+ self,
101
+ user_id: str,
102
+ task_id: str,
103
+ step: int,
104
+ step_result: TaskStepResult,
105
+ ):
106
+ """Save task execution history"""
107
+ if not self._initialized:
108
+ await self.init_database_schema()
109
+
110
+ try:
111
+ if self.connection_pool:
112
+ async with self.connection_pool.acquire() as conn:
113
+ await conn.execute(
114
+ "INSERT INTO task_history (user_id, task_id, step, result, timestamp, status) VALUES ($1, $2, $3, $4, $5, $6)",
115
+ user_id,
116
+ task_id,
117
+ step,
118
+ json.dumps(step_result.dict()),
119
+ datetime.now(),
120
+ step_result.status,
121
+ )
122
+ else:
123
+ conn = await asyncpg.connect(**self.db_config)
124
+ try:
125
+ await conn.execute(
126
+ "INSERT INTO task_history (user_id, task_id, step, result, timestamp, status) VALUES ($1, $2, $3, $4, $5, $6)",
127
+ user_id,
128
+ task_id,
129
+ step,
130
+ json.dumps(step_result.dict()),
131
+ datetime.now(),
132
+ step_result.status,
133
+ )
134
+ finally:
135
+ await conn.close()
136
+
137
+ logger.debug(f"Saved task history for user {user_id}, task {task_id}, step {step}")
138
+ return True
139
+ except Exception as e:
140
+ logger.error(f"Database error saving task history: {e}")
141
+ raise Exception(f"Database error: {e}")
142
+
143
+ async def load_task_history(self, user_id: str, task_id: str) -> List[Dict]:
144
+ """Load task execution history"""
145
+ if not self._initialized:
146
+ await self.init_database_schema()
147
+
148
+ try:
149
+ if self.connection_pool:
150
+ async with self.connection_pool.acquire() as conn:
151
+ records = await conn.fetch(
152
+ "SELECT step, result, timestamp, status FROM task_history WHERE user_id = $1 AND task_id = $2 ORDER BY step ASC",
153
+ user_id,
154
+ task_id,
155
+ )
156
+ else:
157
+ conn = await asyncpg.connect(**self.db_config)
158
+ try:
159
+ records = await conn.fetch(
160
+ "SELECT step, result, timestamp, status FROM task_history WHERE user_id = $1 AND task_id = $2 ORDER BY step ASC",
161
+ user_id,
162
+ task_id,
163
+ )
164
+ finally:
165
+ await conn.close()
166
+
167
+ return [
168
+ {
169
+ "step": r["step"],
170
+ "result": json.loads(r["result"]),
171
+ "timestamp": r["timestamp"].isoformat(),
172
+ "status": r["status"],
173
+ }
174
+ for r in records
175
+ ]
176
+ except Exception as e:
177
+ logger.error(f"Database error loading task history: {e}")
178
+ raise Exception(f"Database error: {e}")
179
+
180
+ async def mark_task_as_cancelled(self, user_id: str, task_id: str):
181
+ """Mark task as cancelled"""
182
+ if not self._initialized:
183
+ await self.init_database_schema()
184
+
185
+ try:
186
+ if self.connection_pool:
187
+ async with self.connection_pool.acquire() as conn:
188
+ await conn.execute(
189
+ "UPDATE task_history SET status = $1 WHERE user_id = $2 AND task_id = $3",
190
+ TaskStatus.CANCELLED.value,
191
+ user_id,
192
+ task_id,
193
+ )
194
+ else:
195
+ conn = await asyncpg.connect(**self.db_config)
196
+ try:
197
+ await conn.execute(
198
+ "UPDATE task_history SET status = $1 WHERE user_id = $2 AND task_id = $3",
199
+ TaskStatus.CANCELLED.value,
200
+ user_id,
201
+ task_id,
202
+ )
203
+ finally:
204
+ await conn.close()
205
+
206
+ logger.info(f"Marked task {task_id} as cancelled for user {user_id}")
207
+ return True
208
+ except Exception as e:
209
+ logger.error(f"Database error marking task as cancelled: {e}")
210
+ raise Exception(f"Database error: {e}")
211
+
212
+ async def check_task_status(self, user_id: str, task_id: str) -> TaskStatus:
213
+ """Check task status"""
214
+ if not self._initialized:
215
+ await self.init_database_schema()
216
+
217
+ try:
218
+ if self.connection_pool:
219
+ async with self.connection_pool.acquire() as conn:
220
+ record = await conn.fetchrow(
221
+ "SELECT status FROM task_history WHERE user_id = $1 AND task_id = $2 ORDER BY step DESC LIMIT 1",
222
+ user_id,
223
+ task_id,
224
+ )
225
+ else:
226
+ conn = await asyncpg.connect(**self.db_config)
227
+ try:
228
+ record = await conn.fetchrow(
229
+ "SELECT status FROM task_history WHERE user_id = $1 AND task_id = $2 ORDER BY step DESC LIMIT 1",
230
+ user_id,
231
+ task_id,
232
+ )
233
+ finally:
234
+ await conn.close()
235
+
236
+ return TaskStatus(record["status"]) if record else TaskStatus.PENDING
237
+ except Exception as e:
238
+ logger.error(f"Database error checking task status: {e}")
239
+ raise Exception(f"Database error: {e}")
240
+
241
+ async def get_user_tasks(self, user_id: str, limit: int = 100) -> List[Dict]:
242
+ """Get user task list"""
243
+ if not self._initialized:
244
+ await self.init_database_schema()
245
+
246
+ try:
247
+ if self.connection_pool:
248
+ async with self.connection_pool.acquire() as conn:
249
+ records = await conn.fetch(
250
+ """SELECT DISTINCT task_id,
251
+ MAX(timestamp) as last_updated,
252
+ (SELECT status FROM task_history th2
253
+ WHERE th2.user_id = $1 AND th2.task_id = th1.task_id
254
+ ORDER BY step DESC LIMIT 1) as status
255
+ FROM task_history th1
256
+ WHERE user_id = $1
257
+ GROUP BY task_id
258
+ ORDER BY last_updated DESC
259
+ LIMIT $2""",
260
+ user_id,
261
+ limit,
262
+ )
263
+ else:
264
+ conn = await asyncpg.connect(**self.db_config)
265
+ try:
266
+ records = await conn.fetch(
267
+ """SELECT DISTINCT task_id,
268
+ MAX(timestamp) as last_updated,
269
+ (SELECT status FROM task_history th2
270
+ WHERE th2.user_id = $1 AND th2.task_id = th1.task_id
271
+ ORDER BY step DESC LIMIT 1) as status
272
+ FROM task_history th1
273
+ WHERE user_id = $1
274
+ GROUP BY task_id
275
+ ORDER BY last_updated DESC
276
+ LIMIT $2""",
277
+ user_id,
278
+ limit,
279
+ )
280
+ finally:
281
+ await conn.close()
282
+
283
+ return [
284
+ {
285
+ "task_id": r["task_id"],
286
+ "last_updated": r["last_updated"].isoformat(),
287
+ "status": r["status"],
288
+ }
289
+ for r in records
290
+ ]
291
+ except Exception as e:
292
+ logger.error(f"Database error getting user tasks: {e}")
293
+ raise Exception(f"Database error: {e}")
294
+
295
+ async def cleanup_old_tasks(self, days_old: int = 30):
296
+ """Clean up old task records"""
297
+ if not self._initialized:
298
+ await self.init_database_schema()
299
+
300
+ try:
301
+ if self.connection_pool:
302
+ async with self.connection_pool.acquire() as conn:
303
+ result = await conn.execute(
304
+ "DELETE FROM task_history WHERE timestamp < NOW() - INTERVAL %s DAY",
305
+ days_old,
306
+ )
307
+ else:
308
+ conn = await asyncpg.connect(**self.db_config)
309
+ try:
310
+ result = await conn.execute(
311
+ "DELETE FROM task_history WHERE timestamp < NOW() - INTERVAL %s DAY",
312
+ days_old,
313
+ )
314
+ finally:
315
+ await conn.close()
316
+
317
+ logger.info(f"Cleaned up old task records: {result}")
318
+ return True
319
+ except Exception as e:
320
+ logger.error(f"Database error during cleanup: {e}")
321
+ return False
322
+
323
+ async def disconnect(self):
324
+ """Disconnect from database and close connection pool"""
325
+ await self.close()
326
+ self._initialized = False
327
+ logger.info("Database disconnected")
328
+
329
+ async def close(self):
330
+ """Close database connection pool"""
331
+ if self.connection_pool:
332
+ await self.connection_pool.close()
333
+ logger.info("Database connection pool closed")