voxora
Model-agnostic Speech-to-Text for Rust.
A candle-native bridge that unifies Whisper, Qwen3-ASR, and future Hugging Face audio models behind one trait, so any Rust application can swap engines without touching inference code.
Status: pre-alpha / engine adapters shipped. The CLI is in
voxora-cli/. The investigation recap and the phased roadmap are indocs/.
Why voxora?
The name is a Latin portmanteau, in the same construction style as Telora:
| Root | Language | Meaning |
|---|---|---|
| vox | Latin | voice |
| ora | Latin | mouth, speech, utterance |
vox + ora = voxora — "voice by mouth" — a name that describes what a
speech-to-text engine does (turn voice into uttered text) and that sounds
right when spoken, because it ends in the open vowel ah, the sound
produced by opening the mouth to speak.
It was chosen from a curated list of ~60 candidates across Latin, Greek, and modern coinages; the criteria were:
- Free on both crates.io and GitHub (
airvzxf/voxora). - Phonetically pronounceable in Spanish and English.
- Mirrors the construction of Telora (tele + ora), so the brand family reads.
- No hyphens (ergonomics in
use voxora::...), no-rssuffix (convention for native Rust crates), no domain suffix like-asr(the name describes the brand, not the function).
Why this project exists
Telora is a local Speech-to-Text assistant for Linux, written in Rust and
originally bound to whisper-rs. As the maintainer explored alternative
models (specifically Qwen3-ASR, via
huggingface/candle#3509
and the qwen3-asr-rs fork),
two problems surfaced:
- candle's maintainers prioritize NVIDIA-native optimization over new model architectures. The Qwen3-ASR PR has been waiting for review for months despite CPU/GPU benchmarks that match or beat PyTorch on RTX 3090.
- The Rust ASR ecosystem is split between ONNX Runtime and whisper.cpp.
transcribe-rs (233⭐)
already provides a
SpeechModeltrait that unifies nine engines, but every engine except whisper.cpp goes through ORT — never candle.
voxora is the candle-native sibling: one trait that wraps candle-native inference engines (Qwen3-ASR today, Voxtral/Granite-Speech tomorrow), with auto-resolution from Hugging Face and a hardware dispatcher (CUDA → Metal → CPU) that lets downstream apps pick the best available accelerator at runtime.
Telora will eventually depend on voxora instead of whisper-rs
directly, becoming model-agnostic the way the maintainer originally
envisioned.
How it fits in the stack
┌─────────────────────────────────────────────────────────────┐
│ Applications: Telora today, any future Rust STT consumer │
└────────────────────────┬────────────────────────────────────┘
│ depends on
▼
┌─────────────────────────────────────────────────────────────┐
│ **voxora** — the candle-native ASR bridge │
│ • AsrEngine trait, TranscribeOptions, TranscriptionResult │
│ • Hugging Face model resolution + quantization selection │
│ • Per-engine adapters (whisper-rs, qwen3-asr-rs, …) │
│ • Hardware dispatcher: CUDA → Metal → CPU │
└────────────────────────┬────────────────────────────────────┘
│ depends on
┌────────────────┼────────────────┐
▼ ▼ ▼
whisper-rs qwen3-asr-rs future engines
(whisper.cpp (candle, via (Voxtral, Granite-
bindings) airvzxf fork) Speech, Parakeet…)
Crates in this workspace
The workspace is a set of small crates that together form the
bridge. The first three rows are the public-API crates a downstream
consumer most commonly depends on; the rest are the implementation
crates behind them. Pick a row, follow the docs.rs link for the
API reference; the publishable crates also ship per-crate
example binaries under voxora-{name}/examples/ (e.g.
cargo run --example transcribe_wav_whisper -p voxora-whisper).
| Crate | crates.io | docs.rs | Role |
|---|---|---|---|
voxora-traits |
crates.io | docs.rs | Canonical traits (AsrEngine, ModelSource) |
voxora-engine |
crates.io | docs.rs | Adapter contract (EngineAdapter, EngineFamily) |
voxora-bridge |
crates.io | docs.rs | Umbrella crate — re-exports traits + engines behind feature flags |
voxora-hf |
crates.io | docs.rs | Hugging Face resolver |
voxora-whisper |
crates.io | docs.rs | whisper.cpp adapter (whisper-rs binding) |
voxora-qwen3asr |
crates.io | docs.rs | Qwen3-ASR adapter (qwen3-asr-rs binding) |
voxora-registry |
crates.io | docs.rs | Central model resolver |
voxora-backend |
crates.io | docs.rs | Hardware backend selection (CPU / Metal / CUDA) |
voxora-config |
crates.io | docs.rs | Env-var cascade (cache dir, HF token) |
voxora-cli |
build only | n/a | CLI binary (voxora list / download / run); publish = false |
voxora-testkit |
dev-only | n/a | Shared fixtures and mocks; publish = false |
voxora-core |
(removed) | (removed) | Deprecated shim around voxora-traits (last release 0.3.1); removed in 0.4.0 |
Status and roadmap
The phased plan is in docs/ROADMAP.md. The short
version:
| Phase | Goal | State |
|---|---|---|
| 0 | Repo scaffolding, docs | done |
| 1 | voxora-traits trait + types (originally voxora-core; split out in 0.3.0) |
done |
| 2 | voxora-hf HF model resolver |
done |
| 3 | voxora-whisper engine adapter |
done |
| 4 | voxora-qwen3asr engine adapter |
done |
| 5 | voxora-cli (list / download / run) |
done |
| 6 | Telora integration | pending |
Coordinated releases
voxora publishes a unified version across every workspace crate
that participates in a release. When voxora X.Y.0 ships, all
participating crates ship at X.Y.0 — you can depend on them with
matching versions and trust they stay in lockstep:
= "X.Y.0"
= "X.Y.0"
= "X.Y.0"
= "X.Y.0"
Picking a single version X.Y.0 and using it across the voxora-*
crates in your Cargo.toml is the supported way to consume voxora.
If you depend on, say, voxora-traits = "X.Y.0" and
voxora-engine = "X.Y.0", you can be confident they were released
together and that their public surfaces are wire-compatible. No need
to read per-crate changelogs to figure out which combinations of
minor versions are compatible — the workspace version is the
compatibility promise.
The full invariant (including the additive-change exception) is
documented in AGENTS.md → Version coordination.
Upgrade guide — 0.3.x → 0.4.0
voxora-core was removed in 0.4.0; depend on voxora-traits
directly. The trait surface (AsrEngine, TranscribeOptions,
ModelSource, etc.) has lived in voxora-traits since 0.3.0; the
shim crate only existed to ease the 0.3.x transition.
In your Cargo.toml, swap voxora-core = "0.3" for
voxora-traits = "0.4":
# Before (0.3.x)
= "0.3"
# After (0.4.0)
= "0.4"
If you used the voxora-core/serde Cargo feature, switch to the
matching voxora-traits/serde feature.
In your source, replace use voxora_core::*; with the matching
voxora-traits import:
// 0.3.x
use AsrEngine;
use TranscribeOptions;
// 0.4.0
use AsrEngine;
use TranscribeOptions;
A single cargo update against every participating crate brings
the whole workspace to 0.4.0 in lockstep:
cargo update -p voxora-traits \
-p voxora-config \
-p voxora-hf \
-p voxora-whisper \
-p voxora-qwen3asr \
-p voxora-engine \
-p voxora-backend \
-p voxora-registry \
-p voxora-bridge
The voxora-bridge umbrella crate is unaffected by the removal
and continues to re-export voxora-traits for one-stop
consumption. Per-crate notes on what changed in 0.4.0 are in each
crate's CHANGELOG.md:
voxora-traits/CHANGELOG.mdvoxora-config/CHANGELOG.mdvoxora-hf/CHANGELOG.mdvoxora-whisper/CHANGELOG.mdvoxora-qwen3asr/CHANGELOG.mdvoxora-engine/CHANGELOG.mdvoxora-backend/CHANGELOG.mdvoxora-registry/CHANGELOG.mdvoxora-bridge/CHANGELOG.md
Quickstart
# Build:
cargo build --release -p voxora-cli
# Download a model:
./target/release/voxora download Qwen/Qwen3-ASR-0.6B
# Transcribe a WAV (engine auto-detected from config.json):
./target/release/voxora run Qwen/Qwen3-ASR-0.6B samples/jfk.wav
# Or pin a specific engine:
./target/release/voxora run ggerganov/whisper.cpp samples/jfk.wav \
--engine whisper --language en
See voxora --help for the full surface. Engine selection falls
back to a --engine <whisper|qwen3-asr> override when config.json
doesn't disambiguate. Hardware flags mirror the engines
(--features cpu (default), metal, cuda).
Investigation
Why this repo exists, the gap it fills, and the options we considered
are documented in docs/INVESTIGATION.md.
Read it before opening an issue — most "why not just X?" questions are
answered there.
License
Apache License, Version 2.0. See LICENSE.
Contributing
See CONTRIBUTING.md and our
Code of Conduct.