A native inference server in Rust. One Axum binary serves an
OpenAI-compatible HTTP API -- chat completions, speech synthesis,
transcription and translation, text and audio embeddings, speaker
diarization, OCR, PII classification/redaction, and a realtime WebRTC
voice protocol -- on top of 18 in-house crates that implement the model
stack directly: CUDA and WGSL kernels, quantized linear algebra, model
graphs, a KV-cache/scheduler engine, speculative decoding, tokenizers,
and safetensors weight loading. No PyTorch, no Python in the Rust
serving path (python/ ships a separate, PyTorch-based server).
The project started as a Rust/Go rewrite of the upstream speaches
realtime endpoint with a language-agnostic conformance rig. That part still exists and still matters (see
"Realtime protocol and conformance" below), but the bulk of the repo is
now the inference engine itself.
Canonical references, in order of authority:
docs/book/05.7-apple-silicon-inference-architecture.md
-- read this first before measuring, quantizing, or optimizing anything
on Apple silicon. How to run LLMs well on the M2 Ultra: the crossover
rule that decides whether fewer bits is actually faster (here it usually
is not -- int8 beats every 4-bit kernel in the survey, and the 31B got
1.96x faster by reading 43.5% more bytes), why our own per-dispatch
profiler overstates small passes ~10x and once invented a 154 ms/token
overhead that did not exist, the standing list of "obviously better"
changes that measured worse, the kernel rules, the per-token cost model
and its consequences for spec decode and batching, and where we stand
versus the field.docs/book/01.4-STATUS.md -- what is actually implemented,
phase by phase, including what is not verified.docs/book/08.4-PERFORMANCE.md -- every measured number,
both backends, with the conditions that make it meaningful.docs/book/08.1-quality-harness.md -- how output
quality is measured, and the chat-template rules that decide whether a
measurement means anything.docs/book/05.1-wgpu-status.md -- the wgpu/Vulkan port:
kernel parity, serving, and remaining work.docs/book/05.6-macos-port-status.md --
the single Apple-silicon document: what serves, the measured matrix, and
the known limitations.docs/book/04.1-fp8.md -- the cross-backend fp8 contract and its
default-flip gate.docs/book/01.1-native-rust-port-prd.md -- the
normative PRD.docs/book/07.1-barge-turn-spec-rfc-v3.md --
the normative realtime protocol spec.CLAUDE.md -- working conventions and operational
gotchas for this machine.23 OpenAI/Anthropic-compatible routes:
| Area | Routes |
|---|---|
| Chat / text | /v1/chat/completions, /v1/completions, /v1/responses, /v1/responses/{id}, /v1/messages, /v1/messages/count_tokens, /v1/models, /v1/embeddings |
| Audio | /v1/audio/speech, /v1/audio/transcriptions, /v1/audio/translations, /v1/audio/embeddings, /v1/audio/diarization, /v1/voice-profiles |
| Vision | /v1/ocr |
| PII | /v1/pii/classify, /v1/pii/classify/batch, /v1/pii/redact/analyze, /v1/pii/redact/render |
| Realtime | /v1/realtime (WebRTC), /v1/realtime/capabilities, /v1/inspect/sessions, /v1/inspect/sessions/history |
Those 23 are the OpenAI/Anthropic-shaped surface, not the whole surface:
the binary mounts 35 distinct paths / 40 method+path pairs. The rest are
operational or non-OpenAI -- /health, /health/ready, /health/sessions,
/version, /metrics, /v1/internal/chat-engines,
/v1/voice-profiles/{name}, three more /v1/inspect/* paths, and the
/v1/fine_tuning/jobs surface (rust/src/oapi/fine_tuning.rs, undocumented
and unaudited).
Which chat/embedding/STT/TTS checkpoints each route can actually serve,
and why: docs/book/02.1-model-compat-matrix.md
is the static trace of the CUDA dispatch;
docs/book/05.6-macos-port-status.md carries the
Apple-silicon measured matrix.
nv-layers::backend::BackendKind has three: Cuda,
Wgpu, Cpu. CUDA is the mature path -- graph capture, paged KV,
speculative decoding all live there. The wgpu path is real and serves
chat with coherent output on five checkpoints across four model graphs
(Gemma4-E4B bf16 and qat-w4a16-ct, Gemma4-31B-NVFP4, Qwen3.6-35B-A3B-NVFP4,
Laguna-XS-2.1-NVFP4 -- all re-verified over HTTP on M2 Ultra,
docs/book/05.6-macos-port-status.md), but it is
newer and less proven (docs/book/05.1-wgpu-status.md).
Backend selection is NV_SERVE_BACKEND=cuda|wgpu|auto; auto is
cuda-first and only falls back to wgpu when CUDA reports a concrete
reason it cannot serve the model. The metal cargo feature is real
-- wired through candle-core/metal in eight crates plus the binary, and
the whole Apple-silicon serving surface runs on it; Metal compute reaches
the GPU through wgpu/WGSL, so nv-kernels' own metal feature is an
empty stub (there are no in-tree .metal kernels). rocm is a stub
everywhere.verify_chain costs 2-5x a decode step, so break-even needs tau~2-7
while the best drafter reaches 1.55-1.73
(docs/book/05.6-macos-port-status.md).nv-ocr) plus DeepSeek-OCR-2,
both served at /v1/ocr (docs/book/06.3-ocr.md)./v1/audio/diarization,
as response_format=diarized_json on transcriptions, and as realtime
data-channel events. The segmentation ONNX is not shipped -- until
rust/scripts/export-diarizen-onnx.py has been run, /v1/audio/diarization
answers 503 model_not_loaded (correctly) and diarized_json returns
every speaker as null with no failure signal (a real defect, tracked
in docs/book/05.6-macos-port-status.md). The
speaker-embedding half works on its own at /v1/audio/embeddings.Numbers go stale within hours here, so this README quotes none.
docs/book/08.4-PERFORMANCE.md is the rule for
quoting one (a number without its basis tuple is not a number), and the
per-platform measured records live in the book's chapter-5 and chapter-8
subchapters. Per docs/book/01.4-STATUS.md, 0 of 9
PRD §11.5 performance gates have been measured -- what exists are real bs=1
decode figures with per-run provenance. Treat any number found outside those
documents as unverified.
Always build through rust/scripts/nvk.sh. It owns the nix
devshell (cached nix print-dev-env, so .cu files are not recompiled
on every invocation), TMPDIR, CUDA_ARCH_LIST, a per-lane
CARGO_TARGET_DIR, and parity-gate env. Bare cargo or ad-hoc
nix develop --command cargo invocations are slower and subtly
different; see CLAUDE.md for the full rationale.
# default: cuda,wgpu features, nv-kernels package
rust/scripts/nvk.sh test --test parity_gdn -- --nocapture
# fast edit loop: wgpu only, skips every nvcc invocation
NVK_FEATURES=wgpu rust/scripts/nvk.sh test --test wgpu_rope
# another crate
NVK_PKG=nv-models rust/scripts/nvk.sh test --test gemma4_moe
# concurrent agents/sessions: one lane each
NVK_LANE=mylane rust/scripts/nvk.sh test --test <suite>
rust/scripts/nvk.sh --help lists all knobs. Real-weight tests are
#[ignore] + feature-gated and opt in via NV_*_TEST=1 env vars.
Note that parity_* suites require both cuda and wgpu features --
running them with NVK_FEATURES=wgpu compiles them to nothing and
reports a vacuous pass.
The server binary lives under rust/. It reads models from nix-hug
symlinks pinned in flake.nix, so run it from a hub shell (which
realizes the pinned HF weights, ~25 GB) or fetch models first:
nix run .#fetch-models # one-time weight fetch
nix develop .#cuda # CUDA shell + pinned model corpus
cd rust && cargo run --features cuda -- --port 8000
nix develop / nix develop .#cuda are the plain (no-hub) shells; see
rust/README.md for shell internals and CUDA build
details.
The realtime side implements a WebRTC voice loop with barge-in:
server-side VAD, streaming STT, end-of-utterance detection, chat
upstream, streaming TTS, and interruption semantics. The normative
contract is docs/book/07.1-barge-turn-spec-rfc-v3.md.
Two implementations exist -- the Rust server and a self-contained Go
server (go/) -- and both replay the shared fixture corpus at
conformance/ in-process against a canonical assertion library. Each
conformance/fixtures/NNN-<name>/ directory is one scenario
(input.jsonl ops in, expected.jsonl canonical wire trace out);
scenario-level protocol coverage lives there, not in ad-hoc scripts.
The 020/030/040 fixture families are skip_when_no_model: true
placeholders pending model-backed runs.
client/ holds Python e2e drivers (built on aiortc, the same WebRTC
stack upstream speaches uses) plus a black-box fuzzer that enforces the
RFC's wire invariants. Every script is a self-contained PEP 723 file --
uv run resolves deps on first use.
./conformance/runner/run_fixture.py --all # language-agnostic
cd go && go test ./internal/realtime/ -run TestConformanceCorpus -v
cd rust && cargo test --test conformance
./client/test_e2e.py --target http://localhost:8000 # over-the-wire STT round-trip
./client/test_e2e_full.py # full conversation loop
./client/fuzz_e2e.py # invariant fuzzer
Both servers report their feature surface at
GET /v1/realtime/capabilities. Per-implementation as-built references:
rust/IMPLEMENTATION.md,
go/IMPLEMENTATION.md.
speaches-plus/
rust/
src/ Axum HTTP binary (routes, chat engine dispatch, realtime)
crates/
nv-kernels CUDA (.cu) + WGSL kernels, parity harness
nv-layers linear/attention/MoE layers, backend selection
nv-models model graphs (Gemma4, Qwen3/3.5/3.6-MoE, Laguna, DeepSeek-OCR-2, ...)
nv-engine scheduler, block manager, paged KV, CUDA graphs
nv-specdecode Eagle3 / DFlash / DSpark / MTP / ngram drafters
nv-quant NVFP4, MXFP4, FP8, int8, Marlin
nv-weights safetensors + quant-config loading
nv-tokenizer tokenizers + incremental detokenization
nv-grammar guided decoding (JSON-schema -> regex -> DFA)
nv-tts Qwen3-TTS pipeline
nv-omni audio/vision encoders, talker, vocoder
nv-aligner forced alignment, timestamps, SRT/VTT
nv-ocr classical CPU OCR pipeline
nv-punkt sentence segmentation
nv-config, nv-runner
go/ Go realtime server (self-contained: pion, whisper.cpp, Kokoro)
python/ Qwen3-Omni multimodal HTTP server (PyTorch; Apache-2.0, derived from upstream speaches)
rocq/ Rocq (Coq) proofs + generators: launch geometry, roofline, KV budgets
conformance/ shared fixture corpus + canonical assertions + runner
client/ Python e2e drivers + fuzzer (aiortc)
studio/ nur studio: React/Vite web UI (proxies /v1 to the server; NUR_API overrides)
inspector/ realtime session inspector web UI
examples/ worked examples (LoRA training notebook + data)
scripts/ repo tooling (strip-comments.py, bench.py, chat.py, dev-env.sh)
docs/book/ the book: narrative chapters + every working doc as dotted subchapters
flake.nix dev shells + pinned model corpus (nix-hug)
Source code: AGPL-3.0-or-later (see LICENSE), except
python/, which derives from upstream speaches and stays Apache-2.0
(see python/LICENSE).
Model weights are governed by their own licenses -- see
NOTICE for the per-model list. The most restrictive bundle
is the DiariZen segmentation model (CC-BY-NC-4.0, non-commercial
only); commercial deployments must replace it. Every other shipped
weight permits commercial use.
2 commits
Rust
75.8%
Python
7.4%
WGSL
4.1%
Cuda
3.3%
Go
2.4%
Rocq Prover
1.7%
TypeScript
1.5%
C++
1.4%
A native inference server in Rust. One Axum binary serves an
OpenAI-compatible HTTP API -- chat completions, speech synthesis,
transcription and translation, text and audio embeddings, speaker
diarization, OCR, PII classification/redaction, and a realtime WebRTC
voice protocol -- on top of 18 in-house crates that implement the model
stack directly: CUDA and WGSL kernels, quantized linear algebra, model
graphs, a KV-cache/scheduler engine, speculative decoding, tokenizers,
and safetensors weight loading. No PyTorch, no Python in the Rust
serving path (python/ ships a separate, PyTorch-based server).
The project started as a Rust/Go rewrite of the upstream speaches
realtime endpoint with a language-agnostic conformance rig. That part still exists and still matters (see
"Realtime protocol and conformance" below), but the bulk of the repo is
now the inference engine itself.
Canonical references, in order of authority:
docs/book/05.7-apple-silicon-inference-architecture.md
-- read this first before measuring, quantizing, or optimizing anything
on Apple silicon. How to run LLMs well on the M2 Ultra: the crossover
rule that decides whether fewer bits is actually faster (here it usually
is not -- int8 beats every 4-bit kernel in the survey, and the 31B got
1.96x faster by reading 43.5% more bytes), why our own per-dispatch
profiler overstates small passes ~10x and once invented a 154 ms/token
overhead that did not exist, the standing list of "obviously better"
changes that measured worse, the kernel rules, the per-token cost model
and its consequences for spec decode and batching, and where we stand
versus the field.docs/book/01.4-STATUS.md -- what is actually implemented,
phase by phase, including what is not verified.docs/book/08.4-PERFORMANCE.md -- every measured number,
both backends, with the conditions that make it meaningful.docs/book/08.1-quality-harness.md -- how output
quality is measured, and the chat-template rules that decide whether a
measurement means anything.docs/book/05.1-wgpu-status.md -- the wgpu/Vulkan port:
kernel parity, serving, and remaining work.docs/book/05.6-macos-port-status.md --
the single Apple-silicon document: what serves, the measured matrix, and
the known limitations.docs/book/04.1-fp8.md -- the cross-backend fp8 contract and its
default-flip gate.docs/book/01.1-native-rust-port-prd.md -- the
normative PRD.docs/book/07.1-barge-turn-spec-rfc-v3.md --
the normative realtime protocol spec.CLAUDE.md -- working conventions and operational
gotchas for this machine.23 OpenAI/Anthropic-compatible routes:
| Area | Routes |
|---|---|
| Chat / text | /v1/chat/completions, /v1/completions, /v1/responses, /v1/responses/{id}, /v1/messages, /v1/messages/count_tokens, /v1/models, /v1/embeddings |
| Audio | /v1/audio/speech, /v1/audio/transcriptions, /v1/audio/translations, /v1/audio/embeddings, /v1/audio/diarization, /v1/voice-profiles |
| Vision | /v1/ocr |
| PII | /v1/pii/classify, /v1/pii/classify/batch, /v1/pii/redact/analyze, /v1/pii/redact/render |
| Realtime | /v1/realtime (WebRTC), /v1/realtime/capabilities, /v1/inspect/sessions, /v1/inspect/sessions/history |
Those 23 are the OpenAI/Anthropic-shaped surface, not the whole surface:
the binary mounts 35 distinct paths / 40 method+path pairs. The rest are
operational or non-OpenAI -- /health, /health/ready, /health/sessions,
/version, /metrics, /v1/internal/chat-engines,
/v1/voice-profiles/{name}, three more /v1/inspect/* paths, and the
/v1/fine_tuning/jobs surface (rust/src/oapi/fine_tuning.rs, undocumented
and unaudited).
Which chat/embedding/STT/TTS checkpoints each route can actually serve,
and why: docs/book/02.1-model-compat-matrix.md
is the static trace of the CUDA dispatch;
docs/book/05.6-macos-port-status.md carries the
Apple-silicon measured matrix.
nv-layers::backend::BackendKind has three: Cuda,
Wgpu, Cpu. CUDA is the mature path -- graph capture, paged KV,
speculative decoding all live there. The wgpu path is real and serves
chat with coherent output on five checkpoints across four model graphs
(Gemma4-E4B bf16 and qat-w4a16-ct, Gemma4-31B-NVFP4, Qwen3.6-35B-A3B-NVFP4,
Laguna-XS-2.1-NVFP4 -- all re-verified over HTTP on M2 Ultra,
docs/book/05.6-macos-port-status.md), but it is
newer and less proven (docs/book/05.1-wgpu-status.md).
Backend selection is NV_SERVE_BACKEND=cuda|wgpu|auto; auto is
cuda-first and only falls back to wgpu when CUDA reports a concrete
reason it cannot serve the model. The metal cargo feature is real
-- wired through candle-core/metal in eight crates plus the binary, and
the whole Apple-silicon serving surface runs on it; Metal compute reaches
the GPU through wgpu/WGSL, so nv-kernels' own metal feature is an
empty stub (there are no in-tree .metal kernels). rocm is a stub
everywhere.verify_chain costs 2-5x a decode step, so break-even needs tau~2-7
while the best drafter reaches 1.55-1.73
(docs/book/05.6-macos-port-status.md).nv-ocr) plus DeepSeek-OCR-2,
both served at /v1/ocr (docs/book/06.3-ocr.md)./v1/audio/diarization,
as response_format=diarized_json on transcriptions, and as realtime
data-channel events. The segmentation ONNX is not shipped -- until
rust/scripts/export-diarizen-onnx.py has been run, /v1/audio/diarization
answers 503 model_not_loaded (correctly) and diarized_json returns
every speaker as null with no failure signal (a real defect, tracked
in docs/book/05.6-macos-port-status.md). The
speaker-embedding half works on its own at /v1/audio/embeddings.Numbers go stale within hours here, so this README quotes none.
docs/book/08.4-PERFORMANCE.md is the rule for
quoting one (a number without its basis tuple is not a number), and the
per-platform measured records live in the book's chapter-5 and chapter-8
subchapters. Per docs/book/01.4-STATUS.md, 0 of 9
PRD §11.5 performance gates have been measured -- what exists are real bs=1
decode figures with per-run provenance. Treat any number found outside those
documents as unverified.
Always build through rust/scripts/nvk.sh. It owns the nix
devshell (cached nix print-dev-env, so .cu files are not recompiled
on every invocation), TMPDIR, CUDA_ARCH_LIST, a per-lane
CARGO_TARGET_DIR, and parity-gate env. Bare cargo or ad-hoc
nix develop --command cargo invocations are slower and subtly
different; see CLAUDE.md for the full rationale.
# default: cuda,wgpu features, nv-kernels package
rust/scripts/nvk.sh test --test parity_gdn -- --nocapture
# fast edit loop: wgpu only, skips every nvcc invocation
NVK_FEATURES=wgpu rust/scripts/nvk.sh test --test wgpu_rope
# another crate
NVK_PKG=nv-models rust/scripts/nvk.sh test --test gemma4_moe
# concurrent agents/sessions: one lane each
NVK_LANE=mylane rust/scripts/nvk.sh test --test <suite>
rust/scripts/nvk.sh --help lists all knobs. Real-weight tests are
#[ignore] + feature-gated and opt in via NV_*_TEST=1 env vars.
Note that parity_* suites require both cuda and wgpu features --
running them with NVK_FEATURES=wgpu compiles them to nothing and
reports a vacuous pass.
The server binary lives under rust/. It reads models from nix-hug
symlinks pinned in flake.nix, so run it from a hub shell (which
realizes the pinned HF weights, ~25 GB) or fetch models first:
nix run .#fetch-models # one-time weight fetch
nix develop .#cuda # CUDA shell + pinned model corpus
cd rust && cargo run --features cuda -- --port 8000
nix develop / nix develop .#cuda are the plain (no-hub) shells; see
rust/README.md for shell internals and CUDA build
details.
The realtime side implements a WebRTC voice loop with barge-in:
server-side VAD, streaming STT, end-of-utterance detection, chat
upstream, streaming TTS, and interruption semantics. The normative
contract is docs/book/07.1-barge-turn-spec-rfc-v3.md.
Two implementations exist -- the Rust server and a self-contained Go
server (go/) -- and both replay the shared fixture corpus at
conformance/ in-process against a canonical assertion library. Each
conformance/fixtures/NNN-<name>/ directory is one scenario
(input.jsonl ops in, expected.jsonl canonical wire trace out);
scenario-level protocol coverage lives there, not in ad-hoc scripts.
The 020/030/040 fixture families are skip_when_no_model: true
placeholders pending model-backed runs.
client/ holds Python e2e drivers (built on aiortc, the same WebRTC
stack upstream speaches uses) plus a black-box fuzzer that enforces the
RFC's wire invariants. Every script is a self-contained PEP 723 file --
uv run resolves deps on first use.
./conformance/runner/run_fixture.py --all # language-agnostic
cd go && go test ./internal/realtime/ -run TestConformanceCorpus -v
cd rust && cargo test --test conformance
./client/test_e2e.py --target http://localhost:8000 # over-the-wire STT round-trip
./client/test_e2e_full.py # full conversation loop
./client/fuzz_e2e.py # invariant fuzzer
Both servers report their feature surface at
GET /v1/realtime/capabilities. Per-implementation as-built references:
rust/IMPLEMENTATION.md,
go/IMPLEMENTATION.md.
speaches-plus/
rust/
src/ Axum HTTP binary (routes, chat engine dispatch, realtime)
crates/
nv-kernels CUDA (.cu) + WGSL kernels, parity harness
nv-layers linear/attention/MoE layers, backend selection
nv-models model graphs (Gemma4, Qwen3/3.5/3.6-MoE, Laguna, DeepSeek-OCR-2, ...)
nv-engine scheduler, block manager, paged KV, CUDA graphs
nv-specdecode Eagle3 / DFlash / DSpark / MTP / ngram drafters
nv-quant NVFP4, MXFP4, FP8, int8, Marlin
nv-weights safetensors + quant-config loading
nv-tokenizer tokenizers + incremental detokenization
nv-grammar guided decoding (JSON-schema -> regex -> DFA)
nv-tts Qwen3-TTS pipeline
nv-omni audio/vision encoders, talker, vocoder
nv-aligner forced alignment, timestamps, SRT/VTT
nv-ocr classical CPU OCR pipeline
nv-punkt sentence segmentation
nv-config, nv-runner
go/ Go realtime server (self-contained: pion, whisper.cpp, Kokoro)
python/ Qwen3-Omni multimodal HTTP server (PyTorch; Apache-2.0, derived from upstream speaches)
rocq/ Rocq (Coq) proofs + generators: launch geometry, roofline, KV budgets
conformance/ shared fixture corpus + canonical assertions + runner
client/ Python e2e drivers + fuzzer (aiortc)
studio/ nur studio: React/Vite web UI (proxies /v1 to the server; NUR_API overrides)
inspector/ realtime session inspector web UI
examples/ worked examples (LoRA training notebook + data)
scripts/ repo tooling (strip-comments.py, bench.py, chat.py, dev-env.sh)
docs/book/ the book: narrative chapters + every working doc as dotted subchapters
flake.nix dev shells + pinned model corpus (nix-hug)
Source code: AGPL-3.0-or-later (see LICENSE), except
python/, which derives from upstream speaches and stays Apache-2.0
(see python/LICENSE).
Model weights are governed by their own licenses -- see
NOTICE for the per-model list. The most restrictive bundle
is the DiariZen segmentation model (CC-BY-NC-4.0, non-commercial
only); commercial deployments must replace it. Every other shipped
weight permits commercial use.
2 commits
Rust
75.8%
Python
7.4%
WGSL
4.1%
Cuda
3.3%
Go
2.4%
Rocq Prover
1.7%
TypeScript
1.5%
C++
1.4%