refactor(prompt-cache): 移除 client_family 命名空间拆分,统一缓存 key 提升复用率

不再按 User-Agent 客户端类型拆分 prompt cache namespace,
所有客户端共享同一缓存 key,版本升级至 v3。
This commit is contained in:
fawney19
2026-03-17 09:49:35 +08:00
parent 438f16094f
commit 2dcf8b8414
2 changed files with 44 additions and 23 deletions

View File

@@ -11,6 +11,7 @@ from src.core.provider_types import ProviderType, normalize_provider_type
from src.utils.url_utils import is_official_openai_api_url from src.utils.url_utils import is_official_openai_api_url
_OFFICIAL_OPENAI_PROMPT_CACHE_FORMATS: frozenset[str] = frozenset({"openai:chat", "openai:cli"}) _OFFICIAL_OPENAI_PROMPT_CACHE_FORMATS: frozenset[str] = frozenset({"openai:chat", "openai:cli"})
_PROMPT_CACHE_NAMESPACE_VERSION = "v3"
_USER_AGENT_CLIENT_FAMILY_PATTERNS: tuple[tuple[tuple[str, ...], str], ...] = ( _USER_AGENT_CLIENT_FAMILY_PATTERNS: tuple[tuple[tuple[str, ...], str], ...] = (
(("codex desktop",), "codex_desktop"), (("codex desktop",), "codex_desktop"),
(("asyncopenai/python", "openai/python"), "openai_python"), (("asyncopenai/python", "openai/python"), "openai_python"),
@@ -69,8 +70,10 @@ def _build_stable_prompt_cache_key(
if not normalized: if not normalized:
return None return None
family = str(client_family or "").strip().lower() or "generic" # Keep the optional argument for call-site compatibility, but ignore it.
namespace = f"aether:{scope}:prompt-cache:v2:user:{normalized}:client:{family}" # Prompt cache reuse is more valuable than splitting namespaces by User-Agent.
_ = client_family
namespace = f"aether:{scope}:prompt-cache:{_PROMPT_CACHE_NAMESPACE_VERSION}:user:{normalized}"
return str(uuid.uuid5(uuid.NAMESPACE_OID, namespace)) return str(uuid.uuid5(uuid.NAMESPACE_OID, namespace))
@@ -154,16 +157,13 @@ def maybe_patch_request_with_prompt_cache_key(
if prompt_cache_key: if prompt_cache_key:
return request_body return request_body
client_family = resolve_prompt_cache_client_family(request_headers)
if scope == "codex": if scope == "codex":
stable_key = build_stable_codex_prompt_cache_key( stable_key = build_stable_codex_prompt_cache_key(
user_api_key_id, user_api_key_id,
client_family=client_family,
) )
else: else:
stable_key = build_stable_openai_prompt_cache_key( stable_key = build_stable_openai_prompt_cache_key(
user_api_key_id, user_api_key_id,
client_family=client_family,
) )
if not stable_key: if not stable_key:
return request_body return request_body

View File

@@ -30,7 +30,7 @@ def test_resolve_prompt_cache_client_family_defaults_to_generic() -> None:
assert resolve_prompt_cache_client_family({"x-test": "1"}) == "generic" assert resolve_prompt_cache_client_family({"x-test": "1"}) == "generic"
def test_build_stable_openai_prompt_cache_key_changes_by_client_family() -> None: def test_build_stable_openai_prompt_cache_key_ignores_client_family() -> None:
python_key = build_stable_openai_prompt_cache_key( python_key = build_stable_openai_prompt_cache_key(
"user-key-123", "user-key-123",
client_family="openai_python", client_family="openai_python",
@@ -42,7 +42,16 @@ def test_build_stable_openai_prompt_cache_key_changes_by_client_family() -> None
assert python_key assert python_key
assert node_key assert node_key
assert python_key != node_key assert python_key == node_key
def test_build_stable_prompt_cache_key_stays_scope_specific() -> None:
openai_key = build_stable_openai_prompt_cache_key("user-key-123")
codex_key = build_stable_codex_prompt_cache_key("user-key-123")
assert openai_key
assert codex_key
assert openai_key != codex_key
def test_maybe_patch_request_with_prompt_cache_key_for_official_chat() -> None: def test_maybe_patch_request_with_prompt_cache_key_for_official_chat() -> None:
@@ -58,10 +67,7 @@ def test_maybe_patch_request_with_prompt_cache_key_for_official_chat() -> None:
) )
assert out is not req assert out is not req
assert out["prompt_cache_key"] == build_stable_openai_prompt_cache_key( assert out["prompt_cache_key"] == build_stable_openai_prompt_cache_key("user-key-123")
"user-key-123",
client_family="openai_python",
)
def test_maybe_patch_request_with_prompt_cache_key_for_official_responses() -> None: def test_maybe_patch_request_with_prompt_cache_key_for_official_responses() -> None:
@@ -77,10 +83,7 @@ def test_maybe_patch_request_with_prompt_cache_key_for_official_responses() -> N
) )
assert out is not req assert out is not req
assert out["prompt_cache_key"] == build_stable_openai_prompt_cache_key( assert out["prompt_cache_key"] == build_stable_openai_prompt_cache_key("user-key-123")
"user-key-123",
client_family="openai_node",
)
def test_maybe_patch_request_with_prompt_cache_key_for_codex_openai_cli() -> None: def test_maybe_patch_request_with_prompt_cache_key_for_codex_openai_cli() -> None:
@@ -96,10 +99,7 @@ def test_maybe_patch_request_with_prompt_cache_key_for_codex_openai_cli() -> Non
) )
assert out is not req assert out is not req
assert out["prompt_cache_key"] == build_stable_codex_prompt_cache_key( assert out["prompt_cache_key"] == build_stable_codex_prompt_cache_key("user-key-123")
"user-key-123",
client_family="codex_desktop",
)
def test_maybe_patch_request_with_prompt_cache_key_skips_official_compact() -> None: def test_maybe_patch_request_with_prompt_cache_key_skips_official_compact() -> None:
@@ -180,7 +180,7 @@ def test_maybe_patch_request_with_prompt_cache_key_skips_unmatched_provider() ->
assert "prompt_cache_key" not in out assert "prompt_cache_key" not in out
def test_maybe_patch_request_with_prompt_cache_key_uses_generic_family_without_user_agent() -> None: def test_maybe_patch_request_with_prompt_cache_key_is_stable_without_user_agent() -> None:
req = {"model": "gpt-5", "messages": [{"role": "user", "content": "hi"}]} req = {"model": "gpt-5", "messages": [{"role": "user", "content": "hi"}]}
out = maybe_patch_request_with_prompt_cache_key( out = maybe_patch_request_with_prompt_cache_key(
@@ -192,7 +192,28 @@ def test_maybe_patch_request_with_prompt_cache_key_uses_generic_family_without_u
) )
assert out is not req assert out is not req
assert out["prompt_cache_key"] == build_stable_openai_prompt_cache_key( assert out["prompt_cache_key"] == build_stable_openai_prompt_cache_key("user-key-123")
"user-key-123",
client_family="generic",
def test_maybe_patch_request_with_prompt_cache_key_ignores_user_agent_variants() -> None:
req_python = {"model": "gpt-5", "input": []}
req_codex = {"model": "gpt-5", "input": []}
out_python = maybe_patch_request_with_prompt_cache_key(
req_python,
provider_api_format="openai:cli",
provider_type="custom",
base_url="https://api.openai.com/v1",
user_api_key_id="user-key-123",
request_headers={"user-agent": "AsyncOpenAI/Python 2.14.0"},
) )
out_codex = maybe_patch_request_with_prompt_cache_key(
req_codex,
provider_api_format="openai:cli",
provider_type="custom",
base_url="https://api.openai.com/v1",
user_api_key_id="user-key-123",
request_headers={"user-agent": "Codex Desktop/0.108.0-alpha.12"},
)
assert out_python["prompt_cache_key"] == out_codex["prompt_cache_key"]