mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-27 01:22:18 +00:00
fix(caching): stand default cache points down when extra_body hides a direct client mark (#43341)
* fix(caching): stand default cache points down when extra_body hides a direct client mark On native /v1/messages the extra_body envelope is dropped, so a client tool mark or root cache_control reaches Anthropic even when extra_body overrides it. The stand-down check only counted the envelope-merged view and injected two default marks on top of the client's. * fix(caching): keep chat completions on the envelope-merged mark count for the default stand-down Chat completions merge extra_body over the request, so a direct tool mark that extra_body replaces never reaches the provider there. Only /v1/messages, where the native transforms drop the envelope, needs to count marks on both sides.
This commit is contained in:
parent
849f3037b4
commit
b2e82cf3be
2 changed files with 50 additions and 5 deletions
|
|
@ -695,6 +695,7 @@ class AnthropicCacheControlHook(CustomPromptManagement):
|
|||
tools: list | None = None,
|
||||
cache_control: object = None,
|
||||
request_kwargs: object = None,
|
||||
on_messages_route: bool = False,
|
||||
) -> bool:
|
||||
"""Return True if the request already carries any client-supplied cache_control.
|
||||
|
||||
|
|
@ -704,10 +705,14 @@ class AnthropicCacheControlHook(CustomPromptManagement):
|
|||
envelope. Configured injection points are an explicit instruction and are
|
||||
applied alongside the client's marks, bounded by the provider cap.
|
||||
"""
|
||||
return (
|
||||
AnthropicCacheControlHook.count_request_cache_breakpoints(messages, system)
|
||||
+ AnthropicCacheControlHook.count_external_cache_breakpoints(tools, cache_control, request_kwargs)
|
||||
) > 0
|
||||
external_breakpoints: Final = (
|
||||
AnthropicCacheControlHook.count_external_cache_breakpoints_on_messages_route(
|
||||
tools, cache_control, request_kwargs
|
||||
)
|
||||
if on_messages_route
|
||||
else AnthropicCacheControlHook.count_external_cache_breakpoints(tools, cache_control, request_kwargs)
|
||||
)
|
||||
return AnthropicCacheControlHook.count_request_cache_breakpoints(messages, system) + external_breakpoints > 0
|
||||
|
||||
@staticmethod
|
||||
def get_default_injection_points(
|
||||
|
|
@ -719,6 +724,7 @@ class AnthropicCacheControlHook(CustomPromptManagement):
|
|||
enable_prompt_caching: bool | None = None,
|
||||
cache_control: object = None,
|
||||
request_kwargs: object = None,
|
||||
on_messages_route: bool = False,
|
||||
) -> list[CacheControlInjectionPoint]:
|
||||
"""Default breakpoints when ``litellm.enable_anthropic_prompt_caching`` is on.
|
||||
|
||||
|
|
@ -739,7 +745,9 @@ class AnthropicCacheControlHook(CustomPromptManagement):
|
|||
if not supports_anthropic_cache_control(model, custom_llm_provider):
|
||||
return []
|
||||
|
||||
if AnthropicCacheControlHook._request_has_cache_control(messages, system, tools, cache_control, request_kwargs):
|
||||
if AnthropicCacheControlHook._request_has_cache_control(
|
||||
messages, system, tools, cache_control, request_kwargs, on_messages_route
|
||||
):
|
||||
return []
|
||||
|
||||
if is_claude_code_one_shot_subagent_request(
|
||||
|
|
@ -968,6 +976,7 @@ class AnthropicCacheControlHook(CustomPromptManagement):
|
|||
enable_prompt_caching=enable_prompt_caching,
|
||||
cache_control=cache_control,
|
||||
request_kwargs=kwargs,
|
||||
on_messages_route=True,
|
||||
)
|
||||
if model is not None
|
||||
else ()
|
||||
|
|
|
|||
|
|
@ -2633,6 +2633,42 @@ class TestConfiguredInjectionPointsSurviveClientMarks:
|
|||
assert kwargs["cache_control"] is root_cache_control
|
||||
assert "litellm_gateway_injected_cache" not in kwargs["litellm_metadata"]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tools,kwargs,injected",
|
||||
[
|
||||
([MARKED_V1_TOOL], {"extra_body": {"tools": [UNMARKED_V1_TOOL]}}, False),
|
||||
(None, {"cache_control": EPHEMERAL, "extra_body": {"cache_control": None}}, False),
|
||||
([UNMARKED_V1_TOOL], {"extra_body": {"tools": [UNMARKED_V1_TOOL]}}, True),
|
||||
],
|
||||
ids=["extra_body_unmarks_direct_tool", "extra_body_nulls_root_cache_control", "no_client_mark_anywhere"],
|
||||
)
|
||||
def test_v1_messages_automatic_defaults_stand_down_for_a_direct_mark_extra_body_hides(
|
||||
self, monkeypatch, tools, kwargs, injected
|
||||
):
|
||||
monkeypatch.setattr(litellm, "enable_anthropic_prompt_caching", True)
|
||||
request_kwargs = {**copy.deepcopy(kwargs), "litellm_metadata": {}}
|
||||
|
||||
result_messages, result_system = self._inject(
|
||||
copy.deepcopy(self.V1_MESSAGES), request_kwargs, tools=copy.deepcopy(tools)
|
||||
)
|
||||
|
||||
assert AnthropicCacheControlHook.count_request_cache_breakpoints(result_messages, result_system) == (
|
||||
2 if injected else 0
|
||||
)
|
||||
assert ("litellm_gateway_injected_cache" in request_kwargs["litellm_metadata"]) is injected
|
||||
|
||||
def test_chat_automatic_defaults_apply_when_extra_body_drops_the_only_client_mark(self, monkeypatch):
|
||||
monkeypatch.setattr(litellm, "enable_anthropic_prompt_caching", True)
|
||||
params = {"extra_body": {"tools": [self.UNMARKED_TOOL]}}
|
||||
|
||||
self._seed(params, copy.deepcopy(self.CLEAN_MESSAGES), tools=[self.MARKED_TOOL_TOP_LEVEL])
|
||||
affinity = AnthropicCacheControlHook.messages_with_default_injections(
|
||||
copy.deepcopy(self.CLEAN_MESSAGES), ["claude-sonnet-4-5"], tools=[self.MARKED_TOOL_TOP_LEVEL], request_kwargs=params
|
||||
)
|
||||
|
||||
assert [p["index"] for p in params["cache_control_injection_points"]] == [None, -1]
|
||||
assert AnthropicCacheControlHook.count_request_cache_breakpoints(affinity) == 2
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"marked_turns,expected_system",
|
||||
[(2, [{"type": "text", "text": "sys", "cache_control": {"type": "ephemeral"}}]), (3, "sys")],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue