fix(cache): surface unsupported breakpoint counts

Warn when caller-provided cache breakpoints already exceed the supported total of four while preserving the original markers and upgrading their TTLs.

Clarify that automatic injection is governed by the remaining breakpoint budget, and add regression coverage proving excess caller markers are never deleted or reordered.
This commit is contained in:
Jason
2026-07-11 12:28:02 +08:00
parent 27ce0a519c
commit 0e563b50c5
2 changed files with 36 additions and 1 deletions
+35
View File
@@ -13,6 +13,15 @@ pub fn inject(body: &mut Value, config: &OptimizerConfig) {
let existing = count_existing(body);
if existing > 4 {
// Existing markers are caller-owned. Do not silently delete or reorder
// them; surface the invalid/unsupported total and leave validation to
// the upstream provider.
log::warn!(
"[OPT] cache: existing breakpoint count {existing} exceeds the supported total of 4; preserving caller input"
);
}
// 升级已有断点的 TTL
upgrade_existing_ttl(body, &config.cache_ttl);
@@ -288,6 +297,32 @@ mod tests {
.is_some());
}
#[test]
fn test_more_than_four_existing_breakpoints_are_preserved() {
let mut body = json!({
"model": "test",
"tools": [
{"name": "t1", "cache_control": {"type": "ephemeral"}},
{"name": "t2", "cache_control": {"type": "ephemeral"}}
],
"system": [
{"type": "text", "text": "s1", "cache_control": {"type": "ephemeral"}},
{"type": "text", "text": "s2", "cache_control": {"type": "ephemeral"}}
],
"messages": [{"role": "user", "content": [
{"type": "text", "text": "m1", "cache_control": {"type": "ephemeral"}},
{"type": "text", "text": "m2"}
]}]
});
inject(&mut body, &default_config());
assert_eq!(count_existing(&body), 5);
assert!(body["messages"][0]["content"][1]
.get("cache_control")
.is_none());
}
#[test]
fn test_system_string_converted_to_array() {
let mut body = json!({
+1 -1
View File
@@ -1450,7 +1450,7 @@ impl RequestForwarder {
// configured TTL rather than silently forcing 5m on this conversion path.
// otherwise system/tools/history are re-sent at full price every round,
// inflating cost and first-token latency. The injector handles the
// string→array `system` conversion and the 4-breakpoint cap.
// string→array `system` conversion and the new-breakpoint budget.
super::cache_injector::inject(
&mut anthropic_body,
&super::types::OptimizerConfig {