diff --git a/litellm/litellm_core_utils/get_litellm_params.py b/litellm/litellm_core_utils/get_litellm_params.py index b8ef9d8cca7..0a6c7e1df9a 100644 --- a/litellm/litellm_core_utils/get_litellm_params.py +++ b/litellm/litellm_core_utils/get_litellm_params.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, Union from litellm.llms.openai.data_residency import infer_openai_data_residency @@ -107,7 +107,7 @@ def get_litellm_params( prompt_id: Optional[str] = None, prompt_variables: Optional[dict] = None, async_call: Optional[bool] = None, - ssl_verify: Optional[bool] = None, + ssl_verify: Optional[Union[bool, str]] = None, merge_reasoning_content_in_choices: Optional[bool] = None, use_litellm_proxy: Optional[bool] = None, api_version: Optional[str] = None, diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 96d0ad48b79..4f75d6b3918 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -1298,7 +1298,7 @@ class BaseLLMHTTPHandler: ) if client is None or not isinstance(client, HTTPHandler): - client = _get_httpx_client() + client = _get_httpx_client(params={"ssl_verify": litellm_params.get("ssl_verify", None)}) json_data = data if files is None and isinstance(data, dict) else None diff --git a/litellm/llms/hosted_vllm/transcriptions/transformation.py b/litellm/llms/hosted_vllm/transcriptions/transformation.py index e726ee33abf..b772710603c 100644 --- a/litellm/llms/hosted_vllm/transcriptions/transformation.py +++ b/litellm/llms/hosted_vllm/transcriptions/transformation.py @@ -6,6 +6,7 @@ from typing import Optional, Union import httpx +from litellm.litellm_core_utils.audio_utils.utils import process_audio_file from litellm.llms.base_llm.audio_transcription.transformation import ( AudioTranscriptionRequestData, ) @@ -54,12 +55,19 @@ class HostedVLLMAudioTranscriptionConfig(OpenAIWhisperAudioTranscriptionConfig): optional_params: dict, litellm_params: dict, ) -> AudioTranscriptionRequestData: - """ - Transform the audio transcription request - """ + processed_audio = process_audio_file(audio_file) + extra_body = optional_params.get("extra_body") or {} + data = { + "model": model, + **{key: value for key, value in optional_params.items() if key != "extra_body"}, + **extra_body, + } + files = { + "file": ( + processed_audio.filename, + processed_audio.file_content, + processed_audio.content_type, + ) + } - data = {"model": model, "file": audio_file, **optional_params} - - return AudioTranscriptionRequestData( - data=data, - ) + return AudioTranscriptionRequestData(data=data, files=files) diff --git a/litellm/main.py b/litellm/main.py index 6fd68921fb0..d7c7ff0bd12 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -7620,7 +7620,9 @@ def transcription( max_retries=max_retries, litellm_params=litellm_params_dict, ) - elif custom_llm_provider == "openai" or (custom_llm_provider in litellm.openai_compatible_providers): + elif custom_llm_provider == "openai" or ( + custom_llm_provider in litellm.openai_compatible_providers and custom_llm_provider != "hosted_vllm" + ): api_base = ( api_base or litellm.api_base diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 6487a8aa33f..f140b90e0ef 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -3205,6 +3205,7 @@ all_litellm_params = ( "use_chat_completions_api", "prompt_label", "shared_session", + "ssl_verify", "search_tool_name", "order", "enable_json_schema_validation", diff --git a/tests/test_litellm/llms/hosted_vllm/transcriptions/test_transformation.py b/tests/test_litellm/llms/hosted_vllm/transcriptions/test_transformation.py new file mode 100644 index 00000000000..8f935e0a053 --- /dev/null +++ b/tests/test_litellm/llms/hosted_vllm/transcriptions/test_transformation.py @@ -0,0 +1,59 @@ +from unittest.mock import AsyncMock, MagicMock, patch + +import httpx +import pytest + +import litellm + + +def _transcription_response() -> httpx.Response: + return httpx.Response( + status_code=200, + headers={"content-type": "application/json"}, + json={"text": "Test transcription"}, + ) + + +def test_transcription_passes_custom_ca_to_sync_http_client() -> None: + client = MagicMock() + client.post.return_value = _transcription_response() + + with patch( + "litellm.llms.custom_httpx.llm_http_handler._get_httpx_client", + return_value=client, + ) as get_httpx_client: + response = litellm.transcription( + model="hosted_vllm/whisper-1", + file=("audio.wav", b"audio", "audio/wav"), + api_base="https://vllm.example.com", + ssl_verify="/path/to/ca-cert.crt", + ) + + assert response.text == "Test transcription" + assert get_httpx_client.call_args.kwargs["params"]["ssl_verify"] == "/path/to/ca-cert.crt" + request = client.post.call_args.kwargs + assert request["data"] == {"model": "whisper-1"} + assert request["files"] == {"file": ("audio.wav", b"audio", "audio/wav")} + + +@pytest.mark.asyncio +async def test_transcription_passes_custom_ca_to_async_http_client() -> None: + client = MagicMock() + client.post = AsyncMock(return_value=_transcription_response()) + + with patch( + "litellm.llms.custom_httpx.llm_http_handler.get_async_httpx_client", + return_value=client, + ) as get_async_httpx_client: + response = await litellm.atranscription( + model="hosted_vllm/whisper-1", + file=("audio.wav", b"audio", "audio/wav"), + api_base="https://vllm.example.com", + ssl_verify="/path/to/ca-cert.crt", + ) + + assert response.text == "Test transcription" + assert get_async_httpx_client.call_args.kwargs["params"]["ssl_verify"] == "/path/to/ca-cert.crt" + request = client.post.call_args.kwargs + assert request["data"] == {"model": "whisper-1"} + assert request["files"] == {"file": ("audio.wav", b"audio", "audio/wav")}