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
aethergraph/server/start.py
CHANGED
|
@@ -1,119 +1,280 @@
|
|
|
1
|
-
# aethergraph/start.py
|
|
2
1
|
from __future__ import annotations
|
|
3
2
|
|
|
4
|
-
import asyncio
|
|
5
3
|
import contextlib
|
|
6
|
-
import
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
7
|
import threading
|
|
8
|
+
import time
|
|
9
|
+
from typing import Any
|
|
8
10
|
|
|
11
|
+
from fastapi import FastAPI
|
|
9
12
|
import uvicorn
|
|
10
13
|
|
|
11
14
|
from aethergraph.config.context import set_current_settings
|
|
12
15
|
from aethergraph.config.loader import load_settings
|
|
16
|
+
from aethergraph.server.loading import GraphLoader, LoadSpec
|
|
17
|
+
from aethergraph.server.server_state import (
|
|
18
|
+
get_running_url_if_any,
|
|
19
|
+
pick_free_port,
|
|
20
|
+
workspace_lock,
|
|
21
|
+
write_server_state,
|
|
22
|
+
)
|
|
13
23
|
|
|
14
|
-
from ..plugins.channel.routes.webui_routes import install_web_channel
|
|
15
24
|
from .app_factory import create_app
|
|
16
25
|
|
|
17
26
|
_started = False
|
|
18
27
|
_server_thread: threading.Thread | None = None
|
|
19
|
-
_shutdown_flag = threading.Event()
|
|
20
28
|
_url: str | None = None
|
|
29
|
+
_uvicorn_server: uvicorn.Server | None = None
|
|
30
|
+
_loader = GraphLoader()
|
|
21
31
|
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
port = s.getsockname()[1]
|
|
29
|
-
s.close()
|
|
30
|
-
return port
|
|
33
|
+
@dataclass
|
|
34
|
+
class ServerHandle:
|
|
35
|
+
url: str
|
|
36
|
+
server: uvicorn.Server
|
|
37
|
+
thread: threading.Thread
|
|
31
38
|
|
|
39
|
+
def stop(self, timeout_s: float = 2.0) -> None:
|
|
40
|
+
self.server.should_exit = True
|
|
41
|
+
self.thread.join(timeout=timeout_s)
|
|
32
42
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
uvicorn.Config(app, host=host, port=port, log_level=log_level, loop="asyncio")
|
|
38
|
-
)
|
|
43
|
+
def block(self) -> None:
|
|
44
|
+
# Loop with a timeout allows Python to process signals (like Ctrl+C)
|
|
45
|
+
while self.thread.is_alive():
|
|
46
|
+
self.thread.join(timeout=1.0)
|
|
39
47
|
|
|
40
|
-
async def runner():
|
|
41
|
-
task = asyncio.create_task(server.serve())
|
|
42
|
-
while not _shutdown_flag.is_set():
|
|
43
|
-
await asyncio.sleep(0.2)
|
|
44
|
-
if not server.should_exit:
|
|
45
|
-
server.should_exit = True
|
|
46
|
-
await task
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
def _make_uvicorn_server(app: FastAPI, host: str, port: int, log_level: str) -> uvicorn.Server:
|
|
50
|
+
"""
|
|
51
|
+
Create a uvicorn.Server we can stop via server.should_exit = True.
|
|
52
|
+
(Safe for background thread.)
|
|
53
|
+
"""
|
|
54
|
+
config = uvicorn.Config(
|
|
55
|
+
app=app,
|
|
56
|
+
host=host,
|
|
57
|
+
port=port,
|
|
58
|
+
log_level=log_level,
|
|
59
|
+
lifespan="on",
|
|
60
|
+
loop="asyncio",
|
|
61
|
+
)
|
|
62
|
+
server = uvicorn.Server(config=config)
|
|
63
|
+
server.install_signal_handlers = lambda: None # type: ignore
|
|
64
|
+
return server
|
|
53
65
|
|
|
54
66
|
|
|
55
67
|
def start_server(
|
|
56
68
|
*,
|
|
57
69
|
workspace: str = "./aethergraph_data",
|
|
58
70
|
host: str = "127.0.0.1",
|
|
59
|
-
port: int =
|
|
71
|
+
port: int = 8745, # 0 = auto free port
|
|
60
72
|
log_level: str = "warning",
|
|
61
73
|
unvicorn_log_level: str = "warning",
|
|
62
74
|
return_container: bool = False,
|
|
63
|
-
|
|
75
|
+
return_handle: bool = False,
|
|
76
|
+
load_modules: list[str] | None = None,
|
|
77
|
+
load_paths: list[str] | None = None,
|
|
78
|
+
project_root: str | None = None,
|
|
79
|
+
strict_load: bool = False,
|
|
80
|
+
) -> str | tuple[str, Any] | tuple[str, ServerHandle] | tuple[str, Any, ServerHandle]:
|
|
64
81
|
"""
|
|
65
|
-
Start the AetherGraph sidecar server in a
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
Start (or reuse) the AetherGraph sidecar server in a normalized and flexible way.
|
|
83
|
+
|
|
84
|
+
This method manages server lifecycle, workspace locking, and dynamic loading of user code.
|
|
85
|
+
It supports both in-process and cross-process server reuse, and can return handles for
|
|
86
|
+
advanced control or integration.
|
|
87
|
+
|
|
88
|
+
Examples:
|
|
89
|
+
Basic usage to start a server and get its URL:
|
|
90
|
+
```python
|
|
91
|
+
url = start_server(workspace="./aethergraph_data", port=0)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Loading user graphs before starting:
|
|
95
|
+
```python
|
|
96
|
+
url = start_server(
|
|
97
|
+
workspace="./aethergraph_data",
|
|
98
|
+
port=0,
|
|
99
|
+
load_paths=["./my_graphs.py"],
|
|
100
|
+
project_root=".",
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Starting and blocking until server exit (notebook/script mode):
|
|
105
|
+
```python
|
|
106
|
+
url, handle = start_server(workspace="./aethergraph_data", port=0, return_handle=True)
|
|
107
|
+
print("Server running at", url)
|
|
108
|
+
try:
|
|
109
|
+
handle.block()
|
|
110
|
+
except KeyboardInterrupt:
|
|
111
|
+
print("Stopping server...")
|
|
112
|
+
handle.stop()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Returning the dependency injection container for advanced use:
|
|
116
|
+
```python
|
|
117
|
+
url, container = start_server(workspace="./aethergraph_data", return_container=True)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Returning both container and handle:
|
|
121
|
+
```python
|
|
122
|
+
url, container, handle = start_server(
|
|
123
|
+
workspace="./aethergraph_data",
|
|
124
|
+
return_container=True,
|
|
125
|
+
return_handle=True,
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
workspace: Persistent storage directory for server state and data.
|
|
131
|
+
host: Host address to bind the server (default "127.0.0.1").
|
|
132
|
+
port: Port to bind the server (0 for auto-pick, or specify a fixed port).
|
|
133
|
+
log_level: Logging level for the application.
|
|
134
|
+
unvicorn_log_level: Logging level for the Uvicorn server.
|
|
135
|
+
return_container: If True, also return the app's dependency injection container.
|
|
136
|
+
return_handle: If True, return a ServerHandle for programmatic control (block/stop).
|
|
137
|
+
load_modules: List of Python modules to import before server start.
|
|
138
|
+
load_paths: List of Python file paths to import before server start.
|
|
139
|
+
project_root: Path to add to sys.path for module resolution during loading.
|
|
140
|
+
strict_load: If True, raise on import/load errors; otherwise, record errors in loader report.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
str: The server URL (e.g., "http://127.0.0.1:53421").
|
|
144
|
+
tuple: Optionally, (url, container), (url, handle), or (url, container, handle)
|
|
145
|
+
depending on the flags set and whether the server was started in-process.
|
|
146
|
+
|
|
147
|
+
Notes:
|
|
148
|
+
- Workspace is a dedicated directory for server data, including logs, caches, and runtime state; multiple processes using the same
|
|
149
|
+
workspace will coordinate to reuse a single server instance. Delete the workspace to reset state.
|
|
150
|
+
- Use handle.block() to wait for server exit when you need to keep the server running in a script or notebook. This is typically not needed
|
|
151
|
+
when using the server in client mode.
|
|
152
|
+
- When you are using Aethergraph UI, use handle.block() to keep the server running so that the UI can connect to it and discover agents/apps.
|
|
68
153
|
"""
|
|
69
|
-
global _started, _server_thread, _url
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
t = threading.Thread(
|
|
81
|
-
target=_run_uvicorn_in_thread,
|
|
82
|
-
args=(app, host, picked_port, unvicorn_log_level),
|
|
83
|
-
name="aethergraph-sidecar",
|
|
84
|
-
daemon=True,
|
|
85
|
-
)
|
|
86
|
-
t.start()
|
|
154
|
+
global _started, _server_thread, _url, _uvicorn_server
|
|
155
|
+
|
|
156
|
+
# In-process fast path
|
|
157
|
+
if _started and _url:
|
|
158
|
+
if return_container or return_handle:
|
|
159
|
+
# We can return these because we're in-process
|
|
160
|
+
# (container is attached to app only when we start it; see below)
|
|
161
|
+
pass
|
|
162
|
+
else:
|
|
163
|
+
print(" - reusing existing in-process server at", _url)
|
|
164
|
+
return _url
|
|
87
165
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
166
|
+
print(" - acquiring workspace lock...")
|
|
167
|
+
# Cross-process coordination: one workspace => one server
|
|
168
|
+
with workspace_lock(workspace):
|
|
169
|
+
running_url = get_running_url_if_any(workspace)
|
|
170
|
+
if running_url:
|
|
171
|
+
# Reuse the already-running sidecar for this workspace
|
|
172
|
+
print(" - reusing existing sidecar server at", running_url)
|
|
173
|
+
_started = True
|
|
174
|
+
_url = running_url
|
|
175
|
+
# Cross-process: we cannot return container/handle
|
|
176
|
+
return running_url
|
|
91
177
|
|
|
92
|
-
|
|
178
|
+
# Load graphs BEFORE server start so /apps, /agents are populated immediately
|
|
179
|
+
spec = LoadSpec(
|
|
180
|
+
modules=load_modules or [],
|
|
181
|
+
paths=load_paths or [],
|
|
182
|
+
project_root=project_root,
|
|
183
|
+
strict=strict_load,
|
|
184
|
+
)
|
|
93
185
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
186
|
+
print(" Loading user graphs with spec:", spec)
|
|
187
|
+
if spec.modules or spec.paths:
|
|
188
|
+
report = _loader.load(spec)
|
|
189
|
+
# Optional: stash report for debugging. We'll attach it to app below.
|
|
190
|
+
_loader.last_report = report
|
|
191
|
+
|
|
192
|
+
# Build app (installs services inside create_app)
|
|
193
|
+
cfg = load_settings()
|
|
194
|
+
set_current_settings(cfg)
|
|
195
|
+
app = create_app(workspace=workspace, cfg=cfg, log_level=log_level)
|
|
196
|
+
# Optional debug info
|
|
197
|
+
app.state.last_load_report = getattr(_loader, "last_report", None)
|
|
198
|
+
|
|
199
|
+
picked_port = pick_free_port(port)
|
|
200
|
+
url = f"http://{host}:{picked_port}"
|
|
201
|
+
|
|
202
|
+
# Create stoppable server object
|
|
203
|
+
server = _make_uvicorn_server(app, host, picked_port, unvicorn_log_level)
|
|
204
|
+
|
|
205
|
+
def _target():
|
|
206
|
+
server.run()
|
|
207
|
+
|
|
208
|
+
t = threading.Thread(
|
|
209
|
+
target=_target,
|
|
210
|
+
name="aethergraph-sidecar",
|
|
211
|
+
daemon=True,
|
|
212
|
+
)
|
|
213
|
+
t.start()
|
|
214
|
+
|
|
215
|
+
# Update globals
|
|
216
|
+
_server_thread = t
|
|
217
|
+
_uvicorn_server = server
|
|
218
|
+
_started = True
|
|
219
|
+
_url = url
|
|
220
|
+
|
|
221
|
+
# Write server.json for discovery
|
|
222
|
+
write_server_state(
|
|
223
|
+
workspace,
|
|
224
|
+
{
|
|
225
|
+
"pid": os.getpid(),
|
|
226
|
+
"host": host,
|
|
227
|
+
"port": picked_port,
|
|
228
|
+
"url": url,
|
|
229
|
+
"workspace": str(Path(workspace).resolve()),
|
|
230
|
+
"started_at": time.time(),
|
|
231
|
+
},
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
print("\n" + "=" * 50)
|
|
235
|
+
# We align the labels to 18 characters (the length of the longest label)
|
|
236
|
+
print(f"[AetherGraph] 🚀 {'Server started at:':<18} {url}")
|
|
237
|
+
print(
|
|
238
|
+
f"[AetherGraph] 🖥️ {'UI:':<18} {url}/ui (if built)"
|
|
239
|
+
) # strangly, this needs two spaces unlike the rest
|
|
240
|
+
print(f"[AetherGraph] 📡 {'API:':<18} {url}/api/v1/")
|
|
241
|
+
print(f"[AetherGraph] 📂 {'Workspace:':<18} {workspace}")
|
|
242
|
+
print("=" * 50 + "\n")
|
|
243
|
+
|
|
244
|
+
handle = ServerHandle(url=url, server=server, thread=t)
|
|
245
|
+
|
|
246
|
+
if return_container and return_handle:
|
|
247
|
+
return url, app.state.container, handle
|
|
248
|
+
if return_container:
|
|
249
|
+
return url, app.state.container
|
|
250
|
+
if return_handle:
|
|
251
|
+
return url, handle
|
|
252
|
+
|
|
253
|
+
return url
|
|
97
254
|
|
|
98
255
|
|
|
99
256
|
async def start_server_async(**kw) -> str:
|
|
100
257
|
# Async-friendly wrapper; still uses a thread to avoid clashing with caller loop
|
|
101
|
-
return start_server(**kw)
|
|
258
|
+
return start_server(**kw) # type: ignore[return-value]
|
|
102
259
|
|
|
103
260
|
|
|
104
261
|
def stop_server():
|
|
105
|
-
"""
|
|
106
|
-
global _started, _server_thread, _url
|
|
262
|
+
"""Stop the in-process background server (useful in tests/notebooks)."""
|
|
263
|
+
global _started, _server_thread, _url, _uvicorn_server
|
|
107
264
|
if not _started:
|
|
108
265
|
return
|
|
109
|
-
|
|
266
|
+
|
|
267
|
+
if _uvicorn_server is not None:
|
|
268
|
+
_uvicorn_server.should_exit = True
|
|
269
|
+
|
|
110
270
|
if _server_thread and _server_thread.is_alive():
|
|
111
271
|
with contextlib.suppress(Exception):
|
|
112
272
|
_server_thread.join(timeout=5)
|
|
273
|
+
|
|
113
274
|
_started = False
|
|
114
275
|
_server_thread = None
|
|
276
|
+
_uvicorn_server = None
|
|
115
277
|
_url = None
|
|
116
|
-
_shutdown_flag.clear()
|
|
117
278
|
|
|
118
279
|
|
|
119
280
|
# backward compatibility
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|