Skip to main content

ZeliSpeech Python SDK — self-hosted, low-latency streaming text-to-speech.

Project description

ZeliSpeech — Python SDK

A small, dependency-light client for ZeliSpeech — self-hosted, low-latency streaming text-to-speech. Point it at your running server and get audio back in a couple of lines. The SDK talks the same authenticated /v1 API documented in the API reference, so the request/response shapes follow mainstream TTS-API conventions.

Install

pip install zeli-tts

Runtime dep: requests. Live playback (play/stream) shells out to ffplay (ffmpeg), mpv, or afplay.

Quickstart

from zeli_tts import ZeliSpeech, play, save, stream

client = ZeliSpeech(
    base_url="https://voice.zeligate.com",
    api_key="sk-zeli-...",                    # optional if the box runs open
)

# 1) Stream — first audio after the first sentence, not the whole passage
audio = client.text_to_speech.stream(
    voice_id="zeli-voice-1",
    text="Hey there, this is Zeli.",
    output_format="mp3_44100_128",
)
for chunk in audio:
    ...                # audio bytes as they generate

# 2) Stream + play live (needs ffplay or mpv)
stream(client.text_to_speech.stream(voice_id="zeli-voice-1", text="Playing as I generate."))

# 3) One-shot — a finished clip
audio = client.text_to_speech.convert(voice_id="zeli-voice-1", text="Hello world.")
save(audio, "hello.mp3")

Client

ZeliSpeech(base_url, *, api_key=None, timeout=60.0)
  • base_urlhttp://host:8000 or https://voice.zeligate.com; do not include a trailing /v1.
  • api_key — sent as Authorization: Bearer … on every request. Required only when the box sets ZELI_API_KEY; a loopback/embedded box can run open.
  • client.capabilities() / client.health() / client.is_ready() — engine info and readiness (handy while a box is warming up).

Synthesis — client.text_to_speech

stream(voice_id, text, *, model_id="zeli-turbo", voice_settings=None,
       output_format="mp3_44100_128", timeout=None) -> AudioStream

convert(voice_id, text, *, ...same...) -> bytes
  • stream(...) returns an AudioStream — iterate it for audio bytes as they generate; .read() drains it to one blob. It exposes .output_format, .sample_rate, .voice (the resolved voice id), and .request_id.
  • convert(...) returns the finished clip in the requested output_format.
  • output_format accepts the full menu — mp3_*, wav_*, raw pcm_*, ulaw_8000, alaw_8000, opus_48000_* — see the output formats reference.
  • voice_settings (stability/style/speed/…) maps onto Turbo delivery — see voice settings.

Voices — client.voices

client.voices.list()                 # -> [Voice(id, label, description, custom), ...]
client.voices.get("zeli-voice-1")    # -> Voice (404s on unknown ids)
client.voices.add(name="My voice", file="me.wav", description="")  # -> Voice
client.voices.delete("custom-abc123")

add clones a voice zero-shot from a short reference clip (~10–20 s of clean single-speaker audio; file may be a path, a file object, or bytes). Cloning and removal apply to the Turbo engine's custom voices, and require a full API key — ephemeral tokens get 401 insufficient_scope here.

Ephemeral tokens — client.tokens

Never ship a real API key to a browser or mobile app: anyone can read it. The intended flow is that your backend (which holds a full api_key) mints a short-lived, synthesis-only token and hands it to the client, which then talks to the API directly with that token.

tok = client.tokens.create_ephemeral(ttl_seconds=300)
# -> {"token": "zsk_temp_…", "expires_at": 1770000000, "scope": "tts"}
  • ttl_seconds is clamped to 30–600 (default 300). expires_at is the epoch second the token dies — enforced server-side on every request, so a leaked token is usable for minutes at most.
  • The returned token has scope "tts": it can call synthesis and read endpoints (text_to_speech.stream/convert, voices.list/get) but not voices.add/delete or tokens.create_ephemeral — those return 401 insufficient_scope.
  • Minting requires a full key, and 501 not_supported is returned on a server without the portal keys table configured.

Backend endpoint example (FastAPI):

from fastapi import FastAPI
from zeli_tts import ZeliSpeech

app = FastAPI()
zeli = ZeliSpeech(base_url="https://voice.zeligate.com", api_key=os.environ["ZELISPEECH_API_KEY"])

@app.post("/api/tts-token")
def tts_token():
    return zeli.tokens.create_ephemeral(ttl_seconds=300)  # browser uses this directly

More examples

# Delivery control (stability / style / speed map onto Turbo knobs)
clip = client.text_to_speech.convert(
    "zeli-voice-1", "Same words, very different delivery!",
    voice_settings={"stability": 0.2, "style": 0.9, "speed": 1.1},
)

# Raw PCM for your own audio pipeline (24 kHz mono int16, no container)
pcm = client.text_to_speech.convert("zeli-voice-1", "Raw samples.", output_format="pcm_24000")

# Telephony formats
ulaw = client.text_to_speech.convert("zeli-voice-1", "For the phone line.", output_format="ulaw_8000")

# Stream straight to a file as it generates
with open("out.mp3", "wb") as f:
    for chunk in client.text_to_speech.stream("zeli-voice-1", "Written as it speaks."):
        f.write(chunk)

# Clone a voice, use it, remove it
voice = client.voices.add(name="My narrator", file="reference.wav")
client.text_to_speech.convert(voice.id, "Cloned voice speaking.")
client.voices.delete(voice.id)

# Play inline in a Jupyter notebook
from IPython.display import Audio
Audio(data=bytes(client.text_to_speech.convert("zeli-voice-1", "Hello, notebook!")))

# Robust error handling
from zeli_tts import APIError, ConnectionError
try:
    client.text_to_speech.convert("zeli-voice-1", "…")
except APIError as e:
    if e.status == "invalid_api_key":
        ...  # rotate/refresh the key
    elif e.status_code == 503:
        ...  # model still loading — retry shortly
except ConnectionError:
    ...      # box unreachable / stopped

Helpers

from zeli_tts import play, save, stream, pcm_to_wav

play(audio)                    # play a finished clip (any output format)
stream(audio_stream)           # play chunks live; returns the collected bytes
save(audio, "out.mp3")         # write bytes to a file (pcm_* wrapped when .wav)
pcm_to_wav(pcm, sample_rate=24000)

Errors

All derive from ZeliSpeechError:

Exception When
ConfigurationError bad base_url, or a missing dep/player
ConnectionError server unreachable (refused / DNS / timeout)
APIError non-2xx from an HTTP endpoint (.status_code, .status, .body)
GenerationError the stream broke mid-synthesis

Auth failures are APIErrors whose .status tells you why:

.status_code .status When
401 missing_api_key auth required but no key sent
401 invalid_api_key unknown, revoked, or expired key/token
401 insufficient_scope an ephemeral token hit a management route
501 not_supported minting on a server without the keys table

Migrating from 0.1.x

0.2.0 moves the SDK onto the authenticated /v1 API and adopts the ZeliSpeech naming. ZeliTTS / ZeliTTSError remain as aliases, but the synthesis calls changed: stream(text, voice=...)stream(voice_id, text), audio now arrives in output_format (mp3 by default) instead of raw PCM, and the engine-knob kwargs (exaggeration, cfg_weight, …) are replaced by voice_settings.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zeli_tts-0.3.1.tar.gz (18.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

zeli_tts-0.3.1-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file zeli_tts-0.3.1.tar.gz.

File metadata

  • Download URL: zeli_tts-0.3.1.tar.gz
  • Upload date:
  • Size: 18.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for zeli_tts-0.3.1.tar.gz
Algorithm Hash digest
SHA256 5cda34278bfaccefafe5bf88344ffd7e750683e0d38ebb0f1b39ee28c9821a9e
MD5 94565c33142277eabf3ae8907626769d
BLAKE2b-256 d11c5f06c6e19531df93ee975375fd29e4fa43f8175654dd380766a9e0407de6

See more details on using hashes here.

File details

Details for the file zeli_tts-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: zeli_tts-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for zeli_tts-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8e7180e8133f3db3be7f5184109247e2c560bfd49e8200135c09e36a0c414329
MD5 1a3731e7ac98d98ad50b27e1788e7073
BLAKE2b-256 58b09c6a930577cf2ff157716112d53ffc9ceb7002b240c620d78785fb14f0dc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page