mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
fix: send whisper timestamp_granularities as bracketed array field
OpenAI's multipart transcription API expects array params as repeated bracketed fields (timestamp_granularities[]=segment & timestamp_granularities[]=word). LiteLLM stored the list under the bare timestamp_granularities key, so OpenAI kept only the last value and a combined ["segment", "word"] request silently returned just one granularity (words OR segments, never both). Rename the key to timestamp_granularities[] in transform_audio_transcription_request so httpx encodes it as the repeated bracketed fields OpenAI expects. This matches how the official openai-python client serializes array form fields (array_format="brackets"). Fixes the multi-value case; single-value arrays already worked and continue to. Co-authored-by: TRAE CLI <noreply@bytedance.com>
This commit is contained in:
parent
ead62528e6
commit
7ec44c042a
2 changed files with 60 additions and 0 deletions
|
|
@ -109,6 +109,16 @@ class OpenAIWhisperAudioTranscriptionConfig(BaseAudioTranscriptionConfig):
|
|||
if "response_format" not in data:
|
||||
data["response_format"] = "verbose_json" # ensures 'duration' is received - used for cost calculation
|
||||
|
||||
# OpenAI's multipart form API expects array params as repeated
|
||||
# bracketed fields (`timestamp_granularities[]=segment` +
|
||||
# `timestamp_granularities[]=word`). Sending the list under the bare
|
||||
# `timestamp_granularities` key makes OpenAI keep only the last value,
|
||||
# so a combined `["segment", "word"]` request silently returns just one
|
||||
# granularity. Rename the key so the list is encoded as `[]` fields.
|
||||
granularities = data.pop("timestamp_granularities", None)
|
||||
if granularities is not None:
|
||||
data["timestamp_granularities[]"] = granularities
|
||||
|
||||
return AudioTranscriptionRequestData(
|
||||
data=data,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,56 @@ class TestWhisperTransformRequestResponseFormat:
|
|||
assert data["response_format"] == "verbose_json"
|
||||
|
||||
|
||||
class TestWhisperTransformTimestampGranularities:
|
||||
"""
|
||||
OpenAI's multipart form API expects array params as repeated bracketed
|
||||
fields (``timestamp_granularities[]``). If the list is sent under the bare
|
||||
``timestamp_granularities`` key, OpenAI keeps only the last value, so a
|
||||
combined ``["segment", "word"]`` request silently returns one granularity.
|
||||
"""
|
||||
|
||||
def _transform(self, optional_params: dict) -> dict:
|
||||
config = OpenAIWhisperAudioTranscriptionConfig()
|
||||
audio_file = io.BytesIO(b"fake audio")
|
||||
audio_file.name = "test.wav"
|
||||
result = config.transform_audio_transcription_request(
|
||||
model="whisper-1",
|
||||
audio_file=audio_file,
|
||||
optional_params=optional_params,
|
||||
litellm_params={},
|
||||
)
|
||||
return result.data
|
||||
|
||||
def test_multi_value_list_uses_bracketed_key(self):
|
||||
"""A multi-value list is sent under the bracketed key so both are honored."""
|
||||
data = self._transform(
|
||||
{
|
||||
"response_format": "verbose_json",
|
||||
"timestamp_granularities": ["segment", "word"],
|
||||
}
|
||||
)
|
||||
assert data["timestamp_granularities[]"] == ["segment", "word"]
|
||||
# The bare key must not be present, otherwise OpenAI drops all but the last.
|
||||
assert "timestamp_granularities" not in data
|
||||
|
||||
def test_single_value_list_uses_bracketed_key(self):
|
||||
"""A single-value list is also sent under the bracketed key for consistency."""
|
||||
data = self._transform(
|
||||
{
|
||||
"response_format": "verbose_json",
|
||||
"timestamp_granularities": ["word"],
|
||||
}
|
||||
)
|
||||
assert data["timestamp_granularities[]"] == ["word"]
|
||||
assert "timestamp_granularities" not in data
|
||||
|
||||
def test_absent_granularities_not_added(self):
|
||||
"""When timestamp_granularities is not provided, no bracketed key is added."""
|
||||
data = self._transform({"response_format": "verbose_json"})
|
||||
assert "timestamp_granularities[]" not in data
|
||||
assert "timestamp_granularities" not in data
|
||||
|
||||
|
||||
class TestWhisperTransformResponse:
|
||||
def _make_response(self, *, text: str, content_type: str, is_json: bool):
|
||||
mock = MagicMock()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue