litellm/tests/llm_translation/test_elevenlabs.py
yuneng-jiang 6a0d03914c
test: drop the cwd-relative sys.path.insert calls from the test suite (#37802)
* test: drop the cwd-relative sys.path.insert calls from the test suite

TQ003 stands at 1,077 across 1,058 files, and 1,015 of them are the same shape:
sys.path.insert(0, os.path.abspath("../..")) and its deeper siblings. The
argument resolves against the working directory rather than the file, so from
the repo root, where every job runs pytest, it inserts the directory two levels
above the checkout. It has never pointed at litellm. The package is installed
into the environment anyway, which is what actually makes the import work, and
what the rule's message has said all along.

Removing them leaves 1,634 imports of sys and os with no remaining reference,
and those go too, except where another test module imports the name back out of
the file. The rest of TQ003 is 62 call sites that resolve against __file__ or a
variable, which are a different question and are left alone.

Collection is identical either way: 45,871 tests and the same 51 pre-existing
collection errors before and after, and ruff reports no new undefined name.

* test: drop the duplicate imports the sys.path sweep exposed to F811

* test(pre-call-utils): restore the os import the new bedrock tests need
2026-08-22 09:25:58 -07:00

211 lines
7.8 KiB
Python

import os
from typing import Any, Dict
import pytest
from unittest.mock import patch, MagicMock
import httpx
import litellm
from base_audio_transcription_unit_tests import BaseLLMAudioTranscriptionTest
os.environ.setdefault("ELEVENLABS_API_KEY", "test-elevenlabs-key")
class TestElevenLabsAudioTranscription(BaseLLMAudioTranscriptionTest):
def get_base_audio_transcription_call_args(self) -> dict:
return {
"model": "elevenlabs/scribe_v1",
}
def get_custom_llm_provider(self) -> litellm.LlmProviders:
return litellm.LlmProviders.ELEVENLABS
def test_elevenlabs_diarize_parameter_passthrough(self):
"""
Test that provider-specific parameters like diarize=True get passed through
to the ElevenLabs request form data.
"""
# Mock successful response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.text = (
'{"text": "Four score and seven years ago", "language_code": "en"}'
)
mock_response.json.return_value = {
"text": "Four score and seven years ago",
"language_code": "en",
"words": [
{"type": "word", "text": "Four", "start": 0.0, "end": 0.5},
{"type": "word", "text": "score", "start": 0.5, "end": 1.0},
],
}
# Create a mock audio file
audio_content = b"fake audio data"
captured_request_data = {}
def mock_post(*args, **kwargs):
# Capture the request data for verification
captured_request_data.update(
{
"url": kwargs.get("url"),
"data": kwargs.get("data"),
"files": kwargs.get("files"),
"headers": kwargs.get("headers"),
"json": kwargs.get("json"),
}
)
return mock_response
# Mock the HTTPHandler.post method which is what actually makes the request
from litellm.llms.custom_httpx.http_handler import HTTPHandler
with patch.object(HTTPHandler, "post", side_effect=mock_post):
try:
result = litellm.transcription(
model="elevenlabs/scribe_v1",
file=audio_content,
diarize=True, # This should be passed through to the form data
language="en", # This should be mapped to language_code
temperature=0.5, # This should also be passed through
custom_param="test_value", # This should also be passed through
)
# Verify the request was made with correct form data
assert "speech-to-text" in captured_request_data["url"]
# Check that form data contains the expected parameters
form_data = captured_request_data["data"]
assert form_data is not None, "Form data should not be None"
print(f"✅ Captured form data: {form_data}")
# Check basic required parameters
assert "model_id" in form_data, "model_id should be in form data"
assert (
form_data["model_id"] == "scribe_v1"
), f"Expected model_id 'scribe_v1', got {form_data['model_id']}"
# Check that diarize parameter is passed through
assert (
"diarize" in form_data
), f"diarize should be in form data. Got: {list(form_data.keys())}"
assert (
form_data["diarize"] == "True"
), f"Expected diarize='True', got {form_data['diarize']}"
# Check that OpenAI language parameter is mapped correctly
assert (
"language_code" in form_data
), "language_code should be in form data"
assert (
form_data["language_code"] == "en"
), f"Expected language_code='en', got {form_data['language_code']}"
# Check that temperature is passed through
assert "temperature" in form_data, "temperature should be in form data"
assert (
form_data["temperature"] == "0.5"
), f"Expected temperature='0.5', got {form_data['temperature']}"
# Check that custom parameters are passed through
assert (
"custom_param" in form_data
), "custom_param should be in form data"
assert (
form_data["custom_param"] == "test_value"
), f"Expected custom_param='test_value', got {form_data['custom_param']}"
# Check that files are included
files = captured_request_data["files"]
assert files is not None, "Files should not be None"
assert "file" in files, "file should be in files"
print("✅ All parameter passthrough tests passed!")
except Exception as e:
print(f"❌ Test failed: {e}")
print(f"Captured request data: {captured_request_data}")
raise
class TestElevenLabsTextToSpeechTransformation:
@pytest.fixture(scope="class")
def config(self):
from litellm.llms.elevenlabs.text_to_speech.transformation import (
ElevenLabsTextToSpeechConfig,
)
return ElevenLabsTextToSpeechConfig()
def test_map_openai_params_maps_voice_and_speed(self, config):
kwargs: Dict[str, Any] = {}
mapped_voice, mapped_params = config.map_openai_params(
model="eleven_multilingual_v2",
optional_params={
"response_format": "mp3",
"speed": 1.25,
"model_id": "eleven_multilingual_v2",
},
voice="alloy",
kwargs=kwargs,
)
assert mapped_voice == config.VOICE_MAPPINGS["alloy"]
assert mapped_params["voice_settings"]["speed"] == pytest.approx(1.25)
assert (
kwargs[config.ELEVENLABS_QUERY_PARAMS_KEY]["output_format"]
== "mp3_44100_128"
)
def test_transform_request_and_url(self, config):
kwargs: Dict[str, Any] = {}
voice_id, optional_params = config.map_openai_params(
model="eleven_multilingual_v2",
optional_params={
"response_format": "pcm",
"model_id": "eleven_multilingual_v2",
"pronunciation_dictionary_locators": [
{"pronunciation_dictionary_id": "dict_1"}
],
},
voice="alloy",
kwargs=kwargs,
)
litellm_params: Dict[str, Any] = {
config.ELEVENLABS_VOICE_ID_KEY: voice_id,
config.ELEVENLABS_QUERY_PARAMS_KEY: kwargs[
config.ELEVENLABS_QUERY_PARAMS_KEY
],
}
headers = config.validate_environment(
headers={}, model="eleven_multilingual_v2", api_key="test-key"
)
request_data = config.transform_text_to_speech_request(
model="eleven_multilingual_v2",
input="Hello world",
voice=voice_id,
optional_params=optional_params,
litellm_params=litellm_params,
headers=headers,
)
assert request_data["dict_body"]["text"] == "Hello world"
assert request_data["dict_body"]["model_id"] == "eleven_multilingual_v2"
assert request_data["dict_body"]["pronunciation_dictionary_locators"] == [
{"pronunciation_dictionary_id": "dict_1"}
]
url = config.get_complete_url(
model="eleven_multilingual_v2",
api_base=None,
litellm_params=litellm_params,
)
assert voice_id in url
assert "output_format=pcm_44100" in url