feat: 引入 Rust executor/gateway sidecar 及 Python 侧双后端适配

- 新增 Rust workspace crates: aether-contracts, aether-executor, aether-gateway
- aether-executor: 支持 Unix Socket/TCP 双传输模式,处理同步/流式上游请求
- aether-gateway: 作为本地主入口代理,集成 /api/internal/gateway/resolve 认证预解析
- Python 侧新增 ExecutionPlan 契约和 RustExecutorClient,各 handler 支持
  executor_backend=rust 时将可序列化请求转发给 Rust executor 执行
- 重构 dev.sh 支持 executor/gateway 进程编排与生命周期管理
- 新增 internal gateway 路由,提供 resolve/passthrough 端点
- handler 层(chat/cli/video/endpoint_checker 等)全面适配 Rust executor 回退逻辑
- pipeline 层支持 trusted auth context 跳过重复认证
- 新增 Rust CI workflow 及对应测试用例
This commit is contained in:
fawney19
2026-03-21 12:57:09 +08:00
parent 46737d32f8
commit d735b6316f
79 changed files with 19032 additions and 522 deletions

View File

@@ -0,0 +1,142 @@
from __future__ import annotations
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock
import httpx
import pytest
def _build_provider_fixture() -> SimpleNamespace:
endpoint = SimpleNamespace(
id="endpoint_1",
base_url="https://upstream.test",
api_format="openai:chat",
is_active=True,
)
key = SimpleNamespace(
id="key_1",
is_active=True,
api_formats=None,
)
return SimpleNamespace(
id="provider_1",
name="openai",
endpoints=[endpoint],
api_keys=[key],
)
@pytest.mark.asyncio
async def test_test_connection_prefers_rust_executor(
monkeypatch: pytest.MonkeyPatch,
) -> None:
from src.api.public import system_catalog as mod
provider = _build_provider_fixture()
monkeypatch.setattr(mod, "_select_provider", lambda _db, _provider_name: provider)
monkeypatch.setattr(mod, "build_test_request_body", lambda *_args, **_kwargs: {"model": "gpt-test"})
monkeypatch.setattr(mod, "get_provider_auth", AsyncMock(return_value=None))
class _DummyBuilder:
def build(self, *_args: object, **_kwargs: object) -> tuple[dict[str, str], dict[str, str]]:
return {"model": "gpt-test"}, {"authorization": "Bearer test"}
monkeypatch.setattr(mod, "PassthroughRequestBuilder", lambda: _DummyBuilder())
monkeypatch.setattr(
mod,
"build_provider_url",
lambda *_args, **_kwargs: "https://upstream.test/v1/chat/completions",
)
proxy_snapshot = object()
monkeypatch.setattr(
mod,
"_build_test_connection_transport_context",
AsyncMock(return_value=({"enabled": True}, {"node_id": "node-1", "tunnel": True}, proxy_snapshot)),
)
rust_response = httpx.Response(
200,
request=httpx.Request("POST", "https://upstream.test/v1/chat/completions"),
json={"id": "resp_rust"},
)
rust_call = AsyncMock(return_value=rust_response)
monkeypatch.setattr(mod, "_try_rust_test_connection_response", rust_call)
get_upstream_client = AsyncMock(side_effect=AssertionError("python upstream client should not be used"))
monkeypatch.setattr(mod.HTTPClientPool, "get_upstream_client", get_upstream_client)
result = await mod.test_connection(
request=SimpleNamespace(query_params={}),
db=MagicMock(),
provider=None,
model="gpt-test",
api_format=None,
)
assert result["status"] == "success"
assert result["response_id"] == "resp_rust"
rust_call.assert_awaited_once()
get_upstream_client.assert_not_awaited()
@pytest.mark.asyncio
async def test_test_connection_fallback_uses_transport_aware_client(
monkeypatch: pytest.MonkeyPatch,
) -> None:
from src.api.public import system_catalog as mod
provider = _build_provider_fixture()
monkeypatch.setattr(mod, "_select_provider", lambda _db, _provider_name: provider)
monkeypatch.setattr(mod, "build_test_request_body", lambda *_args, **_kwargs: {"model": "gpt-test"})
monkeypatch.setattr(mod, "get_provider_auth", AsyncMock(return_value=None))
class _DummyBuilder:
def build(self, *_args: object, **_kwargs: object) -> tuple[dict[str, str], dict[str, str]]:
return {"model": "gpt-test"}, {"authorization": "Bearer test"}
monkeypatch.setattr(mod, "PassthroughRequestBuilder", lambda: _DummyBuilder())
monkeypatch.setattr(
mod,
"build_provider_url",
lambda *_args, **_kwargs: "https://upstream.test/v1/chat/completions",
)
proxy_config = {"enabled": True, "url": "http://proxy.test:8080"}
delegate_cfg = {"node_id": "node-1", "tunnel": True}
monkeypatch.setattr(
mod,
"_build_test_connection_transport_context",
AsyncMock(return_value=(proxy_config, delegate_cfg, None)),
)
monkeypatch.setattr(mod, "_try_rust_test_connection_response", AsyncMock(return_value=None))
upstream_response = httpx.Response(
200,
request=httpx.Request("POST", "https://upstream.test/v1/chat/completions"),
json={"id": "resp_python"},
)
upstream_client = MagicMock()
upstream_client.post = AsyncMock(return_value=upstream_response)
get_upstream_client = AsyncMock(return_value=upstream_client)
monkeypatch.setattr(mod.HTTPClientPool, "get_upstream_client", get_upstream_client)
result = await mod.test_connection(
request=SimpleNamespace(query_params={}),
db=MagicMock(),
provider=None,
model="gpt-test",
api_format=None,
)
assert result["status"] == "success"
assert result["response_id"] == "resp_python"
get_upstream_client.assert_awaited_once_with(delegate_cfg, proxy_config=proxy_config)
upstream_client.post.assert_awaited_once_with(
"https://upstream.test/v1/chat/completions",
json={"model": "gpt-test"},
headers={"authorization": "Bearer test"},
)