speech
User-audio generation for the eval harness.
When a scenario defines a user_audio: block, the harness generates raw audio
for each user turn using that TTS config and streams it to the bot as RTVI
raw-audio messages. The bot’s STT then processes it for real, exercising the
full input audio path.
EvalSpeech sets up an already-built TTSService with a minimal
frame-processor lifecycle (no pipeline or worker) and calls its run_tts
directly for each turn, collecting the TTSAudioRawFrame``s. Only local (e.g.
Kokoro) or HTTP (e.g. ``CartesiaHttpTTSService) services are supported —
websocket-streaming TTS needs a pipeline to manage its connection. Generated
audio is cached at <cache-dir>/<sha256>.wav (keyed by cache_key +
lower-cased text); the cache file’s actual sample rate is checked on load and
regenerated on mismatch, so you can experiment with sample rates without bloating
the cache.
EvalSpeech.from_config() constructs the service from a user_audio
mapping (dispatch + a user_audio.factory escape hatch) and wraps it;
tts_cache_key() and tts_sample_rate() derive the cache identity and
rate.
- pipecat.evals.speech.tts_sample_rate(voice_cfg: dict) int[source]
The sample rate a
user_audioblock asks for (default 16 kHz).
- pipecat.evals.speech.tts_cache_key(voice_cfg: dict) str[source]
A stable identity for a
user_audioconfig, for caching synthesized audio.Covers the audio’s semantic identity (service, voice, model) but not the sample rate, so different rates reuse the same slot (a mismatch just triggers regeneration in
EvalSpeech.generate()).
- class pipecat.evals.speech.EvalSpeech(service: TTSService, *, sample_rate: int, cache_key: str, cache_dir: str | Path | None = None, use_cache: bool = True)[source]
Bases:
objectGenerates user audio for a scenario by calling a TTS service’s
run_tts.Takes an already-built
TTSService;from_config()builds one from a scenario’suser_audiomapping.start()sets the service up with a minimal frame-processor lifecycle (no pipeline or worker — just a task manager, a clock, and aStartFrame), thengenerate()callsrun_ttsdirectly and collects the audio. Synthesized audio is cached on disk (keyed bycache_key+ text), so re-runs skip synthesis entirely.Only local (e.g. Kokoro) or HTTP (e.g.
CartesiaHttpTTSService) services are supported. Websocket-streaming TTS needs a running pipeline to manage its connection lifecycle, whichrun_ttsalone doesn’t drive, so it is rejected (see__init__()).Use as an async context manager:
async with EvalSpeech(service, sample_rate=16000, cache_key="...") as speech: pcm, sample_rate = await speech.generate("hello world")
- __init__(service: TTSService, *, sample_rate: int, cache_key: str, cache_dir: str | Path | None = None, use_cache: bool = True)[source]
Initialize the generator.
- Parameters:
service – A constructed local or HTTP
TTSService(e.g. fromfrom_config()).sample_rate – Output sample rate the service produces.
cache_key – Stable identity for the service config (see
tts_cache_key()); combined with the text to key the cache.cache_dir – Where to store cached audio. Defaults to
<user-cache-dir>/pipecat/tts(or$PIPECAT_EVALS_CACHE_DIR).use_cache – When False, ignore any cached audio and don’t write new cache files — every utterance is freshly synthesized.
- Raises:
ValueError – If
serviceis a websocket-streaming TTS service (run_ttscan’t be driven without a pipeline to manage its connection); use a local or HTTP service instead.
- property sample_rate: int
Sample rate (Hz) of the audio this generates.
- classmethod from_config(voice_cfg: dict, *, cache_dir: str | Path | None = None, use_cache: bool = True) EvalSpeech[source]
Build an
EvalSpeechfrom a scenario’suser_audiomapping.Honors a custom
factory(dotted path to a callable taking(voice_cfg, sample_rate)and returning aTTSService); otherwise dispatches on theservicename. Add providers by extending this. To use a fully custom setup, constructEvalSpeechdirectly with your ownTTSServiceand pass it topipecat.evals.harness.EvalSession.from_scenario().- Parameters:
voice_cfg –
user_audiomapping —service(kokoroorcartesia) andvoiceat minimum; optionalsample_rate(default 16 kHz), plusmodel/api_key(defaults to $CARTESIA_API_KEY) for Cartesia.cache_dir – Where to store cached audio (see
__init__()).use_cache – When False, force fresh synthesis (see
__init__()).
- Returns:
A configured EvalSpeech (not yet started).
Example:
# In the scenario: user_audio.factory: "my_pkg.make_tts" def make_tts(voice_cfg, sample_rate): return RimeTTSService(...)
- async start() None[source]
Set the service up so
generate()can callrun_ttsdirectly.A TTS service normally gets its task manager, clock, and output sample rate from the pipeline that hosts it. Provide a minimal stand-in here (no pipeline, no worker): a
StartFramesets the sample rate and opens any HTTP session the service needs.enable_metricsis off sorun_ttsnever tries to push aMetricsFrame(there is no downstream).
- async generate(text: str) tuple[bytes, int][source]
Return
(pcm, sample_rate)fortext— from cache or freshly synthesized.Call serially: each call runs one
run_ttson the shared service and collects its audio.- Parameters:
text – The utterance to synthesize.
- Returns:
Tuple of
(pcm_bytes, sample_rate)— raw 16-bit little-endian mono PCM.