Skip to main content

echospeaks

echospeaks.ai

Sentence-level TTS audio cache for pipecat voice pipelines. Audio lives in echo-server (the pod-shared cache service; it keeps its own L1/L2 internally) - there is no client-side store. The tier is enabled by passing an api_key in cache_config; without it the wrapped service runs fully stock (no caching, no per-sentence contexts). To get a key, visit echospeaks.ai and contact us.

Layout

echospeaks/
  defaults.py   every SDK-wide tunable default, in one place
  common/       shared dataclasses (CachedAudio, AudioMeta) + logger shim
  cache/        keying (blake2b-128 cache keys + text normalization) and
                remote.py - the echo-server client (the one audio store)
  pipecat_tts/  pipeline integration - mixin.py (read), capture.py (write),
                pending.py (read→write handoff); needs pipecat-ai

How it works

Two pipeline pieces, wired around the host's existing TTS service:

  • Read - EnableTTSCache (echospeaks.pipecat_tts.mixin): drop-in replacement for instantiating the provider class. Every sentence's run_tts asks echo-server (0.5s bounded) - a hit replays the cached PCM + word timestamps with zero provider traffic (barge-in aware); a miss synthesizes live and marks the sentence for capture.
  • Write - TTSCacheCapture (echospeaks.pipecat_tts.capture): a pass-through processor placed right after the TTS service in the pipeline. It buffers each miss-marked sentence's audio + word timings and fire-and-forgets a POST to echo-server (audio base64-encoded inside the v1 JSON envelope; decoded on fetch). Interruptions discard partial buffers, so truncated audio is never stored.

echo-server behavior is strictly fail-open: the lookup is bounded by a 0.5s timeout and every error (down, slow, 401, 5xx) degrades to a plain miss

  • a dead cache server costs money, never quality. The 4xx invalid_* family is logged as an error instead of swallowed (it means this library sent something malformed).

Cache keys (echospeaks.cache.keying) are blake2b-128 hex (32 chars - the audio_key format echo-server validates) over normalized text + provider + voice + model + sample rate, so whitespace/casing never causes a false miss and two voices can never serve each other's audio. Normalization is for key matching only - synthesis always uses the real text.

Provider specifics handled by the wrapper:

  • One provider context per sentence, closed right after its text is sent (dialect-detected: ElevenLabs _close_context, Cartesia flush_audio with continue: false; close is graceful - audio flushes, then the final).
  • A client-side gate caps simultaneous contexts (default 4 - ElevenLabs kills the socket at >5). Slots free on the context's final, interruption, or websocket reconnect.
  • Cache hits take no context and no gate slot; the replayed context is completed explicitly so the sentence serializer never stalls.

Install

pip install echospeaks              # core (no pipecat needed)
pip install "echospeaks[pipecat]"  # with the pipecat pipeline integration

Usage

from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
from echospeaks.pipecat_tts import EnableTTSCache, TTSCacheCapture

# read side: instead of ElevenLabsTTSService(...). Works the same for any
# subclass of a supported provider service.
tts = EnableTTSCache(ElevenLabsTTSService,
    api_key=ELEVENLABS_API_KEY,                      # the TTS provider's key
    cache_config={
        "api_key": ECHO_SERVER_API_KEY,              # echo-server key: caching on
        "tag": AGENT_ID,                             # optional: label stores for filtered listings
    },
    settings=ElevenLabsTTSService.Settings(model="eleven_turbo_v2_5", voice=VOICE_ID),
)

# or Cartesia:
from pipecat.services.cartesia.tts import CartesiaTTSService

tts = EnableTTSCache(CartesiaTTSService,
    api_key=CARTESIA_API_KEY,
    cache_config={"api_key": ECHO_SERVER_API_KEY},
    settings=CartesiaTTSService.Settings(model="sonic-3", voice=VOICE_ID),
)

# write side: right after the TTS service in the pipeline.
pipeline = Pipeline([..., tts, TTSCacheCapture(), transport.output(), ...])

The wrapped service exposes get_and_reset_turn_stats() and log_cache_summary(call_sid=...) for call logs. Skip both pieces and the provider runs fully stock.

cache_config reference

One dict holds every host tunable. Missing or None keys fall back to the defaults in echospeaks/defaults.py; unknown keys are warned about and ignored (never a crash).

Key Default What it does
api_key (unset) echo-server API key (Authorization: Bearer). Setting it enables the cache; unset = caching disabled, service runs fully stock. Process-wide. Visit echospeaks.ai to contact us and get a key.
max_concurrent_context 4 Gate cap on simultaneous provider contexts per service. ElevenLabs hard-kills the socket at >5 (per websocket); Cartesia's limit is account-wide per plan - size this to plan_concurrency ÷ concurrent calls.
sentence_wait_timeout_s 3.0 Longest a sentence waits at a full gate before stale slots are reclaimed (a context's final normally arrives 150–300 ms after close).
replay_chunk_bytes 960 Hit-replay slice size (~30 ms @ 16 kHz mono s16le). Smaller = finer barge-in stop granularity, more frame overhead. Sample-aligned and clamped automatically.
pending_max_entries 512 Bound on the read→write capture handoff registry (process-wide; entries for contexts that never finish age out).
tag (unset) Optional grouping label (an agent id, a campaign, a tenant of your own) attached to every store. echo-server records it per entry, so listings can be filtered with GET /stats/{l1|l2}?tag=. It is NOT part of the cache key: the same sentence stays one shared entry no matter which tag stored it (the latest store's tag wins). Normalized to lowercase [a-z0-9-], max 32 chars; an unusable tag is dropped with a warning, never an error.

How the dict is sourced is the host's business - env vars, a config file, a per-tenant document; the SDK only sees the final dict.

Tests

uv sync && uv run pytest

Includes an HTTP round-trip test against an in-process fake echo-server (auth, miss, store, hit, delete, fail-open).

Metadata

Release files for echospeaks 0.2.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for echospeaks 0.2.3
File Size Uploaded Provenance Actions
echospeaks-0.2.3.tar.gz 20.0 kB Without provenance Details for echospeaks-0.2.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for echospeaks 0.2.3
File Interpreter ABI Platform Actions
echospeaks-0.2.3-py3-none-any.whl Python 3 none any Details for echospeaks-0.2.3-py3-none-any.whl

Total release size: 43.4 kB

Release files / echospeaks-0.2.3.tar.gz

Download URL echospeaks-0.2.3.tar.gz
Size 20.0 kB
Tags Source
SHA-256 checksum
How to use checksums
3500275cfffa7afb32d297c5c178bf49788789e51ac8c56d1cb6e786e5a6c43b
BLAKE2b-256 checksum
How to use checksums
94bbce7f267908e35c7ea8beda6837b04bdcb9bb36be1177936f9f362d2524a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.5

Release files / echospeaks-0.2.3-py3-none-any.whl

Download URL echospeaks-0.2.3-py3-none-any.whl
Size 23.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc6caf9529815b0b177e251ed28d3478da2e776392fa738013d5809db13896bd
BLAKE2b-256 checksum
How to use checksums
95677b6a0dabbc0761e4e395eb76f315a141cdd2aae8ee9bb878ae47d3b8a9ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.5

Security for echospeaks 0.2.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page