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.2

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.2
File Size Uploaded Provenance Actions
echospeaks-0.2.2.tar.gz 20.0 kB Without provenance Details for echospeaks-0.2.2.tar.gz

Built distribution (wheel)

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

Total release size: 43.4 kB

Release files / echospeaks-0.2.2.tar.gz

Download URL echospeaks-0.2.2.tar.gz
Size 20.0 kB
Tags Source
SHA-256 checksum
How to use checksums
6f0792c2615212814505742de71da8a0ab1e5b702a4d0716667d179b573f108f
BLAKE2b-256 checksum
How to use checksums
3b5beea24a7f890ef4f14e69a37cabc2cadd0e115b226bf2a7b652ed2220a6b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.5

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

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

Security for echospeaks 0.2.2

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