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.
- aiecs/__init__.py +72 -0
- aiecs/__main__.py +41 -0
- aiecs/aiecs_client.py +469 -0
- aiecs/application/__init__.py +10 -0
- aiecs/application/executors/__init__.py +10 -0
- aiecs/application/executors/operation_executor.py +363 -0
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +100 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
- aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
- aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
- aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
- aiecs/application/knowledge_graph/search/reranker.py +295 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +484 -0
- aiecs/config/__init__.py +16 -0
- aiecs/config/config.py +498 -0
- aiecs/config/graph_config.py +137 -0
- aiecs/config/registry.py +23 -0
- aiecs/core/__init__.py +46 -0
- aiecs/core/interface/__init__.py +34 -0
- aiecs/core/interface/execution_interface.py +152 -0
- aiecs/core/interface/storage_interface.py +171 -0
- aiecs/domain/__init__.py +289 -0
- aiecs/domain/agent/__init__.py +189 -0
- aiecs/domain/agent/base_agent.py +697 -0
- aiecs/domain/agent/exceptions.py +103 -0
- aiecs/domain/agent/graph_aware_mixin.py +559 -0
- aiecs/domain/agent/hybrid_agent.py +490 -0
- aiecs/domain/agent/integration/__init__.py +26 -0
- aiecs/domain/agent/integration/context_compressor.py +222 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
- aiecs/domain/agent/integration/retry_policy.py +219 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +646 -0
- aiecs/domain/agent/lifecycle.py +296 -0
- aiecs/domain/agent/llm_agent.py +300 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +197 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +160 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
- aiecs/domain/agent/models.py +317 -0
- aiecs/domain/agent/observability.py +407 -0
- aiecs/domain/agent/persistence.py +289 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +161 -0
- aiecs/domain/agent/prompts/formatters.py +189 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +260 -0
- aiecs/domain/agent/tool_agent.py +257 -0
- aiecs/domain/agent/tools/__init__.py +12 -0
- aiecs/domain/agent/tools/schema_generator.py +221 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +477 -0
- aiecs/domain/community/analytics.py +481 -0
- aiecs/domain/community/collaborative_workflow.py +642 -0
- aiecs/domain/community/communication_hub.py +645 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +800 -0
- aiecs/domain/community/community_manager.py +813 -0
- aiecs/domain/community/decision_engine.py +879 -0
- aiecs/domain/community/exceptions.py +225 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +268 -0
- aiecs/domain/community/resource_manager.py +457 -0
- aiecs/domain/community/shared_context_manager.py +603 -0
- aiecs/domain/context/__init__.py +58 -0
- aiecs/domain/context/context_engine.py +989 -0
- aiecs/domain/context/conversation_models.py +354 -0
- aiecs/domain/context/graph_memory.py +467 -0
- aiecs/domain/execution/__init__.py +12 -0
- aiecs/domain/execution/model.py +57 -0
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +130 -0
- aiecs/domain/knowledge_graph/models/evidence.py +194 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
- aiecs/domain/knowledge_graph/models/path.py +179 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
- aiecs/domain/knowledge_graph/models/query.py +272 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
- aiecs/domain/knowledge_graph/models/relation.py +136 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
- aiecs/domain/task/__init__.py +13 -0
- aiecs/domain/task/dsl_processor.py +613 -0
- aiecs/domain/task/model.py +62 -0
- aiecs/domain/task/task_context.py +268 -0
- aiecs/infrastructure/__init__.py +24 -0
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +601 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
- aiecs/infrastructure/graph_storage/cache.py +429 -0
- aiecs/infrastructure/graph_storage/distributed.py +226 -0
- aiecs/infrastructure/graph_storage/error_handling.py +390 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +514 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
- aiecs/infrastructure/graph_storage/metrics.py +357 -0
- aiecs/infrastructure/graph_storage/migration.py +413 -0
- aiecs/infrastructure/graph_storage/pagination.py +471 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
- aiecs/infrastructure/graph_storage/postgres.py +871 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +623 -0
- aiecs/infrastructure/graph_storage/streaming.py +495 -0
- aiecs/infrastructure/messaging/__init__.py +13 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
- aiecs/infrastructure/messaging/websocket_manager.py +298 -0
- aiecs/infrastructure/monitoring/__init__.py +34 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
- aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
- aiecs/infrastructure/monitoring/structured_logger.py +48 -0
- aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
- aiecs/infrastructure/persistence/__init__.py +24 -0
- aiecs/infrastructure/persistence/context_engine_client.py +187 -0
- aiecs/infrastructure/persistence/database_manager.py +333 -0
- aiecs/infrastructure/persistence/file_storage.py +754 -0
- aiecs/infrastructure/persistence/redis_client.py +220 -0
- aiecs/llm/__init__.py +86 -0
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/callbacks/custom_callbacks.py +264 -0
- aiecs/llm/client_factory.py +420 -0
- aiecs/llm/clients/__init__.py +33 -0
- aiecs/llm/clients/base_client.py +193 -0
- aiecs/llm/clients/googleai_client.py +181 -0
- aiecs/llm/clients/openai_client.py +131 -0
- aiecs/llm/clients/vertex_client.py +437 -0
- aiecs/llm/clients/xai_client.py +184 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +275 -0
- aiecs/llm/config/config_validator.py +236 -0
- aiecs/llm/config/model_config.py +151 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +91 -0
- aiecs/main.py +363 -0
- aiecs/scripts/__init__.py +3 -0
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/version_manager.py +215 -0
- aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
- aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
- aiecs/scripts/dependance_check/__init__.py +17 -0
- aiecs/scripts/dependance_check/dependency_checker.py +938 -0
- aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
- aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
- aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
- aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
- aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
- aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
- aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
- aiecs/scripts/tools_develop/README.md +449 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
- aiecs/scripts/tools_develop/verify_tools.py +356 -0
- aiecs/tasks/__init__.py +1 -0
- aiecs/tasks/worker.py +172 -0
- aiecs/tools/__init__.py +299 -0
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +303 -0
- aiecs/tools/apisource/providers/__init__.py +115 -0
- aiecs/tools/apisource/providers/base.py +664 -0
- aiecs/tools/apisource/providers/census.py +401 -0
- aiecs/tools/apisource/providers/fred.py +564 -0
- aiecs/tools/apisource/providers/newsapi.py +412 -0
- aiecs/tools/apisource/providers/worldbank.py +357 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +375 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
- aiecs/tools/apisource/tool.py +850 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +338 -0
- aiecs/tools/base_tool.py +201 -0
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +599 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
- aiecs/tools/docs/content_insertion_tool.py +1333 -0
- aiecs/tools/docs/document_creator_tool.py +1317 -0
- aiecs/tools/docs/document_layout_tool.py +1166 -0
- aiecs/tools/docs/document_parser_tool.py +994 -0
- aiecs/tools/docs/document_writer_tool.py +1818 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
- aiecs/tools/langchain_adapter.py +542 -0
- aiecs/tools/schema_generator.py +275 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +589 -0
- aiecs/tools/search_tool/cache.py +260 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +216 -0
- aiecs/tools/search_tool/core.py +749 -0
- aiecs/tools/search_tool/deduplicator.py +123 -0
- aiecs/tools/search_tool/error_handler.py +271 -0
- aiecs/tools/search_tool/metrics.py +371 -0
- aiecs/tools/search_tool/rate_limiter.py +178 -0
- aiecs/tools/search_tool/schemas.py +277 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
- aiecs/tools/statistics/data_loader_tool.py +564 -0
- aiecs/tools/statistics/data_profiler_tool.py +658 -0
- aiecs/tools/statistics/data_transformer_tool.py +573 -0
- aiecs/tools/statistics/data_visualizer_tool.py +495 -0
- aiecs/tools/statistics/model_trainer_tool.py +487 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
- aiecs/tools/task_tools/__init__.py +86 -0
- aiecs/tools/task_tools/chart_tool.py +732 -0
- aiecs/tools/task_tools/classfire_tool.py +922 -0
- aiecs/tools/task_tools/image_tool.py +447 -0
- aiecs/tools/task_tools/office_tool.py +684 -0
- aiecs/tools/task_tools/pandas_tool.py +635 -0
- aiecs/tools/task_tools/report_tool.py +635 -0
- aiecs/tools/task_tools/research_tool.py +392 -0
- aiecs/tools/task_tools/scraper_tool.py +715 -0
- aiecs/tools/task_tools/stats_tool.py +688 -0
- aiecs/tools/temp_file_manager.py +130 -0
- aiecs/tools/tool_executor/__init__.py +37 -0
- aiecs/tools/tool_executor/tool_executor.py +881 -0
- aiecs/utils/LLM_output_structor.py +445 -0
- aiecs/utils/__init__.py +34 -0
- aiecs/utils/base_callback.py +47 -0
- aiecs/utils/cache_provider.py +695 -0
- aiecs/utils/execution_utils.py +184 -0
- aiecs/utils/logging.py +1 -0
- aiecs/utils/prompt_loader.py +14 -0
- aiecs/utils/token_usage_repository.py +323 -0
- aiecs/ws/__init__.py +0 -0
- aiecs/ws/socket_server.py +52 -0
- aiecs-1.5.1.dist-info/METADATA +608 -0
- aiecs-1.5.1.dist-info/RECORD +302 -0
- aiecs-1.5.1.dist-info/WHEEL +5 -0
- aiecs-1.5.1.dist-info/entry_points.txt +10 -0
- aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
- 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")
|