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,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Relation Validator
|
|
3
|
+
|
|
4
|
+
Validates relations against knowledge graph schema.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional, Tuple
|
|
8
|
+
from aiecs.domain.knowledge_graph.models.entity import Entity
|
|
9
|
+
from aiecs.domain.knowledge_graph.models.relation import Relation
|
|
10
|
+
from aiecs.domain.knowledge_graph.schema.graph_schema import GraphSchema
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RelationValidator:
|
|
14
|
+
"""
|
|
15
|
+
Validate relations against schema rules
|
|
16
|
+
|
|
17
|
+
Ensures that:
|
|
18
|
+
- Relation type exists in schema
|
|
19
|
+
- Source and target entity types are compatible
|
|
20
|
+
- Relation properties match schema
|
|
21
|
+
- Required properties are present
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
```python
|
|
25
|
+
validator = RelationValidator(schema)
|
|
26
|
+
|
|
27
|
+
is_valid, errors = validator.validate_relation(
|
|
28
|
+
relation,
|
|
29
|
+
source_entity,
|
|
30
|
+
target_entity
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if not is_valid:
|
|
34
|
+
print(f"Invalid relation: {errors}")
|
|
35
|
+
```
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, schema: Optional[GraphSchema] = None, strict: bool = False):
|
|
39
|
+
"""
|
|
40
|
+
Initialize relation validator
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
schema: GraphSchema to validate against (optional)
|
|
44
|
+
strict: If True, reject relations not in schema; if False, allow unknown types
|
|
45
|
+
"""
|
|
46
|
+
self.schema = schema
|
|
47
|
+
self.strict = strict
|
|
48
|
+
|
|
49
|
+
def validate_relation(
|
|
50
|
+
self, relation: Relation, source_entity: Entity, target_entity: Entity
|
|
51
|
+
) -> Tuple[bool, List[str]]:
|
|
52
|
+
"""
|
|
53
|
+
Validate a relation against schema
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
relation: Relation to validate
|
|
57
|
+
source_entity: Source entity
|
|
58
|
+
target_entity: Target entity
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Tuple of (is_valid, error_messages)
|
|
62
|
+
"""
|
|
63
|
+
errors = []
|
|
64
|
+
|
|
65
|
+
# Basic validation (always performed)
|
|
66
|
+
if not relation.source_id:
|
|
67
|
+
errors.append("Relation missing source_id")
|
|
68
|
+
if not relation.target_id:
|
|
69
|
+
errors.append("Relation missing target_id")
|
|
70
|
+
if not relation.relation_type:
|
|
71
|
+
errors.append("Relation missing relation_type")
|
|
72
|
+
|
|
73
|
+
# Check entity IDs match
|
|
74
|
+
if relation.source_id != source_entity.id:
|
|
75
|
+
errors.append(
|
|
76
|
+
f"Relation source_id '{relation.source_id}' does not match "
|
|
77
|
+
f"source_entity id '{source_entity.id}'"
|
|
78
|
+
)
|
|
79
|
+
if relation.target_id != target_entity.id:
|
|
80
|
+
errors.append(
|
|
81
|
+
f"Relation target_id '{relation.target_id}' does not match "
|
|
82
|
+
f"target_entity id '{target_entity.id}'"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Schema-based validation (if schema provided)
|
|
86
|
+
if self.schema:
|
|
87
|
+
schema_errors = self._validate_against_schema(relation, source_entity, target_entity)
|
|
88
|
+
errors.extend(schema_errors)
|
|
89
|
+
|
|
90
|
+
return (len(errors) == 0, errors)
|
|
91
|
+
|
|
92
|
+
def validate_relations(
|
|
93
|
+
self, relations: List[Relation], entities: List[Entity]
|
|
94
|
+
) -> List[Tuple[Relation, bool, List[str]]]:
|
|
95
|
+
"""
|
|
96
|
+
Validate multiple relations
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
relations: List of relations to validate
|
|
100
|
+
entities: List of entities (source and targets)
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
List of (relation, is_valid, errors) tuples
|
|
104
|
+
"""
|
|
105
|
+
# Build entity lookup
|
|
106
|
+
entity_lookup = {e.id: e for e in entities}
|
|
107
|
+
|
|
108
|
+
results = []
|
|
109
|
+
for relation in relations:
|
|
110
|
+
source = entity_lookup.get(relation.source_id)
|
|
111
|
+
target = entity_lookup.get(relation.target_id)
|
|
112
|
+
|
|
113
|
+
if not source or not target:
|
|
114
|
+
is_valid = False
|
|
115
|
+
errors = [f"Source or target entity not found for relation {relation.id}"]
|
|
116
|
+
else:
|
|
117
|
+
is_valid, errors = self.validate_relation(relation, source, target)
|
|
118
|
+
|
|
119
|
+
results.append((relation, is_valid, errors))
|
|
120
|
+
|
|
121
|
+
return results
|
|
122
|
+
|
|
123
|
+
def filter_valid_relations(
|
|
124
|
+
self, relations: List[Relation], entities: List[Entity]
|
|
125
|
+
) -> List[Relation]:
|
|
126
|
+
"""
|
|
127
|
+
Filter relations to only valid ones
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
relations: List of relations
|
|
131
|
+
entities: List of entities
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
List of valid relations
|
|
135
|
+
"""
|
|
136
|
+
validation_results = self.validate_relations(relations, entities)
|
|
137
|
+
return [relation for relation, is_valid, errors in validation_results if is_valid]
|
|
138
|
+
|
|
139
|
+
def _validate_against_schema(
|
|
140
|
+
self, relation: Relation, source_entity: Entity, target_entity: Entity
|
|
141
|
+
) -> List[str]:
|
|
142
|
+
"""
|
|
143
|
+
Validate relation against schema rules
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
relation: Relation to validate
|
|
147
|
+
source_entity: Source entity
|
|
148
|
+
target_entity: Target entity
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
List of error messages (empty if valid)
|
|
152
|
+
"""
|
|
153
|
+
errors = []
|
|
154
|
+
|
|
155
|
+
# Check if relation type exists in schema
|
|
156
|
+
if not self.schema.has_relation_type(relation.relation_type):
|
|
157
|
+
if self.strict:
|
|
158
|
+
errors.append(f"Relation type '{relation.relation_type}' not found in schema")
|
|
159
|
+
# In non-strict mode, allow unknown relation types
|
|
160
|
+
return errors
|
|
161
|
+
|
|
162
|
+
# Get relation type schema
|
|
163
|
+
rel_type_schema = self.schema.get_relation_type(relation.relation_type)
|
|
164
|
+
|
|
165
|
+
# Validate source entity type
|
|
166
|
+
if rel_type_schema.source_entity_types:
|
|
167
|
+
if source_entity.entity_type not in rel_type_schema.source_entity_types:
|
|
168
|
+
errors.append(
|
|
169
|
+
f"Source entity type '{source_entity.entity_type}' not allowed for "
|
|
170
|
+
f"relation '{relation.relation_type}'. "
|
|
171
|
+
f"Allowed types: {rel_type_schema.source_entity_types}"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Validate target entity type
|
|
175
|
+
if rel_type_schema.target_entity_types:
|
|
176
|
+
if target_entity.entity_type not in rel_type_schema.target_entity_types:
|
|
177
|
+
errors.append(
|
|
178
|
+
f"Target entity type '{target_entity.entity_type}' not allowed for "
|
|
179
|
+
f"relation '{relation.relation_type}'. "
|
|
180
|
+
f"Allowed types: {rel_type_schema.target_entity_types}"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# TODO: Validate relation properties against schema (if PropertySchema defined)
|
|
184
|
+
# This would check:
|
|
185
|
+
# - Required properties are present
|
|
186
|
+
# - Property types match schema
|
|
187
|
+
# - Values are within allowed ranges/values
|
|
188
|
+
|
|
189
|
+
return errors
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Graph Visualization Utilities
|
|
3
|
+
|
|
4
|
+
Export knowledge graphs to various formats for visualization.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Dict, Any
|
|
8
|
+
from aiecs.domain.knowledge_graph.models.entity import Entity
|
|
9
|
+
from aiecs.domain.knowledge_graph.models.relation import Relation
|
|
10
|
+
from aiecs.domain.knowledge_graph.models.path import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GraphVisualizer:
|
|
14
|
+
"""
|
|
15
|
+
Visualize knowledge graphs in various formats
|
|
16
|
+
|
|
17
|
+
Supports export to:
|
|
18
|
+
- DOT format (Graphviz)
|
|
19
|
+
- JSON for D3.js, Cytoscape.js, etc.
|
|
20
|
+
- NetworkX-compatible format
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```python
|
|
24
|
+
visualizer = GraphVisualizer()
|
|
25
|
+
|
|
26
|
+
# Export to DOT
|
|
27
|
+
dot = visualizer.to_dot(entities, relations)
|
|
28
|
+
with open("graph.dot", "w") as f:
|
|
29
|
+
f.write(dot)
|
|
30
|
+
|
|
31
|
+
# Export to JSON
|
|
32
|
+
json_data = visualizer.to_json(entities, relations)
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def to_dot(
|
|
37
|
+
self,
|
|
38
|
+
entities: List[Entity],
|
|
39
|
+
relations: List[Relation],
|
|
40
|
+
graph_name: str = "knowledge_graph",
|
|
41
|
+
include_properties: bool = True,
|
|
42
|
+
max_label_length: int = 50,
|
|
43
|
+
) -> str:
|
|
44
|
+
"""
|
|
45
|
+
Export graph to DOT format (Graphviz)
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
entities: List of entities
|
|
49
|
+
relations: List of relations
|
|
50
|
+
graph_name: Name of the graph
|
|
51
|
+
include_properties: Include entity properties in labels
|
|
52
|
+
max_label_length: Maximum label length
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
DOT format string
|
|
56
|
+
"""
|
|
57
|
+
lines = []
|
|
58
|
+
lines.append(f"digraph {graph_name} {{")
|
|
59
|
+
lines.append(" rankdir=LR;")
|
|
60
|
+
lines.append(" node [shape=box, style=rounded];")
|
|
61
|
+
lines.append("")
|
|
62
|
+
|
|
63
|
+
# Add nodes
|
|
64
|
+
for entity in entities:
|
|
65
|
+
label = self._create_entity_label(entity, include_properties, max_label_length)
|
|
66
|
+
node_id = self._sanitize_id(entity.id)
|
|
67
|
+
color = self._get_entity_color(entity.entity_type)
|
|
68
|
+
|
|
69
|
+
lines.append(
|
|
70
|
+
f' {node_id} [label="{label}", fillcolor="{color}", style="rounded,filled"];'
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
lines.append("")
|
|
74
|
+
|
|
75
|
+
# Add edges
|
|
76
|
+
for relation in relations:
|
|
77
|
+
source_id = self._sanitize_id(relation.source_id)
|
|
78
|
+
target_id = self._sanitize_id(relation.target_id)
|
|
79
|
+
label = relation.relation_type
|
|
80
|
+
|
|
81
|
+
lines.append(f' {source_id} -> {target_id} [label="{label}"];')
|
|
82
|
+
|
|
83
|
+
lines.append("}")
|
|
84
|
+
return "\n".join(lines)
|
|
85
|
+
|
|
86
|
+
def to_json(
|
|
87
|
+
self,
|
|
88
|
+
entities: List[Entity],
|
|
89
|
+
relations: List[Relation],
|
|
90
|
+
format: str = "d3",
|
|
91
|
+
) -> Dict[str, Any]:
|
|
92
|
+
"""
|
|
93
|
+
Export graph to JSON format
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
entities: List of entities
|
|
97
|
+
relations: List of relations
|
|
98
|
+
format: JSON format ("d3", "cytoscape", "networkx")
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Dictionary suitable for JSON export
|
|
102
|
+
"""
|
|
103
|
+
if format == "d3":
|
|
104
|
+
return self._to_d3_json(entities, relations)
|
|
105
|
+
elif format == "cytoscape":
|
|
106
|
+
return self._to_cytoscape_json(entities, relations)
|
|
107
|
+
elif format == "networkx":
|
|
108
|
+
return self._to_networkx_json(entities, relations)
|
|
109
|
+
else:
|
|
110
|
+
raise ValueError(f"Unsupported format: {format}")
|
|
111
|
+
|
|
112
|
+
def _to_d3_json(self, entities: List[Entity], relations: List[Relation]) -> Dict[str, Any]:
|
|
113
|
+
"""Export to D3.js force-directed graph format"""
|
|
114
|
+
nodes = []
|
|
115
|
+
for entity in entities:
|
|
116
|
+
nodes.append(
|
|
117
|
+
{
|
|
118
|
+
"id": entity.id,
|
|
119
|
+
"name": entity.properties.get("name", entity.id),
|
|
120
|
+
"type": entity.entity_type,
|
|
121
|
+
"properties": entity.properties,
|
|
122
|
+
"group": hash(entity.entity_type) % 10, # Color group
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
links = []
|
|
127
|
+
for relation in relations:
|
|
128
|
+
links.append(
|
|
129
|
+
{
|
|
130
|
+
"source": relation.source_id,
|
|
131
|
+
"target": relation.target_id,
|
|
132
|
+
"type": relation.relation_type,
|
|
133
|
+
"properties": relation.properties,
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return {"nodes": nodes, "links": links}
|
|
138
|
+
|
|
139
|
+
def _to_cytoscape_json(
|
|
140
|
+
self, entities: List[Entity], relations: List[Relation]
|
|
141
|
+
) -> Dict[str, Any]:
|
|
142
|
+
"""Export to Cytoscape.js format"""
|
|
143
|
+
elements = []
|
|
144
|
+
|
|
145
|
+
# Add nodes
|
|
146
|
+
for entity in entities:
|
|
147
|
+
elements.append(
|
|
148
|
+
{
|
|
149
|
+
"data": {
|
|
150
|
+
"id": entity.id,
|
|
151
|
+
"label": entity.properties.get("name", entity.id),
|
|
152
|
+
"type": entity.entity_type,
|
|
153
|
+
"properties": entity.properties,
|
|
154
|
+
},
|
|
155
|
+
"group": "nodes",
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Add edges
|
|
160
|
+
for relation in relations:
|
|
161
|
+
elements.append(
|
|
162
|
+
{
|
|
163
|
+
"data": {
|
|
164
|
+
"id": relation.id,
|
|
165
|
+
"source": relation.source_id,
|
|
166
|
+
"target": relation.target_id,
|
|
167
|
+
"label": relation.relation_type,
|
|
168
|
+
"properties": relation.properties,
|
|
169
|
+
},
|
|
170
|
+
"group": "edges",
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return {"elements": elements}
|
|
175
|
+
|
|
176
|
+
def _to_networkx_json(
|
|
177
|
+
self, entities: List[Entity], relations: List[Relation]
|
|
178
|
+
) -> Dict[str, Any]:
|
|
179
|
+
"""Export to NetworkX node-link format"""
|
|
180
|
+
nodes = []
|
|
181
|
+
for entity in entities:
|
|
182
|
+
nodes.append(
|
|
183
|
+
{
|
|
184
|
+
"id": entity.id,
|
|
185
|
+
"entity_type": entity.entity_type,
|
|
186
|
+
**entity.properties,
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
links = []
|
|
191
|
+
for relation in relations:
|
|
192
|
+
links.append(
|
|
193
|
+
{
|
|
194
|
+
"source": relation.source_id,
|
|
195
|
+
"target": relation.target_id,
|
|
196
|
+
"relation_type": relation.relation_type,
|
|
197
|
+
**relation.properties,
|
|
198
|
+
}
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
"directed": True,
|
|
203
|
+
"multigraph": False,
|
|
204
|
+
"graph": {},
|
|
205
|
+
"nodes": nodes,
|
|
206
|
+
"links": links,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
def to_mermaid(
|
|
210
|
+
self,
|
|
211
|
+
entities: List[Entity],
|
|
212
|
+
relations: List[Relation],
|
|
213
|
+
max_entities: int = 50,
|
|
214
|
+
) -> str:
|
|
215
|
+
"""
|
|
216
|
+
Export graph to Mermaid diagram format
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
entities: List of entities
|
|
220
|
+
relations: List of relations
|
|
221
|
+
max_entities: Maximum entities to include
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Mermaid diagram string
|
|
225
|
+
"""
|
|
226
|
+
lines = []
|
|
227
|
+
lines.append("graph LR")
|
|
228
|
+
|
|
229
|
+
# Limit entities for readability
|
|
230
|
+
entities_subset = entities[:max_entities]
|
|
231
|
+
entity_ids = {e.id for e in entities_subset}
|
|
232
|
+
|
|
233
|
+
# Add nodes
|
|
234
|
+
for entity in entities_subset:
|
|
235
|
+
label = entity.properties.get("name", entity.id)
|
|
236
|
+
label = label[:30] # Truncate long labels
|
|
237
|
+
node_id = self._sanitize_id(entity.id)
|
|
238
|
+
lines.append(f' {node_id}["{label}"]')
|
|
239
|
+
|
|
240
|
+
# Add edges (only between included entities)
|
|
241
|
+
for relation in relations:
|
|
242
|
+
if relation.source_id in entity_ids and relation.target_id in entity_ids:
|
|
243
|
+
source_id = self._sanitize_id(relation.source_id)
|
|
244
|
+
target_id = self._sanitize_id(relation.target_id)
|
|
245
|
+
label = relation.relation_type
|
|
246
|
+
lines.append(f" {source_id} -->|{label}| {target_id}")
|
|
247
|
+
|
|
248
|
+
return "\n".join(lines)
|
|
249
|
+
|
|
250
|
+
def export_path_to_dot(self, path: Path) -> str:
|
|
251
|
+
"""
|
|
252
|
+
Export a single path to DOT format
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
path: Path to export
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
DOT format string
|
|
259
|
+
"""
|
|
260
|
+
lines = []
|
|
261
|
+
lines.append("digraph path {")
|
|
262
|
+
lines.append(" rankdir=LR;")
|
|
263
|
+
lines.append(" node [shape=box, style=rounded];")
|
|
264
|
+
lines.append("")
|
|
265
|
+
|
|
266
|
+
# Add nodes from path
|
|
267
|
+
for entity in path.nodes:
|
|
268
|
+
label = entity.properties.get("name", entity.id)
|
|
269
|
+
node_id = self._sanitize_id(entity.id)
|
|
270
|
+
lines.append(f' {node_id} [label="{label}"];')
|
|
271
|
+
|
|
272
|
+
lines.append("")
|
|
273
|
+
|
|
274
|
+
# Add edges from path
|
|
275
|
+
for relation in path.edges:
|
|
276
|
+
source_id = self._sanitize_id(relation.source_id)
|
|
277
|
+
target_id = self._sanitize_id(relation.target_id)
|
|
278
|
+
label = relation.relation_type
|
|
279
|
+
lines.append(f' {source_id} -> {target_id} [label="{label}"];')
|
|
280
|
+
|
|
281
|
+
lines.append("}")
|
|
282
|
+
return "\n".join(lines)
|
|
283
|
+
|
|
284
|
+
def _create_entity_label(
|
|
285
|
+
self, entity: Entity, include_properties: bool, max_length: int
|
|
286
|
+
) -> str:
|
|
287
|
+
"""Create label for entity node"""
|
|
288
|
+
label_parts = [f"{entity.entity_type}: {entity.id}"]
|
|
289
|
+
|
|
290
|
+
if include_properties and entity.properties:
|
|
291
|
+
# Add key properties
|
|
292
|
+
for key, value in list(entity.properties.items())[:3]:
|
|
293
|
+
label_parts.append(f"{key}: {value}")
|
|
294
|
+
|
|
295
|
+
label = "\\n".join(label_parts)
|
|
296
|
+
|
|
297
|
+
if len(label) > max_length:
|
|
298
|
+
label = label[:max_length] + "..."
|
|
299
|
+
|
|
300
|
+
return label
|
|
301
|
+
|
|
302
|
+
def _sanitize_id(self, id_str: str) -> str:
|
|
303
|
+
"""Sanitize ID for DOT format"""
|
|
304
|
+
# Replace special characters
|
|
305
|
+
sanitized = id_str.replace("-", "_").replace(":", "_").replace(" ", "_")
|
|
306
|
+
# Ensure it starts with a letter
|
|
307
|
+
if not sanitized[0].isalpha():
|
|
308
|
+
sanitized = "n_" + sanitized
|
|
309
|
+
return sanitized
|
|
310
|
+
|
|
311
|
+
def _get_entity_color(self, entity_type: str) -> str:
|
|
312
|
+
"""Get color for entity type"""
|
|
313
|
+
colors = {
|
|
314
|
+
"Person": "lightblue",
|
|
315
|
+
"Company": "lightgreen",
|
|
316
|
+
"Product": "lightyellow",
|
|
317
|
+
"Location": "lightcoral",
|
|
318
|
+
"Event": "lightpink",
|
|
319
|
+
"Document": "lightgray",
|
|
320
|
+
}
|
|
321
|
+
return colors.get(entity_type, "white")
|
aiecs/common/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Common knowledge graph utilities and patterns"""
|
|
2
|
+
|
|
3
|
+
from .runnable import (
|
|
4
|
+
Runnable,
|
|
5
|
+
RunnableConfig,
|
|
6
|
+
RunnableState,
|
|
7
|
+
ExecutionMetrics,
|
|
8
|
+
CircuitBreaker,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Runnable",
|
|
13
|
+
"RunnableConfig",
|
|
14
|
+
"RunnableState",
|
|
15
|
+
"ExecutionMetrics",
|
|
16
|
+
"CircuitBreaker",
|
|
17
|
+
]
|