Support stateful streaming STT sessions instead of repeated whole-utterance uploads

Author: andimarafiotiCreated Aug 28, 2026Updated Aug 28, 2026

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/transcriptions for 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 PartialTranscription output;
  • the committed final transcript becomes the existing final Transcription and 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 VADAudio snapshots?
  • 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 openai behavior 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/transcriptions backend.
  • Implementing or deploying a first-party STT inference server in this issue.
  • Changing TTS or LLM behavior.

Source: huggingface/speech-to-speech