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,378 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom Query Executor
|
|
3
|
+
|
|
4
|
+
Executes custom queries with pattern matching, projection, and aggregation.
|
|
5
|
+
|
|
6
|
+
Phase: 3.3 - Full Custom Query Execution
|
|
7
|
+
Version: 1.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import List, Dict, Any, Optional
|
|
11
|
+
from aiecs.domain.knowledge_graph.models.query import GraphQuery
|
|
12
|
+
from aiecs.infrastructure.graph_storage.base import GraphStore
|
|
13
|
+
from aiecs.application.knowledge_graph.pattern_matching.pattern_matcher import (
|
|
14
|
+
PatternMatcher,
|
|
15
|
+
PatternMatch,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CustomQueryExecutor:
|
|
20
|
+
"""
|
|
21
|
+
Custom Query Executor
|
|
22
|
+
|
|
23
|
+
Executes custom queries with:
|
|
24
|
+
- Pattern matching
|
|
25
|
+
- Result projection
|
|
26
|
+
- Aggregation
|
|
27
|
+
- Grouping
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, graph_store: GraphStore):
|
|
31
|
+
"""
|
|
32
|
+
Initialize custom query executor
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
graph_store: Graph storage backend
|
|
36
|
+
"""
|
|
37
|
+
self.graph_store = graph_store
|
|
38
|
+
self.pattern_matcher = PatternMatcher(graph_store)
|
|
39
|
+
|
|
40
|
+
async def execute(
|
|
41
|
+
self, query: GraphQuery, start_entity_id: Optional[str] = None
|
|
42
|
+
) -> Dict[str, Any]:
|
|
43
|
+
"""
|
|
44
|
+
Execute a custom query
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
query: Graph query with custom patterns
|
|
48
|
+
start_entity_id: Optional starting entity ID
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Query results with matches, projections, and aggregations
|
|
52
|
+
"""
|
|
53
|
+
# Execute pattern matching
|
|
54
|
+
matches = await self._execute_pattern_matching(query, start_entity_id)
|
|
55
|
+
|
|
56
|
+
# Apply projection if specified
|
|
57
|
+
if query.projection:
|
|
58
|
+
projected_results = self._apply_projection(matches, query.projection)
|
|
59
|
+
else:
|
|
60
|
+
projected_results = [self._match_to_dict(match) for match in matches]
|
|
61
|
+
|
|
62
|
+
# Apply aggregation if specified
|
|
63
|
+
if query.aggregations:
|
|
64
|
+
aggregated_results = self._apply_aggregation(
|
|
65
|
+
projected_results, query.aggregations, query.group_by
|
|
66
|
+
)
|
|
67
|
+
return {
|
|
68
|
+
"matches": len(matches),
|
|
69
|
+
"results": aggregated_results,
|
|
70
|
+
"aggregated": True,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
"matches": len(matches),
|
|
75
|
+
"results": projected_results,
|
|
76
|
+
"aggregated": False,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async def _execute_pattern_matching(
|
|
80
|
+
self, query: GraphQuery, start_entity_id: Optional[str]
|
|
81
|
+
) -> List[PatternMatch]:
|
|
82
|
+
"""
|
|
83
|
+
Execute pattern matching based on query
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
query: Graph query
|
|
87
|
+
start_entity_id: Optional starting entity ID
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
List of pattern matches
|
|
91
|
+
"""
|
|
92
|
+
max_matches = query.max_results or 100
|
|
93
|
+
|
|
94
|
+
# Single pattern
|
|
95
|
+
if query.pattern:
|
|
96
|
+
return await self.pattern_matcher.match_pattern(
|
|
97
|
+
query.pattern, start_entity_id, max_matches
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Multiple patterns (required)
|
|
101
|
+
if query.patterns:
|
|
102
|
+
if query.optional_patterns:
|
|
103
|
+
# Required + optional patterns
|
|
104
|
+
return await self.pattern_matcher.match_optional_patterns(
|
|
105
|
+
query.patterns,
|
|
106
|
+
query.optional_patterns,
|
|
107
|
+
start_entity_id,
|
|
108
|
+
max_matches,
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
# Only required patterns
|
|
112
|
+
return await self.pattern_matcher.match_multiple_patterns(
|
|
113
|
+
query.patterns, start_entity_id, max_matches
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# No patterns specified
|
|
117
|
+
return []
|
|
118
|
+
|
|
119
|
+
def _match_to_dict(self, match: PatternMatch) -> Dict[str, Any]:
|
|
120
|
+
"""
|
|
121
|
+
Convert pattern match to dictionary
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
match: Pattern match
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Dictionary representation
|
|
128
|
+
"""
|
|
129
|
+
return {
|
|
130
|
+
"entities": [
|
|
131
|
+
{
|
|
132
|
+
"id": entity.id,
|
|
133
|
+
"type": entity.entity_type,
|
|
134
|
+
"properties": entity.properties,
|
|
135
|
+
}
|
|
136
|
+
for entity in match.entities
|
|
137
|
+
],
|
|
138
|
+
"relations": [
|
|
139
|
+
{
|
|
140
|
+
"id": relation.id,
|
|
141
|
+
"type": relation.relation_type,
|
|
142
|
+
"source": relation.source_id,
|
|
143
|
+
"target": relation.target_id,
|
|
144
|
+
"properties": relation.properties,
|
|
145
|
+
}
|
|
146
|
+
for relation in match.relations
|
|
147
|
+
],
|
|
148
|
+
"score": match.score,
|
|
149
|
+
"bindings": match.bindings,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
def _apply_projection(
|
|
153
|
+
self, matches: List[PatternMatch], projection: List[str]
|
|
154
|
+
) -> List[Dict[str, Any]]:
|
|
155
|
+
"""
|
|
156
|
+
Apply projection to matches
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
matches: Pattern matches
|
|
160
|
+
projection: Fields to project
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Projected results
|
|
164
|
+
"""
|
|
165
|
+
projected = []
|
|
166
|
+
|
|
167
|
+
for match in matches:
|
|
168
|
+
result = {}
|
|
169
|
+
|
|
170
|
+
for field in projection:
|
|
171
|
+
value = self._extract_field(match, field)
|
|
172
|
+
result[field] = value
|
|
173
|
+
|
|
174
|
+
projected.append(result)
|
|
175
|
+
|
|
176
|
+
return projected
|
|
177
|
+
|
|
178
|
+
def _extract_field(self, match: PatternMatch, field: str) -> Any:
|
|
179
|
+
"""
|
|
180
|
+
Extract a field value from a match
|
|
181
|
+
|
|
182
|
+
Supports dot notation for nested fields:
|
|
183
|
+
- "id" -> first entity's ID
|
|
184
|
+
- "entities[0].name" -> first entity's name
|
|
185
|
+
- "entities[0].properties.age" -> first entity's age property
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
match: Pattern match
|
|
189
|
+
field: Field path to extract
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Field value
|
|
193
|
+
"""
|
|
194
|
+
# Handle simple fields
|
|
195
|
+
if field == "score":
|
|
196
|
+
return match.score
|
|
197
|
+
|
|
198
|
+
if field == "entity_count":
|
|
199
|
+
return len(match.entities)
|
|
200
|
+
|
|
201
|
+
if field == "relation_count":
|
|
202
|
+
return len(match.relations)
|
|
203
|
+
|
|
204
|
+
# Handle entity fields
|
|
205
|
+
if field.startswith("entities"):
|
|
206
|
+
# Parse entities[0].name or entities[0].properties.age
|
|
207
|
+
parts = field.split(".")
|
|
208
|
+
|
|
209
|
+
# Extract index
|
|
210
|
+
if "[" in parts[0]:
|
|
211
|
+
index_str = parts[0].split("[")[1].split("]")[0]
|
|
212
|
+
index = int(index_str)
|
|
213
|
+
|
|
214
|
+
if index >= len(match.entities):
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
entity = match.entities[index]
|
|
218
|
+
|
|
219
|
+
# Extract nested field
|
|
220
|
+
if len(parts) == 1:
|
|
221
|
+
return {
|
|
222
|
+
"id": entity.id,
|
|
223
|
+
"type": entity.entity_type,
|
|
224
|
+
"properties": entity.properties,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if parts[1] == "id":
|
|
228
|
+
return entity.id
|
|
229
|
+
elif parts[1] == "type":
|
|
230
|
+
return entity.entity_type
|
|
231
|
+
elif parts[1] == "properties" and len(parts) > 2:
|
|
232
|
+
return entity.properties.get(parts[2])
|
|
233
|
+
elif parts[1] == "properties":
|
|
234
|
+
return entity.properties
|
|
235
|
+
|
|
236
|
+
# Handle relation fields
|
|
237
|
+
if field.startswith("relations"):
|
|
238
|
+
parts = field.split(".")
|
|
239
|
+
|
|
240
|
+
if "[" in parts[0]:
|
|
241
|
+
index_str = parts[0].split("[")[1].split("]")[0]
|
|
242
|
+
index = int(index_str)
|
|
243
|
+
|
|
244
|
+
if index >= len(match.relations):
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
relation = match.relations[index]
|
|
248
|
+
|
|
249
|
+
if len(parts) == 1:
|
|
250
|
+
return {
|
|
251
|
+
"id": relation.id,
|
|
252
|
+
"type": relation.relation_type,
|
|
253
|
+
"source": relation.source_id,
|
|
254
|
+
"target": relation.target_id,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if parts[1] == "id":
|
|
258
|
+
return relation.id
|
|
259
|
+
elif parts[1] == "type":
|
|
260
|
+
return relation.relation_type
|
|
261
|
+
elif parts[1] == "source":
|
|
262
|
+
return relation.source_id
|
|
263
|
+
elif parts[1] == "target":
|
|
264
|
+
return relation.target_id
|
|
265
|
+
elif parts[1] == "properties" and len(parts) > 2:
|
|
266
|
+
return relation.properties.get(parts[2])
|
|
267
|
+
|
|
268
|
+
# Handle bindings
|
|
269
|
+
if field.startswith("bindings."):
|
|
270
|
+
binding_name = field.split(".")[1]
|
|
271
|
+
return match.bindings.get(binding_name)
|
|
272
|
+
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
def _apply_aggregation(
|
|
276
|
+
self,
|
|
277
|
+
results: List[Dict[str, Any]],
|
|
278
|
+
aggregations: Dict[str, str],
|
|
279
|
+
group_by: Optional[List[str]] = None,
|
|
280
|
+
) -> List[Dict[str, Any]]:
|
|
281
|
+
"""
|
|
282
|
+
Apply aggregations to results
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
results: Projected results
|
|
286
|
+
aggregations: Aggregation functions (e.g., {"count": "COUNT", "avg_age": "AVG(age)"})
|
|
287
|
+
group_by: Optional fields to group by
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Aggregated results
|
|
291
|
+
"""
|
|
292
|
+
if group_by:
|
|
293
|
+
# Group results
|
|
294
|
+
groups = {}
|
|
295
|
+
|
|
296
|
+
for result in results:
|
|
297
|
+
# Create group key
|
|
298
|
+
key_parts = []
|
|
299
|
+
for field in group_by:
|
|
300
|
+
key_parts.append(str(result.get(field, "")))
|
|
301
|
+
key = tuple(key_parts)
|
|
302
|
+
|
|
303
|
+
if key not in groups:
|
|
304
|
+
groups[key] = []
|
|
305
|
+
groups[key].append(result)
|
|
306
|
+
|
|
307
|
+
# Aggregate each group
|
|
308
|
+
aggregated = []
|
|
309
|
+
|
|
310
|
+
for key, group_results in groups.items():
|
|
311
|
+
agg_result = {}
|
|
312
|
+
|
|
313
|
+
# Add group by fields
|
|
314
|
+
for i, field in enumerate(group_by):
|
|
315
|
+
agg_result[field] = key[i]
|
|
316
|
+
|
|
317
|
+
# Apply aggregations
|
|
318
|
+
for agg_name, agg_func in aggregations.items():
|
|
319
|
+
agg_result[agg_name] = self._compute_aggregation(group_results, agg_func)
|
|
320
|
+
|
|
321
|
+
aggregated.append(agg_result)
|
|
322
|
+
|
|
323
|
+
return aggregated
|
|
324
|
+
else:
|
|
325
|
+
# Aggregate all results
|
|
326
|
+
agg_result = {}
|
|
327
|
+
|
|
328
|
+
for agg_name, agg_func in aggregations.items():
|
|
329
|
+
agg_result[agg_name] = self._compute_aggregation(results, agg_func)
|
|
330
|
+
|
|
331
|
+
return [agg_result]
|
|
332
|
+
|
|
333
|
+
def _compute_aggregation(self, results: List[Dict[str, Any]], agg_func: str) -> Any:
|
|
334
|
+
"""
|
|
335
|
+
Compute an aggregation function
|
|
336
|
+
|
|
337
|
+
Supports:
|
|
338
|
+
- COUNT: Count of results
|
|
339
|
+
- SUM(field): Sum of field values
|
|
340
|
+
- AVG(field): Average of field values
|
|
341
|
+
- MIN(field): Minimum field value
|
|
342
|
+
- MAX(field): Maximum field value
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
results: Results to aggregate
|
|
346
|
+
agg_func: Aggregation function string
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
Aggregated value
|
|
350
|
+
"""
|
|
351
|
+
if agg_func == "COUNT":
|
|
352
|
+
return len(results)
|
|
353
|
+
|
|
354
|
+
# Parse function and field
|
|
355
|
+
if "(" in agg_func:
|
|
356
|
+
func_name = agg_func.split("(")[0]
|
|
357
|
+
field = agg_func.split("(")[1].split(")")[0]
|
|
358
|
+
|
|
359
|
+
# Extract field values
|
|
360
|
+
values = []
|
|
361
|
+
for result in results:
|
|
362
|
+
value = result.get(field)
|
|
363
|
+
if value is not None and isinstance(value, (int, float)):
|
|
364
|
+
values.append(value)
|
|
365
|
+
|
|
366
|
+
if not values:
|
|
367
|
+
return None
|
|
368
|
+
|
|
369
|
+
if func_name == "SUM":
|
|
370
|
+
return sum(values)
|
|
371
|
+
elif func_name == "AVG":
|
|
372
|
+
return sum(values) / len(values)
|
|
373
|
+
elif func_name == "MIN":
|
|
374
|
+
return min(values)
|
|
375
|
+
elif func_name == "MAX":
|
|
376
|
+
return max(values)
|
|
377
|
+
|
|
378
|
+
return None
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Knowledge Graph Profiling
|
|
3
|
+
|
|
4
|
+
Performance profiling and analysis tools for knowledge graph operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from aiecs.application.knowledge_graph.profiling.query_profiler import (
|
|
8
|
+
QueryProfiler,
|
|
9
|
+
QueryProfile,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = ["QueryProfiler", "QueryProfile"]
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Query Plan Visualizer
|
|
3
|
+
|
|
4
|
+
Visualize query execution plans and performance profiles.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any, List
|
|
8
|
+
from aiecs.application.knowledge_graph.profiling.query_profiler import (
|
|
9
|
+
QueryProfile,
|
|
10
|
+
)
|
|
11
|
+
from aiecs.domain.knowledge_graph.models.query_plan import QueryPlan
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class QueryPlanVisualizer:
|
|
15
|
+
"""
|
|
16
|
+
Visualize query plans and execution profiles
|
|
17
|
+
|
|
18
|
+
Generates text-based visualizations of query plans and performance data.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
```python
|
|
22
|
+
visualizer = QueryPlanVisualizer()
|
|
23
|
+
|
|
24
|
+
# Visualize query plan
|
|
25
|
+
plan_viz = visualizer.visualize_plan(query_plan)
|
|
26
|
+
print(plan_viz)
|
|
27
|
+
|
|
28
|
+
# Visualize execution profile
|
|
29
|
+
profile_viz = visualizer.visualize_profile(query_profile)
|
|
30
|
+
print(profile_viz)
|
|
31
|
+
```
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def visualize_plan(self, plan: QueryPlan, show_costs: bool = True) -> str:
|
|
35
|
+
"""
|
|
36
|
+
Visualize a query plan
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
plan: Query plan to visualize
|
|
40
|
+
show_costs: Whether to show cost estimates
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Text visualization of the plan
|
|
44
|
+
"""
|
|
45
|
+
lines = []
|
|
46
|
+
lines.append("=" * 60)
|
|
47
|
+
lines.append("QUERY PLAN")
|
|
48
|
+
lines.append("=" * 60)
|
|
49
|
+
|
|
50
|
+
if show_costs:
|
|
51
|
+
total_cost = plan.calculate_total_cost()
|
|
52
|
+
lines.append(f"Total Estimated Cost: {total_cost:.2f}")
|
|
53
|
+
lines.append("")
|
|
54
|
+
|
|
55
|
+
for i, step in enumerate(plan.steps, 1):
|
|
56
|
+
lines.append(f"Step {i}: {step.operation.value}")
|
|
57
|
+
lines.append(f" Description: {step.description}")
|
|
58
|
+
|
|
59
|
+
if show_costs:
|
|
60
|
+
lines.append(f" Estimated Cost: {step.estimated_cost:.2f}")
|
|
61
|
+
|
|
62
|
+
if step.depends_on:
|
|
63
|
+
lines.append(f" Depends On: {', '.join(step.depends_on)}")
|
|
64
|
+
|
|
65
|
+
if step.metadata:
|
|
66
|
+
lines.append(f" Metadata: {step.metadata}")
|
|
67
|
+
|
|
68
|
+
lines.append("")
|
|
69
|
+
|
|
70
|
+
lines.append("=" * 60)
|
|
71
|
+
return "\n".join(lines)
|
|
72
|
+
|
|
73
|
+
def visualize_profile(self, profile: QueryProfile, show_steps: bool = True) -> str:
|
|
74
|
+
"""
|
|
75
|
+
Visualize a query execution profile
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
profile: Query profile to visualize
|
|
79
|
+
show_steps: Whether to show individual steps
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Text visualization of the profile
|
|
83
|
+
"""
|
|
84
|
+
lines = []
|
|
85
|
+
lines.append("=" * 60)
|
|
86
|
+
lines.append("QUERY EXECUTION PROFILE")
|
|
87
|
+
lines.append("=" * 60)
|
|
88
|
+
lines.append(f"Query ID: {profile.query_id}")
|
|
89
|
+
lines.append(f"Query Type: {profile.query_type}")
|
|
90
|
+
lines.append(f"Total Duration: {profile.duration_ms:.2f}ms")
|
|
91
|
+
lines.append("")
|
|
92
|
+
|
|
93
|
+
if show_steps and profile.steps:
|
|
94
|
+
lines.append("Execution Steps:")
|
|
95
|
+
lines.append("-" * 60)
|
|
96
|
+
|
|
97
|
+
for i, step in enumerate(profile.steps, 1):
|
|
98
|
+
duration = step["duration_ms"]
|
|
99
|
+
percentage = (duration / profile.duration_ms * 100) if profile.duration_ms else 0
|
|
100
|
+
|
|
101
|
+
lines.append(f"{i}. {step['name']}")
|
|
102
|
+
lines.append(f" Duration: {duration:.2f}ms ({percentage:.1f}%)")
|
|
103
|
+
|
|
104
|
+
# Show bar chart
|
|
105
|
+
bar_length = int(percentage / 2) # Scale to 50 chars max
|
|
106
|
+
bar = "█" * bar_length
|
|
107
|
+
lines.append(f" [{bar:<50}]")
|
|
108
|
+
|
|
109
|
+
if step.get("metadata"):
|
|
110
|
+
lines.append(f" Metadata: {step['metadata']}")
|
|
111
|
+
|
|
112
|
+
lines.append("")
|
|
113
|
+
|
|
114
|
+
if profile.metadata:
|
|
115
|
+
lines.append("Query Metadata:")
|
|
116
|
+
for key, value in profile.metadata.items():
|
|
117
|
+
lines.append(f" {key}: {value}")
|
|
118
|
+
lines.append("")
|
|
119
|
+
|
|
120
|
+
lines.append("=" * 60)
|
|
121
|
+
return "\n".join(lines)
|
|
122
|
+
|
|
123
|
+
def visualize_comparison(self, profiles: List[QueryProfile]) -> str:
|
|
124
|
+
"""
|
|
125
|
+
Visualize comparison of multiple query profiles
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
profiles: List of profiles to compare
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Text visualization comparing profiles
|
|
132
|
+
"""
|
|
133
|
+
if not profiles:
|
|
134
|
+
return "No profiles to compare"
|
|
135
|
+
|
|
136
|
+
lines = []
|
|
137
|
+
lines.append("=" * 60)
|
|
138
|
+
lines.append("QUERY PROFILE COMPARISON")
|
|
139
|
+
lines.append("=" * 60)
|
|
140
|
+
|
|
141
|
+
# Summary table
|
|
142
|
+
lines.append(f"{'Query ID':<20} {'Type':<15} {'Duration (ms)':<15}")
|
|
143
|
+
lines.append("-" * 60)
|
|
144
|
+
|
|
145
|
+
for profile in profiles:
|
|
146
|
+
duration = f"{profile.duration_ms:.2f}" if profile.duration_ms else "N/A"
|
|
147
|
+
lines.append(f"{profile.query_id:<20} {profile.query_type:<15} {duration:<15}")
|
|
148
|
+
|
|
149
|
+
lines.append("")
|
|
150
|
+
|
|
151
|
+
# Statistics
|
|
152
|
+
durations = [p.duration_ms for p in profiles if p.duration_ms]
|
|
153
|
+
if durations:
|
|
154
|
+
lines.append("Statistics:")
|
|
155
|
+
lines.append(f" Average: {sum(durations) / len(durations):.2f}ms")
|
|
156
|
+
lines.append(f" Min: {min(durations):.2f}ms")
|
|
157
|
+
lines.append(f" Max: {max(durations):.2f}ms")
|
|
158
|
+
|
|
159
|
+
lines.append("=" * 60)
|
|
160
|
+
return "\n".join(lines)
|
|
161
|
+
|
|
162
|
+
def export_to_json(self, profile: QueryProfile) -> Dict[str, Any]:
|
|
163
|
+
"""
|
|
164
|
+
Export profile to JSON format
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
profile: Query profile to export
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Dictionary representation suitable for JSON export
|
|
171
|
+
"""
|
|
172
|
+
return profile.to_dict()
|
|
173
|
+
|
|
174
|
+
def generate_flamegraph_data(self, profile: QueryProfile) -> List[Dict[str, Any]]:
|
|
175
|
+
"""
|
|
176
|
+
Generate data for flamegraph visualization
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
profile: Query profile
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
List of flamegraph data points
|
|
183
|
+
"""
|
|
184
|
+
data = []
|
|
185
|
+
|
|
186
|
+
for step in profile.steps:
|
|
187
|
+
data.append(
|
|
188
|
+
{
|
|
189
|
+
"name": step["name"],
|
|
190
|
+
"value": step["duration_ms"],
|
|
191
|
+
"percentage": (
|
|
192
|
+
(step["duration_ms"] / profile.duration_ms * 100)
|
|
193
|
+
if profile.duration_ms
|
|
194
|
+
else 0
|
|
195
|
+
),
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
return data
|