aiecs 1.0.1__py3-none-any.whl → 1.7.6__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 +399 -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 +3870 -0
- aiecs/domain/agent/exceptions.py +99 -0
- aiecs/domain/agent/graph_aware_mixin.py +569 -0
- aiecs/domain/agent/hybrid_agent.py +1435 -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 +884 -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 +364 -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 +224 -36
- aiecs/llm/client_resolver.py +155 -0
- aiecs/llm/clients/__init__.py +38 -0
- aiecs/llm/clients/base_client.py +324 -0
- aiecs/llm/clients/google_function_calling_mixin.py +457 -0
- aiecs/llm/clients/googleai_client.py +241 -0
- aiecs/llm/clients/openai_client.py +158 -0
- aiecs/llm/clients/openai_compatible_mixin.py +367 -0
- aiecs/llm/clients/vertex_client.py +897 -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 +1323 -0
- aiecs/tools/docs/document_layout_tool.py +1160 -0
- aiecs/tools/docs/document_parser_tool.py +1011 -0
- aiecs/tools/docs/document_writer_tool.py +1829 -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 +175 -131
- 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.6.dist-info}/METADATA +52 -15
- aiecs-1.7.6.dist-info/RECORD +337 -0
- aiecs-1.7.6.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.6.dist-info}/WHEEL +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Health Checks for Graph Storage Backends
|
|
3
|
+
|
|
4
|
+
Provides health check endpoints and monitoring for graph storage backends,
|
|
5
|
+
enabling production-ready health monitoring and alerting.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import asyncio
|
|
10
|
+
from typing import Dict, Any, Optional, List
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from datetime import datetime, timedelta
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class HealthStatus(str, Enum):
|
|
19
|
+
"""Health check status"""
|
|
20
|
+
|
|
21
|
+
HEALTHY = "healthy"
|
|
22
|
+
DEGRADED = "degraded"
|
|
23
|
+
UNHEALTHY = "unhealthy"
|
|
24
|
+
UNKNOWN = "unknown"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class HealthCheckResult:
|
|
29
|
+
"""
|
|
30
|
+
Result of a health check
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
```python
|
|
34
|
+
result = HealthCheckResult(
|
|
35
|
+
status=HealthStatus.HEALTHY,
|
|
36
|
+
message="All checks passed",
|
|
37
|
+
response_time_ms=12.5,
|
|
38
|
+
details={"connection_pool": "ok", "query_test": "ok"}
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
status: HealthStatus
|
|
44
|
+
message: str
|
|
45
|
+
response_time_ms: float = 0.0
|
|
46
|
+
timestamp: Optional[datetime] = None
|
|
47
|
+
details: Optional[Dict[str, Any]] = None
|
|
48
|
+
error: Optional[str] = None
|
|
49
|
+
|
|
50
|
+
def __post_init__(self):
|
|
51
|
+
if self.timestamp is None:
|
|
52
|
+
self.timestamp = datetime.utcnow()
|
|
53
|
+
if self.details is None:
|
|
54
|
+
self.details = {}
|
|
55
|
+
|
|
56
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
57
|
+
"""Convert to dictionary"""
|
|
58
|
+
return {
|
|
59
|
+
"status": self.status.value,
|
|
60
|
+
"message": self.message,
|
|
61
|
+
"response_time_ms": round(self.response_time_ms, 2),
|
|
62
|
+
"timestamp": self.timestamp.isoformat() if self.timestamp is not None else None,
|
|
63
|
+
"details": self.details,
|
|
64
|
+
"error": self.error,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
def is_healthy(self) -> bool:
|
|
68
|
+
"""Check if status is healthy"""
|
|
69
|
+
return self.status == HealthStatus.HEALTHY
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class HealthChecker:
|
|
73
|
+
"""
|
|
74
|
+
Health checker for graph storage backends
|
|
75
|
+
|
|
76
|
+
Performs comprehensive health checks including:
|
|
77
|
+
- Connection availability
|
|
78
|
+
- Query execution
|
|
79
|
+
- Response time
|
|
80
|
+
- Resource availability
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
```python
|
|
84
|
+
checker = HealthChecker(store)
|
|
85
|
+
result = await checker.check_health()
|
|
86
|
+
|
|
87
|
+
if result.is_healthy():
|
|
88
|
+
print("Store is healthy")
|
|
89
|
+
else:
|
|
90
|
+
print(f"Store is {result.status}: {result.message}")
|
|
91
|
+
```
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
store: Any,
|
|
97
|
+
timeout_seconds: float = 5.0,
|
|
98
|
+
query_timeout_ms: float = 1000.0,
|
|
99
|
+
):
|
|
100
|
+
"""
|
|
101
|
+
Initialize health checker
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
store: Graph store instance
|
|
105
|
+
timeout_seconds: Overall health check timeout
|
|
106
|
+
query_timeout_ms: Query execution timeout
|
|
107
|
+
"""
|
|
108
|
+
self.store = store
|
|
109
|
+
self.timeout_seconds = timeout_seconds
|
|
110
|
+
self.query_timeout_ms = query_timeout_ms
|
|
111
|
+
|
|
112
|
+
async def check_health(self) -> HealthCheckResult:
|
|
113
|
+
"""
|
|
114
|
+
Perform comprehensive health check
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
HealthCheckResult with status and details
|
|
118
|
+
"""
|
|
119
|
+
start_time = asyncio.get_event_loop().time()
|
|
120
|
+
details: Dict[str, Any] = {}
|
|
121
|
+
errors: List[str] = []
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
# Check 1: Store initialization
|
|
125
|
+
if not hasattr(self.store, "_is_initialized") or not self.store._is_initialized:
|
|
126
|
+
return HealthCheckResult(
|
|
127
|
+
status=HealthStatus.UNHEALTHY,
|
|
128
|
+
message="Store not initialized",
|
|
129
|
+
response_time_ms=0.0,
|
|
130
|
+
error="Store not initialized",
|
|
131
|
+
)
|
|
132
|
+
details["initialized"] = True
|
|
133
|
+
|
|
134
|
+
# Check 2: Connection availability
|
|
135
|
+
connection_ok = await self._check_connection()
|
|
136
|
+
details["connection"] = "ok" if connection_ok else "failed"
|
|
137
|
+
if not connection_ok:
|
|
138
|
+
errors.append("Connection check failed")
|
|
139
|
+
|
|
140
|
+
# Check 3: Query execution
|
|
141
|
+
query_ok = await self._check_query_execution()
|
|
142
|
+
details["query_execution"] = "ok" if query_ok else "failed"
|
|
143
|
+
if not query_ok:
|
|
144
|
+
errors.append("Query execution failed")
|
|
145
|
+
|
|
146
|
+
# Check 4: Response time
|
|
147
|
+
response_time = await self._check_response_time()
|
|
148
|
+
details["response_time_ms"] = response_time
|
|
149
|
+
if response_time > self.query_timeout_ms:
|
|
150
|
+
errors.append(f"Response time too high: {response_time}ms")
|
|
151
|
+
|
|
152
|
+
# Check 5: Resource availability (if applicable)
|
|
153
|
+
if hasattr(self.store, "pool"):
|
|
154
|
+
pool_ok = await self._check_connection_pool()
|
|
155
|
+
details["connection_pool"] = "ok" if pool_ok else "degraded"
|
|
156
|
+
if not pool_ok:
|
|
157
|
+
errors.append("Connection pool issues")
|
|
158
|
+
|
|
159
|
+
# Determine status
|
|
160
|
+
if errors:
|
|
161
|
+
if len(errors) >= 2:
|
|
162
|
+
status = HealthStatus.UNHEALTHY
|
|
163
|
+
else:
|
|
164
|
+
status = HealthStatus.DEGRADED
|
|
165
|
+
message = "; ".join(errors)
|
|
166
|
+
else:
|
|
167
|
+
status = HealthStatus.HEALTHY
|
|
168
|
+
message = "All health checks passed"
|
|
169
|
+
|
|
170
|
+
response_time_ms = (asyncio.get_event_loop().time() - start_time) * 1000
|
|
171
|
+
|
|
172
|
+
return HealthCheckResult(
|
|
173
|
+
status=status,
|
|
174
|
+
message=message,
|
|
175
|
+
response_time_ms=response_time_ms,
|
|
176
|
+
details=details,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
response_time_ms = (asyncio.get_event_loop().time() - start_time) * 1000
|
|
181
|
+
logger.error(f"Health check failed: {e}", exc_info=True)
|
|
182
|
+
|
|
183
|
+
return HealthCheckResult(
|
|
184
|
+
status=HealthStatus.UNHEALTHY,
|
|
185
|
+
message=f"Health check exception: {str(e)}",
|
|
186
|
+
response_time_ms=response_time_ms,
|
|
187
|
+
error=str(e),
|
|
188
|
+
details=details,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
async def _check_connection(self) -> bool:
|
|
192
|
+
"""Check if connection is available"""
|
|
193
|
+
try:
|
|
194
|
+
# Try a simple operation
|
|
195
|
+
if hasattr(self.store, "get_stats"):
|
|
196
|
+
await asyncio.wait_for(self.store.get_stats(), timeout=self.timeout_seconds)
|
|
197
|
+
return True
|
|
198
|
+
return False
|
|
199
|
+
except Exception as e:
|
|
200
|
+
logger.debug(f"Connection check failed: {e}")
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
async def _check_query_execution(self) -> bool:
|
|
204
|
+
"""Check if queries can be executed"""
|
|
205
|
+
try:
|
|
206
|
+
# Try a simple query (get_stats or similar)
|
|
207
|
+
if hasattr(self.store, "get_stats"):
|
|
208
|
+
await asyncio.wait_for(self.store.get_stats(), timeout=self.timeout_seconds)
|
|
209
|
+
return True
|
|
210
|
+
return False
|
|
211
|
+
except Exception as e:
|
|
212
|
+
logger.debug(f"Query execution check failed: {e}")
|
|
213
|
+
return False
|
|
214
|
+
|
|
215
|
+
async def _check_response_time(self) -> float:
|
|
216
|
+
"""Check average response time"""
|
|
217
|
+
try:
|
|
218
|
+
times = []
|
|
219
|
+
for _ in range(3):
|
|
220
|
+
start = asyncio.get_event_loop().time()
|
|
221
|
+
if hasattr(self.store, "get_stats"):
|
|
222
|
+
await self.store.get_stats()
|
|
223
|
+
elapsed = (asyncio.get_event_loop().time() - start) * 1000
|
|
224
|
+
times.append(elapsed)
|
|
225
|
+
|
|
226
|
+
return sum(times) / len(times) if times else 0.0
|
|
227
|
+
except Exception:
|
|
228
|
+
return float("inf")
|
|
229
|
+
|
|
230
|
+
async def _check_connection_pool(self) -> bool:
|
|
231
|
+
"""Check connection pool health"""
|
|
232
|
+
try:
|
|
233
|
+
if hasattr(self.store, "pool"):
|
|
234
|
+
pool = self.store.pool
|
|
235
|
+
|
|
236
|
+
# Check pool size
|
|
237
|
+
if hasattr(pool, "get_size"):
|
|
238
|
+
size = pool.get_size()
|
|
239
|
+
free = pool.get_idle_size()
|
|
240
|
+
|
|
241
|
+
# Pool is healthy if not completely exhausted
|
|
242
|
+
return free > 0 or size < pool.get_max_size()
|
|
243
|
+
|
|
244
|
+
# If we can't check, assume OK
|
|
245
|
+
return True
|
|
246
|
+
return True
|
|
247
|
+
except Exception:
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
async def check_liveness(self) -> bool:
|
|
251
|
+
"""
|
|
252
|
+
Quick liveness check (faster than full health check)
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
True if store is alive, False otherwise
|
|
256
|
+
"""
|
|
257
|
+
try:
|
|
258
|
+
if hasattr(self.store, "get_stats"):
|
|
259
|
+
await asyncio.wait_for(self.store.get_stats(), timeout=1.0)
|
|
260
|
+
return True
|
|
261
|
+
return False
|
|
262
|
+
except Exception:
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
async def check_readiness(self) -> bool:
|
|
266
|
+
"""
|
|
267
|
+
Readiness check (can handle requests)
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
True if store is ready, False otherwise
|
|
271
|
+
"""
|
|
272
|
+
result = await self.check_health()
|
|
273
|
+
return result.status in [HealthStatus.HEALTHY, HealthStatus.DEGRADED]
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class HealthMonitor:
|
|
277
|
+
"""
|
|
278
|
+
Continuous health monitoring
|
|
279
|
+
|
|
280
|
+
Monitors health status over time and tracks metrics.
|
|
281
|
+
|
|
282
|
+
Example:
|
|
283
|
+
```python
|
|
284
|
+
monitor = HealthMonitor(checker, interval_seconds=30)
|
|
285
|
+
await monitor.start()
|
|
286
|
+
|
|
287
|
+
# Get current status
|
|
288
|
+
status = monitor.get_current_status()
|
|
289
|
+
|
|
290
|
+
# Get health history
|
|
291
|
+
history = monitor.get_health_history()
|
|
292
|
+
```
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
def __init__(self, checker: HealthChecker, interval_seconds: float = 30.0):
|
|
296
|
+
"""
|
|
297
|
+
Initialize health monitor
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
checker: Health checker instance
|
|
301
|
+
interval_seconds: Check interval in seconds
|
|
302
|
+
"""
|
|
303
|
+
self.checker = checker
|
|
304
|
+
self.interval_seconds = interval_seconds
|
|
305
|
+
self.health_history: List[HealthCheckResult] = []
|
|
306
|
+
self.max_history = 100
|
|
307
|
+
self._monitoring = False
|
|
308
|
+
self._task: Optional[asyncio.Task[None]] = None
|
|
309
|
+
|
|
310
|
+
async def start(self) -> None:
|
|
311
|
+
"""Start continuous monitoring"""
|
|
312
|
+
if self._monitoring:
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
self._monitoring = True
|
|
316
|
+
self._task = asyncio.create_task(self._monitor_loop())
|
|
317
|
+
logger.info("Health monitoring started")
|
|
318
|
+
|
|
319
|
+
async def stop(self) -> None:
|
|
320
|
+
"""Stop continuous monitoring"""
|
|
321
|
+
self._monitoring = False
|
|
322
|
+
if self._task:
|
|
323
|
+
self._task.cancel()
|
|
324
|
+
try:
|
|
325
|
+
await self._task
|
|
326
|
+
except asyncio.CancelledError:
|
|
327
|
+
pass
|
|
328
|
+
logger.info("Health monitoring stopped")
|
|
329
|
+
|
|
330
|
+
async def _monitor_loop(self) -> None:
|
|
331
|
+
"""Monitoring loop"""
|
|
332
|
+
while self._monitoring:
|
|
333
|
+
try:
|
|
334
|
+
result = await self.checker.check_health()
|
|
335
|
+
self.health_history.append(result)
|
|
336
|
+
|
|
337
|
+
# Keep only recent history
|
|
338
|
+
if len(self.health_history) > self.max_history:
|
|
339
|
+
self.health_history.pop(0)
|
|
340
|
+
|
|
341
|
+
# Log unhealthy status
|
|
342
|
+
if not result.is_healthy():
|
|
343
|
+
logger.warning(f"Health check failed: {result.status} - {result.message}")
|
|
344
|
+
|
|
345
|
+
await asyncio.sleep(self.interval_seconds)
|
|
346
|
+
except asyncio.CancelledError:
|
|
347
|
+
break
|
|
348
|
+
except Exception as e:
|
|
349
|
+
logger.error(f"Health monitoring error: {e}", exc_info=True)
|
|
350
|
+
await asyncio.sleep(self.interval_seconds)
|
|
351
|
+
|
|
352
|
+
def get_current_status(self) -> Optional[HealthCheckResult]:
|
|
353
|
+
"""Get most recent health check result"""
|
|
354
|
+
return self.health_history[-1] if self.health_history else None
|
|
355
|
+
|
|
356
|
+
def get_health_history(self, limit: int = 10) -> List[HealthCheckResult]:
|
|
357
|
+
"""Get recent health check history"""
|
|
358
|
+
return self.health_history[-limit:]
|
|
359
|
+
|
|
360
|
+
def get_uptime_percentage(self, window_minutes: int = 60) -> float:
|
|
361
|
+
"""
|
|
362
|
+
Calculate uptime percentage over time window
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
window_minutes: Time window in minutes
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Uptime percentage (0-100)
|
|
369
|
+
"""
|
|
370
|
+
cutoff = datetime.utcnow() - timedelta(minutes=window_minutes)
|
|
371
|
+
|
|
372
|
+
recent = [r for r in self.health_history if r.timestamp is not None and r.timestamp >= cutoff]
|
|
373
|
+
|
|
374
|
+
if not recent:
|
|
375
|
+
return 0.0
|
|
376
|
+
|
|
377
|
+
healthy_count = sum(1 for r in recent if r.is_healthy())
|
|
378
|
+
return (healthy_count / len(recent)) * 100.0
|