diff --git a/litellm/responses/litellm_completion_transformation/transformation.py b/litellm/responses/litellm_completion_transformation/transformation.py index 8f5cd72cbcd..b8e60d40e29 100644 --- a/litellm/responses/litellm_completion_transformation/transformation.py +++ b/litellm/responses/litellm_completion_transformation/transformation.py @@ -2259,6 +2259,9 @@ class LiteLLMCompletionResponsesConfig: file_dict["file_data"] = item["file_data"] if item.get("filename"): file_dict["filename"] = item["filename"] + media_format: Final = item.get("format") + if media_format is not None: + file_dict["format"] = media_format bucket_owner: Final = item.get("bucket_owner") if bucket_owner is not None and _is_s3_url(file_id or item.get("file_data")): file_dict["bucket_owner"] = bucket_owner @@ -2283,6 +2286,9 @@ class LiteLLMCompletionResponsesConfig: if bucket_owner is not None and _is_s3_url(url) else ChatCompletionImageUrlObject(url=url, detail=detail) ) + media_format: Final = item.get("format") + if media_format is not None: + image_url_obj["format"] = media_format return ChatCompletionImageObject(type="image_url", image_url=image_url_obj) diff --git a/tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py b/tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py index c3a74465139..c17946a2f6b 100644 --- a/tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py +++ b/tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py @@ -44,6 +44,68 @@ from litellm.types.utils import ( class TestLiteLLMCompletionResponsesConfig: + @pytest.mark.parametrize("part_type", ["input_image", "input_file"]) + @pytest.mark.parametrize( + ("key", "media_format", "expected_format"), + [("key", "image/png", "png"), ("key.jpeg", "image/png", "png"), ("key.png", None, "png")], + ) + def test_explicit_responses_media_format_reaches_the_converse_s3_image( + self, part_type: str, key: str, media_format: str | None, expected_format: str + ) -> None: + uri: Final = f"s3://bucket/{key}" + messages: Final = LiteLLMCompletionResponsesConfig.transform_responses_api_input_to_messages( + input=[ + { + "role": "user", + "content": [ + { + "type": part_type, + "image_url" if part_type == "input_image" else "file_id": uri, + **({"format": media_format} if media_format is not None else {}), + } + ], + } + ], + responses_api_request={}, + ) + request: Final = litellm.AmazonConverseConfig()._transform_request( + model="us.amazon.nova-2-lite-v1:0", + messages=messages, + optional_params={}, + litellm_params={}, + headers={}, + ) + + assert request["messages"] == [ + { + "role": "user", + "content": [{"image": {"format": expected_format, "source": {"s3Location": {"uri": uri}}}}], + } + ] + + @pytest.mark.parametrize("part_type", ["input_image", "input_file"]) + def test_extensionless_responses_s3_media_without_format_is_a_bad_request(self, part_type: str) -> None: + messages: Final = LiteLLMCompletionResponsesConfig.transform_responses_api_input_to_messages( + input=[ + { + "role": "user", + "content": [ + {"type": part_type, "image_url" if part_type == "input_image" else "file_id": "s3://bucket/key"} + ], + } + ], + responses_api_request={}, + ) + + with pytest.raises(litellm.BadRequestError, match="Cannot tell the media type"): + litellm.AmazonConverseConfig()._transform_request( + model="us.amazon.nova-2-lite-v1:0", + messages=messages, + optional_params={}, + litellm_params={}, + headers={}, + ) + def test_transform_input_file_item_to_file_item_with_file_id(self): """Test transformation of input_file item with file_id to Chat Completion file format""" # Setup