From 6ef0eed7c71d5f5ed36e489aa0f6751d584ff312 Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Mon, 7 Sep 2026 10:28:48 +0200 Subject: [PATCH] image control added --- litellm/llms/sap/chat/models.py | 10 +++++++++ .../llms/sap/chat/test_sap_transformation.py | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/litellm/llms/sap/chat/models.py b/litellm/llms/sap/chat/models.py index 48ac3ca5ee5..babe8aa3d0c 100644 --- a/litellm/llms/sap/chat/models.py +++ b/litellm/llms/sap/chat/models.py @@ -50,6 +50,16 @@ class ImageURLContent(BaseModel): class ImageContent(BaseModel): type_: Literal["image_url"] = Field(default="image_url", alias="type") image_url: ImageURLContent + cache_control: CacheControl | None = None + + @model_serializer(mode="wrap") + def _serialize( + self, handler: SerializerFunctionWrapHandler, info: SerializationInfo + ) -> dict: # mutable-ok: pydantic serializer contract requires bare dict return + result = handler(self) + if result.get("cache_control") is None: + result.pop("cache_control", None) + return result class FunctionObj(BaseModel): diff --git a/tests/test_litellm/llms/sap/chat/test_sap_transformation.py b/tests/test_litellm/llms/sap/chat/test_sap_transformation.py index 20a038cef43..d54dc02ff57 100644 --- a/tests/test_litellm/llms/sap/chat/test_sap_transformation.py +++ b/tests/test_litellm/llms/sap/chat/test_sap_transformation.py @@ -919,3 +919,25 @@ class TestCacheControl: content = self._template(body)[0]["content"] assert "cache_control" not in content[0] assert content[1].get("cache_control") == {"type": "ephemeral"} + + def test_message_level_cache_control_folded_onto_trailing_image_block(self): + """A top-level cache_control with a trailing image part lands on that image. + + The Anthropic spec allows cache_control on any content block including + image_url. Previously ImageContent had no cache_control field, so the + marker was silently dropped by Pydantic validation. + """ + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "Describe this image."}, + {"type": "image_url", "image_url": {"url": "data:image/png;base64,abc="}}, + ], + "cache_control": {"type": "ephemeral"}, + } + ] + body = self._transform("anthropic--claude-3-5-sonnet", messages) + content = self._template(body)[0]["content"] + assert "cache_control" not in content[0] + assert content[1].get("cache_control") == {"type": "ephemeral"}