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,514 @@
|
|
|
1
|
+
"""
|
|
2
|
+
In-Memory Graph Store Implementation
|
|
3
|
+
|
|
4
|
+
Implements Tier 1 of GraphStore interface using networkx.
|
|
5
|
+
Tier 2 methods work automatically via default implementations.
|
|
6
|
+
|
|
7
|
+
This is ideal for:
|
|
8
|
+
- Development and testing
|
|
9
|
+
- Small graphs (< 100K nodes)
|
|
10
|
+
- Prototyping
|
|
11
|
+
- Scenarios where persistence is not required
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import List, Optional, Dict
|
|
15
|
+
import networkx as nx
|
|
16
|
+
from aiecs.domain.knowledge_graph.models.entity import Entity
|
|
17
|
+
from aiecs.domain.knowledge_graph.models.relation import Relation
|
|
18
|
+
from aiecs.infrastructure.graph_storage.base import GraphStore
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InMemoryGraphStore(GraphStore):
|
|
22
|
+
"""
|
|
23
|
+
In-Memory Graph Store using NetworkX
|
|
24
|
+
|
|
25
|
+
**Implementation Strategy**:
|
|
26
|
+
- Uses networkx.DiGraph for graph structure
|
|
27
|
+
- Stores Entity objects as node attributes
|
|
28
|
+
- Stores Relation objects as edge attributes
|
|
29
|
+
- Implements ONLY Tier 1 methods
|
|
30
|
+
- Tier 2 methods (traverse, find_paths, etc.) work automatically!
|
|
31
|
+
|
|
32
|
+
**Features**:
|
|
33
|
+
- Fast for small-medium graphs
|
|
34
|
+
- No external dependencies
|
|
35
|
+
- Full Python ecosystem integration
|
|
36
|
+
- Rich graph algorithms from networkx
|
|
37
|
+
|
|
38
|
+
**Limitations**:
|
|
39
|
+
- Not persistent (lost on restart)
|
|
40
|
+
- Limited by RAM
|
|
41
|
+
- No concurrent access control
|
|
42
|
+
- No vector search optimization
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
```python
|
|
46
|
+
store = InMemoryGraphStore()
|
|
47
|
+
await store.initialize()
|
|
48
|
+
|
|
49
|
+
# Add entities
|
|
50
|
+
entity = Entity(id="person_1", entity_type="Person", properties={"name": "Alice"})
|
|
51
|
+
await store.add_entity(entity)
|
|
52
|
+
|
|
53
|
+
# Tier 2 methods work automatically
|
|
54
|
+
paths = await store.traverse("person_1", max_depth=3)
|
|
55
|
+
```
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self):
|
|
59
|
+
"""Initialize in-memory graph store"""
|
|
60
|
+
self.graph: Optional[nx.DiGraph] = None
|
|
61
|
+
self.entities: Dict[str, Entity] = {}
|
|
62
|
+
self.relations: Dict[str, Relation] = {}
|
|
63
|
+
self._initialized = False
|
|
64
|
+
|
|
65
|
+
# =========================================================================
|
|
66
|
+
# TIER 1 IMPLEMENTATION - Core CRUD Operations
|
|
67
|
+
# =========================================================================
|
|
68
|
+
|
|
69
|
+
async def initialize(self) -> None:
|
|
70
|
+
"""Initialize the in-memory graph"""
|
|
71
|
+
if self._initialized:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
self.graph = nx.DiGraph()
|
|
75
|
+
self.entities = {}
|
|
76
|
+
self.relations = {}
|
|
77
|
+
self._initialized = True
|
|
78
|
+
|
|
79
|
+
async def close(self) -> None:
|
|
80
|
+
"""Close and cleanup (nothing to do for in-memory)"""
|
|
81
|
+
self.graph = None
|
|
82
|
+
self.entities = {}
|
|
83
|
+
self.relations = {}
|
|
84
|
+
self._initialized = False
|
|
85
|
+
|
|
86
|
+
async def add_entity(self, entity: Entity) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Add entity to graph
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
entity: Entity to add
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
ValueError: If entity already exists
|
|
95
|
+
RuntimeError: If store not initialized
|
|
96
|
+
"""
|
|
97
|
+
if not self._initialized:
|
|
98
|
+
raise RuntimeError("GraphStore not initialized. Call initialize() first.")
|
|
99
|
+
|
|
100
|
+
if entity.id in self.entities:
|
|
101
|
+
raise ValueError(f"Entity with ID '{entity.id}' already exists")
|
|
102
|
+
|
|
103
|
+
# Add to networkx graph
|
|
104
|
+
self.graph.add_node(entity.id, entity=entity)
|
|
105
|
+
|
|
106
|
+
# Add to entity index
|
|
107
|
+
self.entities[entity.id] = entity
|
|
108
|
+
|
|
109
|
+
async def get_entity(self, entity_id: str) -> Optional[Entity]:
|
|
110
|
+
"""
|
|
111
|
+
Get entity by ID
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
entity_id: Entity ID
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Entity if found, None otherwise
|
|
118
|
+
"""
|
|
119
|
+
if not self._initialized:
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
return self.entities.get(entity_id)
|
|
123
|
+
|
|
124
|
+
async def add_relation(self, relation: Relation) -> None:
|
|
125
|
+
"""
|
|
126
|
+
Add relation to graph
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
relation: Relation to add
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
ValueError: If relation already exists or entities don't exist
|
|
133
|
+
RuntimeError: If store not initialized
|
|
134
|
+
"""
|
|
135
|
+
if not self._initialized:
|
|
136
|
+
raise RuntimeError("GraphStore not initialized. Call initialize() first.")
|
|
137
|
+
|
|
138
|
+
if relation.id in self.relations:
|
|
139
|
+
raise ValueError(f"Relation with ID '{relation.id}' already exists")
|
|
140
|
+
|
|
141
|
+
# Validate entities exist
|
|
142
|
+
if relation.source_id not in self.entities:
|
|
143
|
+
raise ValueError(f"Source entity '{relation.source_id}' not found")
|
|
144
|
+
if relation.target_id not in self.entities:
|
|
145
|
+
raise ValueError(f"Target entity '{relation.target_id}' not found")
|
|
146
|
+
|
|
147
|
+
# Add to networkx graph
|
|
148
|
+
self.graph.add_edge(
|
|
149
|
+
relation.source_id,
|
|
150
|
+
relation.target_id,
|
|
151
|
+
key=relation.id,
|
|
152
|
+
relation=relation,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Add to relation index
|
|
156
|
+
self.relations[relation.id] = relation
|
|
157
|
+
|
|
158
|
+
async def get_relation(self, relation_id: str) -> Optional[Relation]:
|
|
159
|
+
"""
|
|
160
|
+
Get relation by ID
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
relation_id: Relation ID
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Relation if found, None otherwise
|
|
167
|
+
"""
|
|
168
|
+
if not self._initialized:
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
return self.relations.get(relation_id)
|
|
172
|
+
|
|
173
|
+
async def get_neighbors(
|
|
174
|
+
self,
|
|
175
|
+
entity_id: str,
|
|
176
|
+
relation_type: Optional[str] = None,
|
|
177
|
+
direction: str = "outgoing",
|
|
178
|
+
) -> List[Entity]:
|
|
179
|
+
"""
|
|
180
|
+
Get neighboring entities
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
entity_id: Entity ID to get neighbors for
|
|
184
|
+
relation_type: Optional filter by relation type
|
|
185
|
+
direction: "outgoing", "incoming", or "both"
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of neighboring entities
|
|
189
|
+
"""
|
|
190
|
+
if not self._initialized or entity_id not in self.graph:
|
|
191
|
+
return []
|
|
192
|
+
|
|
193
|
+
neighbors = []
|
|
194
|
+
|
|
195
|
+
# Get outgoing neighbors
|
|
196
|
+
if direction in ("outgoing", "both"):
|
|
197
|
+
for target_id in self.graph.successors(entity_id):
|
|
198
|
+
# Check relation type filter
|
|
199
|
+
if relation_type:
|
|
200
|
+
edge_data = self.graph.get_edge_data(entity_id, target_id)
|
|
201
|
+
if edge_data:
|
|
202
|
+
relation = edge_data.get("relation")
|
|
203
|
+
if relation and relation.relation_type == relation_type:
|
|
204
|
+
if target_id in self.entities:
|
|
205
|
+
neighbors.append(self.entities[target_id])
|
|
206
|
+
else:
|
|
207
|
+
if target_id in self.entities:
|
|
208
|
+
neighbors.append(self.entities[target_id])
|
|
209
|
+
|
|
210
|
+
# Get incoming neighbors
|
|
211
|
+
if direction in ("incoming", "both"):
|
|
212
|
+
for source_id in self.graph.predecessors(entity_id):
|
|
213
|
+
# Check relation type filter
|
|
214
|
+
if relation_type:
|
|
215
|
+
edge_data = self.graph.get_edge_data(source_id, entity_id)
|
|
216
|
+
if edge_data:
|
|
217
|
+
relation = edge_data.get("relation")
|
|
218
|
+
if relation and relation.relation_type == relation_type:
|
|
219
|
+
if source_id in self.entities:
|
|
220
|
+
neighbors.append(self.entities[source_id])
|
|
221
|
+
else:
|
|
222
|
+
if source_id in self.entities:
|
|
223
|
+
neighbors.append(self.entities[source_id])
|
|
224
|
+
|
|
225
|
+
return neighbors
|
|
226
|
+
|
|
227
|
+
async def get_all_entities(
|
|
228
|
+
self, entity_type: Optional[str] = None, limit: Optional[int] = None
|
|
229
|
+
) -> List[Entity]:
|
|
230
|
+
"""
|
|
231
|
+
Get all entities, optionally filtered by type
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
entity_type: Optional filter by entity type
|
|
235
|
+
limit: Optional limit on number of entities
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
List of entities
|
|
239
|
+
"""
|
|
240
|
+
if not self._initialized:
|
|
241
|
+
return []
|
|
242
|
+
|
|
243
|
+
entities = list(self.entities.values())
|
|
244
|
+
|
|
245
|
+
# Filter by entity type if specified
|
|
246
|
+
if entity_type:
|
|
247
|
+
entities = [e for e in entities if e.entity_type == entity_type]
|
|
248
|
+
|
|
249
|
+
# Apply limit if specified
|
|
250
|
+
if limit:
|
|
251
|
+
entities = entities[:limit]
|
|
252
|
+
|
|
253
|
+
return entities
|
|
254
|
+
|
|
255
|
+
# =========================================================================
|
|
256
|
+
# TIER 2 METHODS - Inherited from base class
|
|
257
|
+
# =========================================================================
|
|
258
|
+
# traverse(), find_paths(), subgraph_query(), execute_query()
|
|
259
|
+
# work automatically through default implementations!
|
|
260
|
+
|
|
261
|
+
# =========================================================================
|
|
262
|
+
# OPTIONAL: Override Tier 2 methods for optimization
|
|
263
|
+
# =========================================================================
|
|
264
|
+
|
|
265
|
+
async def vector_search(
|
|
266
|
+
self,
|
|
267
|
+
query_embedding: List[float],
|
|
268
|
+
entity_type: Optional[str] = None,
|
|
269
|
+
max_results: int = 10,
|
|
270
|
+
score_threshold: float = 0.0,
|
|
271
|
+
) -> List[tuple]:
|
|
272
|
+
"""
|
|
273
|
+
Optimized vector search for in-memory store
|
|
274
|
+
|
|
275
|
+
Performs brute-force cosine similarity over all entities with embeddings.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
query_embedding: Query vector
|
|
279
|
+
entity_type: Optional filter by entity type
|
|
280
|
+
max_results: Maximum number of results
|
|
281
|
+
score_threshold: Minimum similarity score (0.0-1.0)
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
List of (entity, similarity_score) tuples, sorted descending
|
|
285
|
+
"""
|
|
286
|
+
if not self._initialized:
|
|
287
|
+
return []
|
|
288
|
+
|
|
289
|
+
if not query_embedding:
|
|
290
|
+
raise ValueError("Query embedding cannot be empty")
|
|
291
|
+
|
|
292
|
+
import numpy as np
|
|
293
|
+
|
|
294
|
+
query_vec = np.array(query_embedding, dtype=np.float32)
|
|
295
|
+
query_norm = np.linalg.norm(query_vec)
|
|
296
|
+
|
|
297
|
+
if query_norm == 0:
|
|
298
|
+
return []
|
|
299
|
+
|
|
300
|
+
scored_entities = []
|
|
301
|
+
|
|
302
|
+
for entity in self.entities.values():
|
|
303
|
+
# Filter by entity type if specified
|
|
304
|
+
if entity_type and entity.entity_type != entity_type:
|
|
305
|
+
continue
|
|
306
|
+
|
|
307
|
+
# Skip entities without embeddings
|
|
308
|
+
if not entity.embedding:
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
# Compute cosine similarity
|
|
312
|
+
entity_vec = np.array(entity.embedding, dtype=np.float32)
|
|
313
|
+
entity_norm = np.linalg.norm(entity_vec)
|
|
314
|
+
|
|
315
|
+
if entity_norm == 0:
|
|
316
|
+
continue
|
|
317
|
+
|
|
318
|
+
# Cosine similarity
|
|
319
|
+
similarity = np.dot(query_vec, entity_vec) / (query_norm * entity_norm)
|
|
320
|
+
# Normalize to 0-1 range
|
|
321
|
+
similarity = (similarity + 1) / 2
|
|
322
|
+
|
|
323
|
+
# Filter by threshold
|
|
324
|
+
if similarity >= score_threshold:
|
|
325
|
+
scored_entities.append((entity, float(similarity)))
|
|
326
|
+
|
|
327
|
+
# Sort by score descending and return top results
|
|
328
|
+
scored_entities.sort(key=lambda x: x[1], reverse=True)
|
|
329
|
+
return scored_entities[:max_results]
|
|
330
|
+
|
|
331
|
+
async def text_search(
|
|
332
|
+
self,
|
|
333
|
+
query_text: str,
|
|
334
|
+
entity_type: Optional[str] = None,
|
|
335
|
+
max_results: int = 10,
|
|
336
|
+
score_threshold: float = 0.0,
|
|
337
|
+
method: str = "bm25",
|
|
338
|
+
) -> List[tuple]:
|
|
339
|
+
"""
|
|
340
|
+
Optimized text search for in-memory store
|
|
341
|
+
|
|
342
|
+
Performs text similarity search over entity properties using BM25, Jaccard,
|
|
343
|
+
cosine similarity, or Levenshtein distance.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
query_text: Query text string
|
|
347
|
+
entity_type: Optional filter by entity type
|
|
348
|
+
max_results: Maximum number of results
|
|
349
|
+
score_threshold: Minimum similarity score (0.0-1.0)
|
|
350
|
+
method: Similarity method ("bm25", "jaccard", "cosine", "levenshtein")
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
List of (entity, similarity_score) tuples, sorted descending
|
|
354
|
+
"""
|
|
355
|
+
if not self._initialized:
|
|
356
|
+
return []
|
|
357
|
+
|
|
358
|
+
if not query_text:
|
|
359
|
+
return []
|
|
360
|
+
|
|
361
|
+
from aiecs.application.knowledge_graph.search.text_similarity import (
|
|
362
|
+
BM25Scorer,
|
|
363
|
+
jaccard_similarity_text,
|
|
364
|
+
cosine_similarity_text,
|
|
365
|
+
normalized_levenshtein_similarity,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Get candidate entities
|
|
369
|
+
entities = list(self.entities.values())
|
|
370
|
+
if entity_type:
|
|
371
|
+
entities = [e for e in entities if e.entity_type == entity_type]
|
|
372
|
+
|
|
373
|
+
if not entities:
|
|
374
|
+
return []
|
|
375
|
+
|
|
376
|
+
scored_entities = []
|
|
377
|
+
|
|
378
|
+
# Extract text from entities (combine properties into searchable text)
|
|
379
|
+
entity_texts = []
|
|
380
|
+
for entity in entities:
|
|
381
|
+
# Combine all string properties into searchable text
|
|
382
|
+
text_parts = []
|
|
383
|
+
for key, value in entity.properties.items():
|
|
384
|
+
if isinstance(value, str):
|
|
385
|
+
text_parts.append(value)
|
|
386
|
+
elif isinstance(value, (list, tuple)):
|
|
387
|
+
text_parts.extend(str(v) for v in value if isinstance(v, str))
|
|
388
|
+
entity_text = " ".join(text_parts)
|
|
389
|
+
entity_texts.append((entity, entity_text))
|
|
390
|
+
|
|
391
|
+
if method == "bm25":
|
|
392
|
+
# Use BM25 scorer
|
|
393
|
+
corpus = [text for _, text in entity_texts]
|
|
394
|
+
scorer = BM25Scorer(corpus)
|
|
395
|
+
scores = scorer.score(query_text)
|
|
396
|
+
|
|
397
|
+
for (entity, _), score in zip(entity_texts, scores):
|
|
398
|
+
if score >= score_threshold:
|
|
399
|
+
scored_entities.append((entity, float(score)))
|
|
400
|
+
|
|
401
|
+
elif method == "jaccard":
|
|
402
|
+
for entity, text in entity_texts:
|
|
403
|
+
score = jaccard_similarity_text(query_text, text)
|
|
404
|
+
if score >= score_threshold:
|
|
405
|
+
scored_entities.append((entity, score))
|
|
406
|
+
|
|
407
|
+
elif method == "cosine":
|
|
408
|
+
for entity, text in entity_texts:
|
|
409
|
+
score = cosine_similarity_text(query_text, text)
|
|
410
|
+
if score >= score_threshold:
|
|
411
|
+
scored_entities.append((entity, score))
|
|
412
|
+
|
|
413
|
+
elif method == "levenshtein":
|
|
414
|
+
for entity, text in entity_texts:
|
|
415
|
+
score = normalized_levenshtein_similarity(query_text, text)
|
|
416
|
+
if score >= score_threshold:
|
|
417
|
+
scored_entities.append((entity, score))
|
|
418
|
+
|
|
419
|
+
else:
|
|
420
|
+
raise ValueError(
|
|
421
|
+
f"Unknown text search method: {method}. Use 'bm25', 'jaccard', 'cosine', or 'levenshtein'"
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Sort by score descending and return top results
|
|
425
|
+
scored_entities.sort(key=lambda x: x[1], reverse=True)
|
|
426
|
+
return scored_entities[:max_results]
|
|
427
|
+
|
|
428
|
+
async def find_paths(
|
|
429
|
+
self,
|
|
430
|
+
source_entity_id: str,
|
|
431
|
+
target_entity_id: str,
|
|
432
|
+
max_depth: int = 5,
|
|
433
|
+
max_paths: int = 10,
|
|
434
|
+
) -> List:
|
|
435
|
+
"""
|
|
436
|
+
Optimized path finding using networkx algorithms
|
|
437
|
+
|
|
438
|
+
Overrides default implementation to use networkx.all_simple_paths
|
|
439
|
+
for better performance.
|
|
440
|
+
"""
|
|
441
|
+
from aiecs.domain.knowledge_graph.models.path import Path
|
|
442
|
+
|
|
443
|
+
if not self._initialized:
|
|
444
|
+
return []
|
|
445
|
+
|
|
446
|
+
if source_entity_id not in self.graph or target_entity_id not in self.graph:
|
|
447
|
+
return []
|
|
448
|
+
|
|
449
|
+
try:
|
|
450
|
+
# Use networkx's optimized path finding
|
|
451
|
+
paths = []
|
|
452
|
+
for node_path in nx.all_simple_paths(
|
|
453
|
+
self.graph,
|
|
454
|
+
source_entity_id,
|
|
455
|
+
target_entity_id,
|
|
456
|
+
cutoff=max_depth,
|
|
457
|
+
):
|
|
458
|
+
# Convert node IDs to Entity and Relation objects
|
|
459
|
+
entities = [
|
|
460
|
+
self.entities[node_id] for node_id in node_path if node_id in self.entities
|
|
461
|
+
]
|
|
462
|
+
|
|
463
|
+
# Get relations between consecutive nodes
|
|
464
|
+
edges = []
|
|
465
|
+
for i in range(len(node_path) - 1):
|
|
466
|
+
edge_data = self.graph.get_edge_data(node_path[i], node_path[i + 1])
|
|
467
|
+
if edge_data and "relation" in edge_data:
|
|
468
|
+
edges.append(edge_data["relation"])
|
|
469
|
+
|
|
470
|
+
if len(entities) == len(node_path):
|
|
471
|
+
paths.append(Path(nodes=entities, edges=edges))
|
|
472
|
+
|
|
473
|
+
if len(paths) >= max_paths:
|
|
474
|
+
break
|
|
475
|
+
|
|
476
|
+
return paths
|
|
477
|
+
|
|
478
|
+
except nx.NetworkXNoPath:
|
|
479
|
+
return []
|
|
480
|
+
|
|
481
|
+
# =========================================================================
|
|
482
|
+
# UTILITY METHODS
|
|
483
|
+
# =========================================================================
|
|
484
|
+
|
|
485
|
+
def get_stats(self) -> Dict[str, int]:
|
|
486
|
+
"""
|
|
487
|
+
Get graph statistics
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
Dictionary with node count, edge count, etc.
|
|
491
|
+
"""
|
|
492
|
+
if not self._initialized:
|
|
493
|
+
return {"nodes": 0, "edges": 0, "entities": 0, "relations": 0}
|
|
494
|
+
|
|
495
|
+
return {
|
|
496
|
+
"nodes": self.graph.number_of_nodes(),
|
|
497
|
+
"edges": self.graph.number_of_edges(),
|
|
498
|
+
"entities": len(self.entities),
|
|
499
|
+
"relations": len(self.relations),
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
def clear(self) -> None:
|
|
503
|
+
"""Clear all data from the graph"""
|
|
504
|
+
if self._initialized:
|
|
505
|
+
self.graph.clear()
|
|
506
|
+
self.entities.clear()
|
|
507
|
+
self.relations.clear()
|
|
508
|
+
|
|
509
|
+
def __str__(self) -> str:
|
|
510
|
+
stats = self.get_stats()
|
|
511
|
+
return f"InMemoryGraphStore(entities={stats['entities']}, relations={stats['relations']})"
|
|
512
|
+
|
|
513
|
+
def __repr__(self) -> str:
|
|
514
|
+
return self.__str__()
|