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
|
@@ -23,10 +23,34 @@ __all__ = [
|
|
|
23
23
|
|
|
24
24
|
class _ServiceHandle:
|
|
25
25
|
"""
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
This class provides ergonomic access to a service instance, allowing attribute access,
|
|
27
|
+
direct retrieval, and forwarding of calls if the underlying service is callable.
|
|
28
|
+
Examples:
|
|
29
|
+
Accessing service attributes:
|
|
30
|
+
```python
|
|
31
|
+
handle = _ServiceHandle("my_service", service_instance)
|
|
32
|
+
value = handle.some_attribute
|
|
33
|
+
```
|
|
34
|
+
Retrieving the service instance:
|
|
35
|
+
```python
|
|
36
|
+
svc = handle()
|
|
37
|
+
```
|
|
38
|
+
Forwarding calls to a callable service:
|
|
39
|
+
```python
|
|
40
|
+
result = handle(arg1, arg2)
|
|
41
|
+
```
|
|
42
|
+
Args:
|
|
43
|
+
name: The name of the service for identification and error reporting.
|
|
44
|
+
bound_service: The actual service instance to be wrapped.
|
|
45
|
+
Attribute Access:
|
|
46
|
+
All attribute lookups are delegated to the underlying service instance.
|
|
47
|
+
Calling:
|
|
48
|
+
- If called with no arguments, returns the underlying service instance.
|
|
49
|
+
- If called with arguments and the service is callable, forwards the call.
|
|
50
|
+
- Raises TypeError if the service is not callable and called with arguments.
|
|
51
|
+
Returns:
|
|
52
|
+
The result of the underlying service's __call__ method, or the service instance itself
|
|
53
|
+
if called with no arguments.
|
|
30
54
|
"""
|
|
31
55
|
|
|
32
56
|
__slots__ = ("_name", "_svc")
|
|
@@ -7,6 +7,9 @@ from datetime import datetime
|
|
|
7
7
|
import importlib
|
|
8
8
|
from typing import TYPE_CHECKING, Any
|
|
9
9
|
|
|
10
|
+
from aethergraph.api.v1.deps import RequestIdentity
|
|
11
|
+
from aethergraph.services.scope.scope import Scope
|
|
12
|
+
|
|
10
13
|
if TYPE_CHECKING:
|
|
11
14
|
from aethergraph.core.graph.task_node import TaskNodeRuntime
|
|
12
15
|
|
|
@@ -24,6 +27,10 @@ from .node_services import NodeServices
|
|
|
24
27
|
class ExecutionContext:
|
|
25
28
|
run_id: str
|
|
26
29
|
graph_id: str | None
|
|
30
|
+
session_id: str | None
|
|
31
|
+
agent_id: str | None
|
|
32
|
+
app_id: str | None
|
|
33
|
+
identity: RequestIdentity | None
|
|
27
34
|
graph_inputs: dict[str, Any]
|
|
28
35
|
outputs_by_node: dict[str, dict[str, Any]]
|
|
29
36
|
services: NodeServices
|
|
@@ -32,7 +39,7 @@ class ExecutionContext:
|
|
|
32
39
|
resume_payload: dict[str, Any] | None = None
|
|
33
40
|
should_run_fn: Callable[[], bool] | None = None
|
|
34
41
|
resume_router: ResumeRouter | None = None # ResumeRouter
|
|
35
|
-
|
|
42
|
+
scope: Scope | None = None # Node Scope
|
|
36
43
|
# Back-compat shim
|
|
37
44
|
bound_memory: BoundMemoryAdapter | None = None
|
|
38
45
|
|
|
@@ -40,27 +47,18 @@ class ExecutionContext:
|
|
|
40
47
|
return NodeContext(
|
|
41
48
|
run_id=self.run_id,
|
|
42
49
|
graph_id=self.graph_id or "",
|
|
50
|
+
session_id=self.session_id,
|
|
43
51
|
node_id=node.node_id,
|
|
44
52
|
services=self.services,
|
|
53
|
+
identity=self.identity,
|
|
45
54
|
resume_payload=self.resume_payload,
|
|
55
|
+
scope=self.scope,
|
|
56
|
+
agent_id=self.agent_id,
|
|
57
|
+
app_id=self.app_id,
|
|
46
58
|
# back-compat for old ctx.mem()
|
|
47
59
|
bound_memory=self.bound_memory,
|
|
48
60
|
)
|
|
49
61
|
|
|
50
|
-
# def as_node_context(self, ad) -> "NodeContext":
|
|
51
|
-
# """ Create a NodeContext representing this execution context itself as a node.
|
|
52
|
-
# Useful for ad-hoc contexts that don't have real nodes.
|
|
53
|
-
# """
|
|
54
|
-
# return NodeContext(
|
|
55
|
-
# run_id=self.run_id,
|
|
56
|
-
# graph_id=self.graph_id or "",
|
|
57
|
-
# node_id="ad_",
|
|
58
|
-
# services=self.services,
|
|
59
|
-
# resume_payload=self.resume_payload,
|
|
60
|
-
# # back-compat for old ctx.mem()
|
|
61
|
-
# bound_memory=self.bound_memory,
|
|
62
|
-
# )
|
|
63
|
-
|
|
64
62
|
# ----- helpers used by step forward() -----
|
|
65
63
|
def now(self) -> datetime:
|
|
66
64
|
return self.clock.now()
|
|
@@ -5,10 +5,12 @@ import threading
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
import uuid
|
|
7
7
|
|
|
8
|
+
from aethergraph.api.v1.deps import RequestIdentity
|
|
8
9
|
from aethergraph.contracts.errors.errors import GraphHasPendingWaits
|
|
9
10
|
from aethergraph.contracts.services.state_stores import GraphSnapshot
|
|
11
|
+
from aethergraph.core.graph.task_graph import TaskGraph
|
|
10
12
|
from aethergraph.core.runtime.recovery import hash_spec, recover_graph_run
|
|
11
|
-
from aethergraph.services.container.default_container import build_default_container
|
|
13
|
+
from aethergraph.services.container.default_container import build_default_container # adjust path
|
|
12
14
|
from aethergraph.services.state_stores.graph_observer import PersistenceObserver
|
|
13
15
|
from aethergraph.services.state_stores.resume_policy import (
|
|
14
16
|
assert_snapshot_json_only,
|
|
@@ -20,6 +22,7 @@ from ..execution.retry_policy import RetryPolicy
|
|
|
20
22
|
from ..graph.graph_fn import GraphFunction
|
|
21
23
|
from ..graph.graph_refs import resolve_any as _resolve_any
|
|
22
24
|
from ..runtime.runtime_env import RuntimeEnv
|
|
25
|
+
from ..runtime.runtime_metering import current_meter_context
|
|
23
26
|
from ..runtime.runtime_services import ensure_services_installed
|
|
24
27
|
from .run_registration import RunRegistrationGuard
|
|
25
28
|
|
|
@@ -51,7 +54,7 @@ async def _attach_persistence(graph, env, spec, snapshot_every=1) -> Persistence
|
|
|
51
54
|
|
|
52
55
|
|
|
53
56
|
async def _build_env(
|
|
54
|
-
owner, inputs: dict[str, Any], **rt_overrides
|
|
57
|
+
owner, inputs: dict[str, Any], identity: RequestIdentity | None = None, **rt_overrides
|
|
55
58
|
) -> tuple[RuntimeEnv, RetryPolicy, int]:
|
|
56
59
|
container = _get_container()
|
|
57
60
|
# apply optional overrides onto the container instance
|
|
@@ -60,12 +63,36 @@ async def _build_env(
|
|
|
60
63
|
setattr(container, k, v)
|
|
61
64
|
|
|
62
65
|
run_id = rt_overrides.get("run_id") or f"run-{uuid.uuid4().hex[:8]}"
|
|
66
|
+
session_id = rt_overrides.get("session_id")
|
|
67
|
+
graph_id = getattr(owner, "graph_id", None) or getattr(owner, "name", None)
|
|
68
|
+
|
|
69
|
+
# Prefer runtime overrides (from RunRecord) over static graph metadata -- UI provenance
|
|
70
|
+
agent_id = (
|
|
71
|
+
rt_overrides.get("agent_id")
|
|
72
|
+
or getattr(owner, "agent_id", None)
|
|
73
|
+
or getattr(getattr(owner, "spec", None) or {}, "agent_id", None)
|
|
74
|
+
or (getattr(owner, "spec", None) or {}).get("agent_id") # for dict-like spec
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
app_id = (
|
|
78
|
+
rt_overrides.get("app_id")
|
|
79
|
+
or getattr(owner, "app_id", None)
|
|
80
|
+
or getattr(getattr(owner, "spec", None) or {}, "app_id", None)
|
|
81
|
+
or (getattr(owner, "spec", None) or {}).get("app_id")
|
|
82
|
+
)
|
|
83
|
+
|
|
63
84
|
env = RuntimeEnv(
|
|
64
85
|
run_id=run_id,
|
|
86
|
+
graph_id=graph_id,
|
|
87
|
+
session_id=session_id,
|
|
88
|
+
identity=identity,
|
|
65
89
|
graph_inputs=inputs,
|
|
66
90
|
outputs_by_node={},
|
|
67
91
|
container=container,
|
|
92
|
+
agent_id=agent_id,
|
|
93
|
+
app_id=app_id,
|
|
68
94
|
)
|
|
95
|
+
|
|
69
96
|
retry = rt_overrides.get("retry") or RetryPolicy()
|
|
70
97
|
max_conc = rt_overrides.get("max_concurrency", getattr(owner, "max_concurrency", 4))
|
|
71
98
|
return env, retry, max_conc
|
|
@@ -139,7 +166,7 @@ def _resolve_graph_outputs(
|
|
|
139
166
|
continuations=continuations,
|
|
140
167
|
) from e
|
|
141
168
|
|
|
142
|
-
return
|
|
169
|
+
return result
|
|
143
170
|
|
|
144
171
|
|
|
145
172
|
def _resolve_graph_outputs_or_waits(graph, inputs, env, *, raise_on_waits: bool = True):
|
|
@@ -187,6 +214,7 @@ async def load_latest_snapshot_json(store, run_id: str) -> dict[str, Any] | None
|
|
|
187
214
|
# JsonGraphStateStore serializes GraphSnapshot via snap.__dict__
|
|
188
215
|
# load_latest_snapshot already returns a GraphSnapshot(**jsondict).
|
|
189
216
|
# Convert back to plain JSON-ish dict:
|
|
217
|
+
|
|
190
218
|
return {
|
|
191
219
|
"run_id": snap.run_id,
|
|
192
220
|
"graph_id": snap.graph_id,
|
|
@@ -196,22 +224,124 @@ async def load_latest_snapshot_json(store, run_id: str) -> dict[str, Any] | None
|
|
|
196
224
|
}
|
|
197
225
|
|
|
198
226
|
|
|
227
|
+
def _register_metering_context(
|
|
228
|
+
env: RuntimeEnv, target: GraphFunction | TaskGraph | Any
|
|
229
|
+
) -> dict[str, Any]:
|
|
230
|
+
"""
|
|
231
|
+
Build a metering context dict from the RuntimeEnv.
|
|
232
|
+
"""
|
|
233
|
+
run_id = env.run_id
|
|
234
|
+
# derive graph_id from target if possible - GraphFunction.name or TaskGraph.spec.graph_id
|
|
235
|
+
graph_id = getattr(target, "name", None) or getattr(
|
|
236
|
+
getattr(target, "spec", None), "graph_id", None
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# user id etc through auth context if available
|
|
240
|
+
user_id = getattr(getattr(env, "identity", None), "user_id", None)
|
|
241
|
+
org_id = getattr(getattr(env, "identity", None), "org_id", None)
|
|
242
|
+
|
|
243
|
+
token = current_meter_context.set(
|
|
244
|
+
{
|
|
245
|
+
"run_id": run_id,
|
|
246
|
+
"graph_id": graph_id,
|
|
247
|
+
"user_id": user_id,
|
|
248
|
+
"org_id": org_id,
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
return token
|
|
252
|
+
|
|
253
|
+
|
|
199
254
|
# ---------- public API ----------
|
|
200
|
-
async def run_async(
|
|
255
|
+
async def run_async(
|
|
256
|
+
target,
|
|
257
|
+
inputs: dict[str, Any] | None = None,
|
|
258
|
+
identity: RequestIdentity | None = None,
|
|
259
|
+
**rt_overrides,
|
|
260
|
+
):
|
|
201
261
|
"""
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
262
|
+
Execute a TaskGraph or GraphFunction asynchronously with optional persistence and resumability.
|
|
263
|
+
|
|
264
|
+
This method handles environment setup, cold-resume from persisted state (if available),
|
|
265
|
+
input validation, scheduling, and output resolution. It supports both fresh runs and
|
|
266
|
+
resuming incomplete runs, automatically wiring up persistence observers and enforcing
|
|
267
|
+
snapshot policies.
|
|
268
|
+
|
|
269
|
+
Examples:
|
|
270
|
+
Running a graph function:
|
|
271
|
+
```python
|
|
272
|
+
result = await run_async(my_graph_fn, {"x": 1, "y": 2})
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Running a TaskGraph with custom run ID and identity:
|
|
276
|
+
```python
|
|
277
|
+
result = await run_async(
|
|
278
|
+
my_task_graph,
|
|
279
|
+
{"input": 42},
|
|
280
|
+
run_id="custom-run-123",
|
|
281
|
+
identity=my_identity, # Only used with API requests. Ignored when running locally.
|
|
282
|
+
max_concurrency=8
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
target: The TaskGraph, GraphFunction, or builder to execute.
|
|
288
|
+
inputs: Dictionary of input values for the graph.
|
|
289
|
+
identity: Optional RequestIdentity for user/session context.
|
|
290
|
+
**rt_overrides: Optional runtime overrides for environment and execution. Recognized runtime overrides include:
|
|
291
|
+
|
|
292
|
+
- run_id (str): Custom run identifier.
|
|
293
|
+
- session_id (str): Session identifier for grouping runs.
|
|
294
|
+
- agent_id (str): Agent identifier for provenance.
|
|
295
|
+
- app_id (str): Application identifier for provenance.
|
|
296
|
+
- retry (RetryPolicy): Custom retry policy.
|
|
297
|
+
- max_concurrency (int): Maximum number of concurrent tasks.
|
|
298
|
+
- Any additional container attributes supported by your environment.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
dict: The resolved outputs of the graph, or a status dict if waiting on continuations.
|
|
302
|
+
|
|
303
|
+
Raises:
|
|
304
|
+
GraphHasPendingWaits: If the graph is waiting on external events and outputs are not ready.
|
|
305
|
+
TypeError: If the target is not a valid TaskGraph or GraphFunction.
|
|
306
|
+
|
|
307
|
+
Notes:
|
|
308
|
+
- Speficially for GraphFunctions, you can directly use `await graph_fn(**inputs)` without needing `run_async`.
|
|
309
|
+
- `graph_fn` is not resumable; use TaskGraphs for persistence and recovery features.
|
|
310
|
+
- when using `graph` for persistence/resumability, ensure your outputs are JSON-serializable, for examples:
|
|
311
|
+
- primitive types (str, int, float, bool, None)
|
|
312
|
+
- lists/dicts of primitive types
|
|
313
|
+
|
|
314
|
+
- graph that can be resumed with JSON-serializable outputs:
|
|
315
|
+
```python
|
|
316
|
+
@graphify(...)
|
|
317
|
+
def my_graph(...):
|
|
318
|
+
...
|
|
319
|
+
return {"result": 42, "data": [1, 2, 3], "info": None} # valid JSON-serializable output
|
|
320
|
+
```
|
|
321
|
+
- graph that cannot be resumed due to non-JSON-serializable outputs:
|
|
322
|
+
```python
|
|
323
|
+
@graphify(...)
|
|
324
|
+
def my_graph(...):
|
|
325
|
+
...
|
|
326
|
+
return {"chekpoint": torch.pt, "file": open("data.bin", "rb")} # invalid outputs for resuming (but valid for fresh runs)
|
|
327
|
+
```
|
|
328
|
+
- Despite this, you can still use `graph` without persistence features; just avoid resuming such graphs.
|
|
329
|
+
|
|
205
330
|
"""
|
|
206
331
|
inputs = inputs or {}
|
|
207
332
|
# GraphFunction path
|
|
208
333
|
if isinstance(target, GraphFunction):
|
|
209
|
-
env, retry, max_conc = await _build_env(target, inputs, **rt_overrides)
|
|
210
|
-
|
|
334
|
+
env, retry, max_conc = await _build_env(target, inputs, identity=identity, **rt_overrides)
|
|
335
|
+
token = _register_metering_context(env, target) # set metering context
|
|
336
|
+
try:
|
|
337
|
+
return await target.run(env=env, max_concurrency=max_conc, **inputs)
|
|
338
|
+
finally:
|
|
339
|
+
# reset metering context
|
|
340
|
+
current_meter_context.reset(token)
|
|
211
341
|
|
|
212
342
|
# TaskGraph path
|
|
213
343
|
graph = _materialize_task_graph(target)
|
|
214
|
-
env, retry, max_conc = await _build_env(graph, inputs, **rt_overrides)
|
|
344
|
+
env, retry, max_conc = await _build_env(graph, inputs, identity=identity, **rt_overrides)
|
|
215
345
|
|
|
216
346
|
# Extract spec for run/recovery ...
|
|
217
347
|
spec = getattr(graph, "spec", None) or getattr(graph, "get_spec", lambda: None)()
|
|
@@ -266,33 +396,44 @@ async def run_async(target, inputs: dict[str, Any] | None = None, **rt_overrides
|
|
|
266
396
|
)
|
|
267
397
|
|
|
268
398
|
# Register for resumes and run
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
399
|
+
token = _register_metering_context(env, target) # set metering context
|
|
400
|
+
try:
|
|
401
|
+
with RunRegistrationGuard(run_id=env.run_id, scheduler=sched, container=env.container):
|
|
402
|
+
try:
|
|
403
|
+
await sched.run()
|
|
404
|
+
except asyncio.CancelledError:
|
|
405
|
+
raise
|
|
406
|
+
finally:
|
|
407
|
+
# FINAL SNAPSHOT on normal or cancelled exit (if store exists)
|
|
408
|
+
if store and obs:
|
|
409
|
+
artifacts = getattr(env.container, "artifacts", None)
|
|
410
|
+
snap = await snapshot_from_graph(
|
|
411
|
+
run_id=graph.state.run_id or env.run_id,
|
|
412
|
+
graph_id=graph.graph_id,
|
|
413
|
+
rev=graph.state.rev,
|
|
414
|
+
spec_hash=hash_spec(spec),
|
|
415
|
+
state_obj=graph.state,
|
|
416
|
+
artifacts=artifacts,
|
|
417
|
+
allow_externalize=False, # FIXME: artifact writer async loop error; set False to *avoid* writing artifacts during snapshot
|
|
418
|
+
include_wait_spec=True,
|
|
419
|
+
)
|
|
420
|
+
await store.save_snapshot(snap)
|
|
421
|
+
|
|
422
|
+
# Resolve graph-level outputs (will raise if waits)
|
|
423
|
+
return _resolve_graph_outputs_or_waits(graph, inputs, env, raise_on_waits=True)
|
|
424
|
+
finally:
|
|
425
|
+
# reset metering context
|
|
426
|
+
current_meter_context.reset(token)
|
|
292
427
|
|
|
293
428
|
|
|
294
429
|
async def run_or_resume_async(
|
|
295
|
-
target,
|
|
430
|
+
target,
|
|
431
|
+
inputs: dict[str, Any],
|
|
432
|
+
*,
|
|
433
|
+
run_id: str | None = None,
|
|
434
|
+
session_id: str | None = None,
|
|
435
|
+
identity: RequestIdentity | None = None,
|
|
436
|
+
**rt_overrides,
|
|
296
437
|
):
|
|
297
438
|
"""
|
|
298
439
|
If state exists for run_id → cold resume, else fresh run.
|
|
@@ -300,7 +441,9 @@ async def run_or_resume_async(
|
|
|
300
441
|
"""
|
|
301
442
|
if run_id is not None:
|
|
302
443
|
rt_overrides = dict(rt_overrides or {}, run_id=run_id)
|
|
303
|
-
|
|
444
|
+
if session_id is not None:
|
|
445
|
+
rt_overrides = dict(rt_overrides or {}, session_id=session_id)
|
|
446
|
+
return await run_async(target, inputs, identity=identity, **rt_overrides)
|
|
304
447
|
|
|
305
448
|
|
|
306
449
|
# sync adapter (optional, safe in notebooks/servers)
|
|
@@ -344,6 +487,48 @@ class _LoopThread:
|
|
|
344
487
|
_LOOP = _LoopThread()
|
|
345
488
|
|
|
346
489
|
|
|
347
|
-
def run(
|
|
490
|
+
def run(
|
|
491
|
+
target,
|
|
492
|
+
inputs: dict[str, Any] | None = None,
|
|
493
|
+
identity: RequestIdentity | None = None,
|
|
494
|
+
**rt_overrides,
|
|
495
|
+
):
|
|
496
|
+
"""
|
|
497
|
+
Execute a target graph node synchronously with the provided inputs.
|
|
498
|
+
|
|
499
|
+
This function submits the execution of a target node (or graph) to the event loop,
|
|
500
|
+
allowing for asynchronous execution while providing a synchronous interface.
|
|
501
|
+
Runtime configuration overrides can be supplied as keyword arguments.
|
|
502
|
+
|
|
503
|
+
Examples:
|
|
504
|
+
Running a graph node with default inputs:
|
|
505
|
+
```python
|
|
506
|
+
future = run(my_node)
|
|
507
|
+
result = future.result()
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Running with custom inputs and runtime overrides:
|
|
511
|
+
```python
|
|
512
|
+
future = run(my_node, inputs={"x": 42}, timeout=10)
|
|
513
|
+
output = future.result()
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
target: The graph node or callable to execute.
|
|
518
|
+
inputs: Optional dictionary of input values to pass to the node.
|
|
519
|
+
identity: Optional RequestIdentity for user/session context.
|
|
520
|
+
**rt_overrides: Additional keyword arguments to override runtime configuration.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
concurrent.futures.Future: A future representing the asynchronous execution of the node.
|
|
524
|
+
|
|
525
|
+
Notes:
|
|
526
|
+
- This function is suitable for use in synchronous contexts where asynchronous execution is desired.
|
|
527
|
+
- It is recommended to use asynchronous execution directly when possible for better performance and responsiveness.
|
|
528
|
+
|
|
529
|
+
Warnings:
|
|
530
|
+
- KeyboardInterrupt handling may not be perfect; consider using an async main function when possible.
|
|
531
|
+
- This function blocks the calling thread until the execution is complete.
|
|
532
|
+
"""
|
|
348
533
|
inputs = inputs or {}
|
|
349
|
-
return _LOOP.submit(run_async(target, inputs, **rt_overrides))
|
|
534
|
+
return _LOOP.submit(run_async(target, inputs, identity=identity, **rt_overrides))
|