from __future__ import annotations from collections.abc import Iterable, Iterator from time_machine.application.encounter_service import EncounterService from time_machine.domain.events import ErrorEvent, TemporalEvent from time_machine.ports.speech import STTAdapter class SpeechOrchestrator: def __init__(self, stt: STTAdapter, encounter_service: EncounterService) -> None: self.stt = stt self.encounter_service = encounter_service def handle_audio_turn( self, encounter_id: str, audio: object, ) -> Iterator[TemporalEvent]: transcript = self.stt.transcribe(audio) if not transcript.text.strip(): yield ErrorEvent( message="No speech could be decoded from the signal.", severity="recoverable", metadata={"encounter_id": encounter_id}, ) return yield from self.encounter_service.handle_text_turn( encounter_id=encounter_id, text=transcript.text.strip(), ) def handle_audio_stream( self, encounter_id: str, audio_chunks: Iterable[bytes], ) -> Iterator[TemporalEvent]: final_text = "" for event in self.stt.stream_transcript(audio_chunks): yield event if event.transcript.is_final: final_text = event.transcript.text final_text = final_text.strip() if final_text: yield from self.encounter_service.handle_text_turn( encounter_id=encounter_id, text=final_text, ) else: yield ErrorEvent( message="No complete speech turn arrived from the signal.", severity="recoverable", metadata={"encounter_id": encounter_id}, )