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,222 @@
1
+ """
2
+ Context Compression
3
+
4
+ Smart context compression for token limits.
5
+ """
6
+
7
+ import logging
8
+ from typing import List, Optional
9
+ from enum import Enum
10
+
11
+ from aiecs.llm import LLMMessage
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class CompressionStrategy(Enum):
17
+ """Context compression strategies."""
18
+
19
+ TRUNCATE_MIDDLE = "truncate_middle"
20
+ TRUNCATE_START = "truncate_start"
21
+ PRESERVE_RECENT = "preserve_recent"
22
+ SUMMARIZE = "summarize"
23
+
24
+
25
+ class ContextCompressor:
26
+ """
27
+ Smart context compression for managing token limits.
28
+
29
+ Example:
30
+ compressor = ContextCompressor(max_tokens=4000)
31
+ compressed = compressor.compress_messages(messages)
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ max_tokens: int = 4000,
37
+ strategy: CompressionStrategy = CompressionStrategy.PRESERVE_RECENT,
38
+ preserve_system: bool = True,
39
+ ):
40
+ """
41
+ Initialize context compressor.
42
+
43
+ Args:
44
+ max_tokens: Maximum token limit
45
+ strategy: Compression strategy
46
+ preserve_system: Always preserve system messages
47
+ """
48
+ self.max_tokens = max_tokens
49
+ self.strategy = strategy
50
+ self.preserve_system = preserve_system
51
+
52
+ def compress_messages(
53
+ self,
54
+ messages: List[LLMMessage],
55
+ priority_indices: Optional[List[int]] = None,
56
+ ) -> List[LLMMessage]:
57
+ """
58
+ Compress message list to fit within token limit.
59
+
60
+ Args:
61
+ messages: List of messages
62
+ priority_indices: Optional indices of high-priority messages
63
+
64
+ Returns:
65
+ Compressed message list
66
+ """
67
+ # Estimate tokens
68
+ total_tokens = self._estimate_tokens(messages)
69
+
70
+ if total_tokens <= self.max_tokens:
71
+ return messages
72
+
73
+ logger.debug(
74
+ f"Compressing {len(messages)} messages from ~{total_tokens} to ~{self.max_tokens} tokens"
75
+ )
76
+
77
+ # Apply compression strategy
78
+ if self.strategy == CompressionStrategy.PRESERVE_RECENT:
79
+ return self._compress_preserve_recent(messages, priority_indices)
80
+ elif self.strategy == CompressionStrategy.TRUNCATE_MIDDLE:
81
+ return self._compress_truncate_middle(messages, priority_indices)
82
+ elif self.strategy == CompressionStrategy.TRUNCATE_START:
83
+ return self._compress_truncate_start(messages)
84
+ else:
85
+ # Default: preserve recent
86
+ return self._compress_preserve_recent(messages, priority_indices)
87
+
88
+ def _compress_preserve_recent(
89
+ self, messages: List[LLMMessage], priority_indices: Optional[List[int]]
90
+ ) -> List[LLMMessage]:
91
+ """Preserve recent messages and priority messages."""
92
+ priority_indices = set(priority_indices or [])
93
+ compressed = []
94
+
95
+ # Always include system messages if enabled
96
+ if self.preserve_system:
97
+ system_msgs = [msg for msg in messages if msg.role == "system"]
98
+ compressed.extend(system_msgs)
99
+
100
+ # Calculate remaining budget
101
+ remaining_tokens = self.max_tokens - self._estimate_tokens(compressed)
102
+
103
+ # Add priority messages
104
+ for idx in priority_indices:
105
+ if idx < len(messages) and messages[idx] not in compressed:
106
+ msg_tokens = self._estimate_tokens([messages[idx]])
107
+ if msg_tokens <= remaining_tokens:
108
+ compressed.append(messages[idx])
109
+ remaining_tokens -= msg_tokens
110
+
111
+ # Add recent messages (from end)
112
+ for msg in reversed(messages):
113
+ if msg not in compressed:
114
+ msg_tokens = self._estimate_tokens([msg])
115
+ if msg_tokens <= remaining_tokens:
116
+ compressed.insert(len(compressed), msg)
117
+ remaining_tokens -= msg_tokens
118
+ else:
119
+ break
120
+
121
+ return compressed
122
+
123
+ def _compress_truncate_middle(
124
+ self, messages: List[LLMMessage], priority_indices: Optional[List[int]]
125
+ ) -> List[LLMMessage]:
126
+ """Keep start and end messages, truncate middle."""
127
+ if len(messages) <= 4:
128
+ return messages
129
+
130
+ # Keep first 2 and last 2 by default
131
+ keep_start = 2
132
+ keep_end = 2
133
+
134
+ # Adjust based on token budget
135
+ start_msgs = messages[:keep_start]
136
+ end_msgs = messages[-keep_end:]
137
+
138
+ compressed = (
139
+ start_msgs
140
+ + [
141
+ LLMMessage(
142
+ role="system",
143
+ content="[... conversation history compressed ...]",
144
+ )
145
+ ]
146
+ + end_msgs
147
+ )
148
+
149
+ return compressed
150
+
151
+ def _compress_truncate_start(self, messages: List[LLMMessage]) -> List[LLMMessage]:
152
+ """Keep recent messages, truncate start."""
153
+ compressed = []
154
+ remaining_tokens = self.max_tokens
155
+
156
+ # Process from end
157
+ for msg in reversed(messages):
158
+ msg_tokens = self._estimate_tokens([msg])
159
+ if msg_tokens <= remaining_tokens:
160
+ compressed.insert(0, msg)
161
+ remaining_tokens -= msg_tokens
162
+ else:
163
+ break
164
+
165
+ return compressed
166
+
167
+ def _estimate_tokens(self, messages: List[LLMMessage]) -> int:
168
+ """
169
+ Estimate token count for messages.
170
+
171
+ Args:
172
+ messages: List of messages
173
+
174
+ Returns:
175
+ Estimated token count
176
+ """
177
+ total_chars = sum(len(msg.content) for msg in messages)
178
+ # Rough estimate: 4 chars ≈ 1 token
179
+ return total_chars // 4
180
+
181
+ def compress_text(self, text: str, max_tokens: int) -> str:
182
+ """
183
+ Compress text to fit within token limit.
184
+
185
+ Args:
186
+ text: Text to compress
187
+ max_tokens: Maximum tokens
188
+
189
+ Returns:
190
+ Compressed text
191
+ """
192
+ estimated_tokens = len(text) // 4
193
+
194
+ if estimated_tokens <= max_tokens:
195
+ return text
196
+
197
+ # Truncate to fit
198
+ max_chars = max_tokens * 4
199
+ if len(text) <= max_chars:
200
+ return text
201
+
202
+ return text[: max_chars - 20] + "... [truncated]"
203
+
204
+
205
+ def compress_messages(
206
+ messages: List[LLMMessage],
207
+ max_tokens: int = 4000,
208
+ strategy: CompressionStrategy = CompressionStrategy.PRESERVE_RECENT,
209
+ ) -> List[LLMMessage]:
210
+ """
211
+ Convenience function for compressing messages.
212
+
213
+ Args:
214
+ messages: List of messages
215
+ max_tokens: Maximum token limit
216
+ strategy: Compression strategy
217
+
218
+ Returns:
219
+ Compressed message list
220
+ """
221
+ compressor = ContextCompressor(max_tokens=max_tokens, strategy=strategy)
222
+ return compressor.compress_messages(messages)
@@ -0,0 +1,252 @@
1
+ """
2
+ ContextEngine Adapter
3
+
4
+ Adapter for integrating agent persistence with AIECS ContextEngine.
5
+ """
6
+
7
+ import logging
8
+ import uuid
9
+ from typing import Dict, Any, Optional, List, TYPE_CHECKING
10
+ from datetime import datetime
11
+
12
+ if TYPE_CHECKING:
13
+ from aiecs.domain.context.context_engine import ContextEngine
14
+
15
+ from aiecs.domain.agent.base_agent import BaseAIAgent
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ContextEngineAdapter:
21
+ """
22
+ Adapter for persisting agent state to ContextEngine.
23
+
24
+ Uses ContextEngine's checkpoint system for versioned state storage
25
+ and TaskContext for session-based state management.
26
+ """
27
+
28
+ def __init__(self, context_engine: "ContextEngine", user_id: str = "system"):
29
+ """
30
+ Initialize adapter.
31
+
32
+ Args:
33
+ context_engine: ContextEngine instance
34
+ user_id: User identifier for session management
35
+ """
36
+ if context_engine is None:
37
+ raise ValueError("ContextEngine instance is required")
38
+
39
+ self.context_engine = context_engine
40
+ self.user_id = user_id
41
+ self._agent_state_prefix = "agent_state"
42
+ self._agent_conversation_prefix = "agent_conversation"
43
+ logger.info("ContextEngineAdapter initialized")
44
+
45
+ async def save_agent_state(
46
+ self,
47
+ agent_id: str,
48
+ state: Dict[str, Any],
49
+ version: Optional[str] = None,
50
+ ) -> str:
51
+ """
52
+ Save agent state to ContextEngine using checkpoint system.
53
+
54
+ Args:
55
+ agent_id: Agent identifier
56
+ state: Agent state dictionary
57
+ version: Optional version identifier (auto-generated if None)
58
+
59
+ Returns:
60
+ Version identifier
61
+ """
62
+ if version is None:
63
+ version = str(uuid.uuid4())
64
+
65
+ checkpoint_data = {
66
+ "agent_id": agent_id,
67
+ "state": state,
68
+ "timestamp": datetime.utcnow().isoformat(),
69
+ "version": version,
70
+ }
71
+
72
+ # Store as checkpoint (thread_id = agent_id)
73
+ await self.context_engine.store_checkpoint(
74
+ thread_id=agent_id,
75
+ checkpoint_id=version,
76
+ checkpoint_data=checkpoint_data,
77
+ metadata={"type": "agent_state", "agent_id": agent_id},
78
+ )
79
+
80
+ logger.debug(f"Saved agent {agent_id} state version {version} to ContextEngine")
81
+ return version
82
+
83
+ async def load_agent_state(
84
+ self, agent_id: str, version: Optional[str] = None
85
+ ) -> Optional[Dict[str, Any]]:
86
+ """
87
+ Load agent state from ContextEngine.
88
+
89
+ Args:
90
+ agent_id: Agent identifier
91
+ version: Optional version identifier (loads latest if None)
92
+
93
+ Returns:
94
+ Agent state dictionary or None
95
+ """
96
+ checkpoint = await self.context_engine.get_checkpoint(
97
+ thread_id=agent_id, checkpoint_id=version
98
+ )
99
+
100
+ if checkpoint and "data" in checkpoint:
101
+ checkpoint_data = checkpoint["data"]
102
+ if isinstance(checkpoint_data, dict) and "state" in checkpoint_data:
103
+ logger.debug(f"Loaded agent {agent_id} state version {version or 'latest'}")
104
+ return checkpoint_data["state"]
105
+
106
+ logger.debug(f"No state found for agent {agent_id} version {version or 'latest'}")
107
+ return None
108
+
109
+ async def list_agent_versions(self, agent_id: str) -> List[Dict[str, Any]]:
110
+ """
111
+ List all versions of an agent's state.
112
+
113
+ Args:
114
+ agent_id: Agent identifier
115
+
116
+ Returns:
117
+ List of version metadata dictionaries
118
+ """
119
+ checkpoints = await self.context_engine.list_checkpoints(thread_id=agent_id)
120
+ if not checkpoints:
121
+ return []
122
+
123
+ versions = []
124
+ for checkpoint in checkpoints:
125
+ # list_checkpoints returns dicts with "data" key containing
126
+ # checkpoint_data
127
+ if isinstance(checkpoint, dict):
128
+ data = checkpoint.get("data", {})
129
+ if isinstance(data, dict) and "version" in data:
130
+ versions.append(
131
+ {
132
+ "version": data["version"],
133
+ "timestamp": data.get("timestamp"),
134
+ "metadata": checkpoint.get("metadata", {}),
135
+ }
136
+ )
137
+
138
+ # Sort by timestamp descending
139
+ versions.sort(key=lambda v: v.get("timestamp", ""), reverse=True)
140
+ return versions
141
+
142
+ async def save_conversation_history(
143
+ self, session_id: str, messages: List[Dict[str, Any]]
144
+ ) -> None:
145
+ """
146
+ Save conversation history to ContextEngine.
147
+
148
+ Args:
149
+ session_id: Session identifier
150
+ messages: List of message dictionaries with 'role' and 'content'
151
+ """
152
+ # Ensure session exists
153
+ session = await self.context_engine.get_session(session_id)
154
+ if not session:
155
+ await self.context_engine.create_session(
156
+ session_id=session_id,
157
+ user_id=self.user_id,
158
+ metadata={"type": "agent_conversation"},
159
+ )
160
+
161
+ # Store messages using ContextEngine's conversation API
162
+ for msg in messages:
163
+ role = msg.get("role", "user")
164
+ content = msg.get("content", "")
165
+ metadata = msg.get("metadata", {})
166
+
167
+ await self.context_engine.add_conversation_message(
168
+ session_id=session_id,
169
+ role=role,
170
+ content=content,
171
+ metadata=metadata,
172
+ )
173
+
174
+ logger.debug(f"Saved {len(messages)} messages to session {session_id}")
175
+
176
+ async def load_conversation_history(
177
+ self, session_id: str, limit: int = 50
178
+ ) -> List[Dict[str, Any]]:
179
+ """
180
+ Load conversation history from ContextEngine.
181
+
182
+ Args:
183
+ session_id: Session identifier
184
+ limit: Maximum number of messages to retrieve
185
+
186
+ Returns:
187
+ List of message dictionaries
188
+ """
189
+ messages = await self.context_engine.get_conversation_history(
190
+ session_id=session_id, limit=limit
191
+ )
192
+
193
+ # Convert ConversationMessage objects to dictionaries
194
+ result = []
195
+ for msg in messages:
196
+ result.append(
197
+ {
198
+ "role": msg.role,
199
+ "content": msg.content,
200
+ "timestamp": (
201
+ msg.timestamp.isoformat()
202
+ if hasattr(msg.timestamp, "isoformat")
203
+ else str(msg.timestamp)
204
+ ),
205
+ "metadata": msg.metadata,
206
+ }
207
+ )
208
+
209
+ logger.debug(f"Loaded {len(result)} messages from session {session_id}")
210
+ return result
211
+
212
+ async def delete_agent_state(self, agent_id: str, version: Optional[str] = None) -> None:
213
+ """
214
+ Delete agent state from ContextEngine.
215
+
216
+ Args:
217
+ agent_id: Agent identifier
218
+ version: Optional version identifier (deletes all if None)
219
+ """
220
+ # Note: ContextEngine doesn't have explicit delete for checkpoints
221
+ # We'll store a tombstone checkpoint or rely on TTL
222
+ if version:
223
+ # Store empty state as deletion marker
224
+ await self.context_engine.store_checkpoint(
225
+ thread_id=agent_id,
226
+ checkpoint_id=f"{version}_deleted",
227
+ checkpoint_data={"deleted": True, "original_version": version},
228
+ metadata={"type": "deletion_marker"},
229
+ )
230
+ logger.debug(f"Marked agent {agent_id} state version {version or 'all'} for deletion")
231
+
232
+ # AgentPersistence Protocol implementation
233
+ async def save(self, agent: BaseAIAgent) -> None:
234
+ """Save agent state (implements AgentPersistence protocol)."""
235
+ state = agent.to_dict()
236
+ await self.save_agent_state(agent.agent_id, state)
237
+
238
+ async def load(self, agent_id: str) -> Dict[str, Any]:
239
+ """Load agent state (implements AgentPersistence protocol)."""
240
+ state = await self.load_agent_state(agent_id)
241
+ if state is None:
242
+ raise KeyError(f"Agent {agent_id} not found in storage")
243
+ return state
244
+
245
+ async def exists(self, agent_id: str) -> bool:
246
+ """Check if agent state exists (implements AgentPersistence protocol)."""
247
+ state = await self.load_agent_state(agent_id)
248
+ return state is not None
249
+
250
+ async def delete(self, agent_id: str) -> None:
251
+ """Delete agent state (implements AgentPersistence protocol)."""
252
+ await self.delete_agent_state(agent_id)
@@ -0,0 +1,219 @@
1
+ """
2
+ Enhanced Retry Policy
3
+
4
+ Sophisticated retry logic with exponential backoff and error classification.
5
+ """
6
+
7
+ import asyncio
8
+ import random
9
+ import logging
10
+ from typing import Callable, Any
11
+ from enum import Enum
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ErrorType(Enum):
17
+ """Error types for classification."""
18
+
19
+ RATE_LIMIT = "rate_limit"
20
+ TIMEOUT = "timeout"
21
+ SERVER_ERROR = "server_error"
22
+ CLIENT_ERROR = "client_error"
23
+ NETWORK_ERROR = "network_error"
24
+ UNKNOWN = "unknown"
25
+
26
+
27
+ class ErrorClassifier:
28
+ """Classifies errors for retry strategy."""
29
+
30
+ @staticmethod
31
+ def classify(error: Exception) -> ErrorType:
32
+ """
33
+ Classify error type.
34
+
35
+ Args:
36
+ error: Exception to classify
37
+
38
+ Returns:
39
+ ErrorType
40
+ """
41
+ error_str = str(error).lower()
42
+ error_type_name = type(error).__name__.lower()
43
+
44
+ # Rate limit errors
45
+ if "rate limit" in error_str or "429" in error_str:
46
+ return ErrorType.RATE_LIMIT
47
+
48
+ # Timeout errors
49
+ if "timeout" in error_str or "timed out" in error_str:
50
+ return ErrorType.TIMEOUT
51
+
52
+ # Server errors (5xx)
53
+ if any(code in error_str for code in ["500", "502", "503", "504"]):
54
+ return ErrorType.SERVER_ERROR
55
+
56
+ # Client errors (4xx)
57
+ if any(code in error_str for code in ["400", "401", "403", "404"]):
58
+ return ErrorType.CLIENT_ERROR
59
+
60
+ # Network errors
61
+ if any(term in error_type_name for term in ["connection", "network", "socket"]):
62
+ return ErrorType.NETWORK_ERROR
63
+
64
+ return ErrorType.UNKNOWN
65
+
66
+ @staticmethod
67
+ def is_retryable(error_type: ErrorType) -> bool:
68
+ """
69
+ Determine if error type should be retried.
70
+
71
+ Args:
72
+ error_type: Error type
73
+
74
+ Returns:
75
+ True if retryable
76
+ """
77
+ retryable_types = {
78
+ ErrorType.RATE_LIMIT,
79
+ ErrorType.TIMEOUT,
80
+ ErrorType.SERVER_ERROR,
81
+ ErrorType.NETWORK_ERROR,
82
+ }
83
+ return error_type in retryable_types
84
+
85
+
86
+ class EnhancedRetryPolicy:
87
+ """
88
+ Enhanced retry policy with exponential backoff and jitter.
89
+
90
+ Example:
91
+ policy = EnhancedRetryPolicy(max_retries=5, base_delay=1.0)
92
+ result = await policy.execute_with_retry(my_async_function, arg1, arg2)
93
+ """
94
+
95
+ def __init__(
96
+ self,
97
+ max_retries: int = 3,
98
+ base_delay: float = 1.0,
99
+ max_delay: float = 60.0,
100
+ exponential_base: float = 2.0,
101
+ jitter: bool = True,
102
+ ):
103
+ """
104
+ Initialize retry policy.
105
+
106
+ Args:
107
+ max_retries: Maximum number of retry attempts
108
+ base_delay: Base delay in seconds
109
+ max_delay: Maximum delay in seconds
110
+ exponential_base: Base for exponential backoff
111
+ jitter: Whether to add random jitter
112
+ """
113
+ self.max_retries = max_retries
114
+ self.base_delay = base_delay
115
+ self.max_delay = max_delay
116
+ self.exponential_base = exponential_base
117
+ self.jitter = jitter
118
+
119
+ def calculate_delay(self, attempt: int, error_type: ErrorType) -> float:
120
+ """
121
+ Calculate delay for retry attempt.
122
+
123
+ Args:
124
+ attempt: Retry attempt number (0-indexed)
125
+ error_type: Type of error
126
+
127
+ Returns:
128
+ Delay in seconds
129
+ """
130
+ # Base exponential backoff
131
+ delay = min(self.base_delay * (self.exponential_base**attempt), self.max_delay)
132
+
133
+ # Adjust for error type
134
+ if error_type == ErrorType.RATE_LIMIT:
135
+ # Longer delay for rate limits
136
+ delay *= 2
137
+
138
+ # Add jitter to prevent thundering herd
139
+ if self.jitter:
140
+ delay *= 0.5 + random.random()
141
+
142
+ return delay
143
+
144
+ async def execute_with_retry(self, func: Callable, *args, **kwargs) -> Any:
145
+ """
146
+ Execute function with retry logic.
147
+
148
+ Args:
149
+ func: Async function to execute
150
+ *args: Function arguments
151
+ **kwargs: Function keyword arguments
152
+
153
+ Returns:
154
+ Function result
155
+
156
+ Raises:
157
+ Exception: If all retries exhausted
158
+ """
159
+ last_error = None
160
+
161
+ for attempt in range(self.max_retries + 1):
162
+ try:
163
+ result = await func(*args, **kwargs)
164
+
165
+ # Log success after retries
166
+ if attempt > 0:
167
+ logger.info(f"Succeeded after {attempt} retries")
168
+
169
+ return result
170
+
171
+ except Exception as e:
172
+ last_error = e
173
+
174
+ # Classify error
175
+ error_type = ErrorClassifier.classify(e)
176
+
177
+ # Check if we should retry
178
+ if attempt >= self.max_retries:
179
+ logger.error(f"Max retries ({self.max_retries}) exhausted")
180
+ break
181
+
182
+ if not ErrorClassifier.is_retryable(error_type):
183
+ logger.error(f"Non-retryable error: {error_type.value}")
184
+ break
185
+
186
+ # Calculate delay and wait
187
+ delay = self.calculate_delay(attempt, error_type)
188
+ logger.warning(
189
+ f"Attempt {attempt + 1} failed ({error_type.value}). "
190
+ f"Retrying in {delay:.2f}s..."
191
+ )
192
+ await asyncio.sleep(delay)
193
+
194
+ # All retries exhausted
195
+ raise last_error
196
+
197
+
198
+ async def with_retry(
199
+ func: Callable,
200
+ max_retries: int = 3,
201
+ base_delay: float = 1.0,
202
+ *args,
203
+ **kwargs,
204
+ ) -> Any:
205
+ """
206
+ Convenience function for executing with retry.
207
+
208
+ Args:
209
+ func: Async function to execute
210
+ max_retries: Maximum number of retries
211
+ base_delay: Base delay in seconds
212
+ *args: Function arguments
213
+ **kwargs: Function keyword arguments
214
+
215
+ Returns:
216
+ Function result
217
+ """
218
+ policy = EnhancedRetryPolicy(max_retries=max_retries, base_delay=base_delay)
219
+ return await policy.execute_with_retry(func, *args, **kwargs)