aiecs 1.0.1__py3-none-any.whl → 1.7.17__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.
Potentially problematic release.
This version of aiecs might be problematic. Click here for more details.
- aiecs/__init__.py +13 -16
- aiecs/__main__.py +7 -7
- aiecs/aiecs_client.py +269 -75
- aiecs/application/executors/operation_executor.py +79 -54
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/data_quality.py +302 -0
- aiecs/application/knowledge_graph/builder/data_reshaping.py +293 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +369 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +490 -0
- aiecs/application/knowledge_graph/builder/import_optimizer.py +396 -0
- aiecs/application/knowledge_graph/builder/schema_inference.py +462 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +563 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +1384 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +317 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +98 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +422 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +347 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +241 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +78 -0
- aiecs/application/knowledge_graph/fusion/ab_testing.py +395 -0
- aiecs/application/knowledge_graph/fusion/abbreviation_expander.py +327 -0
- aiecs/application/knowledge_graph/fusion/alias_index.py +597 -0
- aiecs/application/knowledge_graph/fusion/alias_matcher.py +384 -0
- aiecs/application/knowledge_graph/fusion/cache_coordinator.py +343 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +433 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +511 -0
- aiecs/application/knowledge_graph/fusion/evaluation_dataset.py +240 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +632 -0
- aiecs/application/knowledge_graph/fusion/matching_config.py +489 -0
- aiecs/application/knowledge_graph/fusion/name_normalizer.py +352 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +183 -0
- aiecs/application/knowledge_graph/fusion/semantic_name_matcher.py +464 -0
- aiecs/application/knowledge_graph/fusion/similarity_pipeline.py +534 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +342 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +366 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +195 -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 +341 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +500 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +163 -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 +913 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +866 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +475 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +396 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +208 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +170 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +855 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +518 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +27 -0
- aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +211 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +592 -0
- aiecs/application/knowledge_graph/retrieval/strategy_types.py +23 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +457 -0
- aiecs/application/knowledge_graph/search/reranker.py +293 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +535 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +392 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +305 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +271 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +239 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +313 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +471 -0
- aiecs/config/__init__.py +20 -5
- aiecs/config/config.py +762 -31
- aiecs/config/graph_config.py +131 -0
- aiecs/config/tool_config.py +435 -0
- aiecs/core/__init__.py +29 -13
- aiecs/core/interface/__init__.py +2 -2
- aiecs/core/interface/execution_interface.py +22 -22
- aiecs/core/interface/storage_interface.py +37 -88
- aiecs/core/registry/__init__.py +31 -0
- aiecs/core/registry/service_registry.py +92 -0
- aiecs/domain/__init__.py +270 -1
- aiecs/domain/agent/__init__.py +191 -0
- aiecs/domain/agent/base_agent.py +3949 -0
- aiecs/domain/agent/exceptions.py +99 -0
- aiecs/domain/agent/graph_aware_mixin.py +569 -0
- aiecs/domain/agent/hybrid_agent.py +1731 -0
- aiecs/domain/agent/integration/__init__.py +29 -0
- aiecs/domain/agent/integration/context_compressor.py +216 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +587 -0
- aiecs/domain/agent/integration/protocols.py +281 -0
- aiecs/domain/agent/integration/retry_policy.py +218 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +1892 -0
- aiecs/domain/agent/lifecycle.py +291 -0
- aiecs/domain/agent/llm_agent.py +692 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +1124 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +163 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +86 -0
- aiecs/domain/agent/models.py +894 -0
- aiecs/domain/agent/observability.py +479 -0
- aiecs/domain/agent/persistence.py +449 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +159 -0
- aiecs/domain/agent/prompts/formatters.py +187 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +253 -0
- aiecs/domain/agent/tool_agent.py +444 -0
- aiecs/domain/agent/tools/__init__.py +15 -0
- aiecs/domain/agent/tools/schema_generator.py +377 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +469 -0
- aiecs/domain/community/analytics.py +432 -0
- aiecs/domain/community/collaborative_workflow.py +648 -0
- aiecs/domain/community/communication_hub.py +634 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +796 -0
- aiecs/domain/community/community_manager.py +803 -0
- aiecs/domain/community/decision_engine.py +849 -0
- aiecs/domain/community/exceptions.py +231 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +234 -0
- aiecs/domain/community/resource_manager.py +461 -0
- aiecs/domain/community/shared_context_manager.py +589 -0
- aiecs/domain/context/__init__.py +40 -10
- aiecs/domain/context/context_engine.py +1910 -0
- aiecs/domain/context/conversation_models.py +87 -53
- aiecs/domain/context/graph_memory.py +582 -0
- aiecs/domain/execution/model.py +12 -4
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +148 -0
- aiecs/domain/knowledge_graph/models/evidence.py +178 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +184 -0
- aiecs/domain/knowledge_graph/models/path.py +171 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +171 -0
- aiecs/domain/knowledge_graph/models/query.py +261 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +181 -0
- aiecs/domain/knowledge_graph/models/relation.py +202 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +131 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +253 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +143 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +163 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +691 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +209 -0
- aiecs/domain/task/dsl_processor.py +172 -56
- aiecs/domain/task/model.py +20 -8
- aiecs/domain/task/task_context.py +27 -24
- aiecs/infrastructure/__init__.py +0 -2
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +837 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +458 -0
- aiecs/infrastructure/graph_storage/cache.py +424 -0
- aiecs/infrastructure/graph_storage/distributed.py +223 -0
- aiecs/infrastructure/graph_storage/error_handling.py +380 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +294 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +1197 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +446 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +431 -0
- aiecs/infrastructure/graph_storage/metrics.py +344 -0
- aiecs/infrastructure/graph_storage/migration.py +400 -0
- aiecs/infrastructure/graph_storage/pagination.py +483 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +456 -0
- aiecs/infrastructure/graph_storage/postgres.py +1563 -0
- aiecs/infrastructure/graph_storage/property_storage.py +353 -0
- aiecs/infrastructure/graph_storage/protocols.py +76 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +642 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +1373 -0
- aiecs/infrastructure/graph_storage/streaming.py +487 -0
- aiecs/infrastructure/graph_storage/tenant.py +412 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +92 -54
- aiecs/infrastructure/messaging/websocket_manager.py +51 -35
- aiecs/infrastructure/monitoring/__init__.py +22 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +45 -11
- aiecs/infrastructure/monitoring/global_metrics_manager.py +212 -0
- aiecs/infrastructure/monitoring/structured_logger.py +3 -7
- aiecs/infrastructure/monitoring/tracing_manager.py +63 -35
- aiecs/infrastructure/persistence/__init__.py +14 -1
- aiecs/infrastructure/persistence/context_engine_client.py +184 -0
- aiecs/infrastructure/persistence/database_manager.py +67 -43
- aiecs/infrastructure/persistence/file_storage.py +180 -103
- aiecs/infrastructure/persistence/redis_client.py +74 -21
- aiecs/llm/__init__.py +73 -25
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +26 -19
- aiecs/llm/client_factory.py +230 -37
- aiecs/llm/client_resolver.py +155 -0
- aiecs/llm/clients/__init__.py +38 -0
- aiecs/llm/clients/base_client.py +328 -0
- aiecs/llm/clients/google_function_calling_mixin.py +415 -0
- aiecs/llm/clients/googleai_client.py +314 -0
- aiecs/llm/clients/openai_client.py +158 -0
- aiecs/llm/clients/openai_compatible_mixin.py +367 -0
- aiecs/llm/clients/vertex_client.py +1186 -0
- aiecs/llm/clients/xai_client.py +201 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +272 -0
- aiecs/llm/config/config_validator.py +206 -0
- aiecs/llm/config/model_config.py +143 -0
- aiecs/llm/protocols.py +149 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +89 -0
- aiecs/main.py +140 -121
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +138 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/module_checker.py +499 -0
- aiecs/scripts/aid/version_manager.py +235 -0
- aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +1 -0
- aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +1 -0
- aiecs/scripts/dependance_check/__init__.py +15 -0
- aiecs/scripts/dependance_check/dependency_checker.py +1835 -0
- aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +192 -90
- aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +203 -71
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +21 -14
- aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +1 -1
- aiecs/scripts/knowledge_graph/__init__.py +3 -0
- aiecs/scripts/knowledge_graph/run_threshold_experiments.py +212 -0
- aiecs/scripts/migrations/multi_tenancy/README.md +142 -0
- aiecs/scripts/tools_develop/README.md +671 -0
- aiecs/scripts/tools_develop/README_CONFIG_CHECKER.md +273 -0
- aiecs/scripts/tools_develop/TOOLS_CONFIG_GUIDE.md +1287 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_all_tools_config.py +548 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +257 -0
- aiecs/scripts/tools_develop/pre-commit-schema-coverage.sh +66 -0
- aiecs/scripts/tools_develop/schema_coverage.py +511 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +475 -0
- aiecs/scripts/tools_develop/verify_executor_config_fix.py +98 -0
- aiecs/scripts/tools_develop/verify_tools.py +352 -0
- aiecs/tasks/__init__.py +0 -1
- aiecs/tasks/worker.py +115 -47
- aiecs/tools/__init__.py +194 -72
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +632 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +417 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +385 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +330 -0
- aiecs/tools/apisource/providers/__init__.py +112 -0
- aiecs/tools/apisource/providers/base.py +671 -0
- aiecs/tools/apisource/providers/census.py +397 -0
- aiecs/tools/apisource/providers/fred.py +535 -0
- aiecs/tools/apisource/providers/newsapi.py +409 -0
- aiecs/tools/apisource/providers/worldbank.py +352 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +363 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +376 -0
- aiecs/tools/apisource/tool.py +832 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +334 -0
- aiecs/tools/base_tool.py +415 -21
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +607 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2350 -0
- aiecs/tools/docs/content_insertion_tool.py +1320 -0
- aiecs/tools/docs/document_creator_tool.py +1464 -0
- aiecs/tools/docs/document_layout_tool.py +1160 -0
- aiecs/tools/docs/document_parser_tool.py +1016 -0
- aiecs/tools/docs/document_writer_tool.py +2008 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +807 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +944 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +524 -0
- aiecs/tools/langchain_adapter.py +300 -138
- aiecs/tools/schema_generator.py +455 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +581 -0
- aiecs/tools/search_tool/cache.py +264 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +224 -0
- aiecs/tools/search_tool/core.py +778 -0
- aiecs/tools/search_tool/deduplicator.py +119 -0
- aiecs/tools/search_tool/error_handler.py +242 -0
- aiecs/tools/search_tool/metrics.py +343 -0
- aiecs/tools/search_tool/rate_limiter.py +172 -0
- aiecs/tools/search_tool/schemas.py +275 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +646 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +508 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +684 -0
- aiecs/tools/statistics/data_loader_tool.py +555 -0
- aiecs/tools/statistics/data_profiler_tool.py +638 -0
- aiecs/tools/statistics/data_transformer_tool.py +580 -0
- aiecs/tools/statistics/data_visualizer_tool.py +498 -0
- aiecs/tools/statistics/model_trainer_tool.py +507 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +472 -0
- aiecs/tools/task_tools/__init__.py +49 -36
- aiecs/tools/task_tools/chart_tool.py +200 -184
- aiecs/tools/task_tools/classfire_tool.py +268 -267
- aiecs/tools/task_tools/image_tool.py +220 -141
- aiecs/tools/task_tools/office_tool.py +226 -146
- aiecs/tools/task_tools/pandas_tool.py +477 -121
- aiecs/tools/task_tools/report_tool.py +390 -142
- aiecs/tools/task_tools/research_tool.py +149 -79
- aiecs/tools/task_tools/scraper_tool.py +339 -145
- aiecs/tools/task_tools/stats_tool.py +448 -209
- aiecs/tools/temp_file_manager.py +26 -24
- aiecs/tools/tool_executor/__init__.py +18 -16
- aiecs/tools/tool_executor/tool_executor.py +364 -52
- aiecs/utils/LLM_output_structor.py +74 -48
- aiecs/utils/__init__.py +14 -3
- aiecs/utils/base_callback.py +0 -3
- aiecs/utils/cache_provider.py +696 -0
- aiecs/utils/execution_utils.py +50 -31
- aiecs/utils/prompt_loader.py +1 -0
- aiecs/utils/token_usage_repository.py +37 -11
- aiecs/ws/socket_server.py +14 -4
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/METADATA +52 -15
- aiecs-1.7.17.dist-info/RECORD +337 -0
- aiecs-1.7.17.dist-info/entry_points.txt +13 -0
- aiecs/config/registry.py +0 -19
- aiecs/domain/context/content_engine.py +0 -982
- aiecs/llm/base_client.py +0 -99
- aiecs/llm/openai_client.py +0 -125
- aiecs/llm/vertex_client.py +0 -186
- aiecs/llm/xai_client.py +0 -184
- aiecs/scripts/dependency_checker.py +0 -857
- aiecs/scripts/quick_dependency_check.py +0 -269
- aiecs/tools/task_tools/search_api.py +0 -7
- aiecs-1.0.1.dist-info/RECORD +0 -90
- aiecs-1.0.1.dist-info/entry_points.txt +0 -7
- /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
- /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
- /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
- /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/WHEEL +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.17.dist-info}/top_level.txt +0 -0
|
@@ -3,9 +3,10 @@ import logging
|
|
|
3
3
|
import time
|
|
4
4
|
import uuid
|
|
5
5
|
from typing import Dict, List, Any, Optional
|
|
6
|
-
from celery import Celery
|
|
7
|
-
from celery.exceptions import TimeoutError as CeleryTimeoutError
|
|
6
|
+
from celery import Celery # type: ignore[import-untyped]
|
|
7
|
+
from celery.exceptions import TimeoutError as CeleryTimeoutError # type: ignore[import-untyped]
|
|
8
8
|
from asyncio import TimeoutError as AsyncioTimeoutError
|
|
9
|
+
|
|
9
10
|
# Removed direct import to avoid circular dependency
|
|
10
11
|
# Tasks are referenced by string names instead
|
|
11
12
|
from aiecs.domain.execution.model import TaskStatus, ErrorCode
|
|
@@ -20,33 +21,42 @@ class CeleryTaskManager:
|
|
|
20
21
|
|
|
21
22
|
def __init__(self, config: Dict[str, Any]):
|
|
22
23
|
self.config = config
|
|
23
|
-
self.celery_app = None
|
|
24
|
+
self.celery_app: Optional[Celery] = None
|
|
24
25
|
self._init_celery()
|
|
25
26
|
|
|
26
27
|
def _init_celery(self):
|
|
27
28
|
"""Initialize Celery application"""
|
|
28
29
|
try:
|
|
29
30
|
self.celery_app = Celery(
|
|
30
|
-
|
|
31
|
-
broker=self.config.get(
|
|
32
|
-
backend=self.config.get(
|
|
31
|
+
"service_executor",
|
|
32
|
+
broker=self.config.get("broker_url", "redis://redis:6379/0"),
|
|
33
|
+
backend=self.config.get("backend_url", "redis://redis:6379/0"),
|
|
33
34
|
)
|
|
34
35
|
|
|
35
36
|
# Configure Celery
|
|
36
37
|
self.celery_app.conf.update(
|
|
37
|
-
task_serializer=self.config.get(
|
|
38
|
-
accept_content=self.config.get(
|
|
39
|
-
result_serializer=self.config.get(
|
|
40
|
-
timezone=self.config.get(
|
|
41
|
-
enable_utc=self.config.get(
|
|
42
|
-
task_queues=self.config.get(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
task_serializer=self.config.get("task_serializer", "json"),
|
|
39
|
+
accept_content=self.config.get("accept_content", ["json"]),
|
|
40
|
+
result_serializer=self.config.get("result_serializer", "json"),
|
|
41
|
+
timezone=self.config.get("timezone", "UTC"),
|
|
42
|
+
enable_utc=self.config.get("enable_utc", True),
|
|
43
|
+
task_queues=self.config.get(
|
|
44
|
+
"task_queues",
|
|
45
|
+
{
|
|
46
|
+
"fast_tasks": {
|
|
47
|
+
"exchange": "fast_tasks",
|
|
48
|
+
"routing_key": "fast_tasks",
|
|
49
|
+
},
|
|
50
|
+
"heavy_tasks": {
|
|
51
|
+
"exchange": "heavy_tasks",
|
|
52
|
+
"routing_key": "heavy_tasks",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
),
|
|
56
|
+
worker_concurrency=self.config.get(
|
|
57
|
+
"worker_concurrency",
|
|
58
|
+
{"fast_worker": 10, "heavy_worker": 2},
|
|
59
|
+
),
|
|
50
60
|
)
|
|
51
61
|
|
|
52
62
|
logger.info("Celery application initialized successfully")
|
|
@@ -54,8 +64,18 @@ class CeleryTaskManager:
|
|
|
54
64
|
logger.error(f"Failed to initialize Celery: {e}")
|
|
55
65
|
raise
|
|
56
66
|
|
|
57
|
-
def execute_celery_task(
|
|
58
|
-
|
|
67
|
+
def execute_celery_task(
|
|
68
|
+
self,
|
|
69
|
+
task_name: str,
|
|
70
|
+
queue: str,
|
|
71
|
+
user_id: str,
|
|
72
|
+
task_id: str,
|
|
73
|
+
step: int,
|
|
74
|
+
mode: str,
|
|
75
|
+
service: str,
|
|
76
|
+
input_data: Dict[str, Any],
|
|
77
|
+
context: Dict[str, Any],
|
|
78
|
+
):
|
|
59
79
|
"""
|
|
60
80
|
Execute Celery task
|
|
61
81
|
|
|
@@ -81,6 +101,8 @@ class CeleryTaskManager:
|
|
|
81
101
|
celery_task_name = "aiecs.tasks.worker.execute_heavy_task"
|
|
82
102
|
|
|
83
103
|
# Send task to Celery
|
|
104
|
+
if self.celery_app is None:
|
|
105
|
+
raise RuntimeError("Celery app not initialized")
|
|
84
106
|
return self.celery_app.send_task(
|
|
85
107
|
celery_task_name,
|
|
86
108
|
kwargs={
|
|
@@ -91,12 +113,17 @@ class CeleryTaskManager:
|
|
|
91
113
|
"mode": mode,
|
|
92
114
|
"service": service,
|
|
93
115
|
"input_data": input_data,
|
|
94
|
-
"context": context
|
|
116
|
+
"context": context,
|
|
95
117
|
},
|
|
96
|
-
queue=queue
|
|
118
|
+
queue=queue,
|
|
97
119
|
)
|
|
98
120
|
|
|
99
|
-
async def execute_task(
|
|
121
|
+
async def execute_task(
|
|
122
|
+
self,
|
|
123
|
+
task_name: str,
|
|
124
|
+
input_data: Dict[str, Any],
|
|
125
|
+
context: Dict[str, Any],
|
|
126
|
+
) -> Any:
|
|
100
127
|
"""
|
|
101
128
|
Execute a single task using Celery for asynchronous processing
|
|
102
129
|
"""
|
|
@@ -106,14 +133,16 @@ class CeleryTaskManager:
|
|
|
106
133
|
mode = input_data.get("mode", "default")
|
|
107
134
|
service = input_data.get("service", "default")
|
|
108
135
|
queue = input_data.get("queue", "fast_tasks")
|
|
109
|
-
timeout = self.config.get(
|
|
136
|
+
timeout = self.config.get("task_timeout_seconds", 300)
|
|
110
137
|
|
|
111
138
|
try:
|
|
112
139
|
# Use string-based task names to avoid circular imports
|
|
113
140
|
celery_task_name = "aiecs.tasks.worker.execute_task"
|
|
114
|
-
if queue ==
|
|
141
|
+
if queue == "heavy_tasks":
|
|
115
142
|
celery_task_name = "aiecs.tasks.worker.execute_heavy_task"
|
|
116
143
|
|
|
144
|
+
if self.celery_app is None:
|
|
145
|
+
raise RuntimeError("Celery app not initialized")
|
|
117
146
|
result = self.celery_app.send_task(
|
|
118
147
|
celery_task_name,
|
|
119
148
|
kwargs={
|
|
@@ -124,9 +153,9 @@ class CeleryTaskManager:
|
|
|
124
153
|
"mode": mode,
|
|
125
154
|
"service": service,
|
|
126
155
|
"input_data": input_data,
|
|
127
|
-
"context": context
|
|
156
|
+
"context": context,
|
|
128
157
|
},
|
|
129
|
-
queue=queue
|
|
158
|
+
queue=queue,
|
|
130
159
|
)
|
|
131
160
|
|
|
132
161
|
return result.get(timeout=timeout)
|
|
@@ -136,14 +165,14 @@ class CeleryTaskManager:
|
|
|
136
165
|
return {
|
|
137
166
|
"status": TaskStatus.TIMED_OUT,
|
|
138
167
|
"error_code": ErrorCode.TIMEOUT_ERROR,
|
|
139
|
-
"error_message": str(e)
|
|
168
|
+
"error_message": str(e),
|
|
140
169
|
}
|
|
141
170
|
except Exception as e:
|
|
142
171
|
logger.error(f"Error executing Celery task {task_name}: {e}", exc_info=True)
|
|
143
172
|
return {
|
|
144
173
|
"status": TaskStatus.FAILED,
|
|
145
174
|
"error_code": ErrorCode.EXECUTION_ERROR,
|
|
146
|
-
"error_message": str(e)
|
|
175
|
+
"error_message": str(e),
|
|
147
176
|
}
|
|
148
177
|
|
|
149
178
|
async def execute_heavy_task(self, task_name: str, input_data: Dict, context: Dict) -> Any:
|
|
@@ -168,7 +197,7 @@ class CeleryTaskManager:
|
|
|
168
197
|
"message": "Invalid DSL step: missing task name",
|
|
169
198
|
"status": TaskStatus.FAILED,
|
|
170
199
|
"error_code": ErrorCode.VALIDATION_ERROR,
|
|
171
|
-
"error_message": "Task name is required"
|
|
200
|
+
"error_message": "Task name is required",
|
|
172
201
|
}
|
|
173
202
|
|
|
174
203
|
# Determine task type
|
|
@@ -188,6 +217,8 @@ class CeleryTaskManager:
|
|
|
188
217
|
step_num = context.get("step", 0)
|
|
189
218
|
|
|
190
219
|
# Send task to Celery
|
|
220
|
+
if self.celery_app is None:
|
|
221
|
+
raise RuntimeError("Celery app not initialized")
|
|
191
222
|
celery_task = self.celery_app.send_task(
|
|
192
223
|
celery_task_name,
|
|
193
224
|
kwargs={
|
|
@@ -198,13 +229,13 @@ class CeleryTaskManager:
|
|
|
198
229
|
"mode": context.get("mode", "multi_task"),
|
|
199
230
|
"service": context.get("service", "summarizer"),
|
|
200
231
|
"input_data": input_data,
|
|
201
|
-
"context": context
|
|
232
|
+
"context": context,
|
|
202
233
|
},
|
|
203
|
-
queue=queue
|
|
234
|
+
queue=queue,
|
|
204
235
|
)
|
|
205
236
|
|
|
206
237
|
try:
|
|
207
|
-
timeout_seconds = self.config.get(
|
|
238
|
+
timeout_seconds = self.config.get("task_timeout_seconds", 300)
|
|
208
239
|
start_time = time.time()
|
|
209
240
|
|
|
210
241
|
# Wait for task completion
|
|
@@ -223,7 +254,7 @@ class CeleryTaskManager:
|
|
|
223
254
|
"result": result,
|
|
224
255
|
"completed": True,
|
|
225
256
|
"message": f"Completed task {task_name}",
|
|
226
|
-
"status": TaskStatus.COMPLETED
|
|
257
|
+
"status": TaskStatus.COMPLETED,
|
|
227
258
|
}
|
|
228
259
|
else:
|
|
229
260
|
error = celery_task.get(propagate=False)
|
|
@@ -237,7 +268,7 @@ class CeleryTaskManager:
|
|
|
237
268
|
"message": f"Failed to execute task: {error}",
|
|
238
269
|
"status": status,
|
|
239
270
|
"error_code": error_code,
|
|
240
|
-
"error_message": str(error)
|
|
271
|
+
"error_message": str(error),
|
|
241
272
|
}
|
|
242
273
|
|
|
243
274
|
except AsyncioTimeoutError as e:
|
|
@@ -248,7 +279,7 @@ class CeleryTaskManager:
|
|
|
248
279
|
"message": "Task execution timed out",
|
|
249
280
|
"status": TaskStatus.TIMED_OUT,
|
|
250
281
|
"error_code": ErrorCode.TIMEOUT_ERROR,
|
|
251
|
-
"error_message": str(e)
|
|
282
|
+
"error_message": str(e),
|
|
252
283
|
}
|
|
253
284
|
except Exception as e:
|
|
254
285
|
return {
|
|
@@ -258,31 +289,31 @@ class CeleryTaskManager:
|
|
|
258
289
|
"message": f"Failed to execute {category}/{task_name}",
|
|
259
290
|
"status": TaskStatus.FAILED,
|
|
260
291
|
"error_code": ErrorCode.EXECUTION_ERROR,
|
|
261
|
-
"error_message": str(e)
|
|
292
|
+
"error_message": str(e),
|
|
262
293
|
}
|
|
263
294
|
|
|
264
295
|
def get_task_result(self, task_id: str):
|
|
265
296
|
"""Get task result"""
|
|
266
297
|
try:
|
|
298
|
+
if self.celery_app is None:
|
|
299
|
+
raise RuntimeError("Celery app not initialized")
|
|
267
300
|
result = self.celery_app.AsyncResult(task_id)
|
|
268
301
|
return {
|
|
269
302
|
"task_id": task_id,
|
|
270
303
|
"status": result.status,
|
|
271
304
|
"result": result.result if result.ready() else None,
|
|
272
305
|
"successful": result.successful() if result.ready() else None,
|
|
273
|
-
"failed": result.failed() if result.ready() else None
|
|
306
|
+
"failed": result.failed() if result.ready() else None,
|
|
274
307
|
}
|
|
275
308
|
except Exception as e:
|
|
276
309
|
logger.error(f"Error getting task result for {task_id}: {e}")
|
|
277
|
-
return {
|
|
278
|
-
"task_id": task_id,
|
|
279
|
-
"status": "ERROR",
|
|
280
|
-
"error": str(e)
|
|
281
|
-
}
|
|
310
|
+
return {"task_id": task_id, "status": "ERROR", "error": str(e)}
|
|
282
311
|
|
|
283
312
|
def cancel_task(self, task_id: str):
|
|
284
313
|
"""Cancel task"""
|
|
285
314
|
try:
|
|
315
|
+
if self.celery_app is None:
|
|
316
|
+
raise RuntimeError("Celery app not initialized")
|
|
286
317
|
self.celery_app.control.revoke(task_id, terminate=True)
|
|
287
318
|
logger.info(f"Task {task_id} cancelled")
|
|
288
319
|
return True
|
|
@@ -295,18 +326,21 @@ class CeleryTaskManager:
|
|
|
295
326
|
Batch execute tasks
|
|
296
327
|
"""
|
|
297
328
|
results = []
|
|
298
|
-
batch_size = self.config.get(
|
|
299
|
-
rate_limit = self.config.get(
|
|
329
|
+
batch_size = self.config.get("batch_size", 10)
|
|
330
|
+
rate_limit = self.config.get("rate_limit_requests_per_second", 5)
|
|
300
331
|
|
|
301
332
|
for i in range(0, len(tasks), batch_size):
|
|
302
|
-
batch = tasks[i:i + batch_size]
|
|
333
|
+
batch = tasks[i : i + batch_size]
|
|
303
334
|
batch_results = await asyncio.gather(
|
|
304
|
-
*[
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
335
|
+
*[
|
|
336
|
+
self.execute_task(
|
|
337
|
+
task["task_name"],
|
|
338
|
+
task.get("input_data", {}),
|
|
339
|
+
task.get("context", {}),
|
|
340
|
+
)
|
|
341
|
+
for task in batch
|
|
342
|
+
],
|
|
343
|
+
return_exceptions=True,
|
|
310
344
|
)
|
|
311
345
|
results.extend(batch_results)
|
|
312
346
|
await asyncio.sleep(1.0 / rate_limit)
|
|
@@ -316,6 +350,8 @@ class CeleryTaskManager:
|
|
|
316
350
|
def get_queue_info(self) -> Dict[str, Any]:
|
|
317
351
|
"""Get queue information"""
|
|
318
352
|
try:
|
|
353
|
+
if self.celery_app is None:
|
|
354
|
+
raise RuntimeError("Celery app not initialized")
|
|
319
355
|
inspect = self.celery_app.control.inspect()
|
|
320
356
|
active_tasks = inspect.active()
|
|
321
357
|
scheduled_tasks = inspect.scheduled()
|
|
@@ -324,7 +360,7 @@ class CeleryTaskManager:
|
|
|
324
360
|
return {
|
|
325
361
|
"active_tasks": active_tasks,
|
|
326
362
|
"scheduled_tasks": scheduled_tasks,
|
|
327
|
-
"reserved_tasks": reserved_tasks
|
|
363
|
+
"reserved_tasks": reserved_tasks,
|
|
328
364
|
}
|
|
329
365
|
except Exception as e:
|
|
330
366
|
logger.error(f"Error getting queue info: {e}")
|
|
@@ -333,6 +369,8 @@ class CeleryTaskManager:
|
|
|
333
369
|
def get_worker_stats(self) -> Dict[str, Any]:
|
|
334
370
|
"""Get worker statistics"""
|
|
335
371
|
try:
|
|
372
|
+
if self.celery_app is None:
|
|
373
|
+
raise RuntimeError("Celery app not initialized")
|
|
336
374
|
inspect = self.celery_app.control.inspect()
|
|
337
375
|
stats = inspect.stats()
|
|
338
376
|
return stats or {}
|
|
@@ -45,11 +45,7 @@ class WebSocketManager:
|
|
|
45
45
|
return self.server
|
|
46
46
|
|
|
47
47
|
try:
|
|
48
|
-
self.server = await serve(
|
|
49
|
-
self._handle_client_connection,
|
|
50
|
-
self.host,
|
|
51
|
-
self.port
|
|
52
|
-
)
|
|
48
|
+
self.server = await serve(self._handle_client_connection, self.host, self.port)
|
|
53
49
|
self._running = True
|
|
54
50
|
logger.info(f"WebSocket server started on {self.host}:{self.port}")
|
|
55
51
|
return self.server
|
|
@@ -69,7 +65,7 @@ class WebSocketManager:
|
|
|
69
65
|
if self.active_connections:
|
|
70
66
|
await asyncio.gather(
|
|
71
67
|
*[conn.close() for conn in self.active_connections],
|
|
72
|
-
return_exceptions=True
|
|
68
|
+
return_exceptions=True,
|
|
73
69
|
)
|
|
74
70
|
self.active_connections.clear()
|
|
75
71
|
|
|
@@ -81,15 +77,19 @@ class WebSocketManager:
|
|
|
81
77
|
|
|
82
78
|
try:
|
|
83
79
|
async for message in websocket:
|
|
84
|
-
|
|
80
|
+
# Decode bytes to str if needed
|
|
81
|
+
message_str = message if isinstance(message, str) else message.decode('utf-8')
|
|
82
|
+
await self._handle_client_message(websocket, message_str)
|
|
85
83
|
except websockets.exceptions.ConnectionClosed:
|
|
86
84
|
logger.info(f"WebSocket connection closed: {client_addr}")
|
|
87
85
|
except Exception as e:
|
|
88
86
|
logger.error(f"WebSocket error for {client_addr}: {e}")
|
|
89
87
|
finally:
|
|
90
88
|
self.active_connections.discard(websocket)
|
|
91
|
-
|
|
89
|
+
try:
|
|
92
90
|
await websocket.close()
|
|
91
|
+
except Exception:
|
|
92
|
+
pass # Connection already closed
|
|
93
93
|
|
|
94
94
|
async def _handle_client_message(self, websocket: ServerConnection, message: str):
|
|
95
95
|
"""Handle client message"""
|
|
@@ -123,7 +123,7 @@ class WebSocketManager:
|
|
|
123
123
|
callback = self.callback_registry[callback_id]
|
|
124
124
|
confirmation = UserConfirmation(
|
|
125
125
|
proceed=data.get("proceed", False),
|
|
126
|
-
feedback=data.get("feedback")
|
|
126
|
+
feedback=data.get("feedback"),
|
|
127
127
|
)
|
|
128
128
|
try:
|
|
129
129
|
callback(confirmation)
|
|
@@ -141,14 +141,17 @@ class WebSocketManager:
|
|
|
141
141
|
|
|
142
142
|
if user_id and task_id:
|
|
143
143
|
# Task cancellation logic can be added here
|
|
144
|
-
# Since database manager access is needed, this functionality may
|
|
144
|
+
# Since database manager access is needed, this functionality may
|
|
145
|
+
# need to be implemented through callbacks
|
|
145
146
|
logger.info(f"Task cancellation requested: user={user_id}, task={task_id}")
|
|
146
|
-
await self.broadcast_message(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
await self.broadcast_message(
|
|
148
|
+
{
|
|
149
|
+
"type": "task_cancelled",
|
|
150
|
+
"user_id": user_id,
|
|
151
|
+
"task_id": task_id,
|
|
152
|
+
"timestamp": asyncio.get_event_loop().time(),
|
|
153
|
+
}
|
|
154
|
+
)
|
|
152
155
|
else:
|
|
153
156
|
logger.warning("Invalid cancellation request: missing user_id or task_id")
|
|
154
157
|
|
|
@@ -157,7 +160,7 @@ class WebSocketManager:
|
|
|
157
160
|
pong_data = {
|
|
158
161
|
"type": "pong",
|
|
159
162
|
"timestamp": asyncio.get_event_loop().time(),
|
|
160
|
-
"original_data": data
|
|
163
|
+
"original_data": data,
|
|
161
164
|
}
|
|
162
165
|
await self._send_to_client(websocket, pong_data)
|
|
163
166
|
|
|
@@ -167,32 +170,37 @@ class WebSocketManager:
|
|
|
167
170
|
if user_id:
|
|
168
171
|
# User-specific subscription logic can be implemented here
|
|
169
172
|
logger.info(f"User {user_id} subscribed to updates")
|
|
170
|
-
await self._send_to_client(
|
|
171
|
-
|
|
172
|
-
"user_id": user_id
|
|
173
|
-
|
|
173
|
+
await self._send_to_client(
|
|
174
|
+
websocket,
|
|
175
|
+
{"type": "subscription_confirmed", "user_id": user_id},
|
|
176
|
+
)
|
|
174
177
|
|
|
175
178
|
async def _send_error(self, websocket: ServerConnection, error_message: str):
|
|
176
179
|
"""Send error message to client"""
|
|
177
180
|
error_data = {
|
|
178
181
|
"type": "error",
|
|
179
182
|
"message": error_message,
|
|
180
|
-
"timestamp": asyncio.get_event_loop().time()
|
|
183
|
+
"timestamp": asyncio.get_event_loop().time(),
|
|
181
184
|
}
|
|
182
185
|
await self._send_to_client(websocket, error_data)
|
|
183
186
|
|
|
184
187
|
async def _send_to_client(self, websocket: ServerConnection, data: Dict[str, Any]):
|
|
185
188
|
"""Send data to specific client"""
|
|
186
189
|
try:
|
|
187
|
-
|
|
188
|
-
await websocket.send(json.dumps(data))
|
|
190
|
+
await websocket.send(json.dumps(data))
|
|
189
191
|
except Exception as e:
|
|
190
192
|
logger.error(f"Failed to send message to client: {e}")
|
|
191
193
|
|
|
192
|
-
async def notify_user(
|
|
194
|
+
async def notify_user(
|
|
195
|
+
self,
|
|
196
|
+
step_result: TaskStepResult,
|
|
197
|
+
user_id: str,
|
|
198
|
+
task_id: str,
|
|
199
|
+
step: int,
|
|
200
|
+
) -> UserConfirmation:
|
|
193
201
|
"""Notify user of task step result"""
|
|
194
202
|
callback_id = str(uuid.uuid4())
|
|
195
|
-
confirmation_future = asyncio.Future()
|
|
203
|
+
confirmation_future: asyncio.Future[UserConfirmation] = asyncio.Future()
|
|
196
204
|
|
|
197
205
|
# Register callback
|
|
198
206
|
self.callback_registry[callback_id] = lambda confirmation: confirmation_future.set_result(confirmation)
|
|
@@ -209,16 +217,18 @@ class WebSocketManager:
|
|
|
209
217
|
"error_message": step_result.error_message,
|
|
210
218
|
"user_id": user_id,
|
|
211
219
|
"task_id": task_id,
|
|
212
|
-
"timestamp": asyncio.get_event_loop().time()
|
|
220
|
+
"timestamp": asyncio.get_event_loop().time(),
|
|
213
221
|
}
|
|
214
222
|
|
|
215
223
|
try:
|
|
216
|
-
# Broadcast to all connected clients (can be optimized to send only
|
|
224
|
+
# Broadcast to all connected clients (can be optimized to send only
|
|
225
|
+
# to specific users)
|
|
217
226
|
await self.broadcast_message(notification_data)
|
|
218
227
|
|
|
219
228
|
# Wait for user confirmation with timeout
|
|
220
229
|
try:
|
|
221
|
-
|
|
230
|
+
# 5 minute timeout
|
|
231
|
+
return await asyncio.wait_for(confirmation_future, timeout=300)
|
|
222
232
|
except asyncio.TimeoutError:
|
|
223
233
|
logger.warning(f"User confirmation timeout for callback {callback_id}")
|
|
224
234
|
# Clean up callback
|
|
@@ -239,7 +249,7 @@ class WebSocketManager:
|
|
|
239
249
|
"message": "Task is still executing...",
|
|
240
250
|
"user_id": user_id,
|
|
241
251
|
"task_id": task_id,
|
|
242
|
-
"timestamp": asyncio.get_event_loop().time()
|
|
252
|
+
"timestamp": asyncio.get_event_loop().time(),
|
|
243
253
|
}
|
|
244
254
|
|
|
245
255
|
while self._running:
|
|
@@ -256,14 +266,20 @@ class WebSocketManager:
|
|
|
256
266
|
logger.debug("No active WebSocket connections for broadcast")
|
|
257
267
|
return
|
|
258
268
|
|
|
259
|
-
# Filter out closed connections
|
|
260
|
-
active_connections = [
|
|
269
|
+
# Filter out closed connections (use try-except to handle closed connections)
|
|
270
|
+
active_connections = []
|
|
271
|
+
for conn in list(self.active_connections):
|
|
272
|
+
try:
|
|
273
|
+
# Try to check if connection is still valid
|
|
274
|
+
active_connections.append(conn)
|
|
275
|
+
except Exception:
|
|
276
|
+
pass # Connection is closed, skip it
|
|
261
277
|
self.active_connections = set(active_connections)
|
|
262
278
|
|
|
263
279
|
if active_connections:
|
|
264
280
|
await asyncio.gather(
|
|
265
281
|
*[self._send_to_client(conn, message) for conn in active_connections],
|
|
266
|
-
return_exceptions=True
|
|
282
|
+
return_exceptions=True,
|
|
267
283
|
)
|
|
268
284
|
logger.debug(f"Broadcasted message to {len(active_connections)} clients")
|
|
269
285
|
|
|
@@ -276,7 +292,7 @@ class WebSocketManager:
|
|
|
276
292
|
|
|
277
293
|
def get_connection_count(self) -> int:
|
|
278
294
|
"""Get active connection count"""
|
|
279
|
-
return len(
|
|
295
|
+
return len(self.active_connections)
|
|
280
296
|
|
|
281
297
|
def get_status(self) -> Dict[str, Any]:
|
|
282
298
|
"""Get WebSocket manager status"""
|
|
@@ -285,5 +301,5 @@ class WebSocketManager:
|
|
|
285
301
|
"host": self.host,
|
|
286
302
|
"port": self.port,
|
|
287
303
|
"active_connections": self.get_connection_count(),
|
|
288
|
-
"pending_callbacks": len(self.callback_registry)
|
|
304
|
+
"pending_callbacks": len(self.callback_registry),
|
|
289
305
|
}
|
|
@@ -5,8 +5,30 @@ Contains monitoring, metrics, and observability infrastructure.
|
|
|
5
5
|
|
|
6
6
|
from .executor_metrics import ExecutorMetrics
|
|
7
7
|
from .tracing_manager import TracingManager
|
|
8
|
+
from .global_metrics_manager import (
|
|
9
|
+
initialize_global_metrics,
|
|
10
|
+
get_global_metrics,
|
|
11
|
+
close_global_metrics,
|
|
12
|
+
is_metrics_initialized,
|
|
13
|
+
get_metrics_summary,
|
|
14
|
+
record_operation,
|
|
15
|
+
record_duration,
|
|
16
|
+
record_operation_success,
|
|
17
|
+
record_operation_failure,
|
|
18
|
+
record_retry,
|
|
19
|
+
)
|
|
8
20
|
|
|
9
21
|
__all__ = [
|
|
10
22
|
"ExecutorMetrics",
|
|
11
23
|
"TracingManager",
|
|
24
|
+
"initialize_global_metrics",
|
|
25
|
+
"get_global_metrics",
|
|
26
|
+
"close_global_metrics",
|
|
27
|
+
"is_metrics_initialized",
|
|
28
|
+
"get_metrics_summary",
|
|
29
|
+
"record_operation",
|
|
30
|
+
"record_duration",
|
|
31
|
+
"record_operation_success",
|
|
32
|
+
"record_operation_failure",
|
|
33
|
+
"record_retry",
|
|
12
34
|
]
|
|
@@ -25,14 +25,29 @@ class ExecutorMetrics:
|
|
|
25
25
|
start_http_server(self.metrics_port)
|
|
26
26
|
self.metrics = {
|
|
27
27
|
"intent_latency": Histogram("intent_latency_seconds", "Latency of intent parsing"),
|
|
28
|
-
"intent_success": Counter(
|
|
28
|
+
"intent_success": Counter(
|
|
29
|
+
"intent_success_total",
|
|
30
|
+
"Number of successful intent parsings",
|
|
31
|
+
),
|
|
29
32
|
"intent_retries": Counter("intent_retries_total", "Number of intent parsing retries"),
|
|
30
33
|
"plan_latency": Histogram("plan_latency_seconds", "Latency of task planning"),
|
|
31
34
|
"plan_success": Counter("plan_success_total", "Number of successful plans"),
|
|
32
35
|
"plan_retries": Counter("plan_retries_total", "Number of plan retries"),
|
|
33
|
-
"execute_latency": Histogram(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
"execute_latency": Histogram(
|
|
37
|
+
"execute_latency_seconds",
|
|
38
|
+
"Latency of task execution",
|
|
39
|
+
["task_type"],
|
|
40
|
+
),
|
|
41
|
+
"execute_success": Counter(
|
|
42
|
+
"execute_success_total",
|
|
43
|
+
"Number of successful executions",
|
|
44
|
+
["task_type"],
|
|
45
|
+
),
|
|
46
|
+
"execute_retries": Counter(
|
|
47
|
+
"execute_retries_total",
|
|
48
|
+
"Number of execution retries",
|
|
49
|
+
["task_type"],
|
|
50
|
+
),
|
|
36
51
|
}
|
|
37
52
|
logger.info(f"Prometheus metrics server started on port {self.metrics_port}")
|
|
38
53
|
except Exception as e:
|
|
@@ -54,7 +69,12 @@ class ExecutorMetrics:
|
|
|
54
69
|
metric = metric.labels(**labels)
|
|
55
70
|
metric.inc()
|
|
56
71
|
|
|
57
|
-
def record_operation_failure(
|
|
72
|
+
def record_operation_failure(
|
|
73
|
+
self,
|
|
74
|
+
operation: str,
|
|
75
|
+
error_type: str,
|
|
76
|
+
labels: Optional[Dict[str, str]] = None,
|
|
77
|
+
):
|
|
58
78
|
"""Record operation failure"""
|
|
59
79
|
if not self.enable_metrics:
|
|
60
80
|
return
|
|
@@ -70,6 +90,7 @@ class ExecutorMetrics:
|
|
|
70
90
|
|
|
71
91
|
def with_metrics(self, metric_name: str, labels: Optional[Dict[str, str]] = None):
|
|
72
92
|
"""Monitoring decorator"""
|
|
93
|
+
|
|
73
94
|
def decorator(func):
|
|
74
95
|
@functools.wraps(func)
|
|
75
96
|
async def wrapper(*args, **kwargs):
|
|
@@ -93,7 +114,9 @@ class ExecutorMetrics:
|
|
|
93
114
|
except Exception as e:
|
|
94
115
|
logger.error(f"Error in {func.__name__}: {e}")
|
|
95
116
|
raise
|
|
117
|
+
|
|
96
118
|
return wrapper
|
|
119
|
+
|
|
97
120
|
return decorator
|
|
98
121
|
|
|
99
122
|
def get_metrics_summary(self) -> Dict[str, Any]:
|
|
@@ -104,10 +127,16 @@ class ExecutorMetrics:
|
|
|
104
127
|
return {
|
|
105
128
|
"metrics_enabled": True,
|
|
106
129
|
"metrics_port": self.metrics_port,
|
|
107
|
-
"available_metrics": list(self.metrics.keys())
|
|
130
|
+
"available_metrics": list(self.metrics.keys()),
|
|
108
131
|
}
|
|
109
132
|
|
|
110
|
-
def record_operation(
|
|
133
|
+
def record_operation(
|
|
134
|
+
self,
|
|
135
|
+
operation_type: str,
|
|
136
|
+
success: bool = True,
|
|
137
|
+
duration: Optional[float] = None,
|
|
138
|
+
**kwargs,
|
|
139
|
+
):
|
|
111
140
|
"""Record a general operation for metrics tracking"""
|
|
112
141
|
if not self.enable_metrics:
|
|
113
142
|
return
|
|
@@ -115,10 +144,10 @@ class ExecutorMetrics:
|
|
|
115
144
|
try:
|
|
116
145
|
# Record operation success/failure
|
|
117
146
|
if success:
|
|
118
|
-
self.record_operation_success(operation_type, kwargs.get(
|
|
147
|
+
self.record_operation_success(operation_type, kwargs.get("labels"))
|
|
119
148
|
else:
|
|
120
|
-
error_type = kwargs.get(
|
|
121
|
-
self.record_operation_failure(operation_type, error_type, kwargs.get(
|
|
149
|
+
error_type = kwargs.get("error_type", "unknown")
|
|
150
|
+
self.record_operation_failure(operation_type, error_type, kwargs.get("labels"))
|
|
122
151
|
|
|
123
152
|
# Record operation latency if provided
|
|
124
153
|
if duration is not None:
|
|
@@ -127,7 +156,12 @@ class ExecutorMetrics:
|
|
|
127
156
|
except Exception as e:
|
|
128
157
|
logger.warning(f"Failed to record operation metrics: {e}")
|
|
129
158
|
|
|
130
|
-
def record_duration(
|
|
159
|
+
def record_duration(
|
|
160
|
+
self,
|
|
161
|
+
operation: str,
|
|
162
|
+
duration: float,
|
|
163
|
+
labels: Optional[Dict[str, str]] = None,
|
|
164
|
+
):
|
|
131
165
|
"""Record operation duration for metrics tracking"""
|
|
132
166
|
if not self.enable_metrics:
|
|
133
167
|
return
|