Add realtime speech transcription transport - #129
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds the realtime speech transcription “transport” layer for RSVP spoken responses: microphone capture → PCM16@16kHz chunking → provider-independent streaming transcription session → provider adapters (ElevenLabs Scribe v2 Realtime, Deepgram Nova-3), plus preflight robustness and a focused test suite.
Changes:
- Introduces a provider-neutral
StreamingTranscribercontract and aSpeechSessionthat gates audio and manages one-at-a-time utterances. - Adds microphone continuous audio-frame subscription (AudioWorklet w/ ScriptProcessor fallback) and a PCM16 16kHz capture/resampling pipeline.
- Adds token/credential endpoint handling and expands preflight logic to tolerate transient muted tracks (with timeouts paused during mute).
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/speechToken.test.ts | Tests trusted-origin validation and prevents leaking session tokens to untrusted endpoints. |
| tests/speechSession.test.ts | Tests utterance gating, commit/finalization flows, error mapping, cancellation, and idempotent close. |
| tests/speechPreflight.test.ts | Adds coverage for transient mute recovery and rejection beyond the grace period. |
| tests/elevenLabsRealtimeTranscriber.test.ts | Validates URL/config generation, keyterms policy, token provider behavior, and WS lifecycle/error mapping for ElevenLabs. |
| tests/deepgramRealtimeTranscriber.test.ts | Validates Deepgram URL/config, auth protocols, audio send/finalize behavior, and close-during-credential retrieval. |
| tests/audioCapture.test.ts | Tests PCM conversion, streaming resampler continuity, and gating behavior in microphone capture. |
| components/speech/transcriber.ts | Defines shared transcriber types, events, and error model for realtime streaming. |
| components/speech/speechToken.ts | Implements credential/token retrieval with timeout, request body, and trusted-origin checks for default endpoint selection. |
| components/speech/speechSession.ts | Implements session state machine, utterance lifecycle, audio forwarding gate, and finalization/cancellation logic. |
| components/speech/speechPreflight.ts | Enhances preflight to tolerate temporary microphone mute and pauses deadlines during mute windows. |
| components/speech/microphoneCaptureWorkletSource.ts | Provides an AudioWorklet processor source string for continuous frame capture. |
| components/speech/microphone.ts | Extends MicrophoneSession with audio-frame subscriptions and implements worklet/script-processor capture sources. |
| components/speech/elevenLabsRealtimeTranscriber.ts | Implements ElevenLabs realtime WS adapter, URL construction, keyterm normalization, and event/error mapping. |
| components/speech/deepgramRealtimeTranscriber.ts | Implements Deepgram realtime WS adapter, URL construction, auth protocol usage, and event/error mapping. |
| components/speech/audioCapture.ts | Converts streamed microphone frames into gated PCM16@16kHz chunks with streaming resampling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+76
to
+77
| const endpoint = | ||
| options.endpoint ?? buildSpeechTokenEndpoint(await getEasyEyesBaseUrl()); |
Comment on lines
+433
to
+447
| private readonly handleSocketClose = (event: CloseEvent): void => { | ||
| const expected = this.closeExpected || this.stateValue === "closed"; | ||
| if (!expected && this.stateValue !== "failed") { | ||
| this.handleFailure( | ||
| new TranscriberError( | ||
| "connectionFailure", | ||
| `The Deepgram connection closed unexpectedly${ | ||
| event.code ? ` (code ${event.code})` : "" | ||
| }.`, | ||
| { retryable: true }, | ||
| ), | ||
| ); | ||
| } | ||
| this.emitClosed(expected); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds the realtime speech transport needed for automatic spoken responses in RSVP reading.
This is the foundation phase only.
What changed
Microphone audio capture
AudioWorkletwhen available, with aScriptProcessorNodefallback.Provider-independent transcription
StreamingTranscriberinterface with typed states, events, and errors.Preflight robustness
Tests
npm run check:tsThe tests cover: