fix(prompt-caching): align _can_carry_marker with last-part-dict marking

Follow-up to the salvaged #57845 fix. _can_carry_marker used
any(isinstance(part, dict)) but _apply_cache_marker only marks the LAST
content part, so a list whose last element is a non-dict passed the carrier
gate yet received no marker — wasting one of the four breakpoints. Tighten
the predicate to require content[-1] to be a dict (mirroring the apply
logic) and add a regression test. Flagged by a 3-agent review.
This commit is contained in:
kshitijk4poor 2026-07-04 12:12:15 +05:30 committed by kshitij
parent 8b797f7a7b
commit 52cf9dbada
2 changed files with 24 additions and 1 deletions

View file

@ -65,7 +65,11 @@ def _can_carry_marker(msg: dict, native_anthropic: bool) -> bool:
if content is None or content == "":
return False
if isinstance(content, list):
return any(isinstance(part, dict) for part in content)
# _apply_cache_marker only marks the LAST content part, so the carrier
# predicate must agree: a list whose last element isn't a dict cannot
# actually receive a marker and would waste a breakpoint. Mirror the
# `content` truthiness + last-element-dict check in _apply_cache_marker.
return bool(content) and isinstance(content[-1], dict)
return isinstance(content, str)

View file

@ -98,6 +98,25 @@ class TestCanCarryMarker:
assert _can_carry_marker({"role": "tool", "content": "result"}, native_anthropic=False) is True
assert _can_carry_marker({"role": "tool", "content": ""}, native_anthropic=False) is False
def test_openrouter_list_carrier_requires_last_part_dict(self):
"""Carrier predicate must agree with _apply_cache_marker, which only marks
the LAST content part. A list whose last element isn't a dict cannot carry
a marker and must not consume a breakpoint."""
# Last part is a dict -> carrier.
assert _can_carry_marker(
{"role": "user", "content": [{"type": "text", "text": "a"}]},
native_anthropic=False,
) is True
# Last part is a non-dict (stray raw string) -> NOT a carrier, even though
# an earlier part is a dict. Previously this passed the gate but got no
# marker, wasting a breakpoint.
assert _can_carry_marker(
{"role": "user", "content": [{"type": "text", "text": "a"}, "trailing raw"]},
native_anthropic=False,
) is False
# Empty list -> not a carrier.
assert _can_carry_marker({"role": "user", "content": []}, native_anthropic=False) is False
class TestApplyAnthropicCacheControl:
def test_empty_messages(self):