Skip to content

feat(speechify): add Speechify TTS plugin#1968

Open
luke-speechify wants to merge 10 commits into
livekit:mainfrom
luke-speechify:lo/add-speechify-plugin
Open

feat(speechify): add Speechify TTS plugin#1968
luke-speechify wants to merge 10 commits into
livekit:mainfrom
luke-speechify:lo/add-speechify-plugin

Conversation

@luke-speechify

@luke-speechify luke-speechify commented Jul 6, 2026

Copy link
Copy Markdown

Summary

Adds @livekit/agents-plugin-speechify — a Speechify TTS plugin for Agents JS with streaming audio and word-level timestamps, built on Speechify's official @speechify/api SDK.

There is currently no Speechify plugin in agents-js; this adds one following the existing plugin conventions (mirrors the cartesia package layout).

Details

Speechify's word-level speech marks are only returned by the batch /audio/speech endpoint, so SynthesizeStream chunks streamed input into per-sentence sequential /audio/speech calls, emitting audio and aligned word timestamps as each sentence completes — near-streaming time-to-first-audio plus alignedTranscript.

  • capabilities: { streaming: true, alignedTranscript: true }
  • SynthesizeStream (sentence-chunked, cumulative word-mark offsets) + ChunkedStream (one-shot)
  • Uses the official @speechify/api vendor SDK
  • Added to the README plugin table and turbo.json env allowlist (SPEECHIFY_API_KEY)
  • Changeset included

Maintenance & distribution

This plugin is maintained by Speechify and distributed by LiveKit as part of livekit/agents-js. Speechify keeps a mirror of the plugin source at Speechify-AI/livekit-plugin-js for maintenance and issue triage, and proposes changes upstream here. The canonical, user-installable package is @livekit/agents-plugin-speechify, published by LiveKit — there is no separate Speechify-published package.

Verification

  • pnpm build (ESM + CJS + type declarations) succeeds
  • pnpm lint (eslint/prettier/turbo) clean
  • tsc --noEmit clean
  • Live synthesis verified against the Speechify API; streaming produces valid 24 kHz PCM frames with monotonic word timestamps

Happy to adjust to match maintainer preferences.

@changeset-bot

changeset-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d861665

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 37 packages
Name Type
@livekit/agents-plugin-speechify Patch
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-anthropic Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch
@livekit/agents-plugins-test Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@CLAassistant

CLAassistant commented Jul 6, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

devin-ai-integration[bot]

This comment was marked as resolved.

@luke-speechify luke-speechify marked this pull request as draft July 6, 2026 12:56
Add @livekit/agents-plugin-speechify: streaming text-to-speech with
word-level timestamps via the official @speechify/api SDK. Chunks
streamed input into sequential /audio/speech calls, emitting audio and
timed transcripts per sentence (streaming + alignedTranscript). Maintained
by Speechify (Speechify-AI/livekit-plugin-js).
@luke-speechify luke-speechify force-pushed the lo/add-speechify-plugin branch from 6869087 to 26fede2 Compare July 6, 2026 13:24
@luke-speechify luke-speechify marked this pull request as ready for review July 13, 2026 20:27
@luke-speechify luke-speechify requested a review from a team as a code owner July 13, 2026 20:27
devin-ai-integration[bot]

This comment was marked as resolved.

… drop model cast)

The SDK v3.0.0 renamed the auth constructor option from apiKey to token, and
v3.0.1's GetSpeechRequest.Model union now includes simba-3.0 and simba-3.2
natively so the plugin no longer needs to widen it via a cast.

- plugins/speechify/package.json: @speechify/api ^2.0.0 -> ^3.0.1
- plugins/speechify/src/tts.ts:
  - rename public TTSOptions.apiKey -> token; pass through to
    new SpeechifyClient({ token, baseUrl })
  - update JSDoc + local variable + Omit-union for updateOptions
  - drop the `opts.model as Speechify.GetSpeechRequest.Model` cast + comment;
    v3.0.1's Model union already covers simba-english / simba-multilingual /
    simba-3.0 / simba-3.2
- pnpm-lock.yaml: refreshed
Addresses Devin review feedback on livekit#1968.

The base class SynthesizeStream.monitorMetrics gates all TTS metric
emission on this.#startedHrTime being set, which only happens via
markStarted(). Without it, TTFB, duration, and character-count metrics
were silently dropped for all Speechify streaming synthesis, and
USERDATA_TTS_STARTED_TIME never got stamped on frames so downstream
transcription timing was degraded.

Matches the elevenlabs / cartesia / resemble / deepgram plugin pattern.
devin-ai-integration[bot]

This comment was marked as resolved.

Addresses Devin review findings on livekit#1968 that both
Chunked and Synthesize streams close `this.queue` in their run()
catch blocks before re-throwing. The base class already closes the
queue in its own `finally` after run() returns, and it retries run()
on retryable APIErrors - so closing on error made the next retry's
`queue.put(...)` throw 'Queue is closed', turning transient network
errors into permanent failures.

Matches the guard the minimax plugin documents on its own catch.
devin-ai-integration[bot]

This comment was marked as resolved.

Addresses Devin review finding on livekit#1968: calling
`sentenceStream.close()` immediately after `endInput()` closes the
tokenizer's output queue before its internal async loop has a chance
to flush the last buffered sentence, so the final `queue.put(token)`
throws 'Queue is closed' and consume() never sees the tail. Result:
the last sentence of a TTS response could be silently dropped.

Matches the neuphonic plugin pattern: only call endInput(), let the
tokenizer close its own output queue when its input drains.
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.

2 participants