feat(codex): 基于用户 API key 生成稳定的 prompt_cache_key,实现跨 provider key 的 prompt 缓存复用

- prepare_context 传递 user_api_key_id 到 CodexRequestContext
- wrap_request 中调用 patch_openai_cli_request_for_codex 注入 prompt_cache_key
- 客户端已提供 prompt_cache_key 时不覆盖
- 补充对应单元测试
This commit is contained in:
fawney19
2026-03-16 17:25:52 +08:00
parent c070e5a9f6
commit c97c9332eb
9 changed files with 156 additions and 8 deletions

View File

@@ -6,7 +6,9 @@ import jwt
import pytest
from src.api.handlers.base.request_builder import PassthroughRequestBuilder
from src.services.provider.adapters.codex.context import set_codex_request_context
from src.services.provider.adapters.codex.request_patching import (
build_stable_codex_prompt_cache_key,
maybe_patch_request_for_codex,
patch_openai_cli_request_for_codex,
)
@@ -47,6 +49,37 @@ def test_patch_openai_cli_request_for_codex_is_passthrough_except_internal_senti
assert out["input"][0]["role"] == "system"
def test_patch_openai_cli_request_for_codex_generates_stable_prompt_cache_key_from_user_api_key_id() -> (
None
):
req = {"model": "gpt-test", "input": []}
out = patch_openai_cli_request_for_codex(req, user_api_key_id="user-key-123")
assert out is not req
assert out["prompt_cache_key"] == build_stable_codex_prompt_cache_key("user-key-123")
def test_patch_openai_cli_request_for_codex_preserves_existing_prompt_cache_key() -> None:
req = {"model": "gpt-test", "input": [], "prompt_cache_key": "client-cache-key"}
out = patch_openai_cli_request_for_codex(req, user_api_key_id="user-key-123")
assert out["prompt_cache_key"] == "client-cache-key"
def test_patch_openai_cli_request_for_codex_ignores_provider_key_and_uses_only_user_api_key_id() -> (
None
):
req = {"model": "gpt-test", "input": []}
out_a = patch_openai_cli_request_for_codex(req, user_api_key_id="user-key-123")
out_b = patch_openai_cli_request_for_codex(req, user_api_key_id="user-key-123")
assert out_a["prompt_cache_key"] == out_b["prompt_cache_key"]
assert out_a["prompt_cache_key"] == build_stable_codex_prompt_cache_key("user-key-123")
def test_maybe_patch_request_for_codex_is_noop_for_non_codex() -> None:
req = {"model": "gpt-test", "input": []}
out = maybe_patch_request_for_codex(
@@ -119,6 +152,66 @@ def test_codex_envelope_extra_headers_does_not_inject_synthetic_headers() -> Non
assert codex_oauth_envelope.extra_headers() is None
def test_codex_envelope_wrap_request_injects_stable_prompt_cache_key_from_user_api_key() -> None:
from src.services.provider.adapters.codex.envelope import codex_oauth_envelope
try:
codex_oauth_envelope.prepare_context(
provider_config=None,
key_id="provider-key-123",
user_api_key_id="user-key-123",
is_stream=True,
)
out, url_model = codex_oauth_envelope.wrap_request(
{"model": "gpt-test", "input": []},
model="gpt-test",
url_model=None,
decrypted_auth_config=None,
)
finally:
set_codex_request_context(None)
assert url_model is None
assert out["prompt_cache_key"] == build_stable_codex_prompt_cache_key("user-key-123")
def test_codex_envelope_wrap_request_same_user_different_provider_keys_share_prompt_cache_key() -> (
None
):
from src.services.provider.adapters.codex.envelope import codex_oauth_envelope
try:
codex_oauth_envelope.prepare_context(
provider_config=None,
key_id="provider-key-123",
user_api_key_id="user-key-123",
is_stream=True,
)
out_a, _ = codex_oauth_envelope.wrap_request(
{"model": "gpt-test", "input": []},
model="gpt-test",
url_model=None,
decrypted_auth_config=None,
)
codex_oauth_envelope.prepare_context(
provider_config=None,
key_id="provider-key-456",
user_api_key_id="user-key-123",
is_stream=True,
)
out_b, _ = codex_oauth_envelope.wrap_request(
{"model": "gpt-test", "input": []},
model="gpt-test",
url_model=None,
decrypted_auth_config=None,
)
finally:
set_codex_request_context(None)
assert out_a["prompt_cache_key"] == out_b["prompt_cache_key"]
assert out_a["prompt_cache_key"] == build_stable_codex_prompt_cache_key("user-key-123")
def test_codex_passthrough_builder_preserves_real_codex_headers() -> None:
from src.services.provider.adapters.codex.envelope import codex_oauth_envelope