fix(realtime): ignore a late speechStart for a finished Muse turn

A duplicate speechStart for a turn that already stopped used to make that
closed turn active again, so the next turnless PUSH_TO_TALK transcript was
routed to the finished item and dropped.
This commit is contained in:
ryan-crabbe-berri 2026-09-12 12:53:03 -07:00
parent 6b78438c99
commit a2e383a1a5
2 changed files with 20 additions and 0 deletions

View file

@ -495,6 +495,8 @@ class MuseEventTransformer:
def _speech_start(self, message: Mapping[str, JsonValue]) -> _TurnState:
turn: Final = self._turn(_required_turn_id(message, "speechStart"))
if turn.stopped:
return turn
turn.started = True
self._active_turn_id = turn.item_id
return turn

View file

@ -407,6 +407,24 @@ def test_finished_turn_ignores_late_duplicates():
)
def test_late_duplicate_speech_start_does_not_capture_the_next_turnless_transcript():
transformer = MuseEventTransformer()
transformer.configure(MuseSessionConfig(MUSE_MODEL, "PUSH_TO_TALK", 24_000, ()))
transformer.transform(json.loads(_event("speechStart", turnId="turn-1")))
transformer.transform(json.loads(_event("speechComplete", turnId="turn-1", transcript="first")))
assert transformer.transform(json.loads(_event("speechStart", turnId="turn-1"))) == ()
events = transformer.transform(json.loads(_event("transcript", transcript="second", final=True)))
assert [event["type"] for event in events] == [
"input_audio_buffer.speech_started",
"input_audio_buffer.speech_stopped",
"conversation.item.input_audio_transcription.completed",
]
assert events[2]["transcript"] == "second"
assert events[2]["item_id"] != "turn-1"
def test_turn_memory_is_bounded_by_turn_limit():
transformer = MuseEventTransformer(turn_limit=2)