aethergraph 0.1.0a1__py3-none-any.whl → 0.1.0a3__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 +296 -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 +196 -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.0a3.dist-info}/METADATA +138 -31
  249. aethergraph-0.1.0a3.dist-info/RECORD +356 -0
  250. aethergraph-0.1.0a3.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.0a3.dist-info}/WHEEL +0 -0
  265. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/licenses/LICENSE +0 -0
  266. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/licenses/NOTICE +0 -0
  267. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/top_level.txt +0 -0
@@ -24,6 +24,31 @@ class EphemeralKV:
24
24
  return f"{self._prefix}{k}" if self._prefix else k
25
25
 
26
26
  async def get(self, key: str, default: Any = None) -> Any:
27
+ """
28
+ Retrieve the value associated with a key from the ephemeral key-value store.
29
+
30
+ This method checks for key existence and expiration, automatically removing
31
+ expired entries. If the key does not exist or is expired, the provided
32
+ `default` value is returned.
33
+
34
+ Examples:
35
+ Basic usage to fetch a value:
36
+ ```python
37
+ value = await context.kv().get("session_token")
38
+ ```
39
+
40
+ Providing a default if the key is missing or expired:
41
+ ```python
42
+ user_id = await context.kv().get("user_id", default=None)
43
+ ```
44
+
45
+ Args:
46
+ key: The string key to look up.
47
+ default: The value to return if the key is not found or has expired.
48
+
49
+ Returns:
50
+ The stored value if present and not expired; otherwise, the `default`.
51
+ """
27
52
  k = self._k(key)
28
53
  with self._lock:
29
54
  e = self._data.get(k)
@@ -35,11 +60,60 @@ class EphemeralKV:
35
60
  return e.value
36
61
 
37
62
  async def set(self, key: str, value: Any, *, ttl_s: int | None = None) -> None:
63
+ """
64
+ Store a value in the ephemeral key-value store with optional expiration.
65
+
66
+ This method inserts or updates the value for a given key, optionally setting
67
+ a time-to-live (TTL) in seconds. If a TTL is provided, the entry will expire
68
+ and be automatically removed after the specified duration.
69
+
70
+ Examples:
71
+ Basic usage to store a value:
72
+ ```python
73
+ await context.kv().set("session_token", "abc123")
74
+ ```
75
+
76
+ Storing a value with a 10-minute expiration:
77
+ ```python
78
+ await context.kv().set("user_id", 42, ttl_s=600)
79
+ ```
80
+
81
+ Args:
82
+ key: The string key under which to store the value.
83
+ value: The value to store (any serializable object).
84
+ ttl_s: Optional expiration time in seconds. If None, the value does not expire.
85
+
86
+ Returns:
87
+ None
88
+ """
38
89
  k = self._k(key)
39
90
  with self._lock:
40
91
  self._data[k] = KVEntry(value=value, expire_at=(time.time() + ttl_s) if ttl_s else None)
41
92
 
42
93
  async def delete(self, key: str) -> None:
94
+ """
95
+ Remove a key and its associated value from the ephemeral key-value store.
96
+
97
+ This method deletes the specified key from the store if it exists. If the key
98
+ does not exist, the operation is a no-op and does not raise an error.
99
+
100
+ Examples:
101
+ Basic usage to delete a key:
102
+ ```python
103
+ await context.kv().delete("session_token")
104
+ ```
105
+
106
+ Deleting a user-specific cache entry:
107
+ ```python
108
+ await context.kv().delete(f"user_cache:{user_id}")
109
+ ```
110
+
111
+ Args:
112
+ key: The string key to remove from the store.
113
+
114
+ Returns:
115
+ None
116
+ """
43
117
  k = self._k(key)
44
118
  with self._lock:
45
119
  self._data.pop(k, None)
@@ -47,6 +121,52 @@ class EphemeralKV:
47
121
  async def list_append_unique(
48
122
  self, key: str, items: list[dict], *, id_key: str = "id", ttl_s: int | None = None
49
123
  ) -> list[dict]:
