mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
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:
103
tests/api/handlers/openai/test_video_rust_sync.py
Normal file
103
tests/api/handlers/openai/test_video_rust_sync.py
Normal file
@@ -0,0 +1,103 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
import src.api.handlers.openai.video_handler as video_mod
|
||||
from src.api.handlers.openai.video_handler import OpenAIVideoHandler
|
||||
|
||||
|
||||
def _make_handler() -> OpenAIVideoHandler:
|
||||
return OpenAIVideoHandler(
|
||||
db=SimpleNamespace(),
|
||||
user=SimpleNamespace(id="user-1"),
|
||||
api_key=SimpleNamespace(id="api-key-1"),
|
||||
request_id="req-video-sync-test",
|
||||
client_ip="127.0.0.1",
|
||||
user_agent="pytest",
|
||||
start_time=0.0,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_create_task_uses_rust_sync_helper(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
handler = _make_handler()
|
||||
monkeypatch.setattr(video_mod.UsageService, "create_pending_usage", lambda *args, **kwargs: None)
|
||||
monkeypatch.setattr(
|
||||
handler._normalizer,
|
||||
"video_request_to_internal",
|
||||
lambda body: SimpleNamespace(model=str(body.get("model") or "sora-2")),
|
||||
)
|
||||
|
||||
candidate = SimpleNamespace(provider=SimpleNamespace(name="provider-1", id="prov-1"))
|
||||
endpoint = SimpleNamespace(
|
||||
id="ep-1",
|
||||
api_family="openai",
|
||||
endpoint_kind="video",
|
||||
base_url="https://api.openai.com",
|
||||
body_rules=None,
|
||||
)
|
||||
provider_key = SimpleNamespace(id="key-1")
|
||||
|
||||
monkeypatch.setattr(
|
||||
handler,
|
||||
"_resolve_upstream_key",
|
||||
AsyncMock(return_value=("upstream-key", endpoint, provider_key)),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
handler,
|
||||
"_build_upstream_url",
|
||||
lambda base_url: "https://api.openai.com/v1/videos",
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
handler,
|
||||
"_build_upstream_headers",
|
||||
lambda original_headers, upstream_key, endpoint, **kwargs: {
|
||||
"authorization": f"Bearer {upstream_key}"
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
video_mod.HTTPClientPool,
|
||||
"get_default_client_async",
|
||||
AsyncMock(side_effect=AssertionError("python fallback should not run")),
|
||||
)
|
||||
|
||||
async def _fake_rust_sync(**kwargs: object) -> httpx.Response:
|
||||
assert kwargs["method"] == "POST"
|
||||
assert kwargs["url"] == "https://api.openai.com/v1/videos"
|
||||
assert kwargs["provider_id"] == "prov-1"
|
||||
assert kwargs["endpoint_id"] == "ep-1"
|
||||
assert kwargs["key_id"] == "key-1"
|
||||
assert kwargs["body"] == {"model": "sora-2", "prompt": "hello"}
|
||||
return httpx.Response(
|
||||
200,
|
||||
request=httpx.Request("POST", str(kwargs["url"])),
|
||||
json={"id": "ext-1"},
|
||||
)
|
||||
|
||||
create_failed = AsyncMock()
|
||||
monkeypatch.setattr(handler, "_try_rust_sync_http_response", _fake_rust_sync)
|
||||
monkeypatch.setattr(handler, "_create_failed_task_and_usage", create_failed)
|
||||
|
||||
async def _fake_submit_with_failover(**kwargs: object) -> JSONResponse:
|
||||
response = await kwargs["submit_func"](candidate)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["id"] == "ext-1"
|
||||
return JSONResponse(status_code=400, content={"error": {"message": "stop"}})
|
||||
|
||||
monkeypatch.setattr(handler, "_submit_with_failover", _fake_submit_with_failover)
|
||||
|
||||
response = await handler.handle_create_task(
|
||||
http_request=SimpleNamespace(),
|
||||
original_headers={},
|
||||
original_request_body={"model": "sora-2", "prompt": "hello"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
create_failed.assert_awaited_once()
|
||||
Reference in New Issue
Block a user