Skip to content

feat: Phase 3 — streaming TTS WebSocket & self-hosted distribution credentials - #166

Open
dg-coreylweathers wants to merge 1 commit into
feat/phase-2-read-stream-modelsfrom
feat/phase-3-tts-ws-selfhosted
Open

feat: Phase 3 — streaming TTS WebSocket & self-hosted distribution credentials#166
dg-coreylweathers wants to merge 1 commit into
feat/phase-2-read-stream-modelsfrom
feat/phase-3-tts-ws-selfhosted

Conversation

@dg-coreylweathers

Copy link
Copy Markdown
Contributor

Phase 3 — Streaming TTS WebSocket + self-hosted credentials

Stacked on #165 (Phase 2). Base is feat/phase-2-read-stream-models; GitHub will retarget to main as the stack merges.

What's included

  • [Enhancement] Add Text-to-Speech WebSocket streaming support #148 / [Enhancement] Add Text-to-Speech WebSocket streaming support #147 / Text to Speech - Websocket API #95 — Streaming TTS over a WebSocket. Speak::speak_stream()SpeakStreamBuilder (model / encoding / sample rate); handle() opens the connection → SpeakStreamHandle with speak / flush / clear / close, and receives audio + events. SpeakStreamHandle: futures::Stream<Item = Result<SpeakResponse>>. SpeakResponse (Audio / Metadata / Flushed / Cleared / Warning / Unknown) is #[non_exhaustive]; unknown message types are preserved rather than breaking the stream. The speak feature now pulls the WebSocket deps. Modeled on src/listen/websocket.rs. Example text_to_speech_websocket.
  • Self-hosted distribution credentials. Deepgram::self_hosted()SelfHosted with list / get / create / delete_distribution_credentials + typed response models. Example self_hosted_credentials.

Verification (Rust 1.97 container)

  • build / clippy -D warnings / fmt / cargo test --all --all-features (142 tests, incl. TTS-WS protocol + self-hosted deserialization tests) ✅
  • per-feature cargo check incl. speak-only (now pulls WS deps) ✅
  • cargo semver-checks: no new breaking changes (only the inherited Phase 1 Extra metadata support #130 streaming breaks).
  • Live-verified against production: TTS WebSocket connected, emitted Metadata + Flushed, and streamed 219 KB of audio. Self-hosted list returns a correct 403 INSUFFICIENT_PERMISSIONS on accounts without the self-hosted scope (confirms the request path).

Closes #148, #147, #95

…credentials

- TTS WebSocket (#148/#147/#95): Speak::speak_stream() -> SpeakStreamBuilder
  (model/encoding/sample_rate); handle() opens the connection and returns a
  SpeakStreamHandle for sending text (speak/flush/clear/close) and receiving
  audio + events. SpeakStreamHandle implements futures::Stream; SpeakResponse
  (Audio/Metadata/Flushed/Cleared/Warning/Unknown) is #[non_exhaustive] and
  unknown message types are preserved rather than breaking the stream. The
  speak feature now enables the WebSocket deps. New example
  text_to_speech_websocket.
- Self-hosted distribution credentials: Deepgram::self_hosted() -> SelfHosted
  with list/get/create/delete_distribution_credentials, plus typed response
  models. New example self_hosted_credentials.

All additive (cargo semver-checks: no new breaking changes beyond the
Phase 1 #130 streaming change). Live-verified the TTS WebSocket against
production; the self-hosted list path returns a correct 403 on accounts
without the self-hosted scope.

Closes #148, #147, #95

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dg-coreylweathers

Copy link
Copy Markdown
Contributor Author

Addressed the review's BLOCKING finding: the TTS-WS worker busy-spun (and hung on current-thread runtimes) after input close because it kept selecting on the drained input channel. run_worker now stops selecting on message_rx once input is closed and only awaits server messages (via a shared handle_incoming helper). Verified: re-ran the example under #[tokio::main(flavor = "current_thread")] — it completes cleanly (219KB audio) where it previously would hang. Also documented the send-then-drain contract and the nil-request_id fallback on SpeakStreamHandle. The self-hosted create/get envelope matches list per the API docs (no secret token field in the create response).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant