diff --git a/litellm/llms/soniox/common_utils.py b/litellm/llms/soniox/common_utils.py index 2fd8ecf54de..ee25465e128 100644 --- a/litellm/llms/soniox/common_utils.py +++ b/litellm/llms/soniox/common_utils.py @@ -241,7 +241,8 @@ def _group_tokens_into_cues( subtitles never bridge pauses in speech, - adding the next word would exceed _CUE_MAX_CHARS of display width (~two subtitle lines; East-Asian wide characters count double), or - - the cue would span more than _CUE_MAX_DURATION_MS. + - adding the next word would make the cue span more than + _CUE_MAX_DURATION_MS. A cue also ends after sentence-final punctuation, which keeps cue breaks at natural seams. Cue timestamps come straight from token timestamps; words without timestamps stay attached to the surrounding cue. @@ -276,8 +277,9 @@ def _group_tokens_into_cues( ) gap_exceeded = start_ms is not None and cue_end is not None and (start_ms - cue_end) >= _CUE_GAP_MS chars_exceeded = _text_width(_cue_text(current)) + _text_width(word["text"]) > _CUE_MAX_CHARS + word_end = word["end_ms"] if word["end_ms"] is not None else start_ms duration_exceeded = ( - start_ms is not None and cue_start is not None and (start_ms - cue_start) >= _CUE_MAX_DURATION_MS + word_end is not None and cue_start is not None and (word_end - cue_start) > _CUE_MAX_DURATION_MS ) if speaker_changed or gap_exceeded or chars_exceeded or duration_exceeded: _flush() diff --git a/tests/test_litellm/llms/soniox/audio_transcription/test_soniox_audio_transcription_transformation.py b/tests/test_litellm/llms/soniox/audio_transcription/test_soniox_audio_transcription_transformation.py index 2917d5663ac..cf7b5679dfe 100644 --- a/tests/test_litellm/llms/soniox/audio_transcription/test_soniox_audio_transcription_transformation.py +++ b/tests/test_litellm/llms/soniox/audio_transcription/test_soniox_audio_transcription_transformation.py @@ -455,6 +455,19 @@ class TestCueGroupingAlignment: assert "Guten" not in result assert "00:00:00,000 --> 00:00:00,600" in result + def test_should_split_before_word_whose_end_crosses_duration_cap(self): + from litellm.llms.soniox.common_utils import render_soniox_tokens_as_srt + + tokens = [{"text": " hm", "start_ms": i * 650, "end_ms": i * 650 + 600} for i in range(10)] + [ + {"text": " boom", "start_ms": 6900, "end_ms": 7600} + ] + result = render_soniox_tokens_as_srt(tokens) + cues = result.strip().split("\n\n") + assert len(cues) == 2 + assert "00:00:00,000 --> 00:00:06,450" in cues[0] + assert "00:00:06,900 --> 00:00:07,600" in cues[1] + assert cues[1].endswith("boom") + def test_should_keep_untimestamped_word_in_cue(self): from litellm.llms.soniox.common_utils import render_soniox_tokens_as_srt