This is the high-level procedure for adding a new model family to
transcribe.cpp, starting from only a Hugging Face repo name.
The process is mostly linear. Numerical bring-up and benchmark work loop back into conversion and graph implementation, but a port should still move through the same named stages and produce the same named artifacts.
Speech models fall into different architectural patterns. Identifying the pattern early shapes every downstream decision (graph structure, KV cache strategy, dump points, decoding loop).
Common patterns:
- Encoder + transducer (Parakeet): conformer encoder, RNNT/TDT joint network. Single-shot decode, no autoregressive loop.
- Encoder-decoder with cross-attention (Cohere, Whisper): encoder produces hidden states, decoder attends to them via cross-attention. Autoregressive decoding with KV cache.
- Audio-LLM / token injection (Qwen3-ASR, Qwen2-Audio): audio
encoder produces embeddings that replace placeholder tokens in a
causal LM's input sequence. No cross-attention — the LM processes
the fused audio+text sequence. Autoregressive decoding with KV
cache. The decoder is architecturally a standard LLM;
llama.cppis a useful reference for the decoder side. - Encoder + CTC: encoder-only with a CTC head. No decoder, no autoregressive loop.
The implementer should identify which pattern applies during Step 1 (research) and record it in the family note. For multimodal models with independent components (e.g. audio encoder + text LM), the encoder and decoder bring-up can proceed in parallel.
The authoritative stage chain is the eight porting-N skills under
.claude/skills/:
1-intake → 2-oracle → 3-convert → 4-cpp → 5-quants → 6-bench → 7-wer → 8-ship
Each skill owns its postconditions and is the source of truth for what must exist by the time you advance. Read the corresponding SKILL.md before starting each stage.
The high-level intent of each stage:
- Intake — research identity, capabilities; draft the family doc's
## Capability Validationtable; clear Preflight Gate A. - Oracle — run the reference framework on every manifest case,
capture tensor dumps + transcripts and a provisional tolerances file
sized from per-tensor magnitude (
1e-4 × p99_abs/1e-5 × rms). - Convert — produce only the reference-dtype GGUF; clear Preflight Gate B.
- C++ — implement
src/arch/<family>/, finalize tolerances, run the family-doc Capability Validation table, gate full ref-dtype WER vs the measured Oracle reference baseline. - Quants — generate the shipped quant matrix, CLI smoke each produced GGUF, and take a tentative quant WER read for human review.
- Bench — performance matrix; every accepted iteration re-runs
validate.py all. - WER — full release WER sweep; ref-dtype hard gate against the measured Oracle reference baseline, quants human-reviewed and not auto-gated.
- Ship — checklist-driven local prep of the family doc, model card, HF YAML, HF README.
The detailed testing contract is in
docs/model-family-testing.md.
Every family should end with:
docs/porting/families/<family>.mdscripts/envs/<family>/pyproject.toml- converter support, normally
scripts/convert-<family>.py - reference dump support, ideally through a unified reference dumper
tests/tolerances/<family>.json- synthetic fixture support under
tests/fixtures/ tests/<family>_smoke.cpptests/<family>_real_smoke.cpptests/<family>_e2e_smoke.cppor an equivalent legacy-named test- bench matrix support in
scripts/bench/run.py - manifest support under
tests/golden/<family>/soscripts/validate.pycan run reference -> C++ -> compare
Numerical validation workflow is described in
4-numerical-validation.md. Common
numerical failure patterns and fixes are collected in
4a-numerical-troubleshooting.md.
Use a stable family key everywhere:
parakeet
cohere
<new-family>
Use that key in paths, manifests, CTest names, report names, tolerance files, and environment names. If the upstream architecture string differs from the repo family key, record both in the family note.
The existing Parakeet and Cohere ports now have the central numerical validation path in place:
uv run scripts/validate.py all --family parakeet
uv run scripts/validate.py all --family cohereRemaining gaps are narrower:
- Reference dump scripts are split by family.
- Golden manifests are intentionally minimal v2 manifests and do not yet record full snapshot hashes or artifact cache keys.
- Benchmark comparison exists as a script, but the porting process does not yet require reference baseline rows and accuracy hashes.
- Default
ctestno longer depends on source-tree numerical golden payloads; numerical payloads are generated underbuild/validate/.
New families should follow this guide rather than copying the historical shape verbatim.