Support stateful streaming STT sessions instead of repeated whole-utterance uploads
Summary
Add a stateful streaming STT backend for live microphone audio. The current OpenAI-compatible STT backend is request-oriented: it sends accumulated or final audio as an in-memory WAV to POST /v1/audio/transcriptions. Progressive transcription therefore re-uploads the accumulated utterance, while final transcription independently uploads the full utterance again and waits for the complete response.
Keep the current file/request backend for compatible servers, but add a transport that streams each audio chunk once over a persistent transcription session and commits the buffered turn when speech-to-speech's VAD/Smart Turn logic decides the user turn is ready.
This is a follow-up to #367, where stateful/bidirectional microphone transcription was explicitly a non-goal, and is related to the deferred streaming-STT work in #363.
Current limitation
OpenAICompatibleSTTHandler currently:
- encodes the current 16 kHz mono utterance as a complete WAV;
- calls
POST /v1/audio/transcriptionsfor each dispatched progressive snapshot; - allows at most one progressive request in flight and drops newer progressive work while it runs;
- sends the final transcription as a separate request without reusing an active progressive session or its result.
This preserves broad /audio/transcriptions compatibility, but it has poor scaling characteristics for live audio:
- progressive bandwidth and provider work grow with the accumulated utterance;
- the final request repeats the full upload;
- the LLM still waits for a new final request after local turn detection;
- the adapter cannot use remote STT implementations that return transcript deltas while audio is arriving.
OpenAI's current live-audio workflow uses a Realtime transcription session: audio is appended incrementally, transcript deltas arrive during speech, and the application can explicitly commit the input buffer at its own turn boundary. See: https://developers.openai.com/api/docs/guides/realtime-transcription
Proposed behavior
Introduce a separate stateful streaming STT backend or transport capability, for example --stt openai-realtime, without changing the existing --stt openai file/request contract.
The intended ownership is:
- speech-to-speech continues to own input VAD, Smart Turn, turn revisions, session state, and response admission;
- the remote transcription service owns only incremental ASR inference;
- upstream automatic turn detection is disabled when supported;
- incoming PCM is appended to the active remote transcription buffer once;
- speech-to-speech explicitly commits the buffer when its local turn decision is ready;
- remote transcript deltas become
PartialTranscriptionoutput; - the committed final transcript becomes the existing final
Transcriptionand triggers LLM work; - reopen, supersession, cancellation, disconnect, and pipeline reuse cannot publish stale transcripts into a newer turn or session.
The precise protocol abstraction should allow an OpenAI Realtime transcription implementation first while leaving room for compatible self-hosted streaming ASR services.
Design questions
- Where should live PCM be tapped so it reaches both local VAD and the streaming STT transport without going through cumulative
VADAudiosnapshots? - Should one remote transcription connection live for the full realtime client session, or be recreated per user turn?
- How should a soft-ended turn that reopens map to remote buffer commit/clear operations and local
turn_revision? - What connection recovery behavior is safe without replaying already-committed audio or duplicating transcripts?
- How should endpoint-wide admission limits interact with long-lived streaming connections?
Acceptance criteria
- Each input audio chunk is transmitted at most once during a healthy streaming session; progressive transcription does not re-upload accumulated WAV windows.
- Local VAD/Smart Turn remains authoritative for committing a user turn.
- Partial transcript events are routed to the correct input item and remain compatible with the existing append-only Realtime event handling.
- A committed final transcript triggers exactly one LLM request for the latest turn revision.
- Reopened turns, cancellation, disconnect, and pipeline reuse cannot leak late transcript output.
- The implementation logs remote STT connection/setup time and VAD-commit-to-final-transcript latency so it can be compared with
--stt openai. - Fake-server tests cover deltas, explicit commit, final transcription, reconnect/failure, turn reopen, cancellation, and session reuse.
- Existing
--stt openaibehavior and compatibility remain unchanged.
Non-goals
- Replacing speech-to-speech's VAD or Smart Turn logic with provider-side VAD.
- Removing the file-oriented
/v1/audio/transcriptionsbackend. - Implementing or deploying a first-party STT inference server in this issue.
- Changing TTS or LLM behavior.
Source: huggingface/speech-to-speech