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
|
File without changes
|
|
@@ -22,8 +22,12 @@ def current_builder() -> GraphBuilder | None:
|
|
|
22
22
|
class GraphBuilder:
|
|
23
23
|
_auto_counter = itertools.count(1)
|
|
24
24
|
|
|
25
|
-
def __init__(
|
|
26
|
-
self
|
|
25
|
+
def __init__(
|
|
26
|
+
self, *, name: str = "default_graph", agent_id: str | None = None, app_id: str | None = None
|
|
27
|
+
):
|
|
28
|
+
self.spec = TaskGraphSpec(
|
|
29
|
+
graph_id=name, nodes={}, meta={}, agent_id=agent_id, app_id=app_id
|
|
30
|
+
)
|
|
27
31
|
self.graph = TaskGraph(spec=self.spec)
|
|
28
32
|
self.graph.ensure_inputs_node()
|
|
29
33
|
|
|
@@ -181,9 +185,9 @@ class GraphBuilder:
|
|
|
181
185
|
|
|
182
186
|
|
|
183
187
|
@contextmanager
|
|
184
|
-
def graph(*, name: str = "default_graph"):
|
|
188
|
+
def graph(*, name: str = "default_graph", agent_id: str | None = None, app_id: str | None = None):
|
|
185
189
|
"""Context manager that yields a GraphBuilder to build a TaskGraph."""
|
|
186
|
-
builder = GraphBuilder(name=name)
|
|
190
|
+
builder = GraphBuilder(name=name, agent_id=agent_id, app_id=app_id)
|
|
187
191
|
token = _GRAPH_CTX.set(builder)
|
|
188
192
|
try:
|
|
189
193
|
yield builder.graph
|
|
@@ -2,8 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
4
|
import inspect
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
from aethergraph.core.runtime.run_registration import RunRegistrationGuard
|
|
8
|
+
from aethergraph.services.registry.agent_app_meta import (
|
|
9
|
+
build_agent_meta,
|
|
10
|
+
build_app_meta,
|
|
11
|
+
)
|
|
7
12
|
|
|
8
13
|
from ..execution.retry_policy import RetryPolicy
|
|
9
14
|
from ..runtime.runtime_env import RuntimeEnv
|
|
@@ -22,6 +27,8 @@ class GraphFunction:
|
|
|
22
27
|
inputs: list[str] | None = None,
|
|
23
28
|
outputs: list[str] | None = None,
|
|
24
29
|
version: str = "0.1.0",
|
|
30
|
+
agent_id: str | None = None,
|
|
31
|
+
app_id: str | None = None,
|
|
25
32
|
):
|
|
26
33
|
self.graph_id = name
|
|
27
34
|
self.name = name
|
|
@@ -33,6 +40,8 @@ class GraphFunction:
|
|
|
33
40
|
self.last_graph = None
|
|
34
41
|
self.last_context = None
|
|
35
42
|
self.last_memory_snapshot = None
|
|
43
|
+
self.agent_id = agent_id
|
|
44
|
+
self.app_id = app_id
|
|
36
45
|
|
|
37
46
|
async def run(
|
|
38
47
|
self,
|
|
@@ -62,7 +71,7 @@ class GraphFunction:
|
|
|
62
71
|
)
|
|
63
72
|
node_ctx = runtime_ctx.create_node_context(node=node_spec)
|
|
64
73
|
|
|
65
|
-
with graph(name=self.graph_id) as G:
|
|
74
|
+
with graph(name=self.graph_id, agent_id=self.agent_id, app_id=self.app_id) as G:
|
|
66
75
|
interp = Interpreter(G, env, retry=retry, max_concurrency=max_concurrency)
|
|
67
76
|
run_id = env.run_id
|
|
68
77
|
|
|
@@ -192,28 +201,160 @@ def graph_fn(
|
|
|
192
201
|
inputs: list[str] | None = None,
|
|
193
202
|
outputs: list[str] | None = None,
|
|
194
203
|
version: str = "0.1.0",
|
|
195
|
-
|
|
204
|
+
*,
|
|
205
|
+
entrypoint: bool = False,
|
|
206
|
+
flow_id: str | None = None,
|
|
207
|
+
tags: list[str] | None = None,
|
|
208
|
+
as_agent: dict[str, Any] | None = None,
|
|
209
|
+
as_app: dict[str, Any] | None = None,
|
|
196
210
|
) -> Callable[[Callable], GraphFunction]:
|
|
197
|
-
"""
|
|
211
|
+
"""
|
|
212
|
+
Decorator to define a graph function and optionally register it as an agent or app.
|
|
213
|
+
|
|
214
|
+
This decorator wraps a Python function as a `GraphFunction`, enabling it to be executed
|
|
215
|
+
as a node-based graph with runtime context, retry policy, and concurrency controls.
|
|
216
|
+
It also supports rich metadata registration for agent and app discovery.
|
|
198
217
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
218
|
+
Examples:
|
|
219
|
+
Basic usage:
|
|
220
|
+
```python
|
|
221
|
+
@graph_fn(
|
|
222
|
+
name="add_numbers",
|
|
223
|
+
inputs=["a", "b"],
|
|
224
|
+
outputs=["sum"],
|
|
225
|
+
)
|
|
226
|
+
async def add_numbers(a: int, b: int):
|
|
227
|
+
return {"sum": a + b}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Registering as an agent with metadata:
|
|
231
|
+
```python
|
|
232
|
+
@graph_fn(
|
|
233
|
+
name="chat_agent",
|
|
234
|
+
inputs=["message", "files", "context_refs", "session_id", "user_meta"],
|
|
235
|
+
outputs=["response"],
|
|
236
|
+
as_agent={
|
|
237
|
+
"id": "chatbot",
|
|
238
|
+
"title": "Chat Agent",
|
|
239
|
+
"description": "Conversational AI agent.",
|
|
240
|
+
"mode": "chat_v1",
|
|
241
|
+
"icon": "chat",
|
|
242
|
+
"tags": ["chat", "nlp"],
|
|
243
|
+
},
|
|
244
|
+
)
|
|
245
|
+
async def chat_agent(...):
|
|
246
|
+
...
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Registering as an app:
|
|
250
|
+
```python
|
|
251
|
+
@graph_fn(
|
|
252
|
+
name="summarizer",
|
|
253
|
+
inputs=[],
|
|
254
|
+
outputs=["summary"],
|
|
255
|
+
as_app={
|
|
256
|
+
"id": "summarizer-app",
|
|
257
|
+
"name": "Text Summarizer",
|
|
258
|
+
"description": "Summarizes input text.",
|
|
259
|
+
"category": "Productivity",
|
|
260
|
+
"tags": ["nlp", "summary"],
|
|
261
|
+
},
|
|
262
|
+
)
|
|
263
|
+
async def summarizer():
|
|
264
|
+
...
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
name: Unique name for the graph function.
|
|
269
|
+
inputs: List of input parameter names. If `as_agent` is provided with `mode="chat_v1"`,
|
|
270
|
+
this must match `["message", "files", "context_refs", "session_id", "user_meta"]`.
|
|
271
|
+
outputs: List of output keys returned by the function.
|
|
272
|
+
version: Version string for the graph function (default: "0.1.0").
|
|
273
|
+
entrypoint: If True, marks this graph as the main entrypoint for a flow. [Currently unused]
|
|
274
|
+
flow_id: Optional flow identifier for grouping related graphs.
|
|
275
|
+
tags: List of string tags for discovery and categorization.
|
|
276
|
+
as_agent: Optional dictionary defining agent metadata. Used when running through Aethergraph UI. See additional information below.
|
|
277
|
+
as_app: Optional dictionary defining app metadata. Used when running through Aethergraph UI. See additional information below.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Callable: A decorator that wraps the function as a `GraphFunction` and registers it
|
|
281
|
+
in the runtime registry, with agent/app metadata if provided.
|
|
282
|
+
|
|
283
|
+
Notes:
|
|
284
|
+
- as_agent and as_app are not needed to define a graph; they are only for registration purposes for use in Aethergraph UI.
|
|
285
|
+
- When registering as an agent, the `as_agent` dictionary should include at least an "id" key.
|
|
286
|
+
- When registering as an app, the `as_app` dictionary should include at least an "id" key.
|
|
287
|
+
- The decorated function can be either synchronous or asynchronous.
|
|
288
|
+
- Fields `inputs` and `outputs` are can be inferred from the function signature if not explicitly provided, but it's recommended to declare them for clarity.
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
def decorator(fn: Callable) -> GraphFunction:
|
|
292
|
+
agent_id = as_agent.get("id") if as_agent else None
|
|
293
|
+
app_id = as_app.get("id") if as_app else None
|
|
294
|
+
gf = GraphFunction(
|
|
295
|
+
name=name,
|
|
296
|
+
fn=fn,
|
|
297
|
+
inputs=inputs,
|
|
298
|
+
outputs=outputs,
|
|
299
|
+
version=version,
|
|
300
|
+
agent_id=agent_id,
|
|
301
|
+
app_id=app_id,
|
|
302
|
+
)
|
|
202
303
|
registry = current_registry()
|
|
203
304
|
|
|
204
|
-
if registry is
|
|
305
|
+
if registry is None:
|
|
306
|
+
# no registry available, just return the graph function
|
|
307
|
+
return gf
|
|
308
|
+
|
|
309
|
+
base_tags = tags or []
|
|
310
|
+
graph_meta: dict[str, Any] = {
|
|
311
|
+
"kind": "graphfn",
|
|
312
|
+
"entrypoint": entrypoint,
|
|
313
|
+
"flow_id": flow_id or name,
|
|
314
|
+
"tags": base_tags,
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
registry.register(
|
|
318
|
+
nspace="graphfn",
|
|
319
|
+
name=name,
|
|
320
|
+
version=version,
|
|
321
|
+
obj=gf,
|
|
322
|
+
meta=graph_meta,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# Register as agent if requested
|
|
326
|
+
# 4Agent meta (if any)
|
|
327
|
+
agent_meta = build_agent_meta(
|
|
328
|
+
graph_name=name,
|
|
329
|
+
version=version,
|
|
330
|
+
graph_meta=graph_meta,
|
|
331
|
+
agent_cfg=as_agent,
|
|
332
|
+
)
|
|
333
|
+
if agent_meta is not None:
|
|
334
|
+
registry.register(
|
|
335
|
+
nspace="agent",
|
|
336
|
+
name=agent_meta["id"],
|
|
337
|
+
version=version,
|
|
338
|
+
obj=gf,
|
|
339
|
+
meta=agent_meta,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
# 5) App meta (if any)
|
|
343
|
+
app_meta = build_app_meta(
|
|
344
|
+
graph_name=name,
|
|
345
|
+
version=version,
|
|
346
|
+
graph_meta=graph_meta,
|
|
347
|
+
app_cfg=as_app,
|
|
348
|
+
)
|
|
349
|
+
if app_meta is not None:
|
|
205
350
|
registry.register(
|
|
206
|
-
nspace="
|
|
207
|
-
name=
|
|
351
|
+
nspace="app",
|
|
352
|
+
name=app_meta["id"],
|
|
208
353
|
version=version,
|
|
209
|
-
obj=gf,
|
|
354
|
+
obj=gf,
|
|
355
|
+
meta=app_meta,
|
|
210
356
|
)
|
|
211
357
|
|
|
212
|
-
if agent:
|
|
213
|
-
assert (
|
|
214
|
-
registry is not None
|
|
215
|
-
), "No registry available to register agent, make sure to have a current_registry() set up."
|
|
216
|
-
registry.register(nspace="agent", name=agent, version=version, obj=gf)
|
|
217
358
|
return gf
|
|
218
359
|
|
|
219
360
|
return decorator
|
|
@@ -14,6 +14,8 @@ class TaskGraphSpec:
|
|
|
14
14
|
io: IOSpec = field(default_factory=IOSpec) # inputs/outputs
|
|
15
15
|
bindings: IOBindings | None = None # input/output bindings
|
|
16
16
|
meta: dict[str, Any] = field(default_factory=dict) # additional metadata
|
|
17
|
+
agent_id: str | None = None # for agent-invoked runs
|
|
18
|
+
app_id: str | None = None # for app-invoked runs
|
|
17
19
|
|
|
18
20
|
def canonical(self) -> str:
|
|
19
21
|
return f"graph:{self.graph_id}@{self.version}"
|
|
@@ -37,6 +39,12 @@ class TaskGraphSpec:
|
|
|
37
39
|
f"outputs: {_fmt_outputs_map(self.outputs)}",
|
|
38
40
|
]
|
|
39
41
|
|
|
42
|
+
def __items__(self, key: str) -> Any:
|
|
43
|
+
return getattr(self, key)
|
|
44
|
+
|
|
45
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
46
|
+
return getattr(self, key, default)
|
|
47
|
+
|
|
40
48
|
|
|
41
49
|
@dataclass
|
|
42
50
|
class GraphView:
|
|
@@ -1,29 +1,103 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from aethergraph.services.registry.agent_app_meta import build_agent_meta, build_app_meta
|
|
4
7
|
|
|
5
8
|
from ..runtime.runtime_registry import current_registry
|
|
6
9
|
from .task_graph import TaskGraph
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
def graphify(
|
|
10
|
-
|
|
13
|
+
name="default_graph",
|
|
14
|
+
inputs=(),
|
|
15
|
+
outputs=None,
|
|
16
|
+
version="0.1.0",
|
|
17
|
+
*,
|
|
18
|
+
entrypoint: bool = False,
|
|
19
|
+
flow_id: str | None = None,
|
|
20
|
+
tags: list[str] | None = None,
|
|
21
|
+
as_agent: dict[str, Any] | None = None,
|
|
22
|
+
as_app: dict[str, Any] | None = None,
|
|
11
23
|
):
|
|
12
24
|
"""
|
|
13
|
-
Decorator
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
Decorator to define a `TaskGraph` and optionally register it as an agent or app.
|
|
26
|
+
|
|
27
|
+
This decorator wraps a Python function as a `TaskGraph`, enabling it to be executed
|
|
28
|
+
as a node-based graph with runtime context, retry policy, and concurrency controls.
|
|
29
|
+
It also supports rich metadata registration for agent and app discovery.
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
Basic usage:
|
|
33
|
+
```python
|
|
34
|
+
@graphify(
|
|
35
|
+
name="add_numbers",
|
|
36
|
+
inputs=["a", "b"],
|
|
37
|
+
outputs=["sum"],
|
|
38
|
+
)
|
|
39
|
+
async def add_numbers(a: int, b: int):
|
|
40
|
+
return {"sum": a + b}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Registering as an agent with metadata:
|
|
44
|
+
```python
|
|
45
|
+
@graphify(
|
|
46
|
+
name="chat_agent",
|
|
47
|
+
inputs=["message", "files", "context_refs", "session_id", "user_meta"],
|
|
48
|
+
outputs=["response"],
|
|
49
|
+
as_agent={
|
|
50
|
+
"id": "chatbot",
|
|
51
|
+
"title": "Chat Agent",
|
|
52
|
+
"description": "Conversational AI agent.",
|
|
53
|
+
"mode": "chat_v1",
|
|
54
|
+
"icon": "chat",
|
|
55
|
+
"tags": ["chat", "nlp"],
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
async def chat_agent(...):
|
|
59
|
+
...
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Registering as an app:
|
|
63
|
+
```python
|
|
64
|
+
@graphify(
|
|
65
|
+
name="summarizer",
|
|
66
|
+
inputs=[],
|
|
67
|
+
outputs=["summary"],
|
|
68
|
+
as_app={
|
|
69
|
+
"id": "summarizer-app",
|
|
70
|
+
"name": "Text Summarizer",
|
|
71
|
+
"description": "Summarizes input text.",
|
|
72
|
+
"category": "Productivity",
|
|
73
|
+
"tags": ["nlp", "summary"],
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
async def summarizer():
|
|
77
|
+
...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
name: Unique name for the graph function.
|
|
82
|
+
inputs: List of input parameter names. If `as_agent` is provided with `mode="chat_v1"`,
|
|
83
|
+
this must match `["message", "files", "context_refs", "session_id", "user_meta"]`.
|
|
84
|
+
outputs: List of output keys returned by the function.
|
|
85
|
+
version: Version string for the graph function (default: "0.1.0").
|
|
86
|
+
entrypoint: If True, marks this graph as the main entrypoint for a flow. [Currently unused]
|
|
87
|
+
flow_id: Optional flow identifier for grouping related graphs.
|
|
88
|
+
tags: List of string tags for discovery and categorization.
|
|
89
|
+
as_agent: Optional dictionary defining agent metadata. Used when running through Aethergraph UI. See additional information below.
|
|
90
|
+
as_app: Optional dictionary defining app metadata. Used when running through Aethergraph UI. See additional information below.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
TaskGraph: A decorator that transforms a function into a TaskGraph with the specified configuration.
|
|
94
|
+
|
|
95
|
+
Notes:
|
|
96
|
+
- as_agent and as_app are not needed to define a graph; they are only for registration purposes for use in Aethergraph UI.
|
|
97
|
+
- When registering as an agent, the `as_agent` dictionary should include at least an "id" key.
|
|
98
|
+
- When registering as an app, the `as_app` dictionary should include at least an "id" key.
|
|
99
|
+
- The decorated function is a sync function (generate the TaskGraph), despite the underlying `@tool` can be async.
|
|
100
|
+
- Fields `inputs` and `outputs` are can be inferred from the function signature if not explicitly provided, but it's recommended to declare them for clarity.
|
|
27
101
|
"""
|
|
28
102
|
|
|
29
103
|
def _wrap(fn):
|
|
@@ -41,7 +115,10 @@ def graphify(
|
|
|
41
115
|
from .graph_builder import graph
|
|
42
116
|
from .graph_refs import arg
|
|
43
117
|
|
|
44
|
-
|
|
118
|
+
agent_id = as_agent.get("id") if as_agent else None
|
|
119
|
+
app_id = as_app.get("id") if as_app else None
|
|
120
|
+
|
|
121
|
+
with graph(name=name, agent_id=agent_id, app_id=app_id) as g:
|
|
45
122
|
# declarations unchanged...
|
|
46
123
|
if isinstance(inputs, dict):
|
|
47
124
|
g.declare_inputs(required=[], optional=inputs)
|
|
@@ -52,7 +129,7 @@ def graphify(
|
|
|
52
129
|
injected_kwargs = {p: arg(p) for p in overlap}
|
|
53
130
|
|
|
54
131
|
# Run user body
|
|
55
|
-
ret = fn(**injected_kwargs)
|
|
132
|
+
ret = fn(**injected_kwargs)
|
|
56
133
|
|
|
57
134
|
# expose logic (fixed typo + single-output collapse)
|
|
58
135
|
def _is_ref(x):
|
|
@@ -112,16 +189,58 @@ def graphify(
|
|
|
112
189
|
_build.io = _io
|
|
113
190
|
|
|
114
191
|
# ---- Register graph + optional agent ----
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
192
|
+
|
|
193
|
+
registry = current_registry()
|
|
194
|
+
if registry is None:
|
|
195
|
+
return _build
|
|
196
|
+
|
|
197
|
+
base_tags = tags or []
|
|
198
|
+
graph_meta: dict[str, Any] = {
|
|
199
|
+
"kind": "graph",
|
|
200
|
+
"entrypoint": entrypoint,
|
|
201
|
+
"flow_id": flow_id or name,
|
|
202
|
+
"tags": base_tags,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
registry.register(
|
|
206
|
+
nspace="graph",
|
|
207
|
+
name=name,
|
|
208
|
+
version=version,
|
|
209
|
+
obj=_build(),
|
|
210
|
+
meta=graph_meta,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Agent meta (if any)
|
|
214
|
+
agent_meta = build_agent_meta(
|
|
215
|
+
graph_name=name,
|
|
216
|
+
version=version,
|
|
217
|
+
graph_meta=graph_meta,
|
|
218
|
+
agent_cfg=as_agent,
|
|
219
|
+
)
|
|
220
|
+
if agent_meta is not None:
|
|
221
|
+
registry.register(
|
|
222
|
+
nspace="agent",
|
|
223
|
+
name=agent_meta["id"],
|
|
224
|
+
version=version,
|
|
225
|
+
obj=_build(),
|
|
226
|
+
meta=agent_meta,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# App meta (if any)
|
|
230
|
+
app_meta = build_app_meta(
|
|
231
|
+
graph_name=name,
|
|
232
|
+
version=version,
|
|
233
|
+
graph_meta=graph_meta,
|
|
234
|
+
app_cfg=as_app,
|
|
235
|
+
)
|
|
236
|
+
if app_meta is not None:
|
|
237
|
+
registry.register(
|
|
238
|
+
nspace="app",
|
|
239
|
+
name=app_meta["id"],
|
|
240
|
+
version=version,
|
|
241
|
+
obj=_build(),
|
|
242
|
+
meta=app_meta,
|
|
243
|
+
)
|
|
125
244
|
|
|
126
245
|
return _build
|
|
127
246
|
|
|
@@ -39,8 +39,6 @@ class TaskNodeSpec:
|
|
|
39
39
|
condition: bool | dict[str, Any] | callable[[dict[str, Any]], bool] = True
|
|
40
40
|
|
|
41
41
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
42
|
-
reads: set[str] = field(default_factory=set) # state keys to read
|
|
43
|
-
writes: set[str] = field(default_factory=set) # state keys to write
|
|
44
42
|
|
|
45
43
|
tool_name: str | None = None # used for logging/monitoring
|
|
46
44
|
tool_version: str | None = None # used for logging/monitoring
|
|
@@ -10,6 +10,7 @@ class NodeStatus:
|
|
|
10
10
|
DONE = "DONE"
|
|
11
11
|
FAILED = "FAILED"
|
|
12
12
|
SKIPPED = "SKIPPED"
|
|
13
|
+
CANCELLED = "CANCELLED"
|
|
13
14
|
FAILED_TIMEOUT = "FAILED_TIMEOUT"
|
|
14
15
|
WAITING_HUMAN = "WAITING_HUMAN"
|
|
15
16
|
WAITING_ROBOT = "WAITING_ROBOT"
|
|
@@ -56,6 +57,8 @@ class TaskNodeState:
|
|
|
56
57
|
next_wakeup_at: str | None = None # ISO timestamp
|
|
57
58
|
wait_token: str | None = None # for external wait/resume with Continuation
|
|
58
59
|
wait_spec: dict[str, Any] | None = None # spec for waiting (kind, channel, meta, etc.)
|
|
60
|
+
started_at: str | None = None # ISO timestamp
|
|
61
|
+
finished_at: str | None = None # ISO timestamp
|
|
59
62
|
|
|
60
63
|
@property
|
|
61
64
|
def output(self):
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from collections.abc import Iterable
|
|
4
4
|
from dataclasses import asdict, dataclass, field, is_dataclass
|
|
5
|
+
import datetime
|
|
5
6
|
import inspect
|
|
6
7
|
from typing import Any
|
|
7
8
|
import uuid
|
|
@@ -21,6 +22,10 @@ def _dataclass_to_plain(d):
|
|
|
21
22
|
return asdict(d) if is_dataclass(d) else d
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
def _utc_ts() -> float:
|
|
26
|
+
return datetime.datetime.now(tz=datetime.timezone.utc).timestamp()
|
|
27
|
+
|
|
28
|
+
|
|
24
29
|
@dataclass
|
|
25
30
|
class TaskGraph:
|
|
26
31
|
spec: TaskGraphSpec
|
|
@@ -412,10 +417,43 @@ class TaskGraph:
|
|
|
412
417
|
"set_outputs() is not implemented yet. Use set_node_outputs() instead."
|
|
413
418
|
)
|
|
414
419
|
|
|
420
|
+
# async def set_node_status(self, node_id: str, status: NodeStatus) -> None:
|
|
421
|
+
# state = self.state.nodes.get(node_id)
|
|
422
|
+
# if state.status is status:
|
|
423
|
+
# return
|
|
424
|
+
# state.status = status
|
|
425
|
+
# self.state.rev += 1
|
|
426
|
+
# await self._notify_status_change(node_id)
|
|
427
|
+
|
|
415
428
|
async def set_node_status(self, node_id: str, status: NodeStatus) -> None:
|
|
416
429
|
state = self.state.nodes.get(node_id)
|
|
417
|
-
if state
|
|
430
|
+
if state is None:
|
|
431
|
+
raise KeyError(f"Unknown node_id: {node_id}")
|
|
432
|
+
|
|
433
|
+
# no-op if unchanged
|
|
434
|
+
if state.status is status or state.status == status:
|
|
418
435
|
return
|
|
436
|
+
|
|
437
|
+
# --- timestamps ---
|
|
438
|
+
|
|
439
|
+
# 1) First time we go to RUNNING → set started_at (but don't overwrite on resume)
|
|
440
|
+
if status == NodeStatus.RUNNING and getattr(state, "started_at", None) is None:
|
|
441
|
+
state.started_at = _utc_ts()
|
|
442
|
+
|
|
443
|
+
# 2) Terminal states → set finished_at if not already set
|
|
444
|
+
TERMINAL_STATES = {
|
|
445
|
+
getattr(NodeStatus, "DONE", "DONE"),
|
|
446
|
+
getattr(NodeStatus, "FAILED", "FAILED"),
|
|
447
|
+
getattr(NodeStatus, "SKIPPED", "SKIPPED"),
|
|
448
|
+
getattr(NodeStatus, "CANCELLED", None) or getattr(NodeStatus, "CANCELED", None),
|
|
449
|
+
}
|
|
450
|
+
# filter out any Nones in case some names don't exist
|
|
451
|
+
TERMINAL_STATES = {s for s in TERMINAL_STATES if s is not None}
|
|
452
|
+
|
|
453
|
+
if status in TERMINAL_STATES and getattr(state, "finished_at", None) is None:
|
|
454
|
+
state.finished_at = _utc_ts()
|
|
455
|
+
|
|
456
|
+
# --- actual status change + rev bump ---
|
|
419
457
|
state.status = status
|
|
420
458
|
self.state.rev += 1
|
|
421
459
|
await self._notify_status_change(node_id)
|
|
File without changes
|
|
@@ -17,12 +17,40 @@ class _AdhocNode:
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
async def build_adhoc_context(
|
|
20
|
-
*,
|
|
21
20
|
run_id: str | None = None,
|
|
22
21
|
graph_id: str = "adhoc",
|
|
23
22
|
node_id: str = "adhoc",
|
|
24
23
|
**rt_overrides,
|
|
25
24
|
) -> ExecutionContext:
|
|
25
|
+
"""
|
|
26
|
+
Build an ad-hoc execution context for running a single node outside of a scheduled graph.
|
|
27
|
+
This function creates a minimal runtime environment suitable for quick, one-off executions,
|
|
28
|
+
such as testing or interactive exploration. It generates a temporary run and graph context,
|
|
29
|
+
instantiates an ad-hoc node, and returns a node-specific execution context.
|
|
30
|
+
Examples:
|
|
31
|
+
Basic usage with default parameters:
|
|
32
|
+
```python
|
|
33
|
+
node_ctx = await build_adhoc_context()
|
|
34
|
+
```
|
|
35
|
+
Customizing the run and session IDs:
|
|
36
|
+
```python
|
|
37
|
+
node_ctx = await build_adhoc_context(run_id="test-run", session_id="dev-session")
|
|
38
|
+
```
|
|
39
|
+
Overriding runtime parameters:
|
|
40
|
+
```python
|
|
41
|
+
node_ctx = await build_adhoc_context(max_concurrency=4)
|
|
42
|
+
```
|
|
43
|
+
Args:
|
|
44
|
+
run_id: Optional string to uniquely identify this run. If not provided,
|
|
45
|
+
a random ID is generated.
|
|
46
|
+
session_id: Optional string to associate this context with a session.
|
|
47
|
+
graph_id: Identifier for the graph. Defaults to `"adhoc"`.
|
|
48
|
+
node_id: Identifier for the node. Defaults to `"adhoc"`.
|
|
49
|
+
**rt_overrides: Additional runtime overrides, such as `max_concurrency`.
|
|
50
|
+
Returns:
|
|
51
|
+
NodeExecutionContext: The execution context for the ad-hoc node, ready for use.
|
|
52
|
+
"""
|
|
53
|
+
|
|
26
54
|
# Owner can be anything with max_concurrency; we won't really schedule
|
|
27
55
|
class _Owner:
|
|
28
56
|
max_concurrency = rt_overrides.get("max_concurrency", 1)
|
|
@@ -41,15 +69,47 @@ async def build_adhoc_context(
|
|
|
41
69
|
|
|
42
70
|
@asynccontextmanager
|
|
43
71
|
async def open_session(
|
|
44
|
-
*,
|
|
45
72
|
run_id: str | None = None,
|
|
46
73
|
graph_id: str = "adhoc",
|
|
47
74
|
node_id: str = "adhoc",
|
|
48
75
|
**rt_overrides,
|
|
49
76
|
):
|
|
50
77
|
"""
|
|
51
|
-
Open an
|
|
52
|
-
|
|
78
|
+
Open an ad-hoc session context for advanced or scripting use.
|
|
79
|
+
|
|
80
|
+
This asynchronous context manager yields a temporary context that mimics a `NodeContext`
|
|
81
|
+
without requiring a real graph run. It is intended for advanced scenarios where a lightweight,
|
|
82
|
+
ephemeral execution environment is needed, such as scripting, testing, or prototyping.
|
|
83
|
+
|
|
84
|
+
Examples:
|
|
85
|
+
Basic usage to open an ad-hoc session:
|
|
86
|
+
```python
|
|
87
|
+
async with open_session() as ctx:
|
|
88
|
+
# Use ctx as you would a NodeContext
|
|
89
|
+
...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Overriding runtime parameters:
|
|
93
|
+
```python
|
|
94
|
+
async with open_session(graph_id="mygraph", node_id="customnode", foo="bar") as ctx:
|
|
95
|
+
...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
run_id: Optional unique identifier for the run. If None, a random or default value is used.
|
|
100
|
+
graph_id: Identifier for the graph context. Defaults to "adhoc".
|
|
101
|
+
node_id: Identifier for the node context. Defaults to "adhoc".
|
|
102
|
+
**rt_overrides: Arbitrary keyword arguments to override runtime context parameters.
|
|
103
|
+
|
|
104
|
+
Yields:
|
|
105
|
+
NodeContext: An ad-hoc context object suitable for advanced scripting or testing.
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
Any exception raised during context construction or teardown will propagate.
|
|
109
|
+
|
|
110
|
+
Note:
|
|
111
|
+
This context does not persist state or artifacts beyond its lifetime. Use only for
|
|
112
|
+
non-production, ephemeral tasks.
|
|
53
113
|
"""
|
|
54
114
|
ctx = await build_adhoc_context(
|
|
55
115
|
run_id=run_id, graph_id=graph_id, node_id=node_id, **rt_overrides
|