Python bindings for transcribe.cpp, a C/C++ speech-to-text library built on ggml.
Status: in development. Until wheels are published, use a locally built
libtranscribethrough repo auto-discovery orTRANSCRIBE_LIBRARY.
Upgrading from 0.1? See the
0.2 migration guide,
including the replacement of gpu_device= with exact device objects.
import transcribe_cpp
with transcribe_cpp.Model("model.gguf") as model:
with model.session() as session:
result = session.run(pcm_float32_16k_mono)
print(result.text)run() takes mono 16 kHz float32 PCM (buffer-protocol object or sequence). It
does not decode containers or resample; convert audio before calling it.
import numpy as np
pcm = np.asarray(audio, dtype=np.float32) # 1-D, 16 kHz mono
# Downmix stereo first; 2-D input is rejected:
# pcm = audio.mean(axis=1).astype(np.float32)
result = session.run(pcm)Generic run controls use "default" to preserve each model family's shipped
behavior. Models advertising model.supports("pnc") accept pnc="off" or
pnc="on"; models advertising model.supports("itn") accept the equivalent
itn values. The options are available on run(), run_batch(), stream(),
and the one-shot transcribe() helper.
result = session.run(pcm, pnc="off", itn="on")vocabulary (custom terms), prompt (context, or the instruction under
task="instruct") and prefix (text the model continues from) take effect
where model.supports() reports "vocabulary", "context_prompt",
"instruct" or "transcript_prefix".
result = session.run(pcm, vocabulary=["Kubernetes", "gRPC"])Streaming models expose incremental transcription with committed/tentative
text views — see examples/stream_wav.py:
with model.session() as session, session.stream() as stream:
for chunk in pcm_chunks:
stream.feed(chunk)
text = stream.text() # .committed (stable) + .tentative
stream.finalize()
result = stream.snapshot() # language, segments, words, tokens, timingsLong transcriptions can be cancelled from another thread with
session.cancel() — the run raises Aborted with the partial transcript on
exc.partial_result (same for OutputTruncated).
Models whose model.roles include Role.DIARIZE (Sortformer) answer "who
spoke when" through a diarize session. run() returns SpeakerSegment
rows (speaker_id in 1..model.diarize_info.max_speakers), grouped by
speaker. Locking, Busy, cancel() and close() work as on Session. A
model without the role raises UnsupportedRole, as model.session() and
model.capabilities do on a model without Role.ASR (Sortformer serves only
Role.DIARIZE).
with model.diarize_session() as diarizer:
turns = diarizer.run(pcm, family=transcribe_cpp.SortformerDiarizeOptions(
preset="very_high_latency"))
for turn in turns:
print(turn.speaker_id, turn.t0_ms, turn.t1_ms)Models whose model.roles include Role.LANGID (VoxLingua107 ECAPA-TDNN)
identify the spoken language through a langid session. run() returns a
LangIdResult with candidates ranked by p. Codes are the model's own labels
("iw", "jw"; model.langid_label_index("he") resolves aliases), so match
result.code against the ASR model's capabilities.languages yourself.
allowed=None scores every label; allowed_mass is the unrestricted
probability the allowed set captured. An empty allowed list raises
InvalidArgument; clips under model.langid_info.min_audio_ms raise
InputTooShort, and longer ones than max_audio_ms are scored on their first
max_audio_ms. Every allowed label comes back as a candidate. Locking,
Busy, cancel() and close() work as on Session.
with model.langid_session() as lid:
result = lid.run(pcm, allowed=["en", "de", "fr"])
print(result.code, result.candidates[0].p, result.allowed_mass)Models whose model.roles include Role.VAD (Silero VAD, GGUF or the
whisper.cpp .bin) score speech probability per frame of
model.vad_info.frame_samples samples through a VAD session. run() scores one
clip and returns a VadResult with probs and speech segments
(start_sample/end_sample; options default to Silero's
get_speech_timestamps). stream_feed() streams audio of any chunk size and
returns the probabilities of the frames each call completed (first_frame is
the position of the first); stream_flush() scores the zero-padded remainder
and ends the stream, stream_reset() drops it. The VAD stream does not take
the model's ASR stream lease. Locking, Busy, cancel() and close() work as
on Session.
with model.vad_session() as vad:
for seg in vad.run(pcm, min_silence_ms=300).segments:
print(seg.start_sample, seg.end_sample)
frame = model.vad_info.frame_samples
probs = []
for chunk in chunks:
r = vad.stream_feed(chunk)
print(f"{len(r.probs)} frames from sample {r.first_frame * frame}")
probs.extend(r.probs)
probs.extend(vad.stream_flush().probs)Model(backend=...) applies a backend policy ("auto" uses the best
available). transcribe_cpp.backends() returns process-local device objects;
pass one as Model(device=device) for exact selection with no fallback. Persist
a device's device_id, not its runtime handle or index. backend_available(kind)
checks whether a backend policy can currently be satisfied.
device = next(d for d in transcribe_cpp.backends() if d.device_type == "cpu")
with transcribe_cpp.Model("model.gguf", device=device) as model:
print(model.device)| Variable | Effect |
|---|---|
TRANSCRIBE_BACKEND |
overrides the "auto" default; explicit backend= still wins |
TRANSCRIBE_NATIVE_PROVIDER |
forces an installed native provider package, for example cu12 |
TRANSCRIBE_LIBRARY |
loads exactly this shared library |
Planned wheels will bundle CPU plus platform accelerators;
transcribe-cpp[cu12] will add the CUDA 12 provider.
The binding loads the native library at import and verifies its ABI layout and
version before use. Build a shared library, then run from the repo or point
TRANSCRIBE_LIBRARY at it:
cmake -B build-shared -DTRANSCRIBE_BUILD_SHARED=ON
cmake --build build-shared --target transcribe
cd bindings/python
PYTHONPATH=src uv run --no-project python examples/transcribe_wav.py \
../../models/whisper-tiny.en/whisper-tiny.en-Q5_K_M.gguf ../../samples/jfk.wavNo-model tests always run; model tests skip unless smoke assets are present.
Override paths with TRANSCRIBE_SMOKE_MODEL, TRANSCRIBE_SMOKE_AUDIO, and
TRANSCRIBE_SMOKE_STREAMING_MODEL.
cd bindings/python
TRANSCRIBE_LIBRARY=../../build-shared/src/libtranscribe.dylib \
uv run --extra test pytest- One compute call at a time per
Model: the binding serializes calls across all sessions of a model with a model-wide lock (load one model per worker for parallelism). While a stream is active, other runs and stream begins on that model raisetranscribe_cpp.Busy. See theModeldocstring. - Import package:
transcribe_cpp - Distribution:
transcribe-cpp - License: MIT