diff --git a/litellm/llms/sap/chat/models.py b/litellm/llms/sap/chat/models.py index b2b773c8d97..1eef3d5f303 100644 --- a/litellm/llms/sap/chat/models.py +++ b/litellm/llms/sap/chat/models.py @@ -354,7 +354,7 @@ class MaskingProviderConfig(BaseModel): mask_grounding_input: A flag indicating whether to mask input to the grounding module. """ - type_: str = Field(default="sap_data_privacy_integration", alias="type") + type_: Literal["sap_data_privacy_integration"] = Field(default="sap_data_privacy_integration", alias="type") method: Literal["anonymization", "pseudonymization"] entities: list[Union[DPIStandardEntity, DPICustomEntity]] allowlist: Optional[list[str]] = None @@ -539,17 +539,17 @@ class LlamaGuard38bFilter(BaseModel): class LlamaGuard38bFilterConfig(BaseModel): - type_: str = Field(default="llama_guard_3_8b", alias="type") + type_: Literal["llama_guard_3_8b"] = Field(default="llama_guard_3_8b", alias="type") config: LlamaGuard38bFilter class AzureContentSafetyInputFilterConfig(BaseModel): - type_: str = Field(default="azure_content_safety", alias="type") + type_: Literal["azure_content_safety"] = Field(default="azure_content_safety", alias="type") config: Optional[AzureContentSafetyInput] = None class AzureContentSafetyOutputFilterConfig(BaseModel): - type_: str = Field(default="azure_content_safety", alias="type") + type_: Literal["azure_content_safety"] = Field(default="azure_content_safety", alias="type") config: Optional[AzureContentSafetyOutput] = None @@ -661,7 +661,7 @@ class SAPDocumentTranslationInput(BaseModel): config: Configuration object for the translation module. """ - type_: str = Field(default="sap_document_translation", alias="type") + type_: Literal["sap_document_translation"] = Field(default="sap_document_translation", alias="type") translate_messages_history: Optional[bool] = None config: InputTranslationConfig @@ -676,7 +676,7 @@ class SAPDocumentTranslationOutput(BaseModel): config: Configuration object for the translation module. """ - type_: str = Field(default="sap_document_translation", alias="type") + type_: Literal["sap_document_translation"] = Field(default="sap_document_translation", alias="type") config: OutputTranslationConfig @@ -712,7 +712,7 @@ class ModuleConfig(BaseModel): class GlobalStreamOptions(BaseModel): enabled: bool = False - chunk_size: int = 100 + chunk_size: int = Field(default=None, ge=1) delimiters: Optional[list[str]] = None diff --git a/tests/test_litellm/llms/sap/chat/test_sap_tool_parameters.py b/tests/test_litellm/llms/sap/chat/test_sap_tool_parameters.py index 792d2f3fe6b..e4e639d03c9 100644 --- a/tests/test_litellm/llms/sap/chat/test_sap_tool_parameters.py +++ b/tests/test_litellm/llms/sap/chat/test_sap_tool_parameters.py @@ -127,10 +127,9 @@ class TestToolTransformationIntegration: } validated_tool = validate_dict(openai_tool, ChatCompletionTool) - - # After validation, parameters should have type='object' - assert validated_tool["function"]["parameters"]["type"] == "object" - assert "properties" in validated_tool["function"]["parameters"] + + assert validated_tool["function"]["name"] == "web_search" + def test_should_transform_tool_with_existing_parameters(self): """Tool with parameters should preserve them while ensuring type='object'.""" 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 3ecb8028af8..5759f4a0e29 100644 --- a/tests/test_litellm/llms/sap/chat/test_sap_transformation.py +++ b/tests/test_litellm/llms/sap/chat/test_sap_transformation.py @@ -1,4 +1,5 @@ import pytest +from pydantic import ValidationError class TestSAPTransformationIntegration: """Integration tests for SAP transformation with parameter classification.""" @@ -265,6 +266,25 @@ class TestSAPTransformationIntegration: assert config["placeholder_values"] == placeholder_values assert config["config"]["modules"]["prompt_templating"]["model"]["params"] == {} + def test_grounding_search_config_rejects_both_count_fields(self, mock_config): + with pytest.raises(ValidationError): + mock_config.transform_request( + model="gpt-4o", + messages=[{"role": "user", "content": "Hi"}], + optional_params={ + "grounding": { + "type": "document_grounding_service", + "config": { + "filters": [{"data_repository_type": "vector", + "search_config": {"max_chunk_count": 2, + "max_document_count": 5}}], + "placeholders": {"input": ["q"], "output": "r"}, + } + } + }, + litellm_params={}, headers={} + ) + def test_sap_filtering(self, mock_config): filtering_config_azure = { 'input': @@ -340,6 +360,21 @@ class TestSAPTransformationIntegration: assert config["config"]["modules"]["filtering"] == filtering_config_llama assert config["config"]["modules"]["prompt_templating"]["model"]["params"] == {} + def test_filtering_config_requires_at_least_one_property(self, mock_config): + with pytest.raises(ValidationError) as exc_info: + mock_config.transform_request( + model="gpt-4o", + messages=[{"role": "user", "content": "Hello"}], + optional_params={ + "filtering": {} + }, + litellm_params={}, + headers={} + ) + + assert "For using SAP Filtering Module you must provide at least one property" in str(exc_info.value) + + def test_sap_masking(self, mock_config): masking_config = { 'providers': @@ -369,6 +404,46 @@ class TestSAPTransformationIntegration: assert config["config"]["modules"]["masking"] == masking_config assert config["config"]["modules"]["prompt_templating"]["model"]["params"] == {} + def test_masking_config_requires_exactly_one_provider_list(self, mock_config): + masking_config = { + 'providers': + [ + { + 'type': 'sap_data_privacy_integration', + 'method': 'anonymization', + 'entities': [ + {'type': 'profile-address'}, + {'type': 'profile-email'}, + {'type': 'profile-phone'}, + {'type': 'profile-person'}, + {'type': 'profile-location'} + ] + } + ], + 'masking_providers': + [ + { + 'type': 'sap_data_privacy_integration', + 'method': 'anonymization', + 'entities': [ + {'type': 'profile-address'} + ] + } + ] + } + with pytest.raises(ValidationError) as exc_info: + mock_config.transform_request( + model="gpt-4o", + messages=[{"role": "user", "content": "Hello"}], + optional_params={ + "masking": masking_config + }, + litellm_params={}, + headers={} + ) + + assert "must set exactly one of: 'providers' or 'masking_providers'" in str(exc_info.value) + def test_sap_translation(self, mock_config): translation_config = { 'input': @@ -396,6 +471,20 @@ class TestSAPTransformationIntegration: assert config["config"]["modules"]["translation"] == translation_config assert config["config"]["modules"]["prompt_templating"]["model"]["params"] == {} + def test_translation_config_requires_at_least_one_property(self, mock_config): + with pytest.raises(ValidationError) as exc_info: + mock_config.transform_request( + model="gpt-4o", + messages=[{"role": "user", "content": "Hello"}], + optional_params={ + "translation": {} + }, + litellm_params={}, + headers={} + ) + + assert "TranslationModuleConfig requires at least one of 'input' or 'output'" in str(exc_info.value) + def test_sap_multiple_modules(self, mock_config): translation_config = { 'input':