aethergraph 0.1.0a1__py3-none-any.whl → 0.1.0a2__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 (267) hide show
  1. aethergraph/__init__.py +4 -10
  2. aethergraph/__main__.py +293 -0
  3. aethergraph/api/v1/__init__.py +0 -0
  4. aethergraph/api/v1/agents.py +46 -0
  5. aethergraph/api/v1/apps.py +70 -0
  6. aethergraph/api/v1/artifacts.py +415 -0
  7. aethergraph/api/v1/channels.py +89 -0
  8. aethergraph/api/v1/deps.py +168 -0
  9. aethergraph/api/v1/graphs.py +259 -0
  10. aethergraph/api/v1/identity.py +25 -0
  11. aethergraph/api/v1/memory.py +353 -0
  12. aethergraph/api/v1/misc.py +47 -0
  13. aethergraph/api/v1/pagination.py +29 -0
  14. aethergraph/api/v1/runs.py +568 -0
  15. aethergraph/api/v1/schemas.py +535 -0
  16. aethergraph/api/v1/session.py +323 -0
  17. aethergraph/api/v1/stats.py +201 -0
  18. aethergraph/api/v1/viz.py +152 -0
  19. aethergraph/config/config.py +22 -0
  20. aethergraph/config/loader.py +3 -2
  21. aethergraph/config/storage.py +209 -0
  22. aethergraph/contracts/__init__.py +0 -0
  23. aethergraph/contracts/services/__init__.py +0 -0
  24. aethergraph/contracts/services/artifacts.py +27 -14
  25. aethergraph/contracts/services/memory.py +45 -17
  26. aethergraph/contracts/services/metering.py +129 -0
  27. aethergraph/contracts/services/runs.py +50 -0
  28. aethergraph/contracts/services/sessions.py +87 -0
  29. aethergraph/contracts/services/state_stores.py +3 -0
  30. aethergraph/contracts/services/viz.py +44 -0
  31. aethergraph/contracts/storage/artifact_index.py +88 -0
  32. aethergraph/contracts/storage/artifact_store.py +99 -0
  33. aethergraph/contracts/storage/async_kv.py +34 -0
  34. aethergraph/contracts/storage/blob_store.py +50 -0
  35. aethergraph/contracts/storage/doc_store.py +35 -0
  36. aethergraph/contracts/storage/event_log.py +31 -0
  37. aethergraph/contracts/storage/vector_index.py +48 -0
  38. aethergraph/core/__init__.py +0 -0
  39. aethergraph/core/execution/forward_scheduler.py +13 -2
  40. aethergraph/core/execution/global_scheduler.py +21 -15
  41. aethergraph/core/execution/step_forward.py +10 -1
  42. aethergraph/core/graph/__init__.py +0 -0
  43. aethergraph/core/graph/graph_builder.py +8 -4
  44. aethergraph/core/graph/graph_fn.py +156 -15
  45. aethergraph/core/graph/graph_spec.py +8 -0
  46. aethergraph/core/graph/graphify.py +146 -27
  47. aethergraph/core/graph/node_spec.py +0 -2
  48. aethergraph/core/graph/node_state.py +3 -0
  49. aethergraph/core/graph/task_graph.py +39 -1
  50. aethergraph/core/runtime/__init__.py +0 -0
  51. aethergraph/core/runtime/ad_hoc_context.py +64 -4
  52. aethergraph/core/runtime/base_service.py +28 -4
  53. aethergraph/core/runtime/execution_context.py +13 -15
  54. aethergraph/core/runtime/graph_runner.py +222 -37
  55. aethergraph/core/runtime/node_context.py +510 -6
  56. aethergraph/core/runtime/node_services.py +12 -5
  57. aethergraph/core/runtime/recovery.py +15 -1
  58. aethergraph/core/runtime/run_manager.py +783 -0
  59. aethergraph/core/runtime/run_manager_local.py +204 -0
  60. aethergraph/core/runtime/run_registration.py +2 -2
  61. aethergraph/core/runtime/run_types.py +89 -0
  62. aethergraph/core/runtime/runtime_env.py +136 -7
  63. aethergraph/core/runtime/runtime_metering.py +71 -0
  64. aethergraph/core/runtime/runtime_registry.py +36 -13
  65. aethergraph/core/runtime/runtime_services.py +194 -6
  66. aethergraph/core/tools/builtins/toolset.py +1 -1
  67. aethergraph/core/tools/toolkit.py +5 -0
  68. aethergraph/plugins/agents/default_chat_agent copy.py +90 -0
  69. aethergraph/plugins/agents/default_chat_agent.py +171 -0
  70. aethergraph/plugins/agents/shared.py +81 -0
  71. aethergraph/plugins/channel/adapters/webui.py +112 -112
  72. aethergraph/plugins/channel/routes/webui_routes.py +367 -102
  73. aethergraph/plugins/channel/utils/slack_utils.py +115 -59
  74. aethergraph/plugins/channel/utils/telegram_utils.py +88 -47
  75. aethergraph/plugins/channel/websockets/weibui_ws.py +172 -0
  76. aethergraph/runtime/__init__.py +15 -0
  77. aethergraph/server/app_factory.py +190 -34
  78. aethergraph/server/clients/channel_client.py +202 -0
  79. aethergraph/server/http/channel_http_routes.py +116 -0
  80. aethergraph/server/http/channel_ws_routers.py +45 -0
  81. aethergraph/server/loading.py +117 -0
  82. aethergraph/server/server.py +131 -0
  83. aethergraph/server/server_state.py +240 -0
  84. aethergraph/server/start.py +227 -66
  85. aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  86. aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  87. aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  88. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  89. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  90. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  91. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  92. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  93. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  94. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  95. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  96. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  97. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  98. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  99. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  100. aethergraph/server/ui_static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  101. aethergraph/server/ui_static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  102. aethergraph/server/ui_static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  103. aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  104. aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  105. aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  106. aethergraph/server/ui_static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  107. aethergraph/server/ui_static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  108. aethergraph/server/ui_static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  109. aethergraph/server/ui_static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  110. aethergraph/server/ui_static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  111. aethergraph/server/ui_static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  112. aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  113. aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  114. aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  115. aethergraph/server/ui_static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  116. aethergraph/server/ui_static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  117. aethergraph/server/ui_static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  118. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  119. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  120. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  121. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  122. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  123. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  124. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  125. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  126. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  127. aethergraph/server/ui_static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  128. aethergraph/server/ui_static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  129. aethergraph/server/ui_static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  130. aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  131. aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  132. aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  133. aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  134. aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  135. aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  136. aethergraph/server/ui_static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  137. aethergraph/server/ui_static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  138. aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  139. aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  140. aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  141. aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  142. aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  143. aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  144. aethergraph/server/ui_static/assets/index-BR5GtXcZ.css +1 -0
  145. aethergraph/server/ui_static/assets/index-CQ0HZZ83.js +400 -0
  146. aethergraph/server/ui_static/index.html +15 -0
  147. aethergraph/server/ui_static/logo.png +0 -0
  148. aethergraph/services/artifacts/__init__.py +0 -0
  149. aethergraph/services/artifacts/facade.py +1239 -132
  150. aethergraph/services/auth/{dev.py → authn.py} +0 -8
  151. aethergraph/services/auth/authz.py +100 -0
  152. aethergraph/services/channel/__init__.py +0 -0
  153. aethergraph/services/channel/channel_bus.py +19 -1
  154. aethergraph/services/channel/factory.py +13 -1
  155. aethergraph/services/channel/ingress.py +311 -0
  156. aethergraph/services/channel/queue_adapter.py +75 -0
  157. aethergraph/services/channel/session.py +502 -19
  158. aethergraph/services/container/default_container.py +122 -43
  159. aethergraph/services/continuations/continuation.py +6 -0
  160. aethergraph/services/continuations/stores/fs_store.py +19 -0
  161. aethergraph/services/eventhub/event_hub.py +76 -0
  162. aethergraph/services/kv/__init__.py +0 -0
  163. aethergraph/services/kv/ephemeral.py +244 -0
  164. aethergraph/services/llm/__init__.py +0 -0
  165. aethergraph/services/llm/generic_client copy.py +691 -0
  166. aethergraph/services/llm/generic_client.py +1288 -187
  167. aethergraph/services/llm/providers.py +3 -1
  168. aethergraph/services/llm/types.py +47 -0
  169. aethergraph/services/llm/utils.py +284 -0
  170. aethergraph/services/logger/std.py +3 -0
  171. aethergraph/services/mcp/__init__.py +9 -0
  172. aethergraph/services/mcp/http_client.py +38 -0
  173. aethergraph/services/mcp/service.py +225 -1
  174. aethergraph/services/mcp/stdio_client.py +41 -6
  175. aethergraph/services/mcp/ws_client.py +44 -2
  176. aethergraph/services/memory/__init__.py +0 -0
  177. aethergraph/services/memory/distillers/llm_long_term.py +234 -0
  178. aethergraph/services/memory/distillers/llm_meta_summary.py +398 -0
  179. aethergraph/services/memory/distillers/long_term.py +225 -0
  180. aethergraph/services/memory/facade/__init__.py +3 -0
  181. aethergraph/services/memory/facade/chat.py +440 -0
  182. aethergraph/services/memory/facade/core.py +447 -0
  183. aethergraph/services/memory/facade/distillation.py +424 -0
  184. aethergraph/services/memory/facade/rag.py +410 -0
  185. aethergraph/services/memory/facade/results.py +315 -0
  186. aethergraph/services/memory/facade/retrieval.py +139 -0
  187. aethergraph/services/memory/facade/types.py +77 -0
  188. aethergraph/services/memory/facade/utils.py +43 -0
  189. aethergraph/services/memory/facade_dep.py +1539 -0
  190. aethergraph/services/memory/factory.py +9 -3
  191. aethergraph/services/memory/utils.py +10 -0
  192. aethergraph/services/metering/eventlog_metering.py +470 -0
  193. aethergraph/services/metering/noop.py +25 -4
  194. aethergraph/services/rag/__init__.py +0 -0
  195. aethergraph/services/rag/facade.py +279 -23
  196. aethergraph/services/rag/index_factory.py +2 -2
  197. aethergraph/services/rag/node_rag.py +317 -0
  198. aethergraph/services/rate_limit/inmem_rate_limit.py +24 -0
  199. aethergraph/services/registry/__init__.py +0 -0
  200. aethergraph/services/registry/agent_app_meta.py +419 -0
  201. aethergraph/services/registry/registry_key.py +1 -1
  202. aethergraph/services/registry/unified_registry.py +74 -6
  203. aethergraph/services/scope/scope.py +159 -0
  204. aethergraph/services/scope/scope_factory.py +164 -0
  205. aethergraph/services/state_stores/serialize.py +5 -0
  206. aethergraph/services/state_stores/utils.py +2 -1
  207. aethergraph/services/viz/__init__.py +0 -0
  208. aethergraph/services/viz/facade.py +413 -0
  209. aethergraph/services/viz/viz_service.py +69 -0
  210. aethergraph/storage/artifacts/artifact_index_jsonl.py +180 -0
  211. aethergraph/storage/artifacts/artifact_index_sqlite.py +426 -0
  212. aethergraph/storage/artifacts/cas_store.py +422 -0
  213. aethergraph/storage/artifacts/fs_cas.py +18 -0
  214. aethergraph/storage/artifacts/s3_cas.py +14 -0
  215. aethergraph/storage/artifacts/utils.py +124 -0
  216. aethergraph/storage/blob/fs_blob.py +86 -0
  217. aethergraph/storage/blob/s3_blob.py +115 -0
  218. aethergraph/storage/continuation_store/fs_cont.py +283 -0
  219. aethergraph/storage/continuation_store/inmem_cont.py +146 -0
  220. aethergraph/storage/continuation_store/kvdoc_cont.py +261 -0
  221. aethergraph/storage/docstore/fs_doc.py +63 -0
  222. aethergraph/storage/docstore/sqlite_doc.py +31 -0
  223. aethergraph/storage/docstore/sqlite_doc_sync.py +90 -0
  224. aethergraph/storage/eventlog/fs_event.py +136 -0
  225. aethergraph/storage/eventlog/sqlite_event.py +47 -0
  226. aethergraph/storage/eventlog/sqlite_event_sync.py +178 -0
  227. aethergraph/storage/factory.py +432 -0
  228. aethergraph/storage/fs_utils.py +28 -0
  229. aethergraph/storage/graph_state_store/state_store.py +64 -0
  230. aethergraph/storage/kv/inmem_kv.py +103 -0
  231. aethergraph/storage/kv/layered_kv.py +52 -0
  232. aethergraph/storage/kv/sqlite_kv.py +39 -0
  233. aethergraph/storage/kv/sqlite_kv_sync.py +98 -0
  234. aethergraph/storage/memory/event_persist.py +68 -0
  235. aethergraph/storage/memory/fs_persist.py +118 -0
  236. aethergraph/{services/memory/hotlog_kv.py → storage/memory/hotlog.py} +8 -2
  237. aethergraph/{services → storage}/memory/indices.py +31 -7
  238. aethergraph/storage/metering/meter_event.py +55 -0
  239. aethergraph/storage/runs/doc_store.py +280 -0
  240. aethergraph/storage/runs/inmen_store.py +82 -0
  241. aethergraph/storage/runs/sqlite_run_store.py +403 -0
  242. aethergraph/storage/sessions/doc_store.py +183 -0
  243. aethergraph/storage/sessions/inmem_store.py +110 -0
  244. aethergraph/storage/sessions/sqlite_session_store.py +399 -0
  245. aethergraph/storage/vector_index/chroma_index.py +138 -0
  246. aethergraph/storage/vector_index/faiss_index.py +179 -0
  247. aethergraph/storage/vector_index/sqlite_index.py +187 -0
  248. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a2.dist-info}/METADATA +138 -31
  249. aethergraph-0.1.0a2.dist-info/RECORD +356 -0
  250. aethergraph-0.1.0a2.dist-info/entry_points.txt +3 -0
  251. aethergraph/services/artifacts/factory.py +0 -35
  252. aethergraph/services/artifacts/fs_store.py +0 -656
  253. aethergraph/services/artifacts/jsonl_index.py +0 -123
  254. aethergraph/services/artifacts/sqlite_index.py +0 -209
  255. aethergraph/services/memory/distillers/episode.py +0 -116
  256. aethergraph/services/memory/distillers/rolling.py +0 -74
  257. aethergraph/services/memory/facade.py +0 -633
  258. aethergraph/services/memory/persist_fs.py +0 -40
  259. aethergraph/services/rag/index/base.py +0 -27
  260. aethergraph/services/rag/index/faiss_index.py +0 -121
  261. aethergraph/services/rag/index/sqlite_index.py +0 -134
  262. aethergraph-0.1.0a1.dist-info/RECORD +0 -182
  263. aethergraph-0.1.0a1.dist-info/entry_points.txt +0 -2
  264. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a2.dist-info}/WHEEL +0 -0
  265. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a2.dist-info}/licenses/LICENSE +0 -0
  266. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a2.dist-info}/licenses/NOTICE +0 -0
  267. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,424 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from aethergraph.contracts.services.memory import Event