124
+ """
125
+ Append unique dictionary items to a list stored in the ephemeral key-value store.
126
+
127
+ This method ensures that only items with unique `id_key` values are added to the list
128
+ associated with the given key. If the key does not exist, a new list is created.
129
+ Optionally, a time-to-live (TTL) can be set for the entry.
130
+
131
+ Examples:
132
+ Basic usage to append unique items:
133
+ ```python
134
+ await context.kv().list_append_unique("recent_users", [{"id": 1, "name": "Alice"}])
135
+ ```
136
+
137
+ Appending multiple items with a custom ID key and expiration:
138
+ ```python
139
+ await context.kv().list_append_unique(
140
+ "tasks",
141
+ [{"task_id": 42, "desc": "Review PR"}],
142
+ id_key="task_id",
143
+ ttl_s=3600
144
+ )
145
+ ```
146
+
147
+ Args:
148
+ key: The string key under which the list is stored.
149
+ items: A list of dictionaries to append. Only items with unique `id_key` values
150
+ (not already present in the list) will be added.
151
+ id_key: The dictionary key used to determine uniqueness (default: `"id"`).
152
+ ttl_s: Optional expiration time in seconds for the updated list. If None, the list does not expire.
153
+
154
+ Returns:
155
+ list[dict]: The updated list of dictionaries after appending unique items.
156
+
157
+ Notes:
158
+ - This method is used for lists of dictionaries where each dictionary has a unique identifier. For example,
159
+ it can be used to maintain a list of recent user actions, ensuring no duplicates based on user ID.
160
+ - Example of the stored list structure:
161
+ ```python
162
+ [
163
+ {"id": 1, "name": "Alice"},
164
+ {"id": 2, "name": "Bob"},
165
+ ...
166
+ ]
167
+ ```
168
+
169
+ """
50
170
  k = self._k(key)
51
171
  with self._lock:
52
172
  cur = list(self._data.get(k, KVEntry([])).value or [])
@@ -56,6 +176,30 @@ class EphemeralKV:
56
176
  return cur
57
177
 
58
178
  async def list_pop_all(self, key: str) -> list:
179
+ """
180
+ Atomically remove and return all items from a list stored in the ephemeral key-value store.
181
+
182
+ This method retrieves the entire list associated with the given key and removes the key from the store.
183
+ If the key does not exist or does not contain a list, an empty list is returned. This operation is atomic
184
+ and ensures no items are left behind after the call.
185
+
186
+ Examples:
187
+ Basic usage to pop all items from a list:
188
+ ```python
189
+ items = await context.kv().list_pop_all("recent_events")
190
+ ```
191
+
192
+ Handling the case where the key may not exist:
193
+ ```python
194
+ logs = await context.kv().list_pop_all("logs") # returns [] if "logs" is missing
195
+ ```
196
+
197
+ Args:
198
+ key: The string key under which the list is stored.
199
+
200
+ Returns:
201
+ list: The list of items that were stored under the key, or an empty list if the key was not found.
202
+ """
59
203
  k = self._k(key)
60
204
  with self._lock:
61
205
  e = self._data.pop(k, None)
@@ -63,13 +207,89 @@ class EphemeralKV:
63
207
 
64
208
  # Optional helpers
65
209
  async def mget(self, keys: list[str]) -> list[Any]:
