Skip to content

Commit 0cb7dd7

Browse files
[Feat] Add provider specific params for deepgram/ (#11638)
* Checkpoint before follow-up message * Add comprehensive tests for Deepgram transcription functionality * clean up transform * just use 1 test * test cleanup * test fix get_complete_url * test rename file * refactor deepgram URL construction * add logging_obj.pre_call * fix unused imports --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent f3f7bc1 commit 0cb7dd7

File tree

5 files changed

+575
-61
lines changed

5 files changed

+575
-61
lines changed

litellm/llms/custom_httpx/llm_http_handler.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,17 @@ def audio_transcriptions(
10381038
else:
10391039
json_data = data
10401040

1041+
## LOGGING
1042+
logging_obj.pre_call(
1043+
input=optional_params.get("query", ""),
1044+
api_key=api_key,
1045+
additional_args={
1046+
"complete_input_dict": {},
1047+
"api_base": complete_url,
1048+
"headers": headers,
1049+
},
1050+
)
1051+
10411052
try:
10421053
# Make the POST request
10431054
response = client.post(

litellm/llms/deepgram/audio_transcription/transformation.py

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import io
66
from typing import List, Optional, Union
7+
from urllib.parse import urlencode
78

89
from httpx import Headers, Response
910

@@ -126,9 +127,9 @@ def transform_audio_transcription_response(
126127

127128
# Add additional metadata matching OpenAI format
128129
response["task"] = "transcribe"
129-
response[
130-
"language"
131-
] = "english" # Deepgram auto-detects but doesn't return language
130+
response["language"] = (
131+
"english" # Deepgram auto-detects but doesn't return language
132+
)
132133
response["duration"] = response_json["metadata"]["duration"]
133134

134135
# Transform words to match OpenAI format
@@ -163,7 +164,95 @@ def get_complete_url(
163164
)
164165
api_base = api_base.rstrip("/") # Remove trailing slash if present
165166

166-
return f"{api_base}/listen?model={model}"
167+
# Build query parameters including the model
168+
all_query_params = {"model": model}
169+
170+
# Add filtered optional parameters
171+
additional_params = self._build_query_params(optional_params, model)
172+
all_query_params.update(additional_params)
173+
174+
# Construct URL with proper query string encoding
175+
base_url = f"{api_base}/listen"
176+
query_string = urlencode(all_query_params)
177+
url = f"{base_url}?{query_string}"
178+
179+
return url
180+
181+
def _should_exclude_param(
182+
self,
183+
param_name: str,
184+
model: str,
185+
) -> bool:
186+
"""
187+
Determines if a parameter should be excluded from the query string.
188+
189+
Args:
190+
param_name: Parameter name
191+
model: Model name
192+
193+
Returns:
194+
True if the parameter should be excluded
195+
"""
196+
# Parameters that are handled elsewhere or not relevant to Deepgram API
197+
excluded_params = {
198+
"model", # Already in the URL path
199+
"OPENAI_TRANSCRIPTION_PARAMS", # Internal litellm parameter
200+
}
201+
202+
# Skip if it's an excluded parameter
203+
if param_name in excluded_params:
204+
return True
205+
206+
# Skip if it's an OpenAI-specific parameter that we handle separately
207+
if param_name in self.get_supported_openai_params(model):
208+
return True
209+
210+
return False
211+
212+
def _format_param_value(self, value) -> str:
213+
"""
214+
Formats a parameter value for use in query string.
215+
216+
Args:
217+
value: The parameter value to format
218+
219+
Returns:
220+
Formatted string value
221+
"""
222+
if isinstance(value, bool):
223+
return str(value).lower()
224+
return str(value)
225+
226+
def _build_query_params(self, optional_params: dict, model: str) -> dict:
227+
"""
228+
Builds a dictionary of query parameters from optional_params.
229+
230+
Args:
231+
optional_params: Dictionary of optional parameters
232+
model: Model name
233+
234+
Returns:
235+
Dictionary of filtered and formatted query parameters
236+
"""
237+
query_params = {}
238+
239+
for key, value in optional_params.items():
240+
# Skip None values
241+
if value is None:
242+
continue
243+
244+
# Skip excluded parameters
245+
if self._should_exclude_param(
246+
param_name=key,
247+
model=model,
248+
):
249+
continue
250+
251+
# Format and add the parameter
252+
formatted_value = self._format_param_value(value)
253+
query_params[key] = formatted_value
254+
255+
return query_params
167256

168257
def validate_environment(
169258
self,
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import io
2+
import os
3+
import pathlib
4+
import sys
5+
6+
import pytest
7+
8+
sys.path.insert(
9+
0, os.path.abspath("../../../../..")
10+
) # Adds the parent directory to the system path
11+
12+
import litellm
13+
from litellm.llms.deepgram.audio_transcription.transformation import (
14+
DeepgramAudioTranscriptionConfig,
15+
)
16+
17+
18+
@pytest.fixture
19+
def test_bytes():
20+
return b"litellm", b"litellm"
21+
22+
23+
@pytest.fixture
24+
def test_io_bytes(test_bytes):
25+
return io.BytesIO(test_bytes[0]), test_bytes[1]
26+
27+
28+
@pytest.fixture
29+
def test_file():
30+
pwd = os.path.dirname(os.path.realpath(__file__))
31+
pwd_path = pathlib.Path(pwd)
32+
test_root = pwd_path.parents[3]
33+
print(f"test_root: {test_root}")
34+
file_path = os.path.join(test_root, "gettysburg.wav")
35+
f = open(file_path, "rb")
36+
content = f.read()
37+
f.seek(0)
38+
return f, content
39+
40+
41+
@pytest.mark.parametrize(
42+
"fixture_name",
43+
[
44+
"test_bytes",
45+
"test_io_bytes",
46+
"test_file",
47+
],
48+
)
49+
def test_audio_file_handling(fixture_name, request):
50+
handler = DeepgramAudioTranscriptionConfig()
51+
(audio_file, expected_output) = request.getfixturevalue(fixture_name)
52+
assert expected_output == handler.transform_audio_transcription_request(
53+
model="deepseek-audio-transcription",
54+
audio_file=audio_file,
55+
optional_params={},
56+
litellm_params={},
57+
)
58+
59+
60+
def test_get_complete_url_basic():
61+
"""Test basic URL generation without optional parameters"""
62+
handler = DeepgramAudioTranscriptionConfig()
63+
url = handler.get_complete_url(
64+
api_base=None,
65+
api_key=None,
66+
model="nova-2",
67+
optional_params={},
68+
litellm_params={},
69+
)
70+
expected_url = "https://api.deepgram.com/v1/listen?model=nova-2"
71+
assert url == expected_url
72+
73+
74+
def test_get_complete_url_with_punctuate():
75+
"""Test URL generation with punctuate parameter"""
76+
handler = DeepgramAudioTranscriptionConfig()
77+
url = handler.get_complete_url(
78+
api_base=None,
79+
api_key=None,
80+
model="nova-2",
81+
optional_params={"punctuate": True},
82+
litellm_params={},
83+
)
84+
expected_url = "https://api.deepgram.com/v1/listen?model=nova-2&punctuate=true"
85+
assert url == expected_url
86+
87+
88+
def test_get_complete_url_with_diarize():
89+
"""Test URL generation with diarize parameter"""
90+
handler = DeepgramAudioTranscriptionConfig()
91+
url = handler.get_complete_url(
92+
api_base=None,
93+
api_key=None,
94+
model="nova-2",
95+
optional_params={"diarize": True},
96+
litellm_params={},
97+
)
98+
expected_url = "https://api.deepgram.com/v1/listen?model=nova-2&diarize=true"
99+
assert url == expected_url
100+
101+
102+
def test_get_complete_url_with_measurements():
103+
"""Test URL generation with measurements parameter"""
104+
handler = DeepgramAudioTranscriptionConfig()
105+
url = handler.get_complete_url(
106+
api_base=None,
107+
api_key=None,
108+
model="nova-2",
109+
optional_params={"measurements": True},
110+
litellm_params={},
111+
)
112+
expected_url = "https://api.deepgram.com/v1/listen?model=nova-2&measurements=true"
113+
assert url == expected_url
114+
115+
116+
def test_get_complete_url_with_multiple_params():
117+
"""Test URL generation with multiple query parameters"""
118+
handler = DeepgramAudioTranscriptionConfig()
119+
url = handler.get_complete_url(
120+
api_base=None,
121+
api_key=None,
122+
model="nova-2",
123+
optional_params={
124+
"punctuate": True,
125+
"diarize": False,
126+
"measurements": True,
127+
"smart_format": True,
128+
},
129+
litellm_params={},
130+
)
131+
# URL should contain all parameters
132+
assert "model=nova-2" in url
133+
assert "punctuate=true" in url
134+
assert "diarize=false" in url
135+
assert "measurements=true" in url
136+
assert "smart_format=true" in url
137+
assert url.startswith("https://api.deepgram.com/v1/listen?")
138+
139+
140+
def test_get_complete_url_with_language_parameter():
141+
"""Test that language parameter is excluded from query string (handled separately)"""
142+
handler = DeepgramAudioTranscriptionConfig()
143+
url = handler.get_complete_url(
144+
api_base=None,
145+
api_key=None,
146+
model="nova-2",
147+
optional_params={
148+
"language": "en",
149+
"punctuate": True,
150+
},
151+
litellm_params={},
152+
)
153+
expected_url = "https://api.deepgram.com/v1/listen?model=nova-2&punctuate=true"
154+
assert url == expected_url
155+
# Language should NOT appear in URL as it's handled separately
156+
assert "language=" not in url
157+
158+
159+
def test_get_complete_url_with_custom_api_base():
160+
"""Test URL generation with custom API base"""
161+
handler = DeepgramAudioTranscriptionConfig()
162+
url = handler.get_complete_url(
163+
api_base="https://custom.deepgram.com/v2",
164+
api_key=None,
165+
model="nova-2",
166+
optional_params={"punctuate": True},
167+
litellm_params={},
168+
)
169+
expected_url = "https://custom.deepgram.com/v2/listen?model=nova-2&punctuate=true"
170+
assert url == expected_url
171+
172+
173+
def test_get_complete_url_with_string_values():
174+
"""Test URL generation with string parameter values"""
175+
handler = DeepgramAudioTranscriptionConfig()
176+
url = handler.get_complete_url(
177+
api_base=None,
178+
api_key=None,
179+
model="nova-2",
180+
optional_params={
181+
"tier": "enhanced",
182+
"version": "latest",
183+
"punctuate": True,
184+
},
185+
litellm_params={},
186+
)
187+
# URL should contain all parameters
188+
assert "model=nova-2" in url
189+
assert "tier=enhanced" in url
190+
assert "version=latest" in url
191+
assert "punctuate=true" in url
192+
assert url.startswith("https://api.deepgram.com/v1/listen?")

tests/test_litellm/llms/deepgram/audio_transcription/test_deepseek_audio_transcription_transformation.py

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)