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.
- aethergraph/__init__.py +4 -10
- aethergraph/__main__.py +293 -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 +190 -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.0a2.dist-info}/METADATA +138 -31
- aethergraph-0.1.0a2.dist-info/RECORD +356 -0
- aethergraph-0.1.0a2.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.0a2.dist-info}/WHEEL +0 -0
- {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a2.dist-info}/licenses/LICENSE +0 -0
- {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a2.dist-info}/licenses/NOTICE +0 -0
- {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
import json
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
6
|
+
|
|
7
|
+
from .utils import short_hash, slug
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from aethergraph.contracts.services.memory import Event
|
|
11
|
+
|
|
12
|
+
from .types import MemoryFacadeInterface
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RAGMixin:
|
|
16
|
+
"""Methods for interacting with RAG services."""
|
|
17
|
+
|
|
18
|
+
# ----------- RAG: DX helpers (key-based) -----------
|
|
19
|
+
async def rag_remember_events(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
key: str = "default",
|
|
23
|
+
where: dict | None = None,
|
|
24
|
+
policy: dict | None = None,
|
|
25
|
+
) -> dict:
|
|
26
|
+
"""
|
|
27
|
+
Bind a RAG corpus by logical key and promote events into it.
|
|
28
|
+
|
|
29
|
+
This method allows you to associate a logical key with a RAG corpus and
|
|
30
|
+
promote events into it based on filtering criteria.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
Promote events into a session corpus:
|
|
34
|
+
```python
|
|
35
|
+
await context.memory().rag_remember_events(
|
|
36
|
+
key="session",
|
|
37
|
+
where={"kinds": ["tool_result"], "limit": 200},
|
|
38
|
+
policy={"min_signal": 0.25},
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
key: Logical key for the RAG corpus. Defaults to `"default"`.
|
|
44
|
+
where: Filtering criteria for selecting events.
|
|
45
|
+
policy: Promotion policy, such as minimum signal threshold.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
dict: A dictionary containing the promotion results.
|
|
49
|
+
"""
|
|
50
|
+
corpus_id = await self.rag_bind(key=key, create_if_missing=True)
|
|
51
|
+
return await self.rag_promote_events(
|
|
52
|
+
corpus_id=corpus_id,
|
|
53
|
+
events=None,
|
|
54
|
+
where=where,
|
|
55
|
+
policy=policy,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
async def rag_remember_docs(
|
|
59
|
+
self,
|
|
60
|
+
docs: Sequence[dict[str, Any]],
|
|
61
|
+
*,
|
|
62
|
+
key: str = "default",
|
|
63
|
+
labels: dict | None = None,
|
|
64
|
+
) -> dict[str, Any]:
|
|
65
|
+
"""
|
|
66
|
+
Bind a RAG corpus by key and upsert documents into it.
|
|
67
|
+
|
|
68
|
+
This method allows you to associate a logical key with a RAG corpus and
|
|
69
|
+
upsert a sequence of documents into it.
|
|
70
|
+
|
|
71
|
+
Examples:
|
|
72
|
+
Upsert documents into a corpus:
|
|
73
|
+
```python
|
|
74
|
+
await context.memory().rag_remember_docs(
|
|
75
|
+
docs=[{"text": "Document 1"}, {"text": "Document 2"}],
|
|
76
|
+
key="knowledge_base",
|
|
77
|
+
labels={"category": "reference"},
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
docs: A sequence of documents to upsert.
|
|
83
|
+
key: Logical key for the RAG corpus. Defaults to `"default"`.
|
|
84
|
+
labels: Metadata labels to associate with the corpus.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
dict: A dictionary containing the upsert results.
|
|
88
|
+
"""
|
|
89
|
+
corpus_id = await self.rag_bind(key=key, create_if_missing=True, labels=labels)
|
|
90
|
+
return await self.rag_upsert(corpus_id=corpus_id, docs=list(docs))
|
|
91
|
+
|
|
92
|
+
async def rag_search_by_key(
|
|
93
|
+
self,
|
|
94
|
+
*,
|
|
95
|
+
key: str = "default",
|
|
96
|
+
query: str,
|
|
97
|
+
k: int = 8,
|
|
98
|
+
filters: dict | None = None,
|
|
99
|
+
mode: Literal["hybrid", "dense"] = "hybrid",
|
|
100
|
+
) -> list[dict]:
|
|
101
|
+
"""
|
|
102
|
+
Resolve a corpus by logical key and perform a search query.
|
|
103
|
+
|
|
104
|
+
This method allows you to search within a RAG corpus identified by a
|
|
105
|
+
logical key.
|
|
106
|
+
|
|
107
|
+
Examples:
|
|
108
|
+
Perform a search query:
|
|
109
|
+
```python
|
|
110
|
+
results = await context.memory().rag_search_by_key(
|
|
111
|
+
key="knowledge_base",
|
|
112
|
+
query="What is the capital of France?",
|
|
113
|
+
k=5,
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
key: Logical key for the RAG corpus. Defaults to `"default"`.
|
|
119
|
+
query: The search query string.
|
|
120
|
+
k: Number of top results to return. Defaults to 8.
|
|
121
|
+
filters: Additional filters for the search.
|
|
122
|
+
mode: Search mode, either `"hybrid"` or `"dense"`. Defaults to `"hybrid"`.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
list[dict]: A list of search results.
|
|
126
|
+
"""
|
|
127
|
+
corpus_id = await self.rag_bind(key=key, create_if_missing=False)
|
|
128
|
+
return await self.rag_search(
|
|
129
|
+
corpus_id=corpus_id,
|
|
130
|
+
query=query,
|
|
131
|
+
k=k,
|
|
132
|
+
filters=filters,
|
|
133
|
+
mode=mode,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
async def rag_answer_by_key(
|
|
137
|
+
self,
|
|
138
|
+
*,
|
|
139
|
+
key: str = "default",
|
|
140
|
+
question: str,
|
|
141
|
+
style: Literal["concise", "detailed"] = "concise",
|
|
142
|
+
with_citations: bool = True,
|
|
143
|
+
k: int = 6,
|
|
144
|
+
) -> dict:
|
|
145
|
+
"""
|
|
146
|
+
Perform RAG QA over a corpus referenced by a logical key.
|
|
147
|
+
|
|
148
|
+
This method allows you to ask a question and retrieve an answer from a
|
|
149
|
+
RAG corpus identified by a logical key.
|
|
150
|
+
|
|
151
|
+
Examples:
|
|
152
|
+
Ask a question:
|
|
153
|
+
```python
|
|
154
|
+
answer = await context.memory().rag_answer_by_key(
|
|
155
|
+
key="knowledge_base",
|
|
156
|
+
question="What is the capital of France?",
|
|
157
|
+
style="detailed",
|
|
158
|
+
)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
key: Logical key for the RAG corpus. Defaults to `"default"`.
|
|
163
|
+
question: The question to ask.
|
|
164
|
+
style: Answer style, either `"concise"` or `"detailed"`. Defaults to `"concise"`.
|
|
165
|
+
with_citations: Whether to include citations in the answer. Defaults to `True`.
|
|
166
|
+
k: Number of top results to consider. Defaults to 6.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
dict: A dictionary containing the answer and related metadata.
|
|
170
|
+
"""
|
|
171
|
+
corpus_id = await self.rag_bind(key=key, create_if_missing=False)
|
|
172
|
+
return await self.rag_answer(
|
|
173
|
+
corpus_id=corpus_id,
|
|
174
|
+
question=question,
|
|
175
|
+
style=style,
|
|
176
|
+
with_citations=with_citations,
|
|
177
|
+
k=k,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
async def rag_upsert(
|
|
181
|
+
self: MemoryFacadeInterface,
|
|
182
|
+
*,
|
|
183
|
+
corpus_id: str,
|
|
184
|
+
docs: Sequence[dict[str, Any]],
|
|
185
|
+
topic: str | None = None,
|
|
186
|
+
) -> dict[str, Any]:
|
|
187
|
+
"""
|
|
188
|
+
Upsert documents into a RAG corpus.
|
|
189
|
+
|
|
190
|
+
This method allows you to add or update documents in a RAG corpus.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
corpus_id: The ID of the RAG corpus.
|
|
194
|
+
docs: A sequence of documents to upsert.
|
|
195
|
+
topic: Optional topic for the documents.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
dict: A dictionary containing the upsert results.
|
|
199
|
+
"""
|
|
200
|
+
if not self.rag:
|
|
201
|
+
raise RuntimeError("RAG facade not configured")
|
|
202
|
+
return await self.rag.upsert_docs(corpus_id=corpus_id, docs=list(docs))
|
|
203
|
+
|
|
204
|
+
async def rag_bind(
|
|
205
|
+
self: MemoryFacadeInterface,
|
|
206
|
+
*,
|
|
207
|
+
corpus_id: str | None = None,
|
|
208
|
+
key: str | None = None,
|
|
209
|
+
create_if_missing: bool = True,
|
|
210
|
+
labels: dict | None = None,
|
|
211
|
+
) -> str:
|
|
212
|
+
"""
|
|
213
|
+
Bind a logical key to a RAG corpus.
|
|
214
|
+
|
|
215
|
+
This method resolves or creates a RAG corpus based on a logical key.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
corpus_id: Optional explicit corpus ID.
|
|
219
|
+
key: Logical key for the RAG corpus.
|
|
220
|
+
create_if_missing: Whether to create the corpus if it doesn't exist.
|
|
221
|
+
labels: Metadata labels to associate with the corpus.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
str: The resolved or created corpus ID.
|
|
225
|
+
"""
|
|
226
|
+
if not self.rag:
|
|
227
|
+
raise RuntimeError("RAG facade not configured")
|
|
228
|
+
|
|
229
|
+
mem_scope = self.memory_scope_id
|
|
230
|
+
if corpus_id:
|
|
231
|
+
cid = corpus_id
|
|
232
|
+
else:
|
|
233
|
+
logical_key = key or "default"
|
|
234
|
+
base = f"{mem_scope}:{logical_key}"
|
|
235
|
+
cid = f"mem:{slug(mem_scope)}:{slug(logical_key)}-{short_hash(base, 8)}"
|
|
236
|
+
|
|
237
|
+
scope_labels = {}
|
|
238
|
+
if self.scope:
|
|
239
|
+
scope_labels = self.scope.rag_labels(scope_id=mem_scope)
|
|
240
|
+
|
|
241
|
+
meta = {"scope": scope_labels, **(labels or {})}
|
|
242
|
+
if create_if_missing:
|
|
243
|
+
await self.rag.add_corpus(cid, meta=meta, scope_labels=scope_labels)
|
|
244
|
+
return cid
|
|
245
|
+
|
|
246
|
+
async def rag_promote_events(
|
|
247
|
+
self: MemoryFacadeInterface,
|
|
248
|
+
*,
|
|
249
|
+
corpus_id: str,
|
|
250
|
+
events: list[Event] | None = None,
|
|
251
|
+
where: dict | None = None,
|
|
252
|
+
policy: dict | None = None,
|
|
253
|
+
) -> dict:
|
|
254
|
+
"""
|
|
255
|
+
Promote events into a RAG corpus.
|
|
256
|
+
|
|
257
|
+
This method selects and promotes events into a RAG corpus based on
|
|
258
|
+
filtering criteria and policies.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
corpus_id: The ID of the RAG corpus.
|
|
262
|
+
events: Optional list of events to promote.
|
|
263
|
+
where: Filtering criteria for selecting events.
|
|
264
|
+
policy: Promotion policy, such as minimum signal threshold.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
dict: A dictionary containing the promotion results.
|
|
268
|
+
"""
|
|
269
|
+
if not self.rag:
|
|
270
|
+
raise RuntimeError("RAG facade not configured")
|
|
271
|
+
policy = policy or {}
|
|
272
|
+
min_signal = policy.get("min_signal", self.default_signal_threshold)
|
|
273
|
+
|
|
274
|
+
if events is None:
|
|
275
|
+
# We use RetrievalMixin's .recent here
|
|
276
|
+
kinds = (where or {}).get("kinds")
|
|
277
|
+
limit = int((where or {}).get("limit", 200))
|
|
278
|
+
recent = await self.recent(kinds=kinds, limit=limit) # type: ignore
|
|
279
|
+
events = [e for e in recent if (getattr(e, "signal", 0.0) or 0.0) >= float(min_signal)]
|
|
280
|
+
|
|
281
|
+
docs: list[dict] = []
|
|
282
|
+
for e in events:
|
|
283
|
+
title = f"{e.kind}:{(e.tool or e.stage or 'n/a')}:{e.ts}"
|
|
284
|
+
scope_labels = (
|
|
285
|
+
self.scope.rag_labels(scope_id=self.memory_scope_id) if self.scope else {}
|
|
286
|
+
)
|
|
287
|
+
labels = {
|
|
288
|
+
**scope_labels,
|
|
289
|
+
"kind": e.kind,
|
|
290
|
+
"tool": e.tool,
|
|
291
|
+
"stage": e.stage,
|
|
292
|
+
"severity": e.severity,
|
|
293
|
+
"tags": list(e.tags or []),
|
|
294
|
+
}
|
|
295
|
+
body = e.text
|
|
296
|
+
if not body:
|
|
297
|
+
body = json.dumps(
|
|
298
|
+
{"inputs": e.inputs, "outputs": e.outputs, "metrics": e.metrics},
|
|
299
|
+
ensure_ascii=False,
|
|
300
|
+
)
|
|
301
|
+
docs.append({"text": body, "title": title, "labels": labels})
|
|
302
|
+
|
|
303
|
+
if not docs:
|
|
304
|
+
return {"added": 0}
|
|
305
|
+
|
|
306
|
+
stats = await self.rag.upsert_docs(corpus_id=corpus_id, docs=docs)
|
|
307
|
+
|
|
308
|
+
# Log result
|
|
309
|
+
await self.write_result(
|
|
310
|
+
tool=f"rag.promote.{corpus_id}",
|
|
311
|
+
outputs=[{"name": "added_docs", "kind": "number", "value": stats.get("added", 0)}],
|
|
312
|
+
tags=["rag", "ingest"],
|
|
313
|
+
message=f"Promoted {stats.get('added', 0)} events",
|
|
314
|
+
severity=2,
|
|
315
|
+
)
|
|
316
|
+
return stats
|
|
317
|
+
|
|
318
|
+
async def rag_answer(
|
|
319
|
+
self: MemoryFacadeInterface,
|
|
320
|
+
*,
|
|
321
|
+
corpus_id: str,
|
|
322
|
+
question: str,
|
|
323
|
+
style: Literal["concise", "detailed"] = "concise",
|
|
324
|
+
with_citations: bool = True,
|
|
325
|
+
k: int = 6,
|
|
326
|
+
) -> dict:
|
|
327
|
+
"""
|
|
328
|
+
Answer a question using a RAG corpus.
|
|
329
|
+
|
|
330
|
+
This method performs question answering over a specified RAG corpus.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
corpus_id: The ID of the RAG corpus.
|
|
334
|
+
question: The question to ask.
|
|
335
|
+
style: Answer style, either `"concise"` or `"detailed"`. Defaults to `"concise"`.
|
|
336
|
+
with_citations: Whether to include citations in the answer. Defaults to `True`.
|
|
337
|
+
k: Number of top results to consider. Defaults to 6.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
dict: A dictionary containing the answer and related metadata.
|
|
341
|
+
"""
|
|
342
|
+
if not self.rag:
|
|
343
|
+
raise RuntimeError("RAG facade not configured")
|
|
344
|
+
|
|
345
|
+
ans = await self.rag.answer(
|
|
346
|
+
corpus_id=corpus_id,
|
|
347
|
+
question=question,
|
|
348
|
+
llm=self.llm,
|
|
349
|
+
style=style,
|
|
350
|
+
with_citations=with_citations,
|
|
351
|
+
k=k,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
outs = [{"name": "answer", "kind": "text", "value": ans.get("answer", "")}]
|
|
355
|
+
for i, rc in enumerate(ans.get("resolved_citations", []), start=1):
|
|
356
|
+
outs.append({"name": f"cite_{i}", "kind": "json", "value": rc})
|
|
357
|
+
|
|
358
|
+
await self.write_result(
|
|
359
|
+
tool=f"rag.answer.{corpus_id}",
|
|
360
|
+
outputs=outs,
|
|
361
|
+
tags=["rag", "qa"],
|
|
362
|
+
message=f"Q: {question}",
|
|
363
|
+
metrics=ans.get("usage", {}),
|
|
364
|
+
severity=2,
|
|
365
|
+
)
|
|
366
|
+
return ans
|
|
367
|
+
|
|
368
|
+
async def rag_search(
|
|
369
|
+
self,
|
|
370
|
+
*,
|
|
371
|
+
corpus_id: str,
|
|
372
|
+
query: str,
|
|
373
|
+
k: int = 8,
|
|
374
|
+
filters: dict | None = None,
|
|
375
|
+
mode: Literal["hybrid", "dense"] = "hybrid",
|
|
376
|
+
) -> list[dict]:
|
|
377
|
+
"""
|
|
378
|
+
Perform a search query over a RAG corpus.
|
|
379
|
+
|
|
380
|
+
This method allows you to search within a specified RAG corpus.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
corpus_id: The ID of the RAG corpus.
|
|
384
|
+
query: The search query string.
|
|
385
|
+
k: Number of top results to return. Defaults to 8.
|
|
386
|
+
filters: Additional filters for the search.
|
|
387
|
+
mode: Search mode, either `"hybrid"` or `"dense"`. Defaults to `"hybrid"`.
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
list[dict]: A list of search results.
|
|
391
|
+
"""
|
|
392
|
+
if not self.rag:
|
|
393
|
+
raise RuntimeError("RAG facade not configured in MemoryFacade")
|
|
394
|
+
|
|
395
|
+
scope = self.scope
|
|
396
|
+
s_filters = scope.rag_filter(scope_id=self.memory_scope_id) if scope else {}
|
|
397
|
+
if filters:
|
|
398
|
+
s_filters.update(filters)
|
|
399
|
+
hits = await self.rag.search(corpus_id, query, k=k, filters=s_filters, mode=mode)
|
|
400
|
+
return [
|
|
401
|
+
dict(
|
|
402
|
+
chunk_id=h.chunk_id,
|
|
403
|
+
doc_id=h.doc_id,
|
|
404
|
+
corpus_id=h.corpus_id,
|
|
405
|
+
score=h.score,
|
|
406
|
+
text=h.text,
|
|
407
|
+
meta=h.meta,
|
|
408
|
+
)
|
|
409
|
+
for h in hits
|
|
410
|
+
]
|