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,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entity Domain Model
|
|
3
|
+
|
|
4
|
+
Represents a node/entity in the knowledge graph with properties and embeddings.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from aiecs.infrastructure.graph_storage.tenant import validate_tenant_id, InvalidTenantIdError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Entity(BaseModel):
|
|
16
|
+
"""
|
|
17
|
+
Knowledge Graph Entity
|
|
18
|
+
|
|
19
|
+
Represents a node in the knowledge graph with:
|
|
20
|
+
- Unique identifier
|
|
21
|
+
- Entity type (e.g., "Person", "Company", "Product")
|
|
22
|
+
- Properties (arbitrary key-value data)
|
|
23
|
+
- Optional vector embedding for semantic search
|
|
24
|
+
- Metadata (timestamps, source info)
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
```python
|
|
28
|
+
entity = Entity(
|
|
29
|
+
id="person_001",
|
|
30
|
+
entity_type="Person",
|
|
31
|
+
properties={
|
|
32
|
+
"name": "John Doe",
|
|
33
|
+
"age": 30,
|
|
34
|
+
"occupation": "Software Engineer"
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
```
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
id: str = Field(..., description="Unique identifier for the entity")
|
|
41
|
+
|
|
42
|
+
entity_type: str = Field(..., description="Type of the entity (e.g., 'Person', 'Company')")
|
|
43
|
+
|
|
44
|
+
properties: dict[str, Any] = Field(
|
|
45
|
+
default_factory=dict,
|
|
46
|
+
description="Arbitrary properties associated with the entity",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
embedding: Optional[list[float]] = Field(default=None, description="Vector embedding for semantic search")
|
|
50
|
+
|
|
51
|
+
created_at: datetime = Field(
|
|
52
|
+
default_factory=datetime.utcnow,
|
|
53
|
+
description="Timestamp when entity was created",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
updated_at: datetime = Field(
|
|
57
|
+
default_factory=datetime.utcnow,
|
|
58
|
+
description="Timestamp when entity was last updated",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
source: Optional[str] = Field(
|
|
62
|
+
default=None,
|
|
63
|
+
description="Source of the entity data (e.g., document ID)",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
tenant_id: Optional[str] = Field(
|
|
67
|
+
default=None,
|
|
68
|
+
description="Tenant identifier for multi-tenant isolation (alphanumeric, hyphens, underscores only)",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
class Config:
|
|
72
|
+
json_encoders = {datetime: lambda v: v.isoformat()}
|
|
73
|
+
# Allow arbitrary types for numpy arrays if needed
|
|
74
|
+
arbitrary_types_allowed = True
|
|
75
|
+
|
|
76
|
+
@field_validator("embedding")
|
|
77
|
+
@classmethod
|
|
78
|
+
def validate_embedding(cls, v: Optional[list[float]]) -> Optional[list[float]]:
|
|
79
|
+
"""Validate embedding is a list of floats"""
|
|
80
|
+
if v is not None:
|
|
81
|
+
if not isinstance(v, list):
|
|
82
|
+
raise ValueError("Embedding must be a list of floats")
|
|
83
|
+
if not all(isinstance(x, (int, float)) for x in v):
|
|
84
|
+
raise ValueError("All embedding values must be numeric")
|
|
85
|
+
return v
|
|
86
|
+
|
|
87
|
+
@model_validator(mode="before")
|
|
88
|
+
@classmethod
|
|
89
|
+
def validate_tenant_id_before(cls, data: Any) -> Any:
|
|
90
|
+
"""Validate tenant_id format before model creation"""
|
|
91
|
+
# Pydantic converts keyword arguments to dict before calling model_validator
|
|
92
|
+
if isinstance(data, dict):
|
|
93
|
+
tenant_id = data.get("tenant_id")
|
|
94
|
+
if tenant_id is not None:
|
|
95
|
+
# Validate and raise InvalidTenantIdError directly
|
|
96
|
+
# Note: Pydantic will wrap this in ValidationError, but the error info is preserved
|
|
97
|
+
validate_tenant_id(tenant_id)
|
|
98
|
+
return data
|
|
99
|
+
|
|
100
|
+
def get_embedding_vector(self) -> Optional[np.ndarray]:
|
|
101
|
+
"""
|
|
102
|
+
Get embedding as numpy array
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
numpy array of embedding or None if no embedding
|
|
106
|
+
"""
|
|
107
|
+
if self.embedding is None:
|
|
108
|
+
return None
|
|
109
|
+
return np.array(self.embedding, dtype=np.float32)
|
|
110
|
+
|
|
111
|
+
def set_embedding_vector(self, vector: np.ndarray) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Set embedding from numpy array
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
vector: Numpy array of embedding values
|
|
117
|
+
"""
|
|
118
|
+
self.embedding = vector.tolist()
|
|
119
|
+
|
|
120
|
+
def get_property(self, key: str, default: Any = None) -> Any:
|
|
121
|
+
"""
|
|
122
|
+
Get a specific property value
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
key: Property key
|
|
126
|
+
default: Default value if key not found
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Property value or default
|
|
130
|
+
"""
|
|
131
|
+
return self.properties.get(key, default)
|
|
132
|
+
|
|
133
|
+
def set_property(self, key: str, value: Any) -> None:
|
|
134
|
+
"""
|
|
135
|
+
Set a property value
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
key: Property key
|
|
139
|
+
value: Property value
|
|
140
|
+
"""
|
|
141
|
+
self.properties[key] = value
|
|
142
|
+
self.updated_at = datetime.utcnow()
|
|
143
|
+
|
|
144
|
+
def __str__(self) -> str:
|
|
145
|
+
return f"Entity(id={self.id}, type={self.entity_type})"
|
|
146
|
+
|
|
147
|
+
def __repr__(self) -> str:
|
|
148
|
+
return f"Entity(id='{self.id}', entity_type='{self.entity_type}', properties={len(self.properties)} keys)"
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Evidence Domain Models
|
|
3
|
+
|
|
4
|
+
Models for representing evidence from reasoning processes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional, Dict, Any
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
from aiecs.domain.knowledge_graph.models.entity import Entity
|
|
11
|
+
from aiecs.domain.knowledge_graph.models.relation import Relation
|
|
12
|
+
from aiecs.domain.knowledge_graph.models.path import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EvidenceType(str, Enum):
|
|
16
|
+
"""Types of evidence"""
|
|
17
|
+
|
|
18
|
+
ENTITY = "entity" # Single entity
|
|
19
|
+
RELATION = "relation" # Single relation
|
|
20
|
+
PATH = "path" # Complete path
|
|
21
|
+
SUBGRAPH = "subgraph" # Subgraph fragment
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Evidence(BaseModel):
|
|
25
|
+
"""
|
|
26
|
+
Evidence from Reasoning
|
|
27
|
+
|
|
28
|
+
Represents a piece of evidence collected during reasoning,
|
|
29
|
+
with confidence scoring and provenance tracking.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
evidence_id: Unique identifier for this evidence
|
|
33
|
+
evidence_type: Type of evidence (entity, relation, path, etc.)
|
|
34
|
+
entities: Entities involved in this evidence
|
|
35
|
+
relations: Relations involved in this evidence
|
|
36
|
+
paths: Paths involved in this evidence (for path-based evidence)
|
|
37
|
+
confidence: Confidence score (0-1, higher = more confident)
|
|
38
|
+
relevance_score: Relevance to the query (0-1)
|
|
39
|
+
explanation: Human-readable explanation of this evidence
|
|
40
|
+
source: Source of this evidence (e.g., "traversal", "vector_search")
|
|
41
|
+
metadata: Additional metadata
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
```python
|
|
45
|
+
evidence = Evidence(
|
|
46
|
+
evidence_id="ev_001",
|
|
47
|
+
evidence_type=EvidenceType.PATH,
|
|
48
|
+
entities=[alice, bob, company],
|
|
49
|
+
relations=[knows_rel, works_at_rel],
|
|
50
|
+
paths=[path],
|
|
51
|
+
confidence=0.9,
|
|
52
|
+
relevance_score=0.85,
|
|
53
|
+
explanation="Alice knows Bob who works at Company X",
|
|
54
|
+
source="multi_hop_traversal"
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
evidence_id: str = Field(..., description="Unique identifier for this evidence")
|
|
60
|
+
|
|
61
|
+
evidence_type: EvidenceType = Field(..., description="Type of evidence")
|
|
62
|
+
|
|
63
|
+
entities: List[Entity] = Field(default_factory=list, description="Entities involved in this evidence")
|
|
64
|
+
|
|
65
|
+
relations: List[Relation] = Field(default_factory=list, description="Relations involved in this evidence")
|
|
66
|
+
|
|
67
|
+
paths: List[Path] = Field(default_factory=list, description="Paths involved in this evidence")
|
|
68
|
+
|
|
69
|
+
confidence: float = Field(
|
|
70
|
+
default=1.0,
|
|
71
|
+
ge=0.0,
|
|
72
|
+
le=1.0,
|
|
73
|
+
description="Confidence score (0-1, higher = more confident)",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
relevance_score: float = Field(default=0.5, ge=0.0, le=1.0, description="Relevance to the query (0-1)")
|
|
77
|
+
|
|
78
|
+
explanation: str = Field(default="", description="Human-readable explanation of this evidence")
|
|
79
|
+
|
|
80
|
+
source: str = Field(default="", description="Source of this evidence (e.g., query step ID)")
|
|
81
|
+
|
|
82
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def combined_score(self) -> float:
|
|
86
|
+
"""Get combined score (confidence * relevance)"""
|
|
87
|
+
return self.confidence * self.relevance_score
|
|
88
|
+
|
|
89
|
+
def get_entity_ids(self) -> List[str]:
|
|
90
|
+
"""Get list of all entity IDs in evidence"""
|
|
91
|
+
return [entity.id for entity in self.entities]
|
|
92
|
+
|
|
93
|
+
def __str__(self) -> str:
|
|
94
|
+
return f"Evidence(type={self.evidence_type}, " f"score={self.combined_score:.2f}, " f"entities={len(self.entities)})"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class ReasoningResult(BaseModel):
|
|
98
|
+
"""
|
|
99
|
+
Result of Reasoning Process
|
|
100
|
+
|
|
101
|
+
Contains evidence collected, answer generated, and reasoning trace.
|
|
102
|
+
|
|
103
|
+
Attributes:
|
|
104
|
+
query: Original query that was answered
|
|
105
|
+
evidence: List of evidence pieces collected
|
|
106
|
+
answer: Generated answer (if applicable)
|
|
107
|
+
confidence: Overall confidence in the answer (0-1)
|
|
108
|
+
reasoning_trace: Human-readable trace of reasoning steps
|
|
109
|
+
execution_time_ms: Time taken to perform reasoning
|
|
110
|
+
metadata: Additional metadata
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
```python
|
|
114
|
+
result = ReasoningResult(
|
|
115
|
+
query="What companies does Alice know people at?",
|
|
116
|
+
evidence=[ev1, ev2, ev3],
|
|
117
|
+
answer="Alice knows people at Company X and Company Y",
|
|
118
|
+
confidence=0.87,
|
|
119
|
+
reasoning_trace="Step 1: Find Alice\nStep 2: Find people Alice knows\n...",
|
|
120
|
+
execution_time_ms=125.5
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
query: str = Field(..., description="Original query that was answered")
|
|
126
|
+
|
|
127
|
+
evidence: List[Evidence] = Field(default_factory=list, description="List of evidence pieces collected")
|
|
128
|
+
|
|
129
|
+
answer: Optional[str] = Field(default=None, description="Generated answer (if applicable)")
|
|
130
|
+
|
|
131
|
+
confidence: float = Field(
|
|
132
|
+
default=0.0,
|
|
133
|
+
ge=0.0,
|
|
134
|
+
le=1.0,
|
|
135
|
+
description="Overall confidence in the answer (0-1)",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
reasoning_trace: List[str] = Field(default_factory=list, description="List of reasoning steps taken")
|
|
139
|
+
|
|
140
|
+
execution_time_ms: Optional[float] = Field(default=None, ge=0.0, description="Time taken to perform reasoning")
|
|
141
|
+
|
|
142
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def evidence_count(self) -> int:
|
|
146
|
+
"""Get number of evidence pieces"""
|
|
147
|
+
return len(self.evidence)
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def has_answer(self) -> bool:
|
|
151
|
+
"""Check if result has an answer"""
|
|
152
|
+
return self.answer is not None and len(self.answer) > 0
|
|
153
|
+
|
|
154
|
+
def get_top_evidence(self, n: int = 5) -> List[Evidence]:
|
|
155
|
+
"""
|
|
156
|
+
Get top N evidence pieces by combined score
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
n: Number of evidence pieces to return
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Top N evidence pieces
|
|
163
|
+
"""
|
|
164
|
+
sorted_evidence = sorted(self.evidence, key=lambda e: e.combined_score, reverse=True)
|
|
165
|
+
return sorted_evidence[:n]
|
|
166
|
+
|
|
167
|
+
def get_trace_string(self) -> str:
|
|
168
|
+
"""Get reasoning trace as a single string"""
|
|
169
|
+
return "\n".join(self.reasoning_trace)
|
|
170
|
+
|
|
171
|
+
def __str__(self) -> str:
|
|
172
|
+
parts = [
|
|
173
|
+
f"ReasoningResult(evidence={self.evidence_count}",
|
|
174
|
+
f"confidence={self.confidence:.2f})",
|
|
175
|
+
]
|
|
176
|
+
if self.has_answer and self.answer:
|
|
177
|
+
parts.insert(1, f"answer={self.answer[:50]}...")
|
|
178
|
+
return " ".join(parts)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Inference Rule Domain Models
|
|
3
|
+
|
|
4
|
+
Models for representing logical inference rules.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Dict, Any
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
from aiecs.domain.knowledge_graph.models.relation import Relation
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RuleType(str, Enum):
|
|
14
|
+
"""Types of inference rules"""
|
|
15
|
+
|
|
16
|
+
TRANSITIVE = "transitive" # A->B, B->C => A->C
|
|
17
|
+
SYMMETRIC = "symmetric" # A->B => B->A
|
|
18
|
+
REFLEXIVE = "reflexive" # A => A->A
|
|
19
|
+
CUSTOM = "custom" # User-defined rule
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InferenceRule(BaseModel):
|
|
23
|
+
"""
|
|
24
|
+
Inference Rule
|
|
25
|
+
|
|
26
|
+
Represents a logical rule for inferring new relations from existing ones.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
rule_id: Unique identifier for this rule
|
|
30
|
+
rule_type: Type of rule (transitive, symmetric, etc.)
|
|
31
|
+
relation_type: Relation type this rule applies to
|
|
32
|
+
description: Human-readable description
|
|
33
|
+
enabled: Whether this rule is enabled
|
|
34
|
+
confidence_decay: How much confidence decreases with each inference step (0-1)
|
|
35
|
+
metadata: Additional metadata
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
```python
|
|
39
|
+
rule = InferenceRule(
|
|
40
|
+
rule_id="rule_001",
|
|
41
|
+
rule_type=RuleType.TRANSITIVE,
|
|
42
|
+
relation_type="WORKS_FOR",
|
|
43
|
+
description="If A works for B and B works for C, then A works for C",
|
|
44
|
+
confidence_decay=0.1
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
rule_id: str = Field(..., description="Unique identifier for this rule")
|
|
50
|
+
|
|
51
|
+
rule_type: RuleType = Field(..., description="Type of inference rule")
|
|
52
|
+
|
|
53
|
+
relation_type: str = Field(..., description="Relation type this rule applies to")
|
|
54
|
+
|
|
55
|
+
description: str = Field(default="", description="Human-readable description of the rule")
|
|
56
|
+
|
|
57
|
+
enabled: bool = Field(default=True, description="Whether this rule is enabled")
|
|
58
|
+
|
|
59
|
+
confidence_decay: float = Field(
|
|
60
|
+
default=0.1,
|
|
61
|
+
ge=0.0,
|
|
62
|
+
le=1.0,
|
|
63
|
+
description="Confidence decay per inference step (0-1)",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
67
|
+
|
|
68
|
+
def can_apply(self, relation: Relation) -> bool:
|
|
69
|
+
"""
|
|
70
|
+
Check if this rule can apply to a given relation
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
relation: Relation to check
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
True if rule can apply
|
|
77
|
+
"""
|
|
78
|
+
if not self.enabled:
|
|
79
|
+
return False
|
|
80
|
+
return relation.relation_type == self.relation_type
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class InferenceStep(BaseModel):
|
|
84
|
+
"""
|
|
85
|
+
Single Inference Step
|
|
86
|
+
|
|
87
|
+
Represents one step in an inference chain, tracking what was inferred
|
|
88
|
+
and how.
|
|
89
|
+
|
|
90
|
+
Attributes:
|
|
91
|
+
step_id: Unique identifier for this step
|
|
92
|
+
inferred_relation: The relation that was inferred
|
|
93
|
+
source_relations: Relations used to infer this one
|
|
94
|
+
rule: The rule that was applied
|
|
95
|
+
confidence: Confidence in this inference (0-1)
|
|
96
|
+
explanation: Human-readable explanation
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
```python
|
|
100
|
+
step = InferenceStep(
|
|
101
|
+
step_id="step_001",
|
|
102
|
+
inferred_relation=inferred_rel,
|
|
103
|
+
source_relations=[rel1, rel2],
|
|
104
|
+
rule=rule,
|
|
105
|
+
confidence=0.9,
|
|
106
|
+
explanation="Inferred from transitive rule"
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
step_id: str = Field(..., description="Unique identifier for this step")
|
|
112
|
+
|
|
113
|
+
inferred_relation: Relation = Field(..., description="The relation that was inferred")
|
|
114
|
+
|
|
115
|
+
source_relations: List[Relation] = Field(..., description="Relations used to infer this one")
|
|
116
|
+
|
|
117
|
+
rule: InferenceRule = Field(..., description="The rule that was applied")
|
|
118
|
+
|
|
119
|
+
confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence in this inference (0-1)")
|
|
120
|
+
|
|
121
|
+
explanation: str = Field(default="", description="Human-readable explanation of the inference")
|
|
122
|
+
|
|
123
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class InferenceResult(BaseModel):
|
|
127
|
+
"""
|
|
128
|
+
Inference Result
|
|
129
|
+
|
|
130
|
+
Contains the results of applying inference rules.
|
|
131
|
+
|
|
132
|
+
Attributes:
|
|
133
|
+
inferred_relations: List of relations that were inferred
|
|
134
|
+
inference_steps: List of inference steps taken
|
|
135
|
+
total_steps: Total number of inference steps
|
|
136
|
+
confidence: Overall confidence in the results
|
|
137
|
+
explanation: Human-readable explanation
|
|
138
|
+
|
|
139
|
+
Example:
|
|
140
|
+
```python
|
|
141
|
+
result = InferenceResult(
|
|
142
|
+
inferred_relations=[rel1, rel2],
|
|
143
|
+
inference_steps=[step1, step2],
|
|
144
|
+
total_steps=2,
|
|
145
|
+
confidence=0.85,
|
|
146
|
+
explanation="Inferred 2 relations using transitive rule"
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
inferred_relations: List[Relation] = Field(
|
|
152
|
+
default_factory=list,
|
|
153
|
+
description="List of relations that were inferred",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
inference_steps: List[InferenceStep] = Field(default_factory=list, description="List of inference steps taken")
|
|
157
|
+
|
|
158
|
+
total_steps: int = Field(default=0, ge=0, description="Total number of inference steps")
|
|
159
|
+
|
|
160
|
+
confidence: float = Field(
|
|
161
|
+
default=1.0,
|
|
162
|
+
ge=0.0,
|
|
163
|
+
le=1.0,
|
|
164
|
+
description="Overall confidence in the results",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
explanation: str = Field(default="", description="Human-readable explanation")
|
|
168
|
+
|
|
169
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def has_results(self) -> bool:
|
|
173
|
+
"""Check if any relations were inferred"""
|
|
174
|
+
return len(self.inferred_relations) > 0
|
|
175
|
+
|
|
176
|
+
def get_explanation_string(self) -> str:
|
|
177
|
+
"""Get explanation as a single string"""
|
|
178
|
+
if not self.explanation:
|
|
179
|
+
return f"Inferred {len(self.inferred_relations)} relations in {self.total_steps} steps"
|
|
180
|
+
return self.explanation
|
|
181
|
+
|
|
182
|
+
def get_step_explanations(self) -> List[str]:
|
|
183
|
+
"""Get explanations for each step"""
|
|
184
|
+
return [step.explanation for step in self.inference_steps]
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Path Domain Model
|
|
3
|
+
|
|
4
|
+
Represents a path through the knowledge graph (sequence of entities and relations).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
from pydantic import BaseModel, Field, field_validator
|
|
9
|
+
from aiecs.domain.knowledge_graph.models.entity import Entity
|
|
10
|
+
from aiecs.domain.knowledge_graph.models.relation import Relation
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Path(BaseModel):
|
|
14
|
+
"""
|
|
15
|
+
Knowledge Graph Path
|
|
16
|
+
|
|
17
|
+
Represents a path through the knowledge graph as a sequence of entities
|
|
18
|
+
connected by relations. Paths are results of graph traversal operations.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
nodes: Sequence of entities in the path
|
|
22
|
+
edges: Sequence of relations connecting the entities
|
|
23
|
+
score: Optional relevance score for the path
|
|
24
|
+
length: Number of hops in the path
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
```python
|
|
28
|
+
path = Path(
|
|
29
|
+
nodes=[entity1, entity2, entity3],
|
|
30
|
+
edges=[relation1_2, relation2_3],
|
|
31
|
+
score=0.85
|
|
32
|
+
)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Invariants:
|
|
36
|
+
- len(edges) == len(nodes) - 1 (for a valid path)
|
|
37
|
+
- edges[i].source_id == nodes[i].id
|
|
38
|
+
- edges[i].target_id == nodes[i+1].id
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
nodes: List[Entity] = Field(..., min_length=1, description="Sequence of entities in the path")
|
|
42
|
+
|
|
43
|
+
edges: List[Relation] = Field(
|
|
44
|
+
default_factory=list,
|
|
45
|
+
description="Sequence of relations connecting entities",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
score: Optional[float] = Field(
|
|
49
|
+
default=None,
|
|
50
|
+
ge=0.0,
|
|
51
|
+
le=1.0,
|
|
52
|
+
description="Relevance score for the path (0.0-1.0)",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
class Config:
|
|
56
|
+
arbitrary_types_allowed = True
|
|
57
|
+
|
|
58
|
+
@field_validator("edges")
|
|
59
|
+
@classmethod
|
|
60
|
+
def validate_path_structure(cls, v: List[Relation], info) -> List[Relation]:
|
|
61
|
+
"""Validate that edges match nodes structure"""
|
|
62
|
+
nodes = info.data.get("nodes", [])
|
|
63
|
+
if not nodes:
|
|
64
|
+
return v
|
|
65
|
+
|
|
66
|
+
# Check edge count
|
|
67
|
+
if len(v) != len(nodes) - 1:
|
|
68
|
+
if len(v) > 0: # Only raise error if edges are provided
|
|
69
|
+
raise ValueError(f"Number of edges ({len(v)}) must be len(nodes) - 1 ({len(nodes) - 1})")
|
|
70
|
+
|
|
71
|
+
# Validate edge connectivity (if we have edges)
|
|
72
|
+
for i, edge in enumerate(v):
|
|
73
|
+
if i < len(nodes) - 1:
|
|
74
|
+
expected_source = nodes[i].id
|
|
75
|
+
expected_target = nodes[i + 1].id
|
|
76
|
+
|
|
77
|
+
if edge.source_id != expected_source:
|
|
78
|
+
raise ValueError(f"Edge {i} source_id ({edge.source_id}) doesn't match " f"node {i} id ({expected_source})")
|
|
79
|
+
|
|
80
|
+
if edge.target_id != expected_target:
|
|
81
|
+
raise ValueError(f"Edge {i} target_id ({edge.target_id}) doesn't match " f"node {i+1} id ({expected_target})")
|
|
82
|
+
|
|
83
|
+
return v
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def length(self) -> int:
|
|
87
|
+
"""
|
|
88
|
+
Get path length (number of hops)
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Number of edges in the path
|
|
92
|
+
"""
|
|
93
|
+
return len(self.edges)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def start_entity(self) -> Entity:
|
|
97
|
+
"""
|
|
98
|
+
Get the starting entity of the path
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
First entity in the path
|
|
102
|
+
"""
|
|
103
|
+
return self.nodes[0]
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def end_entity(self) -> Entity:
|
|
107
|
+
"""
|
|
108
|
+
Get the ending entity of the path
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Last entity in the path
|
|
112
|
+
"""
|
|
113
|
+
return self.nodes[-1]
|
|
114
|
+
|
|
115
|
+
def get_entity_ids(self) -> List[str]:
|
|
116
|
+
"""
|
|
117
|
+
Get list of entity IDs in the path
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
List of entity IDs
|
|
121
|
+
"""
|
|
122
|
+
return [node.id for node in self.nodes]
|
|
123
|
+
|
|
124
|
+
def get_relation_types(self) -> List[str]:
|
|
125
|
+
"""
|
|
126
|
+
Get list of relation types in the path
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
List of relation types
|
|
130
|
+
"""
|
|
131
|
+
return [edge.relation_type for edge in self.edges]
|
|
132
|
+
|
|
133
|
+
def contains_entity(self, entity_id: str) -> bool:
|
|
134
|
+
"""
|
|
135
|
+
Check if path contains a specific entity
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
entity_id: Entity ID to check
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
True if entity is in the path
|
|
142
|
+
"""
|
|
143
|
+
return entity_id in self.get_entity_ids()
|
|
144
|
+
|
|
145
|
+
def contains_relation_type(self, relation_type: str) -> bool:
|
|
146
|
+
"""
|
|
147
|
+
Check if path contains a specific relation type
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
relation_type: Relation type to check
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
True if relation type is in the path
|
|
154
|
+
"""
|
|
155
|
+
return relation_type in self.get_relation_types()
|
|
156
|
+
|
|
157
|
+
def __str__(self) -> str:
|
|
158
|
+
if not self.edges:
|
|
159
|
+
return f"Path({self.nodes[0]})"
|
|
160
|
+
|
|
161
|
+
path_str = str(self.nodes[0].id)
|
|
162
|
+
for edge, node in zip(self.edges, self.nodes[1:]):
|
|
163
|
+
path_str += f" -[{edge.relation_type}]-> {node.id}"
|
|
164
|
+
|
|
165
|
+
if self.score is not None:
|
|
166
|
+
path_str += f" (score={self.score:.3f})"
|
|
167
|
+
|
|
168
|
+
return f"Path({path_str})"
|
|
169
|
+
|
|
170
|
+
def __repr__(self) -> str:
|
|
171
|
+
return f"Path(length={self.length}, nodes={len(self.nodes)}, score={self.score})"
|