Add respeecher tts plugin#3233
Conversation
6450e4e to
9af5141
Compare
d844b57 to
2b09286
Compare
|
|
||
| Support for [Respeecher](https://respeecher.com/)'s TTS in LiveKit Agents. | ||
|
|
||
| More information is available in the docs for the [Respeecher](https://docs.livekit.io/agents/integrations/tts/respeecher/) integration. |
There was a problem hiding this comment.
How can we add documentation for the new plugin?
There was a problem hiding this comment.
when testing I got an error as following, is this correct to put the api key in the url?
aiohttp.client_exceptions.WSServerHandshakeError: 401, message='Invalid response status', url='wss://api.respeecher.com/v1/public/tts/en-rt/tts/websocket?api_key=[xxx]&source=LiveKit-Plugin-Respeecher-Version&version=0.1.0'
also, could you fix the type checks with mypy mypy --install-types --non-interactive -p livekit.plugins.respeecher
| async def _connect_ws(self, timeout: float) -> aiohttp.ClientWebSocketResponse: | ||
| session = self._ensure_session() | ||
| ws_url = self._opts.base_url.replace("https://", "wss://").replace("http://", "ws://") | ||
| full_ws_url = f"{ws_url}{self._opts.model}/tts/websocket?api_key={self._opts.api_key}&source={API_VERSION_HEADER}&version={API_VERSION}" |
There was a problem hiding this comment.
is this the correct that exposes api key in url?
There was a problem hiding this comment.
Hello!
Yes, this is intended since the Websocket protocol doesn't accept custom headers. Sending api keys via query parameters is common workaround. Other plugins, e.g. Cartesia does the same.
However, sending API keys as query parameters should be done only via TLS, so I've added a check for that.
80047b2 to
1f8db02
Compare
Different Respeecher models expose different voice catalogs, so a single hardcoded default (samantha) doesn't generalize once ua-rt is available. Make voice_id a required argument and update callers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The session is lazily obtained from utils.http_context.http_session(), a process-wide shared session. Closing it would break every other plugin and HTTP request in the same job context. Match the pattern used by Cartesia, Resemble, ElevenLabs, Deepgram, and friends. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Preserve APIError/APIStatusError raised inside _recv_task instead of
rewrapping them as a generic APIConnectionError that loses the
original message ("Respeecher returned error: ...").
- Pass conn_options.timeout to ws.receive() so a stalled stream is
detected instead of hanging until external cancellation, matching
the Cartesia/Deepgram pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Retire the old connection pool on model change instead of force-closing it. The model is baked into the WebSocket URL, so the pool must be swapped; force-closing terminated WebSockets held by in-flight streams. Now we hold a reference until aclose(), letting in-flight streams return their connections naturally. - Export SynthesizeStream from the package so callers can use it for type annotations, matching ElevenLabs and other streaming plugins. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Snapshot _TTSOptions per stream via dataclasses.replace so concurrent update_options() can't mutate voice_id/voice_settings/etc. mid-stream. - Track end-of-input with an input_ended flag instead of relying on sent_tokenizer_stream.closed. The tokenizer closes as soon as _input_task drains its channel, which fires before the final continue=false request is sent; a per-sentence "done" arriving in that window could terminate the recv loop prematurely. - Use output_emitter.end_segment() in _recv_task instead of end_input(). Closing the AudioEmitter write channel is the base class's job; the plugin only owns segment boundaries (matches Resemble). - Handle aiohttp.WSMsgType.ERROR explicitly so transport-level failures surface immediately instead of waiting for the next receive timeout. Also extracted the duplicated voice-payload construction into a small helper to keep send paths in sync. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Description
This PR adds a new TTS plugin for LiveKit Agents, integrating Respeecher’s synthesis and streaming API. It currently supports the English voice model and the Ukrainian voice models.
Why Respeecher?
Respeecher’s technology is notable not just for voice quality but also for its ethical design and safeguards. This integration offers: