Thin, host-native adapters that let external agent environments use AverVOX (OSS or Pro) as their local speech engine.
These packages ship separately from AverVOX itself so that host-plugin churn never touches the AppImage or the PyPI package, and so each one can be published to the registry its ecosystem actually uses.
A hosted agent that speaks sends every reply — and with STT, every word the user says — to a speech vendor, and is billed per character for the privilege. Routing that through AverVOX changes three things:
- Nothing leaves the machine. Synthesis and transcription run on the same box as the agent. There is no speech vendor in the path, so conversation text is not subject to anyone else's retention policy, and audio of the user's voice is never uploaded.
- No per-character billing. Speech cost stops scaling with how much the agent talks. A verbose agent and a terse one cost the same.
- It works offline. No API key to expire, no quota to exhaust, no outage to wait out. An agent on a laptop on a plane still speaks.
The tradeoff is honest: cloud voices still sound better than Piper, and the machine does the work. Pro's Kokoro voices narrow the quality gap — judge for yourself from the samples — and the latency numbers below cover the rest.
AverVOX-Integrations/
hermes/ # Hermes Agent TTS/STT plugin + config snippets (PyPI)
openclaw/ # OpenClaw speech provider (npm)
odysseus/ # Docs only until the Odysseus plugin ABI settles
| Repository | Contents |
|---|---|
| AverVOX-OSS | The free edition and the avrvx CLI these adapters call |
| AverVOX Pro | Commercial edition, distributed per licence from avervoxpro.com |
| This repo | Host adapters only — no speech code lives here |
AverVOX remains the local voice engine. Hosts call its CLI bridge; they do not embed Piper/Kokoro/faster-whisper themselves.
| Direction | Mechanism |
|---|---|
| AverVOX → host (Converse) | LLM profile pointing at the host’s OpenAI-compatible /v1 |
| Host → AverVOX (TTS/STT) | avrvx --synthesize / avrvx --transcribe / avrvx --capabilities |
Requires AverVOX 0.5.0 or newer (OSS or Pro):
# Capability probe (OSS vs Pro, engines, features)
avrvx --capabilities
# Text → WAV file (no playback)
avrvx --synthesize --text-file /tmp/in.txt --output /tmp/out.wav
avrvx --synthesize --text "Hello" -o /tmp/out.wav
echo "Hello" | avrvx --synthesize --text - -o /tmp/out.wav
# Pick a voice and rate for one request
avrvx --synthesize --text - -o /tmp/out.wav --voice af_heart --speed 1.2
# Audio file → transcript on stdout
avrvx --transcribe /tmp/message.oggDetails that matter when wiring a host:
--capabilitieswrites JSON to stdout and diagnostics to stderr, so parsing stdout is always safe. Treatedition,licensed, andfeaturesas a stable contract.--synthesizewrites mono PCM16 WAV mode0600and prints the path. The output directory must already exist.- Pass text via
--text-fileor--text -(stdin) rather than--text "…": arguments are visible to other local users in the process list. - Failures print a message on stderr and exit non-zero. Genuine runtime
failures during synthesis/transcription are prefixed
avrvx --<command> failed: …; upfront argument-validation errors are not (e.g.--synthesize requires --output PATHor--speed must be between 0.1 and 4.0both exit 2 with a bare message, no prefix). --voice,--engine, and--speedare per-request overrides that leave the user's saved settings alone. They arrived in 0.5.0, so detect them by checking fortts.voicesin the probe; both plugins do this and simply fall back to the configured voice on an older install.
OSS and Pro share the same commands. Pro unlocks Kokoro, wake word, session
memory, and avrvx --serve — probed via --capabilities, not separate adapters.
Every avrvx call pays for a Python start plus a model load before it produces
a sample, which a host speaking every agent reply pays on every turn.
avrvx --daemon pays it once and serves requests over a Unix socket at
$XDG_RUNTIME_DIR/avervox/bridge.sock, created mode 0600 inside a 0700
directory. It roughly quarters the time to a finished reply; see
Latency.
avrvx --daemon & # or the systemd unit in systemd/
avrvx --capabilities | grep -A3 daemon # reports the socket and whether it is upThe protocol is newline-delimited JSON, one request object per line and one response per line, on a connection a host may keep open across turns:
{"method": "synthesize", "text": "Hello", "output": "/tmp/out.wav", "voice": "af_heart", "speed": 1.1}
{"ok": true, "path": "/tmp/out.wav"}Methods are capabilities, synthesize, transcribe, cancel, and ping.
Failures come back as {"ok": false, "code": "...", "error": "..."}.
Both plugins try the socket first and fall back to spawning avrvx when it is
absent or reports code: "busy", so the daemon is purely an optimisation and
the CLI stays the authoritative contract. Set AVERVOX_BRIDGE=0 in a host's
environment to make that host ignore the daemon.
Waiting for a whole reply to finish rendering before the first word is heard is
the remaining latency most users notice. Adding "stream": true to a
synthesize request makes the daemon send audio as it is produced instead of a
single reply at the end. No output path is needed — nothing touches the disk.
Each frame is a JSON header line followed by exactly bytes of raw
little-endian PCM16, so a reader never has to guess where a frame ends:
{"ok": true, "frame": 0, "rate": 22050, "bytes": 8192}
<8192 bytes of PCM>
{"ok": true, "done": true, "frames": 12}
The shell equivalent is --output -, which writes the same headerless PCM to
stdout and announces the format on stderr before the first sample:
avrvx --synthesize --text "Hello there." --output - | aplay -f S16_LE -r 22050 -c 1Text is split into sentences before synthesis, which sets the frame size. Piper generates incrementally and will usually emit several frames per sentence; Kokoro returns one array per call, so its granularity is exactly one frame per sentence. Either way the first frame arrives after the first sentence rather than after the whole reply.
The OpenClaw plugin exposes this as streamSynthesize alongside synthesize,
and the Hermes plugin registers a tools.tts_streaming provider; both fall
back to avrvx --output - when the daemon is not running, and hosts that
cannot play chunks keep using the file-based path.
One caveat on the fallback: it applies only to a daemon that fails before any audio arrives. If the daemon dies mid-stream the plugin raises rather than respawning, since restarting would replay words the user already heard.
Measured through each host's own provider interface — not by timing avrvx
directly — on this project's reference machine, a 2017 laptop, with Piper
en_US-lessac-high at 22.05 kHz. "First audio" is a three-sentence reply and
counts the wait until the first chunk a host can play; "whole reply" is a
one-sentence reply and counts the wait until a finished WAV.
| Spawn per call | Warm daemon | |
|---|---|---|
| Hermes, first audio | 2.6 s | 0.32 s |
| Hermes, whole reply | 3.3 s | 0.77 s |
| OpenClaw, first audio | 2.6 s | 0.36 s |
| OpenClaw, whole reply | 3.2 s | 0.91 s |
The two effects are separable and compose. The daemon removes the Python start and the model load, which is the bulk of it. Streaming then removes the wait for the rest of the reply to render, which is what takes a warm call under half a second — quicker than the audio it is producing, so playback never starves.
Both are opt-in and neither is required: with no daemon and no streaming, the numbers in the first column are what a host gets, and everything still works.
| Directory | Host | Speech | Install |
|---|---|---|---|
hermes/ |
Hermes Agent 0.19.0 | TTS + STT, streaming | pip install avervox-hermes * |
openclaw/ |
OpenClaw 2026.6.33 (npm) / 2026.7.2 (source, minGatewayVersion) |
TTS, streaming | openclaw plugins install @avervox/openclaw-plugin |
odysseus/ |
Odysseus | — | Docs only: point AverVOX Converse at Odysseus /v1 |
* Install into the same Python environment as Hermes Agent. On Ubuntu
24.04, Debian 12+, and Linux Mint 22, a bare pip install outside a virtual
environment fails with error: externally-managed-environment (PEP 668) —
see hermes/README.md for the fix.
Each package README records the exact host versions it was tested against.
OpenClaw is speech-output only because its transcription hook is realtime and
duplex, which avrvx --transcribe cannot satisfy; Odysseus stays docs-only
until its plugin ABI settles.
Speech behavior belongs to AverVOX, not to a cross-host SDK. Anything a second
host would also want — the voice list, cancellation, the daemon — goes into
avrvx, and every adapter inherits it. That is what keeps these packages thin
enough to follow host churn without any speech code moving.
Choosing AverVOX belongs to the host. There is no Hermes/OpenClaw/Odysseus switch in the AverVOX UI and there will not be one: a host selects AverVOX as its own TTS/STT provider in its own config, and AverVOX talks back through an ordinary LLM profile. One switch spanning every host would put the same decision in two places and make AverVOX responsible for tracking each host's plugin API.
Within a host, config-driven CLI wiring is preferred wherever it exists (Hermes
command providers, OpenClaw tts-local-cli); a plugin ships only where richer
UX or registry distribution needs one. Each package documents both paths and
leads with the simpler one.
git clone https://github.com/avrvx/AverVOX-Integrations.git
cd AverVOX-Integrations
# Python packages and the contract tests
python -m pip install -r requirements-dev.txt
pytest
# OpenClaw plugin
cd openclaw && npm install && npm run typecheck && npm testThe contract tests in tests/ run against a stub avrvx, so they
need no models installed. Run them against a real install with
AVRVX_BIN=$(command -v avrvx) pytest -m live.