feat: handle Deepgram detected language when available (#16093)

This commit is contained in:
Geoffray Viossat 2025-10-31 03:19:34 +01:00 • committed by GitHub
parent cde9ba5f93
commit b29ba9e3be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 202 additions and 12 deletions

View file

@ -58,7 +58,7 @@ class DeepgramAudioTranscriptionConfig(BaseAudioTranscriptionConfig):
) -> AudioTranscriptionRequestData:
"""
Processes the audio file input based on its type and returns AudioTranscriptionRequestData.
For Deepgram, the binary audio data is sent directly as the request body.
Args:
@ -69,12 +69,11 @@ class DeepgramAudioTranscriptionConfig(BaseAudioTranscriptionConfig):
"""
# Use common utility to process the audio file
processed_audio = process_audio_file(audio_file)
# Return structured data with binary content and no files
# For Deepgram, we send binary data directly as request body
return AudioTranscriptionRequestData(
data=processed_audio.file_content,
files=None
data=processed_audio.file_content, files=None
)
def transform_audio_transcription_response(
@ -99,9 +98,11 @@ class DeepgramAudioTranscriptionConfig(BaseAudioTranscriptionConfig):
# Add additional metadata matching OpenAI format
response["task"] = "transcribe"
response["language"] = (
"english" # Deepgram auto-detects but doesn't return language
)
# Use detected_language if available, otherwise default to "en"
detected_language = first_channel.get("detected_language")
response["language"] = detected_language if detected_language else "en"
response["duration"] = response_json["metadata"]["duration"]
# Transform words to match OpenAI format
@ -150,7 +151,6 @@ class DeepgramAudioTranscriptionConfig(BaseAudioTranscriptionConfig):
return url
def _format_param_value(self, value) -> str:
"""
Formats a parameter value for use in query string.
@ -180,7 +180,7 @@ class DeepgramAudioTranscriptionConfig(BaseAudioTranscriptionConfig):
provider_specific_params = self.get_provider_specific_params(
optional_params=optional_params,
model=model,
openai_params=self.get_supported_openai_params(model)
openai_params=self.get_supported_openai_params(model),
)
for key, value in provider_specific_params.items():

View file

@ -58,13 +58,13 @@ def test_audio_file_handling(fixture_name, request):
optional_params={},
litellm_params={},
)
# Check that result is AudioTranscriptionRequestData
assert isinstance(result, AudioTranscriptionRequestData)
# Check that data matches expected output
assert result.data == expected_output
# Check that files is None for Deepgram (binary data)
assert result.files is None
@ -202,3 +202,39 @@ def test_get_complete_url_with_string_values():
assert "version=latest" in url
assert "punctuate=true" in url
assert url.startswith("https://api.deepgram.com/v1/listen?")
def test_get_complete_url_with_detect_language():
"""Test URL generation with detect_language parameter"""
handler = DeepgramAudioTranscriptionConfig()
url = handler.get_complete_url(
api_base=None,
api_key=None,
model="nova-2",
optional_params={"detect_language": True},
litellm_params={},
)
expected_url = "https://api.deepgram.com/v1/listen?model=nova-2&detect_language=true"
assert url == expected_url
def test_get_complete_url_with_detect_language_and_other_params():
"""Test URL generation with detect_language and other parameters"""
handler = DeepgramAudioTranscriptionConfig()
url = handler.get_complete_url(
api_base=None,
api_key=None,
model="nova-2",
optional_params={
"detect_language": True,
"punctuate": True,
"diarize": False,
},
litellm_params={},
)
# URL should contain all parameters
assert "model=nova-2" in url
assert "detect_language=true" in url
assert "punctuate=true" in url
assert "diarize=false" in url
assert url.startswith("https://api.deepgram.com/v1/listen?")

View file

@ -277,3 +277,157 @@ class TestDeepgramMockTranscription:
# Verify response
assert response.text == "Hello, this is a test transcription."
def test_transcription_response_with_detected_language(self, test_audio_bytes):
"""Test response transformation when detected_language is present"""
# Mock response with detected_language
mock_response_data = {
"metadata": {
"transaction_key": "deprecated",
"request_id": "test-request-id",
"sha256": "test-sha",
"created": "2024-01-01T00:00:00.000Z",
"duration": 1.2,
"channels": 1,
"models": ["nova-2"],
},
"results": {
"channels": [
{
"detected_language": "fr",
"alternatives": [
{
"transcript": "Bonjour le monde",
"confidence": 0.99,
"words": [
{"word": "Bonjour", "start": 0.0, "end": 0.5, "confidence": 0.99},
{"word": "le", "start": 0.5, "end": 0.7, "confidence": 0.98},
{"word": "monde", "start": 0.7, "end": 1.2, "confidence": 0.97},
]
}
]
}
]
},
}
mock_response = MagicMock()
mock_response.json.return_value = mock_response_data
mock_response.status_code = 200
mock_response.headers = {"Content-Type": "application/json"}
with patch(
"litellm.llms.custom_httpx.http_handler.HTTPHandler.post",
return_value=mock_response,
):
response: TranscriptionResponse = litellm.transcription(
model="deepgram/nova-2",
file=test_audio_bytes,
api_key="test-api-key",
detect_language=True,
)
# Verify that detected_language is used
assert response["language"] == "fr"
assert response.text == "Bonjour le monde"
assert response["task"] == "transcribe"
assert response["duration"] == 1.2
assert len(response["words"]) == 3
def test_transcription_response_without_detected_language(self, test_audio_bytes):
"""Test response transformation when detected_language is not present"""
# Mock response without detected_language
mock_response_data = {
"metadata": {
"transaction_key": "deprecated",
"request_id": "test-request-id",
"sha256": "test-sha",
"created": "2024-01-01T00:00:00.000Z",
"duration": 0.8,
"channels": 1,
"models": ["nova-2"],
},
"results": {
"channels": [
{
"alternatives": [
{
"transcript": "Hello world",
"confidence": 0.99,
}
]
}
]
},
}
mock_response = MagicMock()
mock_response.json.return_value = mock_response_data
mock_response.status_code = 200
mock_response.headers = {"Content-Type": "application/json"}
with patch(
"litellm.llms.custom_httpx.http_handler.HTTPHandler.post",
return_value=mock_response,
):
response: TranscriptionResponse = litellm.transcription(
model="deepgram/nova-2",
file=test_audio_bytes,
api_key="test-api-key",
)
# Verify that default language "en" is used
assert response["language"] == "en"
assert response.text == "Hello world"
assert response["task"] == "transcribe"
assert response["duration"] == 0.8
def test_transcription_response_with_empty_detected_language(self, test_audio_bytes):
"""Test response transformation when detected_language is present but None"""
# Mock response with None detected_language
mock_response_data = {
"metadata": {
"transaction_key": "deprecated",
"request_id": "test-request-id",
"sha256": "test-sha",
"created": "2024-01-01T00:00:00.000Z",
"duration": 1.0,
"channels": 1,
"models": ["nova-2"],
},
"results": {
"channels": [
{
"detected_language": None,
"alternatives": [
{
"transcript": "Test transcript",
"confidence": 0.99,
}
]
}
]
},
}
mock_response = MagicMock()
mock_response.json.return_value = mock_response_data
mock_response.status_code = 200
mock_response.headers = {"Content-Type": "application/json"}
with patch(
"litellm.llms.custom_httpx.http_handler.HTTPHandler.post",
return_value=mock_response,
):
response: TranscriptionResponse = litellm.transcription(
model="deepgram/nova-2",
file=test_audio_bytes,
api_key="test-api-key",
detect_language=True,
)
# Verify that default language "en" is used when detected_language is None
assert response["language"] == "en"
assert response.text == "Test transcript"
assert response["task"] == "transcribe"
assert response["duration"] == 1.0