mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
feat(cache-fingerprint): 新增字段级指纹,支持逐字段 sha256 和字节数追踪
在请求缓存指纹中为每个 cache_relevant 字段单独计算 sha256 和字节数, 便于精确定位哪些字段发生了变化。fingerprint version 升级为 2。
This commit is contained in:
@@ -101,6 +101,22 @@ def _extract_cache_relevant_payload(
|
|||||||
return subset, sorted(subset.keys())
|
return subset, sorted(subset.keys())
|
||||||
|
|
||||||
|
|
||||||
|
def _build_field_fingerprints(payload: Any, field_names: list[str]) -> dict[str, dict[str, Any]]:
|
||||||
|
if not isinstance(payload, Mapping) or not field_names:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
field_fingerprints: dict[str, dict[str, Any]] = {}
|
||||||
|
for field_name in field_names:
|
||||||
|
if field_name not in payload:
|
||||||
|
continue
|
||||||
|
field_sha256, field_bytes = _hash_json_payload(payload[field_name])
|
||||||
|
field_fingerprints[field_name] = {
|
||||||
|
"sha256": field_sha256,
|
||||||
|
"bytes": field_bytes,
|
||||||
|
}
|
||||||
|
return field_fingerprints
|
||||||
|
|
||||||
|
|
||||||
def build_request_cache_fingerprint(
|
def build_request_cache_fingerprint(
|
||||||
provider_request_body: Any,
|
provider_request_body: Any,
|
||||||
*,
|
*,
|
||||||
@@ -117,13 +133,14 @@ def build_request_cache_fingerprint(
|
|||||||
normalized_format,
|
normalized_format,
|
||||||
)
|
)
|
||||||
cache_relevant_sha256, cache_relevant_bytes = _hash_json_payload(cache_relevant_payload)
|
cache_relevant_sha256, cache_relevant_bytes = _hash_json_payload(cache_relevant_payload)
|
||||||
|
field_fingerprints = _build_field_fingerprints(cache_relevant_payload, cache_relevant_keys)
|
||||||
|
|
||||||
top_level_keys = []
|
top_level_keys = []
|
||||||
if isinstance(provider_request_body, Mapping):
|
if isinstance(provider_request_body, Mapping):
|
||||||
top_level_keys = sorted(str(key) for key in provider_request_body.keys())
|
top_level_keys = sorted(str(key) for key in provider_request_body.keys())
|
||||||
|
|
||||||
fingerprint: dict[str, Any] = {
|
fingerprint: dict[str, Any] = {
|
||||||
"version": 1,
|
"version": 2,
|
||||||
"provider_api_format": normalized_format,
|
"provider_api_format": normalized_format,
|
||||||
"payload_sha256": payload_sha256,
|
"payload_sha256": payload_sha256,
|
||||||
"payload_bytes": payload_bytes,
|
"payload_bytes": payload_bytes,
|
||||||
@@ -131,6 +148,7 @@ def build_request_cache_fingerprint(
|
|||||||
"cache_relevant_bytes": cache_relevant_bytes,
|
"cache_relevant_bytes": cache_relevant_bytes,
|
||||||
"top_level_keys": top_level_keys,
|
"top_level_keys": top_level_keys,
|
||||||
"cache_relevant_keys": cache_relevant_keys,
|
"cache_relevant_keys": cache_relevant_keys,
|
||||||
|
"field_fingerprints": field_fingerprints,
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_cache_key = _get_prompt_cache_key(provider_request_body)
|
prompt_cache_key = _get_prompt_cache_key(provider_request_body)
|
||||||
|
|||||||
@@ -65,8 +65,10 @@ def test_build_request_cache_fingerprint_is_stable_for_dict_key_reordering() ->
|
|||||||
|
|
||||||
assert fingerprint_a is not None
|
assert fingerprint_a is not None
|
||||||
assert fingerprint_b is not None
|
assert fingerprint_b is not None
|
||||||
|
assert fingerprint_a["version"] == 2
|
||||||
assert fingerprint_a["payload_sha256"] == fingerprint_b["payload_sha256"]
|
assert fingerprint_a["payload_sha256"] == fingerprint_b["payload_sha256"]
|
||||||
assert fingerprint_a["cache_relevant_sha256"] == fingerprint_b["cache_relevant_sha256"]
|
assert fingerprint_a["cache_relevant_sha256"] == fingerprint_b["cache_relevant_sha256"]
|
||||||
|
assert fingerprint_a["field_fingerprints"] == fingerprint_b["field_fingerprints"]
|
||||||
assert fingerprint_a["prompt_cache_key"] == "pcache-123"
|
assert fingerprint_a["prompt_cache_key"] == "pcache-123"
|
||||||
assert fingerprint_a["cache_relevant_keys"] == [
|
assert fingerprint_a["cache_relevant_keys"] == [
|
||||||
"input",
|
"input",
|
||||||
@@ -75,6 +77,8 @@ def test_build_request_cache_fingerprint_is_stable_for_dict_key_reordering() ->
|
|||||||
"prompt_cache_key",
|
"prompt_cache_key",
|
||||||
"tools",
|
"tools",
|
||||||
]
|
]
|
||||||
|
assert fingerprint_a["field_fingerprints"]["instructions"]["bytes"] > 0
|
||||||
|
assert fingerprint_a["field_fingerprints"]["tools"]["bytes"] > 0
|
||||||
|
|
||||||
|
|
||||||
def test_build_request_cache_fingerprint_ignores_non_prompt_fields_in_cache_hash() -> None:
|
def test_build_request_cache_fingerprint_ignores_non_prompt_fields_in_cache_hash() -> None:
|
||||||
@@ -89,6 +93,8 @@ def test_build_request_cache_fingerprint_ignores_non_prompt_fields_in_cache_hash
|
|||||||
assert fingerprint_b is not None
|
assert fingerprint_b is not None
|
||||||
assert fingerprint_a["payload_sha256"] != fingerprint_b["payload_sha256"]
|
assert fingerprint_a["payload_sha256"] != fingerprint_b["payload_sha256"]
|
||||||
assert fingerprint_a["cache_relevant_sha256"] == fingerprint_b["cache_relevant_sha256"]
|
assert fingerprint_a["cache_relevant_sha256"] == fingerprint_b["cache_relevant_sha256"]
|
||||||
|
assert fingerprint_a["field_fingerprints"] == fingerprint_b["field_fingerprints"]
|
||||||
|
assert "temperature" not in fingerprint_a["field_fingerprints"]
|
||||||
|
|
||||||
|
|
||||||
def test_build_request_cache_fingerprint_tracks_prompt_changes() -> None:
|
def test_build_request_cache_fingerprint_tracks_prompt_changes() -> None:
|
||||||
@@ -102,6 +108,18 @@ def test_build_request_cache_fingerprint_tracks_prompt_changes() -> None:
|
|||||||
assert fingerprint_a is not None
|
assert fingerprint_a is not None
|
||||||
assert fingerprint_b is not None
|
assert fingerprint_b is not None
|
||||||
assert fingerprint_a["cache_relevant_sha256"] != fingerprint_b["cache_relevant_sha256"]
|
assert fingerprint_a["cache_relevant_sha256"] != fingerprint_b["cache_relevant_sha256"]
|
||||||
|
assert (
|
||||||
|
fingerprint_a["field_fingerprints"]["instructions"]["sha256"]
|
||||||
|
!= fingerprint_b["field_fingerprints"]["instructions"]["sha256"]
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
fingerprint_a["field_fingerprints"]["input"]["sha256"]
|
||||||
|
== fingerprint_b["field_fingerprints"]["input"]["sha256"]
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
fingerprint_a["field_fingerprints"]["tools"]["sha256"]
|
||||||
|
== fingerprint_b["field_fingerprints"]["tools"]["sha256"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_sanitize_request_metadata_preserves_cache_fingerprint(
|
def test_sanitize_request_metadata_preserves_cache_fingerprint(
|
||||||
@@ -163,8 +181,10 @@ async def test_message_telemetry_record_success_keeps_response_shape_and_adds_fi
|
|||||||
metadata = captured["metadata"]
|
metadata = captured["metadata"]
|
||||||
assert metadata["model_version"] == "gpt-5.4-2026-03-01"
|
assert metadata["model_version"] == "gpt-5.4-2026-03-01"
|
||||||
assert "response" not in metadata
|
assert "response" not in metadata
|
||||||
|
assert metadata["cache_fingerprint"]["version"] == 2
|
||||||
assert metadata["cache_fingerprint"]["provider_api_format"] == "openai:cli"
|
assert metadata["cache_fingerprint"]["provider_api_format"] == "openai:cli"
|
||||||
assert metadata["cache_fingerprint"]["prompt_cache_key"] == "pcache-123"
|
assert metadata["cache_fingerprint"]["prompt_cache_key"] == "pcache-123"
|
||||||
|
assert "instructions" in metadata["cache_fingerprint"]["field_fingerprints"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -203,5 +223,7 @@ async def test_message_telemetry_record_failure_keeps_request_metadata_and_adds_
|
|||||||
|
|
||||||
metadata = captured["metadata"]
|
metadata = captured["metadata"]
|
||||||
assert metadata["perf"]["ttfb_ms"] == 12
|
assert metadata["perf"]["ttfb_ms"] == 12
|
||||||
|
assert metadata["cache_fingerprint"]["version"] == 2
|
||||||
assert metadata["cache_fingerprint"]["provider_api_format"] == "openai:cli"
|
assert metadata["cache_fingerprint"]["provider_api_format"] == "openai:cli"
|
||||||
assert metadata["cache_fingerprint"]["prompt_cache_key"] == "pcache-123"
|
assert metadata["cache_fingerprint"]["prompt_cache_key"] == "pcache-123"
|
||||||
|
assert "input" in metadata["cache_fingerprint"]["field_fingerprints"]
|
||||||
|
|||||||
Reference in New Issue
Block a user