voxport
Small Python interfaces for text-to-speech and speech-to-text, with
swappable engine adapters. Your code calls one method,
synthesize() or transcribe(), and never touches an engine's API
directly, so switching engines means changing one line where the adapter
is created.
TTSEngine.synthesize(text, voice) -> audio
STTEngine.transcribe(audio) -> text
Included adapters:
KokoroDirectTTS: runs the Kokoro TTS model in your own process. The model loads once and stays in memory, with no server and no network hop.WyomingWhisperSTT: a client for a Wyoming protocol Whisper server, the same kind of server Home Assistant's voice pipeline uses. It loads no model itself; transcription happens on the server.
voxport is a library only: it has no command-line tool or app of its own. It is pre-1.0, so the API may still change between minor versions (see the changelog).
Installation
Requires Python 3.10+. Each adapter's dependencies are an optional extra, so you install only what you use:
pip install "voxport[kokoro]" # KokoroDirectTTS
pip install "voxport[wyoming]" # WyomingWhisperSTT
pip install "voxport[all]" # both
| Extra | Adds | For |
|---|---|---|
kokoro |
Kokoro, torch (about 2 GB) | KokoroDirectTTS |
wyoming |
wyoming | WyomingWhisperSTT |
all |
both of the above | everything |
A plain pip install voxport includes only the interfaces and numpy.
Importing an adapter without its extra raises an ImportError that
names the extra to install.
Things voxport doesn't install for you:
WyomingWhisperSTTneeds a running Wyoming Whisper server. See Running a Wyoming Whisper server.- On first use, Kokoro downloads its model weights and voice packs from
Hugging Face, and spaCy's
en_core_web_smEnglish model. See Offline use for machines that can't reach those. soundfile, used in the examples below to write WAV files.
Usage
Text to speech
import soundfile as sf
from voxport.tts.kokoro_direct import KokoroDirectTTS
tts = KokoroDirectTTS() # reuse one instance; models load once per accent
audio = tts.synthesize("Hello from voxport.", voice="af_heart", speed=1.0)
sf.write("hello.wav", audio, tts.sample_rate) # 24 kHz mono
synthesize() returns a 1-D numpy array. The first letter of the voice
name picks the accent (a American, b British, ...), following
Kokoro's voice naming; pass lang_code= to override it. Kokoro splits
long text into segments itself, so there is no length limit.
Speech to text
from voxport.stt.wyoming_whisper import WyomingWhisperSTT
stt = WyomingWhisperSTT(host="localhost", port=10300) # the defaults
text = stt.transcribe(audio_16k, language="en")
audio_16k is a 1-D numpy array of mono audio at stt.sample_rate
(16 kHz), either int16 samples or floats in [-1, 1]. Resample first if
your audio has a different rate; Kokoro's output, for example, is
24 kHz. Each call opens a new connection to the server.
From async code
Both calls block, and transcribe() runs its own asyncio event loop, so
from code that already has a running loop, call them in a worker thread:
audio = await asyncio.to_thread(tts.synthesize, text, "bm_george")
text = await asyncio.to_thread(stt.transcribe, audio_16k, "en")
Writing your own adapter
An adapter subclasses TTSEngine or STTEngine, sets sample_rate,
and implements the one abstract method:
import numpy as np
from voxport.tts.base import TTSEngine
class SilenceTTS(TTSEngine):
"""A toy engine: one second of silence, whatever the text."""
sample_rate = 16000
def synthesize(self, text: str, voice: str, speed: float = 1.0):
return np.zeros(self.sample_rate, dtype=np.float32)
Code written against TTSEngine works with SilenceTTS and
KokoroDirectTTS unchanged. Adapters may accept extra, engine-specific
keyword arguments (like KokoroDirectTTS's lang_code), as long as the
base signature still works.
Offline use
By default, Kokoro's weights and voice packs download to Hugging Face's
standard cache (~/.cache/huggingface, or wherever HF_HOME points).
To run on a machine that can't reach huggingface.co:
- On a machine with internet access, synthesize once with each voice you plan to use, so the model and those voice packs are cached.
- Copy that cache to the offline machine, keeping Hugging Face's
hub/models--<org>--<name>/...layout, and pointHF_HOMEat it. - Set
HF_HUB_OFFLINE=1on the offline machine. voxport never sets this for you. With it set, only voices already in the cache work; an uncached voice fails with a Hub connection error instead of downloading.
Kokoro's English phonemizer also needs spaCy's en_core_web_sm model,
which downloads from GitHub, not Hugging Face, so HF_HUB_OFFLINE
doesn't cover it. It downloads once per Python environment, so install
it while online:
python -m spacy download en_core_web_sm
Repo-local cache (source checkouts only). If you run voxport from a
clone, you can keep the cache inside it instead: create an hf_cache/
folder at the repo root (it's gitignored) and KokoroDirectTTS uses it
as HF_HOME, unless you've set HF_HOME yourself. That makes the
cache easy to copy along with the checkout.
Running a Wyoming Whisper server
WyomingWhisperSTT needs a server to talk to. For local testing, the
easiest option is the rhasspy/wyoming-whisper
Docker image:
docker run -d --name whisper --restart unless-stopped -p 10300:10300 -v wyoming-data:/data rhasspy/wyoming-whisper --model tiny-int8 --language en
The image is named rhasspy/wyoming-whisper, even though its GitHub
repo is wyoming-faster-whisper. docker logs whisper shows Ready
once the model has loaded. tiny-int8 is small and fast but not very
accurate; use a larger model when transcription quality matters.
If you already run Home Assistant with its Whisper add-on, you can point
WyomingWhisperSTT at that server's host and port instead.
Development
git clone https://github.com/brienalkire/voxport.git
cd voxport
pip install -e ".[all]" pytest
python -m pytest
The tests need no network, no Whisper server and no Kokoro model:
WyomingWhisperSTT is tested against a fake in-process Wyoming server.
Installing .[wyoming] instead of .[all] is enough to run them and
skips the 2 GB torch download.
License
Metadata
Release files for voxport 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| voxport-0.4.0.tar.gz | 14.8 kB | With provenance | Details for voxport-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| voxport-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for voxport-0.4.0-py3-none-any.whl |
Total release size: 26.3 kB
Release files / voxport-0.4.0.tar.gz
| Download URL | voxport-0.4.0.tar.gz |
|---|---|
| Size | 14.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cc770d3c4f78d6672fb7224cd71ccaed55ed197af3cf10ec59c59d1967f809bb
|
|
BLAKE2b-256 checksum How to use checksums |
1fe5d3d0b2b012180a4ac9c413dd3a954aea4b595bb16d1588ca302261374618
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logRelease files / voxport-0.4.0-py3-none-any.whl
| Download URL | voxport-0.4.0-py3-none-any.whl |
|---|---|
| Size | 11.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
76caf4a10d7509df931da80a75747c6b2ba90b6d9decadabd6d74a1657ab6b70
|
|
BLAKE2b-256 checksum How to use checksums |
7759519b051c739d5a20509638db373f75be3b2b9bd57e0834bcd92c6b3b4320
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logSecurity for voxport 0.4.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for voxport-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logA 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.