mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-09 04:30:20 +08:00
feat(tunnel): 请求体流式传输 & OpenAI CLI 请求 key 排序优化
Hub 端: - open_local_stream 不再接收 body 参数,改为通过 push_local_request_body 分块推送 - 请求体按 32KB 分帧发送,避免大请求一次性压缩和传输 - local_relay 改为流式解析 envelope 和转发请求体 Proxy 端: - stream_handler 改为流式传输请求体到上游,不再预先收集完整 body - upstream_client 请求体类型从 Full<Bytes> 改为 UnsyncBoxBody 以支持流式传输 - dispatcher 将 StreamEnd/StreamError 事件转发给 stream handler Python 端: - hub_transport relay envelope 改为异步生成器流式发送 - 提取 reorder_openai_cli_request_prefix_keys 为公共函数 - Codex passthrough 路径也应用稳定的前缀 key 排序
This commit is contained in:
@@ -58,6 +58,31 @@ def test_patch_openai_cli_request_for_codex_preserves_existing_prompt_cache_key(
|
||||
assert out["prompt_cache_key"] == "client-cache-key"
|
||||
|
||||
|
||||
def test_patch_openai_cli_request_for_codex_reorders_stable_prefix_keys() -> None:
|
||||
req = {
|
||||
"temperature": 0.7,
|
||||
"input": [],
|
||||
"metadata": {"request_id": "abc"},
|
||||
"model": "gpt-test",
|
||||
"tools": [{"type": "function", "name": "demo"}],
|
||||
"instructions": "keep",
|
||||
"store": True,
|
||||
"_aether_compact": True,
|
||||
}
|
||||
|
||||
out = patch_openai_cli_request_for_codex(req)
|
||||
|
||||
assert list(out.keys()) == [
|
||||
"model",
|
||||
"instructions",
|
||||
"tools",
|
||||
"input",
|
||||
"temperature",
|
||||
"metadata",
|
||||
"store",
|
||||
]
|
||||
|
||||
|
||||
def test_patch_openai_cli_request_for_codex_does_not_inject_prompt_cache_key() -> None:
|
||||
req = {"model": "gpt-test", "input": [], "_aether_compact": True}
|
||||
|
||||
@@ -151,6 +176,34 @@ def test_openai_cli_normalizer_codex_variant_keeps_instructions_missing_for_defa
|
||||
assert patched["instructions"] == "You are GPT-5."
|
||||
|
||||
|
||||
def test_openai_cli_normalizer_patch_for_codex_reorders_stable_prefix_keys() -> None:
|
||||
from src.core.api_format.conversion.normalizers.openai_cli import OpenAICliNormalizer
|
||||
|
||||
normalizer = OpenAICliNormalizer()
|
||||
out = normalizer.patch_for_variant(
|
||||
{
|
||||
"temperature": 0.7,
|
||||
"input": [],
|
||||
"metadata": {"request_id": "abc"},
|
||||
"model": "gpt-test",
|
||||
"tools": [{"type": "function", "name": "demo"}],
|
||||
"instructions": "keep",
|
||||
"_aether_compact": True,
|
||||
},
|
||||
"codex",
|
||||
)
|
||||
|
||||
assert out is not None
|
||||
assert list(out.keys()) == [
|
||||
"model",
|
||||
"instructions",
|
||||
"tools",
|
||||
"input",
|
||||
"temperature",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
|
||||
def test_codex_envelope_extra_headers_does_not_inject_synthetic_headers() -> None:
|
||||
from src.services.provider.adapters.codex.envelope import codex_oauth_envelope
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class _FakeRelayClient:
|
||||
def __init__(self, response: httpx.Response) -> None:
|
||||
self.response = response
|
||||
self.sent_request: httpx.Request | None = None
|
||||
self.sent_body: bytes | None = None
|
||||
|
||||
def build_request(self, method: str, url: str, **kwargs: Any) -> httpx.Request:
|
||||
return httpx.Request(method, url, **kwargs)
|
||||
@@ -22,6 +23,7 @@ class _FakeRelayClient:
|
||||
async def send(self, request: httpx.Request, *, stream: bool = False) -> httpx.Response:
|
||||
_ = stream
|
||||
self.sent_request = request
|
||||
self.sent_body = await request.aread()
|
||||
self.response.request = request
|
||||
return self.response
|
||||
|
||||
@@ -56,7 +58,7 @@ async def test_transport_encodes_local_relay_envelope(monkeypatch: pytest.Monkey
|
||||
await response.aclose()
|
||||
|
||||
assert fake_client.sent_request is not None
|
||||
payload = fake_client.sent_request.content
|
||||
payload = fake_client.sent_body
|
||||
assert payload is not None
|
||||
meta_len = struct.unpack("!I", payload[:4])[0]
|
||||
meta = json.loads(payload[4 : 4 + meta_len].decode("utf-8"))
|
||||
@@ -88,3 +90,33 @@ async def test_transport_maps_relay_timeout_to_read_timeout(
|
||||
|
||||
with pytest.raises(httpx.ReadTimeout, match="relay timed out"):
|
||||
await transport.handle_async_request(request)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_transport_streams_request_body_from_async_generator(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
transport = HubTunnelTransport("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(transport, "_relay_client", fake_client)
|
||||
|
||||
async def body() -> Any:
|
||||
yield b'{"hello":'
|
||||
yield b'"world"}'
|
||||
|
||||
request = httpx.Request(
|
||||
"POST",
|
||||
"https://example.com/v1/chat/completions",
|
||||
headers={"content-type": "application/json"},
|
||||
content=body(),
|
||||
)
|
||||
|
||||
response = await transport.handle_async_request(request)
|
||||
assert response.status_code == 200
|
||||
await response.aclose()
|
||||
|
||||
payload = fake_client.sent_body
|
||||
assert payload is not None
|
||||
meta_len = struct.unpack("!I", payload[:4])[0]
|
||||
assert payload[4 + meta_len :] == b'{"hello":"world"}'
|
||||
|
||||
Reference in New Issue
Block a user