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,410 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lazy Loading Support for Graph Storage
|
|
3
|
+
|
|
4
|
+
Provides lazy loading of entities and relations to reduce memory usage
|
|
5
|
+
and improve performance when working with large graphs.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Optional, List, Dict, Any, AsyncIterator
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
|
|
12
|
+
from aiecs.domain.knowledge_graph.models.entity import Entity
|
|
13
|
+
from aiecs.domain.knowledge_graph.models.relation import Relation
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class LazyEntity:
|
|
20
|
+
"""
|
|
21
|
+
Lazy-loaded entity wrapper
|
|
22
|
+
|
|
23
|
+
Only loads full entity data when accessed, reducing memory usage.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
```python
|
|
27
|
+
lazy_entity = LazyEntity(id="person_1", store=store)
|
|
28
|
+
|
|
29
|
+
# Entity not loaded yet
|
|
30
|
+
print(lazy_entity.id) # Just ID, no DB query
|
|
31
|
+
|
|
32
|
+
# Load full entity when needed
|
|
33
|
+
entity = await lazy_entity.load()
|
|
34
|
+
print(entity.properties["name"]) # DB query executed
|
|
35
|
+
```
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
id: str
|
|
39
|
+
entity_type: Optional[str] = None
|
|
40
|
+
_store: Any = None
|
|
41
|
+
_loaded_entity: Optional[Entity] = field(default=None, init=False, repr=False)
|
|
42
|
+
_is_loaded: bool = field(default=False, init=False, repr=False)
|
|
43
|
+
|
|
44
|
+
async def load(self, force: bool = False) -> Optional[Entity]:
|
|
45
|
+
"""
|
|
46
|
+
Load the full entity from storage
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
force: Force reload even if already loaded
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Full Entity object or None if not found
|
|
53
|
+
"""
|
|
54
|
+
if not force and self._is_loaded:
|
|
55
|
+
return self._loaded_entity
|
|
56
|
+
|
|
57
|
+
if not self._store:
|
|
58
|
+
raise RuntimeError("No store provided for lazy loading")
|
|
59
|
+
|
|
60
|
+
self._loaded_entity = await self._store.get_entity(self.id)
|
|
61
|
+
self._is_loaded = True
|
|
62
|
+
return self._loaded_entity
|
|
63
|
+
|
|
64
|
+
async def get(self, property_name: str, default: Any = None) -> Any:
|
|
65
|
+
"""
|
|
66
|
+
Get a specific property (loads entity if needed)
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
property_name: Property to retrieve
|
|
70
|
+
default: Default value if property not found
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Property value or default
|
|
74
|
+
"""
|
|
75
|
+
entity = await self.load()
|
|
76
|
+
if not entity:
|
|
77
|
+
return default
|
|
78
|
+
return entity.properties.get(property_name, default)
|
|
79
|
+
|
|
80
|
+
def is_loaded(self) -> bool:
|
|
81
|
+
"""Check if entity has been loaded"""
|
|
82
|
+
return self._is_loaded
|
|
83
|
+
|
|
84
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
85
|
+
"""Convert to dictionary (only includes loaded data)"""
|
|
86
|
+
result = {"id": self.id}
|
|
87
|
+
if self.entity_type:
|
|
88
|
+
result["entity_type"] = self.entity_type
|
|
89
|
+
if self._is_loaded and self._loaded_entity:
|
|
90
|
+
result["properties"] = self._loaded_entity.properties
|
|
91
|
+
return result
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class LazyRelation:
|
|
96
|
+
"""
|
|
97
|
+
Lazy-loaded relation wrapper
|
|
98
|
+
|
|
99
|
+
Only loads full relation data when accessed.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
id: str
|
|
103
|
+
source_id: str
|
|
104
|
+
target_id: str
|
|
105
|
+
relation_type: Optional[str] = None
|
|
106
|
+
_store: Any = None
|
|
107
|
+
_loaded_relation: Optional[Relation] = field(default=None, init=False, repr=False)
|
|
108
|
+
_is_loaded: bool = field(default=False, init=False, repr=False)
|
|
109
|
+
|
|
110
|
+
async def load(self, force: bool = False) -> Optional[Relation]:
|
|
111
|
+
"""Load the full relation from storage"""
|
|
112
|
+
if not force and self._is_loaded:
|
|
113
|
+
return self._loaded_relation
|
|
114
|
+
|
|
115
|
+
if not self._store:
|
|
116
|
+
raise RuntimeError("No store provided for lazy loading")
|
|
117
|
+
|
|
118
|
+
self._loaded_relation = await self._store.get_relation(self.id)
|
|
119
|
+
self._is_loaded = True
|
|
120
|
+
return self._loaded_relation
|
|
121
|
+
|
|
122
|
+
async def get_source(self) -> Optional[Entity]:
|
|
123
|
+
"""Get source entity (lazy loaded)"""
|
|
124
|
+
if not self._store:
|
|
125
|
+
return None
|
|
126
|
+
return await self._store.get_entity(self.source_id)
|
|
127
|
+
|
|
128
|
+
async def get_target(self) -> Optional[Entity]:
|
|
129
|
+
"""Get target entity (lazy loaded)"""
|
|
130
|
+
if not self._store:
|
|
131
|
+
return None
|
|
132
|
+
return await self._store.get_entity(self.target_id)
|
|
133
|
+
|
|
134
|
+
def is_loaded(self) -> bool:
|
|
135
|
+
"""Check if relation has been loaded"""
|
|
136
|
+
return self._is_loaded
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class LazyLoadingMixin:
|
|
140
|
+
"""
|
|
141
|
+
Mixin providing lazy loading capabilities for graph stores
|
|
142
|
+
|
|
143
|
+
Enables deferred loading of entities and relations to reduce memory usage.
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
```python
|
|
147
|
+
class MyGraphStore(GraphStore, LazyLoadingMixin):
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
store = MyGraphStore()
|
|
151
|
+
|
|
152
|
+
# Get lazy entities (no DB queries yet)
|
|
153
|
+
lazy_entities = await store.get_lazy_entities(entity_type="Person")
|
|
154
|
+
|
|
155
|
+
# Load specific entities as needed
|
|
156
|
+
for lazy_entity in lazy_entities[:10]:
|
|
157
|
+
entity = await lazy_entity.load()
|
|
158
|
+
print(entity.properties["name"])
|
|
159
|
+
```
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
async def get_lazy_entity(self, entity_id: str) -> LazyEntity:
|
|
163
|
+
"""
|
|
164
|
+
Get a lazy-loaded entity wrapper
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
entity_id: Entity ID
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
LazyEntity wrapper (not yet loaded from DB)
|
|
171
|
+
"""
|
|
172
|
+
return LazyEntity(id=entity_id, _store=self)
|
|
173
|
+
|
|
174
|
+
async def get_lazy_entities(
|
|
175
|
+
self, entity_type: Optional[str] = None, limit: Optional[int] = None
|
|
176
|
+
) -> List[LazyEntity]:
|
|
177
|
+
"""
|
|
178
|
+
Get lazy-loaded entity wrappers
|
|
179
|
+
|
|
180
|
+
Only fetches IDs and types, not full entity data.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
entity_type: Filter by entity type
|
|
184
|
+
limit: Maximum number of entities
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
List of LazyEntity wrappers
|
|
188
|
+
"""
|
|
189
|
+
# Get lightweight entity list (IDs only)
|
|
190
|
+
entities = await self._get_entity_ids(entity_type=entity_type, limit=limit)
|
|
191
|
+
|
|
192
|
+
return [LazyEntity(id=eid, entity_type=etype, _store=self) for eid, etype in entities]
|
|
193
|
+
|
|
194
|
+
async def _get_entity_ids(
|
|
195
|
+
self, entity_type: Optional[str] = None, limit: Optional[int] = None
|
|
196
|
+
) -> List[tuple[str, str]]:
|
|
197
|
+
"""
|
|
198
|
+
Get entity IDs and types only (efficient query)
|
|
199
|
+
|
|
200
|
+
Backends should override this for better performance.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
List of (entity_id, entity_type) tuples
|
|
204
|
+
"""
|
|
205
|
+
# Default implementation - load full entities (inefficient)
|
|
206
|
+
entities = await self.get_all_entities(entity_type=entity_type, limit=limit)
|
|
207
|
+
return [(e.id, e.entity_type) for e in entities]
|
|
208
|
+
|
|
209
|
+
async def get_lazy_neighbors(
|
|
210
|
+
self,
|
|
211
|
+
entity_id: str,
|
|
212
|
+
relation_type: Optional[str] = None,
|
|
213
|
+
direction: str = "outgoing",
|
|
214
|
+
) -> List[LazyEntity]:
|
|
215
|
+
"""
|
|
216
|
+
Get lazy-loaded neighbor entities
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
entity_id: Source entity ID
|
|
220
|
+
relation_type: Filter by relation type
|
|
221
|
+
direction: "outgoing", "incoming", or "both"
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
List of LazyEntity wrappers for neighbors
|
|
225
|
+
"""
|
|
226
|
+
# Get neighbor IDs without loading full entities
|
|
227
|
+
neighbor_ids = await self._get_neighbor_ids(
|
|
228
|
+
entity_id=entity_id,
|
|
229
|
+
relation_type=relation_type,
|
|
230
|
+
direction=direction,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return [LazyEntity(id=nid, _store=self) for nid in neighbor_ids]
|
|
234
|
+
|
|
235
|
+
async def _get_neighbor_ids(
|
|
236
|
+
self,
|
|
237
|
+
entity_id: str,
|
|
238
|
+
relation_type: Optional[str] = None,
|
|
239
|
+
direction: str = "outgoing",
|
|
240
|
+
) -> List[str]:
|
|
241
|
+
"""
|
|
242
|
+
Get neighbor entity IDs only (efficient query)
|
|
243
|
+
|
|
244
|
+
Backends should override this for better performance.
|
|
245
|
+
"""
|
|
246
|
+
# Default implementation - load full neighbors
|
|
247
|
+
neighbors = await self.get_neighbors(
|
|
248
|
+
entity_id=entity_id,
|
|
249
|
+
relation_type=relation_type,
|
|
250
|
+
direction=direction,
|
|
251
|
+
)
|
|
252
|
+
return [n.id for n in neighbors]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class EntityBatchLoader:
|
|
256
|
+
"""
|
|
257
|
+
Batch loader for efficient loading of multiple entities
|
|
258
|
+
|
|
259
|
+
Collects entity IDs and loads them in batches to reduce DB queries.
|
|
260
|
+
Implements the DataLoader pattern for GraphQL-like efficiency.
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
```python
|
|
264
|
+
loader = EntityBatchLoader(store, batch_size=100)
|
|
265
|
+
|
|
266
|
+
# Queue entities for loading
|
|
267
|
+
e1_future = loader.load("entity_1")
|
|
268
|
+
e2_future = loader.load("entity_2")
|
|
269
|
+
# ... queue many more
|
|
270
|
+
|
|
271
|
+
# Load all queued entities in batch
|
|
272
|
+
await loader.dispatch()
|
|
273
|
+
|
|
274
|
+
# Get results
|
|
275
|
+
entity1 = await e1_future
|
|
276
|
+
entity2 = await e2_future
|
|
277
|
+
```
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
def __init__(self, store: Any, batch_size: int = 100):
|
|
281
|
+
"""
|
|
282
|
+
Initialize batch loader
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
store: Graph store instance
|
|
286
|
+
batch_size: Maximum batch size
|
|
287
|
+
"""
|
|
288
|
+
self.store = store
|
|
289
|
+
self.batch_size = batch_size
|
|
290
|
+
self._queue: List[str] = []
|
|
291
|
+
self._cache: Dict[str, Optional[Entity]] = {}
|
|
292
|
+
self._futures: Dict[str, Any] = {}
|
|
293
|
+
|
|
294
|
+
async def load(self, entity_id: str) -> Optional[Entity]:
|
|
295
|
+
"""
|
|
296
|
+
Load an entity (batched)
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
entity_id: Entity ID to load
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Entity or None if not found
|
|
303
|
+
"""
|
|
304
|
+
# Check cache
|
|
305
|
+
if entity_id in self._cache:
|
|
306
|
+
return self._cache[entity_id]
|
|
307
|
+
|
|
308
|
+
# Queue for batch loading
|
|
309
|
+
if entity_id not in self._queue:
|
|
310
|
+
self._queue.append(entity_id)
|
|
311
|
+
|
|
312
|
+
# Dispatch if batch is full
|
|
313
|
+
if len(self._queue) >= self.batch_size:
|
|
314
|
+
await self.dispatch()
|
|
315
|
+
|
|
316
|
+
return self._cache.get(entity_id)
|
|
317
|
+
|
|
318
|
+
async def dispatch(self) -> None:
|
|
319
|
+
"""
|
|
320
|
+
Dispatch all queued loads
|
|
321
|
+
|
|
322
|
+
Loads all queued entities in batch and updates cache.
|
|
323
|
+
"""
|
|
324
|
+
if not self._queue:
|
|
325
|
+
return
|
|
326
|
+
|
|
327
|
+
# Get unique IDs
|
|
328
|
+
entity_ids = list(set(self._queue))
|
|
329
|
+
self._queue.clear()
|
|
330
|
+
|
|
331
|
+
# Batch load
|
|
332
|
+
entities = await self._batch_fetch_entities(entity_ids)
|
|
333
|
+
|
|
334
|
+
# Update cache
|
|
335
|
+
entity_map = {e.id: e for e in entities}
|
|
336
|
+
for eid in entity_ids:
|
|
337
|
+
self._cache[eid] = entity_map.get(eid)
|
|
338
|
+
|
|
339
|
+
async def _batch_fetch_entities(self, entity_ids: List[str]) -> List[Entity]:
|
|
340
|
+
"""
|
|
341
|
+
Fetch multiple entities efficiently
|
|
342
|
+
|
|
343
|
+
Backends should optimize this for batch retrieval.
|
|
344
|
+
"""
|
|
345
|
+
entities = []
|
|
346
|
+
for eid in entity_ids:
|
|
347
|
+
entity = await self.store.get_entity(eid)
|
|
348
|
+
if entity:
|
|
349
|
+
entities.append(entity)
|
|
350
|
+
return entities
|
|
351
|
+
|
|
352
|
+
def clear_cache(self) -> None:
|
|
353
|
+
"""Clear the entity cache"""
|
|
354
|
+
self._cache.clear()
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
async def lazy_traverse(
|
|
358
|
+
store: Any, start_entity_id: str, max_depth: int = 3, batch_size: int = 100
|
|
359
|
+
) -> AsyncIterator[LazyEntity]:
|
|
360
|
+
"""
|
|
361
|
+
Lazy graph traversal with batch loading
|
|
362
|
+
|
|
363
|
+
Traverses the graph lazily, yielding entities as they're discovered
|
|
364
|
+
without loading the entire subgraph into memory.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
store: Graph store instance
|
|
368
|
+
start_entity_id: Starting entity ID
|
|
369
|
+
max_depth: Maximum traversal depth
|
|
370
|
+
batch_size: Batch size for loading
|
|
371
|
+
|
|
372
|
+
Yields:
|
|
373
|
+
LazyEntity instances as graph is traversed
|
|
374
|
+
|
|
375
|
+
Example:
|
|
376
|
+
```python
|
|
377
|
+
async for lazy_entity in lazy_traverse(store, "person_1", max_depth=3):
|
|
378
|
+
entity = await lazy_entity.load()
|
|
379
|
+
print(f"Found: {entity.id}")
|
|
380
|
+
```
|
|
381
|
+
"""
|
|
382
|
+
visited = set()
|
|
383
|
+
# loader = EntityBatchLoader(store, batch_size=batch_size) # Reserved for
|
|
384
|
+
# future use
|
|
385
|
+
|
|
386
|
+
# BFS traversal
|
|
387
|
+
current_level = [start_entity_id]
|
|
388
|
+
depth = 0
|
|
389
|
+
|
|
390
|
+
while current_level and depth <= max_depth:
|
|
391
|
+
next_level = []
|
|
392
|
+
|
|
393
|
+
for entity_id in current_level:
|
|
394
|
+
if entity_id in visited:
|
|
395
|
+
continue
|
|
396
|
+
|
|
397
|
+
visited.add(entity_id)
|
|
398
|
+
|
|
399
|
+
# Yield lazy entity
|
|
400
|
+
lazy_entity = LazyEntity(id=entity_id, _store=store)
|
|
401
|
+
yield lazy_entity
|
|
402
|
+
|
|
403
|
+
# Get neighbors for next level
|
|
404
|
+
neighbors = await store.get_neighbors(entity_id, direction="outgoing")
|
|
405
|
+
for neighbor in neighbors:
|
|
406
|
+
if neighbor.id not in visited:
|
|
407
|
+
next_level.append(neighbor.id)
|
|
408
|
+
|
|
409
|
+
current_level = next_level
|
|
410
|
+
depth += 1
|