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.
Files changed (302) hide show
  1. aiecs/__init__.py +72 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +469 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +363 -0
  7. aiecs/application/knowledge_graph/__init__.py +7 -0
  8. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
  11. aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
  12. aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
  13. aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
  14. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  15. aiecs/application/knowledge_graph/extractors/base.py +100 -0
  16. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
  17. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
  18. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
  19. aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
  20. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
  21. aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
  22. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
  23. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
  24. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  25. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
  26. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
  27. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  28. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
  29. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  30. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  31. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
  32. aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
  33. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
  34. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  35. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  36. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
  37. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
  38. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
  39. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
  40. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
  41. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
  42. aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
  43. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
  44. aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
  45. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
  46. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  47. aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
  48. aiecs/application/knowledge_graph/search/reranker.py +295 -0
  49. aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
  50. aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
  51. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  52. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
  53. aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
  54. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  55. aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
  56. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  57. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
  58. aiecs/common/__init__.py +9 -0
  59. aiecs/common/knowledge_graph/__init__.py +17 -0
  60. aiecs/common/knowledge_graph/runnable.py +484 -0
  61. aiecs/config/__init__.py +16 -0
  62. aiecs/config/config.py +498 -0
  63. aiecs/config/graph_config.py +137 -0
  64. aiecs/config/registry.py +23 -0
  65. aiecs/core/__init__.py +46 -0
  66. aiecs/core/interface/__init__.py +34 -0
  67. aiecs/core/interface/execution_interface.py +152 -0
  68. aiecs/core/interface/storage_interface.py +171 -0
  69. aiecs/domain/__init__.py +289 -0
  70. aiecs/domain/agent/__init__.py +189 -0
  71. aiecs/domain/agent/base_agent.py +697 -0
  72. aiecs/domain/agent/exceptions.py +103 -0
  73. aiecs/domain/agent/graph_aware_mixin.py +559 -0
  74. aiecs/domain/agent/hybrid_agent.py +490 -0
  75. aiecs/domain/agent/integration/__init__.py +26 -0
  76. aiecs/domain/agent/integration/context_compressor.py +222 -0
  77. aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
  78. aiecs/domain/agent/integration/retry_policy.py +219 -0
  79. aiecs/domain/agent/integration/role_config.py +213 -0
  80. aiecs/domain/agent/knowledge_aware_agent.py +646 -0
  81. aiecs/domain/agent/lifecycle.py +296 -0
  82. aiecs/domain/agent/llm_agent.py +300 -0
  83. aiecs/domain/agent/memory/__init__.py +12 -0
  84. aiecs/domain/agent/memory/conversation.py +197 -0
  85. aiecs/domain/agent/migration/__init__.py +14 -0
  86. aiecs/domain/agent/migration/conversion.py +160 -0
  87. aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
  88. aiecs/domain/agent/models.py +317 -0
  89. aiecs/domain/agent/observability.py +407 -0
  90. aiecs/domain/agent/persistence.py +289 -0
  91. aiecs/domain/agent/prompts/__init__.py +29 -0
  92. aiecs/domain/agent/prompts/builder.py +161 -0
  93. aiecs/domain/agent/prompts/formatters.py +189 -0
  94. aiecs/domain/agent/prompts/template.py +255 -0
  95. aiecs/domain/agent/registry.py +260 -0
  96. aiecs/domain/agent/tool_agent.py +257 -0
  97. aiecs/domain/agent/tools/__init__.py +12 -0
  98. aiecs/domain/agent/tools/schema_generator.py +221 -0
  99. aiecs/domain/community/__init__.py +155 -0
  100. aiecs/domain/community/agent_adapter.py +477 -0
  101. aiecs/domain/community/analytics.py +481 -0
  102. aiecs/domain/community/collaborative_workflow.py +642 -0
  103. aiecs/domain/community/communication_hub.py +645 -0
  104. aiecs/domain/community/community_builder.py +320 -0
  105. aiecs/domain/community/community_integration.py +800 -0
  106. aiecs/domain/community/community_manager.py +813 -0
  107. aiecs/domain/community/decision_engine.py +879 -0
  108. aiecs/domain/community/exceptions.py +225 -0
  109. aiecs/domain/community/models/__init__.py +33 -0
  110. aiecs/domain/community/models/community_models.py +268 -0
  111. aiecs/domain/community/resource_manager.py +457 -0
  112. aiecs/domain/community/shared_context_manager.py +603 -0
  113. aiecs/domain/context/__init__.py +58 -0
  114. aiecs/domain/context/context_engine.py +989 -0
  115. aiecs/domain/context/conversation_models.py +354 -0
  116. aiecs/domain/context/graph_memory.py +467 -0
  117. aiecs/domain/execution/__init__.py +12 -0
  118. aiecs/domain/execution/model.py +57 -0
  119. aiecs/domain/knowledge_graph/__init__.py +19 -0
  120. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  121. aiecs/domain/knowledge_graph/models/entity.py +130 -0
  122. aiecs/domain/knowledge_graph/models/evidence.py +194 -0
  123. aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
  124. aiecs/domain/knowledge_graph/models/path.py +179 -0
  125. aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
  126. aiecs/domain/knowledge_graph/models/query.py +272 -0
  127. aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
  128. aiecs/domain/knowledge_graph/models/relation.py +136 -0
  129. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  130. aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
  131. aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
  132. aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
  133. aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
  134. aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
  135. aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
  136. aiecs/domain/task/__init__.py +13 -0
  137. aiecs/domain/task/dsl_processor.py +613 -0
  138. aiecs/domain/task/model.py +62 -0
  139. aiecs/domain/task/task_context.py +268 -0
  140. aiecs/infrastructure/__init__.py +24 -0
  141. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  142. aiecs/infrastructure/graph_storage/base.py +601 -0
  143. aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
  144. aiecs/infrastructure/graph_storage/cache.py +429 -0
  145. aiecs/infrastructure/graph_storage/distributed.py +226 -0
  146. aiecs/infrastructure/graph_storage/error_handling.py +390 -0
  147. aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
  148. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  149. aiecs/infrastructure/graph_storage/in_memory.py +514 -0
  150. aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
  151. aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
  152. aiecs/infrastructure/graph_storage/metrics.py +357 -0
  153. aiecs/infrastructure/graph_storage/migration.py +413 -0
  154. aiecs/infrastructure/graph_storage/pagination.py +471 -0
  155. aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
  156. aiecs/infrastructure/graph_storage/postgres.py +871 -0
  157. aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
  158. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  159. aiecs/infrastructure/graph_storage/sqlite.py +623 -0
  160. aiecs/infrastructure/graph_storage/streaming.py +495 -0
  161. aiecs/infrastructure/messaging/__init__.py +13 -0
  162. aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
  163. aiecs/infrastructure/messaging/websocket_manager.py +298 -0
  164. aiecs/infrastructure/monitoring/__init__.py +34 -0
  165. aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
  166. aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
  167. aiecs/infrastructure/monitoring/structured_logger.py +48 -0
  168. aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
  169. aiecs/infrastructure/persistence/__init__.py +24 -0
  170. aiecs/infrastructure/persistence/context_engine_client.py +187 -0
  171. aiecs/infrastructure/persistence/database_manager.py +333 -0
  172. aiecs/infrastructure/persistence/file_storage.py +754 -0
  173. aiecs/infrastructure/persistence/redis_client.py +220 -0
  174. aiecs/llm/__init__.py +86 -0
  175. aiecs/llm/callbacks/__init__.py +11 -0
  176. aiecs/llm/callbacks/custom_callbacks.py +264 -0
  177. aiecs/llm/client_factory.py +420 -0
  178. aiecs/llm/clients/__init__.py +33 -0
  179. aiecs/llm/clients/base_client.py +193 -0
  180. aiecs/llm/clients/googleai_client.py +181 -0
  181. aiecs/llm/clients/openai_client.py +131 -0
  182. aiecs/llm/clients/vertex_client.py +437 -0
  183. aiecs/llm/clients/xai_client.py +184 -0
  184. aiecs/llm/config/__init__.py +51 -0
  185. aiecs/llm/config/config_loader.py +275 -0
  186. aiecs/llm/config/config_validator.py +236 -0
  187. aiecs/llm/config/model_config.py +151 -0
  188. aiecs/llm/utils/__init__.py +10 -0
  189. aiecs/llm/utils/validate_config.py +91 -0
  190. aiecs/main.py +363 -0
  191. aiecs/scripts/__init__.py +3 -0
  192. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  193. aiecs/scripts/aid/__init__.py +19 -0
  194. aiecs/scripts/aid/version_manager.py +215 -0
  195. aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
  196. aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
  197. aiecs/scripts/dependance_check/__init__.py +17 -0
  198. aiecs/scripts/dependance_check/dependency_checker.py +938 -0
  199. aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
  200. aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
  201. aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
  202. aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
  203. aiecs/scripts/dependance_patch/__init__.py +7 -0
  204. aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
  205. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  206. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
  207. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
  208. aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
  209. aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
  210. aiecs/scripts/tools_develop/README.md +449 -0
  211. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  212. aiecs/scripts/tools_develop/__init__.py +21 -0
  213. aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
  214. aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
  215. aiecs/scripts/tools_develop/verify_tools.py +356 -0
  216. aiecs/tasks/__init__.py +1 -0
  217. aiecs/tasks/worker.py +172 -0
  218. aiecs/tools/__init__.py +299 -0
  219. aiecs/tools/apisource/__init__.py +99 -0
  220. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  221. aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
  222. aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
  223. aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
  224. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  225. aiecs/tools/apisource/monitoring/metrics.py +303 -0
  226. aiecs/tools/apisource/providers/__init__.py +115 -0
  227. aiecs/tools/apisource/providers/base.py +664 -0
  228. aiecs/tools/apisource/providers/census.py +401 -0
  229. aiecs/tools/apisource/providers/fred.py +564 -0
  230. aiecs/tools/apisource/providers/newsapi.py +412 -0
  231. aiecs/tools/apisource/providers/worldbank.py +357 -0
  232. aiecs/tools/apisource/reliability/__init__.py +12 -0
  233. aiecs/tools/apisource/reliability/error_handler.py +375 -0
  234. aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
  235. aiecs/tools/apisource/tool.py +850 -0
  236. aiecs/tools/apisource/utils/__init__.py +9 -0
  237. aiecs/tools/apisource/utils/validators.py +338 -0
  238. aiecs/tools/base_tool.py +201 -0
  239. aiecs/tools/docs/__init__.py +121 -0
  240. aiecs/tools/docs/ai_document_orchestrator.py +599 -0
  241. aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
  242. aiecs/tools/docs/content_insertion_tool.py +1333 -0
  243. aiecs/tools/docs/document_creator_tool.py +1317 -0
  244. aiecs/tools/docs/document_layout_tool.py +1166 -0
  245. aiecs/tools/docs/document_parser_tool.py +994 -0
  246. aiecs/tools/docs/document_writer_tool.py +1818 -0
  247. aiecs/tools/knowledge_graph/__init__.py +17 -0
  248. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
  249. aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
  250. aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
  251. aiecs/tools/langchain_adapter.py +542 -0
  252. aiecs/tools/schema_generator.py +275 -0
  253. aiecs/tools/search_tool/__init__.py +100 -0
  254. aiecs/tools/search_tool/analyzers.py +589 -0
  255. aiecs/tools/search_tool/cache.py +260 -0
  256. aiecs/tools/search_tool/constants.py +128 -0
  257. aiecs/tools/search_tool/context.py +216 -0
  258. aiecs/tools/search_tool/core.py +749 -0
  259. aiecs/tools/search_tool/deduplicator.py +123 -0
  260. aiecs/tools/search_tool/error_handler.py +271 -0
  261. aiecs/tools/search_tool/metrics.py +371 -0
  262. aiecs/tools/search_tool/rate_limiter.py +178 -0
  263. aiecs/tools/search_tool/schemas.py +277 -0
  264. aiecs/tools/statistics/__init__.py +80 -0
  265. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
  266. aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
  267. aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
  268. aiecs/tools/statistics/data_loader_tool.py +564 -0
  269. aiecs/tools/statistics/data_profiler_tool.py +658 -0
  270. aiecs/tools/statistics/data_transformer_tool.py +573 -0
  271. aiecs/tools/statistics/data_visualizer_tool.py +495 -0
  272. aiecs/tools/statistics/model_trainer_tool.py +487 -0
  273. aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
  274. aiecs/tools/task_tools/__init__.py +86 -0
  275. aiecs/tools/task_tools/chart_tool.py +732 -0
  276. aiecs/tools/task_tools/classfire_tool.py +922 -0
  277. aiecs/tools/task_tools/image_tool.py +447 -0
  278. aiecs/tools/task_tools/office_tool.py +684 -0
  279. aiecs/tools/task_tools/pandas_tool.py +635 -0
  280. aiecs/tools/task_tools/report_tool.py +635 -0
  281. aiecs/tools/task_tools/research_tool.py +392 -0
  282. aiecs/tools/task_tools/scraper_tool.py +715 -0
  283. aiecs/tools/task_tools/stats_tool.py +688 -0
  284. aiecs/tools/temp_file_manager.py +130 -0
  285. aiecs/tools/tool_executor/__init__.py +37 -0
  286. aiecs/tools/tool_executor/tool_executor.py +881 -0
  287. aiecs/utils/LLM_output_structor.py +445 -0
  288. aiecs/utils/__init__.py +34 -0
  289. aiecs/utils/base_callback.py +47 -0
  290. aiecs/utils/cache_provider.py +695 -0
  291. aiecs/utils/execution_utils.py +184 -0
  292. aiecs/utils/logging.py +1 -0
  293. aiecs/utils/prompt_loader.py +14 -0
  294. aiecs/utils/token_usage_repository.py +323 -0
  295. aiecs/ws/__init__.py +0 -0
  296. aiecs/ws/socket_server.py +52 -0
  297. aiecs-1.5.1.dist-info/METADATA +608 -0
  298. aiecs-1.5.1.dist-info/RECORD +302 -0
  299. aiecs-1.5.1.dist-info/WHEEL +5 -0
  300. aiecs-1.5.1.dist-info/entry_points.txt +10 -0
  301. aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
  302. aiecs-1.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,471 @@
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
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
+ logger = logging.getLogger(__name__)
19
+
20
+ T = TypeVar("T")
21
+
22
+
23
+ class PaginationType(str, Enum):
24
+ """Type of pagination strategy"""
25
+
26
+ OFFSET = "offset" # Traditional offset-based pagination
27
+ # Cursor-based pagination (more efficient for large datasets)
28
+ CURSOR = "cursor"
29
+
30
+
31
+ @dataclass
32
+ class PageInfo:
33
+ """Metadata about pagination state"""
34
+
35
+ has_next_page: bool
36
+ has_previous_page: bool
37
+ start_cursor: Optional[str] = None
38
+ end_cursor: Optional[str] = None
39
+ total_count: Optional[int] = None # Only available for offset pagination
40
+ page_size: int = 100
41
+
42
+ def to_dict(self) -> Dict[str, Any]:
43
+ """Convert to dictionary"""
44
+ return {
45
+ "has_next_page": self.has_next_page,
46
+ "has_previous_page": self.has_previous_page,
47
+ "start_cursor": self.start_cursor,
48
+ "end_cursor": self.end_cursor,
49
+ "total_count": self.total_count,
50
+ "page_size": self.page_size,
51
+ }
52
+
53
+
54
+ @dataclass
55
+ class Page(Generic[T]):
56
+ """
57
+ Generic page of results
58
+
59
+ Supports both cursor-based and offset-based pagination.
60
+
61
+ Example:
62
+ ```python
63
+ page = await store.paginate_entities(page_size=100)
64
+
65
+ for entity in page.items:
66
+ print(entity.id)
67
+
68
+ if page.page_info.has_next_page:
69
+ next_page = await store.paginate_entities(
70
+ page_size=100,
71
+ cursor=page.page_info.end_cursor
72
+ )
73
+ ```
74
+ """
75
+
76
+ items: List[T]
77
+ page_info: PageInfo
78
+
79
+ def __len__(self) -> int:
80
+ """Return number of items in page"""
81
+ return len(self.items)
82
+
83
+ def __iter__(self):
84
+ """Allow iteration over items"""
85
+ return iter(self.items)
86
+
87
+ def to_dict(self) -> Dict[str, Any]:
88
+ """Convert to dictionary"""
89
+ return {
90
+ "items": [
91
+ item.model_dump() if hasattr(item, "model_dump") else item for item in self.items
92
+ ],
93
+ "page_info": self.page_info.to_dict(),
94
+ }
95
+
96
+
97
+ class PaginationCursor:
98
+ """
99
+ Cursor for efficient pagination
100
+
101
+ Encodes pagination state (last seen ID, direction) into an opaque string.
102
+ More efficient than offset pagination for large datasets.
103
+ """
104
+
105
+ @staticmethod
106
+ def encode(last_id: str, direction: str = "forward") -> str:
107
+ """
108
+ Encode cursor from last seen ID
109
+
110
+ Args:
111
+ last_id: Last entity/relation ID seen
112
+ direction: Pagination direction ("forward" or "backward")
113
+
114
+ Returns:
115
+ Encoded cursor string
116
+ """
117
+ cursor_data = {"id": last_id, "dir": direction}
118
+ json_str = json.dumps(cursor_data)
119
+ encoded = base64.b64encode(json_str.encode()).decode()
120
+ return encoded
121
+
122
+ @staticmethod
123
+ def decode(cursor: str) -> Dict[str, str]:
124
+ """
125
+ Decode cursor to get last seen ID and direction
126
+
127
+ Args:
128
+ cursor: Encoded cursor string
129
+
130
+ Returns:
131
+ Dictionary with 'id' and 'dir' keys
132
+
133
+ Raises:
134
+ ValueError: If cursor is invalid
135
+ """
136
+ try:
137
+ decoded = base64.b64decode(cursor.encode()).decode()
138
+ cursor_data = json.loads(decoded)
139
+
140
+ if "id" not in cursor_data:
141
+ raise ValueError("Cursor missing 'id' field")
142
+
143
+ return {
144
+ "id": cursor_data["id"],
145
+ "dir": cursor_data.get("dir", "forward"),
146
+ }
147
+ except Exception as e:
148
+ raise ValueError(f"Invalid cursor: {e}")
149
+
150
+
151
+ class PaginationMixin:
152
+ """
153
+ Mixin providing pagination capabilities for graph stores
154
+
155
+ Adds cursor-based and offset-based pagination methods for entities and relations.
156
+
157
+ Example:
158
+ ```python
159
+ class MyGraphStore(GraphStore, PaginationMixin):
160
+ pass
161
+
162
+ store = MyGraphStore()
163
+
164
+ # Cursor-based pagination (recommended for large datasets)
165
+ page1 = await store.paginate_entities(page_size=100)
166
+ page2 = await store.paginate_entities(
167
+ page_size=100,
168
+ cursor=page1.page_info.end_cursor
169
+ )
170
+
171
+ # Offset-based pagination
172
+ page = await store.paginate_entities_offset(page=1, page_size=100)
173
+ ```
174
+ """
175
+
176
+ async def paginate_entities(
177
+ self,
178
+ entity_type: Optional[str] = None,
179
+ page_size: int = 100,
180
+ cursor: Optional[str] = None,
181
+ order_by: str = "id",
182
+ ) -> Page[Entity]:
183
+ """
184
+ Paginate entities using cursor-based pagination
185
+
186
+ Args:
187
+ entity_type: Filter by entity type
188
+ page_size: Number of items per page
189
+ cursor: Cursor for next page (None for first page)
190
+ order_by: Field to order by (default: "id")
191
+
192
+ Returns:
193
+ Page of entities with pagination info
194
+
195
+ Example:
196
+ ```python
197
+ # First page
198
+ page1 = await store.paginate_entities(page_size=100)
199
+
200
+ # Next page
201
+ if page1.page_info.has_next_page:
202
+ page2 = await store.paginate_entities(
203
+ page_size=100,
204
+ cursor=page1.page_info.end_cursor
205
+ )
206
+ ```
207
+ """
208
+ # Decode cursor if provided
209
+ last_id = None
210
+ if cursor:
211
+ try:
212
+ cursor_data = PaginationCursor.decode(cursor)
213
+ last_id = cursor_data["id"]
214
+ except ValueError as e:
215
+ logger.warning(f"Invalid cursor: {e}")
216
+ last_id = None
217
+
218
+ # Fetch page_size + 1 to determine if there's a next page
219
+ limit = page_size + 1
220
+
221
+ # Query entities
222
+ entities = await self._fetch_entities_page(
223
+ entity_type=entity_type,
224
+ last_id=last_id,
225
+ limit=limit,
226
+ order_by=order_by,
227
+ )
228
+
229
+ # Check if there's a next page
230
+ has_next = len(entities) > page_size
231
+ if has_next:
232
+ entities = entities[:page_size]
233
+
234
+ # Create cursors
235
+ start_cursor = PaginationCursor.encode(entities[0].id) if entities else None
236
+ end_cursor = PaginationCursor.encode(entities[-1].id) if entities else None
237
+
238
+ # Create page info
239
+ page_info = PageInfo(
240
+ has_next_page=has_next,
241
+ has_previous_page=cursor is not None,
242
+ start_cursor=start_cursor,
243
+ end_cursor=end_cursor,
244
+ page_size=page_size,
245
+ )
246
+
247
+ return Page(items=entities, page_info=page_info)
248
+
249
+ async def _fetch_entities_page(
250
+ self,
251
+ entity_type: Optional[str],
252
+ last_id: Optional[str],
253
+ limit: int,
254
+ order_by: str,
255
+ ) -> List[Entity]:
256
+ """
257
+ Fetch a page of entities (backend-specific implementation)
258
+
259
+ Args:
260
+ entity_type: Filter by entity type
261
+ last_id: Last entity ID from previous page (for cursor)
262
+ limit: Maximum number of entities to fetch
263
+ order_by: Field to order by
264
+
265
+ Returns:
266
+ List of entities
267
+ """
268
+ # This is a default implementation using get_all_entities
269
+ # Backends should override this for better performance
270
+
271
+ all_entities = await self.get_all_entities(entity_type=entity_type, limit=limit * 2)
272
+
273
+ # Filter by cursor
274
+ if last_id:
275
+ start_index = 0
276
+ for i, entity in enumerate(all_entities):
277
+ if entity.id == last_id:
278
+ start_index = i + 1
279
+ break
280
+ all_entities = all_entities[start_index:]
281
+
282
+ # Sort
283
+ if order_by == "id":
284
+ all_entities.sort(key=lambda e: e.id)
285
+
286
+ # Limit
287
+ return all_entities[:limit]
288
+
289
+ async def paginate_entities_offset(
290
+ self,
291
+ entity_type: Optional[str] = None,
292
+ page: int = 1,
293
+ page_size: int = 100,
294
+ order_by: str = "id",
295
+ ) -> Page[Entity]:
296
+ """
297
+ Paginate entities using offset-based pagination
298
+
299
+ Args:
300
+ entity_type: Filter by entity type
301
+ page: Page number (1-indexed)
302
+ page_size: Number of items per page
303
+ order_by: Field to order by
304
+
305
+ Returns:
306
+ Page of entities with pagination info
307
+
308
+ Note:
309
+ Offset pagination is less efficient for large datasets.
310
+ Consider using cursor-based pagination instead.
311
+
312
+ Example:
313
+ ```python
314
+ # Get page 1
315
+ page1 = await store.paginate_entities_offset(page=1, page_size=100)
316
+
317
+ # Get page 2
318
+ page2 = await store.paginate_entities_offset(page=2, page_size=100)
319
+ ```
320
+ """
321
+ if page < 1:
322
+ raise ValueError("Page number must be >= 1")
323
+
324
+ # Calculate offset
325
+ offset = (page - 1) * page_size
326
+
327
+ # Fetch entities
328
+ all_entities = await self.get_all_entities(entity_type=entity_type)
329
+
330
+ # Sort
331
+ if order_by == "id":
332
+ all_entities.sort(key=lambda e: e.id)
333
+
334
+ # Apply offset and limit
335
+ total_count = len(all_entities)
336
+ entities = all_entities[offset : offset + page_size]
337
+
338
+ # Calculate pagination info
339
+ has_next = offset + page_size < total_count
340
+ has_previous = page > 1
341
+
342
+ page_info = PageInfo(
343
+ has_next_page=has_next,
344
+ has_previous_page=has_previous,
345
+ total_count=total_count,
346
+ page_size=page_size,
347
+ )
348
+
349
+ return Page(items=entities, page_info=page_info)
350
+
351
+ async def paginate_relations(
352
+ self,
353
+ relation_type: Optional[str] = None,
354
+ source_id: Optional[str] = None,
355
+ target_id: Optional[str] = None,
356
+ page_size: int = 100,
357
+ cursor: Optional[str] = None,
358
+ order_by: str = "id",
359
+ ) -> Page[Relation]:
360
+ """
361
+ Paginate relations using cursor-based pagination
362
+
363
+ Args:
364
+ relation_type: Filter by relation type
365
+ source_id: Filter by source entity ID
366
+ target_id: Filter by target entity ID
367
+ page_size: Number of items per page
368
+ cursor: Cursor for next page
369
+ order_by: Field to order by
370
+
371
+ Returns:
372
+ Page of relations with pagination info
373
+ """
374
+ # Decode cursor
375
+ last_id = None
376
+ if cursor:
377
+ try:
378
+ cursor_data = PaginationCursor.decode(cursor)
379
+ last_id = cursor_data["id"]
380
+ except ValueError as e:
381
+ logger.warning(f"Invalid cursor: {e}")
382
+
383
+ # Fetch relations (limit + 1 to check for next page)
384
+ limit = page_size + 1
385
+ relations = await self._fetch_relations_page(
386
+ relation_type=relation_type,
387
+ source_id=source_id,
388
+ target_id=target_id,
389
+ last_id=last_id,
390
+ limit=limit,
391
+ order_by=order_by,
392
+ )
393
+
394
+ # Check for next page
395
+ has_next = len(relations) > page_size
396
+ if has_next:
397
+ relations = relations[:page_size]
398
+
399
+ # Create cursors
400
+ start_cursor = PaginationCursor.encode(relations[0].id) if relations else None
401
+ end_cursor = PaginationCursor.encode(relations[-1].id) if relations else None
402
+
403
+ page_info = PageInfo(
404
+ has_next_page=has_next,
405
+ has_previous_page=cursor is not None,
406
+ start_cursor=start_cursor,
407
+ end_cursor=end_cursor,
408
+ page_size=page_size,
409
+ )
410
+
411
+ return Page(items=relations, page_info=page_info)
412
+
413
+ async def _fetch_relations_page(
414
+ self,
415
+ relation_type: Optional[str],
416
+ source_id: Optional[str],
417
+ target_id: Optional[str],
418
+ last_id: Optional[str],
419
+ limit: int,
420
+ order_by: str,
421
+ ) -> List[Relation]:
422
+ """
423
+ Fetch a page of relations (backend-specific implementation)
424
+
425
+ Backends should override this for better performance.
426
+ """
427
+ # Default implementation - not efficient, should be overridden
428
+ # This is just a placeholder
429
+ relations = []
430
+
431
+ # For now, return empty list
432
+ # Backends should implement efficient relation pagination
433
+ return relations
434
+
435
+
436
+ def paginate_list(items: List[T], page: int = 1, page_size: int = 100) -> Page[T]:
437
+ """
438
+ Paginate an in-memory list
439
+
440
+ Utility function for paginating already-loaded data.
441
+
442
+ Args:
443
+ items: List of items to paginate
444
+ page: Page number (1-indexed)
445
+ page_size: Number of items per page
446
+
447
+ Returns:
448
+ Page of items with pagination info
449
+
450
+ Example:
451
+ ```python
452
+ all_entities = [...] # 1000 entities
453
+ page1 = paginate_list(all_entities, page=1, page_size=100)
454
+ page2 = paginate_list(all_entities, page=2, page_size=100)
455
+ ```
456
+ """
457
+ if page < 1:
458
+ raise ValueError("Page number must be >= 1")
459
+
460
+ offset = (page - 1) * page_size
461
+ total_count = len(items)
462
+ page_items = items[offset : offset + page_size]
463
+
464
+ page_info = PageInfo(
465
+ has_next_page=offset + page_size < total_count,
466
+ has_previous_page=page > 1,
467
+ total_count=total_count,
468
+ page_size=page_size,
469
+ )
470
+
471
+ return Page(items=page_items, page_info=page_info)