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,483 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pagination Support for Graph Storage
|
|
3
|
+
|
|
4
|
+
Provides cursor-based and offset-based pagination for large result sets,
|
|
5
|
+
enabling efficient navigation through millions of entities and relations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import base64
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Generic, TypeVar, List, Optional, Dict, Any, TYPE_CHECKING
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import Enum
|
|
14
|
+
|
|
15
|
+
from aiecs.domain.knowledge_graph.models.entity import Entity
|
|
16
|
+
from aiecs.domain.knowledge_graph.models.relation import Relation
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from aiecs.infrastructure.graph_storage.protocols import PaginationMixinProtocol
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
T = TypeVar("T")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PaginationType(str, Enum):
|
|
27
|
+
"""Type of pagination strategy"""
|
|
28
|
+
|
|
29
|
+
OFFSET = "offset" # Traditional offset-based pagination
|
|
30
|
+
# Cursor-based pagination (more efficient for large datasets)
|
|
31
|
+
CURSOR = "cursor"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class PageInfo:
|
|
36
|
+
"""Metadata about pagination state"""
|
|
37
|
+
|
|
38
|
+
has_next_page: bool
|
|
39
|
+
has_previous_page: bool
|
|
40
|
+
start_cursor: Optional[str] = None
|
|
41
|
+
end_cursor: Optional[str] = None
|
|
42
|
+
total_count: Optional[int] = None # Only available for offset pagination
|
|
43
|
+
page_size: int = 100
|
|
44
|
+
|
|
45
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
46
|
+
"""Convert to dictionary"""
|
|
47
|
+
return {
|
|
48
|
+
"has_next_page": self.has_next_page,
|
|
49
|
+
"has_previous_page": self.has_previous_page,
|
|
50
|
+
"start_cursor": self.start_cursor,
|
|
51
|
+
"end_cursor": self.end_cursor,
|
|
52
|
+
"total_count": self.total_count,
|
|
53
|
+
"page_size": self.page_size,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class Page(Generic[T]):
|
|
59
|
+
"""
|
|
60
|
+
Generic page of results
|
|
61
|
+
|
|
62
|
+
Supports both cursor-based and offset-based pagination.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
```python
|
|
66
|
+
page = await store.paginate_entities(page_size=100)
|
|
67
|
+
|
|
68
|
+
for entity in page.items:
|
|
69
|
+
print(entity.id)
|
|
70
|
+
|
|
71
|
+
if page.page_info.has_next_page:
|
|
72
|
+
next_page = await store.paginate_entities(
|
|
73
|
+
page_size=100,
|
|
74
|
+
cursor=page.page_info.end_cursor
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
items: List[T]
|
|
80
|
+
page_info: PageInfo
|
|
81
|
+
|
|
82
|
+
def __len__(self) -> int:
|
|
83
|
+
"""Return number of items in page"""
|
|
84
|
+
return len(self.items)
|
|
85
|
+
|
|
86
|
+
def __iter__(self):
|
|
87
|
+
"""Allow iteration over items"""
|
|
88
|
+
return iter(self.items)
|
|
89
|
+
|
|
90
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
91
|
+
"""Convert to dictionary"""
|
|
92
|
+
return {
|
|
93
|
+
"items": [item.model_dump() if hasattr(item, "model_dump") else item for item in self.items],
|
|
94
|
+
"page_info": self.page_info.to_dict(),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class PaginationCursor:
|
|
99
|
+
"""
|
|
100
|
+
Cursor for efficient pagination
|
|
101
|
+
|
|
102
|
+
Encodes pagination state (last seen ID, direction) into an opaque string.
|
|
103
|
+
More efficient than offset pagination for large datasets.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def encode(last_id: str, direction: str = "forward") -> str:
|
|
108
|
+
"""
|
|
109
|
+
Encode cursor from last seen ID
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
last_id: Last entity/relation ID seen
|
|
113
|
+
direction: Pagination direction ("forward" or "backward")
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Encoded cursor string
|
|
117
|
+
"""
|
|
118
|
+
cursor_data = {"id": last_id, "dir": direction}
|
|
119
|
+
json_str = json.dumps(cursor_data)
|
|
120
|
+
encoded = base64.b64encode(json_str.encode()).decode()
|
|
121
|
+
return encoded
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def decode(cursor: str) -> Dict[str, str]:
|
|
125
|
+
"""
|
|
126
|
+
Decode cursor to get last seen ID and direction
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
cursor: Encoded cursor string
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Dictionary with 'id' and 'dir' keys
|
|
133
|
+
|
|
134
|
+
Raises:
|
|
135
|
+
ValueError: If cursor is invalid
|
|
136
|
+
"""
|
|
137
|
+
try:
|
|
138
|
+
decoded = base64.b64decode(cursor.encode()).decode()
|
|
139
|
+
cursor_data = json.loads(decoded)
|
|
140
|
+
|
|
141
|
+
if "id" not in cursor_data:
|
|
142
|
+
raise ValueError("Cursor missing 'id' field")
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
"id": cursor_data["id"],
|
|
146
|
+
"dir": cursor_data.get("dir", "forward"),
|
|
147
|
+
}
|
|
148
|
+
except Exception as e:
|
|
149
|
+
raise ValueError(f"Invalid cursor: {e}")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class PaginationMixin:
|
|
153
|
+
"""
|
|
154
|
+
Mixin providing pagination capabilities for graph stores
|
|
155
|
+
|
|
156
|
+
Adds cursor-based and offset-based pagination methods for entities and relations.
|
|
157
|
+
|
|
158
|
+
This mixin expects the class it's mixed into to implement `PaginationMixinProtocol`,
|
|
159
|
+
specifically the `get_all_entities()` method.
|
|
160
|
+
|
|
161
|
+
Example:
|
|
162
|
+
```python
|
|
163
|
+
class MyGraphStore(GraphStore, PaginationMixin):
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
store = MyGraphStore()
|
|
167
|
+
|
|
168
|
+
# Cursor-based pagination (recommended for large datasets)
|
|
169
|
+
page1 = await store.paginate_entities(page_size=100)
|
|
170
|
+
page2 = await store.paginate_entities(
|
|
171
|
+
page_size=100,
|
|
172
|
+
cursor=page1.page_info.end_cursor
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Offset-based pagination
|
|
176
|
+
page = await store.paginate_entities_offset(page=1, page_size=100)
|
|
177
|
+
```
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
if TYPE_CHECKING:
|
|
181
|
+
# Type hint for mypy: this mixin expects PaginationMixinProtocol
|
|
182
|
+
async def get_all_entities(
|
|
183
|
+
self, entity_type: Optional[str] = None, limit: Optional[int] = None
|
|
184
|
+
) -> List[Entity]:
|
|
185
|
+
"""Expected method from PaginationMixinProtocol"""
|
|
186
|
+
...
|
|
187
|
+
|
|
188
|
+
async def paginate_entities(
|
|
189
|
+
self,
|
|
190
|
+
entity_type: Optional[str] = None,
|
|
191
|
+
page_size: int = 100,
|
|
192
|
+
cursor: Optional[str] = None,
|
|
193
|
+
order_by: str = "id",
|
|
194
|
+
) -> Page[Entity]:
|
|
195
|
+
"""
|
|
196
|
+
Paginate entities using cursor-based pagination
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
entity_type: Filter by entity type
|
|
200
|
+
page_size: Number of items per page
|
|
201
|
+
cursor: Cursor for next page (None for first page)
|
|
202
|
+
order_by: Field to order by (default: "id")
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Page of entities with pagination info
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
```python
|
|
209
|
+
# First page
|
|
210
|
+
page1 = await store.paginate_entities(page_size=100)
|
|
211
|
+
|
|
212
|
+
# Next page
|
|
213
|
+
if page1.page_info.has_next_page:
|
|
214
|
+
page2 = await store.paginate_entities(
|
|
215
|
+
page_size=100,
|
|
216
|
+
cursor=page1.page_info.end_cursor
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
"""
|
|
220
|
+
# Decode cursor if provided
|
|
221
|
+
last_id = None
|
|
222
|
+
if cursor:
|
|
223
|
+
try:
|
|
224
|
+
cursor_data = PaginationCursor.decode(cursor)
|
|
225
|
+
last_id = cursor_data["id"]
|
|
226
|
+
except ValueError as e:
|
|
227
|
+
logger.warning(f"Invalid cursor: {e}")
|
|
228
|
+
last_id = None
|
|
229
|
+
|
|
230
|
+
# Fetch page_size + 1 to determine if there's a next page
|
|
231
|
+
limit = page_size + 1
|
|
232
|
+
|
|
233
|
+
# Query entities
|
|
234
|
+
entities = await self._fetch_entities_page(
|
|
235
|
+
entity_type=entity_type,
|
|
236
|
+
last_id=last_id,
|
|
237
|
+
limit=limit,
|
|
238
|
+
order_by=order_by,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Check if there's a next page
|
|
242
|
+
has_next = len(entities) > page_size
|
|
243
|
+
if has_next:
|
|
244
|
+
entities = entities[:page_size]
|
|
245
|
+
|
|
246
|
+
# Create cursors
|
|
247
|
+
start_cursor = PaginationCursor.encode(entities[0].id) if entities else None
|
|
248
|
+
end_cursor = PaginationCursor.encode(entities[-1].id) if entities else None
|
|
249
|
+
|
|
250
|
+
# Create page info
|
|
251
|
+
page_info = PageInfo(
|
|
252
|
+
has_next_page=has_next,
|
|
253
|
+
has_previous_page=cursor is not None,
|
|
254
|
+
start_cursor=start_cursor,
|
|
255
|
+
end_cursor=end_cursor,
|
|
256
|
+
page_size=page_size,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return Page(items=entities, page_info=page_info)
|
|
260
|
+
|
|
261
|
+
async def _fetch_entities_page(
|
|
262
|
+
self,
|
|
263
|
+
entity_type: Optional[str],
|
|
264
|
+
last_id: Optional[str],
|
|
265
|
+
limit: int,
|
|
266
|
+
order_by: str,
|
|
267
|
+
) -> List[Entity]:
|
|
268
|
+
"""
|
|
269
|
+
Fetch a page of entities (backend-specific implementation)
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
entity_type: Filter by entity type
|
|
273
|
+
last_id: Last entity ID from previous page (for cursor)
|
|
274
|
+
limit: Maximum number of entities to fetch
|
|
275
|
+
order_by: Field to order by
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
List of entities
|
|
279
|
+
"""
|
|
280
|
+
# This is a default implementation using get_all_entities
|
|
281
|
+
# Backends should override this for better performance
|
|
282
|
+
|
|
283
|
+
all_entities = await self.get_all_entities(entity_type=entity_type, limit=limit * 2)
|
|
284
|
+
|
|
285
|
+
# Filter by cursor
|
|
286
|
+
if last_id:
|
|
287
|
+
start_index = 0
|
|
288
|
+
for i, entity in enumerate(all_entities):
|
|
289
|
+
if entity.id == last_id:
|
|
290
|
+
start_index = i + 1
|
|
291
|
+
break
|
|
292
|
+
all_entities = all_entities[start_index:]
|
|
293
|
+
|
|
294
|
+
# Sort
|
|
295
|
+
if order_by == "id":
|
|
296
|
+
all_entities.sort(key=lambda e: e.id)
|
|
297
|
+
|
|
298
|
+
# Limit
|
|
299
|
+
return all_entities[:limit]
|
|
300
|
+
|
|
301
|
+
async def paginate_entities_offset(
|
|
302
|
+
self,
|
|
303
|
+
entity_type: Optional[str] = None,
|
|
304
|
+
page: int = 1,
|
|
305
|
+
page_size: int = 100,
|
|
306
|
+
order_by: str = "id",
|
|
307
|
+
) -> Page[Entity]:
|
|
308
|
+
"""
|
|
309
|
+
Paginate entities using offset-based pagination
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
entity_type: Filter by entity type
|
|
313
|
+
page: Page number (1-indexed)
|
|
314
|
+
page_size: Number of items per page
|
|
315
|
+
order_by: Field to order by
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Page of entities with pagination info
|
|
319
|
+
|
|
320
|
+
Note:
|
|
321
|
+
Offset pagination is less efficient for large datasets.
|
|
322
|
+
Consider using cursor-based pagination instead.
|
|
323
|
+
|
|
324
|
+
Example:
|
|
325
|
+
```python
|
|
326
|
+
# Get page 1
|
|
327
|
+
page1 = await store.paginate_entities_offset(page=1, page_size=100)
|
|
328
|
+
|
|
329
|
+
# Get page 2
|
|
330
|
+
page2 = await store.paginate_entities_offset(page=2, page_size=100)
|
|
331
|
+
```
|
|
332
|
+
"""
|
|
333
|
+
if page < 1:
|
|
334
|
+
raise ValueError("Page number must be >= 1")
|
|
335
|
+
|
|
336
|
+
# Calculate offset
|
|
337
|
+
offset = (page - 1) * page_size
|
|
338
|
+
|
|
339
|
+
# Fetch entities
|
|
340
|
+
all_entities = await self.get_all_entities(entity_type=entity_type)
|
|
341
|
+
|
|
342
|
+
# Sort
|
|
343
|
+
if order_by == "id":
|
|
344
|
+
all_entities.sort(key=lambda e: e.id)
|
|
345
|
+
|
|
346
|
+
# Apply offset and limit
|
|
347
|
+
total_count = len(all_entities)
|
|
348
|
+
entities = all_entities[offset : offset + page_size]
|
|
349
|
+
|
|
350
|
+
# Calculate pagination info
|
|
351
|
+
has_next = offset + page_size < total_count
|
|
352
|
+
has_previous = page > 1
|
|
353
|
+
|
|
354
|
+
page_info = PageInfo(
|
|
355
|
+
has_next_page=has_next,
|
|
356
|
+
has_previous_page=has_previous,
|
|
357
|
+
total_count=total_count,
|
|
358
|
+
page_size=page_size,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
return Page(items=entities, page_info=page_info)
|
|
362
|
+
|
|
363
|
+
async def paginate_relations(
|
|
364
|
+
self,
|
|
365
|
+
relation_type: Optional[str] = None,
|
|
366
|
+
source_id: Optional[str] = None,
|
|
367
|
+
target_id: Optional[str] = None,
|
|
368
|
+
page_size: int = 100,
|
|
369
|
+
cursor: Optional[str] = None,
|
|
370
|
+
order_by: str = "id",
|
|
371
|
+
) -> Page[Relation]:
|
|
372
|
+
"""
|
|
373
|
+
Paginate relations using cursor-based pagination
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
relation_type: Filter by relation type
|
|
377
|
+
source_id: Filter by source entity ID
|
|
378
|
+
target_id: Filter by target entity ID
|
|
379
|
+
page_size: Number of items per page
|
|
380
|
+
cursor: Cursor for next page
|
|
381
|
+
order_by: Field to order by
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Page of relations with pagination info
|
|
385
|
+
"""
|
|
386
|
+
# Decode cursor
|
|
387
|
+
last_id = None
|
|
388
|
+
if cursor:
|
|
389
|
+
try:
|
|
390
|
+
cursor_data = PaginationCursor.decode(cursor)
|
|
391
|
+
last_id = cursor_data["id"]
|
|
392
|
+
except ValueError as e:
|
|
393
|
+
logger.warning(f"Invalid cursor: {e}")
|
|
394
|
+
|
|
395
|
+
# Fetch relations (limit + 1 to check for next page)
|
|
396
|
+
limit = page_size + 1
|
|
397
|
+
relations = await self._fetch_relations_page(
|
|
398
|
+
relation_type=relation_type,
|
|
399
|
+
source_id=source_id,
|
|
400
|
+
target_id=target_id,
|
|
401
|
+
last_id=last_id,
|
|
402
|
+
limit=limit,
|
|
403
|
+
order_by=order_by,
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Check for next page
|
|
407
|
+
has_next = len(relations) > page_size
|
|
408
|
+
if has_next:
|
|
409
|
+
relations = relations[:page_size]
|
|
410
|
+
|
|
411
|
+
# Create cursors
|
|
412
|
+
start_cursor = PaginationCursor.encode(relations[0].id) if relations else None
|
|
413
|
+
end_cursor = PaginationCursor.encode(relations[-1].id) if relations else None
|
|
414
|
+
|
|
415
|
+
page_info = PageInfo(
|
|
416
|
+
has_next_page=has_next,
|
|
417
|
+
has_previous_page=cursor is not None,
|
|
418
|
+
start_cursor=start_cursor,
|
|
419
|
+
end_cursor=end_cursor,
|
|
420
|
+
page_size=page_size,
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
return Page(items=relations, page_info=page_info)
|
|
424
|
+
|
|
425
|
+
async def _fetch_relations_page(
|
|
426
|
+
self,
|
|
427
|
+
relation_type: Optional[str],
|
|
428
|
+
source_id: Optional[str],
|
|
429
|
+
target_id: Optional[str],
|
|
430
|
+
last_id: Optional[str],
|
|
431
|
+
limit: int,
|
|
432
|
+
order_by: str,
|
|
433
|
+
) -> List[Relation]:
|
|
434
|
+
"""
|
|
435
|
+
Fetch a page of relations (backend-specific implementation)
|
|
436
|
+
|
|
437
|
+
Backends should override this for better performance.
|
|
438
|
+
"""
|
|
439
|
+
# Default implementation - not efficient, should be overridden
|
|
440
|
+
# This is just a placeholder
|
|
441
|
+
relations: List[Any] = []
|
|
442
|
+
|
|
443
|
+
# For now, return empty list
|
|
444
|
+
# Backends should implement efficient relation pagination
|
|
445
|
+
return relations
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def paginate_list(items: List[T], page: int = 1, page_size: int = 100) -> Page[T]:
|
|
449
|
+
"""
|
|
450
|
+
Paginate an in-memory list
|
|
451
|
+
|
|
452
|
+
Utility function for paginating already-loaded data.
|
|
453
|
+
|
|
454
|
+
Args:
|
|
455
|
+
items: List of items to paginate
|
|
456
|
+
page: Page number (1-indexed)
|
|
457
|
+
page_size: Number of items per page
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
Page of items with pagination info
|
|
461
|
+
|
|
462
|
+
Example:
|
|
463
|
+
```python
|
|
464
|
+
all_entities = [...] # 1000 entities
|
|
465
|
+
page1 = paginate_list(all_entities, page=1, page_size=100)
|
|
466
|
+
page2 = paginate_list(all_entities, page=2, page_size=100)
|
|
467
|
+
```
|
|
468
|
+
"""
|
|
469
|
+
if page < 1:
|
|
470
|
+
raise ValueError("Page number must be >= 1")
|
|
471
|
+
|
|
472
|
+
offset = (page - 1) * page_size
|
|
473
|
+
total_count = len(items)
|
|
474
|
+
page_items = items[offset : offset + page_size]
|
|
475
|
+
|
|
476
|
+
page_info = PageInfo(
|
|
477
|
+
has_next_page=offset + page_size < total_count,
|
|
478
|
+
has_previous_page=page > 1,
|
|
479
|
+
total_count=total_count,
|
|
480
|
+
page_size=page_size,
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
return Page(items=page_items, page_info=page_info)
|