Overview
GandrTTSService converts text to speech using Gandr. It
holds a single WebSocket open and reuses it across turns rather than opening
one per utterance, which is what keeps steady-state first-audio latency low.
Source Repository
Source code, examples, and issues for the Gandr integration
Gandr Website
Voices, languages, and pricing
API Keys
Create a Gandr API key
API Reference
The full HTTP and WebSocket contract
Installation
This is a community-maintained package distributed separately frompipecat-ai:
Prerequisites
- Gandr account: sign up at gandr.ai
- API key: a
gnd_…key from your account page
Required environment variables
GANDR_API_KEY: your Gandr API key (required)
Configuration
str
required
Gandr API key (
gnd_…). Raises ValueError if empty or whitespace.str
default:"wss://tts.gandr.ai/ws"
WebSocket endpoint. Leave as the default unless you have been given a
different one.
GandrTTSService.InputParams
default:"None"
Voice, language and expression configuration. See Input
parameters.
TextAggregationMode
default:"None"
How incoming text is aggregated before synthesis.
float
default:"30.0"
How long to wait for an utterance’s closing frame before treating it as
failed.
float
default:"0.5"
How long to wait before retrying after the server answers
busy.int
default:"3"
Total attempts per utterance, including retries for
busy and for voice
registration.bool
default:"True"
On barge-in, drop and reopen the connection so the next turn’s audio is not
queued behind audio the listener has already interrupted. See
Interruptions.
Input parameters
Passed viaparams=GandrTTSService.InputParams(...).
Stock voices:
gandr-mia, gandr-ava, gandr-jenny, gandr-dane,
gandr-leo, gandr-lewis.
Languages
language takes a bare two-letter code: 23 languages are supported.
Examples: en, es, fr, de, pt, ar, zh, ja.
Usage example
examples/foundational/gandr_tts_basic.py.
Interruptions
The wire protocol has no cancel frame. On barge-in the service therefore drops the connection and reopens it, rather than draining audio nobody is going to hear. Setreconnect_on_interruption=False to keep the connection instead and
discard the interrupted render client-side, at the cost of the next turn
waiting for that render to finish.
Notes
The first turn on a fresh connection is slower, roughly 700 ms while the
session voice cache fills. Every turn after it is materially quicker, which
is why the connection is held warm across turns. A benchmark that measures
only turn one is measuring the cache filling rather than the engine.
Requests are capped at 2,000 characters. Longer text is split losslessly by
split_for_request before sending; you do not need to pre-chunk.Every failure path closes the turn with
TTSStoppedFrame, so a pipeline
cannot hang waiting on a connection that has died.Compatibility
Tested with Pipecat v1.7.0 on Python 3.12.11: clean virtual environment, install, import, unit tests, and service construction. Declared support ispipecat-ai>=0.0.108,<2.0.0.