fix(responses): preserve multimodal cache breakpoints

This commit is contained in:
Simon Sorg 2026-09-21 21:51:39 +02:00
parent f57f994629
commit 59f29ca6df
No known key found for this signature in database
2 changed files with 39 additions and 25 deletions

View file

@ -1069,11 +1069,14 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
verbose_logger.debug("Chat provider: text -> %s", converted)
elif original_type == "image_url":
# Map to responses API image format
converted = cast(
dict,
self._convert_content_to_responses_format_image(
cast(ChatCompletionImageObject, item), role
converted = with_prompt_cache_breakpoint(
cast(
dict,
self._convert_content_to_responses_format_image(
cast(ChatCompletionImageObject, item), role
),
),
item.get("prompt_cache_breakpoint"),
)
result.append(converted)
verbose_logger.debug("Chat provider: image_url -> %s", converted)
@ -1085,8 +1088,11 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
result.append(converted)
verbose_logger.debug("Chat provider: image -> %s", converted)
elif item_type == "file":
converted = _input_file_from_file_value(
cast("ChatCompletionFileObject", item).get("file"), # cast-ok: type tag checked
converted = with_prompt_cache_breakpoint(
_input_file_from_file_value(
cast("ChatCompletionFileObject", item).get("file"), # cast-ok: type tag checked
),
item.get("prompt_cache_breakpoint"),
)
result.append(converted)
verbose_logger.debug("Chat provider: file -> %s", converted)

View file

@ -4185,24 +4185,38 @@ def _system_input_item(text: str) -> dict[str, object]:
return {"type": "message", "role": "system", "content": [{"type": "input_text", "text": text}]}
def test_prompt_cache_breakpoint_survives_chat_to_responses_conversion() -> None:
@pytest.mark.parametrize(
("content_block", "expected_content"),
[
(
{"type": "text", "text": "Stable prefix"},
{"type": "input_text", "text": "Stable prefix"},
),
(
{"type": "image_url", "image_url": "https://example.com/image.png"},
{"type": "input_image", "image_url": "https://example.com/image.png", "detail": "auto"},
),
(
{"type": "file", "file": {"file_id": "file-123"}},
{"type": "input_file", "file_id": "file-123"},
),
],
ids=("text", "image_url", "file"),
)
def test_prompt_cache_breakpoint_survives_chat_to_responses_conversion(
content_block: dict[str, object], expected_content: dict[str, object]
) -> None:
handler: Final = LiteLLMResponsesTransformationHandler()
cache_breakpoint: Final = {"mode": "explicit"}
marked_content: Final = {**content_block, "prompt_cache_breakpoint": cache_breakpoint}
request: Final = handler.transform_request(
model="gpt-5.6-sol",
messages=[
{
"role": "system",
"content": [
{
"type": "text",
"text": "Stable prefix",
"prompt_cache_breakpoint": cache_breakpoint,
}
],
},
{"role": "user", "content": "Use a tool"},
"role": "user",
"content": [marked_content],
}
],
optional_params={"prompt_cache_options": cache_breakpoint},
litellm_params={},
@ -4212,14 +4226,8 @@ def test_prompt_cache_breakpoint_survives_chat_to_responses_conversion() -> None
assert request["input"][0] == {
"type": "message",
"role": "system",
"content": [
{
"type": "input_text",
"text": "Stable prefix",
"prompt_cache_breakpoint": cache_breakpoint,
}
],
"role": "user",
"content": [{**expected_content, "prompt_cache_breakpoint": cache_breakpoint}],
}
assert request["prompt_cache_options"] == cache_breakpoint