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.
- aethergraph/__init__.py +4 -10
- aethergraph/__main__.py +296 -0
- aethergraph/api/v1/__init__.py +0 -0
- aethergraph/api/v1/agents.py +46 -0
- aethergraph/api/v1/apps.py +70 -0
- aethergraph/api/v1/artifacts.py +415 -0
- aethergraph/api/v1/channels.py +89 -0
- aethergraph/api/v1/deps.py +168 -0
- aethergraph/api/v1/graphs.py +259 -0
- aethergraph/api/v1/identity.py +25 -0
- aethergraph/api/v1/memory.py +353 -0
- aethergraph/api/v1/misc.py +47 -0
- aethergraph/api/v1/pagination.py +29 -0
- aethergraph/api/v1/runs.py +568 -0
- aethergraph/api/v1/schemas.py +535 -0
- aethergraph/api/v1/session.py +323 -0
- aethergraph/api/v1/stats.py +201 -0
- aethergraph/api/v1/viz.py +152 -0
- aethergraph/config/config.py +22 -0
- aethergraph/config/loader.py +3 -2
- aethergraph/config/storage.py +209 -0
- aethergraph/contracts/__init__.py +0 -0
- aethergraph/contracts/services/__init__.py +0 -0
- aethergraph/contracts/services/artifacts.py +27 -14
- aethergraph/contracts/services/memory.py +45 -17
- aethergraph/contracts/services/metering.py +129 -0
- aethergraph/contracts/services/runs.py +50 -0
- aethergraph/contracts/services/sessions.py +87 -0
- aethergraph/contracts/services/state_stores.py +3 -0
- aethergraph/contracts/services/viz.py +44 -0
- aethergraph/contracts/storage/artifact_index.py +88 -0
- aethergraph/contracts/storage/artifact_store.py +99 -0
- aethergraph/contracts/storage/async_kv.py +34 -0
- aethergraph/contracts/storage/blob_store.py +50 -0
- aethergraph/contracts/storage/doc_store.py +35 -0
- aethergraph/contracts/storage/event_log.py +31 -0
- aethergraph/contracts/storage/vector_index.py +48 -0
- aethergraph/core/__init__.py +0 -0
- aethergraph/core/execution/forward_scheduler.py +13 -2
- aethergraph/core/execution/global_scheduler.py +21 -15
- aethergraph/core/execution/step_forward.py +10 -1
- aethergraph/core/graph/__init__.py +0 -0
- aethergraph/core/graph/graph_builder.py +8 -4
- aethergraph/core/graph/graph_fn.py +156 -15
- aethergraph/core/graph/graph_spec.py +8 -0
- aethergraph/core/graph/graphify.py +146 -27
- aethergraph/core/graph/node_spec.py +0 -2
- aethergraph/core/graph/node_state.py +3 -0
- aethergraph/core/graph/task_graph.py +39 -1
- aethergraph/core/runtime/__init__.py +0 -0
- aethergraph/core/runtime/ad_hoc_context.py +64 -4
- aethergraph/core/runtime/base_service.py +28 -4
- aethergraph/core/runtime/execution_context.py +13 -15
- aethergraph/core/runtime/graph_runner.py +222 -37
- aethergraph/core/runtime/node_context.py +510 -6
- aethergraph/core/runtime/node_services.py +12 -5
- aethergraph/core/runtime/recovery.py +15 -1
- aethergraph/core/runtime/run_manager.py +783 -0
- aethergraph/core/runtime/run_manager_local.py +204 -0
- aethergraph/core/runtime/run_registration.py +2 -2
- aethergraph/core/runtime/run_types.py +89 -0
- aethergraph/core/runtime/runtime_env.py +136 -7
- aethergraph/core/runtime/runtime_metering.py +71 -0
- aethergraph/core/runtime/runtime_registry.py +36 -13
- aethergraph/core/runtime/runtime_services.py +194 -6
- aethergraph/core/tools/builtins/toolset.py +1 -1
- aethergraph/core/tools/toolkit.py +5 -0
- aethergraph/plugins/agents/default_chat_agent copy.py +90 -0
- aethergraph/plugins/agents/default_chat_agent.py +171 -0
- aethergraph/plugins/agents/shared.py +81 -0
- aethergraph/plugins/channel/adapters/webui.py +112 -112
- aethergraph/plugins/channel/routes/webui_routes.py +367 -102
- aethergraph/plugins/channel/utils/slack_utils.py +115 -59
- aethergraph/plugins/channel/utils/telegram_utils.py +88 -47
- aethergraph/plugins/channel/websockets/weibui_ws.py +172 -0
- aethergraph/runtime/__init__.py +15 -0
- aethergraph/server/app_factory.py +196 -34
- aethergraph/server/clients/channel_client.py +202 -0
- aethergraph/server/http/channel_http_routes.py +116 -0
- aethergraph/server/http/channel_ws_routers.py +45 -0
- aethergraph/server/loading.py +117 -0
- aethergraph/server/server.py +131 -0
- aethergraph/server/server_state.py +240 -0
- aethergraph/server/start.py +227 -66
- aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- aethergraph/server/ui_static/assets/index-BR5GtXcZ.css +1 -0
- aethergraph/server/ui_static/assets/index-CQ0HZZ83.js +400 -0
- aethergraph/server/ui_static/index.html +15 -0
- aethergraph/server/ui_static/logo.png +0 -0
- aethergraph/services/artifacts/__init__.py +0 -0
- aethergraph/services/artifacts/facade.py +1239 -132
- aethergraph/services/auth/{dev.py → authn.py} +0 -8
- aethergraph/services/auth/authz.py +100 -0
- aethergraph/services/channel/__init__.py +0 -0
- aethergraph/services/channel/channel_bus.py +19 -1
- aethergraph/services/channel/factory.py +13 -1
- aethergraph/services/channel/ingress.py +311 -0
- aethergraph/services/channel/queue_adapter.py +75 -0
- aethergraph/services/channel/session.py +502 -19
- aethergraph/services/container/default_container.py +122 -43
- aethergraph/services/continuations/continuation.py +6 -0
- aethergraph/services/continuations/stores/fs_store.py +19 -0
- aethergraph/services/eventhub/event_hub.py +76 -0
- aethergraph/services/kv/__init__.py +0 -0
- aethergraph/services/kv/ephemeral.py +244 -0
- aethergraph/services/llm/__init__.py +0 -0
- aethergraph/services/llm/generic_client copy.py +691 -0
- aethergraph/services/llm/generic_client.py +1288 -187
- aethergraph/services/llm/providers.py +3 -1
- aethergraph/services/llm/types.py +47 -0
- aethergraph/services/llm/utils.py +284 -0
- aethergraph/services/logger/std.py +3 -0
- aethergraph/services/mcp/__init__.py +9 -0
- aethergraph/services/mcp/http_client.py +38 -0
- aethergraph/services/mcp/service.py +225 -1
- aethergraph/services/mcp/stdio_client.py +41 -6
- aethergraph/services/mcp/ws_client.py +44 -2
- aethergraph/services/memory/__init__.py +0 -0
- aethergraph/services/memory/distillers/llm_long_term.py +234 -0
- aethergraph/services/memory/distillers/llm_meta_summary.py +398 -0
- aethergraph/services/memory/distillers/long_term.py +225 -0
- aethergraph/services/memory/facade/__init__.py +3 -0
- aethergraph/services/memory/facade/chat.py +440 -0
- aethergraph/services/memory/facade/core.py +447 -0
- aethergraph/services/memory/facade/distillation.py +424 -0
- aethergraph/services/memory/facade/rag.py +410 -0
- aethergraph/services/memory/facade/results.py +315 -0
- aethergraph/services/memory/facade/retrieval.py +139 -0
- aethergraph/services/memory/facade/types.py +77 -0
- aethergraph/services/memory/facade/utils.py +43 -0
- aethergraph/services/memory/facade_dep.py +1539 -0
- aethergraph/services/memory/factory.py +9 -3
- aethergraph/services/memory/utils.py +10 -0
- aethergraph/services/metering/eventlog_metering.py +470 -0
- aethergraph/services/metering/noop.py +25 -4
- aethergraph/services/rag/__init__.py +0 -0
- aethergraph/services/rag/facade.py +279 -23
- aethergraph/services/rag/index_factory.py +2 -2
- aethergraph/services/rag/node_rag.py +317 -0
- aethergraph/services/rate_limit/inmem_rate_limit.py +24 -0
- aethergraph/services/registry/__init__.py +0 -0
- aethergraph/services/registry/agent_app_meta.py +419 -0
- aethergraph/services/registry/registry_key.py +1 -1
- aethergraph/services/registry/unified_registry.py +74 -6
- aethergraph/services/scope/scope.py +159 -0
- aethergraph/services/scope/scope_factory.py +164 -0
- aethergraph/services/state_stores/serialize.py +5 -0
- aethergraph/services/state_stores/utils.py +2 -1
- aethergraph/services/viz/__init__.py +0 -0
- aethergraph/services/viz/facade.py +413 -0
- aethergraph/services/viz/viz_service.py +69 -0
- aethergraph/storage/artifacts/artifact_index_jsonl.py +180 -0
- aethergraph/storage/artifacts/artifact_index_sqlite.py +426 -0
- aethergraph/storage/artifacts/cas_store.py +422 -0
- aethergraph/storage/artifacts/fs_cas.py +18 -0
- aethergraph/storage/artifacts/s3_cas.py +14 -0
- aethergraph/storage/artifacts/utils.py +124 -0
- aethergraph/storage/blob/fs_blob.py +86 -0
- aethergraph/storage/blob/s3_blob.py +115 -0
- aethergraph/storage/continuation_store/fs_cont.py +283 -0
- aethergraph/storage/continuation_store/inmem_cont.py +146 -0
- aethergraph/storage/continuation_store/kvdoc_cont.py +261 -0
- aethergraph/storage/docstore/fs_doc.py +63 -0
- aethergraph/storage/docstore/sqlite_doc.py +31 -0
- aethergraph/storage/docstore/sqlite_doc_sync.py +90 -0
- aethergraph/storage/eventlog/fs_event.py +136 -0
- aethergraph/storage/eventlog/sqlite_event.py +47 -0
- aethergraph/storage/eventlog/sqlite_event_sync.py +178 -0
- aethergraph/storage/factory.py +432 -0
- aethergraph/storage/fs_utils.py +28 -0
- aethergraph/storage/graph_state_store/state_store.py +64 -0
- aethergraph/storage/kv/inmem_kv.py +103 -0
- aethergraph/storage/kv/layered_kv.py +52 -0
- aethergraph/storage/kv/sqlite_kv.py +39 -0
- aethergraph/storage/kv/sqlite_kv_sync.py +98 -0
- aethergraph/storage/memory/event_persist.py +68 -0
- aethergraph/storage/memory/fs_persist.py +118 -0
- aethergraph/{services/memory/hotlog_kv.py → storage/memory/hotlog.py} +8 -2
- aethergraph/{services → storage}/memory/indices.py +31 -7
- aethergraph/storage/metering/meter_event.py +55 -0
- aethergraph/storage/runs/doc_store.py +280 -0
- aethergraph/storage/runs/inmen_store.py +82 -0
- aethergraph/storage/runs/sqlite_run_store.py +403 -0
- aethergraph/storage/sessions/doc_store.py +183 -0
- aethergraph/storage/sessions/inmem_store.py +110 -0
- aethergraph/storage/sessions/sqlite_session_store.py +399 -0
- aethergraph/storage/vector_index/chroma_index.py +138 -0
- aethergraph/storage/vector_index/faiss_index.py +179 -0
- aethergraph/storage/vector_index/sqlite_index.py +187 -0
- {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/METADATA +138 -31
- aethergraph-0.1.0a3.dist-info/RECORD +356 -0
- aethergraph-0.1.0a3.dist-info/entry_points.txt +3 -0
- aethergraph/services/artifacts/factory.py +0 -35
- aethergraph/services/artifacts/fs_store.py +0 -656
- aethergraph/services/artifacts/jsonl_index.py +0 -123
- aethergraph/services/artifacts/sqlite_index.py +0 -209
- aethergraph/services/memory/distillers/episode.py +0 -116
- aethergraph/services/memory/distillers/rolling.py +0 -74
- aethergraph/services/memory/facade.py +0 -633
- aethergraph/services/memory/persist_fs.py +0 -40
- aethergraph/services/rag/index/base.py +0 -27
- aethergraph/services/rag/index/faiss_index.py +0 -121
- aethergraph/services/rag/index/sqlite_index.py +0 -134
- aethergraph-0.1.0a1.dist-info/RECORD +0 -182
- aethergraph-0.1.0a1.dist-info/entry_points.txt +0 -2
- {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/WHEEL +0 -0
- {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/licenses/LICENSE +0 -0
- {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/licenses/NOTICE +0 -0
- {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
|
+
)
|