210
+ """
211
+ Retrieve multiple values from the ephemeral key-value store in a single call.
212
+
213
+ This method fetches the values associated with each key in the provided list,
214
+ preserving the order of the input. If a key does not exist or is expired, `None`
215
+ is returned in its place.
216
+
217
+ Examples:
218
+ Basic usage to fetch several values:
219
+ ```python
220
+ values = await context.kv().mget(["user_id", "session_token", "profile"])
221
+ ```
222
+
223
+ Handling missing or expired keys:
224
+ ```python
225
+ results = await context.kv().mget(["foo", "bar"])
226
+ # results might be [None, "bar_value"] if "foo" is missing or expired
227
+ ```
228
+
229
+ Args:
230
+ keys: A list of string keys to retrieve from the store.
231
+
232
+ Returns:
233
+ list[Any]: A list of values corresponding to the input keys. If a key is not found
234
+ or has expired, its position in the list will be `None`.
235
+ """
66
236
  return [await self.get(k) for k in keys]
67
237
 
68
238
  async def mset(self, kv: dict[str, Any], *, ttl_s: int | None = None) -> None:
239
+ """
240
+ Set multiple key-value pairs in the ephemeral key-value store.
241
+
242
+ This asynchronous method iterates over the provided dictionary and sets each key-value pair
243
+ in the store, optionally applying a time-to-live (TTL) to each entry. If a TTL is specified,
244
+ each key will expire after the given number of seconds.
245
+
246
+ Examples:
247
+ Basic usage to set multiple values:
248
+ ```python
249
+ await context.kv().mset({"foo": 1, "bar": "baz"})
250
+ ```
251
+
252
+ Setting multiple values with a TTL of 60 seconds:
253
+ ```python
254
+ await context.kv().mset({"session": "abc", "count": 42}, ttl_s=60)
255
+ ```
256
+
257
+ Args:
258
+ kv: A dictionary mapping string keys to values to be stored.
259
+ ttl_s: Optional; the time-to-live for each key in seconds. If None, keys do not expire.
260
+
261
+ Returns:
262
+ None
263
+ """
69
264
  for k, v in kv.items():
70
265
  await self.set(k, v, ttl_s=ttl_s)
71
266
 
72
267
  async def expire(self, key: str, ttl_s: int) -> None:
268
+ """
269
+ Set or update the expiration time (TTL) for a key in the ephemeral key-value store.
270
+
271
+ This method updates the expiration timestamp for an existing key, causing it to expire
272
+ and be automatically removed after the specified number of seconds. If the key does not
273
+ exist, this operation is a no-op.
274
+
275
+ Examples:
276
+ Basic usage to set a 5-minute expiration:
277
+ ```python
278
+ await context.kv().expire("session_token", ttl_s=300)
279
+ ```
280
+
281
+ Updating the TTL for a cached user profile:
282
+ ```python
283
+ await context.kv().expire("user_profile:42", ttl_s=60)
284
+ ```
285
+
286
+ Args:
287
+ key: The string key whose expiration time should be set or updated.
288
+ ttl_s: The time-to-live in seconds from now. After this duration, the key will expire.
289
+
290
+ Returns:
291
+ None
292
+ """
73
293
  k = self._k(key)
74
294
  with self._lock:
75
295
  e = self._data.get(k)
@@ -77,6 +297,30 @@ class EphemeralKV:
77
297
  e.expire_at = time.time() + ttl_s
78
298
 
79
299
  async def purge_expired(self, limit: int = 1000) -> int:
300
+ """
301
+ Remove expired key-value entries from the ephemeral store.
302
+
303
+ This method scans the internal data store for entries whose expiration
304
+ timestamp has passed and removes them, up to the specified limit. It is
305
+ intended to be called periodically to keep the store clean and efficient.
306
+
307
+ Examples:
308
+ Purge up to 1000 expired entries:
309
+ ```python
310
+ removed = await context.kv().purge_expired()
311
+ ```
312
+
313
+ Purge a custom number of expired entries:
314
+ ```python
315
+ removed = await context.kv().purge_expired(limit=500)
316
+ ```
317
+
318
+ Args:
319
+ limit: The maximum number of expired entries to remove in a single call.
320
+
321
+ Returns:
322
+ int: The number of expired entries that were removed from the store.
323
+ """
80
324
  n = 0
81
325
  now = time.time()
82
326
  with self._lock:
File without changes