fix(elevenlabs): keep the flat word list on diarized responses

This commit is contained in:
Ben Langfeld 2026-10-26 16:01:43 -03:00
parent fdfc08ae70
commit f22b17a925
No known key found for this signature in database
2 changed files with 24 additions and 13 deletions

View file

@ -3,7 +3,7 @@ Translates from OpenAI's `/v1/audio/transcriptions` to ElevenLabs's `/v1/speech-
"""
import io
from collections.abc import Iterable, Iterator, Mapping
from collections.abc import Iterable, Iterator, Mapping, Sequence
from itertools import chain, groupby
from types import MappingProxyType
from typing import Final, Protocol
@ -245,7 +245,10 @@ def _transcription_response(response_object: Mapping[str, object]) -> Transcript
words: Final = tuple(_JSON_OBJECTS.validate_python(response_object.get("words", ())))
if any(word.get("speaker_id") is not None for word in words):
return _diarized_response(ElevenLabsSTTChunk.model_validate(response_object))
response: Final = _diarized_response(ElevenLabsSTTChunk.model_validate(response_object))
# Callers already passing diarize=True read the flat word list, so it stays alongside the segments
response["words"] = _openai_words(words)
return response
return _flat_response(response_object, words)
@ -260,19 +263,23 @@ def _flat_response(
# Map ElevenLabs words to OpenAI format
if "words" in response_object:
response["words"] = [
{
"word": word_data.get("text", ""),
"start": word_data.get("start", 0),
"end": word_data.get("end", 0),
}
for word_data in words
if word_data.get("type") == "word"
]
response["words"] = _openai_words(words)
return response
def _openai_words(words: tuple[Mapping[str, object], ...]) -> Sequence[Mapping[str, object]]:
return [
{
"word": word_data.get("text", ""),
"start": word_data.get("start", 0),
"end": word_data.get("end", 0),
}
for word_data in words
if word_data.get("type") == "word"
]
def _diarized_response(transcript: ElevenLabsSTTChunk) -> TranscriptionResponse:
response: Final = TranscriptionResponse(text=transcript.text)
response["task"] = "transcribe"

View file

@ -48,7 +48,7 @@ def test_transform_audio_transcription_response_keeps_only_spoken_words():
"language_code": "en",
"text": "Hello world",
"words": [
{"type": "word", "text": "Hello", "start": 0.0, "end": 0.4},
{"type": "word", "text": "Hello", "start": 0.0, "end": 0.4, "speaker_id": "speaker_0"},
{"type": "spacing", "text": " ", "start": 0.4, "end": 0.5},
{"type": "audio_event", "text": "(laughter)", "start": 0.5, "end": 0.9},
{"type": "word", "text": "world", "start": 0.9, "end": 1.3},
@ -247,7 +247,11 @@ def test_diarized_response_builds_speaker_segments():
"type": "transcript.text.segment",
},
)
assert "words" not in response
assert response["words"] == [
{"word": "Bom", "start": 3.0, "end": 3.4},
{"word": "dia", "start": 3.4, "end": 3.8},
{"word": "Oi", "start": 5.0, "end": 5.3},
]
def test_diarized_response_reports_fractional_duration_as_typed_usage():