diff --git a/litellm/llms/sap/chat/models.py b/litellm/llms/sap/chat/models.py index 2756dd0e67e..945cc4a2d51 100644 --- a/litellm/llms/sap/chat/models.py +++ b/litellm/llms/sap/chat/models.py @@ -39,6 +39,16 @@ class ImageContent(BaseModel): image_url: ImageURLContent +class FileContentData(BaseModel): + file_data: str + filename: str | None = None + + +class FileContent(BaseModel): + type_: Literal["file"] = Field(default="file", alias="type") + file: FileContentData + + class FunctionObj(BaseModel): name: str arguments: str @@ -95,7 +105,13 @@ class SAPMessage(BaseModel): class SAPUserMessage(BaseModel): role: Literal["user"] = "user" - content: Union[str, TextContent, ImageContent, list[Union[TextContent, ImageContent]]] + content: Union[ + str, + TextContent, + ImageContent, + FileContent, + list[Union[TextContent, ImageContent, FileContent]], + ] class SAPAssistantMessage(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 3601bdd0d5e..61726e9aceb 100644 --- a/tests/test_litellm/llms/sap/chat/test_sap_transformation.py +++ b/tests/test_litellm/llms/sap/chat/test_sap_transformation.py @@ -639,3 +639,63 @@ class TestSAPTransformationIntegration: config["config"]["modules"][1]["translation"]["input"]["type"] == "sap_document_translation" ) + + def test_transform_request_with_file_content_part(self, mock_config): + pdf_data = "data:application/pdf;base64,JVBERi0xLjQK" + result = mock_config.transform_request( + model="anthropic--claude-4-sonnet", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": "Summarize this document."}, + { + "type": "file", + "file": {"file_data": pdf_data, "filename": "report.pdf"}, + }, + ], + } + ], + optional_params={}, + litellm_params={}, + headers={}, + ) + + content = result["config"]["modules"]["prompt_templating"]["prompt"]["template"][0]["content"] + assert content == [ + {"type": "text", "text": "Summarize this document."}, + {"type": "file", "file": {"file_data": pdf_data, "filename": "report.pdf"}}, + ] + + def test_transform_request_with_file_content_part_without_filename(self, mock_config): + pdf_data = "data:application/pdf;base64,JVBERi0xLjQK" + result = mock_config.transform_request( + model="gemini-2.5-pro", + messages=[ + { + "role": "user", + "content": [{"type": "file", "file": {"file_data": pdf_data}}], + } + ], + optional_params={}, + litellm_params={}, + headers={}, + ) + + content = result["config"]["modules"]["prompt_templating"]["prompt"]["template"][0]["content"] + assert content == [{"type": "file", "file": {"file_data": pdf_data}}] + + def test_file_content_part_requires_file_data(self, mock_config): + with pytest.raises(ValidationError): + mock_config.transform_request( + model="anthropic--claude-4-sonnet", + messages=[ + { + "role": "user", + "content": [{"type": "file", "file": {"filename": "report.pdf"}}], + } + ], + optional_params={}, + litellm_params={}, + headers={}, + )