refactor: 移除独立 hub/proxy/executor/gateway crate,统一为 gateway tunnel 架构

- 删除 aether-hub、aether-proxy 独立项目及其 Dockerfile/配置
- 删除 crates/aether-executor 和 crates/aether-gateway 全部模块
- 新增 apps/ 目录作为应用入口
- 将 hub 概念重构为 gateway tunnel transport
- 将 executor 重构为 execution runtime
- 新增 tunnel.rs 合约定义和 testkit tunnel/execution_runtime 模块
- 更新 Python 服务层和测试适配新架构命名
This commit is contained in:
fawney19
2026-04-03 14:59:58 +08:00
parent ddf18fed9a
commit 8f26e1a31f
983 changed files with 103097 additions and 105836 deletions
+41 -5
View File
@@ -9,6 +9,11 @@ def test_executor_config_defaults_to_rust_and_unix_socket(
monkeypatch: pytest.MonkeyPatch,
) -> None:
for key in (
"EXECUTION_RUNTIME_BACKEND",
"EXECUTION_RUNTIME_TRANSPORT",
"EXECUTION_RUNTIME_SOCKET_PATH",
"EXECUTION_RUNTIME_BASE_URL",
"EXECUTION_RUNTIME_REQUEST_TIMEOUT",
"EXECUTOR_BACKEND",
"EXECUTOR_TRANSPORT",
"EXECUTOR_SOCKET_PATH",
@@ -20,6 +25,11 @@ def test_executor_config_defaults_to_rust_and_unix_socket(
cfg = Config()
assert cfg.execution_runtime_backend == "rust"
assert cfg.execution_runtime_transport == "unix_socket"
assert cfg.execution_runtime_socket_path == "/tmp/aether-executor.sock"
assert cfg.execution_runtime_base_url == "http://127.0.0.1:5219"
assert cfg.execution_runtime_request_timeout == cfg.http_request_timeout
assert cfg.executor_backend == "rust"
assert cfg.executor_transport == "unix_socket"
assert cfg.executor_socket_path == "/tmp/aether-executor.sock"
@@ -28,16 +38,42 @@ def test_executor_config_defaults_to_rust_and_unix_socket(
def test_executor_config_accepts_env_override(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("EXECUTOR_BACKEND", "RUST")
monkeypatch.setenv("EXECUTOR_TRANSPORT", "TCP")
monkeypatch.setenv("EXECUTOR_SOCKET_PATH", "/var/run/aether.sock")
monkeypatch.setenv("EXECUTOR_BASE_URL", "http://127.0.0.1:9311")
monkeypatch.setenv("EXECUTOR_REQUEST_TIMEOUT", "12.5")
monkeypatch.setenv("EXECUTION_RUNTIME_BACKEND", "RUST")
monkeypatch.setenv("EXECUTION_RUNTIME_TRANSPORT", "TCP")
monkeypatch.setenv("EXECUTION_RUNTIME_SOCKET_PATH", "/var/run/aether.sock")
monkeypatch.setenv("EXECUTION_RUNTIME_BASE_URL", "http://127.0.0.1:9311")
monkeypatch.setenv("EXECUTION_RUNTIME_REQUEST_TIMEOUT", "12.5")
cfg = Config()
assert cfg.execution_runtime_backend == "rust"
assert cfg.execution_runtime_transport == "tcp"
assert cfg.execution_runtime_socket_path == "/var/run/aether.sock"
assert cfg.execution_runtime_base_url == "http://127.0.0.1:9311"
assert cfg.execution_runtime_request_timeout == 12.5
assert cfg.executor_backend == "rust"
assert cfg.executor_transport == "tcp"
assert cfg.executor_socket_path == "/var/run/aether.sock"
assert cfg.executor_base_url == "http://127.0.0.1:9311"
assert cfg.executor_request_timeout == 12.5
def test_executor_config_legacy_envs_still_work(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("EXECUTOR_BACKEND", "RUST")
monkeypatch.setenv("EXECUTOR_TRANSPORT", "TCP")
monkeypatch.setenv("EXECUTOR_SOCKET_PATH", "/tmp/legacy-aether.sock")
monkeypatch.setenv("EXECUTOR_BASE_URL", "http://127.0.0.1:9322")
monkeypatch.setenv("EXECUTOR_REQUEST_TIMEOUT", "14.5")
cfg = Config()
assert cfg.execution_runtime_backend == "rust"
assert cfg.execution_runtime_transport == "tcp"
assert cfg.execution_runtime_socket_path == "/tmp/legacy-aether.sock"
assert cfg.execution_runtime_base_url == "http://127.0.0.1:9322"
assert cfg.execution_runtime_request_timeout == 14.5
assert cfg.executor_backend == "rust"
assert cfg.executor_transport == "tcp"
assert cfg.executor_socket_path == "/tmp/legacy-aether.sock"
assert cfg.executor_base_url == "http://127.0.0.1:9322"
assert cfg.executor_request_timeout == 14.5
+5 -5
View File
@@ -2,14 +2,14 @@ from __future__ import annotations
import base64
from src.services.request.executor_plan import PreparedExecutionPlan
from src.services.request.executor_plan import (
from src.services.request.execution_runtime_plan import PreparedExecutionPlan
from src.services.request.execution_runtime_plan import (
ExecutionPlan,
ExecutionPlanBody,
ExecutionPlanTimeouts,
ExecutionProxySnapshot,
build_execution_plan_body,
should_bypass_remote_executor_url,
should_bypass_remote_execution_runtime_url,
)
@@ -374,9 +374,9 @@ def test_prepared_execution_plan_remote_eligible_rejects_codex_cli_transport() -
assert prepared.remote_eligible is False
def test_should_bypass_remote_executor_url_accepts_backendapi_codex_variant() -> None:
def test_should_bypass_remote_execution_runtime_url_accepts_backendapi_codex_variant() -> None:
assert (
should_bypass_remote_executor_url(
should_bypass_remote_execution_runtime_url(
"https://chatgpt.com/backendapi/codex/responses",
provider_api_format="openai:cli",
client_api_format="openai:cli",
@@ -7,8 +7,8 @@ from typing import Any
import httpx
import pytest
from src.services.proxy_node.hub_config import HubConfig
from src.services.proxy_node.hub_transport import HubTunnelTransport
from src.services.proxy_node.gateway_tunnel_transport import GatewayTunnelTransport
from src.services.proxy_node.tunnel_config import TunnelRelayConfig
class _FakeRelayClient:
@@ -31,19 +31,22 @@ class _FakeRelayClient:
return None
def _relay_config() -> HubConfig:
return HubConfig(
def _relay_config() -> TunnelRelayConfig:
return TunnelRelayConfig(
enabled=True,
url="http://127.0.0.1:8085",
url="http://127.0.0.1:8084",
connect_timeout_seconds=1.0,
)
@pytest.mark.asyncio
async def test_transport_encodes_local_relay_envelope(monkeypatch: pytest.MonkeyPatch) -> None:
transport = HubTunnelTransport("node-1", timeout=12.0)
transport = GatewayTunnelTransport("node-1", timeout=12.0)
fake_client = _FakeRelayClient(httpx.Response(200, content=b"ok"))
monkeypatch.setattr("src.services.proxy_node.hub_transport.get_hub_config", _relay_config)
monkeypatch.setattr(
"src.services.proxy_node.gateway_tunnel_transport.get_tunnel_relay_config",
_relay_config,
)
monkeypatch.setattr(transport, "_relay_client", fake_client)
request = httpx.Request(
@@ -75,7 +78,7 @@ async def test_transport_encodes_local_relay_envelope(monkeypatch: pytest.Monkey
async def test_transport_maps_relay_timeout_to_read_timeout(
monkeypatch: pytest.MonkeyPatch,
) -> None:
transport = HubTunnelTransport("node-1", timeout=12.0)
transport = GatewayTunnelTransport("node-1", timeout=12.0)
fake_client = _FakeRelayClient(
httpx.Response(
504,
@@ -83,7 +86,10 @@ async def test_transport_maps_relay_timeout_to_read_timeout(
content=b"relay timed out",
)
)
monkeypatch.setattr("src.services.proxy_node.hub_transport.get_hub_config", _relay_config)
monkeypatch.setattr(
"src.services.proxy_node.gateway_tunnel_transport.get_tunnel_relay_config",
_relay_config,
)
monkeypatch.setattr(transport, "_relay_client", fake_client)
request = httpx.Request("GET", "https://example.com")
@@ -96,9 +102,12 @@ async def test_transport_maps_relay_timeout_to_read_timeout(
async def test_transport_streams_request_body_from_async_generator(
monkeypatch: pytest.MonkeyPatch,
) -> None:
transport = HubTunnelTransport("node-1", timeout=12.0)
transport = GatewayTunnelTransport("node-1", timeout=12.0)
fake_client = _FakeRelayClient(httpx.Response(200, content=b"ok"))
monkeypatch.setattr("src.services.proxy_node.hub_transport.get_hub_config", _relay_config)
monkeypatch.setattr(
"src.services.proxy_node.gateway_tunnel_transport.get_tunnel_relay_config",
_relay_config,
)
monkeypatch.setattr(transport, "_relay_client", fake_client)
async def body() -> Any:
@@ -2,7 +2,7 @@ from __future__ import annotations
import pytest
from src.services.proxy_node.hub_transport import HubRelayResponseStream
from src.services.proxy_node.gateway_tunnel_transport import TunnelRelayResponseStream
class _FakeResponse:
@@ -19,9 +19,9 @@ class _FakeResponse:
@pytest.mark.asyncio
async def test_hub_relay_response_stream_closes_after_iteration() -> None:
async def test_tunnel_relay_response_stream_closes_after_iteration() -> None:
response = _FakeResponse([b"hello", b"world"])
stream = HubRelayResponseStream(response) # type: ignore[arg-type]
stream = TunnelRelayResponseStream(response) # type: ignore[arg-type]
chunks = []
async for chunk in stream:
@@ -32,9 +32,9 @@ async def test_hub_relay_response_stream_closes_after_iteration() -> None:
@pytest.mark.asyncio
async def test_hub_relay_response_stream_aclose_closes_response() -> None:
async def test_tunnel_relay_response_stream_aclose_closes_response() -> None:
response = _FakeResponse([])
stream = HubRelayResponseStream(response) # type: ignore[arg-type]
stream = TunnelRelayResponseStream(response) # type: ignore[arg-type]
await stream.aclose()
-14
View File
@@ -1,14 +0,0 @@
from src.services.proxy_node.hub_config import HubConfig
def test_local_relay_url_uses_http_path() -> None:
config = HubConfig(
enabled=True,
url="http://127.0.0.1:8085",
connect_timeout_seconds=1.0,
)
assert (
config.local_relay_url("node a/1")
== "http://127.0.0.1:8085/local/relay/node%20a%2F1"
)
+14
View File
@@ -0,0 +1,14 @@
from src.services.proxy_node.tunnel_config import TunnelRelayConfig
def test_local_relay_url_uses_http_path() -> None:
config = TunnelRelayConfig(
enabled=True,
url="http://127.0.0.1:8084",
connect_timeout_seconds=1.0,
)
assert (
config.local_relay_url("node a/1")
== "http://127.0.0.1:8084/api/internal/tunnel/relay/node%20a%2F1"
)
@@ -1,11 +1,11 @@
from src.services.proxy_node.hub_transport import HubTunnelTransport
from src.services.proxy_node.gateway_tunnel_transport import GatewayTunnelTransport
from src.services.proxy_node.tunnel_protocol import Frame, MsgType
from src.services.proxy_node.tunnel_transport import create_tunnel_transport
def test_create_tunnel_transport_always_uses_hub_transport() -> None:
def test_create_tunnel_transport_always_uses_gateway_tunnel_transport() -> None:
transport = create_tunnel_transport("node-1", timeout=12.0)
assert isinstance(transport, HubTunnelTransport)
assert isinstance(transport, GatewayTunnelTransport)
def test_tunnel_protocol_supports_node_status_msg_type() -> None: