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
@@ -0,0 +1,440 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ from typing import TYPE_CHECKING, Any, Literal
5
+
6
+ from aethergraph.contracts.services.memory import Event
7
+
8
+ if TYPE_CHECKING:
9
+ from .types import MemoryFacadeInterface
10
+
11
+
12
+ class ChatMixin:
13
+ """
14
+ Mixin adding chat-related memory functionality to MemoryFacade.
15
+
16
+ Include methods:
17
+ - record_chat
18
+ - record_chat_user
19
+ - record_chat_assistant
20
+ - record_chat_system
21
+ - record_chat_tool
22
+ - recent_chat
23
+ - chat_history_for_llm
24
+ """
25
+
26
+ async def record_chat(
27
+ self: MemoryFacadeInterface,
28
+ role: Literal["user", "assistant", "system", "tool"],
29
+ text: str,
30
+ *,
31
+ tags: list[str] | None = None,
32
+ data: dict[str, Any] | None = None,
33
+ severity: int = 2,
34
+ signal: float | None = None,
35
+ ) -> Event:
36
+ """
37
+ Record a single chat turn in a normalized format.
38
+
39
+ This method automatically handles timestamping, standardizes the `role`,
40
+ and dispatches the event to the configured persistence layer.
41
+
42
+ Examples:
43
+ Basic usage for a user message:
44
+ ```python
45
+ await context.memory().record_chat("user", "Hello graph!")
46
+ ```
47
+
48
+ Recording a tool output with extra metadata:
49
+ ```python
50
+ await context.memory().record_chat(
51
+ "tool",
52
+ "Search results found.",
53
+ data={"query": "weather", "hits": 5}
54
+ )
55
+ ```
56
+
57
+ Args:
58
+ role: The semantic role of the speaker. Must be one of:
59
+ `"user"`, `"assistant"`, `"system"`, or `"tool"`.
60
+ text: The primary text content of the message.
61
+ tags: A list of string labels for categorization. The tag `"chat"`
62
+ is automatically appended to this list.
63
+ data: Arbitrary JSON-serializable dictionary containing extra
64
+ context (e.g., token counts, model names).
65
+ severity: An integer (1-3) indicating importance.
66
+ (1=Low, 2=Normal, 3=High).
67
+ signal: Manual override for the signal strength (0.0 to 1.0).
68
+ If None, it is calculated heuristically.
69
+
70
+ Returns:
71
+ Event: The fully persisted `Event` object containing the generated ID and timestamp.
72
+ """
73
+ extra_tags = ["chat"]
74
+ if tags:
75
+ extra_tags.extend(tags)
76
+ payload: dict[str, Any] = {"role": role, "text": text}
77
+ if data:
78
+ payload.update(data)
79
+
80
+ return await self.record(
81
+ kind="chat.turn",
82
+ text=text,
83
+ data=payload,
84
+ tags=extra_tags,
85
+ severity=severity,
86
+ stage=role,
87
+ signal=signal,
88
+ )
89
+
90
+ async def record_chat_user(
91
+ self: MemoryFacadeInterface,
92
+ text: str,
93
+ *,
94
+ tags: list[str] | None = None,
95
+ data: dict[str, Any] | None = None,
96
+ severity: int = 2,
97
+ signal: float | None = None,
98
+ ) -> Event:
99
+ """
100
+ Record a user chat turn in a normalized format.
101
+
102
+ This method automatically handles timestamping, standardizes the `role`,
103
+ and dispatches the event to the configured persistence layer.
104
+
105
+ Examples:
106
+ Basic usage for a user message:
107
+ ```python
108
+ await context.memory().record_chat_user("Hello, how are you doing?")
109
+ ```
110
+
111
+ Recording a user message with extra metadata:
112
+ ```python
113
+ await context.memory().record_chat_user(
114
+ "I need help with my account.",
115
+ tags=["support", "account"],
116
+ data={"issue": "login failure"}
117
+ )
118
+ ```
119
+
120
+ Args:
121
+ text: The primary text content of the user's message.
122
+ tags: A list of string labels for categorization. The tag `"chat"`
123
+ is automatically appended to this list.
124
+ data: Arbitrary JSON-serializable dictionary containing extra
125
+ context (e.g., user metadata, session details).
126
+ severity: An integer (1-3) indicating importance.
127
+ (1=Low, 2=Normal, 3=High). Defaults to 2.
128
+ signal: Manual override for the signal strength (0.0 to 1.0).
129
+ If None, it is calculated heuristically.
130
+
131
+ Returns:
132
+ Event: The fully persisted `Event` object containing the generated ID and timestamp.
133
+ """
134
+
135
+ return await self.record_chat(
136
+ "user",
137
+ text,
138
+ tags=tags,
139
+ data=data,
140
+ severity=severity,
141
+ signal=signal,
142
+ )
143
+
144
+ async def record_chat_assistant(
145
+ self: MemoryFacadeInterface,
146
+ text: str,
147
+ *,
148
+ tags: list[str] | None = None,
149
+ data: dict[str, Any] | None = None,
150
+ severity: int = 2,
151
+ signal: float | None = None,
152
+ ) -> Event:
153
+ """
154
+ Record an assistant chat turn in a normalized format.
155
+
156
+ This method automatically handles timestamping, standardizes the `role`,
157
+ and dispatches the event to the configured persistence layer.
158
+
159
+ Examples:
160
+ Basic usage for an assistant message:
161
+ ```python
162
+ await context.memory().record_chat_assistant("How can I assist you?")
163
+ ```
164
+
165
+ Recording an assistant message with extra metadata:
166
+ ```python
167
+ await context.memory().record_chat_assistant(
168
+ "Here are the search results.",
169
+ tags=["search", "response"],
170
+ data={"query": "latest news", "results_count": 10}
171
+ )
172
+ ```
173
+
174
+ Args:
175
+ text: The primary text content of the assistant's message.
176
+ tags: A list of string labels for categorization. The tag `"chat"`
177
+ is automatically appended to this list.
178
+ data: Arbitrary JSON-serializable dictionary containing extra
179
+ context (e.g., token counts, model names).
180
+ severity: An integer (1-3) indicating importance.
181
+ (1=Low, 2=Normal, 3=High).
182
+ signal: Manual override for the signal strength (0.0 to 1.0).
183
+ If None, it is calculated heuristically.
184
+
185
+ Returns:
186
+ Event: The fully persisted `Event` object containing the generated ID and timestamp.
187
+ """
188
+ return await self.record_chat(
189
+ "assistant",
190
+ text,
191
+ tags=tags,
192
+ data=data,
193
+ severity=severity,
194
+ signal=signal,
195
+ )
196
+
197
+ async def record_chat_system(
198
+ self: MemoryFacadeInterface,
199
+ text: str,
200
+ *,
201
+ tags: list[str] | None = None,
202
+ data: dict[str, Any] | None = None,
203
+ severity: int = 1,
204
+ signal: float | None = None,
205
+ ) -> Event:
206
+ """
207
+ Record a system message in a normalized format.
208
+
209
+ This method automatically handles timestamping, standardizes the `role`,
210
+ and dispatches the event to the configured persistence layer.
211
+
212
+ Examples:
213
+ Basic usage for a system message:
214
+ ```python
215
+ await context.memory().record_chat_system("System initialized.")
216
+ ```
217
+
218
+ Recording a system message with extra metadata:
219
+ ```python
220
+ await context.memory().record_chat_system(
221
+ "Configuration updated.",
222
+ tags=["config", "update"],
223
+ data={"version": "1.2.3"}
224
+ )
225
+ ```
226
+
227
+ Args:
228
+ text: The primary text content of the system message.
229
+ tags: A list of string labels for categorization. The tag `"chat"`
230
+ is automatically appended to this list.
231
+ data: Arbitrary JSON-serializable dictionary containing extra
232
+ context (e.g., configuration details, system state).
233
+ severity: An integer (1-3) indicating importance.
234
+ (1=Low, 2=Normal, 3=High). Defaults to 1.
235
+ signal: Manual override for the signal strength (0.0 to 1.0).
236
+ If None, it is calculated heuristically.
237
+
238
+ Returns:
239
+ Event: The fully persisted `Event` object containing the generated ID and timestamp.
240
+ """
241
+ return await self.record_chat(
242
+ "system",
243
+ text,
244
+ tags=tags,
245
+ data=data,
246
+ severity=severity,
247
+ signal=signal,
248
+ )
249
+
250
+ async def recent_chat(
251
+ self: MemoryFacadeInterface,
252
+ *,
253
+ limit: int = 50,
254
+ roles: Sequence[str] | None = None,
255
+ ) -> list[dict[str, Any]]:
256
+ """
257
+ Retrieve the most recent chat turns as a normalized list.
258
+
259
+ This method fetches the last `limit` chat events of type `chat.turn`
260
+ and returns them in a standardized format. Each item in the returned
261
+ list contains the timestamp, role, text, and tags associated with the
262
+ chat event.
263
+
264
+ Examples:
265
+ Fetch the last 10 chat turns:
266
+ ```python
267
+ recent_chats = await context.memory().recent_chat(limit=10)
268
+ ```
269
+
270
+ Fetch the last 20 chat turns for specific roles:
271
+ ```python
272
+ recent_chats = await context.memory().recent_chat(
273
+ limit=20, roles=["user", "assistant"]
274
+ )
275
+ ```
276
+
277
+ Args:
278
+ limit: The maximum number of chat events to retrieve. Defaults to 50.
279
+ roles: An optional sequence of roles to filter by (e.g., `["user", "assistant"]`).
280
+
281
+ Returns:
282
+ list[dict[str, Any]]: A list of chat events, each represented as a dictionary
283
+ with the following keys:
284
+ - "ts": The timestamp of the event.
285
+ - "role": The role of the speaker (e.g., "user", "assistant").
286
+ - "text": The text content of the chat message.
287
+ - "tags": A list of tags associated with the event.
288
+ """
289
+ events = await self.recent(kinds=["chat.turn"], limit=limit)
290
+ out: list[dict[str, Any]] = []
291
+
292
+ for e in events:
293
+ # 1) Resolve role (from stage or data)
294
+ role = (
295
+ getattr(e, "stage", None)
296
+ or ((e.data or {}).get("role") if getattr(e, "data", None) else None)
297
+ or "user"
298
+ )
299
+
300
+ if roles is not None and role not in roles:
301
+ continue
302
+
303
+ # 2) Resolve text:
304
+ # - prefer Event.text
305
+ # - fall back to data["text"]
306
+ raw_text = getattr(e, "text", "") or ""
307
+ if not raw_text and getattr(e, "data", None):
308
+ raw_text = (e.data or {}).get("text", "") or ""
309
+
310
+ out.append(
311
+ {
312
+ "ts": getattr(e, "ts", None),
313
+ "role": role,
314
+ "text": raw_text,
315
+ "tags": list(e.tags or []),
316
+ }
317
+ )
318
+
319
+ return out
320
+
321
+ async def chat_history_for_llm(
322
+ self: MemoryFacadeInterface,
323
+ *,
324
+ limit: int = 20,
325
+ include_system_summary: bool = True,
326
+ summary_tag: str = "session",
327
+ summary_scope_id: str | None = None,
328
+ max_summaries: int = 3,
329
+ ) -> dict[str, Any]:
330
+ """
331
+ Build a ready-to-send OpenAI-style chat message list.
332
+
333
+ This method constructs a dictionary containing a summary of previous
334
+ context and a list of chat messages formatted for use with OpenAI-style
335
+ chat models. It includes options to limit the number of messages and
336
+ incorporate long-term summaries.
337
+
338
+ Examples:
339
+ Basic usage with default parameters:
340
+ ```python
341
+ history = await context.memory().chat_history_for_llm()
342
+ ```
343
+
344
+ Including a system summary and limiting messages:
345
+ ```python
346
+ history = await context.memory().chat_history_for_llm(
347
+ limit=10, include_system_summary=True
348
+ )
349
+ ```
350
+
351
+ Args:
352
+ limit: The maximum number of recent chat messages to include. Defaults to 20.
353
+ include_system_summary: Whether to include a system summary of previous
354
+ context. Defaults to True.
355
+ summary_tag: The tag used to filter summaries. Defaults to "session".
356
+ summary_scope_id: An optional scope ID for filtering summaries. Defaults to None.
357
+ max_summaries: The maximum number of summaries to load. Defaults to 3.
358
+
359
+ Returns:
360
+ dict[str, Any]: A dictionary with the following structure:
361
+ - "summary": A combined long-term summary or an empty string.
362
+ - "messages": A list of chat messages, each represented as a dictionary
363
+ with "role" and "content" keys.
364
+
365
+ Example of returned structure:
366
+ ```python
367
+ {
368
+ "summary": "Summary of previous context...",
369
+ "messages": [
370
+ {"role": "system", "content": "Summary of previous context..."},
371
+ {"role": "user", "content": "Hello!"},
372
+ {"role": "assistant", "content": "Hi there! How can I help?"}
373
+ ]
374
+ }
375
+ ```
376
+ """
377
+ messages: list[dict[str, str]] = []
378
+ summary_text = ""
379
+
380
+ if include_system_summary:
381
+ try:
382
+ summaries = await self.load_recent_summaries(
383
+ scope_id=summary_scope_id,
384
+ summary_tag=summary_tag,
385
+ limit=max_summaries,
386
+ )
387
+ except Exception:
388
+ summaries = []
389
+
390
+ parts: list[str] = []
391
+ for s in summaries:
392
+ st = s.get("summary") or s.get("text") or s.get("body") or s.get("value") or ""
393
+ if st:
394
+ parts.append(st)
395
+
396
+ if parts:
397
+ summary_text = "\n\n".join(parts)
398
+ messages.append(
399
+ {
400
+ "role": "system",
401
+ "content": f"Summary of previous context:\n{summary_text}",
402
+ }
403
+ )
404
+
405
+ # Append recent chat turns
406
+ for item in await self.recent_chat(limit=limit):
407
+ role = item["role"]
408
+ # Map unknown roles (e.g. "tool") to "assistant" by default
409
+ mapped_role = role if role in {"user", "assistant", "system"} else "assistant"
410
+ messages.append({"role": mapped_role, "content": item["text"]})
411
+
412
+ return {"summary": summary_text, "messages": messages}
413
+
414
+ async def record_chat_tool(
415
+ self: MemoryFacadeInterface,
416
+ tool_name: str,
417
+ text: str,
418
+ *,
419
+ tags: list[str] | None = None,
420
+ data: dict[str, Any] | None = None,
421
+ severity: int = 2,
422
+ signal: float | None = None,
423
+ ) -> Event:
424
+ """
425
+ TODO: Consider if use this method or just use record_chat directly.
426
+ """
427
+ tool_tags = list(tags or [])
428
+ tool_tags.append(f"tool:{tool_name}")
429
+ payload: dict[str, Any] = {"tool_name": tool_name}
430
+ if data:
431
+ payload.update(data)
432
+
433
+ return await self.record_chat(
434
+ "tool",
435
+ text,
436
+ tags=tool_tags,
437
+ data=payload,
438
+ severity=severity,
439
+ signal=signal,
440
+ )