6
+
7
+ # Assuming this external util exists based on original imports
8
+ from ..utils import _summary_prefix
9
+ from .utils import now_iso, stable_event_id
10
+
11
+ if TYPE_CHECKING:
12
+ from .types import MemoryFacadeInterface
13
+
14
+
15
+ class DistillationMixin:
16
+ """Methods for memory summarization and distillation."""
17
+
18
+ async def distill_long_term(
19
+ self: MemoryFacadeInterface,
20
+ scope_id: str | None = None,
21
+ *,
22
+ summary_tag: str = "session",
23
+ summary_kind: str = "long_term_summary",
24
+ include_kinds: list[str] | None = None,
25
+ include_tags: list[str] | None = None,
26
+ max_events: int = 200,
27
+ min_signal: float | None = None,
28
+ use_llm: bool = False,
29
+ ) -> dict[str, Any]:
30
+ """
31
+ Distill long-term memory summaries based on specified criteria.
32
+ This method generates a long-term memory summary by either using a
33
+ Long-Term Summarizer or an LLM-based Long-Term Summarizer, depending
34
+ on the `use_llm` flag. The summaries are filtered and configured
35
+ based on the provided arguments.
36
+
37
+ Examples:
38
+ Using the default summarizer:
39
+ ```python
40
+ result = await context.memory().distill_long_term(
41
+ scope_id="scope_123",
42
+ include_kinds=["note", "event"],
43
+ max_events=100
44
+ )
45
+ ```
46
+ Using an LLM-based summarizer:
47
+ ```python
48
+ result = await context.memory().distill_long_term(
49
+ scope_id="scope_456",
50
+ use_llm=True,
51
+ summary_tag="custom_summary",
52
+ min_signal=0.5
53
+ )
54
+ ```
55
+
56
+ Args:
57
+ scope_id: The scope ID for the memory to summarize. If None,
58
+ defaults to the instance's `memory_scope_id`.
59
+ summary_tag: A tag to categorize the generated summary. Defaults
60
+ to `"session"`.
61
+ summary_kind: The kind of summary to generate. Defaults to
62
+ `"long_term_summary"`.
63
+ include_kinds: A list of memory kinds to include in the summary.
64
+ If None, all kinds are included.
65
+ include_tags: A list of tags to filter the memories. If None, no
66
+ tag filtering is applied.
67
+ max_events: The maximum number of events to include in the
68
+ summary. Defaults to 200.
69
+ min_signal: The minimum signal threshold for filtering events.
70
+ If None, the default signal threshold is used.
71
+ use_llm: Whether to use an LLM-based summarizer. Defaults to False.
72
+
73
+ Returns:
74
+ dict[str, Any]: A dictionary containing the generated summary.
75
+
76
+ Example return value:
77
+ ```python
78
+ {
79
+ "uri": "file://mem/scope_123/summaries/long_term/2023-10-01T12:00:00Z.json",
80
+ "summary_kind": "long_term_summary",
81
+ "summary_tag": "session",
82
+ "time_window": {"start": "2023-09-01", "end": "2023-09-30"},
83
+ "num_events": 150,
84
+ "included_kinds": ["note", "event"],
85
+ "included_tags": ["important", "meeting"],
86
+ }
87
+ ```
88
+ """
89
+
90
+ scope_id = scope_id or self.memory_scope_id
91
+
92
+ if use_llm:
93
+ if not self.llm:
94
+ raise RuntimeError("LLM client not configured")
95
+ from aethergraph.services.memory.distillers.llm_long_term import LLMLongTermSummarizer
96
+
97
+ d = LLMLongTermSummarizer(
98
+ llm=self.llm,
99
+ summary_kind=summary_kind,
100
+ summary_tag=summary_tag,
101
+ include_kinds=include_kinds,
102
+ include_tags=include_tags,
103
+ max_events=max_events,
104
+ min_signal=min_signal if min_signal is not None else self.default_signal_threshold,
105
+ )
106
+ else:
107
+ from aethergraph.services.memory.distillers.long_term import LongTermSummarizer
108
+
109
+ d = LongTermSummarizer(
110
+ summary_kind=summary_kind,
111
+ summary_tag=summary_tag,
112
+ include_kinds=include_kinds,
113
+ include_tags=include_tags,
114
+ max_events=max_events,
115
+ min_signal=min_signal if min_signal is not None else self.default_signal_threshold,
116
+ )
117
+
118
+ return await d.distill(
119
+ run_id=self.run_id,
120
+ timeline_id=self.timeline_id,
121
+ scope_id=scope_id or self.memory_scope_id,
122
+ hotlog=self.hotlog,
123
+ persistence=self.persistence,
124
+ indices=self.indices,
125
+ docs=self.docs,
126
+ )
127
+
128
+ async def distill_meta_summary(
129
+ self,
130
+ scope_id: str | None = None,
131
+ *,
132
+ source_kind: str = "long_term_summary",
133
+ source_tag: str = "session",
134
+ summary_kind: str = "meta_summary",
135
+ summary_tag: str = "meta",
136
+ max_summaries: int = 20,
137
+ min_signal: float | None = None,
138
+ use_llm: bool = True,
139
+ ) -> dict[str, Any]:
140
+ """
141
+ Generate a meta-summary by distilling existing summary events.
142
+
143
+ This method creates a meta-summary by processing existing long-term
144
+ summaries. It uses an LLM-based summarizer to generate a higher-level
145
+ summary based on the provided arguments.
146
+
147
+ Examples:
148
+ Using the default configuration:
149
+ ```python
150
+ result = await context.memory().distill_meta_summary(
151
+ scope_id="scope_123",
152
+ source_kind="long_term_summary",
153
+ source_tag="session",
154
+ )
155
+ ```
156
+
157
+ Customizing the summary kind and tag:
158
+ ```python
159
+ result = await context.memory().distill_meta_summary(
160
+ scope_id="scope_456",
161
+ summary_kind="meta_summary",
162
+ summary_tag="weekly",
163
+ max_summaries=10,
164
+ )
165
+ ```
166
+
167
+ Args:
168
+ scope_id: The scope ID for the memory to summarize. If None,
169
+ defaults to the instance's `memory_scope_id`.
170
+ source_kind: The kind of source summaries to process. Defaults to
171
+ `"long_term_summary"`.
172
+ source_tag: A tag to filter the source summaries. Defaults to
173
+ `"session"`.
174
+ summary_kind: The kind of meta-summary to generate. Defaults to
175
+ `"meta_summary"`.
176
+ summary_tag: A tag to categorize the generated meta-summary.
177
+ Defaults to `"meta"`.
178
+ max_summaries: The maximum number of source summaries to process.
179
+ Defaults to 20.
180
+ min_signal: The minimum signal threshold for filtering summaries.
181
+ If None, the default signal threshold is used.
182
+ use_llm: Whether to use an LLM-based summarizer. Defaults to True.
183
+
184
+ Returns:
185
+ dict[str, Any]: A dictionary containing the generated meta-summary.
186
+
187
+ Example return value:
188
+ ```python
189
+ {
190
+ "uri": "file://mem/scope_123/summaries/meta/2023-10-01T12:00:00Z.json",
191
+ "summary_kind": "meta_summary",
192
+ "summary_tag": "meta",
193
+ "time_window": {"start": "2023-09-01", "end": "2023-09-30"},
194
+ "num_source_summaries": 15,
195
+ }
196
+ ```
197
+ """
198
+ scope_id = scope_id or self.memory_scope_id # order of precedence
199
+
200
+ if not use_llm:
201
+ # Placeholder for a future non-LLM meta summarizer if desired.
202
+ raise NotImplementedError("Non-LLM meta summarization is not implemented yet")
203
+
204
+ if not self.llm:
205
+ raise RuntimeError("LLM client not configured in MemoryFacade for meta distillation")
206
+
207
+ from aethergraph.services.memory.distillers.llm_meta_summary import (
208
+ LLMMetaSummaryDistiller,
209
+ )
210
+
211
+ d = LLMMetaSummaryDistiller(
212
+ llm=self.llm,
213
+ source_kind=source_kind,
214
+ source_tag=source_tag,
215
+ summary_kind=summary_kind,
216
+ summary_tag=summary_tag,
217
+ max_summaries=max_summaries,
218
+ min_signal=min_signal if min_signal is not None else self.default_signal_threshold,
219
+ )
220
+ return await d.distill(
221
+ run_id=self.run_id,
222
+ timeline_id=self.timeline_id,
223
+ scope_id=scope_id or self.memory_scope_id,
224
+ hotlog=self.hotlog,
225
+ persistence=self.persistence,
226
+ indices=self.indices,
227
+ docs=self.docs,
228
+ )
229
+
230
+ async def load_last_summary(
231
+ self,
232
+ scope_id: str | None = None,
233
+ *,
234
+ summary_tag: str = "session",
235
+ ) -> dict[str, Any] | None:
236
+ """
237
+ Load the most recent JSON summary for the specified memory scope and tag.
238
+
239
+ This method retrieves the latest summary document from the `DocStore`
240
+ based on the provided `scope_id` and `summary_tag`. Summaries are
241
+ identified using the following pattern:
242
+ `mem/{scope_id}/summaries/{summary_tag}/{ts}`.
243
+
244
+ Examples:
245
+ Load the last session summary:
246
+ ```python
247
+ summary = await context.memory().load_last_summary(scope_id="user123", summary_tag="session")
248
+ ```
249
+
250
+ Load the last project summary:
251
+ ```python
252
+ summary = await context.memory().load_last_summary(scope_id="project456", summary_tag="project")
253
+ ```
254
+
255
+ Args:
256
+ scope_id: The memory scope ID. If None, defaults to the current memory scope.
257
+ summary_tag: The tag used to filter summaries (e.g., "session", "project").
258
+ Defaults to "session".
259
+
260
+ Returns:
261
+ dict[str, Any] | None: The most recent summary as a dictionary, or None if no summary is found.
262
+ """
263
+ scope_id = scope_id or self.memory_scope_id
264
+ prefix = _summary_prefix(scope_id, summary_tag)
265
+
266
+ try:
267
+ ids = await self.docs.list()
268
+ except Exception as e:
269
+ self.logger and self.logger.warning("load_last_summary: doc_store.list() failed: %s", e)
270
+ return None
271
+
272
+ # Filter and take the latest
273
+ candidates = [d for d in ids if d.startswith(prefix)]
274
+ if not candidates:
275
+ return None
276
+
277
+ latest_id = sorted(candidates)[-1]
278
+ try:
279
+ return await self.docs.get(latest_id) # type: ignore[return-value]
280
+ except Exception as e:
281
+ self.logger and self.logger.warning(
282
+ "load_last_summary: failed to load %s: %s", latest_id, e
283
+ )
284
+ return None
285
+
286
+ async def load_recent_summaries(
287
+ self,
288
+ scope_id: str | None = None,
289
+ *,
290
+ summary_tag: str = "session",
291
+ limit: int = 3,
292
+ ) -> list[dict[str, Any]]:
293
+ """
294
+ Load the most recent JSON summaries for the specified scope and tag.
295
+
296
+ This method retrieves up to `limit` summaries from the `DocStore`
297
+ based on the provided `scope_id` and `summary_tag`. Summaries are
298
+ identified using the following pattern:
299
+ `mem/{scope_id}/summaries/{summary_tag}/{ts}`.
300
+
301
+ Examples:
302
+ Load the last three session summaries:
303
+ ```python
304
+ summaries = await context.memory().load_recent_summaries(
305
+ scope_id="user123",
306
+ summary_tag="session",
307
+ limit=3
308
+ )
309
+ ```
310
+
311
+ Load the last two project summaries:
312
+ ```python
313
+ summaries = await context.memory().load_recent_summaries(
314
+ scope_id="project456",
315
+ summary_tag="project",
316
+ limit=2
317
+ )
318
+ ```
319
+
320
+ Args:
321
+ scope_id: The memory scope ID. If None, defaults to the current memory scope.
322
+ summary_tag: The tag used to filter summaries (e.g., "session", "project").
323
+ Defaults to "session".
324
+ limit: The maximum number of summaries to return. Defaults to 3.
325
+
326
+ Returns:
327
+ list[dict[str, Any]]: A list of summary dictionaries, ordered from oldest to newest.
328
+ """
329
+ scope_id = scope_id or self.memory_scope_id
330
+ prefix = _summary_prefix(scope_id, summary_tag)
331
+
332
+ try:
333
+ ids = await self.docs.list()
334
+ except Exception as e:
335
+ self.logger and self.logger.warning(
336
+ "load_recent_summaries: doc_store.list() failed: %s", e
337
+ )
338
+ return []
339
+
340
+ candidates = sorted(d for d in ids if d.startswith(prefix))
341
+ if not candidates:
342
+ return []
343
+
344
+ chosen = candidates[-limit:]
345
+ out: list[dict[str, Any]] = []
346
+ for doc_id in chosen:
347
+ try:
348
+ doc = await self.docs.get(doc_id)
349
+ if doc is not None:
350
+ out.append(doc) # type: ignore[arg-type]
351
+ except Exception:
352
+ continue
353
+ return out
354
+
355
+ async def soft_hydrate_last_summary(
356
+ self,
357
+ scope_id: str | None = None,
358
+ *,
359
+ summary_tag: str = "session",
360
+ summary_kind: str = "long_term_summary",
361
+ ) -> dict[str, Any] | None:
362
+ """
363
+ Load the most recent summary for the specified scope and tag, and log a hydrate event.
364
+
365
+ This method retrieves the latest summary document from the `DocStore` based on the
366
+ provided `scope_id` and `summary_tag`. If a summary is found, it logs a hydrate
367
+ event into the current run's HotLog and Persistence layers.
368
+
369
+ Examples:
370
+ Hydrate the last session summary:
371
+ ```python
372
+ summary = await context.memory().soft_hydrate_last_summary(
373
+ scope_id="user123",
374
+ summary_tag="session"
375
+ )
376
+ ```
377
+
378
+ Args:
379
+ scope_id: The memory scope ID. If None, defaults to the current memory scope.
380
+ summary_tag: The tag used to filter summaries (e.g., "session", "project").
381
+ Defaults to "session".
382
+ summary_kind: The kind of summary (e.g., "long_term_summary", "project_summary").
383
+ Defaults to "long_term_summary".
384
+
385
+ Returns:
386
+ dict[str, Any] | None: The loaded summary dictionary if found, otherwise None.
387
+
388
+ Side Effects:
389
+ Appends a hydrate event to HotLog and Persistence for the current timeline.
390
+ """
391
+ scope_id = scope_id or self.memory_scope_id
392
+ summary = await self.load_last_summary(scope_id=scope_id, summary_tag=summary_tag)
393
+ if not summary:
394
+ return None
395
+
396
+ text = summary.get("text") or ""
397
+ preview = text[:2000] + (" …[truncated]" if len(text) > 2000 else "")
398
+
399
+ evt = Event(
400
+ scope_id=self.memory_scope_id or self.run_id,
401
+ event_id=stable_event_id(
402
+ {
403
+ "ts": now_iso(),
404
+ "run_id": self.run_id,
405
+ "kind": f"{summary_kind}_hydrate",
406
+ "summary_tag": summary_tag,
407
+ "preview": preview[:200],
408
+ }
409
+ ),
410
+ ts=now_iso(),
411
+ run_id=self.run_id,
412
+ kind=f"{summary_kind}_hydrate",
413
+ stage="hydrate",
414
+ text=preview,
415
+ tags=["summary", "hydrate", summary_tag],
416
+ data={"summary": summary},
417
+ metrics={"num_events": summary.get("num_events", 0)},
418
+ severity=1,
419
+ signal=0.4,
420
+ )
421
+
422
+ await self.hotlog.append(self.timeline_id, evt, ttl_s=self.hot_ttl_s, limit=self.hot_limit)
423
+ await self.persistence.append_event(self.timeline_id, evt)
424
+ return summary