From 598a5f506101dd519f88d79cd1870bf7865e8398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=A4mmerer?= Date: Wed, 23 Sep 2026 15:11:32 +0200 Subject: [PATCH 1/5] Add the Voxtral transcription engine for Apple Silicon, as a worker that speaks the same queue protocol as the Whisper one voxtral_engine.py decodes with mlx-voxtral on the GPU and owns everything around it: pass sizing from installed RAM, pause-aligned cuts, repetition-loop detection with a temperature ladder and prefix salvage, language guards, recovery of a dropped opening, the percentile log-Mel clamp floor, and word timestamps by CTC forced alignment (emissions from transformers, the Viterbi in numpy in ctc_align.py). A model that is not downloaded and cannot be fetched says so, decided by asking the hub. transcript_corrections.py is the user-maintained find/replace list that stands in for the hotword hook Voxtral does not have, and it sets a speaker name's spelling where the model wrote one that sounds the same and no dictionary knows. Nothing here is imported unless a Voxtral model is selected. Co-Authored-By: Claude Fable 5.1 Co-Authored-By: Claude Opus 5.5 --- environments/requirements_macOS_arm64.txt | 2 + .../requirements_voxtral_macOS_arm64.txt | 47 + noScribe/__init__.py | 2 +- noScribe/ctc_align.py | 154 + noScribe/transcript_corrections.py | 259 ++ noScribe/voxtral_engine.py | 3540 +++++++++++++++++ noScribe/voxtral_mp_worker.py | 84 + 7 files changed, 4087 insertions(+), 1 deletion(-) create mode 100644 environments/requirements_voxtral_macOS_arm64.txt create mode 100644 noScribe/ctc_align.py create mode 100644 noScribe/transcript_corrections.py create mode 100644 noScribe/voxtral_engine.py create mode 100644 noScribe/voxtral_mp_worker.py diff --git a/environments/requirements_macOS_arm64.txt b/environments/requirements_macOS_arm64.txt index 3d5f4144..af876b5b 100644 --- a/environments/requirements_macOS_arm64.txt +++ b/environments/requirements_macOS_arm64.txt @@ -4,6 +4,8 @@ # as torch 2.8. noScribe supplies pyannote with an in-memory waveform, so its # audio path does not require TorchCodec's optional system FFmpeg backend. torch==2.13 +# pyannote.audio's dependency (torch-audiomentations too); noScribe's own +# forced alignment (noScribe/ctc_align.py) is numpy and does not use it. torchaudio==2.11 torchcodec==0.15.0 av<19 # faster-whisper 1.2.1 passes metadata_errors to av.open(), which PyAV 19 removed diff --git a/environments/requirements_voxtral_macOS_arm64.txt b/environments/requirements_voxtral_macOS_arm64.txt new file mode 100644 index 00000000..f837241b --- /dev/null +++ b/environments/requirements_voxtral_macOS_arm64.txt @@ -0,0 +1,47 @@ +# Optional Voxtral transcription engine (Apple Silicon only). +# Install on top of requirements_macOS_arm64.txt to enable the +# "voxtral-mini-8bit" and "voxtral-small-4bit" models in the backend: +# +# pip install -r environments/requirements_voxtral_macOS_arm64.txt +# +# Transcription runs on the Apple Silicon GPU via MLX. When a timestamped +# output is requested, word timestamps are recovered with CTC forced alignment +# against a wav2vec2 model: emissions via transformers, the Viterbi DP in +# numpy (noScribe/ctc_align.py). torchaudio is no longer needed for this; +# the base requirements keep it because pyannote.audio depends on it. +# +# mlx-voxtral is pinned because the engine was validated against one version, +# not because upstream is dead: it went a year without a release, then in +# 2026-08 relicensed to MIT and shipped two releases in one day. mlx and +# mlx-lm are pinned too because voxtral_engine drives decoding through mlx_lm +# internals (generate_step + a small model adapter); a newer mlx-lm could change +# that API or break the frozen mlx-voxtral. These are the versions the engine +# and its byte-identity tests were validated against -- bump them deliberately, +# together. +mlx==0.32.1 +mlx-lm==0.31.3 +# 0.0.6 is the first release that carries every fix this engine works around: +# the stop-token one from 0.0.5 (32000 is the text token " Capital", not a pad +# token) plus the log-Mel and embedding-merge ones. The log-Mel is now computed +# over the whole audio and split afterwards, as Voxtral specifies, which changes +# the encoder input on most frames -- measured to cost nothing at the +# transcript (dWER +0.02 [-0.19, +0.24]), see docs/voxtral-mel-clamp-floor.md +# section 8. mlx>=0.32.1 is its own floor, not ours. +mlx-voxtral==0.0.6 +# transformers 5 is what the engine was validated against, and mlx-lm 0.31.3 +# already requires it -- a lower floor here only allowed an untested 4.x to +# satisfy this file while pip installed 5 anyway. No upper bound: validated on +# 5.16.1 (411 tests green, 2026-09-02). +# +# If you meet the "incorrect regex pattern / set fix_mistral_regex=True" warning, +# it is not about this build. That check is a config.json heuristic (mistral-family +# model_type plus transformers_version < 5.0.0; ours says 4.54.0.dev0) and never +# reads the actual pattern. Our tekken.json carries the CORRECT pattern verbatim, +# and the reference case from the upstream discussion encodes as +# [1039, 1784, 1039] -- the correct 3-token "'"/"The"/"'" split, not the broken +# 4-token one -- on 5.15 and 5.16.1 alike, both of which resolve this build to +# MistralCommonBackend. Do NOT set fix_mistral_regex: it patches a fast-tokenizer +# pre-tokenizer this path does not have. The warning appears only via +# AutoProcessor, which resolves a different backend (see docs/migration-mlx-audio.md). +transformers>=5 +soundfile diff --git a/noScribe/__init__.py b/noScribe/__init__.py index 2dcc305e..5d941502 100644 --- a/noScribe/__init__.py +++ b/noScribe/__init__.py @@ -16,7 +16,7 @@ # Everything main.py used to bind on the package as a side effect of being # imported eagerly. Kept so `import noScribe; noScribe.utils...` still works # and so a typo gets an honest "no attribute" rather than a stale one. -_SUBMODULES = ("main", "audio", "exception", "transcription", "utils") +_SUBMODULES = ("main", "audio", "ctc_align", "exception", "transcription", "utils") # Star-import reads __all__, never __dir__; without this `from noScribe import *` # would bind the helper names above instead of the package's own module. diff --git a/noScribe/ctc_align.py b/noScribe/ctc_align.py new file mode 100644 index 00000000..acf0d75d --- /dev/null +++ b/noScribe/ctc_align.py @@ -0,0 +1,154 @@ +"""CTC forced alignment in numpy -- the Voxtral engine's word-timestamp DP. + +Replaces ``torchaudio.functional.forced_align`` and ``merge_tokens``, keeping +their semantics (unbatched): ``forced_align`` returns one label per frame and +that label's log-probability per frame; ``merge_tokens`` collapses the frames +into spans with an *exclusive* end and the mean score. + +Why this exists rather than the C++ kernel: torchaudio 2.9-2.11 index the DP +buffer in 32 bits and segfault past ~2**31 cells (pytorch/audio#4208) -- numpy +indexes in 64 bits, so the worst case is slow, not dead; and on an exact tie +between the advance-one and advance-two candidates that kernel takes the worse +value (pytorch/audio#4221), this one the larger, so it is a correct Viterbi. +Everywhere else it is frame-for-frame identical to torchaudio (see +tests/test_forced_align_stability.py; the tie cases are in tests/test_ctc_align.py). + +Cost and memory: ~1.35x the C++ kernel, and one byte per DP cell for the +backtrace (as in torchaudio), so the cell budgets in ``voxtral_engine`` still +apply. The measurements, every optimisation tried, and the benches that +reproduce them: ``docs/viterbi-numpy-brief.md``, ``docs/scripts/viterbi_bench*.py``. +""" +from typing import List, NamedTuple + +import numpy as np + +# Same floor as torchaudio. -inf is safe because nothing in the loop subtracts; +# the only operations are comparisons, maxima and additions of finite values. +NEG = np.float32(-np.inf) + + +class TokenSpan(NamedTuple): + """One run of a non-blank label: ``[start, end)`` in frames, mean score.""" + token: int + start: int + end: int + score: float + + +def forced_align(log_probs, targets, blank=0): + """Viterbi alignment of ``targets`` to ``log_probs`` (``[T, vocab]``). + + Returns ``(path, scores)``: the label chosen at each of the ``T`` frames and + its log-probability there. Raises ``ValueError`` where torchaudio does -- + empty targets, a blank outside the vocabulary or inside the targets, or + fewer frames than ``len(targets) + repeats`` (a CTC path needs a blank + between two identical labels). The engine pre-checks the frame budget and + cannot produce the other cases; the raise is what keeps its + ``except -> warn -> spread`` path honest if one ever slips through. + + The DP runs in float32 whatever the input dtype -- measured, and worth a + third of the runtime -- so ``scores`` is float32. + """ + log_probs = np.ascontiguousarray(log_probs, dtype=np.float32) + targets = np.asarray(targets, dtype=np.int64) + T, L = log_probs.shape[0], len(targets) + N = 2 * L + 1 # blank-interleaved states + if L == 0: + raise ValueError("targets must not be empty") + if not 0 <= blank < log_probs.shape[1]: + raise ValueError(f"blank index {blank} is outside the vocabulary " + f"({log_probs.shape[1]} entries)") + repeats = adjacent_repeats(targets) + if T < L + repeats: + raise ValueError(f"targets length is too long for CTC: {L} tokens + " + f"{repeats} repeats need {L + repeats} frames, got {T}") + # A target state carrying the blank label would collapse away in + # merge_tokens: its word loses its span and every later word slides onto + # the wrong token -- a quietly wrong transcript instead of the caller's + # log-and-spread fallback. So reject it, as torchaudio does. + if np.any(targets == blank): + raise ValueError("targets should not contain the blank index " + f"({blank}); found {int(np.count_nonzero(targets == blank))}") + + # Advance-by-two penalty: 0 on token states whose preceding token differs + # (skipping the blank between them is legal), -inf everywhere else. Added + # to the shifted alpha it sinks the illegal lanes in one contiguous add; + # fancy indexing here was the single most expensive line of the loop. + pen = np.full(N, NEG, dtype=np.float32) + pen[(np.flatnonzero(targets[1:] != targets[:-1]) + 1) * 2 + 1] = 0.0 + + # Every buffer is allocated once; the loop allocates only the L-element + # gather of the token emissions (measured: cheaper than any out= form). The backtrace + # table is uint8 on purpose (T x N bytes -- 146 MB on a 300 s chunk; int64 + # would be 1.2 GB) and np.empty because every row t >= 1 is fully written. + alpha = np.full(N, NEG, dtype=np.float32) + nxt = np.full(N, NEG, dtype=np.float32) + two = np.full(N, NEG, dtype=np.float32) # two[:2] stays -inf + c1 = np.zeros(N, dtype=bool) # c1[0] stays False + c2 = np.zeros(N, dtype=bool) + c1v, c2v = c1.view(np.uint8), c2.view(np.uint8) + bt = np.empty((T, N), dtype=np.uint8) + bt[0] = 0 + + alpha[0] = log_probs[0, blank] + alpha[1] = log_probs[0, targets[0]] + + # torchaudio's tie order, per cell with predecessors x0 (stay), x1 (advance + # one), x2 (advance two): 2 only if strictly best, else 1 only if strictly + # better than 0, else 0. Its extra `x1 > x2` clause is the defect above + # and is deliberately not reproduced. + for t in range(1, T): + np.greater(alpha[:-1], alpha[1:], out=c1[1:]) # advance-one beats stay + np.maximum(alpha[1:], alpha[:-1], out=nxt[1:]) + nxt[0] = alpha[0] + np.add(alpha[:-2], pen[2:], out=two[2:]) # advance-two lane + np.greater(two, nxt, out=c2) # ... beats both + np.maximum(nxt, two, out=nxt) + row = bt[t] # 2 where the skip won, else c1 + np.add(c2v, c2v, out=row) # (masked writes scale with + np.maximum(row, c1v, out=row) # mask density; these do not) + lp = log_probs[t] + nxt[0::2] += lp[blank] # blank states: one scalar + nxt[1::2] += lp[targets] # token states: gather of L + alpha, nxt = nxt, alpha + + # torchaudio ends on the final blank only if it is strictly better. + s = N - 1 if alpha[N - 1] > alpha[N - 2] else N - 2 + path = np.empty(T, dtype=np.int64) + scores = np.empty(T, dtype=np.float32) + # Label per state: blank on the even states, the target on the odd ones. + # np.zeros here would be a silent bug for any blank index other than 0. + ext = np.full(N, blank, dtype=np.int64) + ext[1::2] = targets + for t in range(T - 1, -1, -1): # ~T scalar steps, <20 ms + tok = ext[s] + path[t] = tok + scores[t] = log_probs[t, tok] + s -= int(bt[t, s]) # int(): numpy would type `s -= uint8` as uint8 and overflow at N > 255 + return path, scores + + +def adjacent_repeats(targets) -> int: + """Pairs of identical neighbouring targets. Each needs one extra frame, since + a CTC path has to put a blank between two identical labels -- so a window + can only be aligned when ``frames >= len(targets) + adjacent_repeats``.""" + targets = np.asarray(targets) + return int(np.count_nonzero(targets[1:] == targets[:-1])) + + +def merge_tokens(path, scores, blank=0) -> List[TokenSpan]: + """Runs of equal non-blank labels as spans with an exclusive end. + + A token on frames 0, 1, 2 comes back as ``start=0, end=3``: ``end - start`` + is the duration and ``end / fps`` the time after the last frame. The engine + relies on that; do not add a ``+1``. + """ + path = np.asarray(path) + scores = np.asarray(scores) + if len(path) == 0: + return [] + cut = np.flatnonzero(path[1:] != path[:-1]) + 1 + starts = np.concatenate(([0], cut)) + ends = np.concatenate((cut, [len(path)])) + return [TokenSpan(int(path[s]), int(s), int(e), float(scores[s:e].mean())) + for s, e in zip(starts, ends) if path[s] != blank] diff --git a/noScribe/transcript_corrections.py b/noScribe/transcript_corrections.py new file mode 100644 index 00000000..0f1a2f9b --- /dev/null +++ b/noScribe/transcript_corrections.py @@ -0,0 +1,259 @@ +""" +User-editable word corrections for transcripts. + +Speech models (Voxtral in particular, which has no hotword support) reliably +mis-hear proper names — brands, products, programme names. This applies a +simple, predictable find/replace list the user maintains, e.g. turning +"Flor-Öle" into "VitaFlor" or "Sonvida" into "Sonvita". + +The list lives in the noScribe config directory as `voxtral_corrections.yml`. +Format (case-insensitive, whole-word matches): + + - to: VitaFlor + from: [vitaflor, "vita flor", "flor-öl", "flor-öle"] +""" + +import functools +import logging +import os +import re +import unicodedata + +logger = logging.getLogger(__name__) + +CORRECTIONS_FILENAME = "voxtral_corrections.yml" + +DEFAULT_CORRECTIONS = """\ +# noScribe - word corrections for the Voxtral engine +# +# Every "from" variant is replaced by the "to" value (case-insensitive, +# whole words/phrases only). Useful for brand, product and programme names +# the model keeps mis-hearing. One entry may list several spellings. +# +# Keep the variants specific. A short everyday phrase matches ordinary +# sentences too: "balance all" once ate the "Balance all dieser Faktoren". +# +# The file starts empty on purpose - corrections are personal to your +# material. Uncomment and adapt the examples to get started: +# +# - to: VitaFlor +# from: [vitaflor, "vita flor", "flor-öl", "flor-öle"] +# - to: Sonvita +# from: [sonvida, sonvieda, sonwita, "son vita"] +""" + + +def ensure_default_file(config_dir): + """Create the corrections file with a documented default if it is missing. + Returns the path to the file.""" + path = os.path.join(config_dir, CORRECTIONS_FILENAME) + if not os.path.exists(path): + try: + with open(path, "w", encoding="utf-8") as f: + f.write(DEFAULT_CORRECTIONS) + except Exception as e: + logger.warning("Could not create corrections file %s: %s", path, e) + return path + + +def load_corrections(path): + """Load the corrections file into a list of (compiled_regex, replacement).""" + if not path or not os.path.exists(path): + return [] + try: + import yaml + with open(path, encoding="utf-8") as f: + data = yaml.safe_load(f) or [] + except Exception as e: + logger.warning("Could not read corrections file %s: %s", path, e) + return [] + if not isinstance(data, list): + logger.warning("Corrections file %s must contain a list of entries.", path) + return [] + + rules = [] + for entry in data: + # Tell the user about a mistyped entry instead of silently skipping it — + # this file is hand-maintained and typos would otherwise look like the + # feature not working. + if not isinstance(entry, dict): + logger.warning("Ignoring invalid corrections entry (not a mapping): %r", entry) + continue + to = entry.get("to") + frm = entry.get("from") or [] + if isinstance(frm, str): + frm = [frm] + frm = [f for f in frm if f] + if not to or not frm: + logger.warning("Ignoring incomplete corrections entry (needs 'to' and 'from'): %r", entry) + continue + # Longest patterns first so "vita flor oil" wins over "vita flor". + pats = sorted((re.escape(str(f)) for f in frm), key=len, reverse=True) + rx = re.compile(r"(? "Lena", "Sena" -> "Sina", "Ähm" -> "Ann", Bavarian +# "Hamma" -> "Hanna"), so a mis-hearing that changes a sound ("Muna" for +# "Mona") is left to the user's correction list. +# +# Measured over 407k words of transcripts (Whisper and Voxtral output of +# CallHome German, English and Spanish and of AMI, plus private German +# recordings: a podcast, an interview, a video call), each checked against +# 70-150 common first names of its +# language and the names of its own speakers: one change, and a right one -- +# a speaker's name the model had spelled with i for y. The dictionary kept +# the only other candidate ("Monica" beside "Monika") twice. +_SAME_SOUND = ( + (re.compile(r"ph"), "f"), + (re.compile(r"ck"), "k"), + (re.compile(r"c(?=[aoulr]|$)"), "k"), # Marcus / Markus + (re.compile(r"dt$"), "t"), # Schmidt / Schmit + (re.compile(r"(?<=.)y$"), "i"), # Steffy / Steffi; inside a German + # name y is often ü (Sybille) + # an h after a vowel and before none: Mohna / Mona, Noah / Noa; not Johanna + (re.compile(r"(?<=[aeiouäöü])h(?![aeiouäöü])"), ""), +) +# Doubled letters are deliberately not among them: a doubled vowel is another +# sound in Dutch, Finnish and English ("Joon" is not "John"), and a doubled +# consonant shortens the vowel before it in German and Italian, so "Ela" and +# "Ella", "Mila" and "Milla" are different names that sound different. + + +def _spelling_key(word): + """The part of a spelling that can be heard: two words with the same key + sound alike to anyone who does not know how either is written. Accents + count ("Ole" is not "Öle"), and so does every vowel.""" + w = unicodedata.normalize("NFC", word).lower() + for pattern, same in _SAME_SOUND: + w = pattern.sub(same, w) + return w + + +@functools.lru_cache(maxsize=None) +def _spell_checker(): + """macOS's own spell checker, or None. + + Voxtral runs only on Apple Silicon, and pyobjc is already a dependency + there (environments/requirements_macOS_arm64.txt). It works in the spawned + worker without a running app. + """ + try: + from AppKit import NSSpellChecker + return NSSpellChecker.sharedSpellChecker() + except Exception as e: + logger.debug("No spell checker: %s", e) + return None + + +@functools.lru_cache(maxsize=None) +def _dictionary_language(language): + """The spell checker's name for a language code ("pt" -> "pt_BR"), or None + when it has no dictionary for it. Asked about a language it lacks, the + checker calls every word correct, so this must be settled up front.""" + checker = _spell_checker() + code = str(language or "").strip().lower()[:2] + if checker is None or len(code) != 2: + return None + try: + available = [str(lang) for lang in checker.availableLanguages()] + except Exception as e: # a correction is never worth a failed job + logger.debug("No dictionary list: %s", e) + return None + if code in available: + return code + return next((lang for lang in available if lang.startswith(code + "_")), None) + + +def _is_word(word, language): + """Whether the dictionary of `language` (see _dictionary_language) knows + `word`. Anything short of a clear "unknown" for the whole word counts as + known: that leaves the name as the model wrote it, never a real word lost. + + Every way a lookup goes wrong ends up there. Under heavy load the spell + server can stall ("NSSpellServer findMisspelledWordInString timed out" + on stderr), and measured by pausing it, a stalled lookup reports no + misspelling after ~1 s. A dictionary's first lookups while it loads, and + every lookup in the 15 listed dictionaries that accept any word at all + (uk, bg, el, he, hi, id, is, ga, ko, lt, nb, nn, pa, sl, te on macOS 27), + answer the same way. Answers are therefore not cached across calls, so a + stall costs at most one chunk's corrections; an ordinary lookup takes + 1-15 ms, and only the rare word that sounds like a name is asked. + """ + try: + miss, _count = _spell_checker().checkSpellingOfString_startingAt_language_wrap_inSpellDocumentWithTag_wordCount_( + word, 0, language, False, 0, None) + except Exception as e: + logger.debug("Spell check failed: %s", e) # not the word: no transcript text in logs + return True + return not (miss.location == 0 and miss.length == len(word)) + + +def apply_name_corrections(text, names, languages=None): + """Set mis-heard spellings of the speakers' names to the one the user gave. + + A speech model hears a name it does not know and writes one of the ways + it could be spelled ("Mohna" for "Mona", "Steffy" for "Steffi"). Where the + user has told us the names (the speaker-names field), that can be fixed. + + A capitalised word is replaced only when it passes both guards: + * it is spelled differently from exactly one of the names but sounds the + same (`_spelling_key`), + * and no dictionary of `languages` -- one code or several, e.g. the + language the text reads as and the one the file is in -- knows it. + The dictionary keeps real words that happen to be spelled like a name + (every German noun is capitalised), and it knows common names, so + "Marcus" stays "Marcus" next to a speaker called Markus: a real name may + be someone else. Asking every language the text may be in keeps a word of + one language out of the other's gaps, such as an English quote in a German + passage. The measurement is above _SAME_SOUND. + + Without a dictionary (not macOS, Auto before the language is known, or a + language the system has none for) nothing is changed -- and nothing is + either with one of the dictionaries that accept every word (see _is_word). + """ + if isinstance(languages, str) or languages is None: + languages = [languages] + dictionaries = list(dict.fromkeys( + d for d in (_dictionary_language(code) for code in languages) if d)) + if not dictionaries: + return text + # "Mona Muster" or "Anna-Lena" as a speaker name: each capitalised part is + # a name of its own ("del" in "Ana del Río" is not). + parts = {p for n in (names or []) + for p in re.findall(r"[^\W\d_]{3,}", unicodedata.normalize("NFC", str(n))) + if p[0].isupper()} + keys = {} + for p in parts: + keys.setdefault(_spelling_key(p), set()).add(p) + lowered = {p.lower() for p in parts} + verdicts = {} # one lookup per word and chunk (see _is_word) + + def repl(m): + word = m.group(0) + if not word[0].isupper() or word.lower() in lowered: + return word + key = _spelling_key(word) + alike = keys.get(key, ()) + if len(alike) != 1: + return word + if word not in verdicts: + verdicts[word] = any(_is_word(word, d) for d in dictionaries) + return word if verdicts[word] else next(iter(alike)) + + # Not the stem of a contraction or possessive ("Weren't", "Mohna's"). + return re.sub(r"(? +""" + +import functools +import importlib.resources as impres +import logging +import math +import os +import re +import time + +logger = logging.getLogger(__name__) + +SAMPLE_RATE = 16000 + +# Rough count of text tokens Voxtral generates per second of audio, used ONLY to +# drive the intra-pass liveness estimate. Measured German conversation runs at +# ~4.64 text tokens/s (see _transcribe_guarded); this is deliberately set below +# that, so the estimate runs AHEAD of the real position (by ~1.5x) and reaches +# its cap at ~60% of the pass. It is capped at 95% of the pass and snaps to the +# true position when a pass finishes, so running ahead only makes the bar wait +# at the cap for a while, never pass the real chunk boundary. +_EST_TOKENS_PER_SEC = 3.0 + + +def is_available(): + """True if the Voxtral backend and its dependencies can be imported. + + Deliberately NOT cached: _register_voxtral_models re-checks this on every + dropdown open so a backend pip-installed while the app runs appears without a + restart. The check is three cheap importlib.util.find_spec lookups (no heavy + import), so re-running it per dropdown open is negligible.""" + import importlib.util + return all(importlib.util.find_spec(m) is not None + for m in ("mlx_voxtral", "transformers", "soundfile")) + + +def _log(cb, level, msg): + if cb: + try: + cb(level, msg) + except Exception: + pass + else: + levels = {"error": logging.ERROR, "warn": logging.WARNING, + "info": logging.INFO} + logger.log(levels.get(level, logging.DEBUG), msg) + + +# --- Long-audio chunking (reference approach: one pass up to the model's +# context, split only beyond that) ------------------------------------------- +# Voxtral uses flash attention + a rolling KV cache, so a single generate() +# call's memory grows ~linearly with the audio length (NOT O(T^2)). We feed the +# whole file in one pass when it fits the memory budget, and only split longer +# files. The per-pass length is chosen from the machine's RAM by +# `_auto_chunk_sec()` -- see MEM_MODEL below -- unless the caller/config pins it. +PREF_MIN_CHUNK_SEC = 180 # preferred quality floor (below this we warn) +HARD_MIN_CHUNK_SEC = 60 # absolute floor; memory safety wins over this only with a warning +MAX_CHUNK_SEC = 1500 # 25 min: safely under Voxtral's ~30 min / 32k ctx +# 10 min: the longest pass Voxtral has been *measured* on, which is a different +# fact from what fits its context and so a different constant. The 30/40-minute +# figure everyone quotes is a capacity calculation -- 12.5 Hz frame rate against +# 32k tokens -- not a quality result. The paper's own long-form ASR protocol is +# shorter: "we take the one-hour long earnings calls from Earnings-21 and +# Earnings-22, and segment them into shorter, 10 minute variants" +# (arXiv:2507.13264). Beyond that there is no published measurement, and past it +# this project has recorded two distinct failures -- whole chunks coming back +# translated (windows around 1425 s) and passes returning without their opening +# (1195 s and 1226 s of a 1226 s file, while 15 shorter points came back clean). +# Neither is a threshold effect that a slightly shorter window would dodge, so +# this is not a fix for either; it is a refusal to run the model twice as far +# out as anyone has measured it. Raise it when a longer window has been measured. +# The cap costs no quality: a 600 s single pass and two 300 s passes punctuate +# the same material equally (10.87 against 10.66 commas per 100 words), and +# chunk length has an optimum set by training rather than a "longer is better" +# curve. What does cost quality is the *boundary*, which pause-aligned cuts and +# the overlap already address. +TRUSTED_CHUNK_SEC = 600 +# The binding limit is the Voxtral *generate* working set: it must fit in +# physical RAM, because MLX compute on swapped-out buffers thrashes and never +# finishes. The one-off load transient (weights dict + model briefly duplicated) +# is NOT counted here -- it is allowed to swap since it frees before generate +# starts. So this reserve only holds back non-swappable kernel/wired memory, the +# forced aligner that runs alongside, and a margin; the OS/GUI may page out. +# The generate pass may use (total_RAM - this). +# +# Measured on a 32 GB M1 Max: a 21.8 GB pass ran with 2.7 GB still free, and a +# 26.4 GB pass completed as well, so 7 GB is defensible while leaving room for +# the aligner. Users who free up the machine can go further via the config key +# `voxtral_ram_reserve_gb`, which buys noticeably longer passes for the memory +# hungry builds (6-bit small: 229 s at 8 GB, 304 s at 7, 378 s at 6). +RAM_RESERVE_GB = 7 +# Below this much free memory a run does not just get slow, it stops progressing: +# the forced aligner (~2 GB) and the OS still need room next to the model, and +# once MLX has to compute on swapped-out buffers nothing finishes. Used to refuse +# a model outright instead of starting a run that cannot succeed. +# Calibrated on a 32 GB machine: passes peaking at 25 GB (7 GB headroom) ran +# fine, while the 8-bit 24B build at 27.2 GB (4.8 GB headroom) exhausted the swap +# and stopped progressing once the aligner loaded next to it. +MIN_HEADROOM_GB = 6 +# Measured peak unified-memory model per pass: peak_GB ~= fixed + slope*seconds. +# mini : anchored on a real measurement (600 s single pass = 17.4 GB peak on +# an M1 Max; the full 1143 s pass exhausted 32 GB -> unsafe). +# small: the shipped 24B build, voxtral-small-4bit (4-bit LM + lm_head, bf16 +# encoder, 15 GB of weights). Calibrated on the generate_step path: +# fresh-process full-pass peaks on an M1 Max were 16.65 / 17.25 / 18.53 +# / 19.99 / 21.36 GB at 180 / 300 / 600 / 900 / 1200 s of a podcast and +# 17.35 / 18.99 / 22.54 GB at 300 / 600 / 1200 s of an interview, a line +# peak ~= 15.71 + 0.0051*s. The entry over-predicts every point by +# 7-17% (7-12% up to the 600 s cap passes can reach), a thinner +# cushion than mini8's because most of this peak is weights, which do +# not vary. On 32 GB the 600 s cap binds; on 24 GB it allows short +# passes (~70 s auto, up to ~210 s pinned). +# mini8 : the 3B weights at 8 bit with the audio encoder left in bf16 +# (tools/quantize_voxtral.py, mode dense-encoder). RECALIBRATED for the +# generate_step decode path (which chunks the prompt): fresh-process +# full-pass peaks on an M1 Max were 6.82 / 7.41 / 8.72 / 10.00 / 11.18 GB +# at 180 / 300 / 600 / 900 / 1200 s (two podcast files, ~1.6% apart), +# a clean line peak ~= 6.11 + 0.00427*s. The old one-shot-prefill path +# measured ~2.5x that slope (120 s = 7.7, 1100 s = 18.7 GB); switching to +# generate_step genuinely lowered it. The entry below keeps a safety +# margin over the fit (6.5 + 0.0060*s over-predicts every point by +# 11-23%): the slope is dominated by the audio prompt-token rate (~12.5 +# tok/s, fixed by the recording), so speech density moves it only ~6%, +# but machine/version variance warrants the cushion. From 24 GB up, +# mini8 is capped by TRUSTED_CHUNK_SEC anyway, so the recalibration now +# only binds at 16 GB. small6/small8 below are still on old-path +# numbers (local-only builds, not re-measured) -- safe, just +# conservative. +# Why the encoder stays dense: measured against a hand-corrected +# German reference, quantising it costs real accuracy while costing +# nothing to keep -- the encoder runs ONCE per pass, so its precision +# does not affect speed (6.60x vs 6.68x for the fully-quantised +# build), and this build reproduces the bf16 transcript word for word +# at 4.5x the speed. lm_head, by contrast, runs once per generated +# token: leaving it dense costs 27% throughput and measured no better, +# so it stays quantised. +# small8: the 8-bit build of the same layout, shipped until voxtral-small-4bit +# replaced it so the 24B model fits 32 GB (a 6-bit body measured no +# better than 4; 8 bit itself is unmeasured). Now only for a build of +# your own passed as transcribe(voxtral_repo=...); the model menu offers +# VOXTRAL_MODELS alone, and main.RENAMED_MODELS points the old name at +# voxtral-small-4bit. Needs +# ~34 GB minimum, so it is refused below that and wants 48 GB+. Even +# when it fits it runs at ~0.80x realtime -- slower than the recording. +# On a 32 GB machine the 25 GB of weights cannot stay resident: a +# benchmark swapped continuously and did not finish a 25 min pass in +# 4.5 h. This entry exists so auto-sizing refuses it up front there. +# small6: the same 24B weights quantised to 6 bit instead of 4 (converted +# locally from the original mistralai release). Measured 150 s = +# 22.9 GB and 410 s = 26.4 GB, i.e. ~6 GB more fixed than the 4-bit +# build, which is why it only allows much shorter passes on 32 GB. +MEM_MODEL = { + "mini": {"fixed": 7.9, "slope": 0.016}, + "mini8": {"fixed": 6.5, "slope": 0.0060}, # generate_step path; see note above + "small": {"fixed": 16.5, "slope": 0.0070}, # generate_step path; see note above + "small6": {"fixed": 20.9, "slope": 0.0135}, + "small8": {"fixed": 27.4, "slope": 0.0135}, +} + +# Window size for the wav2vec2 alignment forward pass. Kept small so the +# O(T^2) self-attention memory of the *aligner* stays bounded regardless of +# chunk length (this is the aligner, not Voxtral). 20 s is a judgement call, not +# a measured optimum: wav2vec2 is trained on utterances of that order, and the +# window only has to be long enough that a word never straddles two of them. +EMISSION_WINDOW_SEC = 20 +# Hard cap on the forced-alignment DP size, frames * (2*tokens + 1). The DP +# (noScribe/ctc_align.py) keeps one byte per cell for its backtrace, so 2**30 +# cells is a 1 GB table and ~5 s for one call -- and halving a window halves +# both axes of that table, so splitting is cheaper than not splitting +# (measured: 587 -> 200 ms on a 300 s chunk in four pieces). The constant began +# as a guard against torchaudio's 32-bit index overflow; that history and the +# figures are in docs/viterbi-numpy-brief.md, the splitting itself is pinned +# by tests/test_forced_align_cap.py. +FORCED_ALIGN_MAX_CELLS = 2**30 +# `_Aligner.align_prefix` is free to choose its own piece sizes, so it uses a +# smaller budget than the hard cap: measured with the C++ kernel, one call +# peaks at ~1.0 GB and 3.9 s at 2**30 cells but ~0.3 GB and 0.9 s at 2**28 +# (the numpy DP: same memory, ~1.35x the time). The prefix +# salvage runs right after a Voxtral pass, i.e. exactly when memory is +# tightest, and more (smaller) pieces cost no accuracy and slightly less total +# DP work. 2**29 keeps a 1500 s window at ~7 pieces of ~220 s of speech each. +SALVAGE_ALIGN_MAX_CELLS = 2**29 +# When splitting a long file, snap each cut to the clear speaker pause nearest +# the target boundary within a *wide* radius of it (the longest quiet run only +# when there is no clear pause; see _chunk_boundaries). Because the passes are +# long we have plenty of slack to hunt far for a real pause, so a cut lands +# between utterances and never mid-word. 90 s is a judgement call: wide enough +# that a 10-minute pass always finds a real pause, narrow enough that consecutive +# passes stay roughly equal in length. +SILENCE_SEARCH_SEC = 90 +# A frame counts as "silence" when its AMPLITUDE is at or below this fraction of +# the window's typical (75th-percentile) speech level -- about -16.5 dB. Callers +# compare in the power domain and must square it: _frame_energy returns mean +# squared amplitude, and applying the fraction unsquared puts the gate at +# -8.2 dB, which is quiet speech rather than a pause. +QUIET_LEVEL = 0.15 +# Lead-in overlap (seconds) read from the previous chunk so the words right +# after a boundary have preceding audio context; the duplicated overlap is then +# dropped by timestamp. Combined with pause-aware cuts, seams are ~lossless. +# 15 s is a judgement call: a few sentences of context, short against a pass. +OVERLAP_SEC = 15 + +# Known model repositories (MLX). +VOXTRAL_MODELS = { + # Two builds, both quantised on Apple Silicon and published so they download + # on first use (the picker only shows them on arm64 Macs; elsewhere MLX + # cannot run at all). Each keeps the audio encoder in bf16, because the + # encoder runs once per pass, so its precision is nearly free, and + # compressing it measurably costs accuracy on hard audio; the language + # model and lm_head are quantised (docs/voxtral-quantisation.md). + # + # mini (3B): the everyday build, 8 bit. Reproduces the bf16 transcript word + # for word on our hard-German reference at ~4.5x the speed, runs on any Mac + # with 16 GB, and is the one to use for conversational audio. + "voxtral-mini-8bit": "MarkusKaemmerer/Voxtral-Mini-3B-2507-8bit-dense-encoder", + # small (24B): 4 bit, for clean, read-aloud audio and material heavy with + # names (FLEURS de 2.78 % WER against mini's 4.89 %), ~2x realtime, fits a + # 32 GB Mac. It replaced an 8-bit build of the same layout that needs + # ~34 GB; a 6-bit body measured no better than 4, 8 bit is unmeasured + # (docs/voxtral-quantisation.md). See MEM_MODEL / min_ram_gb. + "voxtral-small-4bit": "MarkusKaemmerer/Voxtral-Small-24B-2507-4bit-dense-encoder", +} + +# The raw unquantised source releases: handing one of these to transcribe() +# would download tens of GB (24B: 48 GB) and, because the basename carries no +# bit width, _model_kind would meter it with a quantised profile and wave a +# too-long pass through. No model here points at them, but a direct caller might +# copy one in, so transcribe() refuses them (see below). +SOURCE_REPOS = frozenset({ + "mistralai/Voxtral-Mini-3B-2507", "mistralai/Voxtral-Small-24B-2507", +}) +# The same releases as bare directory names. A copy under the package `models/` +# dir (the documented place for local builds, see _local_copy) resolves to a +# filesystem path, so matching the full repo id alone let exactly the build this +# guard exists to stop walk straight through it. +SOURCE_REPO_NAMES = frozenset(r.rsplit("/", 1)[-1].lower() for r in SOURCE_REPOS) + + +def _local_copy(name): + """Path of a converted build under the package `models/` dir, or None. + + The one place that decides "this build exists locally": resolve_model() + prefers it and has_local_build() gates the picker on it, and both must + agree on the same filesystem fact. + """ + try: + local = impres.files("models") / str(name) + if (local / "config.json").is_file(): + return str(local) + except Exception: + pass + return None + + +def has_local_build(name): + """True if `name` is usable on this machine. Both shipped builds download on + first use, so this is always true for known models; kept as the picker's + gate in case a future entry is local-only again.""" + return name in VOXTRAL_MODELS or _local_copy(name) is not None + +# Forced-alignment (word timestamps) is language dependent. Use a wav2vec2 CTC +# model that matches the transcription language when we have one (best accuracy, +# native alphabet incl. umlauts/accents), and fall back to a multilingual MMS +# aligner for "auto"/"multilingual" or unmapped languages (handles code-switched +# audio like German+English; characters outside its vocabulary are skipped and +# their timing interpolated). +# +# The language-specific table is not redundant with the multilingual model, which +# is the obvious simplification to reach for. Measured 2026-09-02: the MMS +# forced-aligner's vocabulary is 27 characters, a-z plus apostrophe, with NO +# umlauts at all -- it is romanised by design. The German model has a, o and u +# umlauts. Folding German onto MMS would therefore drop a character from every +# word containing one and interpolate its timing. +# +# Neither vocabulary contains the German sharp s. On the hand-corrected references +# that is 13 of 1268 words (1.03 %) and 13 of 6064 letters (0.21 %), and each such +# word still has every other letter aligned, so the word boundary barely moves. +# Not worth chasing a different checkpoint over -- but it is why "native alphabet" +# above means "nearly all of it", not "all". +ALIGN_MODELS = { + "de": "jonatasgrosman/wav2vec2-large-xlsr-53-german", + "en": "jonatasgrosman/wav2vec2-large-xlsr-53-english", + "fr": "jonatasgrosman/wav2vec2-large-xlsr-53-french", + "es": "jonatasgrosman/wav2vec2-large-xlsr-53-spanish", + "it": "jonatasgrosman/wav2vec2-large-xlsr-53-italian", + "nl": "jonatasgrosman/wav2vec2-large-xlsr-53-dutch", + "pt": "jonatasgrosman/wav2vec2-large-xlsr-53-portuguese", + "ru": "jonatasgrosman/wav2vec2-large-xlsr-53-russian", + "pl": "jonatasgrosman/wav2vec2-large-xlsr-53-polish", + "ar": "jonatasgrosman/wav2vec2-large-xlsr-53-arabic", + "fi": "jonatasgrosman/wav2vec2-large-xlsr-53-finnish", + "el": "jonatasgrosman/wav2vec2-large-xlsr-53-greek", + "hu": "jonatasgrosman/wav2vec2-large-xlsr-53-hungarian", + "ja": "jonatasgrosman/wav2vec2-large-xlsr-53-japanese", + "zh": "jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh-cn", +} +ALIGN_MODEL_MULTILINGUAL = "MahmoudAshraf/mms-300m-1130-forced-aligner" + +# Letters an aligner vocabulary may not carry, spelled the way the models were +# actually trained. Applied ONLY where the letter is missing from that model's +# vocabulary, so a model that does carry it keeps the real one -- the German +# model has the umlauts and must not be handed "ae" for "ä". +# +# Measured 2026-09-02 on the 300 s reference. Both mappings are what the models +# themselves produce when decoded freely, which is the only evidence that settles +# the spelling: +# +# sharp s -> "ss". The German aligner writes "weiss" and "heisst" of its own +# accord, so its training text was normalised that way. Dropping the letter +# instead left a word-final /s/ unaccounted for and ended the word too early: +# the three such words moved +80, +100 and +160 ms later, and all 841 other +# words in the chunk moved by exactly 0 ms. Word-internal occurrences do not +# move, because the boundary comes from the letters around them -- confirmed on +# the second reference (hart_780-900), whose four occurrences are all internal +# and where nothing moved at all, in any of its 418 words. Across both +# references word-final is 3 of 1268 words, so this is a small and very local +# correction, not a broad one. +# +# umlauts -> "ae"/"oe"/"ue", not "a"/"o"/"u". The multilingual MMS aligner +# carries a-z and an apostrophe only, and is romanised in the German manner +# rather than stripped -- it writes "naechste" for "nächste". With this table +# its mean frame score on the same audio improves from -0.278 to -0.256 while +# aligning 86 MORE tokens, which biases the mean the other way. +# German only, and deliberately so. Extending the table looks free -- an entry +# can only fire where the letter is missing anyway -- but the romanisation it +# encodes is language-specific: "ae" is the German rule, while uroman, which the +# multilingual model follows, gives plain "a" for Swedish. Languages without +# their own entry in ALIGN_MODELS fall back to that same multilingual model, so +# a table with no notion of language would apply the German rule to Swedish, +# Czech and Turkish alike. Those characters are no longer lost either way: since +# align_words gained its wildcard column they are placed on the audio rather +# than dropped, which is less precise than a correct respelling and much safer +# than a wrong one. Add a language here only after measuring it, the way these +# two were measured -- not on the reasoning that it cannot hurt. +_ALIGN_CHAR_FALLBACKS = {"ä": "ae", "ö": "oe", "ü": "ue", "ß": "ss"} + + +def min_ram_gb(repo): + """Smallest amount of RAM in which this model can realistically run. + + Shown next to each model in the picker, so the choice is made before a run + starts rather than after the machine has begun to swap. + """ + m = MEM_MODEL[_model_kind(repo)] + return m["fixed"] + m["slope"] * HARD_MIN_CHUNK_SEC + MIN_HEADROOM_GB + + +def max_safe_chunk_sec(repo): + """Longest pass (seconds) whose estimated generate peak still fits physical + RAM, i.e. the hard ceiling past which a run stops progressing (see + MIN_HEADROOM_GB). This is THE memory-safety invariant: the picker hint + (min_ram_gb), the automatic sizing and the pinned-value clamp must all + agree with it, so it lives in exactly one place. + + Raises MemoryError when even the shortest pass (HARD_MIN_CHUNK_SEC) cannot + fit -- proceeding would not fail, it would swap forever. + """ + kind = _model_kind(repo) + m = MEM_MODEL[kind] + total = _total_ram_gb() + need = min_ram_gb(repo) + if total < need: + # The whole requirement, headroom included: the peak alone ("17 GB") + # read as if it fit an 18 GB machine that was just refused. + raise MemoryError( + f"Voxtral {kind} needs about {need:.0f} GB even for its shortest " + f"chunk, which does not fit in {total:.0f} GB. Pick a smaller model " + f"(see the memory hint next to each model) or use a machine with more RAM." + ) + if not m["slope"]: + return MAX_CHUNK_SEC + return (total - MIN_HEADROOM_GB - m["fixed"]) / m["slope"] + + +def resolve_align_model(language): + """Pick the alignment model for a language code (e.g. "de"); fall back to the + multilingual aligner for None/"auto"/"multilingual"/unmapped languages.""" + code = (language or "").strip().lower()[:2] + return ALIGN_MODELS.get(code, ALIGN_MODEL_MULTILINGUAL) + + +def _model_kind(repo): + """Classify a repo id / local path into a MEM_MODEL entry. + + Only the final path component (model dir / repo name) is matched, so a + parent directory that happens to contain "small" cannot misclassify the + model and shrink its passes for no reason. The bit width matters as much as + the parameter count: a 6-bit 24B build needs ~6 GB more than the 4-bit one, + and mistaking one for the other would size passes too long and exhaust RAM. + + A name with no recognisable size token falls back to the most conservative + (highest-RAM) profile, never the cheap `mini` one, so an unrecognised build + is sized safely-short rather than metered too generously and swapped. + """ + name = os.path.basename(os.path.normpath(str(repo))).lower() + + def _has(*tokens): + return any(t in name for t in tokens) + + eight = _has("8bit", "8-bit") + # The shipped builds keep the audio encoder in bf16 ("dense-encoder"); the + # encoder is small, so this adds well under a GB and the bit-width profile + # still fits (mini dense-encoder measured 6.35 GB fixed vs 6.0 for uniform + # 8-bit -- within the mini8 entry). Classify by parameter count and bit + # width; the MEM_MODEL entries already carry a safety margin. + if "small" in name or "24b" in name: + if eight: + return "small8" + if _has("6bit", "6-bit"): + return "small6" + if _has("4bit", "4-bit"): + return "small" + # A 24B build whose name carries no bit width at all. `small` is the + # 4-bit profile and the CHEAPEST of the three 24B entries, so metering an + # unquantised or 6-bit copy with it waves a several-hundred-second pass + # through for weights that need 25-48 GB, and the run swaps forever + # instead of being refused. Fall back to the hungriest 24B profile, the + # same direction this function's docstring promises for a name with no + # size token at all. (The mini branch below needs no such guard: without + # a bit width it already lands on `mini`, the bf16 and more expensive of + # the two mini entries.) + logger.warning( + "Voxtral build %r is a 24B model with no bit width in its name; " + "sizing passes with the conservative small8 profile. Name a local " + "build with its bit width (e.g. voxtral-small-8bit) to have it " + "sized correctly.", name) + return "small8" + if "mini" in name or "3b" in name: + return "mini8" if eight else "mini" + # Unrecognised build: the name carries no reliable size signal. Under-sizing + # a pass only runs slower, but over-sizing swaps forever -- so fall back to + # the most memory-hungry profile rather than optimistically metering an + # unknown model as the cheap `mini` one (which would wave a too-long pass + # through on a large model). The old code defaulted anything without "small" + # to mini; that is the direction that can swap. + logger.warning( + "Unrecognised Voxtral build %r; sizing passes with the conservative " + "small8 profile. Name a local build with mini/small and its bit width " + "(e.g. voxtral-mini-8bit) to have it sized correctly.", name) + return "small8" + + +@functools.lru_cache(maxsize=None) +def _total_ram_gb(): + """Total physical RAM in GB (sysctl on macOS; psutil fallback; else 16). + + Cached: total RAM is a process constant, but this is queried once per model + on every model-dropdown open (via App.model_label) and again during a job, + so without the cache each call would spawn a `sysctl` subprocess for a value + that never changes.""" + try: + import subprocess + return int(subprocess.check_output(["sysctl", "-n", "hw.memsize"]).strip()) / 1024**3 + except Exception: + try: + import psutil + return psutil.virtual_memory().total / 1024**3 + except Exception: + return 16.0 # conservative default + + +def _auto_chunk_sec(repo, log_cb=None, ram_reserve_gb=None): + """Choose the per-pass length (seconds) from the machine's RAM. + + Voxtral feeds the whole pass into one generate() call, whose peak + unified-memory is ~`fixed + slope*seconds` (MEM_MODEL). We pick the longest + pass whose estimated peak still fits `(total_RAM - RAM_RESERVE_GB)`, clamped + to [HARD_MIN_CHUNK_SEC, TRUSTED_CHUNK_SEC] and to what `max_safe_chunk_sec` + allows. So most files go through in a single pass on a roomy + machine, while low-RAM machines (or the memory-hungry 24B `small` model) + automatically get shorter, still-safe passes -- and a warning when RAM is the + limiting factor. + """ + kind = _model_kind(repo) + m = MEM_MODEL[kind] + total = _total_ram_gb() + # Refuses outright when even the shortest pass cannot fit: proceeding would + # not merely run slowly -- the working set no longer fits, MLX computes on + # swapped-out buffers and the run stops making progress at all (observed + # with the 8-bit 24B build, which sat at 25 GB while the aligner pushed the + # machine into swap). + ceiling = max_safe_chunk_sec(repo) + # `is None`, not falsiness: an explicit 0 means "hold nothing back" and must + # not quietly turn into the 7 GB default (the GUI already maps its 0 to None). + reserve = RAM_RESERVE_GB if ram_reserve_gb is None else float(ram_reserve_gb) + budget = total - reserve + raw = (budget - m["fixed"]) / m["slope"] if m["slope"] else MAX_CHUNK_SEC + # A reserve below MIN_HEADROOM_GB must not turn into a refusal (the model + # fits -- the *reserve* is what doesn't); it just can't buy passes beyond + # the hard ceiling. + chunk = int(max(HARD_MIN_CHUNK_SEC, + min(TRUSTED_CHUNK_SEC, MAX_CHUNK_SEC, raw, ceiling))) + est_peak = m["fixed"] + m["slope"] * chunk + if raw < HARD_MIN_CHUNK_SEC: + _log(log_cb, "warn", + f"Low RAM for Voxtral {kind} ({total:.0f} GB total): chunks forced " + f"to {chunk}s (~{est_peak:.0f} GB peak) and may still swap. Consider " + f"the mini model or a machine with more RAM.") + elif raw < PREF_MIN_CHUNK_SEC: + _log(log_cb, "info", + f"Voxtral {kind}: {total:.0f} GB RAM -> short {chunk}s chunks " + f"(~{est_peak:.0f} GB peak). More RAM allows longer, higher-context chunks.") + else: + _log(log_cb, "info", + f"Voxtral {kind}: {total:.0f} GB RAM -> ~{chunk // 60}m{chunk % 60:02d}s " + f"chunks (~{est_peak:.0f} GB peak).") + return chunk + + +# --------------------------------------------------------------------------- # +# Text-based language detection for the aligner choice +# --------------------------------------------------------------------------- # +# Voxtral auto-detects the spoken language internally but never reports it -- +# yet the *aligner* choice decides timestamp quality: a char-native +# per-language model clearly beats the romanised multilingual fallback +# (measured German word boundaries @50ms: ~75% for romanised MMS-CTC, +# arXiv 2606.10675). The transcribed text of a chunk exists BEFORE that chunk +# is aligned, so the language can be read off the text itself: function words +# are so frequent that a handful per language decides within a few hundred +# words. Mixed speech (e.g. German with English phrases) keeps the majority +# language's function words dominant, so the majority model wins -- and it +# aligns the minority-language words fine too (same Latin alphabet, and the +# CTC emissions only anchor characters). Only when no language dominates does +# the romanised multilingual model take over. +_STOPWORDS = { + "de": {"der", "die", "das", "und", "ich", "nicht", "ist", "wir", "ein", + "eine", "mit", "auf", "für", "aber", "auch", "dann", "wenn", + "noch", "dass", "sind", "habe", "schon", "mal", "jetzt"}, + "en": {"the", "and", "you", "that", "this", "not", "with", "for", "have", + "are", "was", "but", "they", "what", "just", "like", "know", + "then", "would", "been", "your", "about"}, + "es": {"que", "los", "del", "las", "por", "con", "una", "para", "está", + "pero", "como", "más", "muy", "bien", "eso", "esta", "todo", + "porque", "cuando", "también"}, + "fr": {"les", "des", "est", "que", "qui", "pas", "vous", "nous", "une", + "dans", "pour", "mais", "avec", "sur", "c'est", "plus", "tout", + "être", "fait", "comme"}, + "it": {"che", "per", "con", "una", "sono", "non", "come", "anche", + "questo", "della", "più", "perché", "quindi", "cosa", "molto", + "quando", "questa", "hanno", "fare", "essere"}, + "nl": {"het", "een", "van", "dat", "niet", "voor", "maar", "zijn", "ook", + "dan", "dus", "nog", "naar", "wel", "hebben", "deze", "worden", + "heeft", "kunnen", "moet"}, + "pt": {"que", "não", "uma", "para", "com", "mais", "como", "mas", "por", + "isso", "você", "muito", "então", "tem", "está", "também", "vamos", + "fazer", "quando", "porque"}, + "pl": {"nie", "jest", "się", "tak", "ale", "czy", "jak", "dla", "tego", + "być", "przez", "tylko", "bardzo", "może", "już", "gdzie", + "wszystko", "jeszcze", "przecież", "trzeba"}, +} +# Non-Latin scripts identify their language directly (no stopwords needed). +_SCRIPT_RANGES = ( + ("ja", "぀", "ヿ"), # hiragana + katakana (checked before han) + ("zh", "一", "鿿"), # han + ("ru", "Ѐ", "ӿ"), # cyrillic + ("ar", "؀", "ۿ"), # arabic + ("el", "Ͱ", "Ͽ"), # greek +) + + +# What _detect_language can answer; any other language it can only misname. +_DETECTABLE = frozenset(_STOPWORDS) | {code for code, _, _ in _SCRIPT_RANGES} + + +def _detect_language(text): + """Best-effort language of `text` -> (code, dominance) or (None, 0.0). + + Script check first (CJK/Cyrillic/Arabic/Greek are unambiguous), then + function-word counting for the Latin-script languages. Returns None when + the evidence is thin or no language dominates clearly -- the caller then + keeps its previous choice or the multilingual fallback. + """ + if not text: + return None, 0.0 + letters = [c for c in text if c.isalpha()] + if len(letters) < 40: + return None, 0.0 + for code, lo, hi in _SCRIPT_RANGES: + n = sum(1 for c in letters if lo <= c <= hi) + if n / len(letters) > 0.3: + return code, n / len(letters) + tokens = re.findall(r"[^\W\d_]+(?:'[^\W\d_]+)?", text.lower()) + if len(tokens) < 40: + return None, 0.0 + hits = {code: sum(1 for t in tokens if t in words) + for code, words in _STOPWORDS.items()} + ranked = sorted(hits.items(), key=lambda kv: kv[1], reverse=True) + best_code, best = ranked[0] + second = ranked[1][1] + total = sum(hits.values()) + # Enough absolute evidence, clear lead over the runner-up, and function + # words at a plausible density for connected speech. + if best >= 8 and best >= 1.5 * max(second, 1) and best / len(tokens) >= 0.04: + return best_code, best / max(total, 1) + return None, 0.0 + + +# A second alignment, level-matched, for a pass whose words leave diarized speech +# empty. Normalising each window (_Aligner._forward_windows) cannot lift a voice +# 15-20 dB below one that talks through the same window, and the DP then crowds +# the quiet voice's words into the loud one's time. Dividing the audio by its +# own smoothed envelope first fixes that, but levelling everything costs on +# ordinary material (CallHome +0.1 to +0.24 points of words under the wrong +# speaker after the voice check) and now and then breaks a file that aligned +# well. So it only replaces the first alignment where its words cover the +# diarized speech clearly better -- the one symptom of the failure that needs no +# ground truth. Chosen on 32 recordings of a second-voice corpus with AMI, +# CallHome and VoxConverse, then checked on 154 recordings of that corpus that +# took no part (paired 95 % intervals, words under the wrong speaker after the +# voice check): call centre -1.04 points [-2.21, -0.15], office -0.19 +# [-1.21, +0.45]; on 105 AMI, CallHome and VoxConverse files not a single pass +# switches. Margins from +2 to +10 points lie on one plateau. The second +# alignment runs where the first leaves over LEVEL_COVERAGE_MARGIN of the speech +# uncovered: on 2 of 3 AMI passes (far-field meetings), 6-11 % of CallHome's and +# none of VoxConverse's -- a second forward pass and DP over the pass, some +# seconds against its decode. It runs after _free_decode_buffers, once the first +# alignment's emission is gone, and adds one levelled copy of the pass (96 MB at +# 1500 s, ~0.5 GB for a moment while its envelope is taken) -- well inside +# MIN_HEADROOM_GB, so the pass length needs no allowance for it. +LEVEL_ENVELOPE_SEC = 0.4 # smoothing of the envelope the audio is divided by +LEVEL_FLOOR_PERCENTILE = 30 # the envelope is never taken below this percentile: +# pauses and room tone stay quiet as long as they are +# under that share of the pass (a longer silence is +# lifted with the rest -- the coverage check is what +# keeps such an alignment out). 50 already loses most +# of the gain (32 % of the quiet voice's words wrong +# instead of 5 %) +LEVEL_MAX_GAIN_DB = 60 # and never more than this below the envelope's peak: +# a pass that is over 30 % digital silence has a +# percentile of 0, and near-zero dither beside it +# would otherwise be lifted without bound, or divided +# by zero (3 of 449 measured passes lie below it; with +# it the holdout result above is the same to the word) +LEVEL_COVERAGE_MARGIN = 0.05 # coverage the second alignment must add to be taken +# The coverage's resolution and reach are the values the measurement above used, +# not tuned; the margin and the holdout result rest on them. +COVERAGE_FRAME_SEC = 0.1 +COVERAGE_REACH_SEC = 0.5 # a word counts for the speech this close to it + + +def _level_matched(audio): + """`audio` divided by its moving RMS over LEVEL_ENVELOPE_SEC, never by less + than that envelope's LEVEL_FLOOR_PERCENTILE percentile nor more than + LEVEL_MAX_GAIN_DB below its peak; all-zero audio comes back unchanged. The + moving mean is centred and mirrored at the edges as scipy's uniform_filter1d + has it (the measurement used that; scipy is no dependency here). Buffers are + reused: this runs right after a decode, and a 1500-s pass would otherwise hold + ~0.9 GB of float64 temporaries at once.""" + import numpy as np + n = max(1, int(LEVEL_ENVELOPE_SEC * SAMPLE_RATE)) + sq = np.pad(np.square(audio, dtype=np.float32), (n // 2, (n - 1) // 2), mode="symmetric") + # A running sum of non-negative values never decreases, so the differences are + # never negative and their root never NaN -- unlike uniform_filter1d's running + # update, which dips a hair below zero after a loud stretch and so turned a + # whole measured pass into NaN. + c = np.empty(len(sq) + 1) + c[0] = 0.0 + np.cumsum(sq, dtype=np.float64, out=c[1:]) + del sq + env = c[n:] - c[:-n] + del c + env /= n + np.sqrt(env, out=env) + floor = max(np.percentile(env, LEVEL_FLOOR_PERCENTILE), env.max() * 10 ** (-LEVEL_MAX_GAIN_DB / 20)) + if floor <= 0: + return audio + np.maximum(env, floor, out=env) + out = env.astype(np.float32) + del env + return np.divide(audio, out, out=out) + + +def _speech_coverage(stamps, turns, t_offset): + """Share of diarized speech (COVERAGE_FRAME_SEC frames inside any turn) that + has a word within COVERAGE_REACH_SEC. `turns` are window-relative + (_clip_turns), `stamps` absolute, as align_words returns them.""" + import numpy as np + frames = int(max(end for _, end, _ in turns) / COVERAGE_FRAME_SEC) + 1 + speech = np.zeros(frames, bool) + near = np.zeros(frames, bool) + for start, end, _ in turns: + speech[int(start / COVERAGE_FRAME_SEC):int(end / COVERAGE_FRAME_SEC)] = True + for w in stamps: + near[max(0, int((w["start"] - t_offset - COVERAGE_REACH_SEC) / COVERAGE_FRAME_SEC)): + int((w["end"] - t_offset + COVERAGE_REACH_SEC) / COVERAGE_FRAME_SEC) + 1] = True + return (speech & near).sum() / max(1, speech.sum()) + + +class _AlignerPool: + """Chooses and caches the alignment model chunk by chunk. + + With an explicit language the model stays fixed (as before), but each + chunk's text is still checked so a wrong menu choice produces a warning + instead of silently degraded timestamps. With language=None + (Auto/Multilingual) the model follows the *detected* language of each + chunk -- Auto gets the char-native per-language quality automatically, + the language may change mid-file, and only text without a dominant + language falls back to the romanised multilingual model. Loaded aligners + are cached (bounded -- they are ~1.2 GB each) because a load costs + seconds and languages may alternate.""" + + MAX_CACHED = 2 + + def __init__(self, language, log_cb): + self._explicit = bool((language or "").strip()) + self._language = (language or "").strip().lower()[:2] + self._log_cb = log_cb + self._cache = {} # model name -> _Aligner, insertion-ordered + self._last_model = None + self._warned = False + + def align_for_salvage(self, words, window): + """Alignment for the loop ladder: maps a clean prefix onto its audio + time so that only the remainder has to be decoded again. It lives here + rather than as a closure in transcribe() because a closure binds to a + variable name -- and exactly that broke silently once, when the + alignment architecture was moved onto this pool. + + `remember=False` because this is an *internal* alignment of a fragment + of a pass that may yet be discarded: were it allowed to move the pool's + state, the NEXT chunk would silently inherit the fragment's language, + and the one-off language warning would be spent on text nobody ever + sees. `align_prefix` rather than `align_words` because the prefix covers + only the start of the window: there the audio is not shared out by the + words' character counts; instead the words are split into pieces and + each piece is aligned against all of the remaining audio.""" + return self.aligner_for(" ".join(words), remember=False).align_prefix( + words, window) + + def align_words(self, words, window, t_offset, turns=None): + """Word timestamps for a whole pass, with the aligner its text calls + for. Through here no caller ever holds an aligner: a reference kept + across chunks outlived its eviction, and _release_gpu_memory then + freed nothing (measured in its docstring). + + With `turns` (window-relative, from _clip_turns) naming two speakers or + more, a level-matched second alignment replaces the first where its words + cover the diarized speech by LEVEL_COVERAGE_MARGIN more (see there). It + is not even run where the first already covers 1 - LEVEL_COVERAGE_MARGIN + or more: coverage cannot exceed 1, so the second could never be taken.""" + aligner = self.aligner_for(" ".join(words)) + stamps = aligner.align_words(words, window, t_offset=t_offset) + if not turns or len({label for _, _, label in turns}) < 2: + return stamps + covered = _speech_coverage(stamps, turns, t_offset) + if covered >= 1 - LEVEL_COVERAGE_MARGIN: + return stamps + levelled = aligner.align_words(words, _level_matched(window), t_offset=t_offset) + covered_levelled = _speech_coverage(levelled, turns, t_offset) + # Words spread evenly because a window could not be aligned (prob 0.0) + # sit near every diarized frame and would win on coverage alone. + guessed = lambda ws: sum(not w.get("prob") for w in ws) + if covered_levelled > covered + LEVEL_COVERAGE_MARGIN and guessed(levelled) <= guessed(stamps): + _log(self._log_cb, "info", + f"Word timestamps level-matched: they cover {covered_levelled:.0%} " + f"of the diarized speech instead of {covered:.0%} (a quieter voice).") + return levelled + _log(self._log_cb, "debug", + f"Level-matched word timestamps not taken: {covered_levelled:.0%} of the " + f"diarized speech covered, against {covered:.0%}.") + return stamps + + def aligner_for(self, text, remember=True): + """The aligner for `text`. `remember=False` only reads: the choice for + the next chunk and the one-off language warning stay untouched.""" + detected, _ = _detect_language(text) + if self._explicit: + model = resolve_align_model(self._language) + if (remember and _reads_as_other(detected, self._language) + and not self._warned): + self._warned = True + _log(self._log_cb, "warn", + f"Language is set to '{self._language}' but the transcript " + f"looks like '{detected}'. Word timestamps use the " + f"'{self._language}' alignment model; check the language " + f"setting if that was not intended.") + elif detected: + model = ALIGN_MODELS.get(detected, ALIGN_MODEL_MULTILINGUAL) + if remember and model != self._last_model: + _log(self._log_cb, "info", + f"Detected language '{detected}' -> alignment model: {model}") + else: + # Thin or mixed evidence: keep what worked for the previous chunk, + # multilingual on the very first one. + model = self._last_model or ALIGN_MODEL_MULTILINGUAL + if remember and model != self._last_model: + _log(self._log_cb, "info", + f"No dominant language detected -> alignment model: {model}") + return self._load(model, remember=remember) + + @staticmethod + def _release_gpu_memory(): + """Return an evicted aligner's GPU memory to the system. + + Dropping the reference frees torch's *allocator* but not the driver + reservation: measured, `driver_allocated_memory` stayed at 2.04 GB and + only fell to zero on an explicit `empty_cache()`. On Auto a file with + three alignment languages would otherwise hold that for the rest of the + job, against a MIN_HEADROOM_GB budget MLX is also drawing on. + + **Call this only after the last reference to the evicted aligner is + gone**, and note that dropping the dict entry is not enough on its own. + Measured on one evicted aligner, driver memory went 2.04 GB -> 2.04 GB + while a local still held it, -> 1.01 GB once that reference went away, + and only -> 0.00 GB after a `gc.collect()`: a `Wav2Vec2ForCTC` sits in + reference cycles, so refcounting alone leaves half of it resident until + the collector runs. + """ + try: + import gc + + import torch + gc.collect() + torch.mps.empty_cache() + except Exception: + pass + + def _load(self, model, remember=True): + aligner = self._cache.pop(model, None) + if aligner is None: + # Evict before loading, not after: an aligner is ~1.2 GB, and this + # runs right after a Voxtral pass, when headroom is tightest. Loading + # first would hold MAX_CACHED + 1 of them at once. + while len(self._cache) >= self.MAX_CACHED: + oldest = next(iter(self._cache)) + on_gpu = getattr(self._cache[oldest], "device", "cpu") != "cpu" + del self._cache[oldest] # last reference to the evicted one + if on_gpu: + self._release_gpu_memory() + if self._cache and _unfetchable(model): + # Offline, a chunk in a language whose aligner was never + # downloaded would otherwise end the whole job after its pass + # was decoded. The aligner already in use anchors another + # language's words too (see _STOPWORDS), only less precisely. + fallback = next(reversed(self._cache)) + _log(self._log_cb, "warn", + f"The alignment model {model} is not downloaded and cannot be " + f"fetched now; aligning this chunk with {fallback} instead.") + aligner = self._cache.pop(fallback) + self._cache[fallback] = aligner + return aligner + _log(self._log_cb, "info", f"Loading alignment model: {model}") + aligner = _load_from_hub(lambda: _Aligner(model), "alignment model", + model, _ALIGNER_DOWNLOAD_GB, self._log_cb) + self._cache[model] = aligner # re-insert = mark most recently used + if remember: + self._last_model = model + return aligner + + +def _frame_energy(window, frame): + """Per-frame mean squared amplitude of `window` in `frame`-sample blocks -- + the energy curve used to find quiet spots. Trailing samples that don't fill + a whole frame are dropped.""" + import numpy as np + nf = len(window) // frame + return (window[:nf * frame].reshape(nf, frame).astype(np.float32) ** 2).mean(axis=1) + + +def _quietest_frame_near(audio, target, radius_sec): + """Sample index of the quietest 100 ms frame within +/- `radius_sec` of + `target`, so a cut lands in a pause rather than mid-word. Returns `target` + unchanged when the search window is too small to snap.""" + frame = max(1, int(0.1 * SAMPLE_RATE)) + lo = max(frame, target - int(radius_sec * SAMPLE_RATE)) + hi = min(len(audio) - frame, target + int(radius_sec * SAMPLE_RATE)) + if hi - lo <= frame: + return target + window = audio[lo:hi] + if len(window) // frame < 2: + return target + return lo + int(_frame_energy(window, frame).argmin()) * frame + frame // 2 + + +def _quiet_runs(energy, nf, thresh, good_run, target_f): + """Scan the frame energies for runs at or below `thresh`. + + Returns (nearest_clear_pause, longest_run, longest_run_len) in frame units: + the midpoint of the clear pause (>= `good_run` frames) closest to + `target_f`, plus the longest quiet run as a fallback. + """ + silent = energy <= thresh + best_close, best_close_d = None, None # clear pause nearest target + best_long, best_long_len = None, 0 # longest quiet run (fallback) + i = 0 + while i < nf: + if silent[i]: + j = i + while j < nf and silent[j]: + j += 1 + run, mid = j - i, (i + j) // 2 + if run > best_long_len: + best_long_len, best_long = run, mid + if run >= good_run: + d = abs(mid - target_f) + if best_close_d is None or d < best_close_d: + best_close_d, best_close = d, mid + i = j + else: + i += 1 + return best_close, best_long, best_long_len + + +def _chunk_boundaries(audio, chunk_len, back_len, fwd_len, max_len=None): + """Split `audio` into ~`chunk_len`-sample passes, snapping every cut to a real + speaker pause found in the window `[target - back_len, target + fwd_len]`. + + The search leans *backward*: a shorter pass is always memory-safe, so we hunt + far back (large `back_len`) for a genuine pause and allow only a small forward + reach (`fwd_len`) to avoid growing the pass. Among clear pauses we take the one + *closest to the target* (keeps passes near the intended length); if none is + clearly a pause we fall back to the longest quiet run, then to the target + itself. Cutting between utterances means a pass never splits a word, and with + the lead-in overlap the seam is effectively lossless. + + `max_len` is a hard per-pass cap (the RAM-safe length): no pass — including + the final one after tail-merging and backward snaps — ever exceeds it. And + no pass comes out shorter than about a quarter of `chunk_len`: when backward + snaps leave a remainder just over `max_len` but too short for a full pass + plus a real tail, it is halved instead (see below). + """ + import numpy as np + n = len(audio) + max_len = max_len or chunk_len * 2 + if n <= min(chunk_len, max_len): + return [0, n] + frame = max(1, int(0.05 * SAMPLE_RATE)) # 50 ms resolution + good_run = 8 # >= ~400 ms silence counts as a clear speaker pause + min_run = 3 # >= ~150 ms silence is an acceptable fallback cut + long_tail = chunk_len + chunk_len // 2 + bounds = [0] + while True: + remaining = n - bounds[-1] + # Done when the tail fits one RAM-safe pass AND is not much more than + # the balanced length (small tails merge into the final pass instead of + # becoming a tiny extra chunk) — but a tail beyond max_len always gets + # another cut, so backward snaps can't inflate the final pass past the + # memory budget. + if remaining <= min(max_len, long_tail): + break + # A full pass here would leave less than half a pass behind it. That + # happens when earlier cuts snapped backward and the remainder ended up + # just over max_len: aiming at chunk_len then cut 1 s before the end + # (measured on a crafted 1190 s file at 600 s: 589.5 + 599.9 + 0.55 s). + # Halve the remainder instead; both halves stay within max_len. Either + # way the target lies before the end of the audio. + step = min(chunk_len if remaining >= long_tail else remaining // 2, max_len) + target = bounds[-1] + step + # never let a pass shrink below half the step, even hunting backward + lo = max(bounds[-1] + max(frame, step // 2), target - back_len) + # ...never let a snap or tail-merge push this pass past max_len, and + # never leave less than half a step after the cut + hi = min(target + fwd_len, bounds[-1] + max_len, n - step // 2) + if hi - lo <= 2 * frame: + cut = target + else: + window = audio[lo:hi] + nf = len(window) // frame + energy = _frame_energy(window, frame) + speech = float(np.percentile(energy, 75)) + target_f = (min(target, hi) - lo) / frame # target position in frames + # _frame_energy returns mean SQUARED amplitude, so an amplitude + # fraction has to be squared before it can be compared against it. + # Applying QUIET_LEVEL unsquared put the gate at -8.2 dB below the + # typical speech level -- that is ordinary quiet speech, not a pause + # (a real pause sits 20-40 dB down). Any 400 ms of unstressed + # syllables then counted as a "clear speaker pause" and, being + # nearer the target, beat the genuine pause: measured, the cut moved + # off a real 1.5 s pause at 589.8 s to 598.5 s, mid-word. The + # preceding pass has no overlap to fall back on, so its last word + # was decoded cut in half. + best_close, best_long, best_long_len = _quiet_runs( + energy, nf, speech * QUIET_LEVEL ** 2 + 1e-9, good_run, target_f) + if best_close is None and best_long_len < min_run: + # Nothing that quiet anywhere in the window: a recording with a + # high noise floor (room tone, hum, constant background). Rather + # than give up and cut at the raw target, fall back to the + # looser gate -- its relative minimum is still the best pause on + # offer here, which is what the old threshold always used. + best_close, best_long, best_long_len = _quiet_runs( + energy, nf, speech * QUIET_LEVEL + 1e-9, good_run, target_f) + if best_close is not None: + cut = lo + best_close * frame + frame // 2 + elif best_long_len >= min_run: + cut = lo + best_long * frame + frame // 2 + else: + cut = target + bounds.append(min(max(cut, bounds[-1] + frame), n)) + bounds.append(n) + return bounds + + +def _pass_bounds(audio, chunk_sec): + """Sample offsets of the passes for a RAM-safe pass length of `chunk_sec`. + + Balance the passes: take the fewest passes that keep each within the + RAM-safe length, then split evenly. Even passes share the memory headroom + and avoid a tiny leftover tail (e.g. 1143s @1006 -> 2x571s, not 1006+137), + which also lowers the per-pass peak. + """ + max_len = max(1, int(chunk_sec * SAMPLE_RATE)) + n_passes = max(1, math.ceil(len(audio) / max_len)) + chunk_len = math.ceil(len(audio) / n_passes) + # Wide backward hunt for a real pause (a shorter pass is always memory-safe), + # small forward reach so a cut can't grow the pass much past the RAM budget. + back_len = min(int(SILENCE_SEARCH_SEC * SAMPLE_RATE), chunk_len // 2) + fwd_len = int(min(SILENCE_SEARCH_SEC, 20) * SAMPLE_RATE) + return _chunk_boundaries(audio, chunk_len, back_len, fwd_len, max_len) + + +def resolve_model(name, default_repo=None): + """Prefer a persistent local copy under the package `models/` dir (e.g. + `models/voxtral-mini`) so models don't have to live in the HF cache and + won't be re-fetched after an aborted run. Falls back to the Hugging Face + repo id (download on first use) when no local copy is present. + """ + default_repo = default_repo or VOXTRAL_MODELS.get(name, name) + return _local_copy(name) or default_repo + + +# Friendlier names for whole-component reporting in _quant_summary. +_COMPONENT_LABELS = { + "audio_tower": "audio encoder", + "multi_modal_projector": "projector", +} +_DTYPE_SHORT = {"bfloat16": "bf16", "float16": "fp16", "float32": "fp32"} + + +def _weight_names(repo): + """All tensor names of a local build, from the safetensors index (sharded) + or the single-file safetensors header. Empty list when unreadable.""" + import json + import struct + try: + with open(os.path.join(str(repo), "model.safetensors.index.json")) as f: + return list(json.load(f)["weight_map"]) + except (OSError, ValueError, KeyError): + pass + try: + with open(os.path.join(str(repo), "model.safetensors"), "rb") as f: + (hlen,) = struct.unpack(" loop gone, "nicht nicht" kept, 10.17 commas/100 words +# 1.05 -> loop gone, "nicht nicht" kept, 10.01 +# 1.10 -> loop gone, "nicht nicht" LOST, 9.55 +# +# A barely-there nudge is enough to tip a self-reinforcing loop but too weak to +# override a repetition the model is confident about. +RETRY_REPETITION_PENALTIES = (1.01, 1.1) +# Splitting a looping pass is tried first, because the loop is a long-generation +# effect: the pass that produced 4099 identical words at 410 s transcribed +# cleanly as 2x205 s, with the doubled negation intact. Depth 3 halves a +# 600 s pass down to 75 s; a piece below 45 s would carry too little context +# to be worth a further split. Both are judgement calls, not measured optima. +LOOP_SPLIT_MAX_DEPTH = 3 +LOOP_SPLIT_MIN_SEC = 45 +# Gentle sampling before any penalty, Whisper's own fallback strategy: a bit +# of sampling noise keeps a self-reinforcing loop from re-forming -- without +# deleting repeated words the way a penalty does. Seeded, so retries stay +# reproducible. The rungs are calibrated on a real pathological 346 s window +# (a group session where one speaker repeats the same question to many +# people): greedy LOOP, T=0.2 LOOP, T=0.5 LOOP, T=0.8 CLEAN (fluent text, +# zero breaker kicks) -- low temperatures +# barely move a confident argmax, so the second rung must be high enough to +# actually break the attractor (Whisper's own ladder goes to 0.8/1.0 too). +# T=0.2 stays as the near-free first nudge before the split. +RETRY_TEMPERATURES = ((0.2, 0), (0.8, 1)) # (temperature, seed) + +# --------------------------------------------------------------------------- # +# In-place loop breaking during generation +# --------------------------------------------------------------------------- # +# A degenerate loop repeats one token cycle for thousands of tokens. The +# _LoopBreaker logits processor detects that periodicity while the tokens are +# being generated and bans exactly the one token that would continue the +# cycle -- everywhere else the logits pass through untouched, so a clean pass +# stays bit-identical to plain greedy. Thresholds are deliberately strict: +# the *entire* window must be one exact token cycle, which for a 3-token word +# means ~64 verbatim repeats (a real mantra passage differs long before that, +# and the transcript-level DEGENERATE_CYCLE_REPEATS=12 net stays in place -- +# it has to, because a single stray token ends the exact periodicity this +# processor requires while the loop itself carries on). +LOOP_BREAK_WINDOW = 192 # tail tokens that must be fully periodic +LOOP_BREAK_MAX_PERIOD = 32 # longest token cycle we detect (~20 words) +LOOP_BREAK_MIN_CYCLES = 6 # window/period must fit this many full cycles +LOOP_BREAK_CHECK_EVERY = 16 # steps between periodicity checks (latency/cost) +# A model that re-enters a loop right after being kicked out this many times +# is beyond in-place repair: give up so the retry ladder takes over cheaply +# (~3 windows of wasted tokens instead of a full max_tokens run). +LOOP_BREAK_MAX_KICKS = 3 + + +class _LoopBreaker: + """Logits processor that kills degenerate repetition loops in place. + + Backend-agnostic on purpose: ``tokens`` only needs ``len``/indexing and + ``logits`` item assignment (mlx arrays and numpy both qualify), so the + logic is unit-testable without weights or mlx. + """ + + def __init__(self): + self._tail = [] # our incremental copy of the generated tokens + self._seen = 0 # tokens already folded into _tail (NOT len(_tail)) + self._step = 0 + self._period = 0 # >0 while a ban episode is active + self.kicks = 0 # completed interventions (episodes) + self.gave_up = False + + def _find_period(self): + tail = self._tail + if len(tail) < LOOP_BREAK_WINDOW: + return 0 + t = tail[-LOOP_BREAK_WINDOW:] + for p in range(1, LOOP_BREAK_MAX_PERIOD + 1): + if LOOP_BREAK_WINDOW // p < LOOP_BREAK_MIN_CYCLES: + break + if all(t[i] == t[i - p] for i in range(p, LOOP_BREAK_WINDOW)): + return p + return 0 + + def __call__(self, tokens, logits): + if self.gave_up: + return logits + # Incremental sync (one new token per step); resync fully if the + # bookkeeping ever drifts (e.g. a first call with a non-empty prompt). + # The count of already-seen tokens is tracked separately and must NOT be + # inferred from len(self._tail): the tail is trimmed to a fixed bound + # below, so once it saturates it stops growing and every later step would + # look like a drift and take the full resync branch -- which converts + # ~220 device scalars per generated token and dominates the decode. + n = len(tokens) + if n == self._seen + 1: + self._tail.append(int(tokens[-1])) + elif n != self._seen: + keep = LOOP_BREAK_WINDOW + LOOP_BREAK_MAX_PERIOD + self._tail = [int(x) for x in tokens[-keep:]] + self._seen = n + del self._tail[:-(LOOP_BREAK_WINDOW + LOOP_BREAK_MAX_PERIOD)] + self._step += 1 + + if self._period: + # Active episode: keep banning until the model actually diverges + # (the divergent token breaks the window's periodicity). + if self._find_period() == self._period: + logits[:, self._tail[-self._period]] = float("-inf") + else: + self._period = 0 + return logits + + if self._step % LOOP_BREAK_CHECK_EVERY: + return logits + p = self._find_period() + if p: + self.kicks += 1 + if self.kicks > LOOP_BREAK_MAX_KICKS: + self.gave_up = True + return logits + self._period = p + # Ban the token that would continue the cycle (the one p back). + logits[:, self._tail[-p]] = float("-inf") + return logits + + +def _longest_cycle_repeats(words, max_k=DEGENERATE_CYCLE_MAX_WORDS): + """How often the most-repeated short word cycle repeats back-to-back. + + A cycle of length k shows up as a run of positions where ``w[i] == w[i-k]``; + a run of n such positions means the k-word block was written (n + k) / k + times. Returns the largest count over all k, so "a a a" and "a b a b a b" + are both measured on the same scale. + """ + best = 0 + for k in range(1, max_k + 1): + run = 0 + for i in range(k, len(words)): + run = run + 1 if words[i] == words[i - k] else 0 + best = max(best, (run + k) // k) + return best + + +def _looks_degenerate(text): + """True if a pass collapsed into a repetition loop. + + Voxtral is run without a repetition penalty because that is what Mistral's + reference transcription does and because a penalty strips punctuation from + verbatim speech. Rarely -- seen with the 24B model -- a pass still + degenerates into repeating the same few words thousands of times, which + must not reach the transcript. + + The cycle count is the load-bearing signal. The compression ratio stays as + a net for degeneracies that are not a clean cycle, but it cannot be relied + on for local loops: it is computed over the whole pass, so a loop filling + 4% of a 3300-word chunk moved the ratio from 2.6 to only 2.8 -- and even a + half-loop/half-prose pass measured below the threshold. + """ + import zlib + if not text: + return False + words = text.split() + if len(words) < 30: + return False + if _longest_cycle_repeats(words) >= DEGENERATE_CYCLE_REPEATS: + return True + raw = text.encode("utf-8") + return len(raw) / max(1, len(zlib.compress(raw))) > DEGENERATE_COMPRESSION_RATIO + + +# A looping pass is not wrong from the start: the model transcribes normally +# and only then falls into the attractor, so everything before the loop is its +# best greedy output. Re-running the whole window throws that away and pays to +# regenerate it. Keeping the clean part instead needs one thing the ladder did +# not have -- knowing *where* in the audio that part ends -- which forced +# alignment can answer for a fraction of a decode. +# +# Below this much salvaged audio it is not worth it: a full retry costs about +# the same and keeps the whole window's context, which a resumed pass loses. +SALVAGE_MIN_PREFIX_SEC = 60 +# Word endings that mark a sentence, ignoring trailing quotes and brackets. +_SENTENCE_END = ('.', '!', '?', '…', ':') +_SENTENCE_TRAIL = '"\'»)]' + + +def _degenerate_span(words, max_k=DEGENERATE_CYCLE_MAX_WORDS, + min_repeats=DEGENERATE_CYCLE_REPEATS): + """Word indices [start, end) of the earliest run repeating a cycle at least + `min_repeats` times, or None if the text has no such run. + + _looks_degenerate answers *whether* a pass looped; this answers *where*, + which is what makes keeping the clean part possible. + """ + best = None + for k in range(1, max_k + 1): + run = 0 + for i in range(k, len(words)): + if words[i] != words[i - k]: + run = 0 + continue + run += 1 + if (run + k) // k < min_repeats: + continue + start = i - run - k + 1 + end = i + 1 + while end < len(words) and words[end] == words[end - k]: + end += 1 + if best is None or start < best[0]: + best = (start, end) + break # earliest qualifying run for this k + return best + + +def _prefix_end_index(words, loop_start): + """How many words to keep: everything up to the last sentence that ends + before the loop begins, or 0 when there is no such sentence. + + Cutting at a sentence boundary keeps the seam clean and drops the handful + of words a model tends to produce while sliding into the attractor, + together with the sentence they belong to. + """ + for i in range(loop_start - 1, -1, -1): + if words[i].rstrip(_SENTENCE_TRAIL).endswith(_SENTENCE_END): + return i + 1 + return 0 + + +def _salvage_prefix(text, audio, align_cb): + """Split a looping pass into its clean prefix and the sample index where a + fresh pass should resume. + + Returns ``((prefix, cut), None)``, or ``(None, reason)`` when there is + nothing worth keeping -- the reason is logged, because "it fell back to a + full retry" is otherwise indistinguishable from "the rung never ran". + + Mapping the prefix onto its audio time is `_Aligner.align_prefix`'s job and + is harder than it looks: plain forced alignment has no way to stop early, + so it stretches the prefix's last words across the rest of the window -- + measured, an 85 s prefix came back ending at 299.98 s of a 300 s window, + with flawless scores. See that method for how it is done instead; here it + is enough to know that a prefix which could not be timed for real comes + back evenly spread (prob 0.0) and is refused below. The cut is then snapped + to the locally quietest frame, exactly as the split rung does, so the + resumed pass never starts mid-word. + """ + words = text.split() + span = _degenerate_span(words) + if span is None: + return None, "the degeneration is not a clean cycle, so it cannot be located" + keep = _prefix_end_index(words, span[0]) + if keep == 0: + return None, f"the loop starts at word {span[0]}, before any sentence ends" + prefix = words[:keep] + stamps = align_cb(prefix, audio) + if not stamps: + return None, "the prefix could not be aligned" + # _Aligner falls back to spreading words evenly when real alignment is + # impossible, marking those with prob 0.0. Those times are far too rough to + # cut audio on -- a wrong cut duplicates or drops speech. + if not any(w.get("prob", 0) for w in stamps): + # Nothing in the prefix was really aligned. `align_prefix` reports the + # reason it gave up to the log; here it is enough to know that these + # times are evenly spread, i.e. worthless for cutting audio. + return None, ("the prefix could not be aligned to the audio, so every " + "timestamp is an even guess") + # The check that matters is on the LAST stamp: it is the only one the cut + # below is taken from. `any(...)` over the whole prefix passed as soon as a + # single word carried a real score, so a prefix whose head aligned and whose + # tail was spread or interpolated went straight through and the resume point + # came from an invented, length-proportional timestamp (measured 27 s past + # the truth, worst word 33 s). `prob` is a probability in (0, 1]; 0.0 is the + # sentinel for a word that was spread or interpolated rather than aligned, + # so truthiness is exactly the right test. + if not stamps[-1].get("prob", 0): + return None, ("the end of the clean part was not really aligned, so the " + "resume point would be guesswork") + end_sample = min(int(stamps[-1]["end"] * SAMPLE_RATE), len(audio)) + if end_sample < SALVAGE_MIN_PREFIX_SEC * SAMPLE_RATE: + return None, (f"only {end_sample / SAMPLE_RATE:.0f}s would be kept, " + f"below the {SALVAGE_MIN_PREFIX_SEC}s minimum") + cut = _quietest_frame_near(audio, end_sample, 1.0) + if cut <= 0 or cut > len(audio): + return None, "the resume point fell outside the window" + return (" ".join(prefix), cut), None + + +# --------------------------------------------------------------------------- # +# A pass that came back in the wrong language +# --------------------------------------------------------------------------- # +# Voxtral is not a pure ASR model -- speech translation is an advertised +# capability, and the `[TRANSCRIBE]` token fixes only that text comes out, not +# which language it is in. So a whole pass can come back translated, with no +# loop and nothing else wrong with it. Measured over six runs of one 5h54m +# German recording: four shipped 30 minutes of English from a chunk that had no +# loop at all. Which language wins turns out to hang on the exact window +# length, not on the content -- of ten windows between 1420 s and 1430 s, four +# came back English -- so this cannot be prevented at the prompt, only caught. +# Measured on the two windows that translate reproducibly: writing `lang:xx` +# into the prompt (the documented instrument, and ours is token-identical to +# Mistral's own) fixed 0 of 2, while a retry at the ladder's first temperature +# fixed both. Perturbing the decode beats telling the model what to do, which +# is why this is a reason for the ladder to run rather than a repair of its own. + + +def _other_language(text, want): + """The language `text` reads as when that is decidedly not `want`, else None. + + None whenever there is nothing to compare (no expectation, or too little + text to judge), so an undecidable pass is never called wrong. + """ + got, _ = _detect_language(text) + return got if _reads_as_other(got, want) else None + + +def _reads_as_other(got, want): + """Whether a text the detector reads as `got` is decidedly not in `want`. + + For a language the detector can name, any other reading counts. For one it + cannot, only English does -- the language Voxtral translates into, and the + one the detector names reliably: any other reading may be the detector + misnaming `want` itself. The script check reads all Cyrillic as "ru" and + all Arabic script as "ar", so a correctly pinned Ukrainian or Persian file + was "translated" on every pass; Czech reads as Polish by its function + words. + """ + if not (got and want) or got == want: + return False + return want in _DETECTABLE or got == "en" + + +def _halves_disagree(a, b, log_cb, label, name_a, name_b): + """True when two halves of one window read as different languages. + + Both rungs that repair a window stitch two independently produced halves -- + the kept prefix plus its remainder, and the two sides of a split. Either + half can come back translated, and the seam is the one place where that is + visible without knowing the file's language at all: this test is + *relative*. Speakers who mix German and English run through this material + constantly, so a detector that leans the same way on both halves has to + stay silent; only a genuine change between the halves may fire. + """ + la, _ = _detect_language(a) + lb, _ = _detect_language(b) + if la and lb and la != lb: + _log(log_cb, "warn", + f"{label}: {name_a} reads as '{la}' but {name_b} as '{lb}' -- that " + f"is a translated pass, not a transcript; dropping it and retrying " + f"the whole window.") + return True + return False + + +def _file_language(tally): + """The file's language so far, or None while the evidence is too thin. + + One chunk is never enough: if the *first* chunk is the translated one, + taking its language as the truth would send every later chunk to be + "repaired" into the wrong language -- the guard would cause exactly the + damage it exists to prevent. Two have to agree. + """ + if not tally: + return None + best, n = max(tally.items(), key=lambda kv: kv[1]) + runner_up = max([v for k, v in tally.items() if k != best], default=0) + return best if n >= 2 and n > runner_up else None + + +# --------------------------------------------------------------------------- # +# Recovering a head a long pass dropped +# --------------------------------------------------------------------------- # +# A long window sometimes comes back missing its first seconds of speech: no +# loop, no wrong language, nothing else wrong with it -- the opening is simply +# not in the text. Measured on a 1226 s recording whose first 2.4 s are a remark +# before the take: on Auto the pass returned 3620 words without it, with `de` +# 3631 words with it, and the two agreed everywhere else. The reverse has been +# seen too (a pinned language costing the first ~150 words of a 1426 s window), +# so no prompt setting is the safe side, and it depends on the recording as much +# as on the length -- which is why this is a check rather than a length rule. On +# that recording 20 s through 1180 s all kept the opening and only 1195 s and +# 1226 s lost it; FLEURS material never shows it at any length; but on a raw +# 4.8 h Zoom recording it happens well inside the passes we actually run: of 32 +# windows cut at 300 s and 600 s, three lost their opening on Auto and two with +# `de`, the worst dropping 18 words of fluent speech from a 600 s window and one +# dropping a whole quoted sentence from a 300 s one in both language settings. +# +# That is the repair: decode a short head of the same audio and splice back +# whatever the long pass is missing. The seam is found in the text rather than +# assumed, so a pass that lost nothing (the normal case) is left untouched. +# 60 s is a judgement call: short windows were never seen to lose their +# opening in the sweep, and the probe costs ~10 % of a pass at this length. +HEAD_PROBE_SEC = 60.0 +# Grown only when the seam is not in the probe at all, i.e. the pass lost more +# than the probe covers. Past this the loss is no longer a dropped opening. +HEAD_PROBE_MAX_SEC = 240.0 +# The run of words from the pass that is looked for in the probe. Long enough +# that common words cannot line up by chance, short enough to survive the small +# wording differences between two independent decodes. +HEAD_ANCHOR_WORDS = 8 +HEAD_ANCHOR_MIN_HITS = 6 + + +def _norm_word(word): + """A word reduced to what two decodes of the same speech agree on.""" + return re.sub(r"[^\w]", "", word.lower()) + + +def _find_anchor(probe, anchor, min_hits=HEAD_ANCHOR_MIN_HITS): + """Where in `probe` the `anchor` run begins, or None. Both normalised. + + Two independent decodes of the same speech differ in small ways ("Ja, + herzlich" against "Ja. Herzlich"), so the match is scored rather than exact. + Ties go to the earliest position: that is the one that recovers the least + text, and recovering too little only leaves the status quo while recovering + too much would duplicate words the pass already has. + """ + best_at, best_hits = None, 0 + width = len(anchor) + for i in range(len(probe) - width + 1): + hits = sum(1 for a, b in zip(anchor, probe[i:i + width]) if a == b) + if hits > best_hits: + best_at, best_hits = i, hits + return best_at if best_hits >= min_hits else None + + +def _recover_lost_head(vox, audio, language, text, log_cb, label): + """Put back the opening a long pass dropped. Returns the text either way. + + Left alone when there is no long-window defect to repair (a short pass), no + seam to find (too little text), or nothing missing at the seam. The caller + decides which passes are worth checking at all. + """ + words = text.split() + duration = len(audio) / SAMPLE_RATE + if len(words) < HEAD_ANCHOR_WORDS or duration <= HEAD_PROBE_SEC * 1.5: + return text + anchor = [_norm_word(w) for w in words[:HEAD_ANCHOR_WORDS]] + + # Never probe more than half the pass: past that the probe is a second + # transcription rather than a check, and the defect is no longer a lost + # opening. That holds for the first probe too (a 100 s pass gets a 50 s + # one). Growing doubles and stops at the last doubling within `longest` + # rather than stretching to it: each probe decodes the same opening again, + # and on a real interview a 226 s probe after the 120 s one still did not + # find it -- the anchor is missed on wording, which more audio does not fix. + longest = min(HEAD_PROBE_MAX_SEC, duration / 2) + sec = min(HEAD_PROBE_SEC, longest) + while True: + probe = vox.transcribe_array(audio[:int(sec * SAMPLE_RATE)], language, + max_new_tokens=int(sec * 20) + 512) + if not probe or _looks_degenerate(probe): + # A looping probe is a property of this speech, not of the window + # length, and a degenerate decode is the one that runs to its full + # token budget. Growing it would buy a longer loop, not an answer. + _log(log_cb, "warn", + f"{label}: could not check the opening -- the {sec:.0f}s head " + f"probe came back unusable.") + return text + probe_words = probe.split() + at = _find_anchor([_norm_word(w) for w in probe_words], anchor) + if at is not None: + break + # The pass' opening is not in this probe at all, so it lost more than the + # probe covers. Reach further, up to `longest`. + grown = sec * 2 + if grown > longest: + _log(log_cb, "warn", + f"{label}: could not check the opening -- a {sec:.0f}s head " + f"probe does not contain the first words of the pass.") + return text + sec = grown + + if at == 0: + return text # the pass starts where the audio does: nothing was lost + missing = " ".join(probe_words[:at]).strip() + _log(log_cb, "warn", + f"{label}: the pass dropped its opening ({at} words); recovered from a " + f"{sec:.0f}s head probe.") + return f"{missing} {text.lstrip()}" + + +def _quietest_split(audio): + """Sample index nearest the middle that sits in the quietest 100 ms frame, + so a pass is never split in the middle of a word.""" + return _quietest_frame_near(audio, len(audio) // 2, 5.0) + + +def _clip_turns(turns, w0, w1): + """Speaker turns intersected with the window [w0, w1) (seconds), shifted to + window-relative times. None when nothing meaningful remains.""" + out = [] + for s, e, lbl in turns or []: + s2, e2 = max(s, w0), min(e, w1) + if e2 - s2 > 0.05: + out.append((s2 - w0, e2 - w0, lbl)) + return out or None + + +def _turn_gap_split(audio, turns): + """Sample index of the best speaker-change boundary near the middle, or + None when the window offers no usable one. + + Cutting where the speaker changes is the cleanest split there is: the + context *within* a turn stays intact, and across a turn boundary it + matters least. Candidates are boundaries between consecutive turns of + *different* speakers inside the middle half of the window (keeps the + halves reasonably balanced); overlapping-speech boundaries are skipped + (cutting inside an overlap clips someone mid-word). The winner is the + boundary closest to the middle, snapped to the locally quietest 100 ms. + """ + if not turns: + return None + lo, hi = len(audio) // 4, 3 * len(audio) // 4 + mid = len(audio) // 2 + ordered = sorted(turns, key=lambda t: t[0]) + best = None + # Track the furthest point reached so far and who is still speaking there, + # not merely the immediately preceding turn. Sorted by start time, a short + # turn nested inside a longer one -- pyannote's standard backchannel + # pattern, an "mhm" during someone's sentence -- makes the *next* turn look + # like it follows the backchannel. Comparing against that one's end put the + # cut in the middle of the enclosing speaker's utterance, and made a + # speaker resuming after their own backchannel look like a change. + prev_end, prev_label = ordered[0][1], ordered[0][2] + for s1, e1, l1 in ordered[1:]: + # Same speaker resuming after a pause is not a boundary; neither is a + # boundary buried in overlapping speech (cutting there clips a word). + if l1 != prev_label and s1 >= prev_end - 0.2: + p = int((prev_end + max(s1, prev_end)) / 2 * SAMPLE_RATE) + if lo <= p <= hi and (best is None or abs(p - mid) < abs(best - mid)): + best = p + if e1 >= prev_end: + prev_end, prev_label = e1, l1 + if best is None: + return None + return _quietest_frame_near(audio, best, 1.0) + + +def _turn_profile(turns, dur): + """One-line diarization profile of a window, for the escalation log.""" + ordered = sorted(turns, key=lambda t: t[0]) + speakers = {lbl for _, _, lbl in ordered} + changes = sum(1 for a, b in zip(ordered, ordered[1:]) if a[2] != b[2]) + speech = sum(e - s for s, e, _ in ordered) + dur = max(dur, 1e-9) + return (f"{len(speakers)} speaker(s), {changes} turn change(s) " + f"({60.0 * changes / dur:.1f}/min), ~{min(100.0, 100.0 * speech / dur):.0f}% speech") + + +def _free_decode_buffers(vox): + """Release a finished decode's MLX buffers before an alignment. They are + dead by then, and the aligner's forward runs on the same GPU -- leaving + them resident is exactly the co-residency MIN_HEADROOM_GB is meant to + prevent.""" + try: + vox._mx.clear_cache() + except Exception: + pass + + +def _transcribe_guarded(vox, audio, language, log_cb, label, depth=0, token_cb=None, + turns=None, align_cb=None, want_lang=None): + """Transcribe one pass and repair it if it comes back unusable. + + Two things make a pass unusable, and both take the same repairs. A + repetition loop is the common one. The other is a *translated* pass: + Voxtral is not a pure ASR model -- speech translation is an advertised + capability, and `[TRANSCRIBE]` fixes only that text comes out, not which + language it is in. Measured over six runs of one German recording, four of + them shipped 30 minutes of English from a chunk that had no loop at all, so + a bad language has to make a pass bad here, where the rungs are, rather + than in a second repair path beside them. `want_lang` is the language the + caller expects, or None while it does not know yet. The two defects share + the rungs but not all of them: a pass bad for its language alone skips the + prefix rung (there is no loop to cut off) and never reaches the penalty + rung; after the gentle retry and one split (or, for a window too short to + split, the other temperatures), what still reads as another language is + taken to be spoken in it. + + Repair order, gentlest first: + + 1. Greedy with the _LoopBreaker armed. Clean passes stay bit-identical; + a loop is usually repaired in place at zero extra cost. + 2. Keeping the clean prefix and resuming after it (needs `align_cb`): the + text before the loop is the model's best greedy output, so this is the + only rung that repairs a pass without regenerating what it got right. + It is also the cheapest -- one alignment plus a decode of the remainder, + instead of a decode of the whole window. + 3. Low-temperature sampling (Whisper's own fallback): barely deviates + from greedy, breaks the loop attractor, keeps repeated words. + 4. Splitting -- the loop is a long-generation effect, shorter pieces + often come out clean at plain greedy. The cut prefers a speaker-turn + boundary from the diarization (`turns`, window-relative seconds): + context within a turn stays intact, across a change it matters least. + Without a usable boundary the quietest frame near the middle is used, + as before. + 5. Stronger sampling, then the windowed repetition penalty as the very + last resort, because a penalty silently deletes meaningful repeated + words (a doubled negation flips the meaning of the sentence). + + Failed attempts are cheap: the breaker aborts a hopeless generation after + ~3 loop windows instead of running to max_new_tokens. + """ + dur = len(audio) / SAMPLE_RATE + # 20 tokens/s is ~4.3x reserve: German runs 4.64 text tokens/s measured + # (docs/voxtral-benchmarks.md, section 2). The budget bounds only the + # generation loop -- a high value reserves no memory. + max_new = min(32768, int(dur * 20) + 512) + loop = "repetition loop" + + def attempt(temperature=0.0, seed=None, penalty=1.0): + """(text, defect, info): the defect is None for a usable pass, else + what the log calls it -- `loop`, or the translated-pass description.""" + info = {} + text = vox.transcribe_array(audio, language, max_new_tokens=max_new, + repetition_penalty=penalty, token_cb=token_cb, + temperature=temperature, seed=seed, info=info) + # A gave-up attempt is truncated mid-loop; the full-text detector may + # miss that (a late loop gets diluted), so flag it explicitly. + if info.get("loop_gave_up", False) or _looks_degenerate(text): + return text, loop, info + got = _other_language(text, want_lang) + defect = f"translated pass ('{got}' instead of '{want_lang}')" if got else None + return text, defect, info + + # Fallback candidates for the case where no rung resolves the loop. + # "Shorter is less degenerate" only holds between attempts that actually + # cover the whole window. When the loop breaker gives up, _consume_tokens + # stops pulling tokens mid-stream, so that attempt is the shortest by + # construction and used to win every comparison -- measured, a 148-word + # stump shipped in place of a 2733-word near-complete transcript, and the + # rest of the chunk's speech was gone with only a log line to show for it. + # Partial results are therefore only used when nothing else was produced. + candidates = [] # (text, covers_only_part_of_the_window) + + def keep(candidate, partial=False): + if candidate: + candidates.append((candidate, partial)) + + def best(): + whole = [c for c, part in candidates if not part] + return min(whole or [c for c, _ in candidates], key=len) if candidates else "" + + text, defect, info = attempt() + if info.get("loop_kicks") and not defect: + _log(log_cb, "info", f"{label}: repetition loop broken in place " + f"({info['loop_kicks']} intervention(s)).") + if not defect: + return text + keep(text, bool(info.get("loop_gave_up"))) + + # Keep what the pass got right. This runs before any full re-decode: it is + # cheaper (one alignment plus the remainder) and it is the only rung that + # preserves the greedy output for the clean part instead of re-rolling it. + # A pass that is bad only for its language has no loop to cut off. + if align_cb is not None and depth < LOOP_SPLIT_MAX_DEPTH and defect == loop: + # The looping decode is typically the longest of the job. + _free_decode_buffers(vox) + salvaged, why_not = _salvage_prefix(text, audio, align_cb) + if salvaged is None: + _log(log_cb, "info", f"{label}: cannot keep the clean part " + f"({why_not}); retrying the whole window.") + else: + prefix, cut = salvaged + cut_sec = cut / SAMPLE_RATE + rest_sec = dur - cut_sec + if rest_sec < 1.0: + # Every other rung is gated on its result not looking + # degenerate; this exit returned unchecked, so a prefix that is + # itself degenerate shipped as a success and never escalated. + # (_looks_degenerate's compression arm is not bounded by the + # cycle length _degenerate_span searches for, so it can fire on + # a prefix the span finder considers clean.) + if not _looks_degenerate(prefix): + _log(log_cb, "info", + f"{label}: repetition loop at the very end; " + f"keeping the clean first {cut_sec:.0f}s.") + return prefix + _log(log_cb, "info", + f"{label}: the clean part still looks degenerate; " + f"retrying the whole window.") + keep(prefix, partial=True) # covers only up to the cut + else: + _log(log_cb, "warn", f"{label}: repetition loop after {cut_sec:.0f}s; " + f"keeping that part and re-transcribing the " + f"remaining {rest_sec:.0f}s.") + rest = _transcribe_guarded(vox, audio[cut:], language, log_cb, label, + depth + 1, token_cb, + _clip_turns(turns, cut_sec, dur), align_cb, + want_lang) + joined = f"{prefix} {rest}".strip() + if not _halves_disagree(prefix, rest, log_cb, label, + "the kept part", "the remainder") \ + and not _looks_degenerate(joined): + return joined + keep(joined) + + temps = list(RETRY_TEMPERATURES) + temperature, seed = temps.pop(0) + _log(log_cb, "warn", f"{label}: {defect}; retrying with gentle " + f"sampling (temperature={temperature}).") + retry, rdefect, rinfo = attempt(temperature=temperature, seed=seed) + if not rdefect: + return retry + keep(retry, bool(rinfo.get("loop_gave_up"))) + + # Neither decode looped; both are bad only because they read as another + # language. Perturbing the decode is what fixed both reproducible cases + # (this rung; measured above `_other_language`), and which language wins + # also hangs on the exact window length, which the split changes. So a + # window gets the split once, at the top level, and its halves stop here; + # one too short to split gets the remaining temperatures instead. What + # still reads as another language after that is taken to be spoken in it -- + # an English passage on Auto, or a wrong pinned language -- where climbing + # on to the penalty rung cost 60 decodes of a 600 s window and then deleted + # real repeated words. transcribe() reports the mismatch. + splittable = depth < LOOP_SPLIT_MAX_DEPTH and dur >= 2 * LOOP_SPLIT_MIN_SEC + language_only = loop not in (defect, rdefect) + if language_only and depth > 0: + _log(log_cb, "info", + f"{label}: {rdefect} again at temperature={temperature}, without a " + f"loop; keeping the first decode rather than repairing a language " + f"the audio may really be in.") + return text + + if splittable: + cut = _turn_gap_split(audio, turns) + how = "at a speaker change" if cut is not None else "at the quietest pause" + if cut is None: + cut = _quietest_split(audio) + cut_sec = cut / SAMPLE_RATE + _log(log_cb, "warn", f"{label}: unresolved, splitting the chunk {how} " + f"and retrying ({dur:.0f}s -> {cut_sec:.0f}s + {dur - cut_sec:.0f}s).") + left = _transcribe_guarded(vox, audio[:cut], language, log_cb, label, + depth + 1, token_cb, + _clip_turns(turns, 0, cut_sec), align_cb, want_lang) + right = _transcribe_guarded(vox, audio[cut:], language, log_cb, label, + depth + 1, token_cb, + _clip_turns(turns, cut_sec, dur), align_cb, want_lang) + joined = f"{left} {right}".strip() + if language_only: + # Each half has had its own two decodes at a new window length and + # kept what it read as; a half that came back in the wanted + # language is the repair, and one that did not is taken as spoken. + return text if _looks_degenerate(joined) else joined + if not _halves_disagree(left, right, log_cb, label, + "the first half", "the second half") \ + and not _looks_degenerate(joined): + return joined + keep(joined) + + # Escalating past the gentle stages: log what the diarization saw in this + # window -- it tells a music/no-speech hallucination apart from a + # repetitive many-speaker exchange without digging through the audio. + if turns: + _log(log_cb, "info", f"{label}: diarization profile of this window: " + f"{_turn_profile(turns, dur)}.") + + for temperature, seed in temps: + _log(log_cb, "warn", f"{label}: unresolved; retrying with " + f"temperature={temperature}.") + retry, rdefect, rinfo = attempt(temperature=temperature, seed=seed) + if not rdefect: + return retry + keep(retry, bool(rinfo.get("loop_gave_up"))) + + if language_only: + _log(log_cb, "info", + f"{label}: still reads as another language at every temperature; " + f"keeping the first decode rather than repairing a language the " + f"audio may really be in.") + return text + + # Everything gentler failed. Fall back to the gentlest penalty that works, + # because a stronger one starts deleting meaningful repeated words. + for penalty in RETRY_REPETITION_PENALTIES: + _log(log_cb, "warn", f"{label}: still looping; retrying with " + f"repetition_penalty={penalty}. Note that a penalty can drop " + f"meaningful repeated words.") + retry, rdefect, rinfo = attempt(penalty=penalty) + if not rdefect: + return retry + keep(retry, bool(rinfo.get("loop_gave_up"))) + _log(log_cb, "warn", f"{label}: could not resolve the loop; keeping the " + f"shortest attempt that covers the whole window.") + return best() + + +# A sentence is a run up to and including terminal punctuation; the final +# alternative captures a trailing run that has no terminal punctuation (the last +# pass of a file, or text Voxtral emits unpunctuated) as ONE fragment. An earlier +# `\S+$` fallback matched only the last whitespace token, silently dropping every +# word between the last period and the end of the text. +_SENT_SPLIT = re.compile(r"[^.!?]+[.!?]+|[^.!?]+$", re.UNICODE) + + +# --------------------------------------------------------------------------- # +# The log-Mel clamp floor: a percentile instead of the maximum +# --------------------------------------------------------------------------- # +# Every reference log-Mel path (transformers, mlx-audio, transcribe.cpp and +# mlx-voxtral alike) clamps the spectrogram at `log_max - 8`, with log_max the +# maximum over the *whole* input, so one loud cell sets the floor for the +# entire pass: a 0.1 s transient 28 dB over the speech peak costs a measured +# +1.52 WER points [+0.78, +2.26] over 50 min of VoxPopuli streams. A +# percentile in place of the maximum removes that (+0.01 [-0.03, +0.06]) and +# costs nothing on clean material (-0.12 [-0.85, +0.49], same streams, +# paired); it can only lower the floor, never raise it. The case against -- +# the model was trained on the maximum-clamped input -- and the measurements +# are in docs/voxtral-mel-clamp-floor.md. +# +# On clean material the percentile is not a critical value: 99, 99.9 and +# 99.99 are indistinguishable from the maximum and from each other. Under a +# transient it is, because the transient's own cells sit at the top of the +# distribution and displace the statistic by their count: the floor follows a +# 100 ms full-scale noise burst (~1300 cells) by 7 dB at 99.9 on a 60 s pass, +# whose top 0.1 % is only 768 cells, and by 0.75 dB at 99. 99.99 is unusable +# for the same reason. The defence is count-limited either way: broadband +# noise that fills more than the top percent of a pass (roughly 0.6 s on a +# 60 s pass) is treated as signal and raises the floor like it always did. +# The one regime with a thin margin is a pass that is mostly silence: the +# percentile then sits deeper in the speech cells (24-29 dB below the maximum +# against 20-27 on dense streams), and 50 % room tone between the +# utterances measured +# +0.24 [-0.03, +0.55] -- not demonstrable, but close, and capping the +# floor to fix it was measured and rejected (see MEL_FLOOR_CAP). The 8.0 is +# librosa's `top_db=80` and is the range the model was trained on -- it +# stays. +MEL_FLOOR_PERCENTILE = 99.0 +MEL_FLOOR_RANGE = 8.0 +# Optional lower bound on the floor, in log10 units below the maximum (2.0 = +# 20 dB): `max(percentile, log_max - cap) - 8`. Measured at 20 and 25 dB over +# all four regimes and REJECTED: a cap hangs off the maximum, so a knock +# lifts it along -- +0.81 [+0.18, +1.57] at 20 dB where the uncapped floor +# costs +0.01 -- while buying only +0.24 -> +0.05 on half-silent streams, +# where no interval excludes zero (docs/voxtral-mel-clamp-floor.md, +# section 5). None stays the +# production value; the parameter exists so the measurement scripts can +# drive the arm (spec `99c20` in docs/scripts). +MEL_FLOOR_CAP = None + + +def clamp_log_mel(log_spec, pct=MEL_FLOOR_PERCENTILE, real_frames=None, + cap=MEL_FLOOR_CAP): + """Clamp an unclamped [frames, mels] log-Mel spectrogram at + `percentile(pct) - 8` and apply the reference affinity `(x + 4) / 4`; + with `cap`, the statistic never sits more than `cap` below the maximum. + + The percentile is taken over the first `real_frames` frames only (all of + them by default): the block is zero-padded to a 30 s multiple, and padding + cells sit at the -10 minimum, so a percentile over the whole block would + drift downwards with the amount of padding -- 18 dB lower on a 5 s clip + than on the same clip unpadded. The clamp itself covers every frame. The + maximum of a padded block is never in a padding-only frame, so this + changes nothing at pct=100. + + float32 throughout, with numpy scalars so the dtype holds under both the + legacy and the NEP 50 promotion rules: with pct=100 the result must be + bit-identical to mlx-voxtral's own `maximum(x, max(x) - 8)`, which is how + tests/test_mel_floor.py proves that only the source of the floor changed. + """ + import numpy as np + x = np.asarray(log_spec, dtype=np.float32) + stat = x if real_frames is None else x[:real_frames] + top = np.float32(np.percentile(stat, pct)) + if cap is not None: + top = max(top, stat.max() - np.float32(cap)) + floor = top - np.float32(MEL_FLOOR_RANGE) + return (np.maximum(x, floor) + np.float32(4.0)) / np.float32(4.0) + + +class _PercentileFloorFeatures: + """Drop-in for mlx-voxtral's VoxtralFeatureExtractor on the one input the + engine hands it -- a 16 kHz mono float32 array -- with the clamp floor from + `clamp_log_mel` instead of the maximum. Installed on the processor by + _Voxtral.__init__; the library itself is not patched. + + The unclamped spectrogram is built from the library's own primitives (the + same window, STFT, filter bank and log as its `log_mel_spectrogram`), not + from that function's `global_max` lever. The lever only hands out the + affine spectrogram `(x + 4) / 4`, which loses mantissa bits wherever + x > -2, and from those the reference floor cannot be reproduced exactly on + quiet material: with log_max in [-2, 0) one input in eight gets a floor + one bit off, and with it every clamped cell (measured on 300 synthetic + signals: 12 %). Bit-identity at pct=100 is the proof that only the floor + changed, so the spectrogram has to be exact. + """ + + def __init__(self, pct=MEL_FLOOR_PERCENTILE, cap=MEL_FLOOR_CAP): + self.pct = pct + self.cap = cap + + def __call__(self, raw_speech, sampling_rate=SAMPLE_RATE, **kwargs): + import numpy as np + import mlx.core as mx + from mlx_voxtral.audio_processing import ( + N_FFT, N_FRAMES, N_MELS, N_SAMPLES, HOP_LENGTH, + get_mel_filters, hanning, pad_to_multiple, stft_mlx) + # The library would resample or downmix here; the engine never hands + # it anything but 16 kHz mono (None means "already 16 kHz" there too). + if sampling_rate not in (None, SAMPLE_RATE): + raise ValueError(f"expected {SAMPLE_RATE} Hz audio, got {sampling_rate}") + audio = np.asarray(raw_speech, dtype=np.float32) + if audio.ndim != 1: + raise ValueError(f"expected mono audio, got shape {audio.shape}") + # One spectrogram over the whole (30 s-padded) input, split afterwards, + # as the reference specifies (arXiv:2507.13264 section 2.1). + padded = pad_to_multiple(mx.array(audio), N_SAMPLES) + n_chunks = padded.shape[0] // N_SAMPLES + freqs = stft_mlx(padded, hanning(N_FFT), nperseg=N_FFT, + noverlap=N_FFT - HOP_LENGTH)[:-1] + mel = (mx.abs(freqs) ** 2) @ get_mel_filters(N_MELS).T + log_spec = np.array(mx.log10(mx.maximum(mel, 1e-10))) # [frames, mels] + # Frames that contain any input sample (the STFT is centred, so the + # window reaches N_FFT // 2 samples past the last one). + real_frames = -(-(len(audio) + N_FFT // 2) // HOP_LENGTH) + feats = clamp_log_mel(log_spec, self.pct, real_frames, self.cap).T + feats = feats.reshape(N_MELS, n_chunks, N_FRAMES).transpose(1, 0, 2) + return {"input_features": mx.array(feats)} + + +# --------------------------------------------------------------------------- # +# First-use downloads +# --------------------------------------------------------------------------- # +# What a first-use download weighs, for the one log line before it starts: a +# silent 15 GB download is indistinguishable from a hang. Sizes of the +# published snapshots; every wav2vec2-large aligner is ~1.3 GB of weights. +_DOWNLOAD_GB = { + VOXTRAL_MODELS["voxtral-mini-8bit"]: 5.6, + VOXTRAL_MODELS["voxtral-small-4bit"]: 15, +} +_ALIGNER_DOWNLOAD_GB = 1.3 + + +def _needs_download(repo): + """True when `repo` is a hub id with nothing of it in the local Hugging + Face cache. False whenever that cannot be told, so the log never announces + a download that is not happening. Only the config is looked for, so a + download interrupted after its first files resumes unannounced: which + weight files a snapshot has differs from model to model.""" + if os.path.isdir(str(repo)): + return False + try: + from huggingface_hub import try_to_load_from_cache + return not isinstance(try_to_load_from_cache(str(repo), "config.json"), str) + except Exception: + return False + + +def _hub_offline(): + try: + from huggingface_hub import constants + return bool(constants.HF_HUB_OFFLINE) + except Exception: + return False + + +def _hub_reachable(): + """Whether Hugging Face answers at all -- any HTTP answer counts.""" + try: + import httpx + from huggingface_hub import constants + httpx.head(constants.ENDPOINT, timeout=5) + return True + except Exception: + return False + + +def _unfetchable(repo): + """True when `repo` is not downloaded and cannot be fetched right now.""" + return _needs_download(repo) and (_hub_offline() or not _hub_reachable()) + + +def _not_fetchable_error(what, repo, size_gb): + size = f" (~{size_gb:g} GB)" if size_gb else "" + if _hub_offline(): + how = ("downloads are switched off (HF_HUB_OFFLINE); allow them once so " + "it can be downloaded") + else: + how = ("could not be fetched from Hugging Face (no internet connection, " + "or the site is not reachable); connect once so it can be " + "downloaded") + return RuntimeError(f"The {what} {repo} is not downloaded yet, and {how}{size}.") + + +def _load_from_hub(load, what, repo, size_gb, log_cb): + """`load()`, announced when it will download first. A model that is not + downloaded and cannot be fetched fails with a sentence the user can act on + instead of the library's text; the original stays chained. + + Whether it could be fetched is asked of the hub itself, not read off the + exception: the libraries wrap their errors too differently for that. + transformers turns an aligner's missing files, a 401 for a stale token and + an unwritable cache alike into a bare OSError; a connection that drops + during the download ends in an httpx error, which is no OSError at all; + hf_xet reports a full disk without an errno. So when a missing model fails + to load and the hub does not answer, the model could not be fetched; when + it does answer, the library's own error says what else went wrong. + """ + size = f" (~{size_gb:g} GB)" if size_gb else "" + missing = _needs_download(repo) + if missing and not _hub_offline(): + _log(log_cb, "info", f"Downloading the {what} {repo}{size}; this happens " + f"only once.") + try: + from httpx import HTTPError as _TransportFailure + except Exception: + _TransportFailure = OSError + try: + return load() + except (OSError, _TransportFailure) as exc: # what fetching can raise + if not missing or not (_hub_offline() or not _hub_reachable()): + raise + raise _not_fetchable_error(what, repo, size_gb) from exc + + +# --------------------------------------------------------------------------- # +# Voxtral transcription +# --------------------------------------------------------------------------- # +def _cap_mlx_memory(): + """Nudge MLX to release its reusable buffer cache once usage nears physical + RAM, so cached (not active) buffers don't push the working set into swap. + MLX's limit is *soft* (it never hard-fails an allocation), so the real memory + control is the per-pass length picked by _auto_chunk_sec; this only trims + cache retention near the ceiling. Set just below total RAM so it doesn't + throttle a normal generate pass (which is sized to stay under RAM anyway).""" + try: + import mlx.core as mx + total = int(_total_ram_gb() * 1024**3) + mx.set_memory_limit(max(8 * 1024**3, total - 4 * 1024**3)) + except Exception: + pass + + +class _Voxtral: + """The loaded mlx-voxtral model, driven through `mlx_lm.generate_step`. + + `transcribe_array` is the only entry point; everything else prepares the + prompt, resolves this build's stop tokens or consumes the token stream.""" + + def __init__(self, repo): + import mlx.core as mx + import mlx.nn as nn + from mlx_voxtral import load_voxtral_model, VoxtralProcessor + self._mx = mx + _cap_mlx_memory() + self.model, _ = load_voxtral_model(repo, dtype=mx.bfloat16) + self.proc = VoxtralProcessor.from_pretrained(repo) + self.proc.feature_extractor = _PercentileFloorFeatures() + self._STOP_TOKENS = self._resolve_stop_tokens() + + # Adapter so mlx_lm.generate_step can drive our LM: generate_step calls + # model(tokens, cache=, input_embeddings=) and expects logits, while our + # language_model takes `inputs_embeds` and returns hidden states (the + # lm_head lives on the parent). This bridges both. + class _LMAdapter(nn.Module): + def __init__(self, parent): + super().__init__() + self.language_model = parent.language_model + self.lm_head = parent.lm_head + + def __call__(self, inputs, cache=None, input_embeddings=None): + h = self.language_model(inputs, cache=cache, + inputs_embeds=input_embeddings) + return self.lm_head(h) + + self._lm_adapter = _LMAdapter(self.model) + + # Control-token ids that end a generation: , [/INST] and . + # + # mlx_voxtral.generate_stream defaulted to (2, 4, 32000) up to 0.0.4 and + # called 32000 "a potential padding token" (it defaults to (2, 4, 11) since + # 0.0.5). It is not one: Voxtral's Tekken vocabulary has 131072 entries of + # which only the first 1000 are control tokens, is id 11, and id 32000 + # is the ordinary text token " Capital". Treating it as a stop truncated + # every pass that transcribed that word ("Venture Capital") mid-sentence and + # threw away the rest of the chunk -- silently, because the + # truncated text is clean prose, so neither _looks_degenerate nor the loop + # breaker flags it and no retry rung fires. These values are only the + # fallback; __init__ reads the real ids off the processor. + _STOP_TOKENS = (2, 4, 11) + + def _resolve_stop_tokens(self): + """The build's own / [/INST] / ids, or the class fallback.""" + ids = getattr(self.proc, "_special_token_ids", None) or {} + stops = [] + for key in ("eos", "inst_end", "pad"): + value = ids.get(key) if hasattr(ids, "get") else None + if isinstance(value, int) and value not in stops: + stops.append(value) + return tuple(stops) or _Voxtral._STOP_TOKENS + + def _consume_tokens(self, token_stream, token_cb=None, breaker=None): + """Collect ids from a greedy token stream up to the first stop token + (which is dropped, matching decode(skip_special_tokens=True)). The stream + is already length-bounded by generate_step's max_tokens. + + This deliberately does NOT replicate generate_stream's 10-identical-token + backstop. That backstop cuts a single-token repetition loop off after only + 10 tokens, which lands *under* _looks_degenerate's thresholds (a 12-word + run, or a >4 compression ratio) -- so the degenerate pass would slip + through un-flagged, keeping truncated garbage and losing everything after + the loop. Letting the loop run instead lets _looks_degenerate catch it + (the compression-ratio net) and fire the split/penalty retry, which + recovers clean text. In practice the backstop almost never fired anyway: + real Voxtral loops repeat a *word* ("Jetzt. Jetzt.") whose tokens cycle, + so consecutive-identical-token never reached 10. Output is therefore + identical to model.generate() on every normal pass and better (retried + rather than truncated) on the rare single-token loop.""" + stops = self._STOP_TOKENS + out = [] + last_beat = 0.0 + for t in token_stream: + t = int(t) + if t in stops: + break + # The in-generation loop breaker has given up on this attempt: + # stop wasting tokens, the caller's retry ladder takes over. + if breaker is not None and breaker.gave_up: + break + out.append(t) + # Liveness heartbeat: Voxtral emits a whole pass at once, so without + # this the log/progress sits silent for the entire (possibly minutes- + # long) decode. Throttled to ~1.5 s so it never floods the queue. + if token_cb is not None: + now = time.monotonic() + if now - last_beat >= 1.5: + last_beat = now + try: + token_cb(len(out)) + except Exception: + pass + return out + + def _merged_embeddings(self, mi): + """Audio embeddings written into the prompt at the [AUDIO] placeholders. + + Kept rather than calling the library's `_merge_input_embeddings`, which + since 0.0.6 does the same thing (bit-identical on the batch of 1 that + production uses, same peak memory, same wall clock -- measured on a + 300 s prompt): it is private API, and this copy is testable without the + model, so CI covers it where the model-gated smoke test cannot. + + The dtype promotion is load-bearing. `embed_tokens` returns bf16 while + the projector returns float32 -- its weights are bf16, but the log-Mel + features are float32 and MLX promotes the output to the input's dtype + (measured on voxtral-mini-8bit). Scattering into the bf16 array would + silently round every audio embedding away -- a change that looks like + nothing and shows up only as different logits. + """ + mx = self._mx + import numpy as np + model = self.model + input_ids = mi["input_ids"] + features = mi.get("input_features") + if features is None: + return model.embed_tokens(input_ids) + audio_embeds = model.get_audio_embeds(features) + embeds = model.embed_tokens(input_ids).astype(audio_embeds.dtype) + ids = np.array(input_ids) + # get_audio_embeds returns one stream ([1, n, hidden]) however many 30 s + # chunks went in, so a multi-row prompt shares it. Any other count is + # refused here rather than indexed: reading past the end of an mlx array + # yields *zeros* instead of raising, which would replace the audio with + # silence -- invisible in the text, visible only as wrong logits. + n_audio = audio_embeds.shape[0] + if n_audio not in (1, ids.shape[0]): + raise ValueError( + f"{n_audio} audio streams for a prompt of {ids.shape[0]} rows; " + f"expected one shared stream or one per row.") + for i in range(ids.shape[0]): + pos = np.where(ids[i] == model.config.audio_token_id)[0] + if pos.size == 0: + continue + if pos.size != audio_embeds.shape[1]: + raise ValueError( + f"Batch {i}: {audio_embeds.shape[1]} audio embeddings for " + f"{pos.size} [AUDIO] placeholders in the prompt.") + embeds[i, mx.array(pos)] = audio_embeds[i if n_audio > 1 else 0] + return embeds + + def _fast_generate(self, mi, max_new_tokens, token_cb=None, + temperature=0.0, seed=None, breaker=None): + """Decode through the maintained mlx_lm.generate_step, returning the + generated token ids ([1, n]). + + At temperature 0.0 the output matches model.generate() (same greedy + argmax) on every normal pass; it is faster and -- crucially -- lower peak + memory on long passes, because generate_step processes the (large audio) + prompt in prefill_step_size chunks instead of one forward. Measured on + mini-8bit at a 600s prompt: ~7% faster and ~18% lower peak; near + break-even on short prompts, where memory is not the constraint anyway. + (The one intentional divergence is on a single-token repetition loop -- + see _consume_tokens.) + + temperature > 0 samples through mlx_lm's make_sampler (the retry + ladder's gentle-sampling stages); a fixed `seed` keeps those retries + reproducible. `breaker` is an optional _LoopBreaker armed as a logits + processor. The rare penalty path (repetition_penalty > 1) stays on the + library implementation. + """ + mx = self._mx + from mlx_lm.generate import generate_step + from mlx_lm.models.cache import KVCache + model = self.model + + sampler = None # greedy argmax, matching temperature 0.0 + if temperature > 0: + from mlx_lm.sample_utils import make_sampler + if seed is not None: + mx.random.seed(seed) + sampler = make_sampler(temp=temperature) + + # [seq, hidden] merged audio+text embeddings; generate_step adds the batch. + embeds = self._merged_embeddings(mi)[0] + cache = [KVCache() for _ in range(len(model.language_model.layers))] + stream = generate_step(prompt=mx.array([], dtype=mx.int32), + input_embeddings=embeds, model=self._lm_adapter, + max_tokens=max_new_tokens, sampler=sampler, + logits_processors=[breaker] if breaker else None, + prompt_cache=cache) + toks = self._consume_tokens((t for t, _ in stream), token_cb=token_cb, + breaker=breaker) + return mx.array([toks], dtype=mx.uint32) + + def transcribe_array(self, audio, language, max_new_tokens=4096, + repetition_penalty=1.0, token_cb=None, + temperature=0.0, seed=None, info=None): + """Transcribe one audio array to text. + + `info`, if given, is a dict that receives loop-breaker telemetry for + the retry ladder: {"loop_kicks": int, "loop_gave_up": bool}. + """ + inp = self.proc.apply_transcrition_request(audio=audio, language=language, + sampling_rate=SAMPLE_RATE) + mi = {"input_ids": inp.input_ids, "input_features": inp.input_features} + if getattr(inp, "attention_mask", None) is not None: + mi["attention_mask"] = inp.attention_mask + # temperature=0.0 and NO repetition penalty, matching Mistral's reference + # transcription request. mlx_voxtral defaults repetition_penalty to 1.2, + # which is a chat default: it divides the logit of every token seen in + # the last 20 tokens, and in verbatim speech the most-repeated tokens are + # punctuation and function words. Measured on a 10 min German podcast, + # the default cost 27% of all commas (426 -> 312 over a 20 min podcast, + # 10.51 -> 7.89 per 100 words) and swallowed real repetitions + # ("sehr, sehr" -> "sehr"), which is what made + # transcripts read worse than Whisper's. Generation stays bounded by + # max_new_tokens. + if repetition_penalty == 1.0 and "attention_mask" not in mi: + # Fast path: identical tokens at temperature 0, faster and lower + # peak memory on long passes. See _fast_generate. Skipped when a + # padding mask is present (the fast path assumes a single unpadded + # sequence and the model's internal causal mask). The loop breaker + # rides along and repairs a repetition loop in place; if it gives + # up, the truncated attempt is reported degenerate via `info` so + # the retry ladder never mistakes it for a clean short pass. + breaker = _LoopBreaker() + gen = self._fast_generate(mi, max_new_tokens, token_cb=token_cb, + temperature=temperature, seed=seed, + breaker=breaker) + if info is not None: + info["loop_kicks"] = breaker.kicks + info["loop_gave_up"] = breaker.gave_up + else: + # Pass the resolved stop ids explicitly so both branches stop on + # the ids this build's processor reports (see `_STOP_TOKENS` for + # the history); the library's default is hard-coded. + out = self.model.generate(**mi, max_new_tokens=max_new_tokens, + temperature=temperature, + repetition_penalty=repetition_penalty, + stop_tokens=list(self._STOP_TOKENS)) + gen = out[:, inp.input_ids.shape[1]:] # drop the prompt + return self.proc.decode(gen[0], skip_special_tokens=True).strip() + + +# --------------------------------------------------------------------------- # +# CTC forced alignment (text + audio -> word timestamps) +# --------------------------------------------------------------------------- # +def _align_device(): + """Torch device for the alignment forward pass. + + The aligner used to be the last piece of this module on the CPU, in a + module that only runs on Apple Silicon. On MPS the emissions are the same + -- argmax identical on every frame of a 300 s reference, worst log-prob + deviation ~5e-3 -- and the pass is measurably faster: 14.85 s to 4.60 s for + that clip, which is most of the aligner's runtime since the forward is ~97% + of it. fp16 would be faster again and is deliberately not used: it moves + 0.067% of argmaxes and up to 1.68 in log-prob, enough to shift a word + boundary, and this change is meant to be free of effect. + + Repeats `pyannote_mp_worker.pyannote_proc_entrypoint`'s probe rather than + sharing it, macOS floor included -- MPS before 12.3 is not something to + rely on. There is no shared helper to call: `utils.py` is deliberately + torch-free. Callers that need to pin a device pass `_Aligner(device=...)`. + """ + try: + import platform + import torch + if platform.system() != "Darwin": + return "cpu" + if platform.mac_ver()[0] < "12.3": + return "cpu" + return "mps" if torch.backends.mps.is_available() else "cpu" + except Exception: + return "cpu" + + +class _Aligner: + # Set from the model's vocabulary size once one is loaded. None means the + # emission has no room for a wildcard column, so `_tokenize` falls back to + # dropping what it cannot spell -- which is also what a hand-built _Aligner + # in the tests gets. + wild = None + # Recursion cap for the oversized-window split. Deep enough that halving + # brings a genuinely too-big window under FORCED_ALIGN_MAX_CELLS (the cell + # count shrinks ~4x per level), while still terminating. + MAX_SPLIT_DEPTH = 12 + # A *density* problem is not fixed by halving: the audio is cut at the + # words' character share, so tokens-per-frame comes out the same in both + # halves and the test re-fires at every level. Measured, that ran the tree + # to the full depth and pushed 10-13x the chunk's audio through wav2vec2 + # (~17-21 min of CPU for a 1500 s chunk) to reach leaves that end in + # _spread anyway. Two levels still catch the case where the pause snap + # shifts the character proportion enough to help. + MAX_DENSE_SPLIT_DEPTH = 2 + # Pieces `align_prefix` may chain. The cell cap allows ~440 s of German + # speech per piece against a 1500 s window (and more as the remaining audio + # shrinks), so a prefix filling even the largest chunk we produce needs a + # handful. The cap only stops a pathological chain that advances by a word + # or two per piece from paying for a full-window DP each time. + MAX_PREFIX_PIECES = 16 + # How far the next piece starts before the previous one ended, so its first + # word is whole even when CTC let the previous word run long. + PREFIX_PIECE_BACKOFF_SEC = 0.5 + + def __init__(self, model_name=ALIGN_MODEL_MULTILINGUAL, device=None): + import torch + from transformers import Wav2Vec2ForCTC, Wav2Vec2Processor + self._torch = torch + self.proc = Wav2Vec2Processor.from_pretrained(model_name) + self.model = Wav2Vec2ForCTC.from_pretrained(model_name).eval() + # Left in its loaded dtype on purpose -- see _align_device on fp16. + self.device = device or _align_device() + if self.device != "cpu": + try: + self.model = self.model.to(self.device) + except Exception as exc: + logger.warning("Alignment model will not move to %s (%s); " + "staying on CPU.", self.device, exc) + self.device = "cpu" + self.vocab = self.proc.tokenizer.get_vocab() + # The CTC blank is the index the network actually emits between labels, + # which is NOT reliably the token spelled "": the multilingual MMS + # aligner's vocab is {"": 0, "": 1, ...} and it emits blanks + # on 0, so reading "" picked an index that model never produces -- + # forced_align could then never park on blank during a pause and + # stretched every word to meet its neighbour (measured: inter-word gaps + # all collapsed to zero, words up to 0.8 s too long, scores -3.9 instead + # of -0.09). config.pad_token_id carries the trained blank for both + # families (0 for the per-language jonatasgrosman models and for MMS). + self.blank = getattr(self.model.config, "pad_token_id", None) + if self.blank is None: + self.blank = self.vocab.get("", self.vocab.get("", 0)) + self.delim = self.vocab.get("|", None) + # One column past the model's own vocabulary; see the class attribute. + self.wild = int(getattr(self.model.config, "vocab_size", 0) or 0) or None + + def _emission(self, audio): + """Windowed wav2vec2 log-prob emissions [T, vocab] (bounded memory), + as a float32 numpy array -- the DP downstream is numpy.""" + import numpy as np + torch = self._torch + # Zero-copy view of the (already float32) audio buffer instead of a + # duplicate allocation per alignment call. + wav = torch.from_numpy(np.ascontiguousarray(audio, dtype=np.float32)) + win = int(EMISSION_WINDOW_SEC * SAMPLE_RATE) + # wav2vec2's conv feature extractor raises on inputs shorter than its + # receptive field (~400 samples / 25 ms). A trailing remainder that small + # carries negligible alignment signal, so skip it rather than crash the + # whole job -- the guard is `< min_win`, not just the empty case. + min_win = int(0.025 * SAMPLE_RATE) + try: + parts = self._forward_windows(wav, win, min_win) + except Exception as exc: + # An unsupported op on this backend must not cost the whole job a + # transcript. Fall back once, for good, and say so. The work done + # before the failure is discarded, which is bounded by one chunk + # and happens at most once per aligner. + if self.device == "cpu": + raise + logger.warning("Alignment on %s failed (%s); falling back to CPU.", + self.device, exc) + self.device = "cpu" + self.model = self.model.to("cpu") + parts = self._forward_windows(wav, win, min_win) + if not parts: + return None + out = torch.cat(parts, dim=0) if len(parts) > 1 else parts[0] + # A view for the float32 models this ships with (inference-mode CPU + # tensors); .float() only does work for a half-precision checkpoint, + # whose bfloat16 .numpy() would otherwise raise outside every try. + return out.float().numpy() + + def _forward_windows(self, wav, win, min_win): + """Log-prob emissions per window, always returned on the CPU: the + DP that consumes them is numpy and reads host memory.""" + torch = self._torch + parts = [] + with torch.inference_mode(): + for i in range(0, len(wav), win): + seg = wav[i:i + win] + if seg.numel() < min_win: + continue + # Zero mean, unit variance per window: what the aligners were + # trained on (do_normalize in the preprocessor config of all four + # checked -- MMS and the English, German and Spanish xlsr-53 -- done + # by Wav2Vec2FeatureExtractor, which we bypass). On the raw signal the + # model scales with the recording level, and a quiet voice gets + # emissions too weak to hold its own words -- the DP, which must + # place every character, then crowds them into the louder + # speaker's time. Measured by realigning the same Voxtral text over + # 23 AMI meetings and 64 CallHome calls: words more than 0.5 s off + # AMI's manual stamps 7.1 % -> 3.1 %, words under the wrong + # speaker after the voice check 2.65 % -> 2.24 % (70 of 82 files + # better); on 23 VoxConverse broadcasts, which took no part and are + # mastered, no effect (+0.05 points, 95 % [-0.06, +0.16]). It + # does not reach a voice 15-20 dB below one that talks + # through the whole window: the window's variance is the loud + # voice's, whatever its gain (the level-matched second alignment in + # _AlignerPool.align_words is for that). Population variance and 1e-7, as the + # extractor has them. A window shorter than `win` -- the rest at + # the end of a pass -- takes its statistics from the `win` samples + # that end with it: on its own, a few hundred ms of room tone + # after the last word would be lifted to speech level. + ref = wav[max(0, i + seg.numel() - win):i + seg.numel()] + seg = (seg - ref.mean()) / torch.sqrt(ref.var(correction=0) + 1e-7) + if self.device != "cpu": + seg = seg.to(self.device) + lg = self.model(seg.unsqueeze(0)).logits[0] + parts.append(torch.log_softmax(lg, dim=-1).cpu()) + return parts + + def _tokenize(self, words, wildcard=False): + """Characters -> target ids. With `wildcard`, a character the model + cannot spell becomes `self.wild` instead of vanishing; the caller must + then have extended the emission with that column.""" + tokens, tok_word = [], [] + for wi, w in enumerate(words): + for ch in w.lower(): + if ch in self.vocab: + tokens.append(self.vocab[ch]) + tok_word.append(wi) + continue + # A mapping that exists but cannot be spelled in THIS vocabulary + # must fall through to the wildcard rather than be treated as + # handled -- otherwise the character vanishes silently and the + # word loses an anchor with nothing in the log to say so. + spelled = [self.vocab[c] + for c in _ALIGN_CHAR_FALLBACKS.get(ch, "") + if c in self.vocab] + if spelled: + tokens.extend(spelled) + tok_word.extend([wi] * len(spelled)) + elif wildcard and self.wild is not None and ch.isalnum(): + tokens.append(self.wild) + tok_word.append(wi) + if self.delim is not None: + tokens.append(self.delim) + tok_word.append(-1) + return tokens, tok_word + + @staticmethod + def _adjacent_repeats(tokens): + """The DP's own rule for the extra frames it needs; one definition.""" + from noScribe import ctc_align + return ctc_align.adjacent_repeats(tokens) + + @staticmethod + def _dp_cells(n_targets, frames): + """Size of the forced-alignment DP: one byte per cell for the backtrace + table, and the time to fill it. Every caller checks it against a cap + before handing the work over -- see FORCED_ALIGN_MAX_CELLS.""" + return frames * (2 * n_targets + 1) + + def _predict_frames(self, n_samples): + """Frames `_emission` would return for `n_samples`, without running it. + + _emission feeds the audio through wav2vec2 in EMISSION_WINDOW_SEC + windows and skips a trailing remainder below the conv receptive field, + so the count follows the feature extractor's conv geometry exactly + (verified bit-exact against the real model from 1 s to 45 s). This only + routes the split decision -- the real emission still decides at the + leaf, so an off-by-a-frame here cannot produce a wrong alignment. + """ + cfg = getattr(getattr(self, "model", None), "config", None) + kernels = tuple(getattr(cfg, "conv_kernel", ()) or ()) + strides = tuple(getattr(cfg, "conv_stride", ()) or ()) + win = int(EMISSION_WINDOW_SEC * SAMPLE_RATE) + min_win = int(0.025 * SAMPLE_RATE) + total = 0 + for i in range(0, n_samples, win): + seg = min(win, n_samples - i) + if seg < min_win: + continue + if kernels and strides: + out = seg + for k, s in zip(kernels, strides): + out = (out - k) // s + 1 + else: + out = seg // 320 # geometry unavailable: ~20 ms frames + total += max(0, out) + return total + + def _spread_loudly(self, why, words, audio, t_offset, tokens, frames, + exc_info=False): + """_spread with the log line that makes it visible. Never silent: a + spread window has plausible-looking times that are wrong by up to + minutes, and without this line it is indistinguishable in the log + from a good alignment. The two silent callers of _spread are the + benign ones -- a window under 0.1 s, or one with no alignable + characters at all.""" + logger.warning( + "Forced alignment %s for a %.0fs window (%d words, %d tokens, " + "%d frames); falling back to evenly spread timestamps.", + why, len(audio) / SAMPLE_RATE, len(words), len(tokens), frames, + exc_info=exc_info) + return self._spread(words, audio, t_offset) + + def _spread(self, words, audio, t_offset): + """Fallback: distribute words evenly (by length) over the audio when + real alignment isn't possible (empty/too-dense text).""" + if not words: + return [] + dur = len(audio) / SAMPLE_RATE + total = sum(len(w) for w in words) or len(words) + out, pos = [], 0.0 + for w in words: + s = t_offset + dur * pos + pos += (len(w) or 1) / total + e = t_offset + dur * pos + out.append({"word": w, "start": s, "end": e, "prob": 0.0}) + return out + + def _stamps_from_spans(self, spans, words, tok_word, fps, t_start, t_end): + """Merged CTC token spans -> [{word,start,end,prob}] for `words`. + + Frames are counted from `t_start` (the time the aligned emission + begins) and `t_end` bounds the trailing fill. None when not a single + word could be placed -- the caller decides what to fall back to. + """ + out, ti = {}, 0 + for sp in spans: + if sp.token == self.blank: + continue + if ti < len(tok_word): + wi = tok_word[ti] + if wi >= 0: + s = t_start + sp.start / fps + e = t_start + sp.end / fps + if wi not in out: + # merge_tokens scores are log probabilities (<= 0); + # whisper_mp_worker puts a plain probability in this + # field, and the module docstring promises the same + # shape. Convert here, at the one place a real score + # enters, so 0.0 keeps its meaning downstream: "not + # actually aligned, spread or interpolated". A genuine + # score of exactly 0.0 now maps to 1.0 rather than + # colliding with that sentinel. + out[wi] = [s, e, math.exp(float(sp.score))] + else: + out[wi][1] = e + ti += 1 + + # Fill words whose characters were all out-of-vocabulary (numbers, + # symbols) by interpolating between aligned neighbours. + n = len(words) + idx = sorted(out) + if not idx: + return None + full = [None] * n + for wi in idx: + full[wi] = out[wi] + # Leading/trailing OOV words: spread them over the audio before the + # first / after the last aligned word (capped at ~0.6 s per word) + # instead of collapsing them to zero-width spans, which would turn + # into zero-duration cues that subtitle players skip or reject. + # A tiny per-word floor so a lead/tail OOV word never collapses to a + # zero-duration cue when the first/last aligned word sits exactly on + # the chunk boundary (step would otherwise be 0). The resulting + # sub-frame overlap into neighbouring audio is harmless for cues. + min_step = 0.02 + if idx[0] > 0: + s1 = out[idx[0]][0] + lead = max(t_start, s1 - 0.6 * idx[0]) + step = max((s1 - lead) / idx[0], min_step) + for i in range(idx[0]): + full[i] = [lead + step * i, lead + step * (i + 1), 0.0] + n_tail = n - (idx[-1] + 1) + if n_tail > 0: + e0 = out[idx[-1]][1] + tail_end = min(t_end, e0 + 0.6 * n_tail) + step = max((tail_end - e0) / n_tail, min_step) + for k, i in enumerate(range(idx[-1] + 1, n)): + full[i] = [e0 + step * k, e0 + step * (k + 1), 0.0] + # Interior OOV words: tile the gap between the two aligned + # neighbours, with the same min_step floor the lead/tail runs use. + # They used to collapse to a zero-width [t, t] span -- the very + # thing min_step was introduced to prevent -- and every digit is + # out of vocabulary in these aligners, so a German year or number + # lands here routinely. When such a word forms a cue of its own + # (it ends in '.', so the cue is flushed around it) the result was + # a zero-duration VTT cue, which the spec forbids and players skip; + # nothing downstream guards against it. + for a, b in zip(idx, idx[1:]): + if b - a > 1: + s0, s1 = out[a][1], out[b][0] + step = max((s1 - s0) / (b - a - 1), min_step) + for k in range(a + 1, b): + t0 = s0 + step * (k - a - 1) + full[k] = [t0, t0 + step, 0.0] + return [{"word": words[i], "start": full[i][0], + "end": full[i][1], "prob": full[i][2]} for i in range(n)] + + def align_words(self, words, audio, t_offset=0.0, depth=0): + """Return [{word,start,end,prob}] for `words` against `audio`. + + The words must span the audio. Text that covers only the front of its + window -- what the loop ladder's prefix salvage produces -- belongs in + `align_prefix`; the split below would cut the audio at the words' + character share and place the later words on audio that never contained + them. + + CTC forced alignment requires at least as many audio frames as target + tokens. Dense speech (or a Voxtral over-generation) in a long chunk can + have more character tokens than frames, which makes forced_align raise. + When that happens we split the words in half, cut the audio at a nearby + pause, and align each half recursively — so we always get a transcript + instead of a crash. As a last resort the words are spread evenly. + """ + from noScribe import ctc_align + if not words: + return [] + if len(audio) < int(0.1 * SAMPLE_RATE): + return self._spread(words, audio, t_offset) + + tokens, tok_word = self._tokenize(words, wildcard=True) + if not tokens: + return self._spread(words, audio, t_offset) + + # Route on a PREDICTED frame count. The emission is only needed in order + # to align, not in order to decide whether to split, and computing it up + # front meant every window that then split paid for a wav2vec2 forward + # pass that was thrown away while each half recomputed its own -- + # measured at 2.00x the necessary forward-pass work (~97 s of CPU per + # 1500 s chunk) on every file past ~13.5 min, where too_big always + # fires. The real frame count still governs at the leaf. + n_frames = self._predict_frames(len(audio)) + # forced_align needs one frame per target token PLUS one for each pair + # of adjacent identical tokens (a CTC path has to put a blank between + # them); the DP enforces exactly that and names the repeat count in + # its error. The old `len(tokens) > n_frames * 0.95` left only ~5.3% + # slack, while German text runs 3.9-6.1% adjacent repeats -- 8.9% with + # the multilingual aligner, and up to 29.7% for number-heavy text, where + # every all-OOV word contributes just a delimiter and those land back to + # back. Windows inside that band passed the check, forced_align raised, + # and the whole window silently degraded to evenly-spread timestamps. + repeats = self._adjacent_repeats(tokens) + too_dense = len(tokens) + repeats > n_frames + # Above FORCED_ALIGN_MAX_CELLS (see its comment): split. Smaller + # windows are also much faster to align. + too_big = self._dp_cells(len(tokens), n_frames) > FORCED_ALIGN_MAX_CELLS + splittable = len(words) > 1 and depth < self.MAX_SPLIT_DEPTH + if too_dense and not too_big and depth >= self.MAX_DENSE_SPLIT_DEPTH: + splittable = False # halving does not reduce density + if too_big and not splittable: + # Cannot split further -- do not run a DP that size. + return self._spread_loudly("skipped, window too big to split further", + words, audio, t_offset, tokens, n_frames) + if not too_big and (not too_dense or not splittable): + emission = self._emission(audio) + if emission is None: + return self._spread_loudly("produced no emission", words, audio, + t_offset, tokens, 0) + if self.wild is not None and self.wild in tokens: + import numpy as np + # A character the model has no letter for -- a digit, a symbol, + # a foreign script. Dropping it left the whole word without an + # anchor, so `_stamps_from_spans` interpolated its time across + # the gap between its neighbours and the word came out too wide. + # One extra column, as good as the best real token at every + # frame, lets the DP place it on the audio instead. Measured on + # the 300 s reference, where "10", "20" and "50" are the only + # such words: identical to the nearest alternative (spelling the + # number out in German) to the millisecond on all three, 80 ms + # from the interpolation on average and 160 ms at worst, and not + # one of the other 847 words moved. The wildcard wins on cost: + # spelling out needs a number speller per language, this needs + # none. `align_prefix` deliberately does NOT use it -- its own + # trailing star is a sentinel it searches the spans for, and a + # second star-like column would make that search ambiguous. + # + # The column scores at least as high as any real token at every + # frame, so it does take some frames from its neighbours, and + # where it takes them from is a guess -- the aligner does not + # know what the word sounds like. Measured adversarially by + # replacing every 20th word of the reference with digits, then + # comparing the untouched words against their true times: + # + # distance to the nearest wildcard word mean worst + # 1 (immediately beside) 31.1 ms 1061 ms + # 2 4.2 ms 300 ms + # 3-4 0.4 ms 40 ms + # 5 and beyond 0.0 ms 0 ms + # + # So it is a seam, not a drift: it decays to exactly nothing + # within five words and never accumulates. Dropping the word + # instead keeps that one neighbour closer (worst 480 ms) but is + # worse on average at every density tested (4.6 vs 3.8 ms here, + # 22.3 vs 13.5 at 20 %, 48.1 vs 30.2 at 50 %). Penalising the + # column was measured as the obvious cure and is not one: at + # -0.25 through -2.0 nats nothing changes at all, and at -4.0 + # the number words break (160 ms off) while the worst neighbour + # stays at 1061 ms. There is nothing here to tune. + nb = [i for i in range(emission.shape[1]) if i != self.blank] + emission = np.concatenate( + [emission, emission[:, nb].max(1, keepdims=True)], axis=1) + n_real = emission.shape[0] + fps = n_real / (len(audio) / SAMPLE_RATE) + # The prediction only routed us here; the real frame count decides + # whether the DP can run at all, and within budget. Spreading + # beats a raise. + if len(tokens) + repeats > n_real: + return self._spread_loudly( + f"skipped, {len(tokens)} tokens + {repeats} repeats need " + f"more frames than the window has", + words, audio, t_offset, tokens, n_real) + if self._dp_cells(len(tokens), n_real) > FORCED_ALIGN_MAX_CELLS: + return self._spread_loudly( + "skipped, over the DP cell budget at the real frame count", + words, audio, t_offset, tokens, n_real) + try: + aligned, scores = ctc_align.forced_align( + emission, tokens, blank=self.blank) + spans = ctc_align.merge_tokens(aligned, scores, blank=self.blank) + except Exception: + return self._spread_loudly("failed", words, audio, t_offset, + tokens, n_real, exc_info=True) + + stamps = self._stamps_from_spans(spans, words, tok_word, fps, + t_offset, + t_offset + len(audio) / SAMPLE_RATE) + if stamps is None: + return self._spread_loudly("placed no word", words, audio, + t_offset, tokens, n_real) + return stamps + + # Too dense or too big: split words in half and audio at their + # character share, cut at a nearby pause, recurse. The share is why this + # method needs the words to span the audio -- on text covering only the + # front of the window it put the later words on audio that never held + # them, and they came back with ordinary scores (measured: the resume + # point landed 152 s past the prefix's true end, and that speech was + # dropped from the transcript). `align_prefix` exists for that case. + mid = max(1, len(words) // 2) + first_chars = sum(len(w) for w in words[:mid]) + mid + total_chars = sum(len(w) for w in words) + len(words) + cut = int(len(audio) * first_chars / max(1, total_chars)) + # snap the cut to the quietest 100 ms frame within +/- 3 s, then keep it + # a valid interior split point + fr = max(1, int(0.1 * SAMPLE_RATE)) + cut = _quietest_frame_near(audio, cut, 3.0) + cut = min(max(cut, fr), len(audio) - fr) + left = self.align_words(words[:mid], audio[:cut], t_offset, depth + 1) + right = self.align_words(words[mid:], audio[cut:], + t_offset + cut / SAMPLE_RATE, depth + 1) + return left + right + + def _prefix_piece(self, words, start, frames): + """How many words from `start` one forced_align call can take against + `frames` frames -- the largest count that stays under the cell cap and + inside the density limit, 0 when not even one word fits. + + Both limits grow monotonically with the word count, so a binary search + over the exact predicate is sound (and cheap: ~log2(n) tokenisations). + """ + # The salvage budget is the smaller of the two in production; the min + # is there so that lowering the hard cap still binds. + cap = min(FORCED_ALIGN_MAX_CELLS, SALVAGE_ALIGN_MAX_CELLS) + + def fits(k): + tokens, _ = self._tokenize(words[start:start + k]) + if not tokens: + return False + n = len(tokens) + 1 # + the trailing star token + if n + self._adjacent_repeats(tokens) > frames: + return False + return self._dp_cells(n, frames) <= cap + + if not fits(1): # also covers no frames / no words + return 0 + lo, hi = 1, len(words) - start + while lo < hi: + mid = (lo + hi + 1) // 2 + if fits(mid): + lo = mid + else: + hi = mid - 1 + return lo + + def align_prefix(self, words, audio, t_offset=0.0): + """Timestamps for words that cover only the FRONT of `audio`. + + This is what the loop ladder's prefix salvage needs: a clean prefix of + a looping pass, mapped onto the audio time where it ends. Two things + make that different from `align_words`, and both were measured on real + emissions (300 s of German, ground truth from the aligner's own greedy + decode, so every word's true frame is known): + + 1. **Plain forced alignment cannot end early.** A CTC path has to + account for every frame, and parking on blank across 215 s of real + speech costs far more (-21905) than smearing the prefix's last words + over it, so the maximum-likelihood path does exactly that: the last + word of a 85 s prefix came back ending at 299.98 s of a 300 s + window, and it came back with a *perfect* score (-0.000), which is + why no guard downstream could ever have caught it. It is not a + tie-break artefact -- the drifted path wins by 802 nats. The fix is + the star token from the MMS/torchaudio partial-transcript recipe: an + extra vocabulary column, as good as the best real token at every + frame, appended once after the prefix's tokens, so the aligner can + say "the rest of this audio is not in my text". With it the same + prefix ends 0.02 s (one frame) from the truth, at every prefix + length from 5% to 100% of the window. + + 2. **A long window blows past FORCED_ALIGN_MAX_CELLS.** `align_words` + answers that by halving the words and cutting the audio at their + character share, which is unsound for a prefix: the share assumes + the words fill the window, so the later words were put on audio that + never contained them (measured: resume point 152 s late, that speech + dropped from the transcript). Here the words are cut instead of the + audio -- each piece is small enough for one forced_align call and is + aligned, star and all, against ALL the audio left after the previous + piece. No audio boundary is ever estimated from text length. The + window's emission is computed once and sliced, so the wav2vec2 cost + is that of a single alignment. + + Any piece that cannot be aligned for real fails the whole call to + evenly spread times (prob 0.0), because a chain is only as trustworthy + as its weakest link: a guessed piece boundary would move every later + piece, and those would come back carrying perfectly real scores. + """ + import numpy as np + from noScribe import ctc_align + if not words: + return [] + if len(audio) < int(0.1 * SAMPLE_RATE): + return self._spread(words, audio, t_offset) + + emission = self._emission(audio) + if emission is None: + return self._spread(words, audio, t_offset) + n_frames = emission.shape[0] + dur = len(audio) / SAMPLE_RATE + fps = n_frames / dur + back = int(self.PREFIX_PIECE_BACKOFF_SEC * fps) + # The star column is a per-frame maximum, so slicing rows first would + # give the same values: build it once for the window and let each piece + # take a row view. + star = emission.shape[1] + emission = np.concatenate( + [emission, emission.max(axis=-1, keepdims=True)], axis=-1) + + def extend(new): + # The next piece is given a little audio the previous one already + # used (see the back-off below), so its first word can start just + # before the previous word ended. Keep the returned stamps in order + # -- a non-monotonic list would break cue building if these ever + # reach it. + for st in new: + if stamps and st["start"] < stamps[-1]["end"]: + st["start"] = stamps[-1]["end"] + st["end"] = max(st["end"], st["start"]) + stamps.append(st) + + stamps, f0, i, piece_no, why = [], 0, 0, 0, None + while i < len(words): + piece_no += 1 + if piece_no > self.MAX_PREFIX_PIECES: + why = f"more than {self.MAX_PREFIX_PIECES} pieces" + break + k = self._prefix_piece(words, i, n_frames - f0) + if k <= 0: + why = (f"{len(words) - i} words left do not fit the " + f"{n_frames - f0} frames left") + break + piece = words[i:i + k] + tokens, tok_word = self._tokenize(piece) + try: + # One extra vocabulary column, as good as the best real token at + # every frame, and one extra target token at the very end: that + # is how the aligner is told "after these words the audio is not + # mine". Without it the words are stretched over the rest (see + # the docstring), with it the last word lands frame-exact. + aligned, scores = ctc_align.forced_align( + emission[f0:], tokens + [star], blank=self.blank) + spans = ctc_align.merge_tokens(aligned, scores, blank=self.blank) + except Exception: + logger.warning("Prefix alignment failed on piece %d (%d words, " + "%d tokens, %d frames).", piece_no, len(piece), + len(tokens), n_frames - f0, exc_info=True) + why = "forced alignment raised" + break + t_start = t_offset + f0 / fps + # Where the star begins is where this piece's speech ends, so it is + # also the ceiling for interpolating any trailing out-of-vocabulary + # word -- the window's end would be nonsense here. + t_end = t_offset + dur + # The star is the last target token, so its span is at the end. + last = next((sp for sp in reversed(spans) if sp.token == star), None) + if last is not None: + t_end = min(t_end, t_start + last.start / fps) + got = self._stamps_from_spans(spans, piece, tok_word + [-1], fps, + t_start, t_end) + if got is None: + why = f"piece {piece_no} placed no word at all" + break + if i + k >= len(words): + extend(got) # last piece: keep every word + break + # Chain on the last word that was really aligned. A piece can end + # in out-of-vocabulary words (numbers, symbols) whose times are + # interpolated -- those are no basis for slicing the audio, so they + # are handed back to the next piece instead, which aligns them + # against audio that starts before them. + j = next((x for x in range(len(got) - 1, -1, -1) + if got[x].get("prob")), None) + if j is None: + why = f"piece {piece_no} was not really aligned" + break + extend(got[:j + 1]) + i += j + 1 + # Step back a little so the next piece's first word is whole even + # if this piece's last word ended slightly long. + f_end = int(round((got[j]["end"] - t_offset) * fps)) + f0 = min(max(f0 + 1, f_end - back), n_frames) + if f0 >= n_frames: + why = "the prefix ran past the end of the window" + break + if why: + logger.warning("Prefix alignment degraded to evenly spread times " + "(%s); the caller must not cut audio on these.", why) + return self._spread(words, audio, t_offset) + return stamps + + +# --------------------------------------------------------------------------- # +# Segment building +# --------------------------------------------------------------------------- # +def _split_sentences(text): + return [m.group(0).strip() for m in _SENT_SPLIT.finditer(text) if m.group(0).strip()] + + +# Subtitle cue sizing: aim for short, readable phrases (a few words), not one +# word per cue and not 30-second blocks -- comparable to what noScribe produces +# from Whisper's phrase-level segments. The limits follow common subtitle +# practice (about one line of 42-45 characters, a few seconds on screen); they +# are conventions, not measured optima. +SUB_MAX_CHARS = 45 +SUB_MAX_WORDS = 12 +SUB_MAX_SEC = 6.0 +SUB_MIN_WORDS = 4 + + +def _segments_from_words(word_stamps): + """Group aligned words into subtitle-sized cues. + + A cue is ended at sentence punctuation, at a clause boundary (comma/colon) + once it is long enough, or when it hits a length cap (characters, words or + duration). This keeps cues to a readable phrase rather than a whole + sentence or a fixed time window. + """ + segments = [] + cur = [] + + def flush(): + if not cur: + return + txt = " ".join(w["word"] for w in cur).strip() + if txt: + segments.append({ + "start": cur[0]["start"], + "end": cur[-1]["end"], + "text": " " + txt, + "words": list(cur), + }) + cur.clear() + + for w in word_stamps: + # Close the open cue BEFORE adding a word that would push it past the + # duration cap. `dur` below is measured with the word already appended + # and flush() can only end a cue *including* it, so the cap did not + # actually bind: any non-speech gap longer than SUB_MAX_SEC produced a + # cue as long as the gap (a 30 s interlude gave one 31 s two-word cue, + # an ordinary 9 s thinking pause gave 10.4 s), and the merge pass below + # only ever concatenates, so nothing downstream repaired it. + if cur and (w["end"] - cur[0]["start"]) > SUB_MAX_SEC: + flush() + cur.append(w) + tok = w["word"] + chars = sum(len(x["word"]) + 1 for x in cur) + dur = cur[-1]["end"] - cur[0]["start"] + if tok.endswith((".", "!", "?", "…")): + flush() + elif (len(cur) >= SUB_MIN_WORDS and tok.endswith((",", ";", ":")) + and (chars >= SUB_MAX_CHARS * 0.55 or dur >= SUB_MAX_SEC * 0.55)): + flush() + elif len(cur) >= SUB_MAX_WORDS or chars >= SUB_MAX_CHARS or dur >= SUB_MAX_SEC: + flush() + flush() + + # Merge a tiny leftover cue (e.g. a lone "habe.") back into the previous one + # when the result still fits, so subtitles don't get one-word fragments. + # + # A lone word is a fragment however long it is drawn out, so only the + # two-word case still needs the duration guard. With the guard applied to + # both, a stretched final word survived on its own: "angesagt." after a cue + # closed by SUB_MAX_CHARS ran ~1.2 s into the pause before the next cue and + # stayed a one-word segment. A segment that short takes its speaker from + # whatever the diarization happens to put under it -- 0.4 s of a spurious + # backchannel cluster was enough to hand one word its own speaker and its own + # paragraph, mid-sentence. The combined-length checks below still refuse the + # merge when the result would be an oversized cue, so a lone word after a + # long pause is left alone as before. + merged = [] + for seg in segments: + short = (len(seg["words"]) == 1 + or (len(seg["words"]) == 2 and (seg["end"] - seg["start"]) < 1.2)) + if merged and short: + prev = merged[-1] + combined = prev["words"] + seg["words"] + if len(combined) <= SUB_MAX_WORDS + 3 and (seg["end"] - prev["start"]) <= SUB_MAX_SEC + 2: + prev["words"] = combined + prev["end"] = seg["end"] + prev["text"] = " " + " ".join(w["word"] for w in combined).strip() + continue + merged.append(seg) + return merged + + +def _segments_text_only(text, duration): + """Short path: sentence segments with proportional (approximate) times.""" + sentences = _split_sentences(text) + total = sum(len(s) for s in sentences) or 1 + segments, pos = [], 0 + for s in sentences: + start = duration * pos / total + pos += len(s) + end = duration * pos / total + segments.append({"start": start, "end": end, "text": " " + s, "words": None}) + return segments + + +# --------------------------------------------------------------------------- # +# Public API +# --------------------------------------------------------------------------- # +def transcribe(audio_path, language="de", need_timestamps=True, + voxtral_repo=None, chunk_sec=None, + corrections_path=None, speaker_names=None, ram_reserve_gb=None, + speaker_turns=None, + log_cb=None, progress_cb=None, segment_cb=None): + """ + Transcribe `audio_path` with Voxtral and return noScribe-compatible segments. + + need_timestamps=True -> also run forced alignment for word-level times + (needed for VTT, speaker assignment, pauses). + need_timestamps=False -> fast path, plain text only (approx. segment times). + + corrections_path -> optional YAML word-correction list (brand/product names) + applied to the transcribed text. + chunk_sec -> per-pass length in seconds; None/0 = pick automatically + from RAM (see _auto_chunk_sec). A short file that fits one + pass is never split regardless of this value. + segment_cb -> optional callable(segment_dict); called for each finished + segment as soon as its pass completes, so callers can + stream/autosave partial transcripts instead of waiting + for the whole file. + speaker_turns -> optional [[start_s, end_s, label], ...] from a prior + diarization on the SAME audio timeline. Used to cut + looping chunks at speaker-turn boundaries (the cleanest + split) and to log a diarization profile when a loop + resists the gentle repairs. Without it, splits fall + back to the quietest pause, exactly as before. + """ + import soundfile as sf + + # "auto"/"multilingual" are how the menu spells "no language". Taken + # literally they became the pinned language "au"/"mu" -- in the prompt, in + # the translated-pass guard and in the aligner choice. + if (language or "").strip().lower() in ("", "auto", "multilingual"): + language = None + + from noScribe import transcript_corrections + corrections = transcript_corrections.load_corrections(corrections_path) + if corrections: + _log(log_cb, "info", f"Applying {len(corrections)} word correction(s).") + # The speaker names the user entered are the correct spelling of words that + # are very likely to be spoken; a spelling of one of them that sounds the + # same and is no word is set to theirs (apply_name_corrections). + speaker_names = [n for n in (speaker_names or []) if n] + if speaker_names: + _log(log_cb, "info", f"Correcting the spelling of the speaker names: " + f"{', '.join(speaker_names)}") + + # A missing file must be diagnosed as a missing file, not as whatever the + # memory sizing below happens to find wrong with the machine. + if not os.path.isfile(audio_path): + raise FileNotFoundError(f"Audio file not found: {audio_path}") + # An empty recording is an empty transcript, as with Whisper -- not a + # model load followed by "[as_strided] Negative dimensions" from the + # log-Mel features, which cannot be computed for zero samples. + if sf.info(audio_path).frames == 0: + _log(log_cb, "warn", "The audio contains no samples; nothing to transcribe.") + return [], {"duration": 0.0, "language": language} + + # The 8-bit mini build as the bare-API default: always fetchable from the + # hub and its repo name classifies correctly in MEM_MODEL. + repo = voxtral_repo or VOXTRAL_MODELS["voxtral-mini-8bit"] + # The unquantised source releases carry no bit width in their name, so + # _model_kind would meter them with a quantised profile and wave a too-long + # pass through -- and the 24B one is a 48 GB download. The GUI never gets + # here (it only offers the published builds), but a direct caller must be + # stopped just as early. + if (str(repo) in SOURCE_REPOS + or os.path.basename(os.path.normpath(str(repo))).lower() + in SOURCE_REPO_NAMES): + raise ValueError( + f"{repo} is an unquantised source release, not a runnable build. " + f"Use one of the published builds ({', '.join(VOXTRAL_MODELS)}) or " + f"convert it first with tools/quantize_voxtral.py.") + + # Size the passes BEFORE loading anything: the MemoryError for models that + # cannot fit this machine is only worth something if it comes before 20+ GB + # of weights have already pushed the OS into swap. + # The config value may arrive as a string (YAML round-trip); coerce before + # any arithmetic and fall back to auto-sizing on junk. + try: + chunk_sec = float(chunk_sec) if chunk_sec else None + except (TypeError, ValueError): + chunk_sec = None + if not chunk_sec or chunk_sec <= 0: + chunk_sec = _auto_chunk_sec(repo, log_cb, ram_reserve_gb) + else: + # A pinned voxtral_chunk_sec must not bypass the safety nets (it may + # well predate a switch to a hungrier model). It may exceed the *auto* + # length -- that one holds back the configurable reserve, and pinning + # is the documented way to trade that reserve for context -- but + # neither the hard memory ceiling past which the working set no longer + # fits and the run stops progressing, nor the model-context cap + # (MAX_CHUNK_SEC). A model whose shortest pass cannot fit is refused + # outright, exactly as in the automatic path. + ceiling = max_safe_chunk_sec(repo) # raises MemoryError if unfit + pinned = chunk_sec + chunk_sec = int(min(pinned, ceiling, MAX_CHUNK_SEC, TRUSTED_CHUNK_SEC)) + if chunk_sec < HARD_MIN_CHUNK_SEC: + # The clamp above only ever shortens, so a value this small came + # from the config -- and int() turns anything under 1 s into 0, + # which makes max_len one sample below and splits the file into one + # decode per 50 ms (a 2-minute file became 2401 passes: the job + # never finishes). The automatic path floors at HARD_MIN_CHUNK_SEC; + # a pinned value must not be able to duck under it either. + _log(log_cb, "warn", + f"voxtral_chunk_sec={pinned:g}s is below the " + f"{HARD_MIN_CHUNK_SEC}s minimum; using {HARD_MIN_CHUNK_SEC}s.") + chunk_sec = HARD_MIN_CHUNK_SEC + elif chunk_sec < pinned: + # Three different reasons a pin gets shortened, and saying the wrong + # one is worse than saying nothing: the machine, the model's context, + # or our own refusal to run further out than Voxtral was measured. + if ceiling < min(MAX_CHUNK_SEC, TRUSTED_CHUNK_SEC): + what = "more memory than this machine has" + elif MAX_CHUNK_SEC <= TRUSTED_CHUNK_SEC: + # Unreachable while TRUSTED_CHUNK_SEC stays below the context + # cap; kept for the day it is raised to it, as its comment invites. + what = "more context than the model has" + else: + what = ("a longer window than Voxtral has been measured on " + f"({TRUSTED_CHUNK_SEC}s)") + _log(log_cb, "warn", + f"voxtral_chunk_sec={pinned:.0f}s would need {what}; " + f"using {chunk_sec}s.") + + quant = _quant_summary(repo) + _log(log_cb, "info", f"Loading Voxtral model: {repo}" + + (f" ({quant})" if quant else "")) + vox = _load_from_hub(lambda: _Voxtral(repo), "Voxtral model", repo, + _DOWNLOAD_GB.get(repo), log_cb) + + # The aligner is chosen per chunk from the *transcribed text* (see + # _AlignerPool): the text exists before its chunk is aligned, so Auto can + # use the char-native per-language model instead of the weaker romanised + # multilingual fallback, and mid-file language changes are followed. + aligner_pool = _AlignerPool(language, log_cb) if need_timestamps else None + # With the language set, its aligner is known now: fail before the first + # pass is decoded rather than after it, if it can be neither found nor + # fetched. (Loading it here would keep it resident through the decode.) + if aligner_pool is not None and language: + align_model = resolve_align_model(language) + if _unfetchable(align_model): + raise _not_fetchable_error("alignment model", align_model, + _ALIGNER_DOWNLOAD_GB) + + # Language bookkeeping for the translated-chunk guard. With an explicit + # language there is a target from the first chunk on; on Auto the file has + # to say what it is first (see _file_language), which means the earliest + # chunks can only be flagged after the fact -- they are already streamed to + # the caller by then. + pinned_lang = (language or "").strip().lower()[:2] or None + lang_tally = {} + chunk_langs = [] # (chunk, language) for chunks decoded before `want` + + audio, sr = sf.read(audio_path, dtype="float32") + if audio.ndim > 1: + audio = audio.mean(axis=1) + if sr != SAMPLE_RATE: + raise ValueError(f"Expected {SAMPLE_RATE} Hz audio, got {sr}") + duration = len(audio) / SAMPLE_RATE + # Normalise the diarization turns defensively (they cross a process + # boundary as plain lists): sorted (start_s, end_s, label) tuples, junk + # dropped. None disables the turn-aware splitting. + turns = None + if speaker_turns: + try: + turns = sorted((float(s), float(e), str(lbl)) + for s, e, lbl in speaker_turns if float(e) > float(s)) + except (TypeError, ValueError): + turns = None + turns = turns or None + bounds = _pass_bounds(audio, chunk_sec) + n_chunks = len(bounds) - 1 + if n_chunks > 1: + _log(log_cb, "info", + f"Audio {duration / 60:.1f} min exceeds the longest RAM-safe " + f"decode window ({chunk_sec:.0f}s) -> transcribing in {n_chunks} " + f"chunks (cut at pauses, {OVERLAP_SEC}s overlap).") + # Overlap gives the model lead-in context at a seam; only used on the long + # path where the duplicate can be dropped cleanly by timestamp. + overlap = int(OVERLAP_SEC * SAMPLE_RATE) if aligner_pool is not None else 0 + all_segments = [] + # Progress is measured against the WHOLE audio (each pass contributes its own + # share of the total duration, so a short final pass moves the bar only a + # little), and never runs backward. _prog_max keeps it monotonic across the + # intra-pass estimate, the pass-complete snap, and any split-retry re-decode. + n_samples = max(1, len(audio)) + _prog_max = [0] + + def _emit_progress(pct): + pct = int(pct) + if pct > _prog_max[0]: + _prog_max[0] = pct + else: + pct = _prog_max[0] + if progress_cb: + try: + progress_cb(pct) + except Exception: + pass + + for ci in range(n_chunks): + a0 = bounds[ci] + a1 = bounds[ci + 1] + a_read = max(0, a0 - overlap) if ci > 0 else a0 + chunk = audio[a_read:a1] + t_offset = a_read / SAMPLE_RATE + _log(log_cb, "info", f"Transcribing chunk {ci + 1}/{n_chunks} " + f"({a0 / SAMPLE_RATE:.0f}-{a1 / SAMPLE_RATE:.0f}s)") + # Intra-pass liveness: Voxtral returns the whole pass at once, so estimate + # how far the decode is from the token count and map it onto this pass's + # slice of the overall bar (capped below the pass boundary; the real + # position is set when the pass finishes below). + _base = a0 / n_samples + _span = max(0.0, (a1 - a0) / n_samples) + _exp_tokens = max(1.0, (a1 - a0) / SAMPLE_RATE * _EST_TOKENS_PER_SEC) + + def _heartbeat(ntok, _base=_base, _span=_span, _exp=_exp_tokens): + _emit_progress((_base + min(0.95, ntok / _exp) * _span) * 100) + + label = f"Chunk {ci + 1}/{n_chunks}" + # The diarization's turns in this pass, window-relative: for the loop + # ladder's cuts and for the alignment's coverage check alike. + pass_turns = _clip_turns(turns, t_offset, a1 / SAMPLE_RATE) + # What language should this chunk come back in? With an explicit + # setting there is an answer from the first chunk on; on Auto the file + # has to say what it is first (see _file_language), so the earliest + # chunks go unchecked and can only be named afterwards. + want = pinned_lang or _file_language(lang_tally) + text = _transcribe_guarded(vox, chunk, language, log_cb, label, + token_cb=_heartbeat, + turns=pass_turns, + align_cb=(aligner_pool.align_for_salvage + if aligner_pool is not None else None), + want_lang=want) + if not text: + continue + # Before anything reads the text: a pass that silently dropped its + # opening is not otherwise detectable, and the words are simply gone + # from the transcript. + # + # Only the first pass needs this. Every later one starts OVERLAP_SEC + # early and its overlap words are dropped again below by timestamp, so a + # recovered opening would land before `a0` and be discarded -- which is + # exactly what happened the one time the probe fired on a later chunk. + # The previous pass already transcribed that audio. A loss LONGER than + # the overlap would reach past `a0` and stays uncovered; no such loss has + # been observed, and probing every pass for it cost ~10% of each decode. + if a_read == 0: + text = _recover_lost_head(vox, chunk, language, text, log_cb, label) + chunk_lang, _ = _detect_language(text) + if _other_language(text, want): + # The ladder may have kept this on purpose (the audio can really be + # in that language), so say both: only the listener can tell. + _log(log_cb, "warn", + f"{label}: reads as '{chunk_lang}' rather than '{want}'. Either " + f"it is spoken in that language, or these " + f"{len(text.split())} words are a translation, not a transcript.") + if chunk_lang: + lang_tally[chunk_lang] = lang_tally.get(chunk_lang, 0) + 1 + if not want: + chunk_langs.append((ci + 1, chunk_lang)) + if corrections: + text = transcript_corrections.apply_corrections(text, corrections) + if speaker_names: + # Every language the text may be in: the one it reads as, and the + # one expected (see apply_name_corrections). + text = transcript_corrections.apply_name_corrections( + text, speaker_names, (chunk_lang, want)) + # Unconditional, so the text-only path frees them too. + _free_decode_buffers(vox) + if aligner_pool is not None: + words = re.findall(r"\S+", text) + _log(log_cb, "info", f"{label}: aligning word timestamps " + f"({len(words)} words)...") + stamps = aligner_pool.align_words(words, chunk, t_offset, turns=pass_turns) + if a_read < a0: + # Drop the *words* already covered by the previous pass (the + # overlap region). Filtering at word rather than cue level + # means a cue straddling the seam can neither duplicate its + # pre-seam words nor lose its post-seam ones. + b = a0 / SAMPLE_RATE + stamps = [w for w in stamps if (w["start"] + w["end"]) / 2 >= b] + segs = _segments_from_words(stamps) + else: + segs = _segments_text_only(text, (a1 - a_read) / SAMPLE_RATE) + for seg in segs: + seg["start"] += t_offset + seg["end"] += t_offset + all_segments.extend(segs) + if segment_cb: + # Stream this pass's segments right away so the caller can show + # and autosave a partial transcript during long files. + for seg in segs: + segment_cb(seg) + # Snap the bar to this pass's true end position in the whole audio + # (duration-weighted; the final pass lands on 100%). + _emit_progress(a1 / n_samples * 100) + + # On Auto the file's language is only established once a couple of chunks + # agree, so a chunk translated before that point was streamed to the caller + # unchallenged. It cannot be taken back -- but leaving it unmentioned would + # be worse: the transcript changes language part way through and nothing + # says why. + settled = pinned_lang or _file_language(lang_tally) + odd = [ci for ci, lg in chunk_langs if settled and lg != settled] + if odd: + _log(log_cb, "warn", + f"Chunk(s) {', '.join(str(c) for c in odd)} of {n_chunks} read as " + f"a different language than the rest of the file ('{settled}') and " + f"were already written out before that was known. Voxtral most " + f"likely translated them; they are worth re-running on their own.") + + # Report the language the file settled on, so "auto" comes back as what + # was actually transcribed rather than as the request. + return all_segments, {"duration": duration, "language": settled or language} diff --git a/noScribe/voxtral_mp_worker.py b/noScribe/voxtral_mp_worker.py new file mode 100644 index 00000000..12e64bc2 --- /dev/null +++ b/noScribe/voxtral_mp_worker.py @@ -0,0 +1,84 @@ +""" +Subprocess entry point for the Voxtral transcription backend. + +Mirrors the message protocol of `whisper_mp_worker` so the main app can consume +segments the same way regardless of the chosen engine: + + {"type": "log", "level": "info|warn|error|debug", "msg": str} + {"type": "progress", "pct": int} + {"type": "segment", "segment": {"start","end","text","words"}} + {"type": "result", "ok": True, "info": {...}} + {"type": "result", "ok": False, "error": str, "trace": str} +""" + +import os +import traceback + + +def voxtral_proc_entrypoint(args: dict, q): + try: + # huggingface_hub sends usage telemetry (library, versions) with its + # requests unless told not to, and reads this switch once, when it is + # imported. Keep noScribe processing local, even if the parent + # environment enables telemetry for other apps. + os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1" + from noScribe import voxtral_engine + + def plog(level, msg): + try: + q.put({"type": "log", "level": level, "msg": str(msg)}) + except Exception: + pass + + def progress(pct): + try: + q.put({"type": "progress", "pct": int(pct)}) + except Exception: + pass + + def send_segment(seg): + # Deliberately NOT wrapped in try/except: if the queue breaks we + # must fail the whole job (outer handler -> ok=False) rather than + # silently truncate the transcript and still report success. + q.put({"type": "segment", "segment": seg}) + + # segment_cb streams each pass's segments as soon as it finishes, so + # the GUI can display and autosave a partial transcript during long + # files (and a cancel/crash loses at most the current pass). + _, info = voxtral_engine.transcribe( + audio_path=args["audio_path"], + # None for Auto/Multilingual jobs -> Voxtral auto-detects the + # language, and the aligner is chosen per chunk from the + # transcribed text (char-native model when a language dominates, + # romanised multilingual fallback otherwise). + language=args.get("language_code"), + need_timestamps=args.get("need_timestamps", True), + voxtral_repo=args.get("voxtral_repo"), + # None/0 -> engine picks the per-pass length from available RAM. + chunk_sec=args.get("chunk_sec"), + corrections_path=args.get("corrections_path"), + # correct spelling of names the user already told us + speaker_names=args.get("speaker_names"), + # None -> engine default; lower it on a machine with nothing else running + ram_reserve_gb=args.get("ram_reserve_gb"), + # [[start_s, end_s, label], ...] from the diarization, or None. + # Used to cut looping chunks at speaker-turn boundaries and to log + # a diarization profile when a loop resists repair. + speaker_turns=args.get("speaker_turns"), + log_cb=plog, + progress_cb=progress, + segment_cb=send_segment, + ) + + q.put({"type": "result", "ok": True, "info": info}) + + except Exception as e: + try: + q.put({ + "type": "result", + "ok": False, + "error": f"{type(e).__name__}: {e}", + "trace": traceback.format_exc(), + }) + except Exception: + pass From f997ffcc381fb77a48ece0d0851b1f38049b08a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=A4mmerer?= Date: Wed, 23 Sep 2026 15:12:23 +0200 Subject: [PATCH 2/5] Offer the Voxtral builds in the model picker on arm64 Macs that have the stack installed, and run both engines through one subprocess pump WhisperModel gains engine and repo; main.py registers the builds only where voxtral_engine.is_available(), labels them with the RAM they need, and dispatches on the engine. The Whisper path is the same code, moved: _run_whisper_subprocess_stream now builds its arguments and hands them to the shared pump, which still returns the info object. The CUDA-to-CPU retry stays Whisper's, since Voxtral has no CUDA path to fall back from. A build saved under its earlier name means the one that replaced it, and the macOS spec keeps the MLX stack out of the packaged app, where a partial copy would let the picker offer Voxtral that then fails to load. Co-Authored-By: Claude Fable 5.1 Co-Authored-By: Claude Opus 5.5 --- .gitignore | 4 + noScribe/main.py | 332 +++++++++++++++++++++++++++----- noScribe/transcription.py | 36 +++- pyinstaller/noScribe_macOS.spec | 5 +- trans/noScribe.de.yml | 4 + trans/noScribe.en.yml | 4 + trans/noScribe.es.yml | 4 + trans/noScribe.fr.yml | 4 + trans/noScribe.it.yml | 4 + trans/noScribe.ja.yml | 4 + trans/noScribe.pt.yml | 4 + trans/noScribe.ru.yml | 4 + trans/noScribe.zh-CN.yml | 4 + 13 files changed, 354 insertions(+), 59 deletions(-) diff --git a/.gitignore b/.gitignore index 9b9ea5b3..70ab588e 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,7 @@ diarize_x86_64_mod_unsigned /pyinstaller/win_installer /.history-memo AGENTS.md + +# Voxtral: local model builds/quantised weights (tens of GB, rebuilt in seconds +# with tools/quantize_voxtral.py). +models/voxtral-* diff --git a/noScribe/main.py b/noScribe/main.py index 438c2dd7..7be77e80 100644 --- a/noScribe/main.py +++ b/noScribe/main.py @@ -1011,6 +1011,11 @@ def _init_app_state(app): 'See here for more information: https://github.com/kaixxx/noScribe/wiki/Add-custom-Whisper-models-for-transcription') app.queue = TranscriptionQueue() + # Maps each decorated picker label back to its plain model name, so the + # selected model is recovered by lookup rather than by string-splitting the + # display label on a separator (see model_key). Rebuilt whenever the dropdown + # is populated. + app._model_label_to_name = {} app.audio_files_list = [] app.transcript_files_list = [] app.log_file = None @@ -1031,6 +1036,45 @@ def _init_app_state(app): tmp = transcription.WhisperModelManager(app.user_models_dir) app.whisper_models = tmp.get_installed_models() + _register_voxtral_models(app) + + +# Model names that were published under another name before: a choice saved +# under the old one (config.yml, a script's --model) means the new one, and was +# otherwise silently replaced by the first Whisper model. +RENAMED_MODELS = {'voxtral-small-8bit': 'voxtral-small-4bit'} + + +def _register_voxtral_models(app): + """Add the Voxtral engine as additional model choices (Apple Silicon only, + where mlx-voxtral runs on the GPU). Called at startup and again when the + model dropdown opens, so a build converted while the app is running + (tools/quantize_voxtral.py) appears without a restart.""" + if platform.system() != "Darwin" or platform.machine() != "arm64": + return + try: + from noScribe import voxtral_engine + if voxtral_engine.is_available(): + for _name, _repo in voxtral_engine.VOXTRAL_MODELS.items(): + # Skip a (hypothetical future) local-only entry whose build is not + # present yet. Shipped builds download on first use, so this is + # currently always true for them. + if not voxtral_engine.has_local_build(_name): + continue + resolved = voxtral_engine.resolve_model(_name, _repo) + existing = app.whisper_models.get(_name) + # (Re)register when new, or when the resolved repo changed -- e.g. + # a build converted mid-session (tools/quantize_voxtral.py) flips + # the resolved repo from the HF id to the now-present local path, + # so it is picked up on the next dropdown open without a restart + # (as this function's docstring promises). + if existing is None or getattr(existing, "repo", None) != resolved: + app.whisper_models[_name] = transcription.WhisperModel( + name=_name, path=Path(_name), engine="voxtral", + repo=resolved) + except Exception as e: + logger.warning("Could not register Voxtral engine: %s", e) + class App(ctk.CTk): def __init__(self): @@ -1167,7 +1211,15 @@ def __init__(self, noScribe_parent, master, width = 140, height = 28, corner_rad def _clicked(self, event=0): self.old_value = self.get() - self._values = list(self.noScribe_parent.whisper_models.keys()) + # Pick up models that appeared since startup (e.g. a Voxtral + # build converted while the app is running). + _register_voxtral_models(self.noScribe_parent) + names = list(self.noScribe_parent.whisper_models.keys()) + self._values = [self.noScribe_parent.model_label(n) for n in names] + # Remember label -> name so the selection is recovered by lookup, + # not by splitting the label on MODEL_LABEL_SEP. + self.noScribe_parent._model_label_to_name = dict( + zip(self._values, names)) self._values.append('--------------------') self._values.append(t('label_add_custom_models')) self._dropdown_menu.configure(values=self._values) @@ -1204,6 +1256,7 @@ def _dropdown_callback(self, value: str): dynamic_resizing=False) self.option_menu_whisper_model.grid(column=1, row=3, sticky='e', pady=5) last_whisper_model = get_config('last_whisper_model', 'precise') + last_whisper_model = RENAMED_MODELS.get(last_whisper_model, last_whisper_model) if last_whisper_model in self.whisper_models: self.option_menu_whisper_model.set(last_whisper_model) elif len(self.whisper_models) > 0: @@ -2116,6 +2169,11 @@ def log(self, txt: str = '', tags: list = [], where: str = 'both', link: str = ' txt = f'{txt}\nTraceback:\n{tb}' self.log_file.write(txt) self.log_file.flush() + # Track whether the log currently ends mid-line, so status + # messages arriving while transcript text streams in (which is + # written without a trailing newline) can start on a fresh line. + if txt: + self._log_line_open = not txt.endswith('\n') except Exception as e: # If we get here, both screen and file logging failed # As a last resort, print to stderr to not lose the error @@ -2149,6 +2207,12 @@ def logn(self, txt: str = '', tags: list = [], where: str = 'both', link:str = ' """ Log with a newline appended """ self.log(f'{txt}\n', tags, where, link, tb) + def end_open_line(self) -> None: + """Finish a line left open -- streamed transcript text, or a progress + line from logr -- so the next message does not get glued onto it.""" + if getattr(self, '_log_line_open', False): + self.logn() + def logr(self, txt: str = '', tags: list = [], where: str = 'both', link:str = '', tb: str = '') -> None: """ Replace the last line of the log """ if where != 'file' and not getattr(self, '_headless', False): @@ -2406,6 +2470,47 @@ def set_progress(self, step, value, speaker_detection='none'): if hasattr(row['frame'], 'set_progress'): row['frame'].set_progress(progr) + MODEL_LABEL_SEP = ' · ' + + def model_label(self, name): + """Model name plus the RAM it needs. + + Choosing a model that does not fit does not merely run slowly: the + machine starts swapping and the run stops making progress, which is + impossible to diagnose from the outside. Showing the requirement at the + point of choice is the cheapest way to prevent that. + """ + model = self.whisper_models.get(name) + if getattr(model, 'engine', 'whisper') != 'voxtral': + return name + try: + from noScribe import voxtral_engine + need = voxtral_engine.min_ram_gb(getattr(model, 'repo', name)) + total = voxtral_engine._total_ram_gb() + mark = '' if need <= total else ' ⚠' + return f'{name}{self.MODEL_LABEL_SEP}{need:.0f} GB RAM{mark}' + except Exception as e: + # Degrade to the bare name rather than break the picker, but leave a + # trail: the RAM hint (and its ⚠ overfit marker) is a memory-safety + # cue, so its silent absence would otherwise be undiagnosable. + logger.warning("Could not compute RAM hint for model %r: %s", name, e) + return name + + def model_key(self, label): + """Plain model name from a decorated picker entry. + + Prefers the label->name map built when the dropdown was populated, so a + model name that happens to contain the separator is still recovered + exactly. Falls back to splitting on MODEL_LABEL_SEP for values that never + passed through the dropdown (e.g. a plain name restored at startup, which + has no separator and so returns unchanged).""" + if not label: + return label + mapped = self._model_label_to_name.get(label) + if mapped is not None: + return mapped + return label.split(self.MODEL_LABEL_SEP)[0].strip() + def _on_speaker_detection_changed(self, value=None): """Show the speaker-names field only when speaker detection is active. With detection off ('none') there are no speakers to map names to.""" @@ -2422,7 +2527,10 @@ def save_ui_state(self): config['last_speaker'] = self.option_menu_speaker.get() # Names belong to the selected recording, not to future sessions. config.pop('last_speaker_names', None) - config['last_whisper_model'] = self.option_menu_whisper_model.get() + # model_key(): the picker shows "name · N GB RAM", but startup looks + # the remembered value up in whisper_models by plain name -- storing the + # decorated label would silently forget the choice on the next start. + config['last_whisper_model'] = self.model_key(self.option_menu_whisper_model.get()) config['last_pause'] = self.option_menu_pause.get() config['last_overlapping'] = self.check_box_overlapping.get() config['last_timestamps'] = self.check_box_timestamps.get() @@ -2453,7 +2561,7 @@ def collect_transcription_options(self) -> TranscriptionQueue: stop_time = utils.str_to_ms(val) # Get whisper model path - sel_whisper_model = self.option_menu_whisper_model.get() + sel_whisper_model = self.model_key(self.option_menu_whisper_model.get()) if sel_whisper_model not in self.whisper_models: raise FileNotFoundError(f"The whisper model '{sel_whisper_model}' does not exist.") # Persist the options the moment they are actually used. Saving only in @@ -2669,6 +2777,7 @@ def _process_single_job(self, job: TranscriptionJob): if job.stop > 0: option_info += f'{t("label_stop")} {utils.ms_to_str(job.stop)} | '.replace(':', '꞉') option_info += f'{t("label_language")} {job.language_name} ({languages[job.language_name]}) | ' + option_info += f'{t("label_whisper_model")} {getattr(job.whisper_model, "name", None) or str(job.whisper_model)} | ' option_info += f'{t("label_speaker")} {job.speaker_detection} | ' if job.speaker_names: option_info += f'{t("label_speaker_names")} {", ".join(job.speaker_names)} | ' @@ -2841,6 +2950,7 @@ def find_speaker(diarization, transcript_start, transcript_end) -> str: # Start Diarization: + diarization = None if job.speaker_detection != 'none': try: job.status = JobStatus.SPEAKER_IDENTIFICATION @@ -2880,14 +2990,22 @@ def find_speaker(diarization, transcript_start, transcript_end) -> str: raise Exception(job.error_message) #------------------------------------------------------- - # 3) Transcribe with faster-whisper + # 3) Transcribe with faster-whisper or Voxtral job.status = JobStatus.TRANSCRIPTION self.update_queue_table() self.logn() self.logn(t('start_transcription'), 'highlight') - self.logn(t('loading_whisper')) + # Bound once: which engine runs decides the log line, the + # subprocess, whether the per-segment progress source is used, + # and whether a CUDA failure may retry on CPU. + engine = getattr(job.whisper_model, "engine", "whisper") + is_voxtral = engine == "voxtral" + if is_voxtral: + self.logn(t('loading_voxtral')) + else: + self.logn(t('loading_whisper')) info = None transcription_success = False @@ -3201,15 +3319,27 @@ def __init__(self, d): save_doc() job.has_partial_transcript = True - # per-segment progress based on total duration - try: - progr = round((segment.end/duration) * 100) - self.set_progress(3, progr, job.speaker_detection) - except Exception: - pass + # Per-segment progress, derived from where this segment + # ends in the file. Whisper sends no progress messages, so + # this is its only source. Voxtral does send them, and its + # own estimate runs ahead of the segments it has already + # emitted -- feeding both to the same bar made it jump + # back roughly 28 points per chunk. The engine's stream is + # authoritative there and is kept monotonic at the source + # (voxtral_engine._emit_progress). + if not is_voxtral: + try: + progr = round((segment.end/duration) * 100) + self.set_progress(3, progr, job.speaker_detection) + except Exception: + pass try: - info = self._run_whisper_subprocess_stream(tmp_audio_file, job, on_segment) + if is_voxtral: + info = self._run_voxtral_subprocess_stream( + tmp_audio_file, job, on_segment, diarization=diarization) + else: + info = self._run_whisper_subprocess_stream(tmp_audio_file, job, on_segment) if first_segment: raise ValueError(t('err_empty_transcript')) transcription_success = True @@ -3221,7 +3351,12 @@ def __init__(self, d): self.logn() self.logn(t('transcription_finished'), 'highlight') except Exception as err: - if self._handle_cuda_fallback('whisper', err): + # The CUDA-on-CPU fallback only applies to the Whisper + # (faster-whisper / CTranslate2) backend; Voxtral runs on + # MLX and has no CUDA path, so a Voxtral error must + # propagate rather than trigger a misleading "retry + # Whisper on CPU" prompt and a pointless identical retry. + if engine == "whisper" and self._handle_cuda_fallback('whisper', err): retry_cuda = True else: raise @@ -3357,48 +3492,16 @@ def _handle_cuda_fallback(self, component: str, error: Exception) -> bool: return False - def _run_whisper_subprocess_stream(self, tmp_audio_file: str, job, on_segment): - """Spawn a subprocess to run Faster-Whisper and stream segments. - Calls on_segment(dict) for each segment streamed by the child. - Returns a simple info object (duration at least). - """ - global force_whisper_cpu - # Language code for non-auto/multilingual - language_code = None - if job.language_name not in ('Auto', 'Multilingual'): - try: - language_code = languages[job.language_name] - except Exception: - language_code = None - - # VAD threshold from config - try: - vad_threshold = float(config.get('voice_activity_detection_threshold', '0.5')) - except Exception: - vad_threshold = 0.5 - - args = { - "whisper_model": job.whisper_model, - "device": 'cpu' if force_whisper_cpu else 'auto', - "compute_type": job.whisper_compute_type, - "cpu_threads": number_threads, - "local_files_only": True, - "audio_path": tmp_audio_file, - "language_name": job.language_name, - "language_code": language_code, - "disfluencies": job.disfluencies, - "beam_size": 5, - "word_timestamps": True, - "vad_filter": True, - "vad_threshold": vad_threshold, - "locale": config.get("locale", "en"), - } + def _run_engine_subprocess_stream(self, entrypoint, args, job, on_segment): + """Spawn a transcription worker subprocess and pump its message queue. - # Spawn child process using spawn start method + Shared by the Whisper and Voxtral engines: streams log/progress/segment + messages back to the GUI, honors cancel, and tears the child down + reliably. Returns a simple info object (duration at least). + """ ctx = mp.get_context("spawn") q = ctx.Queue() - from .whisper_mp_worker import whisper_proc_entrypoint - proc = ctx.Process(target=whisper_proc_entrypoint, args=(args, q)) + proc = ctx.Process(target=entrypoint, args=(args, q)) proc.start() # Expose to allow cancel to terminate the child self._mp_proc = proc @@ -3420,6 +3523,7 @@ def _run_whisper_subprocess_stream(self, tmp_audio_file: str, job, on_segment): if not proc.is_alive(): # Process died without sending result exitcode = proc.exitcode + self.end_open_line() self.logn(f"Transcription worker exited unexpectedly (code {exitcode}).", 'error') raise Exception('Subprocess terminated unexpectedly') continue @@ -3428,6 +3532,8 @@ def _run_whisper_subprocess_stream(self, tmp_audio_file: str, job, on_segment): if mtype == "log": level = msg.get("level", "info") txt = msg.get("msg", "") + # Transcript text streams in without trailing newlines. + self.end_open_line() if level == 'error': self.logn(txt, 'error') else: @@ -3457,6 +3563,7 @@ def _run_whisper_subprocess_stream(self, tmp_audio_file: str, job, on_segment): else: err = msg.get('error', 'Transcription failed') trc = msg.get('trace') + self.end_open_line() self.logn(f"Transcription failed: {err}", 'error') if trc: self.logn(trc, where='file') @@ -3493,6 +3600,125 @@ def __init__(self, d): info_obj = _Info(info or {}) return info_obj + @staticmethod + def _job_language_code(job): + """The ISO code for a job's language, or None for Auto/Multilingual or an + unmapped name. Shared by both engine paths so they never diverge on the + sentinel set or the lookup fallback.""" + if job.language_name in ('Auto', 'Multilingual'): + return None + try: + return languages[job.language_name] + except Exception: + return None + + def _run_voxtral_subprocess_stream(self, tmp_audio_file: str, job, on_segment, + diarization=None): + """Spawn a subprocess to run the Voxtral engine and stream segments. + + Word/segment timestamps are only computed (via forced alignment, the + slower "long path") when the output can carry timing: .html/.vtt files + embed audio-sync anchors, so only a plain .txt transcript without + timestamps, speaker detection or pause marking may take the fast + text-only "short path" (whose segment times are approximations). + """ + language_code = self._job_language_code(job) + + need_timestamps = bool( + job.file_ext != 'txt' + or job.timestamps + or (job.speaker_detection and job.speaker_detection != 'none') + or (job.pause and job.pause > 0) + ) + self.logn(t('voxtral_path_long') if need_timestamps else t('voxtral_path_short'), + where='file') + if job.disfluencies: + # Voxtral has no prompt/hotword hook, so the disfluencies option + # cannot steer it; say so instead of silently ignoring the setting. + self.logn(t('voxtral_no_disfluencies'), where='file') + + from noScribe import transcript_corrections + corrections_path = transcript_corrections.ensure_default_file(config_dir) + + # 0 (default) -> engine sizes each pass from available RAM; set a + # positive value in config.yml to pin the per-pass length in seconds. + # Config values may come back as strings, so coerce defensively. + try: + chunk_sec = float(get_config('voxtral_chunk_sec', 0) or 0) + except (TypeError, ValueError): + chunk_sec = 0 + # Same defensive coercion as chunk_sec: a value written as a YAML string + # ("0", "high") would otherwise either slip a truthy "0" through (losing + # the whole safety reserve -> swap) or raise ValueError deep in the + # engine and abort the job. + try: + ram_reserve_gb = float(get_config('voxtral_ram_reserve_gb', 0) or 0) + except (TypeError, ValueError): + ram_reserve_gb = 0 + + args = { + "audio_path": tmp_audio_file, + "language_code": language_code, + "need_timestamps": need_timestamps, + "voxtral_repo": getattr(job.whisper_model, "repo", None), + "chunk_sec": chunk_sec or None, + "corrections_path": corrections_path, + # None unless the job carries speaker names; the engine then + # normalises mis-heard names to those spellings (see + # transcript_corrections.apply_name_corrections). + "speaker_names": getattr(job, "speaker_names", None), + # 0 -> engine default; set voxtral_ram_reserve_gb in config.yml to + # allow longer passes when nothing else runs on the machine. + "ram_reserve_gb": ram_reserve_gb or None, + # Speaker turns from the diarization (same converted-WAV timeline + # the engine reads), in seconds. The engine prefers cutting a + # looping chunk at a turn boundary -- the cleanest split there is -- + # and logs a diarization profile when a loop resists repair. + # Optional: without speaker detection the engine falls back to + # silence-based cuts, exactly as before. + "speaker_turns": ( + [[seg["start"] / 1000.0, seg["end"] / 1000.0, str(seg["label"])] + for seg in diarization] if diarization else None), + } + + from .voxtral_mp_worker import voxtral_proc_entrypoint + return self._run_engine_subprocess_stream(voxtral_proc_entrypoint, args, job, on_segment) + + def _run_whisper_subprocess_stream(self, tmp_audio_file: str, job, on_segment): + """Spawn a subprocess to run Faster-Whisper and stream segments. + Calls on_segment(dict) for each segment streamed by the child. + Returns a simple info object (duration at least). + """ + global force_whisper_cpu + # Language code for non-auto/multilingual + language_code = self._job_language_code(job) + + # VAD threshold from config + try: + vad_threshold = float(config.get('voice_activity_detection_threshold', '0.5')) + except Exception: + vad_threshold = 0.5 + + args = { + "whisper_model": job.whisper_model, + "device": 'cpu' if force_whisper_cpu else 'auto', + "compute_type": job.whisper_compute_type, + "cpu_threads": number_threads, + "local_files_only": True, + "audio_path": tmp_audio_file, + "language_name": job.language_name, + "language_code": language_code, + "disfluencies": job.disfluencies, + "beam_size": 5, + "word_timestamps": True, + "vad_filter": True, + "vad_threshold": vad_threshold, + "locale": config.get("locale", "en"), + } + + from .whisper_mp_worker import whisper_proc_entrypoint + return self._run_engine_subprocess_stream(whisper_proc_entrypoint, args, job, on_segment) + def _run_diarize_subprocess(self, tmp_audio_file: str, job): """Spawn a subprocess to run diarization and return list of segments. Streams child logs/progress back to GUI and honors cancel. @@ -3734,6 +3960,7 @@ def run_cli_mode(args): # Validate and set the whisper model if args.model: + args.model = RENAMED_MODELS.get(args.model, args.model) if args.model not in app.whisper_models: print(f"Error: Model '{args.model}' not found.") print(f"Available models: {', '.join(app.whisper_models.keys())}") @@ -3925,6 +4152,7 @@ def noScribeMain(): # Prefill selected model if provided desired_model_name = None if getattr(args, 'model', None): + args.model = RENAMED_MODELS.get(args.model, args.model) if args.model in app.whisper_models: desired_model_name = args.model else: diff --git a/noScribe/transcription.py b/noScribe/transcription.py index a63feeb9..d8a11b56 100644 --- a/noScribe/transcription.py +++ b/noScribe/transcription.py @@ -1,4 +1,5 @@ import dataclasses +from typing import Optional import importlib.resources as impres import logging from pathlib import Path @@ -13,10 +14,17 @@ class WhisperModel: """ Represents a whisper model or more specifically a model that can be used for transcriptions. + + `engine` selects the backend: "whisper" (faster-whisper, the default) or + "voxtral" (Mistral Voxtral via mlx-voxtral). For the Voxtral engine `repo` + holds the model repository/path and `path` is only a display placeholder + that no caller may read. """ name: str path: Path + engine: str = "whisper" + repo: Optional[str] = None class WhisperModelManager: @@ -58,13 +66,29 @@ def _collect_whisper_models(self, curpath: Path): ) continue - # Check here whether a `model.bin` file is present in - # the directory. This is necessary for a whisper model. + # faster-whisper models have a `model.bin`. A directory without one + # is either a different model format -- e.g. an MLX/Voxtral build, + # which uses safetensors and (for the shipped builds) is registered + # separately by its own engine -- or an incomplete/broken + # faster-whisper download. A safetensors dir is not a faster-whisper + # model, so we don't warn (that was noise for the expected Voxtral + # case); but we still log it at debug so a user-supplied safetensors + # model that never shows up in the picker is diagnosable. A dir with + # neither `model.bin` nor safetensors is a broken download -- warn. if not (entry / "model.bin").exists(): - logger.warning( - "Missing `model.bin` in model dir: %s. Ignoring.", - entry.absolute(), - ) + if any(entry.glob("*.safetensors")): + logger.debug( + "Skipping non-faster-whisper (safetensors) model directory: " + "%s. If this is an MLX/Voxtral build it is handled by its " + "own engine; the faster-whisper scanner ignores it.", + entry.absolute(), + ) + else: + logger.warning( + "Model directory has no `model.bin` (incomplete download?): " + "%s. Ignoring.", + entry.absolute(), + ) continue self.models[entry.name] = WhisperModel( diff --git a/pyinstaller/noScribe_macOS.spec b/pyinstaller/noScribe_macOS.spec index 61752be0..336074e8 100644 --- a/pyinstaller/noScribe_macOS.spec +++ b/pyinstaller/noScribe_macOS.spec @@ -22,7 +22,10 @@ a = Analysis( hookspath=[], hooksconfig={}, runtime_hooks=[], - excludes=['speechbrain'], + # The optional Voxtral stack stays out of the packaged app even when the + # build venv has it: a partial MLX copy would let the model picker offer + # Voxtral and then fail to run it. + excludes=['speechbrain', 'mlx', 'mlx_lm', 'mlx_voxtral'], noarchive=False, optimize=0, ) diff --git a/trans/noScribe.de.yml b/trans/noScribe.de.yml index 62dd440a..97c57769 100644 --- a/trans/noScribe.de.yml +++ b/trans/noScribe.de.yml @@ -47,6 +47,9 @@ de: ask_speaker_names_count: 'Sie haben %{n_names} Name(n) eingegeben, aber die Sprecher:innenzahl ist auf %{n_speakers} gesetzt. Die Namen werden dadurch womöglich den falschen Personen zugeordnet. Trotzdem starten?' warn_speaker_names_more_speakers: 'Es wurden mehr Sprecher:innen erkannt als Namen eingegeben (%{n_names}). Die überzähligen werden stattdessen nummeriert, im Anschluss an die benannten und in der Reihenfolge ihres Auftretens.' label_whisper_model: 'Modell:' + voxtral_path_long: 'Voxtral-Engine: Transkription mit Wort-Zeitmarken (Forced Alignment).' + voxtral_path_short: 'Voxtral-Engine: schnelle Transkription ohne Zeitmarken.' + voxtral_no_disfluencies: 'Hinweis: Die Voxtral-Engine unterstützt die Option "Füllworte" nicht; Füllworte erscheinen so, wie das Modell sie hört.' label_add_custom_models: 'KI-Modelle hinzufügen...' label_overlapping: 'Überlappende Sprache:' label_pause: 'Pausen markieren:' @@ -97,6 +100,7 @@ de: start_canceling: 'Abbrechen läuft...' start_transcription: 'Transkription...' loading_whisper: 'Whisper laden' + loading_voxtral: 'Voxtral laden' pyannote_cuda_retry: 'PyAnnote CUDA-Fehler erkannt. Erneuter Versuch auf der CPU.' whisper_cuda_retry: 'Whisper CUDA-Fehler erkannt. Erneuter Versuch auf der CPU.' vad: 'Sprachaktivitätserkennung...' diff --git a/trans/noScribe.en.yml b/trans/noScribe.en.yml index 04cb84d2..dd4bd9ea 100644 --- a/trans/noScribe.en.yml +++ b/trans/noScribe.en.yml @@ -48,6 +48,9 @@ en: ask_speaker_names_count: 'You entered %{n_names} name(s), but the number of speakers is set to %{n_speakers}. The names may be assigned to the wrong people. Start anyway?' warn_speaker_names_more_speakers: 'More speakers were detected than names were entered (%{n_names}). The extra speakers are numbered instead, continuing after the named ones in the order they appear.' label_whisper_model: 'Model:' + voxtral_path_long: 'Voxtral engine: transcription with word timestamps (forced alignment).' + voxtral_path_short: 'Voxtral engine: fast transcription without timestamps.' + voxtral_no_disfluencies: 'Note: the Voxtral engine does not support the disfluencies option; fillers are transcribed as the model hears them.' label_add_custom_models: 'Add AI model...' label_overlapping: 'Overlapping speech:' label_pause: 'Mark pause:' @@ -101,6 +104,7 @@ en: start_canceling: 'Canceling... (please wait a second)' start_transcription: 'Transcription...' loading_whisper: 'Loading whisper' + loading_voxtral: 'Loading Voxtral' pyannote_cuda_retry: 'PyAnnote CUDA error detected. Retrying on CPU.' whisper_cuda_retry: 'Whisper CUDA error detected. Retrying on CPU.' vad: 'Voice activity detection...' diff --git a/trans/noScribe.es.yml b/trans/noScribe.es.yml index e8270beb..a847f2cd 100644 --- a/trans/noScribe.es.yml +++ b/trans/noScribe.es.yml @@ -46,6 +46,9 @@ es: ask_speaker_names_count: 'Has introducido %{n_names} nombre(s), pero el número de oradores está en %{n_speakers}. Los nombres podrían asignarse a las personas equivocadas. ¿Iniciar de todos modos?' warn_speaker_names_more_speakers: 'Se han detectado más oradores que los nombres introducidos (%{n_names}). Los oradores sobrantes se numeran en su lugar, a continuación de los nombrados y por orden de aparición.' label_whisper_model: 'Modelo:' + voxtral_path_long: 'Motor Voxtral: transcripción con marcas de tiempo por palabra (alineación forzada).' + voxtral_path_short: 'Motor Voxtral: transcripción rápida sin marcas de tiempo.' + voxtral_no_disfluencies: 'Nota: el motor Voxtral no admite la opción de disfluencias; las muletillas se transcriben tal como las oye el modelo.' label_add_custom_models: 'Añadir modelo AI...' label_overlapping: 'Discurso solapado:' label_pause: 'Marca pausas:' @@ -96,6 +99,7 @@ es: start_canceling: 'Cancelando... (por favor, espera un momento)' start_transcription: 'Transcribiendo...' loading_whisper: 'Cargando whisper' + loading_voxtral: 'Cargando Voxtral' pyannote_cuda_retry: 'Se detectó un error CUDA en PyAnnote. Reintentando en la CPU.' whisper_cuda_retry: 'Se detectó un error CUDA en Whisper. Reintentando en la CPU.' vad: 'Detección de actividad de voz...' diff --git a/trans/noScribe.fr.yml b/trans/noScribe.fr.yml index ddaaa980..cecfac5c 100644 --- a/trans/noScribe.fr.yml +++ b/trans/noScribe.fr.yml @@ -46,6 +46,9 @@ fr: ask_speaker_names_count: 'Vous avez saisi %{n_names} nom(s), mais le nombre de locuteurs est réglé sur %{n_speakers}. Les noms risquent d''être attribués aux mauvaises personnes. Démarrer quand même ?' warn_speaker_names_more_speakers: 'Plus de locuteurs ont été détectés que de noms saisis (%{n_names}). Les locuteurs en trop sont numérotés à la place, à la suite des locuteurs nommés et dans l''ordre d''apparition.' label_whisper_model: "Modèle :" + voxtral_path_long: "Moteur Voxtral : transcription avec horodatage des mots (alignement forcé)." + voxtral_path_short: "Moteur Voxtral : transcription rapide sans horodatage." + voxtral_no_disfluencies: "Remarque : le moteur Voxtral ne prend pas en charge l'option des disfluences ; les hésitations sont transcrites telles que le modèle les entend." label_add_custom_models: "Ajouter un modèle d'IA..." label_overlapping: 'Chevauchements de parole' label_pause: 'Marquer les pauses :' @@ -96,6 +99,7 @@ fr: start_canceling: "Annulation en cours... (veuillez patienter un instant)" start_transcription: "Transcription en cours..." loading_whisper: "Chargement de whisper" + loading_voxtral: "Chargement de Voxtral" pyannote_cuda_retry: 'Erreur CUDA PyAnnote détectée. Nouvel essai sur le CPU.' whisper_cuda_retry: 'Erreur CUDA Whisper détectée. Nouvel essai sur le CPU.' vad: "Détection d'activité vocale..." diff --git a/trans/noScribe.it.yml b/trans/noScribe.it.yml index b7333f07..8ba2ca0d 100644 --- a/trans/noScribe.it.yml +++ b/trans/noScribe.it.yml @@ -46,6 +46,9 @@ it: ask_speaker_names_count: 'Hai inserito %{n_names} nome/i, ma il numero di oratori è impostato su %{n_speakers}. I nomi potrebbero essere assegnati alle persone sbagliate. Avviare comunque?' warn_speaker_names_more_speakers: 'Sono stati rilevati più oratori dei nomi inseriti (%{n_names}). Gli oratori in eccesso vengono invece numerati, di seguito a quelli con nome e in ordine di apparizione.' label_whisper_model: 'Modello:' + voxtral_path_long: 'Motore Voxtral: trascrizione con marche temporali per parola (allineamento forzato).' + voxtral_path_short: 'Motore Voxtral: trascrizione rapida senza marche temporali.' + voxtral_no_disfluencies: 'Nota: il motore Voxtral non supporta l''opzione disfluenze; gli intercalari vengono trascritti come il modello li sente.' label_add_custom_models: 'Aggiungi modello AI...' label_overlapping: 'Discorso sovrapposto:' label_pause: 'Segna le pause:' @@ -96,6 +99,7 @@ it: start_canceling: 'Annullamento in corso... (attendere un secondo)' start_transcription: 'Trascrizione in corso...' loading_whisper: 'Caricamento di whisper' + loading_voxtral: 'Caricamento di Voxtral' pyannote_cuda_retry: 'Rilevato errore CUDA di PyAnnote. Riprovo sulla CPU.' whisper_cuda_retry: 'Rilevato errore CUDA di Whisper. Riprovo sulla CPU.' vad: "Rilevamento dell'attività vocale..." diff --git a/trans/noScribe.ja.yml b/trans/noScribe.ja.yml index fec2c51c..525b1ef0 100644 --- a/trans/noScribe.ja.yml +++ b/trans/noScribe.ja.yml @@ -47,6 +47,9 @@ ja: ask_speaker_names_count: '%{n_names}件の名前が入力されましたが、話者数は%{n_speakers}に設定されています。名前が誤った人物に割り当てられる可能性があります。それでも開始しますか?' warn_speaker_names_more_speakers: '入力された名前(%{n_names}件)より多くの話者が検出されました。余分な話者には代わりに、名前の付いた話者に続く番号が登場順に付きます。' label_whisper_model: 'モデル:' + voxtral_path_long: 'Voxtralエンジン:単語タイムスタンプ付き文字起こし(強制アライメント)。' + voxtral_path_short: 'Voxtralエンジン:タイムスタンプなしの高速文字起こし。' + voxtral_no_disfluencies: '注:Voxtralエンジンはフィラーオプションに対応していません。フィラーはモデルが聞き取ったとおりに書き起こされます。' label_add_custom_models: 'AIモデルを追加...' label_overlapping: 'オーバーラッピング・スピーチ:' label_pause: 'マークはポーズをとる:' @@ -96,6 +99,7 @@ ja: start_canceling: 'キャンセル中...(しばらくお待ちください)' start_transcription: '転写を開始中...' loading_whisper: 'whisperの読み込み中' + loading_voxtral: 'Voxtralの読み込み中' pyannote_cuda_retry: 'PyAnnote の CUDA エラーを検出しました。CPU で再試行します。' whisper_cuda_retry: 'Whisper の CUDA エラーを検出しました。CPU で再試行します。' vad: '音声活動検出...' diff --git a/trans/noScribe.pt.yml b/trans/noScribe.pt.yml index 6892b289..fc151b00 100644 --- a/trans/noScribe.pt.yml +++ b/trans/noScribe.pt.yml @@ -47,6 +47,9 @@ pt: ask_speaker_names_count: 'Introduziu %{n_names} nome(s), mas o número de oradores está definido como %{n_speakers}. Os nomes podem ser atribuídos às pessoas erradas. Iniciar mesmo assim?' warn_speaker_names_more_speakers: 'Foram detetados mais oradores do que os nomes inseridos (%{n_names}). Os oradores excedentes são numerados em vez disso, a seguir aos nomeados e por ordem de aparecimento.' label_whisper_model: 'Modelo:' + voxtral_path_long: 'Motor Voxtral: transcrição com marcas de tempo por palavra (alinhamento forçado).' + voxtral_path_short: 'Motor Voxtral: transcrição rápida sem marcas de tempo.' + voxtral_no_disfluencies: 'Nota: o motor Voxtral não suporta a opção de disfluências; os vícios de linguagem são transcritos como o modelo os ouve.' label_add_custom_models: 'Adicionar modelo de IA...' label_overlapping: 'Sobreposição:' label_pause: 'Marcar pausas:' @@ -97,6 +100,7 @@ pt: start_canceling: 'Cancelando... (aguarde um segundo)' start_transcription: 'Transcrição...' loading_whisper: 'Carregando whisper' + loading_voxtral: 'Carregando Voxtral' pyannote_cuda_retry: 'Erro CUDA do PyAnnote detectado. Tentando novamente na CPU.' whisper_cuda_retry: 'Erro CUDA do Whisper detectado. Tentando novamente na CPU.' vad: 'Detecção de atividade de voz...' diff --git a/trans/noScribe.ru.yml b/trans/noScribe.ru.yml index 86fffdab..4affc535 100644 --- a/trans/noScribe.ru.yml +++ b/trans/noScribe.ru.yml @@ -47,6 +47,9 @@ ru: ask_speaker_names_count: 'Введено имён: %{n_names}, но число говорящих задано как %{n_speakers}. Имена могут быть присвоены не тем людям. Всё равно начать?' warn_speaker_names_more_speakers: 'Обнаружено больше говорящих, чем введено имён (%{n_names}). Остальные говорящие вместо этого нумеруются вслед за названными по имени, в порядке появления.' label_whisper_model: 'Модель:' + voxtral_path_long: 'Движок Voxtral: транскрипция с пословными временными метками (принудительное выравнивание).' + voxtral_path_short: 'Движок Voxtral: быстрая транскрипция без временных меток.' + voxtral_no_disfluencies: 'Примечание: движок Voxtral не поддерживает опцию слов-паразитов; заполнители записываются так, как их слышит модель.' label_add_custom_models: 'Добавить модель искусственного интеллекта...' label_overlapping: 'Перекрытие речи:' label_pause: 'Отметьте паузы:' @@ -97,6 +100,7 @@ ru: start_canceling: 'Отмена... (пожалуйста, подождите)' start_transcription: 'Транскрибация...' loading_whisper: 'Загрузка whisper' + loading_voxtral: 'Загрузка Voxtral' pyannote_cuda_retry: 'Обнаружена ошибка CUDA в PyAnnote. Повторяю на CPU.' whisper_cuda_retry: 'Обнаружена ошибка CUDA в Whisper. Повторяю на CPU.' vad: 'Обнаружение голосовой активности...' diff --git a/trans/noScribe.zh-CN.yml b/trans/noScribe.zh-CN.yml index ef8cce98..fa122be8 100644 --- a/trans/noScribe.zh-CN.yml +++ b/trans/noScribe.zh-CN.yml @@ -46,6 +46,9 @@ zh-CN: &defaults ask_speaker_names_count: '您输入了 %{n_names} 个名字,但说话人数量设置为 %{n_speakers}。名字可能会分配给错误的人。仍要开始吗?' warn_speaker_names_more_speakers: '检测到的说话人数量超过输入的名字数量(%{n_names})。多余的说话人将改为编号,接在已命名的说话人之后,按出现顺序排列。' label_whisper_model: '人工智能模型:' + voxtral_path_long: 'Voxtral引擎:带逐词时间戳的转录(强制对齐)。' + voxtral_path_short: 'Voxtral引擎:无时间戳的快速转录。' + voxtral_no_disfluencies: '注意:Voxtral引擎不支持语气词选项;语气词将按模型听到的原样转录。' label_add_custom_models: '添加人工智能模型...' label_overlapping: '重叠发言:' label_pause: '标记停顿:' @@ -96,6 +99,7 @@ zh-CN: &defaults start_canceling: '正在取消...(请稍候)' start_transcription: '正在转录...' loading_whisper: '正在加载whisper' + loading_voxtral: '正在加载Voxtral' pyannote_cuda_retry: '检测到 PyAnnote 的 CUDA 错误。正在使用 CPU 重试。' whisper_cuda_retry: '检测到 Whisper 的 CUDA 错误。正在使用 CPU 重试。' vad: '语音活动检测...' From a4efee739ec05fa83a149bd4e853de7453c79853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=A4mmerer?= Date: Wed, 23 Sep 2026 15:12:36 +0200 Subject: [PATCH 3/5] Guard the Voxtral engine with the regression tests its defects produced, all of which run or skip cleanly without the MLX stack Most of these pin a failure found on real recordings: loop breaking, forced-alignment caps and density, a lost opening, prefix salvage, translated passes, the log-Mel floor. test_forced_align_stability.py holds the numpy Viterbi to a reference recorded from torchaudio (tests/data/forced_align_ref.npz), and test_worker_import_lightweight.py now also keeps the Voxtral worker stdlib-only at import. test_voxtral_transcribe_guards.py drives transcribe() with the model and the aligners stubbed out, for what lies between the pieces: the language that reaches the model, an evicted aligner, an empty file, a model that cannot be fetched. Co-Authored-By: Claude Fable 5.1 Co-Authored-By: Claude Opus 5.5 --- tests/data/forced_align_ref.npz | Bin 0 -> 910450 bytes tests/test_align_input_level.py | 244 ++++++ tests/test_align_language.py | 235 ++++++ tests/test_ctc_align.py | 169 ++++ tests/test_cut_and_cue_quality.py | 203 +++++ tests/test_fast_generate.py | 110 +++ tests/test_forced_align_cap.py | 63 ++ tests/test_forced_align_density.py | 148 ++++ tests/test_forced_align_stability.py | 126 +++ tests/test_loop_breaker.py | 321 ++++++++ tests/test_lost_head_recovery.py | 184 +++++ tests/test_mel_floor.py | 280 +++++++ tests/test_merged_embeddings.py | 137 ++++ tests/test_quant_summary.py | 81 ++ tests/test_quantize_group_guard.py | 99 +++ tests/test_salvage_prefix_alignment.py | 268 +++++++ tests/test_transcript_corrections.py | 981 ++++++++++++++++++++++++ tests/test_turn_split.py | 254 ++++++ tests/test_voxtral_pin.py | 57 ++ tests/test_voxtral_safety_guards.py | 227 ++++++ tests/test_voxtral_smoke.py | 87 +++ tests/test_voxtral_transcribe_guards.py | 328 ++++++++ tests/test_voxtral_worker.py | 139 ++++ tests/test_word_prob_format.py | 64 ++ tests/test_worker_import_lightweight.py | 5 +- 25 files changed, 4808 insertions(+), 2 deletions(-) create mode 100644 tests/data/forced_align_ref.npz create mode 100644 tests/test_align_input_level.py create mode 100644 tests/test_align_language.py create mode 100644 tests/test_ctc_align.py create mode 100644 tests/test_cut_and_cue_quality.py create mode 100644 tests/test_fast_generate.py create mode 100644 tests/test_forced_align_cap.py create mode 100644 tests/test_forced_align_density.py create mode 100644 tests/test_forced_align_stability.py create mode 100644 tests/test_loop_breaker.py create mode 100644 tests/test_lost_head_recovery.py create mode 100644 tests/test_mel_floor.py create mode 100644 tests/test_merged_embeddings.py create mode 100644 tests/test_quant_summary.py create mode 100644 tests/test_quantize_group_guard.py create mode 100644 tests/test_salvage_prefix_alignment.py create mode 100644 tests/test_transcript_corrections.py create mode 100644 tests/test_turn_split.py create mode 100644 tests/test_voxtral_pin.py create mode 100644 tests/test_voxtral_safety_guards.py create mode 100644 tests/test_voxtral_smoke.py create mode 100644 tests/test_voxtral_transcribe_guards.py create mode 100644 tests/test_voxtral_worker.py create mode 100644 tests/test_word_prob_format.py diff --git a/tests/data/forced_align_ref.npz b/tests/data/forced_align_ref.npz new file mode 100644 index 0000000000000000000000000000000000000000..9c44ffc2258680ff4daab21f9d171b502ea66164 GIT binary patch literal 910450 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 z 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 z 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 N^>kk|p!EJO4Cm+LD*mfO=6(dW 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

^DZjJUiK$b~jH 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 rf{Ypb| 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 literal 0 HcmV?d00001 diff --git a/tests/test_align_input_level.py b/tests/test_align_input_level.py new file mode 100644 index 00000000..e11e66da --- /dev/null +++ b/tests/test_align_input_level.py @@ -0,0 +1,244 @@ +"""The aligner sees each window at zero mean and unit variance, whatever the level. + +Found on a voice 17 dB below its neighbour: fed the raw signal, the aligner +stamped its words 10-20 s early, into the louder speaker's time. Mechanism and +measurement are in the comment in _Aligner._forward_windows. + +The model here is a stand-in whose logits are a plain function of its input, +so any level that reaches it shows up in the emissions. +""" +import types + +import numpy as np +import pytest + +torch = pytest.importorskip("torch") + +from noScribe import voxtral_engine as ve # noqa: E402 +from noScribe.voxtral_engine import EMISSION_WINDOW_SEC, SAMPLE_RATE, _Aligner # noqa: E402 + +FRAME = 320 # samples per emission frame, as wav2vec2's 20 ms stride +WIN, MIN_WIN = int(EMISSION_WINDOW_SEC * SAMPLE_RATE), int(0.025 * SAMPLE_RATE) # as _emission passes them + + +class _LevelSensitiveModel: + """logits[t] = [mean, rms, max] of frame t -- no normalisation of its own.""" + + def __init__(self): + self.seen = [] + + def __call__(self, batch): + x = batch[0] + self.seen.append(x.clone()) + frames = x[: len(x) // FRAME * FRAME].reshape(-1, FRAME) + logits = torch.stack([frames.mean(1), frames.pow(2).mean(1).sqrt(), frames.max(1).values], 1) + return types.SimpleNamespace(logits=logits.unsqueeze(0)) + + +def _aligner(): + al = object.__new__(_Aligner) + al._torch = torch + al.device = "cpu" + al.model = _LevelSensitiveModel() + return al + + +def _speech_like(seconds, seed=0): + rng = np.random.default_rng(seed) + n = int(seconds * SAMPLE_RATE) + envelope = 0.5 + 0.5 * np.sin(np.linspace(0, 40 * np.pi, n)) ** 2 + return (rng.standard_normal(n) * envelope * 0.1).astype(np.float32) + + +@pytest.mark.parametrize("gain", [0.14, 0.3, 3.0]) # 0.14 = the 17 dB of the finding +def test_emissions_do_not_depend_on_the_recording_level(gain): + """Equal up to the extractor's 1e-7 epsilon, which at 17 dB down is ~0.1 % of + the variance (~2e-3 in these emissions); without the normalisation the gap is + ~0.4, so the tolerance still tells the two apart.""" + audio = _speech_like(45) # three windows at 20 s, the last one short + ref = _aligner()._forward_windows(torch.from_numpy(audio), WIN, MIN_WIN) + got = _aligner()._forward_windows(torch.from_numpy(audio * gain), WIN, MIN_WIN) + assert len(ref) == len(got) == 3 + for a, b in zip(ref, got): + np.testing.assert_allclose(a.numpy(), b.numpy(), atol=1e-2) + + +def _extractor_norm(x): + """Wav2Vec2FeatureExtractor.zero_mean_unit_var_norm without padding, in numpy.""" + x = np.asarray(x, dtype=np.float64) + return (x - x.mean()) / np.sqrt(x.var() + 1e-7) + + +def test_every_window_reaches_the_model_normalised_on_its_own(): + """Per window, not per call: a loud window must not set the scale of a quiet one, + a DC offset is removed, and a very quiet window keeps the extractor's epsilon.""" + loud, quiet = _speech_like(20, seed=1), _speech_like(20, seed=2) * 0.005 + 0.3 + al = _aligner() + al._forward_windows(torch.from_numpy(np.concatenate([loud, quiet])), WIN, MIN_WIN) + assert len(al.model.seen) == 2 + for x, src in zip(al.model.seen, (loud, quiet)): + np.testing.assert_allclose(x.numpy(), _extractor_norm(src), atol=2e-4) + + +def test_matches_the_feature_extractor_itself(): + transformers = pytest.importorskip("transformers") + x = _speech_like(7, seed=3) * 0.02 + 0.01 + al = _aligner() + al._forward_windows(torch.from_numpy(x), WIN, MIN_WIN) + want = transformers.Wav2Vec2FeatureExtractor.zero_mean_unit_var_norm([x], None)[0] + np.testing.assert_allclose(al.model.seen[0].numpy(), want, atol=2e-4) + + +def test_a_short_rest_takes_the_scale_of_the_window_that_ends_with_it(): + """The rest after the last full window must not be scaled on its own: 0.3 s of + room tone after speech would otherwise reach the model at unit variance.""" + speech = _speech_like(20, seed=4) + tone = (np.random.default_rng(5).standard_normal(int(0.3 * SAMPLE_RATE)) * 1e-3).astype(np.float32) + x = np.concatenate([speech, tone]) + al = _aligner() + al._forward_windows(torch.from_numpy(x), WIN, MIN_WIN) + assert len(al.model.seen) == 2 + ref = x[len(x) - WIN:] + want = (tone - ref.astype(np.float64).mean()) / np.sqrt(ref.astype(np.float64).var() + 1e-7) + np.testing.assert_allclose(al.model.seen[1].numpy(), want, atol=2e-4) + assert float(al.model.seen[1].std()) < 0.05 # stays room tone, not speech + + +def test_silence_does_not_blow_up(): + """An all-zero window has no variance; the epsilon keeps it finite.""" + al = _aligner() + out = al._forward_windows(torch.zeros(5 * SAMPLE_RATE), WIN, MIN_WIN) + assert np.isfinite(out[0].numpy()).all() + + +# --------------------------------------------------------------------------- # +# The level-matched second alignment (_AlignerPool.align_words) +# --------------------------------------------------------------------------- # + + +def test_level_matching_survives_digital_silence(): + """The measurement's envelope (scipy's running filter) dipped below zero after a + loud stretch; its root was NaN, the percentile floor NaN, and with it the whole + pass -- every word then landed in the last second. Exact zeros must stay finite.""" + x = np.concatenate([_speech_like(10, seed=6), np.zeros(8 * SAMPLE_RATE, np.float32), _speech_like(10, seed=7) * 0.1]) + y = ve._level_matched(x) + assert np.isfinite(y).all() + assert y.dtype == np.float32 and len(y) == len(x) + + +@pytest.mark.parametrize("pause_sec, lifted", [(2, False), (10, True)]) +def test_level_matching_lifts_the_quiet_voice_and_a_pause_only_past_the_floor(pause_sec, lifted): + """The floor is the envelope's LEVEL_FLOOR_PERCENTILE percentile: a pause under + that share of the pass stays a pause, a longer one is lifted with the rest -- + which is what the coverage check in _AlignerPool.align_words guards against.""" + loud, quiet = _speech_like(10, seed=8), _speech_like(10, seed=9) * 0.1 # 20 dB apart + room = (np.random.default_rng(10).standard_normal(pause_sec * SAMPLE_RATE) * 1e-4).astype(np.float32) + y = ve._level_matched(np.concatenate([loud, room, quiet])) + rms = lambda a: float(np.sqrt(np.mean(np.square(a, dtype=np.float64)))) + n, p = 10 * SAMPLE_RATE, pause_sec * SAMPLE_RATE + a, b, c = y[:n], y[n + SAMPLE_RATE // 2:n + p - SAMPLE_RATE // 2], y[n + p:] + assert 0.7 < rms(c) / rms(a) < 1.4 # the quiet voice now as loud as the loud one + assert (rms(b) > 0.3 * rms(a)) == lifted + + +def test_level_matching_lifts_no_more_than_its_cap(): + """Over 30 % near-silence puts the percentile floor at the dither's own level, + ~100 dB down: uncapped, the dither would come out as loud as the speech. An + all-zero pass has nothing to level and comes back as it was.""" + speech = _speech_like(5, seed=13) + dither = (np.random.default_rng(14).standard_normal(8 * SAMPLE_RATE) * 1e-6).astype(np.float32) + y = ve._level_matched(np.concatenate([speech, dither])) + assert np.isfinite(y).all() + assert np.abs(y[-4 * SAMPLE_RATE:]).max() < 0.2 # capped: -100 dB + 60 dB; uncapped peaks near 4 + silent = np.zeros(4 * SAMPLE_RATE, np.float32) + assert np.array_equal(ve._level_matched(silent), silent) + + +def test_level_matching_moves_its_mean_as_the_measured_filter_did(): + """Agrees with the scipy envelope the measurement used, edges included.""" + ndimage = pytest.importorskip("scipy.ndimage") + x = np.concatenate([_speech_like(3, seed=11), _speech_like(3, seed=12) * 0.05]) + env = np.sqrt(np.maximum(ndimage.uniform_filter1d(np.square(x, dtype=np.float64), + int(ve.LEVEL_ENVELOPE_SEC * SAMPLE_RATE)), 0)) + want = x / np.maximum(env, np.percentile(env, ve.LEVEL_FLOOR_PERCENTILE)) + np.testing.assert_allclose(ve._level_matched(x), want, rtol=1e-4, atol=1e-6) + + +def test_speech_coverage_counts_speech_with_a_word_nearby(): + turns = [(0.0, 10.0, "A"), (20.0, 30.0, "B")] + words = [{"start": 100.0 + t, "end": 100.0 + t + 0.3} for t in np.arange(0, 10, 0.8)] # t_offset 100 + assert abs(ve._speech_coverage(words, turns, 100.0) - 0.5) < 0.03 + assert ve._speech_coverage([], turns, 100.0) == 0.0 + + +class _FakeAligner: + """Spreads the words evenly over `first` seconds of the window on the first call + and over `second` seconds on the second (level-matched) one.""" + + def __init__(self, first=20.0, second=40.0, second_prob=0.9): + self.calls, self.spans, self.second_prob = [], (first, second), second_prob + + def align_words(self, words, window, t_offset=0.0): + self.calls.append(window) + span = self.spans[min(len(self.calls), 2) - 1] + step = span / len(words) + prob = 0.9 if len(self.calls) == 1 else self.second_prob + return [{"word": w, "start": t_offset + i * step, "end": t_offset + i * step + 0.3, "prob": prob} + for i, w in enumerate(words)] + + +def _pool(fake): + pool = ve._AlignerPool("en", None) + pool.aligner_for = lambda text, remember=True: fake + return pool + + +WORDS = [f"w{i}" for i in range(60)] +WINDOW = np.zeros(40 * SAMPLE_RATE, np.float32) +TWO = [(0.0, 20.0, "A"), (20.0, 40.0, "B")] + + +def test_quiet_speakers_empty_turn_is_filled_by_the_level_matched_alignment(): + fake = _FakeAligner() + got = _pool(fake).align_words(WORDS, WINDOW, 5.0, turns=TWO) + assert len(fake.calls) == 2 + assert max(w["end"] for w in got) > 5.0 + 30.0 # B's turn has words now + + +@pytest.mark.parametrize("turns", [None, [(0.0, 20.0, "A"), (20.0, 40.0, "A")]]) +def test_without_two_diarized_speakers_one_alignment_is_all(turns): + fake = _FakeAligner() + _pool(fake).align_words(WORDS, WINDOW, 5.0, turns=turns) + assert len(fake.calls) == 1 + + +def test_no_second_alignment_where_it_could_not_win(): + """Coverage >= 1 - margin already: the second could never add the margin.""" + fake = _FakeAligner() + _pool(fake).align_words(WORDS, WINDOW, 5.0, turns=[(0.0, 20.0, "A"), (19.5, 20.0, "B")]) + assert len(fake.calls) == 1 + + +@pytest.mark.parametrize("margin, taken", [(0.05, True), (0.4, False)]) +def test_the_second_alignment_is_taken_only_past_the_margin(monkeypatch, margin, taken): + """First covers ~31 % of the speech, second ~61 %: +30 points clears the default + margin and not one of 40 -- and 31 % is below 1 - 0.4, so it does run both.""" + monkeypatch.setattr(ve, "LEVEL_COVERAGE_MARGIN", margin) + fake = _FakeAligner(first=12.0, second=24.0) + got = _pool(fake).align_words(WORDS, WINDOW, 5.0, turns=TWO) + assert len(fake.calls) == 2 + assert (max(w["end"] for w in got) > 5.0 + 20.0) == taken + + +def test_a_second_alignment_that_covers_no_more_is_not_taken(): + fake = _FakeAligner(first=20.0, second=20.0) + got = _pool(fake).align_words(WORDS, WINDOW, 5.0, turns=TWO) + assert len(fake.calls) == 2 and max(w["end"] for w in got) < 5.0 + 21.0 + + +def test_a_second_alignment_spread_by_the_fallback_is_not_taken(): + """Evenly spread words (prob 0.0, a window the DP could not align) cover the + speech well without being aligned at all; they must not win on coverage.""" + fake = _FakeAligner(second_prob=0.0) + got = _pool(fake).align_words(WORDS, WINDOW, 5.0, turns=TWO) + assert len(fake.calls) == 2 and max(w["end"] for w in got) < 5.0 + 21.0 diff --git a/tests/test_align_language.py b/tests/test_align_language.py new file mode 100644 index 00000000..e5827a88 --- /dev/null +++ b/tests/test_align_language.py @@ -0,0 +1,235 @@ +"""Tests for text-based aligner selection (_detect_language / _AlignerPool). + +Auto jobs used to fall back to the romanised multilingual aligner even for +plain German audio -- measurably the weakest choice for German word +boundaries. The pool reads the language off each chunk's transcribed text +(which exists before that chunk is aligned) and picks the char-native +per-language model; mixed text without a dominant language keeps the +multilingual fallback, and an explicit-but-wrong language setting warns. +""" +import pytest + +import noScribe.voxtral_engine as v +from noScribe.voxtral_engine import ( + ALIGN_MODELS, + ALIGN_MODEL_MULTILINGUAL, + _AlignerPool, + _detect_language, +) + +GERMAN = ("Und dann haben wir gesagt, dass wir das nicht einfach so machen, " + "weil die Sache ja auch für die anderen wichtig ist. Aber wenn wir " + "ehrlich sind, ist das schon ein großer Schritt, und ich glaube, " + "dass wir jetzt auf einem guten Weg sind. ") * 4 +ENGLISH = ("And then you know we just said that this is not what they wanted, " + "but if you think about it, they would have been fine with the " + "idea, because it was just like the other things we did. ") * 4 +# Denglisch: German matrix with English phrases mixed in -- the German +# function words still dominate, so the German model must win. +DENGLISCH = ("Und dann haben wir das Mindset komplett geändert, you know, " + "weil die Journey ja auch ein Commitment ist. Aber wenn wir das " + "nicht committen, dann ist das eben not the end of the world, " + "und ich glaube, dass wir da jetzt all in gehen sollten. ") * 4 +RUSSIAN = "Мы посмотрели на это и решили, что так будет лучше для всех. " * 6 +JAPANESE = "それでは、今日はこのテーマについて話しましょう。よろしくお願いします。" * 6 + + +def test_detects_the_major_languages(): + assert _detect_language(GERMAN)[0] == "de" + assert _detect_language(ENGLISH)[0] == "en" + assert _detect_language(RUSSIAN)[0] == "ru" + assert _detect_language(JAPANESE)[0] == "ja" + + +def test_denglisch_resolves_to_the_majority_language(): + assert _detect_language(DENGLISCH)[0] == "de" + + +def test_balanced_mix_and_thin_evidence_stay_undetected(): + assert _detect_language(GERMAN[:200] + " " + ENGLISH[:200] + + GERMAN[200:400] + ENGLISH[200:400])[0] is None + assert _detect_language("Hallo und danke.")[0] is None + assert _detect_language("")[0] is None + + +# --------------------------------------------------------------------------- # +# _AlignerPool +# --------------------------------------------------------------------------- # +@pytest.fixture +def fake_aligner(monkeypatch): + loads = [] + + class _Fake: + def __init__(self, model): + loads.append(model) + self.model = model + + monkeypatch.setattr(v, "_Aligner", _Fake) + monkeypatch.setattr(v, "_unfetchable", lambda model: False) # no network in tests + return loads + + +def test_auto_picks_the_char_native_model(fake_aligner): + logs = [] + pool = _AlignerPool(None, lambda lvl, m: logs.append((lvl, m))) + a = pool.aligner_for(GERMAN) + assert a.model == ALIGN_MODELS["de"] + assert fake_aligner == [ALIGN_MODELS["de"]] + assert any("Detected language 'de'" in m for _, m in logs) + + +def test_auto_without_dominant_language_uses_multilingual(fake_aligner): + pool = _AlignerPool(None, None) + a = pool.aligner_for("Hallo und danke.") # thin evidence + assert a.model == ALIGN_MODEL_MULTILINGUAL + + +def test_language_change_between_chunks_switches_and_caches(fake_aligner): + pool = _AlignerPool(None, None) + assert pool.aligner_for(GERMAN).model == ALIGN_MODELS["de"] + assert pool.aligner_for(ENGLISH).model == ALIGN_MODELS["en"] + assert pool.aligner_for(GERMAN).model == ALIGN_MODELS["de"] # cached + assert fake_aligner == [ALIGN_MODELS["de"], ALIGN_MODELS["en"]] # 2 loads only + + +def test_cache_is_bounded(fake_aligner): + pool = _AlignerPool(None, None) + pool.aligner_for(GERMAN) + pool.aligner_for(ENGLISH) + pool.aligner_for(RUSSIAN) + assert len(pool._cache) == _AlignerPool.MAX_CACHED + + +def test_undetected_chunk_keeps_the_previous_choice(fake_aligner): + pool = _AlignerPool(None, None) + pool.aligner_for(GERMAN) + a = pool.aligner_for("Hm. Ja. Okay.") # no evidence -> stick with de + assert a.model == ALIGN_MODELS["de"] + assert fake_aligner == [ALIGN_MODELS["de"]] + + +def test_explicit_language_stays_but_mismatch_warns_once(fake_aligner): + logs = [] + pool = _AlignerPool("en", lambda lvl, m: logs.append((lvl, m))) + a = pool.aligner_for(GERMAN) # user picked English, audio is German + assert a.model == ALIGN_MODELS["en"] # explicit choice is respected + warns = [m for lvl, m in logs if lvl == "warn"] + assert len(warns) == 1 and "looks like 'de'" in warns[0] + pool.aligner_for(GERMAN) # second chunk: no repeat warning + assert len([m for lvl, m in logs if lvl == "warn"]) == 1 + + +def test_explicit_matching_language_does_not_warn(fake_aligner): + logs = [] + pool = _AlignerPool("de", lambda lvl, m: logs.append((lvl, m))) + assert pool.aligner_for(GERMAN).model == ALIGN_MODELS["de"] + assert not [m for lvl, m in logs if lvl == "warn"] + + +def test_missing_letters_use_the_spelling_the_model_was_trained_on(): + """Letters outside an aligner's vocabulary are respelled, not dropped. + + Dropping them silently cost real accuracy: the German model has no sharp s, + so a word ending in one had its closing /s/ unaccounted for and ended up to + 160 ms too early. The models say which spelling to use -- decoded freely the + German aligner writes "weiss", and the romanised multilingual one writes + "naechste" -- so the sharp s becomes "ss" and the umlauts become "ae"/"oe"/ + "ue". Substitution only ever applies where the letter is genuinely missing: + the German model carries the umlauts and must keep them. + """ + class _FakeAligner: + _tokenize = v._Aligner._tokenize + + def __init__(self, chars): + self.vocab = {c: i for i, c in enumerate(chars, start=1)} + self.delim = None + + german = _FakeAligner("abcdefghijklmnopqrstuvwxyzäöü") + tokens, _ = german._tokenize(["weiß"]) + assert [c for c in "weiss"] == [ + k for t in tokens for k, i in german.vocab.items() if i == t + ] + # the umlauts it does carry stay themselves + tokens, _ = german._tokenize(["über"]) + assert german.vocab["ü"] == tokens[0] + + mms = _FakeAligner("abcdefghijklmnopqrstuvwxyz'") + tokens, tok_word = mms._tokenize(["über"]) + assert [c for c in "ueber"] == [ + k for t in tokens for k, i in mms.vocab.items() if i == t + ] + # every token still points at its own word + assert tok_word == [0] * len(tokens) + + # a letter with no mapping is still dropped rather than guessed at + assert mms._tokenize(["café"])[0] == [mms.vocab[c] for c in "caf"] + + +def test_unspellable_characters_become_a_wildcard_not_a_hole(): + """A word the model has no letters for still gets tokens of its own. + + Digits are the common case: "20 Sitzungen a 50 Minuten" gave the aligner + nothing to place, so `_stamps_from_spans` interpolated those words across + the gap between their neighbours and they came out too wide, carrying the + prob 0.0 that means "not actually aligned". With the wildcard the DP places + them on the audio -- measured identical, to the millisecond, to spelling the + numbers out in German, and 80 ms from the interpolation on average. + """ + class _FakeAligner: + _tokenize = v._Aligner._tokenize + + def __init__(self): + self.vocab = {c: i for i, c in enumerate("abcdefghijklmnopqrstuvwxyz", 1)} + self.delim = None + self.wild = 99 + + al = _FakeAligner() + # off by default, so the prefix-salvage path keeps its unambiguous star + assert al._tokenize(["20"])[0] == [] + tokens, tok_word = al._tokenize(["20"], wildcard=True) + assert tokens == [99, 99] and tok_word == [0, 0] + # punctuation is not audible and must not claim frames + assert al._tokenize(["!?"], wildcard=True)[0] == [] + # a model without the extra column never emits one + al.wild = None + assert al._tokenize(["20"], wildcard=True)[0] == [] + + +def test_an_unusable_respelling_still_reaches_the_wildcard(): + """A mapping that exists but cannot be spelled must not swallow the letter. + + `_tokenize` used to treat "a fallback entry exists" as "the fallback + worked", so a vocabulary missing one of the replacement letters dropped the + character silently -- the word lost an anchor and fell back to interpolation + with nothing in the log. Both shipped models carry a-z so no current entry + hits this, which is exactly why it needs a test. + """ + class _FakeAligner: + _tokenize = v._Aligner._tokenize + + def __init__(self, chars): + self.vocab = {c: i for i, c in enumerate(chars, start=1)} + self.delim = None + self.wild = 99 + + without_s = _FakeAligner("abcdefghijklmnopqrtuvwxyz") # no "s" for "ss" + assert without_s._tokenize(["weiß"], wildcard=True)[0][-1] == 99 + with_s = _FakeAligner("abcdefghijklmnopqrstuvwxyz") + assert 99 not in with_s._tokenize(["weiß"], wildcard=True)[0] + # without the wildcard the old behaviour stands: the letter is dropped + assert without_s._tokenize(["weiß"])[0] == [ + without_s.vocab[c] for c in "wei" + ] + + +def test_a_language_the_detector_cannot_name_draws_no_warning(fake_aligner): + """All Cyrillic reads as Russian, so a correctly pinned Ukrainian file was + told "set to 'uk' but the transcript looks like 'ru' ... check the language + setting" -- the one advice that is wrong for it.""" + ukrainian = ("Ми ще раз усе обговорили, бо я не був певен, чи це підходить, " + "але тепер усе зрозуміло, і ми вже рухаємося далі, бо коли інші " + "ще раз подивляться, у мене є відчуття, що все буде добре.") + logs = [] + pool = _AlignerPool("uk", lambda lvl, m: logs.append((lvl, m))) + pool.aligner_for(ukrainian) + assert not [m for lvl, m in logs if lvl == "warn"] diff --git a/tests/test_ctc_align.py b/tests/test_ctc_align.py new file mode 100644 index 00000000..e988f3ab --- /dev/null +++ b/tests/test_ctc_align.py @@ -0,0 +1,169 @@ +"""Independent oracles for noScribe.ctc_align, the numpy forced-alignment DP. + +The recorded torchaudio reference in test_forced_align_stability.py is the +acceptance test; it has two blind spots that this file closes, both found the +hard way while writing the DP: + +* every recorded case uses ``blank=0``, so a DP that hard-codes 0 as the blank + label passes all 43 of them and mislabels every blank frame in production + with any other blank index -- here the blank varies; +* random emissions never tie exactly, so the tie order is never exercised -- + here the log-probs are small integers, which makes ties common, and the + oracle is brute force over every path, which has no tie order at all. + +Neither oracle needs torch, so this runs on the Linux CI without the stack. +""" +import itertools + +import numpy as np +import pytest + +from noScribe.ctc_align import TokenSpan, adjacent_repeats, forced_align, merge_tokens + + +def _collapse(path, blank): + out, prev = [], None + for x in path: + if x != blank and x != prev: + out.append(int(x)) + prev = x + return out + + +def _best_valid_score(lp, targets, blank): + """Highest total log-prob over *every* frame labelling that spells the + targets -- exhaustive, so it is an oracle rather than another Viterbi.""" + T, C = lp.shape + best = None + for cand in itertools.product(range(C), repeat=T): + if _collapse(cand, blank) == list(targets): + s = sum(float(lp[t, c]) for t, c in enumerate(cand)) + best = s if best is None or s > best else best + return best + + +def _scalar_viterbi(lp, targets, blank): + """The textbook DP in plain Python ints and floats: no uint8 backtrace, no + vectorisation, nothing shared with the implementation under test except + the tie order (2 only if strictly best, 1 only if strictly better than 0).""" + T, L = lp.shape[0], len(targets) + S = 2 * L + 1 + ninf = float("-inf") + alpha = [ninf] * S + alpha[0], alpha[1] = float(lp[0, blank]), float(lp[0, targets[0]]) + back = [[0] * S for _ in range(T)] + for t in range(1, T): + nxt = [ninf] * S + nxt[0] = alpha[0] + float(lp[t, blank]) + for i in range(1, S): + x0, x1, x2 = alpha[i], alpha[i - 1], ninf + if i % 2 and i > 1 and targets[i // 2] != targets[i // 2 - 1]: + x2 = alpha[i - 2] + if x2 > x1 and x2 > x0: + r, back[t][i] = x2, 2 + elif x1 > x0: + r, back[t][i] = x1, 1 + else: + r, back[t][i] = x0, 0 + lab = blank if i % 2 == 0 else targets[i // 2] + nxt[i] = r + float(lp[t, lab]) + alpha = nxt + s = S - 1 if alpha[S - 1] > alpha[S - 2] else S - 2 + path = [0] * T + for t in range(T - 1, -1, -1): + path[t] = blank if s % 2 == 0 else int(targets[s // 2]) + s -= back[t][s] + return path + + +def _tiny_cases(blank, n, seed): + rng = np.random.default_rng(seed) + out = [] + while len(out) < n: + C = int(rng.integers(3, 6)) + T = int(rng.integers(2, 6)) + L = int(rng.integers(1, 4)) + tg = [int(x) for x in rng.integers(0, C, size=L) if x != blank] + if not tg or blank >= C: + continue + if T < len(tg) + adjacent_repeats(tg): + continue + # small integers: exact ties everywhere, which is the point + lp = -rng.integers(0, 3, size=(T, C)).astype(np.float32) + out.append((lp, tg)) + return out + + +@pytest.mark.parametrize("blank", [0, 2, 4]) +def test_returns_a_most_likely_valid_path_on_every_tiny_case(blank): + for lp, tg in _tiny_cases(blank, n=120, seed=blank): + path, scores = forced_align(lp, tg, blank=blank) + assert _collapse(path, blank) == tg, (path, tg) + assert np.array_equal(scores, lp[np.arange(len(path)), path]) + assert float(scores.sum()) == pytest.approx(_best_valid_score(lp, tg, blank), abs=1e-5) + + +def test_the_torchaudio_tie_defect_is_not_reproduced(): + """pytorch/audio#4221: on this input torchaudio 2.11 returns [1, 2, 2] with + score -3.0 because its chain falls through to the worst predecessor when + the advance-one and advance-two candidates tie. The optimum is -1.0.""" + lp = np.array([[0., -1., -1.], [0., -1., -2.], [-2., 0., 0.]], dtype=np.float32) + path, scores = forced_align(lp, [1, 2], blank=0) + assert _collapse(path, 0) == [1, 2] + assert float(scores.sum()) == -1.0 + + +@pytest.mark.parametrize("blank", [0, 7]) +def test_matches_a_scalar_viterbi_past_the_uint8_range(blank): + """N = 2L+1 > 255 exercises the backtrace cursor: written as `s -= bt[t, s]` + it overflows as uint8 and was invisible on every small case.""" + rng = np.random.default_rng(7) + T, C, L = 400, 12, 160 + tg = [int(x) for x in rng.integers(0, C, size=L) if x != blank] + logits = rng.standard_normal((T, C)).astype(np.float32) + logits[:, blank] += 1.5 # blanks must actually reach the path + lp = logits - np.log(np.exp(logits).sum(-1, keepdims=True)) + assert 2 * len(tg) + 1 > 255 + path, scores = forced_align(lp, tg, blank=blank) + assert path.tolist() == _scalar_viterbi(lp, tg, blank) + assert _collapse(path, blank) == tg + + +def test_merge_tokens_has_an_exclusive_end_and_a_mean_score(): + path = [0, 1, 1, 0, 2, 2, 2] + scores = np.array([-9, -1, -3, -9, -2, -4, -6], dtype=np.float32) + assert merge_tokens(path, scores) == [ + TokenSpan(1, 1, 3, -2.0), # frames 1, 2 -> end 3, not 2 + TokenSpan(2, 4, 7, -4.0), + ] + # a blank index other than 0 is filtered, and 0 is then an ordinary label + assert merge_tokens([3, 0, 0, 3], np.zeros(4, np.float32), blank=3) == [TokenSpan(0, 1, 3, 0.0)] + assert merge_tokens([0, 0], [0.0, 0.0]) == [] + assert merge_tokens([], []) == [] # torchaudio returned [] here too + + +def test_rejects_what_torchaudio_rejects(): + lp = np.zeros((3, 4), dtype=np.float32) + with pytest.raises(ValueError, match="empty"): + forced_align(lp, []) + with pytest.raises(ValueError, match="too long"): + forced_align(lp, [1, 1, 1]) # 3 tokens + 2 repeats need 5 frames + with pytest.raises(ValueError, match="blank"): + forced_align(lp, [1, 0, 2], blank=0) + with pytest.raises(ValueError, match="outside"): + forced_align(lp, [1, 2], blank=-1) # would silently index the last column + with pytest.raises(ValueError, match="outside"): + forced_align(lp, [1, 2], blank=4) + forced_align(lp, [1, 2, 3]) # and the tight fit itself is fine + assert adjacent_repeats([1, 1, 1, 2, 2]) == 3 and adjacent_repeats([7]) == 0 + + +def test_accepts_float64_and_non_contiguous_emissions(): + rng = np.random.default_rng(3) + lp = rng.standard_normal((50, 6)).astype(np.float64) + tg = [1, 2, 2, 5] + path, scores = forced_align(lp, tg) + path_f, _ = forced_align(np.asfortranarray(lp), tg) + assert np.array_equal(path, path_f) + assert scores.dtype == np.float32 # the DP works in float32 by design + diff --git a/tests/test_cut_and_cue_quality.py b/tests/test_cut_and_cue_quality.py new file mode 100644 index 00000000..8d60c2d0 --- /dev/null +++ b/tests/test_cut_and_cue_quality.py @@ -0,0 +1,203 @@ +"""Where a chunk is cut, and how long a subtitle cue may become. + +Both are places where a wrong value produces no visible error, only a silently +worse transcript: a cut in the middle of a word, or a cue that stays on screen +for half a minute. That is why they are pinned here. +""" +import numpy as np + +from noScribe.voxtral_engine import ( + QUIET_LEVEL, + SAMPLE_RATE, + SUB_MAX_SEC, + _chunk_boundaries, + _pass_bounds, + _frame_energy, + _segments_from_words, +) + + +def _noise(rng, n, amp): + return (rng.standard_normal(n) * amp).astype(np.float32) + + +def _speech(rng, seconds, amp=0.30): + return _noise(rng, int(seconds * SAMPLE_RATE), amp) + + +def _fill(arr, t0, t1, amp, rng): + i0, i1 = int(t0 * SAMPLE_RATE), int(t1 * SAMPLE_RATE) + arr[i0:i1] = _noise(rng, i1 - i0, amp) + + +# --------------------------------------------------------------------------- # +# Cut search +# --------------------------------------------------------------------------- # +def test_quiet_speech_is_not_mistaken_for_a_pause(): + """_frame_energy yields power (amplitude^2). With QUIET_LEVEL applied to it + unsquared, the threshold sat at -8.2 dB instead of -16.5 dB -- that is, at + quiet speech, not at a pause. Every 400 ms passage of unstressed syllables + thus counted as a "clear speaker pause" and, being closer to the target, + beat the real pause: the cut landed in the middle of a word.""" + rng = np.random.default_rng(0) + audio = _speech(rng, 1200) + _fill(audio, 589.0, 590.5, 0.0002, rng) # real pause + _fill(audio, 598.2, 598.8, 0.30 * 10 ** (-12 / 20), rng) # quiet speech + + bounds = _chunk_boundaries(audio, 600 * SAMPLE_RATE, 90 * SAMPLE_RATE, + 20 * SAMPLE_RATE, 600 * SAMPLE_RATE) + cut = bounds[1] / SAMPLE_RATE + assert 589.0 <= cut <= 590.5, f"cut at {cut:.2f}s instead of in the pause" + + +def test_noisy_recording_still_finds_its_best_pause(): + """Counter-check for the sharper threshold: a recording with a high noise + floor (room tone, hum) has no real silence anywhere. It must therefore not + fall back to the unsnapped target value but still take the relative + minimum.""" + rng = np.random.default_rng(1) + audio = _speech(rng, 1200) + _fill(audio, 589.0, 590.5, 0.30 * 10 ** (-10 / 20), rng) # only a dip + + bounds = _chunk_boundaries(audio, 600 * SAMPLE_RATE, 90 * SAMPLE_RATE, + 20 * SAMPLE_RATE, 600 * SAMPLE_RATE) + cut = bounds[1] / SAMPLE_RATE + assert 589.0 <= cut <= 590.5, f"cut at {cut:.2f}s, dip missed" + + +def test_a_backward_snap_does_not_leave_a_sub_second_tail(): + """The first cut snapped back to a pause at 589.2 s, leaving 600.5 s -- + just over the 600 s cap, so one more cut was forced. It aimed at a full + pass, found the pause at 1189.3 s and left a final pass of 0.55 s: a whole + Voxtral decode (and an aligner call) for half a second of audio, with no + context, although the docstring promises tails merge instead.""" + rng = np.random.default_rng(3) + audio = _speech(rng, 1190) + for t in (589.2, 1189.3): + _fill(audio, t, t + 0.6, 0.0002, rng) + passes = np.diff(_pass_bounds(audio, 600)) / SAMPLE_RATE + assert passes.max() <= 600, passes + assert passes.min() >= 600 / 8, passes + + +def test_pass_plan_invariants_on_random_files(): + """Passes never exceed the RAM-safe length (memory safety), never run + backward, and never come out as a tiny leftover -- whatever the length of + the file and wherever its pauses happen to sit. Short passes keep the audio + small; the invariants do not depend on the scale.""" + rng = np.random.default_rng(7) + base = _speech(rng, 420) + for _ in range(60): + chunk_sec = float(rng.uniform(60, 120)) + n = int(rng.uniform(5, 420) * SAMPLE_RATE) + audio = base[:n].copy() + for t in rng.uniform(0, n / SAMPLE_RATE, size=int(rng.integers(0, 12))): + _fill(audio, t, min(t + rng.uniform(0.2, 1.5), n / SAMPLE_RATE), + 0.0002, rng) + bounds = _pass_bounds(audio, chunk_sec) + passes = np.diff(bounds) + max_len = int(chunk_sec * SAMPLE_RATE) + assert bounds[0] == 0 and bounds[-1] == n + assert (passes > 0).all(), bounds + assert passes.max() <= max_len, (chunk_sec, n, bounds) + if n > max_len: + assert passes.min() >= max_len // 8, (chunk_sec, n, bounds) + + +def test_quiet_level_is_compared_in_the_power_domain(): + """The constant is an amplitude ratio; _frame_energy is power. The + difference is 2x in dB and exactly the mistake fixed above.""" + rng = np.random.default_rng(2) + loud = _frame_energy(_speech(rng, 1.0, 0.30), 800) + quiet = _frame_energy(_speech(rng, 1.0, 0.30 * QUIET_LEVEL), 800) + # The quiet passage sits at QUIET_LEVEL**2 of the power, not at QUIET_LEVEL. + assert np.median(quiet) < np.median(loud) * QUIET_LEVEL ** 2 * 2 + assert np.median(quiet) > np.median(loud) * QUIET_LEVEL ** 2 * 0.5 + + +# --------------------------------------------------------------------------- # +# Cue length +# --------------------------------------------------------------------------- # +def _cues(stamps): + return [(round(s["start"], 2), round(s["end"], 2), s["text"].strip()) + for s in _segments_from_words(stamps)] + + +def _w(word, start, end, prob=0.9): + return {"word": word, "start": start, "end": end, "prob": prob} + + +def test_a_long_gap_does_not_produce_an_overlong_cue(): + """SUB_MAX_SEC was only checked once the word was already in the cue, and + flush() can only end a cue including that word -- so every speech pause + longer than the ceiling produced a cue of exactly that length (measured: + 31 s for two words around a piece of music).""" + cues = _cues([_w("Musik", 0.50, 1.00), _w("beginnt.", 31.00, 31.50)]) + assert len(cues) == 2, cues + assert all(end - start <= SUB_MAX_SEC for start, end, _ in cues), cues + + +def test_an_ordinary_thinking_pause_also_splits_the_cue(): + """No special case for music: a 9-second thinking pause in the middle of a + sentence was already enough for a 10.4 s cue.""" + cues = _cues([_w("Ich", 0.0, 0.3), _w("glaube", 0.4, 0.9), + _w("dass", 10.0, 10.4), _w("es.", 10.5, 10.8)]) + assert len(cues) == 2, cues + assert all(end - start <= SUB_MAX_SEC for start, end, _ in cues), cues + + +def test_normal_speech_still_lands_in_one_cue(): + """Counter-check: without a large gap the new pre-check must not chop anything up.""" + stamps = [_w(w, i * 0.4, i * 0.4 + 0.35) + for i, w in enumerate("wir haben das gestern kurz besprochen".split())] + assert len(_cues(stamps)) == 1 + + +def test_no_cue_has_zero_duration(): + """A cue with start == end is invalid per the WebVTT specification; the + writer in utils does not check for it, so it has to be right here.""" + stamps = [_w("Das", 0.0, 0.3), _w("war", 0.4, 0.7), + _w("1990.", 0.7, 0.7, prob=0.0), # OOV, interpolated + _w("Genau.", 3.0, 3.4)] + for start, end, text in _cues(stamps): + assert end > start, f"cue without duration: {text!r}" + + +def test_a_lone_word_never_keeps_its_own_cue(): + """The repair step against one-word fragments applied the duration + condition to a single word as well. Measured on "Digitale Welt Video 7" + (00:03:47): SUB_MAX_CHARS closed the cue after "...Reorganisation" (49 + characters), and "angesagt." ran, stretched, ~1.2 s into the pause before + the next cue -- the condition flipped, and the word stayed a segment of + its own. A segment that short takes its speaker from whatever the + diarization puts underneath it: 0.4 s of a backchannel cluster gave the + word in the middle of the sentence its own speaker and its own paragraph.""" + words = "haben sie jetzt schon die nächste Reorganisation".split() + stamps = [_w(w, 225.0 + i * 0.40, 225.0 + i * 0.40 + 0.35) + for i, w in enumerate(words)] + stamps.append(_w("angesagt.", 227.85, 229.05)) # 1.2 s, stretched + cues = _cues(stamps) + assert len(cues) == 1, cues + assert cues[0][2].endswith("Reorganisation angesagt."), cues + + +def test_a_lone_word_after_a_long_pause_still_stands_alone(): + """Counter-check: the length limits stay in charge. If the word lies so far + behind the previous cue that the merged cue would reach beyond + SUB_MAX_SEC + 2, it must not be created -- otherwise a subtitle stands + across the whole pause.""" + stamps = [_w("Also", 0.0, 0.4), _w("gut", 0.5, 0.9)] + stamps.append(_w("weiter.", 12.0, 13.2)) + cues = _cues(stamps) + assert len(cues) == 2, cues + + +def test_the_two_word_fragment_keeps_its_duration_guard(): + """Only the single word is independent of its duration. Two words that + together last longer than 1.2 s are an ordinary short cue and are still + not glued on.""" + stamps = [_w("Das", 0.0, 0.3), _w("ist", 0.4, 0.7), _w("so.", 0.8, 1.1)] + stamps.append(_w("Sehr", 4.0, 4.9)) + stamps.append(_w("gut.", 5.0, 5.8)) # 2 words, 1.8 s + cues = _cues(stamps) + assert len(cues) == 2, cues diff --git a/tests/test_fast_generate.py b/tests/test_fast_generate.py new file mode 100644 index 00000000..8f74e910 --- /dev/null +++ b/tests/test_fast_generate.py @@ -0,0 +1,110 @@ +"""Unit tests for _Voxtral._consume_tokens, the token-stream bookkeeping of the +fast greedy path. + +_fast_generate delegates generation to the maintained mlx_lm.generate_step and +keeps only the consumption logic here: stop at the first stop token. It +deliberately does NOT replicate generate_stream's 10-identical-token backstop -- +a repetition loop must run so the engine's text-level _looks_degenerate detector +can catch it and retry. These tests drive that logic with plain int streams -- +no weights, no mlx, no real Voxtral. +""" +from noScribe.voxtral_engine import _Voxtral + + +def _consume(stream): + v = _Voxtral.__new__(_Voxtral) # no __init__: we only exercise _consume_tokens + return v._consume_tokens(iter(stream)) + + +def test_stops_at_first_stop_token_excluded(): + # 2 is a stop token; it and everything after are dropped. + assert _consume([5, 6, 7, 2, 9, 9, 9]) == [5, 6, 7] + + +def test_each_stop_token_ends_generation(): + for stop in _Voxtral._STOP_TOKENS: + assert _consume([8, 9, stop, 8, 8]) == [8, 9], f"stop={stop}" + + +def test_repetition_loop_passes_through_for_the_degeneracy_detector(): + # No token backstop: a long single-token run is kept in full (up to the stop), + # so _looks_degenerate can see it and trigger a retry instead of it being + # truncated to 10 tokens and slipping through. + assert _consume([3] * 50 + [2]) == [3] * 50 + + +def test_consumes_until_stop_regardless_of_repeats(): + assert _consume([9] * 9 + [1] + [9] * 9 + [2]) == [9] * 9 + [1] + [9] * 9 + + +def test_empty_stream(): + assert _consume([]) == [] + + +def test_immediate_stop_gives_empty(): + assert _consume([2, 5, 6]) == [] + + +def test_stop_tokens_are_control_tokens_never_text(): + """Voxtral's Tekken vocabulary has 131072 entries of which only the first + 1000 are control tokens. mlx_voxtral's default stop set contains 32000 and + calls it "a potential padding token" -- it is not, it is the ordinary text + token " Capital", and stopping there truncated every pass that transcribed + that word (silently: the remaining text reads as clean prose, so neither + _looks_degenerate nor the loop breaker flags it). is id 11.""" + stops = set(_Voxtral._STOP_TOKENS) + assert stops == {2, 4, 11} # , [/INST], + assert 32000 not in stops + assert all(t < 1000 for t in stops), "a stop token outside the control range" + + +def test_stop_tokens_are_read_from_the_build(): + """The class constant is only a fallback; a loaded model uses the ids its + own processor reports, so a build numbering its controls differently is + still stopped correctly.""" + from types import SimpleNamespace + + v = _Voxtral.__new__(_Voxtral) + v.proc = SimpleNamespace(_special_token_ids={"eos": 7, "inst_end": 8, + "pad": 9, "bos": 1}) + assert v._resolve_stop_tokens() == (7, 8, 9) # bos is not a stop + # A processor that reports nothing falls back to the class constant. + v.proc = SimpleNamespace(_special_token_ids={}) + assert v._resolve_stop_tokens() == _Voxtral._STOP_TOKENS + + +def test_token_cb_fires_throttled_without_affecting_output(monkeypatch): + """The liveness heartbeat calls token_cb(collected_count), throttled by wall + clock, and must not change which tokens are collected.""" + import noScribe.voxtral_engine as v + # advance monotonic() enough each read that the >=1.5s throttle fires + ticks = iter([0.0, 10.0, 20.0, 30.0, 40.0, 50.0]) + monkeypatch.setattr(v.time, "monotonic", lambda: next(ticks, 999.0)) + seen = [] + vox = _Voxtral.__new__(_Voxtral) + out = vox._consume_tokens(iter([10, 21, 12, 2, 99]), token_cb=seen.append) + assert out == [10, 21, 12] # stop token 2 (and 99 after it) dropped + assert seen and seen[-1] <= 3 # reports the running collected-count + + +def test_token_cb_absent_is_fine(): + assert _consume([5, 6, 2]) == [5, 6] # default token_cb=None path unchanged + + +def test_breaker_gave_up_stops_consumption_early(): + """When the loop breaker gives up mid-generation, _consume_tokens must stop + pulling tokens immediately instead of running to the stop token.""" + class _GiveUpAfter: + def __init__(self, n): + self.n = n + self.reads = 0 + @property + def gave_up(self): + self.reads += 1 + return self.reads > self.n + + v = _Voxtral.__new__(_Voxtral) + stream = iter([7] * 1000 + [2]) + out = v._consume_tokens(stream, breaker=_GiveUpAfter(5)) + assert len(out) == 5 # truncated, not the full 1000 + assert next(stream) == 7 # generator not exhausted diff --git a/tests/test_forced_align_cap.py b/tests/test_forced_align_cap.py new file mode 100644 index 00000000..7edbcb2e --- /dev/null +++ b/tests/test_forced_align_cap.py @@ -0,0 +1,63 @@ +"""Oversized alignment windows are split before the DP ever sees them. + +FORCED_ALIGN_MAX_CELLS is a memory and latency budget for the numpy DP; its +comment in voxtral_engine has the figures and the history. This test drives +align_words with a fake emission (no wav2vec2 download) over a case above the +cap and asserts every actual DP invocation stays under it while every word is +still timestamped. +""" +import numpy as np +import pytest + +torch = pytest.importorskip("torch") +from noScribe import ctc_align # noqa: E402 +from noScribe.voxtral_engine import ( # noqa: E402 + _Aligner, + FORCED_ALIGN_MAX_CELLS, + SAMPLE_RATE, +) + +VOCAB_SIZE = 30 # incl. blank + + +def _stub_aligner(): + al = object.__new__(_Aligner) + al.vocab = {ch: i + 1 for i, ch in enumerate("abcdefghijklmnopqrstuvwxyz")} + al.blank = 0 + al.delim = None + gen = torch.Generator().manual_seed(0) + # 50 fps at 16 kHz, random log-probs -- shape is all that matters here. + al._emission = lambda audio: torch.log_softmax( + torch.rand((len(audio) // 320, VOCAB_SIZE), generator=gen), dim=-1 + ).numpy() + return al + + +def test_oversized_window_splits_below_cap(monkeypatch): + al = _stub_aligner() + + calls = [] + real_fa = ctc_align.forced_align + + def checked_fa(log_probs, targets, blank=0): + calls.append(_Aligner._dp_cells(len(targets), log_probs.shape[0])) + return real_fa(log_probs, targets, blank=blank) + + monkeypatch.setattr(ctc_align, "forced_align", checked_fa) + + # 1100 s -> 55_000 frames; 2000 x 5-char words -> 10_000 tokens + # -> 55_000 * 20_001 = 1.10e9 cells, just above the 2**30 cap. + words = ["abcde"] * 2000 + audio = np.zeros(1100 * SAMPLE_RATE, dtype=np.float32) + n_frames = len(audio) // 320 + assert n_frames * (2 * len(words) * 5 + 1) > FORCED_ALIGN_MAX_CELLS + + out = al.align_words(words, audio) + + assert calls, "forced_align was never reached" + assert all(c <= FORCED_ALIGN_MAX_CELLS for c in calls) + assert len(calls) >= 2 # the window was actually split + assert len(out) == len(words) + assert all(w["end"] >= w["start"] for w in out) + starts = [w["start"] for w in out] + assert starts == sorted(starts) diff --git a/tests/test_forced_align_density.py b/tests/test_forced_align_density.py new file mode 100644 index 00000000..dc5eb592 --- /dev/null +++ b/tests/test_forced_align_density.py @@ -0,0 +1,148 @@ +"""When forced_align can run at all -- and what happens when it cannot. + +The DP demands `n_frames >= len(targets) + n_repeats` (as torchaudio did before +it), where n_repeats counts the pairs of immediately adjacent identical tokens +(a CTC path has to place a blank between two identical labels). The old +pre-check left only ~5.3% headroom, but German text has 4-30% such pairs -- the +windows in between got through, forced_align raised, and the `except` silently +turned that into evenly spread timestamps for the whole window. +""" +import logging + +import numpy as np +import pytest + +torch = pytest.importorskip("torch") + +from noScribe import ctc_align # noqa: E402 +from noScribe.voxtral_engine import ( # noqa: E402 + SAMPLE_RATE, + _Aligner, +) + +VOCAB_SIZE = 30 + + +def _stub_aligner(): + """Aligner without weights: real split/check logic, faked emissions. + + The emission yields exactly as many frames as _predict_frames predicts, so + that routing and the leaf check see the same number in the test. + """ + al = object.__new__(_Aligner) + al.vocab = {ch: i + 1 for i, ch in enumerate("abcdefghijklmnopqrstuvwxyz")} + al.blank = 0 + al.delim = None + gen = torch.Generator().manual_seed(0) + al._emission = lambda audio: torch.log_softmax( + torch.rand((al._predict_frames(len(audio)), VOCAB_SIZE), generator=gen), + dim=-1).numpy() + return al + + +# --------------------------------------------------------------------------- # +# The condition itself +# --------------------------------------------------------------------------- # +@pytest.mark.parametrize("tokens", [ + [7, 18, 18, 11, 25], # one pair + [7, 18, 18, 11, 25, 25], # two pairs + [4, 4, 4, 9, 9], # a triple run counts as two pairs + [3, 1, 4, 1, 5], # no repetition +]) +def test_repeat_count_matches_what_the_dp_demands(tokens): + """Measured against the DP itself: the smallest T at which forced_align + no longer raises is exactly len(tokens) + _adjacent_repeats.""" + need = _Aligner._adjacent_repeats(tokens) + len(tokens) + smallest = None + for T in range(len(tokens), len(tokens) + 12): + emission = np.zeros((T, VOCAB_SIZE), dtype=np.float32) + try: + ctc_align.forced_align(emission, tokens, blank=0) + smallest = T + break + except ValueError: + continue + assert smallest == need + + +def test_a_window_in_the_repeat_band_does_not_degrade_silently(caplog): + """Exactly the case the old 0.95 bound let through: barely enough frames + for the tokens, but not for the repeats. No exception may escape -- and if + an even spread is all that is left, it has to be in the log, otherwise a + broken window cannot be told from a good one. Until 2026-08 this stood + only in this docstring: the leaf check spread silently, only the `except` + path next to it warned.""" + al = _stub_aligner() + words = ["aabb"] * 40 # every word brings two pairs along + audio = np.zeros(int(3.5 * SAMPLE_RATE), dtype=np.float32) + tokens, _ = al._tokenize(words) + frames = al._predict_frames(len(audio)) + assert len(tokens) <= frames, "test setup: the tokens must just about fit" + assert len(tokens) + al._adjacent_repeats(tokens) > frames, \ + "test setup: only the repeats may push it over" + + with caplog.at_level(logging.WARNING, logger="noScribe.voxtral_engine"): + out = al.align_words(words, audio) # must not raise + assert len(out) == len(words) + assert all(w["end"] >= w["start"] for w in out) + # After splitting, some halves manage a real alignment; at least one leaf + # stays too dense and is spread -- and has to say so. + assert any(w["prob"] == 0.0 for w in out), "test setup: no leaf was spread" + spread_lines = [r for r in caplog.records if "evenly spread" in r.getMessage()] + assert spread_lines, "spread, but nothing in the log" + assert "repeats need more frames" in spread_lines[0].getMessage() + + +def test_prediction_matches_the_emission_it_replaces(): + """The split decision runs on predicted frames, so that a window which is + going to be split anyway does not pay for a discarded wav2vec2 pass. + Prediction and real emission therefore have to agree.""" + al = _stub_aligner() + for seconds in (0.5, 2.5, 20.0, 20.5, 47.3): + audio = np.zeros(int(seconds * SAMPLE_RATE), dtype=np.float32) + assert al._emission(audio).shape[0] == al._predict_frames(len(audio)) + + +# --------------------------------------------------------------------------- # +# Partial coverage: the salvage prefix +# --------------------------------------------------------------------------- # +def test_a_partial_prefix_has_its_own_entry_point(): + """The split cuts the audio by the words' character share -- admissible + only when the words fill the window. A salvage prefix does not; for that + there is `align_prefix` (see test_salvage_prefix_alignment). What is + pinned here is that `align_words` does not accept this case in the first + place, instead of silently computing it wrong.""" + import inspect + assert "words_span_audio" not in inspect.signature(_Aligner.align_words).parameters + assert hasattr(_Aligner, "align_prefix") + + +def test_a_full_window_is_still_split_as_before(): + """Counter-check: full coverage (the normal case) still splits.""" + al = _stub_aligner() + words = ["abcde"] * 2000 + audio = np.zeros(1100 * SAMPLE_RATE, dtype=np.float32) + out = al.align_words(words, audio) + assert len(out) == len(words) + assert any(w["prob"] != 0.0 for w in out), "nothing at all was aligned" + + +def test_dense_recursion_is_bounded(): + """Halving does not lower the density: the audio is cut at the same + character share as the words, so tokens/frames is the same in both halves + and the check fires again on every level. Unchecked, this ran to full + depth and pushed 10-13 times the chunk through wav2vec2, only to end in + leaves that spread evenly anyway.""" + al = _stub_aligner() + seen = [] + inner = al._emission + al._emission = lambda audio: (seen.append(len(audio)), inner(audio))[1] + + words = ["abcdefghij"] * 400 # very dense for short audio + audio = np.zeros(int(4.0 * SAMPLE_RATE), dtype=np.float32) + out = al.align_words(words, audio) + + assert len(out) == len(words) + # 2 levels of density split => at most 2^2 leaves, plus their emissions. + assert len(seen) <= 2 ** _Aligner.MAX_DENSE_SPLIT_DEPTH, len(seen) + assert sum(seen) <= 3 * len(audio), "too much audio computed more than once" diff --git a/tests/test_forced_align_stability.py b/tests/test_forced_align_stability.py new file mode 100644 index 00000000..be100831 --- /dev/null +++ b/tests/test_forced_align_stability.py @@ -0,0 +1,126 @@ +"""Acceptance test for the Voxtral aligner's DP against a recorded reference. + +The word timestamps come from ``noScribe.ctc_align`` (``forced_align`` + +``merge_tokens``), a numpy replacement for the torchaudio kernel of the same +names. ``tests/data/forced_align_ref.npz`` holds 43 cases -- random emissions +incl. heavy repeats, tight T == L+R fits, single-token targets -- with the +inputs (``lp_i``, ``targets_i``) and torchaudio's answer to them (``paths_i``, +``scores_i``, ``spani_i``, ``spanf_i``), recorded from torchaudio 2.8 on +2026-07-22 and verified identical on 2.11. The numpy DP must reproduce every +path and span bit-for-bit; that is what makes it a drop-in rather than a +rewrite. + +The inputs are stored, not regenerated, so this file needs neither torch nor +torchaudio and runs wherever ``tests/test_ctc_align.py`` does. Only +``--regenerate`` needs both, because the reference is meant to stay the +*other* implementation's answer: + + python tests/test_forced_align_stability.py --regenerate + +What the reference cannot see, tests/test_ctc_align.py covers: blank indices +other than 0, and exact ties, where the numpy DP is deliberately *better* +than torchaudio (pytorch/audio#4221). +""" +from pathlib import Path + +import numpy as np +import pytest + +from noScribe import ctc_align + +REF_PATH = Path(__file__).parent / "data" / "forced_align_ref.npz" +N_CASES = 43 + + +@pytest.fixture(scope="module") +def ref(): + return np.load(REF_PATH) + + +@pytest.mark.parametrize("idx", range(N_CASES)) +def test_forced_align_matches_recorded_reference(ref, idx): + lp, targets = ref[f"lp_{idx}"], ref[f"targets_{idx}"] + paths, scores = ctc_align.forced_align(lp, targets, blank=0) + spans = ctc_align.merge_tokens(paths, scores) + # The integer outputs decide the word timestamps -- they must be + # bit-identical everywhere (verified across torchaudio 2.8/2.11 and + # macOS arm64 / Linux x86_64). The reference keeps torchaudio's batch axis. + assert np.array_equal(ref[f"paths_{idx}"][0], paths), "alignment path changed" + assert np.array_equal(ref[f"spani_{idx}"], + [[s.token, s.start, s.end] for s in spans]), "token spans changed" + # Float scores may differ in the last ulp with the platform's reduction + # order, so a tight tolerance instead of equality. + assert np.allclose(ref[f"scores_{idx}"][0], scores, rtol=0, atol=1e-5), "frame scores drifted" + assert np.allclose(ref[f"spanf_{idx}"], [s.score for s in spans], rtol=0, atol=1e-5), \ + "span scores drifted" + + +def test_reference_is_complete(ref): + """Every case carries inputs and all four recorded outputs, and the inputs + are consistent with each other -- a half-regenerated file must not pass.""" + for idx in range(N_CASES): + lp, targets = ref[f"lp_{idx}"], ref[f"targets_{idx}"] + assert lp.dtype == np.float32 and lp.ndim == 2 + assert ref[f"paths_{idx}"].shape == (1, lp.shape[0]) + assert targets.min() >= 1 and targets.max() < lp.shape[1] + assert len(ref[f"spani_{idx}"]) == len(ref[f"spanf_{idx}"]) >= 1 + + +# --------------------------------------------------------------------------- # +# Recording the reference: torch + torchaudio, and only here +# --------------------------------------------------------------------------- # +def _cases(): + """Deterministic case specs: (T, C, targets) -- the set the file was + recorded from. Changing it changes every key, so only with --regenerate.""" + rng = np.random.default_rng(42) + specs = [] + for i in range(40): + C = int(rng.integers(5, 60)) # vocab incl. blank + L = int(rng.integers(1, 80)) # target length + tg = rng.integers(1, C, size=L) + if i % 3 == 0 and L > 3: # force heavy repeats sometimes + tg[1::2] = tg[0:-1:2][: len(tg[1::2])] + R = int(np.sum(tg[1:] == tg[:-1])) + T = int(L + R + rng.integers(0, 200)) # from tightest possible upward + specs.append((T, C, tg)) + specs.append((1, 5, np.array([2]))) # single frame, single token + specs.append((500, 40, np.array([7]))) # long audio, one token + specs.append((7, 6, np.array([3, 3, 3, 3]))) # T == L+R exactly + assert len(specs) == N_CASES + return specs + + +def _record(T, C, tg): + """torchaudio's answer to a seeded random emission, as stored in the file.""" + import torch + import torchaudio.functional as F + + g = torch.Generator().manual_seed(hash((T, C, len(tg))) % (2**31)) + lp = torch.randn((1, T, C), generator=g).log_softmax(-1) + targets = torch.tensor(tg, dtype=torch.int32).unsqueeze(0) + paths, scores = F.forced_align(lp, targets, blank=0) + spans = F.merge_tokens(paths[0], scores[0]) + return { + "lp": lp[0].numpy().astype(np.float32), + "targets": np.asarray(tg, dtype=np.int64), + "paths": paths.numpy(), + "scores": scores.numpy(), + "spani": np.array([[s.token, s.start, s.end] for s in spans], dtype=np.int64), + "spanf": np.array([s.score for s in spans], dtype=np.float64), + } + + +if __name__ == "__main__": + import sys + + if "--regenerate" not in sys.argv: + sys.exit("run via pytest, or pass --regenerate to rewrite the reference") + out = {} + for i, spec in enumerate(_cases()): + for key, arr in _record(*spec).items(): + out[f"{key}_{i}"] = arr + REF_PATH.parent.mkdir(exist_ok=True) + np.savez_compressed(REF_PATH, **out) + import torchaudio + + print(f"wrote {REF_PATH} from torchaudio {torchaudio.__version__}") diff --git a/tests/test_loop_breaker.py b/tests/test_loop_breaker.py new file mode 100644 index 00000000..329f4317 --- /dev/null +++ b/tests/test_loop_breaker.py @@ -0,0 +1,321 @@ +"""Unit tests for the in-place loop breaker and the retry ladder order. + +_LoopBreaker is backend-agnostic (tokens need len/indexing, logits item +assignment), so these tests drive it with plain lists and numpy arrays -- +no weights, no mlx. The ladder tests script a stub Voxtral to verify the +escalation order: greedy -> gentle sampling -> split -> stronger sampling -> +penalty, with the penalty strictly last. +""" +import numpy as np + +from noScribe.voxtral_engine import ( + _LoopBreaker, + _other_language, + _transcribe_guarded, + LOOP_BREAK_MAX_KICKS, + LOOP_BREAK_MAX_PERIOD, + LOOP_BREAK_WINDOW, + RETRY_REPETITION_PENALTIES, + RETRY_TEMPERATURES, + SAMPLE_RATE, +) + +VOCAB = 100 + + +def _drive(breaker, seq): + """Feed `seq` step by step the way generate_step calls a logits processor: + at each step the processor sees the tokens generated so far and the next + logits row. Returns the set of steps at which a ban was placed.""" + bans = [] + for i in range(len(seq)): + logits = np.zeros((1, VOCAB), dtype=np.float32) + out = breaker(seq[:i], logits) + banned = np.where(np.isinf(out[0]))[0] + if banned.size: + bans.append((i, int(banned[0]))) + return bans + + +# --------------------------------------------------------------------------- # +# _LoopBreaker +# --------------------------------------------------------------------------- # +def test_clean_text_is_never_touched(): + rng = np.random.default_rng(0) + seq = rng.integers(0, VOCAB, size=2000).tolist() + br = _LoopBreaker() + assert _drive(br, seq) == [] + assert br.kicks == 0 and not br.gave_up + + +def test_short_legitimate_repetition_is_not_flagged(): + # A phrase repeated 5x (35 tokens) then normal text: far below the + # full-window threshold, must pass untouched ("sehr, sehr" stays safe). + rng = np.random.default_rng(1) + seq = [3, 1, 4, 1, 5, 9, 2] * 5 + rng.integers(0, VOCAB, size=1000).tolist() + br = _LoopBreaker() + assert _drive(br, seq) == [] + assert br.kicks == 0 + + +def test_loop_gets_banned_at_cycle_continuation(): + cycle = [3, 1, 4, 1, 5, 9, 2] + seq = cycle * 60 # 420 tokens of pure loop + br = _LoopBreaker() + bans = _drive(br, seq) + assert bans, "loop was never detected" + first_step, banned_tok = bans[0] + # Detection needs a full periodic window first. + assert first_step >= LOOP_BREAK_WINDOW + # The banned token is exactly the one that would continue the cycle. + assert banned_tok == seq[first_step - len(cycle)] + assert br.kicks == 1 and not br.gave_up + + +def test_repeated_reentry_gives_up(): + # The "model" ignores every kick: after each ban episode one divergent + # token, then straight back into the loop. After LOOP_BREAK_MAX_KICKS + # episodes the breaker must give up so the retry ladder takes over. + cycle = list(range(7)) + seq = [] + for burst in range(LOOP_BREAK_MAX_KICKS + 1): + seq += cycle * ((LOOP_BREAK_WINDOW // len(cycle)) + 4) + seq.append(99) # divergence ends the episode + br = _LoopBreaker() + _drive(br, seq) + assert br.kicks == LOOP_BREAK_MAX_KICKS + 1 + assert br.gave_up + + +# --------------------------------------------------------------------------- # +# Retry ladder order +# --------------------------------------------------------------------------- # +DEGEN = ("na " * 500).strip() # word-run 500 -> degenerate +DEGEN_SHORT = ("na " * 40).strip() # degenerate, but the shortest candidate +CLEAN = "alles gut, kurzer sauberer text" +NO_SPLIT_AUDIO = np.zeros(60 * SAMPLE_RATE, dtype=np.float32) # < 2*45s + + +class _ScriptedVox: + """transcribe_array stub: pops scripted (text, info) results and records + the decode parameters of every attempt.""" + + def __init__(self, script): + self.script = list(script) + self.calls = [] + + def transcribe_array(self, audio, language, max_new_tokens=4096, + repetition_penalty=1.0, token_cb=None, + temperature=0.0, seed=None, info=None): + self.calls.append((temperature, repetition_penalty)) + text, extra = self.script.pop(0) + if info is not None: + info.update(extra) + return text + + +def test_clean_pass_is_single_attempt(): + vox = _ScriptedVox([(CLEAN, {})]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t") + assert out == CLEAN + assert vox.calls == [(0.0, 1.0)] + + +def test_inplace_repair_is_accepted_without_retry(): + vox = _ScriptedVox([(CLEAN, {"loop_kicks": 2, "loop_gave_up": False})]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t") + assert out == CLEAN + assert len(vox.calls) == 1 + + +def test_gave_up_attempt_is_never_trusted(): + # The truncated text looks clean to the text detector (dilution), but the + # breaker gave up -- the ladder must retry, not accept it. + vox = _ScriptedVox([ + (CLEAN, {"loop_gave_up": True, "loop_kicks": 4}), + (CLEAN, {}), + ]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t") + assert out == CLEAN + t0, _ = RETRY_TEMPERATURES[0] + assert vox.calls == [(0.0, 1.0), (t0, 1.0)] + + +def test_gentle_sampling_comes_before_any_penalty(): + vox = _ScriptedVox([(DEGEN, {}), (CLEAN, {})]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t") + assert out == CLEAN + t0, _ = RETRY_TEMPERATURES[0] + assert vox.calls == [(0.0, 1.0), (t0, 1.0)] + + +def test_full_ladder_order_and_shortest_fallback(): + # Everything fails; audio too short to split, so the order is: + # greedy, T=0.2, split, T=0.8, penalty 1.01, penalty 1.1 -- penalty strictly last. + vox = _ScriptedVox([ + (DEGEN, {}), (DEGEN, {}), (DEGEN, {}), (DEGEN_SHORT, {}), (DEGEN, {}), + ]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t") + expected = [(0.0, 1.0)] + expected += [(t, 1.0) for t, _ in RETRY_TEMPERATURES] + expected += [(0.0, p) for p in RETRY_REPETITION_PENALTIES] + assert vox.calls == expected + assert out == DEGEN_SHORT # shortest garbage wins when nothing resolves + + +def test_a_truncated_attempt_never_beats_a_complete_one(): + """When the breaker gives up, _consume_tokens cuts the stream off in the + middle of the run -- so that attempt is the shortest by construction. As + long as the ladder simply took the shortest candidate at the end, it won + every comparison: measured, a 148-word stump went into the transcript + instead of an almost complete 2733-word transcript, and the rest of the + chunk was gone.""" + stump = ("Ein sauberer Anfang, der abbricht. " + "na " * 20).strip() + vox = _ScriptedVox([ + (stump, {"loop_gave_up": True, "loop_kicks": 4}), + (DEGEN, {}), (DEGEN, {}), (DEGEN, {}), (DEGEN, {}), + ]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t") + assert out != stump, "the truncated attempt won again" + assert out == DEGEN + assert len(out.split()) > len(stump.split()) + + +def test_a_truncated_attempt_is_used_when_it_is_all_there_is(): + """Counter-check: it must not lead to nothing coming back at all.""" + stump = "nur dieses Bruchstück" + vox = _ScriptedVox([(stump, {"loop_gave_up": True})] * 5) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t") + assert out == stump + + +def test_incremental_sync_survives_the_tail_bound(): + """The count of tokens seen must not be derived from len(self._tail): the + buffer stops growing at the bound, so from then on every step looked like + an offset and took the full resync -- ~220 device scalars per generated + token, a measured ~10 ms of extra latency per token.""" + class _Tokens(list): + def __init__(self, items): + super().__init__(items) + self.slices = 0 + + def __getitem__(self, item): + if isinstance(item, slice): + self.slices += 1 + return list.__getitem__(self, item) + + rng = np.random.default_rng(3) + seq = rng.integers(0, VOCAB, size=3 * LOOP_BREAK_WINDOW).tolist() + br = _LoopBreaker() + slices = 0 + for i in range(len(seq)): + toks = _Tokens(seq[:i]) + br(toks, np.zeros((1, VOCAB), dtype=np.float32)) + slices += toks.slices + + assert slices == 0, f"{slices} full resynchronisations" + assert br._seen == len(seq) - 1 + # The buffer stays bounded nonetheless. + assert len(br._tail) <= LOOP_BREAK_WINDOW + LOOP_BREAK_MAX_PERIOD + + +# --------------------------------------------------------------------------- # +# A window that really is in another language +# --------------------------------------------------------------------------- # +ENGLISH = ("And then you know what they said, that this was not the way we would " + "have done it, but they just went with it anyway and I think that was " + "the right call for the team, because you have to trust the people " + "who are there and not second guess them all the time when they are " + "the ones who know the work best.") +GERMAN = ("Und dann haben wir das noch mal besprochen, weil ich nicht sicher war, " + "ob das so passt, aber jetzt ist es klar und wir sind auch schon " + "weiter, denn wenn die anderen dann noch mal schauen, habe ich das " + "Gefühl, dass es schon gut wird und wir das jetzt so lassen können, " + "ohne dass noch mal jemand etwas ändert.") + + +def test_a_window_really_in_another_language_does_not_climb_the_ladder(): + """A German file with an English passage on Auto (or a wrong pinned + language): the language check marked every decode bad, no rung could make + English speech come out German, and the split recursed to its leaves -- + 60 decodes of a 600 s window -- before best() shipped the penalty attempt, + the one rung that deletes real repeated words. Now the gentle retry and one + split, whose halves stop after their own two decodes, settle it: six + decodes, four of them half as long. Nor is the prefix rung tried: with no + loop to cut off it only logged that it could not find one, which read as + if the pass had looped.""" + audio = np.zeros(600 * SAMPLE_RATE, dtype=np.float32) + vox = _ScriptedVox([(ENGLISH, {})] * 6) # a seventh decode would fail + logged = [] + out = _transcribe_guarded(vox, audio, "de", lambda level, msg: logged.append(msg), + "t", want_lang="de", align_cb=lambda words, window: []) + assert out == f"{ENGLISH} {ENGLISH}" + t0, _ = RETRY_TEMPERATURES[0] + assert vox.calls == [(0.0, 1.0), (t0, 1.0)] * 3 + assert not any("clean part" in m for m in logged), logged + + +def test_the_split_still_repairs_a_translation_that_survives_the_retry(): + """Which language Voxtral writes in hangs on the exact window length, so + the split -- which changes it -- is the rung most likely to undo a + translation the gentle retry could not. It still runs once.""" + audio = np.zeros(600 * SAMPLE_RATE, dtype=np.float32) + vox = _ScriptedVox([(ENGLISH, {}), (ENGLISH, {}), (GERMAN, {}), (GERMAN, {})]) + out = _transcribe_guarded(vox, audio, "de", None, "t", want_lang="de") + assert out == f"{GERMAN} {GERMAN}" + assert len(vox.calls) == 4 + + +def test_a_language_the_detector_cannot_name_is_never_called_translated(): + """The script check reads all Cyrillic as Russian, so a correctly pinned + Ukrainian file came back "translated ('ru' instead of 'uk')" on every + pass, each one sent up the ladder for nothing. Czech reads as Polish by + its function words, the same way.""" + ukrainian = ("Ми ще раз усе обговорили, бо я не був певен, чи це підходить, " + "але тепер усе зрозуміло, і ми вже рухаємося далі, бо коли інші " + "ще раз подивляться, у мене є відчуття, що все буде добре.") + assert _other_language(ukrainian, "uk") is None + assert _other_language(ukrainian, "de") == "ru" # a language it can name + assert _other_language(ENGLISH, "de") == "en" + + +def test_english_from_a_language_the_detector_cannot_name_is_still_caught(): + """Silencing every language outside the detector's list also silenced the + one translation Voxtral makes: a pinned Hindi, Finnish or Korean pass that + came back in English was neither retried nor reported any more.""" + for want in ("hi", "fi", "ko", "uk", "sv"): + assert _other_language(ENGLISH, want) == "en", want + + +def test_a_spurious_translation_is_still_retried(): + """Counter-check: what the language check exists for. A German window that + came back translated is repaired by the gentle-sampling rung.""" + vox = _ScriptedVox([(ENGLISH, {}), (GERMAN, {})]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t", want_lang="de") + assert out == GERMAN + t0, _ = RETRY_TEMPERATURES[0] + assert vox.calls == [(0.0, 1.0), (t0, 1.0)] + + +def test_a_window_too_short_to_split_still_gets_every_temperature(): + """A window under two split halves (every pass of the 24B build on a 24 GB + Mac is ~71 s) stopped after the gentle retry, where the old ladder still + tried the next temperature -- the rung left to change the decode there.""" + vox = _ScriptedVox([(ENGLISH, {}), (ENGLISH, {}), (GERMAN, {})]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t", want_lang="de") + assert out == GERMAN + assert [t for t, _ in vox.calls] == [0.0] + [t for t, _ in RETRY_TEMPERATURES] + + vox = _ScriptedVox([(ENGLISH, {})] * 3) # really English: no penalty rung + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t", want_lang="de") + assert out == ENGLISH + assert all(penalty == 1.0 for _, penalty in vox.calls) and len(vox.calls) == 3 + + +def test_a_loop_then_another_language_keeps_climbing(): + """Only a pair of loop-free decodes stops the ladder early. When the greedy + pass looped, the next rungs still run as before.""" + vox = _ScriptedVox([(DEGEN, {}), (ENGLISH, {}), (GERMAN, {})]) + out = _transcribe_guarded(vox, NO_SPLIT_AUDIO, "de", None, "t", want_lang="de") + assert out == GERMAN + assert len(vox.calls) == 3 diff --git a/tests/test_lost_head_recovery.py b/tests/test_lost_head_recovery.py new file mode 100644 index 00000000..94b5c152 --- /dev/null +++ b/tests/test_lost_head_recovery.py @@ -0,0 +1,184 @@ +"""Tests for putting back the opening a long Voxtral pass dropped. + +A long window sometimes returns without its first seconds of speech: no loop, +no wrong language, nothing else to notice it by -- the words are just gone. The +repair decodes a short head of the same audio (short windows keep the opening) +and splices back whatever the pass is missing, finding the seam in the text +rather than assuming where it is. + +The numbers below are the real ones: on a 1226 s recording whose first 2.4 s +are a remark before the take, the pass came back missing exactly those 12 words. +""" +import numpy as np +import pytest + +from noScribe.voxtral_engine import ( + HEAD_ANCHOR_WORDS, + HEAD_PROBE_MAX_SEC, + HEAD_PROBE_SEC, + SAMPLE_RATE, + _find_anchor, + _norm_word, + _recover_lost_head, +) + +LEAD = "Zeichnung an, aber ich schlage ja den vorderen Teil eh weg. Ja." +BODY = ("Herzlich willkommen. Willst du Spaß haben und erfolgreich sein in der " + "digitalen Welt? Ja, unbedingt. Und machst du dir Sorgen über die " + "Zukunft in der digitalen Welt? Na, ich kenne viele, die sich Sorgen " + "machen. Kennst du viele, die sich Sorgen machen?") + + +class FakeVox: + """Stands in for the model: returns canned probe text, records the windows.""" + + def __init__(self, *replies): + self.replies = list(replies) + self.windows_sec = [] + + def transcribe_array(self, audio, language, max_new_tokens=4096, **kw): + self.windows_sec.append(len(audio) / SAMPLE_RATE) + return self.replies[min(len(self.windows_sec) - 1, len(self.replies) - 1)] + + +def _audio(seconds): + return np.zeros(int(seconds * SAMPLE_RATE), dtype=np.float32) + + +def _log(level, msg): + pass + + +# --------------------------------------------------------------------------- # +# _find_anchor +# --------------------------------------------------------------------------- # +def _n(text): + return [_norm_word(w) for w in text.split()] + + +def test_anchor_is_found_after_the_dropped_opening(): + at = _find_anchor(_n(f"{LEAD} {BODY}"), _n(BODY)[:HEAD_ANCHOR_WORDS]) + assert at == len(LEAD.split()) + + +def test_anchor_at_zero_when_nothing_was_dropped(): + assert _find_anchor(_n(BODY), _n(BODY)[:HEAD_ANCHOR_WORDS]) == 0 + + +def test_anchor_survives_wording_differences_between_two_decodes(): + """The probe and the pass are independent decodes: "Ja, herzlich" against + "Ja. Herzlich", "Spaß" against "Spass". Six of eight words still match.""" + probe = _n(f"{LEAD} Herzlich willkommen. Willst du Spass haben und " + f"erfolgreicher sein in der digitalen Welt?") + assert _find_anchor(probe, _n(BODY)[:HEAD_ANCHOR_WORDS]) == len(LEAD.split()) + + +def test_no_anchor_when_the_texts_are_unrelated(): + probe = _n("Völlig anderer Text der nichts mit dem Durchgang zu tun hat und " + "auch sonst keine Wörter teilt") + assert _find_anchor(probe, _n(BODY)[:HEAD_ANCHOR_WORDS]) is None + + +def test_the_earliest_of_equally_good_positions_wins(): + """Recovering too little leaves the status quo; too much would duplicate.""" + anchor = _n("eins zwei drei vier") + probe = _n("eins zwei drei vier fünf eins zwei drei vier") + assert _find_anchor(probe, anchor, min_hits=4) == 0 + + +# --------------------------------------------------------------------------- # +# _recover_lost_head +# --------------------------------------------------------------------------- # +def test_the_dropped_opening_is_put_back(): + vox = FakeVox(f"{LEAD} {BODY}") + out = _recover_lost_head(vox, _audio(1226), None, BODY, _log, "Chunk 1/1") + assert out == f"{LEAD} {BODY}" + assert vox.windows_sec == [HEAD_PROBE_SEC] + + +def test_a_pass_that_lost_nothing_is_returned_untouched(): + vox = FakeVox(BODY) + assert _recover_lost_head(vox, _audio(1226), None, BODY, _log, "x") is BODY + + +def test_a_short_pass_is_not_probed_at_all(): + """Short windows do not have the defect -- probing them only costs a decode.""" + vox = FakeVox(BODY) + short = _audio(HEAD_PROBE_SEC * 1.5) + assert _recover_lost_head(vox, short, None, BODY, _log, "x") is BODY + assert vox.windows_sec == [] + + +def test_too_little_text_to_anchor_on_is_left_alone(): + vox = FakeVox(BODY) + stub = " ".join(BODY.split()[:HEAD_ANCHOR_WORDS - 1]) + assert _recover_lost_head(vox, _audio(1226), None, stub, _log, "x") is stub + assert vox.windows_sec == [] + + +def test_a_degenerate_probe_is_not_spliced_in(): + """A looping probe must never reach the transcript -- and must not be grown. + + A loop is a property of this speech, not of the window length, and a + degenerate decode is the one that runs to its full token budget. Growing it + buys a longer loop, not an answer.""" + vox = FakeVox("ja ja " * 60) + out = _recover_lost_head(vox, _audio(1226), None, BODY, _log, "x") + assert out is BODY + assert vox.windows_sec == [HEAD_PROBE_SEC], "grew the probe instead of giving up" + + +def test_the_probe_grows_when_the_seam_is_beyond_it(): + """A pass that lost more than the first probe covers is still recovered.""" + long_lead = " ".join(f"wort{i}" for i in range(200)) + vox = FakeVox("nichts davon hier drin", f"{long_lead} {BODY}") + out = _recover_lost_head(vox, _audio(1226), None, BODY, _log, "x") + assert out == f"{long_lead} {BODY}" + assert vox.windows_sec == [HEAD_PROBE_SEC, HEAD_PROBE_SEC * 2] + + +def test_growing_stops_and_the_pass_is_kept(): + vox = FakeVox("nichts davon hier drin") + out = _recover_lost_head(vox, _audio(1226), None, BODY, _log, "x") + assert out is BODY + assert max(vox.windows_sec) <= HEAD_PROBE_MAX_SEC + + +def test_growing_never_probes_more_than_half_the_pass(): + """The probe is a cheap check, not a second transcription of the window.""" + vox = FakeVox("nichts davon hier drin") + _recover_lost_head(vox, _audio(200), None, BODY, _log, "x") + assert max(vox.windows_sec) <= 100 + + +def test_the_first_probe_also_stays_within_half_the_pass(): + """Passes of 90-120 s got the full 60 s first probe -- more than half the + pass, which the rule above forbids for every probe, not just the grown ones.""" + vox = FakeVox("nichts davon hier drin") + _recover_lost_head(vox, _audio(100), None, BODY, _log, "x") + assert vox.windows_sec == [50] + + +def test_growing_stops_at_the_last_doubling_within_half_the_pass(): + """Stretching the last probe to half the pass decoded the same opening once + more: 60, 120 and then 200 s on a 400 s pass, and on a real interview a + 226 s probe after 120 s that still missed the anchor.""" + for seconds in (241, 400, 450): + vox = FakeVox("nichts davon hier drin") + _recover_lost_head(vox, _audio(seconds), None, BODY, _log, "x") + assert vox.windows_sec == [HEAD_PROBE_SEC, HEAD_PROBE_SEC * 2], seconds + + +@pytest.mark.parametrize("lang", [None, "de"]) +def test_the_probe_runs_in_the_pass_language(lang): + """Whatever the pass was decoded with, the probe has to match it.""" + seen = [] + + class Recorder(FakeVox): + def transcribe_array(self, audio, language, max_new_tokens=4096, **kw): + seen.append(language) + return super().transcribe_array(audio, language, max_new_tokens, **kw) + + _recover_lost_head(Recorder(f"{LEAD} {BODY}"), _audio(1226), lang, BODY, + _log, "x") + assert seen == [lang] diff --git a/tests/test_mel_floor.py b/tests/test_mel_floor.py new file mode 100644 index 00000000..1056dba8 --- /dev/null +++ b/tests/test_mel_floor.py @@ -0,0 +1,280 @@ +"""The log-Mel clamp floor from a percentile instead of the maximum. + +mlx-voxtral clamps the log-Mel at `log_max - 8` with log_max taken over the +whole input, so one loud cell -- a door slam -- raises the floor for the whole +pass and costs a measured +1.52 WER points at 28 dB +(docs/voxtral-mel-clamp-floor.md). The engine therefore replaces the processor's +feature extractor with one whose floor comes from a percentile of the +spectrogram (MEL_FLOOR_PERCENTILE). + +Two things are pinned. First, that the replacement changes *only* the floor: +with pct=100 its output must be bit-identical to the library's own path, on +loud, quiet and silent input -- the proof that the spectrogram underneath is +the reference one, and the guard that catches the library's mel path drifting +away from our copy of it. Second, the mechanism: a transient that lifts the +maximum floor by more than 20 dB moves the percentile floor by a fraction of a +dB. Not by nothing -- the transient's own cells displace the top of the +distribution a little -- which is why the model-level test at the end asserts +a small difference, not identical text. + +The numpy tests run everywhere; the library comparisons need mlx-voxtral, and +the last test the local model build. +""" +import importlib.resources as impres + +import numpy as np +import pytest + +from noScribe.voxtral_engine import (MEL_FLOOR_PERCENTILE, MEL_FLOOR_RANGE, + SAMPLE_RATE, _PercentileFloorFeatures, + _Voxtral, _local_copy, clamp_log_mel) + +_MODEL = _local_copy("voxtral-mini-8bit") + + +def _reference_clamp(x): + """mlx-voxtral's own clamp and affinity, spelled out in float32.""" + x = np.asarray(x, dtype=np.float32) + floor = x.max() - np.float32(MEL_FLOOR_RANGE) + return (np.maximum(x, floor) + np.float32(4.0)) / np.float32(4.0) + + +def _floor_db(features): + """The clamp floor a feature block was built with, in dB up to a constant: + the floor is the smallest value in the block (every block has cells on it + -- the lowest mel filters of the 128-band Slaney bank are empty, and the + 30 s padding is silent), x4 undoes the affinity's scale (its offset + cancels in the differences this is used for), x10 turns log10 into dB.""" + return float(np.asarray(features).min()) * 40.0 + + +def _with_block(clean, at=0.45, seconds=0.1): + """The dose instrument of the clamp-floor measurements: a full-scale block + dropped into a quiet recording.""" + spiked = clean.copy() + pos = int(at * len(spiked)) + spiked[pos:pos + int(seconds * SAMPLE_RATE)] = 1.0 + return spiked + + +def _raw_spectrogram(seed, top, seconds=30): + """A log-Mel-shaped block whose maximum lands at `top`.""" + rng = np.random.default_rng(seed) + x = rng.normal(-4.0, 2.0, size=(128, 100 * seconds)).astype(np.float32) + return x - x.max() + np.float32(top) + + +# --------------------------------------------------------------------------- # +# numpy only +# --------------------------------------------------------------------------- # +@pytest.mark.parametrize("top", [2.0, 0.4, -1.3, -6.0]) +def test_percentile_100_is_the_reference_clamp(top): + """With the percentile at 100 the function is the library's formula -- + bit for bit, in float32, whichever range the maximum falls in.""" + x = _raw_spectrogram(0, top) + out = clamp_log_mel(x, 100) + assert out.dtype == np.float32 + assert np.array_equal(out, _reference_clamp(x)) + + +def test_the_percentile_floor_never_sits_above_the_reference_floor(): + """The one direction the change can go: cells the reference keeps are kept + unchanged, cells it clamps may only come out lower. Lowering the floor + exposes noise detail the model ignores; raising it destroys quiet speech.""" + x = _raw_spectrogram(1, 1.5) + ref = _reference_clamp(x) + out = clamp_log_mel(x) + kept = x >= x.max() - np.float32(MEL_FLOOR_RANGE) + assert np.array_equal(out[kept], ref[kept]) + assert np.all(out <= ref) + assert np.any(out < ref) + + +def test_the_cap_bounds_the_floor_but_never_raises_it_past_the_reference(): + """With `cap`, the statistic never sits more than cap below the maximum: + on a spectrogram whose 99th percentile is far below the top, the capped + floor lands exactly at max - cap - 8, and at pct=100 the cap is inert + (the percentile is already the maximum), so the bit-identity control + covers the capped path too.""" + x = _raw_spectrogram(3, 1.5) + assert np.array_equal(clamp_log_mel(x, 100, cap=np.float32(2.0)), + _reference_clamp(x)) + uncapped_top = np.float32(np.percentile(x, MEL_FLOOR_PERCENTILE)) + assert uncapped_top < x.max() - np.float32(2.0) # the cap has work to do + capped = clamp_log_mel(x, MEL_FLOOR_PERCENTILE, cap=np.float32(2.0)) + expected_floor = (x.max() - np.float32(2.0) - np.float32(MEL_FLOOR_RANGE) + + np.float32(4.0)) / np.float32(4.0) + assert capped.min() == expected_floor + assert np.all(capped >= clamp_log_mel(x, MEL_FLOOR_PERCENTILE)) + assert np.all(capped <= _reference_clamp(x)) + + +def test_a_burst_of_loud_cells_moves_the_reference_floor_but_not_the_percentile(): + """A 0.1 s transient is a couple of hundred cells among 1.5 million on a + two-minute pass. The maximum floor follows it all the way; the percentile + floor moves by the rank displacement those cells cause at the top of the + distribution -- a fraction of a dB at 99, where the top percent of a + 120 s pass is 15 000 cells. (At 99.9 the same burst would move it by + 0.8 dB here and by 5 dB on a single 30 s block; that is why 99.)""" + clean = _raw_spectrogram(2, 1.5, seconds=120) + spiked = clean.copy() + spiked[:20, 5400:5410] = np.float32(4.5) # 30 dB over the maximum, low bins, 10 frames + rise_max = _floor_db(clamp_log_mel(spiked, 100)) - _floor_db(clamp_log_mel(clean, 100)) + rise_pct = _floor_db(clamp_log_mel(spiked)) - _floor_db(clamp_log_mel(clean)) + assert rise_max > 25.0 + assert 0.0 <= rise_pct < 0.5 + + +# --------------------------------------------------------------------------- # +# against the library +# --------------------------------------------------------------------------- # +def _speech_like(seconds, level, seed=0): + """Harmonics under a syllable-rate envelope plus a little noise -- enough + structure for the mel cells to spread like speech does, at any level.""" + rng = np.random.default_rng(seed) + t = np.arange(int(seconds * SAMPLE_RATE)) / SAMPLE_RATE + env = 0.5 * (1.0 + np.sin(2 * np.pi * 3.0 * t)) + x = sum(np.sin(2 * np.pi * f * t + k) + for k, f in enumerate((140, 280, 560, 1100, 2300))) * env + x += 0.05 * rng.standard_normal(len(t)) + return (x / np.abs(x).max() * level).astype(np.float32) + + +def _library_features(audio): + from mlx_voxtral.audio_processing import VoxtralFeatureExtractor + out = VoxtralFeatureExtractor()(audio, sampling_rate=SAMPLE_RATE, + return_tensors="mlx") + return np.array(out["input_features"]) + + +@pytest.mark.parametrize("audio", [ + pytest.param(_speech_like(61.0, 0.9), id="full-scale, three chunks with padding"), + pytest.param(_speech_like(20.0, 0.06), id="quiet: log_max in [-2, 0)"), + pytest.param(_speech_like(8.0, 0.001), id="-60 dB"), + pytest.param(np.zeros(5 * SAMPLE_RATE, np.float32), id="silence"), +]) +def test_pct_100_is_bit_identical_to_the_library(audio): + """The acceptance control from the work order: at percentile 100 the + engine's extractor must reproduce mlx-voxtral's features bit for bit. The + quiet case is the one the library's `global_max` lever cannot pass.""" + pytest.importorskip("mlx_voxtral") + ours = np.array(_PercentileFloorFeatures(100)(audio)["input_features"]) + ref = _library_features(audio) + assert ours.shape == ref.shape + assert ours.dtype == ref.dtype == np.float32 + assert np.array_equal(ours, ref) + + +def test_a_transient_barely_moves_the_percentile_floor_on_real_features(): + """A full-scale 0.1 s block in a quiet recording, the dose instrument of + the clamp-floor write-up: the library's floor follows it by tens of dB, + the engine's by a fraction of one.""" + pytest.importorskip("mlx_voxtral") + clean = _speech_like(60.0, 0.1) + spiked = _with_block(clean) + ex = _PercentileFloorFeatures() + rise_max = _floor_db(_library_features(spiked)) - _floor_db(_library_features(clean)) + rise_pct = (_floor_db(ex(spiked)["input_features"]) + - _floor_db(ex(clean)["input_features"])) + assert rise_max > 20.0 + # The block also *replaces* a tenth of a second of speech cells, so the + # percentile may move a hair either way -- hence the absolute bound. + assert abs(rise_pct) < 1.0 + + +def test_the_floor_does_not_depend_on_the_padding(): + """The block is zero-padded to a 30 s multiple and the padding cells sit + at the -10 minimum, so the percentile has to be taken over the input's own + frames: a 10 s clip in a 30 s block must get the floor of its own + spectrogram, not the one the block's percentile would give (several dB + lower, and 18 dB lower on a 5 s clip).""" + pytest.importorskip("mlx_voxtral") + import mlx.core as mx + from mlx_voxtral.audio_processing import log_mel_spectrogram + clip = _speech_like(10.0, 0.3) + # The unpadded spectrogram through the library's no-clamp lever (its + # affinity inverted; lossy in the last bits, which a dB check ignores). + raw = np.array(log_mel_spectrogram(mx.array(clip), global_max=-1e6)) * 4.0 - 4.0 + own_floor = (np.percentile(raw, MEL_FLOOR_PERCENTILE) - MEL_FLOOR_RANGE) * 10.0 + block = np.pad(raw, ((0, 0), (0, 3000 - raw.shape[1])), constant_values=-10.0) + block_floor = (np.percentile(block, MEL_FLOOR_PERCENTILE) - MEL_FLOOR_RANGE) * 10.0 + assert own_floor - block_floor > 1.0 # the two answers differ + got = _floor_db(_PercentileFloorFeatures()(clip)["input_features"]) - 40.0 + assert abs(got - own_floor) < 0.01 + + +def test_the_engine_installs_the_percentile_floor(monkeypatch): + """_Voxtral.__init__ swaps the processor's extractor, so the whole engine + -- every pass, every retry rung, the head probe -- sees the same features. + Driven with stubs: no weights, no model, just the constructor.""" + mlx_voxtral = pytest.importorskip("mlx_voxtral") + from types import SimpleNamespace + + model = SimpleNamespace(language_model=None, lm_head=None) + proc = SimpleNamespace(feature_extractor="library", _special_token_ids=None) + monkeypatch.setattr(mlx_voxtral, "load_voxtral_model", + lambda repo, dtype=None: (model, None)) + monkeypatch.setattr(mlx_voxtral.VoxtralProcessor, "from_pretrained", + staticmethod(lambda repo: proc)) + vox = _Voxtral("stub") + assert isinstance(vox.proc.feature_extractor, _PercentileFloorFeatures) + assert vox.proc.feature_extractor.pct == MEL_FLOOR_PERCENTILE + + +# --------------------------------------------------------------------------- # +# at the transcript, on the local model +# --------------------------------------------------------------------------- # +def _word_edits(a, b): + """Words touched by the edit between two transcripts (Levenshtein on + words, counting substitutions, insertions and deletions).""" + a, b = a.split(), b.split() + prev = list(range(len(b) + 1)) + for i, wa in enumerate(a, 1): + cur = [i] + for j, wb in enumerate(b, 1): + cur.append(min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (wa != wb))) + prev = cur + return prev[-1] + + +@pytest.mark.skipif(_MODEL is None, + reason="local models/voxtral-mini-8bit not present (opt-in)") +def test_a_transient_costs_the_transcript_far_less_under_the_percentile_floor(tmp_path): + """The knock case from the work order at the transcript: two minutes of + the bundled interview, attenuated to a quiet recording, once clean and + once with a full-scale 0.1 s block at 45 %. Under the library floor the + block rewrites words all over the passage; under the engine's floor the + residual shift of a fraction of a dB may still flip a word or two, and + the test allows exactly that -- a handful of words, and fewer than the + library loses -- rather than pretending the text is identical.""" + pytest.importorskip("mlx_voxtral") + sf = pytest.importorskip("soundfile") + from mlx_voxtral.audio_processing import VoxtralFeatureExtractor + from noScribe.audio.convert import ToWav + + # The pipeline's own conversion to 16 kHz mono, as in test_pyannote_waveform_load. + wav = tmp_path / "interview.wav" + with ToWav(impres.files("tests") / "data" / "interview.mp3", wav) as towav: + towav.stop_after(132_000) + while towav.convert(): + pass + audio, sr = sf.read(str(wav), dtype="float32") + assert sr == SAMPLE_RATE + clean = audio[12 * SAMPLE_RATE:132 * SAMPLE_RATE] + clean = (clean / np.abs(clean).max() * 0.1).astype(np.float32) # -20 dB + spiked = _with_block(clean) + + vox = _Voxtral(_MODEL) + engine_floor = vox.proc.feature_extractor + + def edits(extractor): + vox.proc.feature_extractor = extractor + a = vox.transcribe_array(clean, "de") + b = vox.transcribe_array(spiked, "de") + return _word_edits(a, b), len(a.split()) + + edits_pct, n_words = edits(engine_floor) + edits_max, _ = edits(VoxtralFeatureExtractor()) + assert n_words > 200 + assert edits_pct < edits_max, (edits_pct, edits_max) + assert edits_pct <= 0.02 * n_words, (edits_pct, n_words) diff --git a/tests/test_merged_embeddings.py b/tests/test_merged_embeddings.py new file mode 100644 index 00000000..e5cc526d --- /dev/null +++ b/tests/test_merged_embeddings.py @@ -0,0 +1,137 @@ +"""Unit tests for _Voxtral._merged_embeddings, the audio/text prompt merge. + +The prompt is almost entirely [AUDIO] placeholders (375 tokens per 30 s), and +the projected audio embeddings have to land on exactly those positions. The +engine merges them itself rather than calling mlx_voxtral's private +`_merge_input_embeddings` (equivalent since 0.0.6, on the batch of 1 production +uses) -- so these run in CI, where the model-gated smoke test does not. + +Driven with a stub model: no weights, no real Voxtral, but real mlx arrays, +because the dtype promotion these pin is an mlx behaviour. +""" +import numpy as np +import pytest + +mx = pytest.importorskip("mlx.core") + +from noScribe.voxtral_engine import _Voxtral + +AUDIO_ID = 24 +HIDDEN = 4 + + +class _Config: + audio_token_id = AUDIO_ID + + +class _StubModel: + """Text embeddings in bf16 (as the real embed_tokens returns), audio + embeddings in float32 (as the real projector returns).""" + + config = _Config() + + def __init__(self, audio_embeds): + self.audio_embeds = audio_embeds + self.seen_features = None + + def embed_tokens(self, input_ids): + ids = np.array(input_ids).astype(np.float32) + rows = np.repeat(ids[..., None], HIDDEN, axis=-1) + np.arange(HIDDEN) + return mx.array(rows).astype(mx.bfloat16) + + def get_audio_embeds(self, features): + self.seen_features = features + return self.audio_embeds + + +def _engine(audio_embeds): + v = _Voxtral.__new__(_Voxtral) # no __init__: only the merge is exercised + v._mx = mx + v.model = _StubModel(audio_embeds) + return v + + +def _audio(n, batch=1): + """Values that bf16 cannot represent exactly, so a lost promotion shows.""" + vals = 0.1 + np.arange(batch * n * HIDDEN, dtype=np.float32) * 0.017 + return mx.array(vals.reshape(batch, n, HIDDEN)) + + +def test_audio_lands_on_the_placeholders_and_text_is_untouched(): + ids = mx.array([[1, AUDIO_ID, AUDIO_ID, AUDIO_ID, 7]]) + audio = _audio(3) + out = _engine(audio)._merged_embeddings({"input_ids": ids, "input_features": "mel"}) + assert out.shape == (1, 5, HIDDEN) + assert mx.all(out[0, 1:4] == audio[0]), "audio embeddings not placed verbatim" + text = _StubModel(audio).embed_tokens(ids).astype(out.dtype) + assert mx.all(out[0, 0] == text[0, 0]) and mx.all(out[0, 4] == text[0, 4]) + + +def test_non_contiguous_placeholders_keep_their_order(): + ids = mx.array([[AUDIO_ID, 5, AUDIO_ID, 6, AUDIO_ID]]) + audio = _audio(3) + out = _engine(audio)._merged_embeddings({"input_ids": ids, "input_features": "mel"}) + for k, pos in enumerate((0, 2, 4)): + assert mx.all(out[0, pos] == audio[0, k]), f"placeholder {k} got the wrong row" + + +def test_the_result_is_promoted_to_the_audio_dtype(): + """embed_tokens is bf16, the projector is float32. Scattering into the bf16 + array would round every audio embedding away -- silently.""" + ids = mx.array([[1, AUDIO_ID, AUDIO_ID, 7]]) + audio = _audio(2) + out = _engine(audio)._merged_embeddings({"input_ids": ids, "input_features": "mel"}) + assert out.dtype == mx.float32 + assert mx.all(out[0, 1:3] == audio[0]), "audio embeddings lost precision" + assert not mx.all(out[0, 1:3] == audio[0].astype(mx.bfloat16).astype(mx.float32)), \ + "test value survives a bf16 round-trip, so it cannot detect the bug" + + +def test_several_batch_rows_each_get_their_own_audio(): + ids = mx.array([[AUDIO_ID, AUDIO_ID, 3], [4, AUDIO_ID, AUDIO_ID]]) + audio = _audio(2, batch=2) + out = _engine(audio)._merged_embeddings({"input_ids": ids, "input_features": "mel"}) + assert mx.all(out[0, 0:2] == audio[0]) and mx.all(out[1, 1:3] == audio[1]) + + +def test_one_audio_stream_is_shared_by_every_batch_row(): + """get_audio_embeds returns [1, n, hidden] whatever went in, so a multi-row + prompt shares it. Indexing it per row reads past the end, and mlx answers + that with zeros instead of raising -- audio silently replaced by nothing.""" + ids = mx.array([[AUDIO_ID, AUDIO_ID, 3], [4, AUDIO_ID, AUDIO_ID]]) + audio = _audio(2) # batch 1, as the encoder really returns it + out = _engine(audio)._merged_embeddings({"input_ids": ids, "input_features": "mel"}) + assert mx.all(out[0, 0:2] == audio[0]) and mx.all(out[1, 1:3] == audio[0]) + + +def test_an_impossible_stream_count_is_refused_rather_than_zero_filled(): + """Neither one shared stream nor one per row. Indexing past the end of an + mlx array returns zeros rather than raising, so the prompt would silently + lose its audio.""" + ids = mx.array([[AUDIO_ID, AUDIO_ID], [AUDIO_ID, AUDIO_ID], + [AUDIO_ID, AUDIO_ID]]) + with pytest.raises(ValueError, match="audio streams"): + _engine(_audio(2, batch=2))._merged_embeddings( + {"input_ids": ids, "input_features": "mel"}) + + +def test_a_prompt_without_audio_is_just_the_text_embeddings(): + ids = mx.array([[1, 2, 3]]) + engine = _engine(_audio(0)) + out = engine._merged_embeddings({"input_ids": ids, "input_features": None}) + assert out.dtype == mx.bfloat16 + assert engine.model.seen_features is None, "encoder ran for a prompt with no audio" + + +def test_a_count_mismatch_is_refused_rather_than_scattered(): + """Placing N embeddings on M placeholders would corrupt the prompt quietly.""" + ids = mx.array([[AUDIO_ID, AUDIO_ID, AUDIO_ID]]) + with pytest.raises(ValueError, match="placeholder"): + _engine(_audio(2))._merged_embeddings({"input_ids": ids, "input_features": "mel"}) + + +def test_the_encoder_sees_the_features_it_was_given(): + ids = mx.array([[AUDIO_ID]]) + engine = _engine(_audio(1)) + engine._merged_embeddings({"input_ids": ids, "input_features": "the-mel"}) + assert engine.model.seen_features == "the-mel" diff --git a/tests/test_quant_summary.py b/tests/test_quant_summary.py new file mode 100644 index 00000000..5baa5de1 --- /dev/null +++ b/tests/test_quant_summary.py @@ -0,0 +1,81 @@ +"""Tests for _quant_summary: the log line must state the *exact* build. + +The config's quantization dict only lists what was quantised; deliberately +dense components (this project's bf16 audio encoder) appear nowhere in it. +The summary therefore also checks the weight names for components without +quantisation scales -- these tests pin that down with synthetic model dirs. +""" +import json +import struct + +import pytest + +from noScribe.voxtral_engine import _quant_summary + + +def _write_model(tmp_path, config, weight_names=None, sharded=True): + (tmp_path / "config.json").write_text(json.dumps(config)) + if weight_names is None: + return tmp_path + if sharded: + idx = {"weight_map": {n: "model-00001.safetensors" for n in weight_names}} + (tmp_path / "model.safetensors.index.json").write_text(json.dumps(idx)) + else: + header = json.dumps({n: {} for n in weight_names}).encode() + (tmp_path / "model.safetensors").write_bytes( + struct.pack("= 2, "the prefix fitted into one computation after all" + assert all(c <= cap for c in cells), cells + assert len(out) == len(words) + # This is the number the cut is made from. + assert out[-1]["prob"] != 0.0, "the prefix end was not really aligned" + assert abs(out[-1]["end"] - true_end) < 0.5, (out[-1]["end"], true_end) + # ...and the prefix stays at the front: it must not run into the foreign material. + assert out[-1]["end"] < n_frames / FPS * 0.6 + starts = [w["start"] for w in out] + assert starts == sorted(starts) + assert all(w["end"] >= w["start"] for w in out) + + +def test_the_audio_is_never_cut_by_character_share(monkeypatch): + """The actual defect: it is the WORDS that get split, never the audio by + character share. Every piece therefore has to run against the entire + remaining audio -- recognisable by the frame count of every computation + matching the remainder, not a fraction estimated from the text length.""" + n_frames = 6000 + words = _words(100) + al = _stub_aligner(None) + emission, true_end = _planted(al, words, n_frames) + al._emission = lambda audio: emission + + tokens, _ = al._tokenize(words) + monkeypatch.setattr(v, "FORCED_ALIGN_MAX_CELLS", + n_frames * (2 * len(tokens) + 1) // 3) + + seen = [] + real = ctc_align.forced_align + monkeypatch.setattr( + ctc_align, "forced_align", + lambda e, t, blank=0: (seen.append(e.shape[0]), real(e, t, blank=blank))[1]) + + al.align_prefix(words, _audio(n_frames)) + + assert seen[0] == n_frames, "the first piece did not see the whole window" + # Every following piece starts at the end of the previous one and keeps + # everything after it: the remaining length shrinks but always ends at the + # window end. + assert seen == sorted(seen, reverse=True) + # The remainder is real remaining audio, not the prefix's estimated + # character share: after the last word there is still foreign material in + # the window. + assert seen[-1] > (n_frames - true_end * FPS) * 0.8 + + +def test_a_small_window_is_still_one_single_alignment(monkeypatch): + """Counter-check: where one computation is enough, nothing may be chopped + up -- the chain is the exception, not the new normal.""" + n_frames = 3000 + words = _words(30) + al = _stub_aligner(None) + emission, true_end = _planted(al, words, n_frames) + al._emission = lambda audio: emission + cells = _count_cells(monkeypatch) + + out = al.align_prefix(words, _audio(n_frames)) + + assert len(cells) == 1 and cells[0] <= FORCED_ALIGN_MAX_CELLS + assert abs(out[-1]["end"] - true_end) < 0.5 + assert out[-1]["prob"] != 0.0 + + +# --------------------------------------------------------------------------- # +# What has to happen when a piece cannot really be aligned +# --------------------------------------------------------------------------- # +def test_one_failed_piece_invalidates_the_whole_chain(monkeypatch): + """A chain is only as good as its weakest link: had a piece boundary been + guessed, all following pieces would be off -- and would come back with + immaculate scores, i.e. undetectably. A failed piece therefore has to + throw the whole result onto evenly spread times (prob 0.0), which + `_salvage_prefix` by construction does not cut on.""" + n_frames = 6000 + words = _words(100) + al = _stub_aligner(None) + emission, _ = _planted(al, words, n_frames) + al._emission = lambda audio: emission + + tokens, _ = al._tokenize(words) + monkeypatch.setattr(v, "FORCED_ALIGN_MAX_CELLS", + n_frames * (2 * len(tokens) + 1) // 3) + + calls = [] + real = ctc_align.forced_align + + def flaky(log_probs, targets, blank=0): + calls.append(1) + if len(calls) == 2: + raise RuntimeError("simulated failure in the second piece") + return real(log_probs, targets, blank=blank) + + monkeypatch.setattr(ctc_align, "forced_align", flaky) + + out = al.align_prefix(words, _audio(n_frames)) + + assert len(out) == len(words) + assert all(w["prob"] == 0.0 for w in out), \ + "a partial result of the chain was reported as a real time" + # ...and _salvage_prefix refuses on it. + text = " ".join(words[:len(words) // 2]) + ". " + "dass das, " * 40 + got, why = v._salvage_prefix(text, _audio(n_frames), + lambda w, a: al.align_prefix(w, a)) + assert got is None and why + + +def test_the_ladder_asks_for_a_prefix_alignment(monkeypatch): + """Wiring: the rung has to be given `align_prefix`. `align_words` with + `words_span_audio=False` visibly degrades to `_spread` on large windows -- + exactly the ceiling that is meant to fall here.""" + from noScribe.voxtral_engine import _AlignerPool + + used = [] + + class _Stub: + def align_prefix(self, words, audio, t_offset=0.0): + used.append("align_prefix") + return [{"word": w, "start": i, "end": i + 1, "prob": -0.1} + for i, w in enumerate(words)] + + def align_words(self, words, audio, t_offset=0.0, words_span_audio=True): + used.append("align_words") + return [] + + pool = _AlignerPool("de", None) + pool.aligner_for = lambda text, remember=True: _Stub() + out = pool.align_for_salvage(["Guten", "Morgen."], "AUDIO") + + assert used == ["align_prefix"], used + assert out[-1]["end"] == 2 diff --git a/tests/test_transcript_corrections.py b/tests/test_transcript_corrections.py new file mode 100644 index 00000000..1b882e14 --- /dev/null +++ b/tests/test_transcript_corrections.py @@ -0,0 +1,981 @@ +""" +Tests for the `transcript_corrections.py` module. +""" + +import pytest + +from noScribe import transcript_corrections as tc + + +def test_apply_corrections_is_literal(): + """A user-supplied replacement must never be read as a regex template.""" + rules = [(__import__("re").compile(r"(? untouched + assert tc.apply_name_corrections("Hallo Steffi, sagt Mona.", names, "de") == "Hallo Steffi, sagt Mona." + # lower case is no name ("mohna" mid-sentence), and the curly possessive is + # a possessive like the straight one + assert tc.apply_name_corrections("die mohna und Mohna\u2019s Hut.", names, "de") \ + == "die mohna und Mohna\u2019s Hut." + + +def test_a_mis_hearing_that_changes_a_sound_is_left_alone(dictionaries): + """"Muna" is no word either, but it is not how "Mona" sounds: it may be + someone else, and "Leni" next to a speaker called Lena certainly is.""" + assert tc.apply_name_corrections("Ich bin Muna.", ["Mona"], "de") == "Ich bin Muna." + assert tc.apply_name_corrections("Then Leni said hello.", ["Lena"], "en") \ + == "Then Leni said hello." + + +def test_apply_name_corrections_works_in_any_language_with_a_dictionary(dictionaries): + assert tc.apply_name_corrections("Then Mohna said hello.", ["Mona"], "en") \ + == "Then Mona said hello." + + +def test_apply_name_corrections_takes_each_capitalised_part_of_a_full_name(dictionaries): + """A speaker entered as "Mona Muster" or "Lena-Mona" is two names; the old + `isalpha()` filter dropped such an entry whole. A particle is no name: "del" + in "Ana del R\u00edo", and a name typed in lower case would write itself + in lower case into the transcript ("Mohna" -> "mona").""" + assert tc.apply_name_corrections("Ich bin Mohna Muhster.", ["Mona Muster"], "de") \ + == "Ich bin Mona Muster." + assert tc.apply_name_corrections("Ich bin Mohna.", ["Lena-Mona"], "de") == "Ich bin Mona." + assert tc.apply_name_corrections("Ich bin Mohna.", ["mona"], "de") == "Ich bin Mohna." + + +def test_apply_name_corrections_keeps_a_real_name_that_sounds_alike(dictionaries): + """"Marcus" is a name of its own and may be someone else; only a spelling + that is no word at all is taken for a mis-hearing.""" + assert tc.apply_name_corrections("Heute mit Marcus hier.", ["Markus"], "de") \ + == "Heute mit Marcus hier." + + +def test_apply_name_corrections_leaves_an_ambiguous_word_alone(dictionaries): + """"Sahra" is spelled like both speakers; picking one would be a guess.""" + assert tc.apply_name_corrections("Ich bin Sahra.", ["Sarah", "Sara"], "de") \ + == "Ich bin Sahra." + + +def test_apply_name_corrections_leaves_real_words_alone(dictionaries): + """Every German noun is capitalised, and "Mohn" is spelled like a speaker + called Mon; the dictionary keeps it. So does a possessive, with a straight + or a curly apostrophe: its stem is not a word of its own.""" + for sentence, names in [ + ("Dann Mohn und Marcus.", ["Mon", "Markus"]), + ("Mohna's Hut und Mohna\u2019s Tasche.", ["Mona"]), + ]: + assert tc.apply_name_corrections(sentence, names, "de") == sentence + + +def test_a_word_any_of_the_languages_knows_is_kept(dictionaries): + """Text the script check reads as one language may be in another, and a + dictionary knows nothing of its neighbour's words: a word stays when any + of the languages the text may be in knows it.""" + assert tc.apply_name_corrections("Mohn kam.", ["Mon"], ["xx", "de"]) == "Mohn kam." + assert tc.apply_name_corrections("Mohna kam.", ["Mona"], ["xx", "de"]) == "Mona kam." + + +def test_apply_name_corrections_needs_a_dictionary_for_the_language(dictionaries): + """Without one there is no telling a mis-heard name from a word -- and + macOS, asked about a language it has no dictionary for, calls every word + correct -- so nothing is changed.""" + for language in ("zh", None, "", "auto", ["zh", None]): + assert tc.apply_name_corrections("Mohna kam.", ["Mona"], language) == "Mohna kam." + + +def test_dictionary_language_maps_codes_to_what_the_system_has(): + pytest.importorskip("AppKit") + if tc._spell_checker() is None: + pytest.skip("no spell checker") + available = [str(lang) for lang in tc._spell_checker().availableLanguages()] + if "pt" not in available and any(l.startswith("pt_") for l in available): + assert tc._dictionary_language("pt").startswith("pt_") + assert tc._dictionary_language("xx") is None + assert tc._dictionary_language(None) is None + assert tc._dictionary_language("auto") is None + + +def test_apply_name_corrections_with_the_macos_dictionary(): + """The same guards against the real dictionaries the engine will ask.""" + pytest.importorskip("AppKit") + if tc._dictionary_language("de") is None or tc._dictionary_language("en") is None: + pytest.skip("no German or English dictionary installed") + for text, names, language in [ + ("Dann Mohn und Marcus.", ["Mon", "Markus"], "de"), # spelled alike, known + ("Then Carl said hello.", ["Karl"], "en"), + ("\u00c4hm, also das war so.", ["Ann"], "de"), # a filler + ("Hamma scho, sagt Leni.", ["Hanna", "Lena"], "de"), # dialect, another name + ]: + assert tc.apply_name_corrections(text, names, language) == text + # Under the load of a full test run the spell server sometimes stalls, and + # a stalled lookup answers "a word" (see _is_word); asking again is what + # the next chunk of a real job does too. + for text, language, expected in [ + ("Ich bin Mohna Muster.", "de", "Ich bin Mona Muster."), + ("Then Mohna came to Rome.", "en", "Then Mona came to Rome."), + ]: + for _ in range(5): + out = tc.apply_name_corrections(text, ["Mona"], language) + if out == expected: + break + assert out == expected + + +_NOT_FOUND = 2**63 - 1 # NSNotFound: the location of "no misspelling" + + +class _FakeChecker: + """Answers like NSSpellChecker, with every word unknown (flagged whole) + unless told to answer as a stalled spell server does, or to flag a range + that is not the word.""" + def __init__(self, flagged=None): + self.flagged = flagged + self.asked = [] + + def checkSpellingOfString_startingAt_language_wrap_inSpellDocumentWithTag_wordCount_( + self, word, start, language, wrap, tag, count): + from types import SimpleNamespace + self.asked.append(word) + location, length = self.flagged or (0, len(word)) + return SimpleNamespace(location=location, length=length), 1 + + +@pytest.mark.parametrize("flagged", [ + (_NOT_FOUND, 0), # a stalled server: measured to report no misspelling + (1, 2), # a range that is not the word + (0, 2), # nor is one that covers only its start +]) +def test_only_a_clear_unknown_lets_a_name_in(monkeypatch, flagged): + """A wrong "unknown" would rewrite a real word into a name, so only a + verdict on the whole word counts -- and a stall, which answers "no + misspelling", is not remembered: the next chunk asks again.""" + monkeypatch.setattr(tc, "_dictionary_language", lambda language: "de") + monkeypatch.setattr(tc, "_spell_checker", lambda: _FakeChecker(flagged)) + assert tc.apply_name_corrections("Die Mohna kam.", ["Mona"], "de") == "Die Mohna kam." + monkeypatch.setattr(tc, "_spell_checker", lambda: _FakeChecker()) + assert tc.apply_name_corrections("Die Mohna kam.", ["Mona"], "de") == "Die Mona kam." + + +def test_each_word_is_looked_up_once_per_chunk(monkeypatch): + checker = _FakeChecker() + monkeypatch.setattr(tc, "_dictionary_language", lambda language: "de") + monkeypatch.setattr(tc, "_spell_checker", lambda: checker) + assert tc.apply_name_corrections("Mohna, Mohna und Mohna.", ["Mona"], "de") \ + == "Mona, Mona und Mona." + assert checker.asked == ["Mohna"] + + +def test_a_spell_checker_that_fails_costs_no_job(monkeypatch): + """Name correction runs after each pass is decoded; the dictionary list was + the one AppKit call outside a guard, and an error there failed the job.""" + class _Broken: + def availableLanguages(self): + raise RuntimeError("the spell server is gone") + + monkeypatch.setattr(tc, "_spell_checker", lambda: _Broken()) + tc._dictionary_language.cache_clear() + try: + assert tc.apply_name_corrections("Die Mohna kam.", ["Mona"], "de") == "Die Mohna kam." + finally: + tc._dictionary_language.cache_clear() + + +def test_apply_name_corrections_ignores_empty_and_short_names(dictionaries): + assert tc.apply_name_corrections("Mohna kam.", [], "de") == "Mohna kam." + assert tc.apply_name_corrections("Mohna kam.", ["Ro"], "de") == "Mohna kam." + + +def test_degenerate_detector_separates_real_text_from_loops(): + """The repetition-loop detector must catch a runaway pass without ever + flagging real speech. + + Calibrated on real transcripts (Whisper and Voxtral, German): the longest + run of identical words is 2 and the compression ratio 2.59-2.63, while an + observed loop ran to 690 identical words at a ratio of 5.75. + """ + from noScribe.voxtral_engine import _looks_degenerate + + real = _clean_german_paragraph() + assert not _looks_degenerate(real) + # a pass that collapses into a loop, also when it starts out fine + assert _looks_degenerate("Jetzt. " * 40) + assert _looks_degenerate(real + " " + "Jetzt. " * 200) + # short passes are never judged (a brief pass is legitimate) + assert not _looks_degenerate("Ja ja ja ja ja.") + assert not _looks_degenerate("") + + +# Cycles taken from loops that reached finished transcripts before the +# detector counted cycles instead of identical neighbours. Every one of them +# is two words long, so no two adjacent words are equal and the old run +# counter measured 1 where the truth ran from 19 to 68 repeats. +OBSERVED_LOOP_CYCLES = ["dass das, ", "es ist, ", "ja, ich, ", "Macht ist... "] + + +@pytest.mark.parametrize("cycle", OBSERVED_LOOP_CYCLES) +def test_two_word_loops_are_caught_even_when_buried_in_real_speech(cycle): + """Regression: each of these shipped in a transcript unflagged. + + The loop is checked in the shape it actually occurred -- a short burst + inside an otherwise clean chunk. That matters, because the compression + ratio is computed over the whole pass and cannot see a local loop: at the + real 4% ratio it moved from 2.6 to 2.8, far below the 4.0 threshold. + """ + from noScribe.voxtral_engine import _looks_degenerate + + clean = _clean_german_paragraph() + loop = cycle * 19 # the mildest two-word loop actually observed + assert _looks_degenerate(loop.strip()) + assert _looks_degenerate(clean + " " + loop) + # and still when the loop is the same 4% slice of the chunk it was in the + # transcript that prompted this test + padded = " ".join([clean] * ((len(loop.split()) * 25) // len(clean.split()) + 1)) + assert _looks_degenerate(padded + " " + loop) + + +def test_real_speech_stays_clean_under_the_cycle_counter(): + """The counter must not fire on the repetition real speech does contain. + + Over 278 real units (Voxtral chunks and finished Whisper transcripts) + nothing that reads as speech exceeded 11 repeats, against a threshold of + 12. The margin is thin by measurement, not by choice -- see the constant. + """ + from noScribe.voxtral_engine import (DEGENERATE_CYCLE_REPEATS, + _longest_cycle_repeats, _looks_degenerate) + + clean = _clean_german_paragraph() + assert _longest_cycle_repeats(clean.split()) < DEGENERATE_CYCLE_REPEATS + # Emphatic repetition and a filler stutter, at the length real speech + # reaches. A longer synthetic case used to live here and was the only + # reason the threshold sat at 20; the corpus never produced anything like + # it, while a real 19-repeat loop went undetected because of it. + for phrase in ("Ja, ja, ja, genau so. ", "Also, also, also, ich meine. "): + assert not _looks_degenerate(clean + " " + phrase * 2 + clean) + + +def _clean_german_paragraph(): + return ( + "Und wenn du schon ein Produkt nimmst, hervorragend, dann hast du schon einen " + "Schritt weiter gemacht als viele andere. Meine Empfehlung wäre trotzdem, es " + "einmal auszuprobieren. Der Test zeigt dir nämlich ganz konkret, wo deine Werte " + "wirklich liegen, und danach kannst du die Veränderung tatsächlich messen. Das " + "Schöne daran ist, dass du überhaupt kein Risiko eingehst. Selbst wenn sich " + "herausstellt, dass dein bisheriges Öl völlig in Ordnung war, hast du wenigstens " + "Klarheit gewonnen. Für mich persönlich war genau das der entscheidende Punkt, " + "weil ich vorher jahrelang im Dunkeln getappt bin. Wer Kinder hat, sollte sie " + "unbedingt ebenfalls testen lassen, denn die brauchen anteilig sogar mehr." + ) + + +def _fake_audio(seconds=200): + import numpy as np + from noScribe.voxtral_engine import SAMPLE_RATE + a = (np.random.RandomState(0).randn(seconds * SAMPLE_RATE).astype(np.float32)) * 0.1 + mid = seconds // 2 + a[mid * SAMPLE_RATE:int((mid + 0.5) * SAMPLE_RATE)] = 0.0 # a clear pause to cut at + return a + + +VARIED_TEXT = [ + "Ich kann es euch jetzt auch nicht nicht erzählen, weil die Sache zu wichtig ist. " + "Wir haben lange überlegt und uns dann für den direkten Weg entschieden. " + "Der Test zeigt dir schwarz auf weiß, wo du stehst, und danach entscheidest du.", + "Was mich am meisten überzeugt hat, war die Klarheit der Werte nach acht Wochen. " + "Vorher hätte ich behauptet, alles richtig zu machen, und lag damit daneben. " + "Wer Kinder hat, sollte sie ebenfalls testen lassen, ihr Bedarf liegt höher.", +] + + +class _FakeVoxtral: + """Loops on long passes at penalty 1.0 (any temperature), transcribes + anything else fine -- exercising the split stage of the ladder.""" + + def __init__(self, always_loop=False): + self.calls = [] + self.always_loop = always_loop + self._i = 0 + + def transcribe_array(self, audio, language, max_new_tokens=0, repetition_penalty=1.0, + token_cb=None, temperature=0.0, seed=None, info=None): + from noScribe.voxtral_engine import SAMPLE_RATE + dur = len(audio) / SAMPLE_RATE + self.calls.append((round(dur), repetition_penalty)) + if repetition_penalty == 1.0 and (self.always_loop or dur > 150): + return "Und das ist jetzt passiert. " + "Jetzt. " * 300 + text = VARIED_TEXT[self._i % len(VARIED_TEXT)] + self._i += 1 + return text + + +def test_looping_pass_is_split_not_penalised(): + """Splitting must be tried first: a penalty deletes meaningful repetitions + (it turned "nicht nicht erzählen" into "nicht erzählen", inverting it).""" + from noScribe.voxtral_engine import _transcribe_guarded, _looks_degenerate + + vox = _FakeVoxtral() + out = _transcribe_guarded(vox, _fake_audio(), "de", None, "Pass 1/1") + assert not _looks_degenerate(out) + assert "nicht nicht" in out # meaningful repetition survives + assert all(p == 1.0 for _, p in vox.calls) # no penalty was needed + # greedy 200s, gentle-sampling retry 200s (this fake loops regardless of + # temperature), then the split resolves it at 2x100s. + assert [d for d, _ in vox.calls] == [200, 200, 100, 100] + + +def test_penalty_is_the_last_resort_when_splitting_fails(): + from noScribe.voxtral_engine import _transcribe_guarded, _looks_degenerate + + vox = _FakeVoxtral(always_loop=True) + out = _transcribe_guarded(vox, _fake_audio(), "de", None, "Pass 1/1") + assert not _looks_degenerate(out) + assert vox.calls[0] == (200, 1.0) # unpenalised attempt first + penalties = [p for _, p in vox.calls if p != 1.0] + assert penalties # ...penalty only afterwards + assert penalties[0] == 1.01 # and the gentlest one first + + +def test_split_point_lands_in_the_pause(): + from noScribe.voxtral_engine import _quietest_split, SAMPLE_RATE + + cut = _quietest_split(_fake_audio(200)) / SAMPLE_RATE + assert 99.9 <= cut <= 100.6 + + +def test_model_ram_hint_round_trips(): + """The picker shows the RAM requirement, but the app must still get the + plain model name back out of the decorated entry.""" + from types import SimpleNamespace + import noScribe.main as m + from noScribe import transcription, voxtral_engine as v + + stub = SimpleNamespace(whisper_models={}, MODEL_LABEL_SEP=m.App.MODEL_LABEL_SEP, + _model_label_to_name={}) + stub.whisper_models["voxtral-mini-8bit"] = transcription.WhisperModel( + name="voxtral-mini-8bit", path=None, engine="voxtral", + repo="models/voxtral-mini-8bit") + stub.whisper_models["precise"] = transcription.WhisperModel(name="precise", path=None) + + label = m.App.model_label(stub, "voxtral-mini-8bit") + assert "GB RAM" in label + # As the dropdown does, remember the label -> name mapping. + stub._model_label_to_name = {label: "voxtral-mini-8bit"} + assert m.App.model_key(stub, label) == "voxtral-mini-8bit" + # A decorated label not in the map still round-trips via the separator split. + stub._model_label_to_name = {} + assert m.App.model_key(stub, label) == "voxtral-mini-8bit" + # non-Voxtral models are shown unchanged + assert m.App.model_label(stub, "precise") == "precise" + assert m.App.model_key(stub, "precise") == "precise" + + +def test_model_too_large_for_the_machine_is_refused(): + """Starting a run that cannot fit does not fail loudly -- it swaps until + nothing progresses. Refuse up front instead.""" + import pytest + from noScribe import voxtral_engine as v + + orig = v._total_ram_gb + try: + v._total_ram_gb = lambda: 16.0 + with pytest.raises(MemoryError): + v._auto_chunk_sec("models/voxtral-small-8bit", None) + # the small 3B build still works on the same machine + assert v._auto_chunk_sec("models/voxtral-mini-8bit", None) > 0 + finally: + v._total_ram_gb = orig + + +class _StopRun(Exception): + """Sentinel raised by the stubbed model constructor: proves the run got + exactly as far as the model load and no further.""" + + +def _tiny_wav(tmp_path): + import numpy as np + import soundfile as sf + p = tmp_path / "t.wav" + sf.write(p, np.zeros(16000, dtype="float32"), 16000) + return str(p) + + +def test_refusal_happens_before_the_model_is_loaded(monkeypatch, tmp_path): + """The point of refusing an oversized model is to refuse *before* 20+ GB of + weights push the machine into swap -- so transcribe() must size passes + first and only then construct the model.""" + import pytest + from noScribe import voxtral_engine as v + + wav = _tiny_wav(tmp_path) + loaded = [] + monkeypatch.setattr(v, "_Voxtral", lambda repo: loaded.append(repo)) + monkeypatch.setattr(v, "_total_ram_gb", lambda: 16.0) + with pytest.raises(MemoryError): + v.transcribe(wav, voxtral_repo="models/voxtral-small-8bit") + assert loaded == [] # refused without ever touching the weights + # ...but a missing FILE is a missing file, not a memory problem + with pytest.raises(FileNotFoundError): + v.transcribe(str(tmp_path / "missing.wav"), + voxtral_repo="models/voxtral-small-8bit") + assert loaded == [] + + +def test_pinned_chunk_sec_cannot_bypass_the_memory_ceiling(monkeypatch, tmp_path): + """A voxtral_chunk_sec pinned while experimenting with mini must not let a + hungrier model run passes whose working set cannot fit (that run would swap + forever, not fail) -- nor exceed the model-context cap on a huge machine.""" + import pytest + from noScribe import voxtral_engine as v + + wav = _tiny_wav(tmp_path) + + def stop(repo): + raise _StopRun + + monkeypatch.setattr(v, "_Voxtral", stop) + monkeypatch.setattr(v, "_total_ram_gb", lambda: 32.0) + warnings = [] + with pytest.raises(_StopRun): # got past sizing, stopped at model load + v.transcribe(wav, voxtral_repo="models/voxtral-small-6bit", + chunk_sec=1500, need_timestamps=False, + log_cb=lambda lvl, msg: warnings.append((lvl, msg))) + # 6-bit small on 32 GB: hard ceiling (32-6-20.9)/0.0135 = 377s + assert any(lvl == "warn" and "voxtral_chunk_sec" in msg and "377" in msg + for lvl, msg in warnings) + # a model that cannot fit at all is refused even with a pinned length + monkeypatch.setattr(v, "_total_ram_gb", lambda: 16.0) + with pytest.raises(MemoryError): + v.transcribe(wav, voxtral_repo="models/voxtral-small-8bit", chunk_sec=60) + # A huge machine still gets the shorter of the two caps, and the warning has + # to name the one that actually bound. Saying "more context than the model + # has" about a window the model has plenty of context for is worse than + # saying nothing. + monkeypatch.setattr(v, "_total_ram_gb", lambda: 128.0) + + def pin(seconds): + warnings.clear() + with pytest.raises(_StopRun): + v.transcribe(wav, voxtral_repo="models/voxtral-mini-8bit", + chunk_sec=seconds, need_timestamps=False, + log_cb=lambda lvl, msg: warnings.append((lvl, msg))) + return [msg for lvl, msg in warnings + if lvl == "warn" and "voxtral_chunk_sec" in msg] + + msgs = pin(2400) + assert any(str(v.TRUSTED_CHUNK_SEC) in m and "measured" in m for m in msgs), msgs + assert not any("context" in m for m in msgs), msgs + + # With the measured cap lifted past the context cap, the context wording is + # the correct one again. + monkeypatch.setattr(v, "TRUSTED_CHUNK_SEC", v.MAX_CHUNK_SEC + 600) + msgs = pin(2400) + assert any("context" in m and str(v.MAX_CHUNK_SEC) in m for m in msgs), msgs + + +def test_low_reserve_is_not_a_refusal(monkeypatch): + """voxtral_ram_reserve_gb below MIN_HEADROOM_GB is the documented way to + use a freed-up machine; it must clamp to the hard ceiling, not refuse a + model that fits (regression: est_peak ~= total - reserve tripped the + refusal for every reserve < 6).""" + from noScribe import voxtral_engine as v + + monkeypatch.setattr(v, "_total_ram_gb", lambda: 32.0) + # mini bf16 profile on 32 GB with reserve 5 used to raise MemoryError + chunk = v._auto_chunk_sec("models/voxtral-mini", None, ram_reserve_gb=5) + assert chunk > 0 + # and never beyond the hard ceiling: peak stays under total - MIN_HEADROOM + m = v.MEM_MODEL["mini"] + assert m["fixed"] + m["slope"] * chunk <= 32.0 - v.MIN_HEADROOM_GB + 0.01 + + +def test_unquantised_source_repo_is_refused(monkeypatch, tmp_path): + """The raw mistralai releases are conversion *sources*; the engine must + refuse them instead of downloading tens of GB it then meters with the wrong + memory profile.""" + import pytest + from noScribe import voxtral_engine as v + + loaded = [] + monkeypatch.setattr(v, "_Voxtral", lambda repo: loaded.append(repo)) + for src in v.SOURCE_REPOS: + with pytest.raises(ValueError, match="quantize_voxtral"): + v.transcribe(_tiny_wav(tmp_path), voxtral_repo=src) + assert loaded == [] + + +def test_dense_encoder_builds_classify_by_bit_width(): + """The shipped builds keep the encoder in bf16 ("dense-encoder"); that adds + under a GB, so they must classify by their bit width -- both as a local dir + and as the published hub repo id downloaded on first use.""" + from noScribe import voxtral_engine as v + + assert v._model_kind("models/voxtral-mini-8bit") == "mini8" + assert v._model_kind("MarkusKaemmerer/Voxtral-Mini-3B-2507-8bit-dense-encoder") == "mini8" + assert v._model_kind("MarkusKaemmerer/Voxtral-Small-24B-2507-4bit-dense-encoder") == "small" + assert v._model_kind("MarkusKaemmerer/Voxtral-Small-24B-2507-8bit-dense-encoder") == "small8" + # older uniform names still map correctly + assert v._model_kind("models/voxtral-small-6bit") == "small6" + + +def test_bare_api_default_model_exists(): + """transcribe() without voxtral_repo must fall back to a key that is + actually in VOXTRAL_MODELS (the mini rename broke this silently once).""" + from noScribe import voxtral_engine as v + assert "voxtral-mini-8bit" in v.VOXTRAL_MODELS + + +def test_published_builds_are_offered(monkeypatch): + """Both shipped builds download on first use, so has_local_build is true + even with no local copy; an unknown name still needs a local build.""" + from noScribe import voxtral_engine as v + + monkeypatch.setattr(v, "_local_copy", lambda name: None) + assert v.has_local_build("voxtral-mini-8bit") + assert v.has_local_build("voxtral-small-4bit") + assert not v.has_local_build("some-unbuilt-experiment") + + monkeypatch.setattr(v, "_local_copy", lambda name: f"models/{name}") + assert v.has_local_build("some-unbuilt-experiment") + + +def test_split_sentences_keeps_unpunctuated_tail(): + """A pass whose text does not end in . ! ? must keep ALL its words, not just + the last one. An earlier `\\S+$` fallback dropped everything between the last + period and the final token (silent transcript loss on the .txt path).""" + from noScribe.voxtral_engine import _split_sentences + assert _split_sentences("das ist ein test ohne punkt") == [ + "das ist ein test ohne punkt"] + assert _split_sentences("Hallo. Wie geht es dir") == [ + "Hallo.", "Wie geht es dir"] + # normal punctuated text is unchanged + assert _split_sentences("Ein Satz. Noch einer.") == ["Ein Satz.", "Noch einer."] + + +def test_model_kind_unknown_is_conservative(): + """An unrecognised build (no mini/small/size token) must fall back to the + most memory-hungry profile, never the cheap `mini` one -- under-sizing only + runs slower, over-sizing swaps forever.""" + from noScribe.voxtral_engine import _model_kind + assert _model_kind("voxtral-mini-8bit") == "mini8" + assert _model_kind("voxtral-small-8bit") == "small8" + assert _model_kind("voxtral-small-4bit") == "small" + assert _model_kind("totally-unknown-build") == "small8" + # a 24B build that forgot the "small" token is still metered as big + assert _model_kind("my-voxtral-24b-8bit") == "small8" + + +# --------------------------------------------------------------------------- # +# Keeping the clean prefix of a looping pass +# --------------------------------------------------------------------------- # + +CLEAN_PREFIX = ( + "Wir haben gestern lange über die eigentlichen Ziele gesprochen. " + "Danach kam ziemlich unvermittelt die Frage nach den Werten auf. " + "Ich fand diese Diskussion ausgesprochen aufschlussreich und ehrlich. " + "Am Ende blieb trotzdem eine gewisse Unsicherheit im Raum stehen. " + "Deshalb schauen wir uns die einzelnen Schritte heute genauer an. " + "Meine Empfehlung wäre, zunächst mit einer kleinen Übung zu beginnen. " + "Wer damit Schwierigkeiten hat, meldet sich einfach kurz bei mir. " + "Später sprechen wir dann über die Erfahrungen aus der Praxis. " + "Vielleicht ergibt sich daraus schon eine erste gemeinsame Richtung. " + "Für heute soll uns dieser Einstieg aber erst einmal genügen." +) + + +class _LoopsLateVoxtral: + """Transcribes a long window well for a while and only then falls into a + loop -- the shape a real pass has. Shorter windows come out clean.""" + + def __init__(self): + self.calls = [] + self._i = 0 + + def transcribe_array(self, audio, language, max_new_tokens=0, repetition_penalty=1.0, + token_cb=None, temperature=0.0, seed=None, info=None): + from noScribe.voxtral_engine import SAMPLE_RATE + dur = len(audio) / SAMPLE_RATE + self.calls.append(round(dur)) + if dur > 150: + return CLEAN_PREFIX + " " + "dass das, " * 40 + text = VARIED_TEXT[self._i % len(VARIED_TEXT)] + self._i += 1 + return text + + +def _align_one_word_per_second(words, window): + """Stand-in for forced alignment: one word per second, confidently.""" + return [{"word": w, "start": float(i), "end": float(i + 1), "prob": 0.9} + for i, w in enumerate(words)] + + +def _align_by_spreading(words, window): + """What _Aligner returns when real alignment is impossible: words spread + evenly over the window, marked with prob 0.0.""" + return [dict(s, prob=0.0) for s in _align_one_word_per_second(words, window)] + + +def test_degenerate_span_finds_where_the_loop_starts(): + from noScribe.voxtral_engine import _degenerate_span + + words = ("Ein ganz normaler Satz mit Inhalt. " + "dass das, " * 40).split() + start, end = _degenerate_span(words) + assert start == 6 # right after the clean sentence + assert end == len(words) + # a cycle that repeats too few times is not a loop + assert _degenerate_span("dass das, dass das, dass das,".split()) is None + + +def test_prefix_ends_at_the_last_completed_sentence(): + from noScribe.voxtral_engine import _prefix_end_index + + words = "Erster Satz. Zweiter Satz. Und dann kippt es weg weg weg".split() + # the loop starts at "weg" (index 8); the unfinished sentence before it goes too + assert _prefix_end_index(words, 8) == 4 + # nothing to keep when no sentence ends before the loop + assert _prefix_end_index("und dann weg weg weg".split(), 2) == 0 + + +def test_clean_prefix_is_kept_and_only_the_rest_is_redone(): + """The point of the rung: no second decode of the whole window, and the + part the model got right is preserved verbatim rather than re-rolled.""" + from noScribe.voxtral_engine import _transcribe_guarded, _looks_degenerate + + vox = _LoopsLateVoxtral() + out = _transcribe_guarded(vox, _fake_audio(200), "de", None, "Pass 1/1", + align_cb=_align_one_word_per_second) + + assert not _looks_degenerate(out) + assert "dass das," not in out + assert "Deshalb schauen wir uns die einzelnen Schritte heute genauer an." in out + # exactly two decodes: the original pass, then only what came after the loop + assert len(vox.calls) == 2, vox.calls + assert vox.calls[0] == 200 + assert 100 <= vox.calls[1] <= 106, vox.calls # ~96s kept, remainder redone + + +def test_no_salvage_when_alignment_only_spread_the_words(): + """Spread positions are far too rough to cut audio on -- a wrong cut + duplicates or drops speech, so the ladder must fall back instead.""" + from noScribe.voxtral_engine import _transcribe_guarded, _looks_degenerate + + logged = [] + vox = _LoopsLateVoxtral() + out = _transcribe_guarded(vox, _fake_audio(200), "de", + lambda level, msg: logged.append(msg), "Pass 1/1", + align_cb=_align_by_spreading) + assert not _looks_degenerate(out) + assert vox.calls[:2] == [200, 200] # no salvage; the full retry happened + # ...and the log states the reason, so a refused salvage can be told from + # a rung that never ran at all + assert any("cannot keep the clean part" in m and "even guess" in m + for m in logged), logged + + +def test_declining_because_the_clean_part_is_short_says_so(): + from noScribe.voxtral_engine import _transcribe_guarded, SALVAGE_MIN_PREFIX_SEC + + logged = [] + _transcribe_guarded(_FakeVoxtral(), _fake_audio(200), "de", + lambda level, msg: logged.append(msg), "Pass 1/1", + align_cb=_align_one_word_per_second) + assert any(f"below the {SALVAGE_MIN_PREFIX_SEC}s minimum" in m for m in logged), logged + +def test_no_salvage_when_the_clean_part_is_too_short(): + """Below the minimum a full retry costs about the same and keeps the whole + window's context, which a resumed pass loses.""" + from noScribe.voxtral_engine import _transcribe_guarded, _looks_degenerate + + vox = _FakeVoxtral() # loops after five words + out = _transcribe_guarded(vox, _fake_audio(200), "de", None, "Pass 1/1", + align_cb=_align_one_word_per_second) + assert not _looks_degenerate(out) + assert [d for d, _ in vox.calls] == [200, 200, 100, 100] # unchanged ladder + + +ENGLISH_PREFIX = ( + "We spent a long time yesterday talking about what the actual goals are. " + "After that there was a rather sudden question about the values. " + "I thought that discussion was remarkably insightful and honest. " + "In the end a certain uncertainty was still left in the room. " + "That is why we are going to look at the individual steps more closely. " + "My recommendation would be to start with a small exercise first. " + "Anyone who has trouble with it should just get in touch with me. " + "Later on we will talk about the experiences from actual practice. " + "Perhaps a first shared direction will already come out of that. " + "For today this introduction should be enough for us though." +) + + +class _TranslatesThenLoops: + """The measured case from run 7: the greedy pass translates the window + into English and then loops; every shorter pass comes back German. That + very pass is the one the prefix rung keeps.""" + + def __init__(self): + self.calls = [] + + def transcribe_array(self, audio, language, max_new_tokens=0, repetition_penalty=1.0, + token_cb=None, temperature=0.0, seed=None, info=None): + from noScribe.voxtral_engine import SAMPLE_RATE + dur = len(audio) / SAMPLE_RATE + self.calls.append((round(dur), temperature)) + if dur > 150 and temperature == 0.0: + return ENGLISH_PREFIX + " " + "dass das, " * 40 + return CLEAN_PREFIX + + +def test_a_translated_prefix_is_not_stitched_onto_a_german_remainder(): + """Voxtral occasionally translates instead of transcribing. All other + rungs throw such a pass away and decode afresh -- which repairs the + translation as a side effect. The prefix rung is the only one that KEEPS + it, so it has to look: measured (run 7), the first 902 s of a chunk stayed + English while the freshly transcribed remainder was German, and the + transcript switched language in the middle of the chunk.""" + from noScribe.voxtral_engine import (_transcribe_guarded, _detect_language, + _looks_degenerate) + + # Test setup: the two halves really have to be detected as different, + # otherwise the test checks nothing. + assert _detect_language(ENGLISH_PREFIX)[0] == "en" + assert _detect_language(CLEAN_PREFIX)[0] == "de" + + logged = [] + vox = _TranslatesThenLoops() + out = _transcribe_guarded(vox, _fake_audio(200), "de", + lambda level, msg: logged.append(msg), "Pass 1/1", + align_cb=_align_one_word_per_second) + + assert not _looks_degenerate(out) + assert _detect_language(out)[0] == "de", \ + "the English prefix was stitched into the result" + assert "welcome" not in out.lower() and "yesterday" not in out.lower() + # ...and the reason is in the log, otherwise it looks like an ordinary loop + assert any("translated pass" in m for m in logged), logged + + +def test_a_salvage_whose_halves_agree_is_still_kept(): + """Counter-check: the guard compares prefix against remainder, not against + the configured language. Speakers mixing German and English are the + normal case in this material -- a guard that checked against the setting + would refuse good salvages by the dozen.""" + from noScribe.voxtral_engine import _transcribe_guarded + + vox = _LoopsLateVoxtral() + out = _transcribe_guarded(vox, _fake_audio(200), "en", None, "Pass 1/1", + align_cb=_align_one_word_per_second) + assert out.startswith(CLEAN_PREFIX[:40]) # salvaged although 'en' is set + assert vox.calls[:2] == [200, 104] # prefix kept, remainder redone + + +# --------------------------------------------------------------------------- # +# A whole chunk comes back in the wrong language +# --------------------------------------------------------------------------- # +def test_one_chunk_never_establishes_the_files_language(): + """The most dangerous mistake would be to take the language of the FIRST + chunk as the truth: if that is the translated one, every following chunk + would be 'repaired' into the wrong language. Two have to agree.""" + from noScribe.voxtral_engine import _file_language + + assert _file_language({}) is None + assert _file_language({"en": 1}) is None # the outlier alone + assert _file_language({"en": 1, "de": 1}) is None # a tie decides nothing + assert _file_language({"en": 1, "de": 2}) == "de" + assert _file_language({"de": 5, "en": 1}) == "de" + + +class _TranslatesWithoutLooping: + """The measured case from runs 1-4: the greedy pass comes back English, + without any loop at all. Only an attempt with temperature yields + German.""" + + def __init__(self): + self.calls = [] + + def transcribe_array(self, audio, language=None, max_new_tokens=0, + repetition_penalty=1.0, token_cb=None, temperature=0.0, + seed=None, info=None): + self.calls.append(temperature) + return ENGLISH_PREFIX if temperature == 0.0 else CLEAN_PREFIX + + +def test_a_translated_pass_makes_the_ladder_run_even_without_a_loop(): + """A translation is just as unusable as a loop and gets the same repairs + -- the case needs no second ladder alongside. Measured: writing `lang:de` + into the prompt brought back 0 of 2 flipped windows, one temperature + attempt brought back both.""" + from noScribe.voxtral_engine import (_transcribe_guarded, _detect_language, + _looks_degenerate) + + assert _detect_language(ENGLISH_PREFIX)[0] == "en" # test setup + logged = [] + vox = _TranslatesWithoutLooping() + out = _transcribe_guarded(vox, _fake_audio(200), "de", + lambda lvl, msg: logged.append(msg), "Chunk 5/15", + want_lang="de") + + assert not _looks_degenerate(out) + assert _detect_language(out)[0] == "de", "the translation was shipped" + assert vox.calls[:2] == [0.0, 0.2], vox.calls + assert any("translated pass" in m for m in logged), logged + + +def test_without_an_expected_language_nothing_is_called_translated(): + """Counter-check: as long as the file has not said what it is, no pass may + be discarded because of its language -- otherwise the first chunk decides + for all that follow.""" + from noScribe.voxtral_engine import _transcribe_guarded + + vox = _TranslatesWithoutLooping() + out = _transcribe_guarded(vox, _fake_audio(200), "de", None, "Chunk 1/15") + assert out == ENGLISH_PREFIX and vox.calls == [0.0] + + +def test_a_seam_between_two_languages_is_refused(): + """Both rungs that repair a window sew together two separately produced + halves. If one of them flips into English, the transcript switches + language in the middle of the chunk -- measured in run 7, where the kept + prefix was 902 s of English and the freshly transcribed remainder German.""" + from noScribe.voxtral_engine import _halves_disagree + + logged = [] + log = lambda lvl, msg: logged.append(msg) + assert _halves_disagree(ENGLISH_PREFIX, CLEAN_PREFIX, log, "Chunk 1/15", + "the kept part", "the remainder") + assert any("translated pass" in m for m in logged), logged + # Same language on both sides, and too short to judge: both stay silent. + assert not _halves_disagree(CLEAN_PREFIX, CLEAN_PREFIX, log, "x", "a", "b") + assert not _halves_disagree(CLEAN_PREFIX, "Ja, genau.", log, "x", "a", "b") + + +def test_transcribe_asks_the_ladder_for_a_language(): + """Wiring: the case from runs 1-4 had NO loop AT ALL. The expectation + therefore has to go into the ladder, not be checked afterwards.""" + import inspect + from noScribe import voxtral_engine as v + + src = inspect.getsource(v.transcribe) + assert "want_lang=want" in src, "the ladder is not told the expected language" + assert "_file_language(" in src, "the file language is not determined" + # ...and whatever went out before the language became known is named. + assert "already written out" in src + + +def test_remembered_model_survives_the_decorated_picker(): + """The model choice is stored as its plain name, not as the display text. + + The picker shows "name · N GB RAM"; at start-up the remembered value is + looked up in whisper_models, which holds plain names only. A merge had lost + the model_key() call, which would have silently forgotten the choice at + the next start.""" + import inspect + import noScribe.main as m + + src = inspect.getsource(m.App.save_ui_state) + assert "self.model_key(" in src, "save_ui_state stores the display text" diff --git a/tests/test_turn_split.py b/tests/test_turn_split.py new file mode 100644 index 00000000..ed8cbb27 --- /dev/null +++ b/tests/test_turn_split.py @@ -0,0 +1,254 @@ +"""Tests for the speaker-turn-aware loop split and the escalation diagnosis. + +The cleanest place to cut a looping chunk is where the speaker changes: +context within a turn stays intact, across the change it matters least. +These tests pin the boundary selection (different-speaker only, no overlap +cuts, middle half, nearest-to-middle wins), the window clipping used by the +recursion, and that the ladder actually prefers the turn boundary over the +quietest-frame fallback -- plus the diarization profile line once a loop +resists the gentle repairs. +""" +import numpy as np + +from noScribe.voxtral_engine import ( + SAMPLE_RATE, + _clip_turns, + _transcribe_guarded, + _turn_gap_split, + _turn_profile, +) + + +def _audio(seconds, silences=()): + """Noisy audio with 1 s of silence centred on each given second mark.""" + rng = np.random.default_rng(0) + a = (rng.standard_normal(int(seconds * SAMPLE_RATE)) * 0.1).astype(np.float32) + for at in silences: + a[int((at - 0.5) * SAMPLE_RATE):int((at + 0.5) * SAMPLE_RATE)] = 0.0 + return a + + +# --------------------------------------------------------------------------- # +# _turn_gap_split +# --------------------------------------------------------------------------- # +def test_cut_lands_on_the_speaker_change(): + audio = _audio(200, silences=(100,)) + turns = [(0.0, 99.5, "A"), (100.5, 200.0, "B")] + cut = _turn_gap_split(audio, turns) + assert cut is not None + assert 99.0 <= cut / SAMPLE_RATE <= 101.0 + +def test_same_speaker_pause_is_not_a_turn_boundary(): + audio = _audio(200, silences=(100,)) + turns = [(0.0, 99.5, "A"), (100.5, 200.0, "A")] + assert _turn_gap_split(audio, turns) is None + +def test_overlapping_speech_boundary_is_skipped(): + audio = _audio(200, silences=(100,)) + turns = [(0.0, 105.0, "A"), (95.0, 200.0, "B")] # 10 s overlap + assert _turn_gap_split(audio, turns) is None + +def test_boundary_outside_the_middle_half_is_ignored(): + audio = _audio(200, silences=(20.5,)) + turns = [(0.0, 20.0, "A"), (21.0, 200.0, "B")] # change at 10% of the window + assert _turn_gap_split(audio, turns) is None + +def test_nearest_boundary_to_the_middle_wins(): + audio = _audio(200, silences=(60.5, 100.0)) + turns = [(0.0, 60.0, "A"), (61.0, 99.5, "B"), (100.5, 200.0, "C")] + cut = _turn_gap_split(audio, turns) + assert 99.0 <= cut / SAMPLE_RATE <= 101.0 + +def test_no_turns_means_no_turn_split(): + assert _turn_gap_split(_audio(10), None) is None + assert _turn_gap_split(_audio(10), []) is None + + +# --------------------------------------------------------------------------- # +# _clip_turns / _turn_profile +# --------------------------------------------------------------------------- # +def test_clip_turns_shifts_to_window_relative_times(): + turns = [(10.0, 30.0, "A"), (40.0, 80.0, "B")] + assert _clip_turns(turns, 20.0, 60.0) == [(0.0, 10.0, "A"), (20.0, 40.0, "B")] + assert _clip_turns(turns, 90.0, 120.0) is None + assert _clip_turns(None, 0.0, 10.0) is None + +def test_turn_profile_counts_speakers_changes_and_coverage(): + turns = [(0.0, 30.0, "A"), (30.0, 60.0, "B")] + p = _turn_profile(turns, 60.0) + assert "2 speaker(s)" in p and "1 turn change(s)" in p and "100% speech" in p + + +# --------------------------------------------------------------------------- # +# Ladder integration +# --------------------------------------------------------------------------- # +VARIED = ("Der Test zeigt dir schwarz auf weiß, wo du stehst. " + "Wir haben lange überlegt und uns dann entschieden.") + + +class _FakeVox: + """Loops on windows > 150 s (any temperature); short windows come out fine.""" + + def __init__(self): + self.calls = [] + + def transcribe_array(self, audio, language, max_new_tokens=0, + repetition_penalty=1.0, token_cb=None, + temperature=0.0, seed=None, info=None): + dur = len(audio) / SAMPLE_RATE + self.calls.append(round(dur)) + if repetition_penalty == 1.0 and dur > 150: + return "Und dann. " + "Jetzt. " * 300 + return VARIED + + +def test_ladder_prefers_the_turn_boundary_over_the_middle(): + # Turn change at 130 s, well away from the middle. The quietest-frame + # fallback only searches +/-5 s around 100 s, so 130/70 halves prove the + # turn boundary was used. + audio = _audio(200, silences=(100.0, 130.0)) + turns = [(0.0, 129.5, "A"), (130.5, 200.0, "B")] + vox = _FakeVox() + out = _transcribe_guarded(vox, audio, "de", None, "t", turns=turns) + assert VARIED in out + assert vox.calls == [200, 200, 130, 70] # greedy, T=0.2, then the two halves + +def test_diagnosis_line_appears_when_gentle_repairs_fail(): + audio = _audio(60) # too short to split + turns = [(0.0, 30.0, "A"), (30.0, 60.0, "B")] + + class _AlwaysLoops(_FakeVox): + def transcribe_array(self, *a, **kw): + super().transcribe_array(*a, **kw) + return "Und dann. " + "Jetzt. " * 300 + + logs = [] + _transcribe_guarded(_AlwaysLoops(), audio, "de", + lambda lvl, msg: logs.append(msg), "t", turns=turns) + assert any("diarization profile" in m and "2 speaker(s)" in m for m in logs) + + +def test_alignment_pool_serves_the_loop_ladder(): + """Keeping the prefix needs a time mapping. It hung as a closure on a + variable in transcribe() and silently became a no-op when the alignment + architecture was moved onto the pool -- the cherry-pick went through + without conflicts, the tests stayed green, the feature was dead. Now it + belongs to the pool, and that is pinned here.""" + from noScribe.voxtral_engine import _AlignerPool + + pool = _AlignerPool("de", None) + seen = {} + + class _StubAligner: + def align_prefix(self, words, audio, t_offset=0.0): + seen["words"], seen["audio"] = words, audio + seen["how"] = "align_prefix" + return [{"word": w, "start": i, "end": i + 1, "prob": -0.1} + for i, w in enumerate(words)] + + def align_words(self, words, audio, t_offset=0.0, words_span_audio=True): + seen["how"] = "align_words" + return [] + + def _pick(text, remember=True): + seen["remember"] = remember + return _StubAligner() + + pool.aligner_for = _pick + out = pool.align_for_salvage(["Guten", "Morgen."], "AUDIO") + assert seen["words"] == ["Guten", "Morgen."] and seen["audio"] == "AUDIO" + assert out[-1]["end"] == 2 + # The prefix only covers the start of the window. `align_words` cannot do + # that: it has no way of stopping early and drags the last words across + # the remaining audio (measured: an 85 s prefix ended at 299.98 s of a + # 300 s window, with immaculate scores). + assert seen["how"] == "align_prefix" + # ...and an internal alignment of a fragment that may well be discarded + # must not set the model choice for the next chunk. + assert seen["remember"] is False + + +def test_salvage_alignment_does_not_move_the_pool_state(monkeypatch): + """Regression: the salvage rung went through aligner_for and thereby + overwrote _last_model -- the next chunk without a dominant language of its + own silently inherited the language of a fragment that often does not + even end up in the transcript. The same applied to the one-off language + warning.""" + from noScribe.voxtral_engine import _AlignerPool, ALIGN_MODELS + + loaded = [] + + class _Stub: + def align_prefix(self, words, audio, t_offset=0.0): + return [{"word": w, "start": 0.0, "end": 1.0, "prob": -0.1} + for w in words] + + def _fake_load(self, model, remember=True): + loaded.append(model) + if remember: + self._last_model = model + return _Stub() + + monkeypatch.setattr(_AlignerPool, "_load", _fake_load) + + # _detect_language needs >= 40 tokens, otherwise the test would run empty. + german = ("der die das und ich nicht ist wir ein eine mit auf für aber " + "auch dann wenn noch dass sind habe schon mal jetzt ") * 2 + english = (("the and you that this not with for have are was but they " + "what just like know then would been your about ") * 2).split() + from noScribe.voxtral_engine import _detect_language + assert _detect_language(german)[0] == "de" + assert _detect_language(" ".join(english))[0] == "en" + + # Auto: chunk 1 is German and sets the choice. + pool = _AlignerPool(None, None) + pool.aligner_for(german) + assert pool._last_model == ALIGN_MODELS["de"] + # An English salvage passage must not change that. + pool.align_for_salvage(english, "AUDIO") + assert pool._last_model == ALIGN_MODELS["de"], \ + "the fragment overwrote the model choice of the next chunk" + + # Explicit language: the one-off warning belongs to the transcript. + warned = [] + pool2 = _AlignerPool("de", lambda lvl, msg: warned.append((lvl, msg))) + pool2.align_for_salvage(english, "AUDIO") + assert not warned, "the warning was spent on a fragment" + assert pool2._warned is False + + +def test_transcribe_hands_the_ladder_a_real_callback(): + """Counter-check on the wiring: transcribe() has to pass the pool method + on, not None and not a stale local variable.""" + import inspect + from noScribe import voxtral_engine as v + + src = inspect.getsource(v.transcribe) + call = src[src.index("_transcribe_guarded("):] + assert "align_cb=(aligner_pool.align_for_salvage" in call, \ + "the ladder no longer gets a time mapping" + + +def test_backchannel_inside_a_turn_does_not_move_the_cut(): + """A short turn nested inside a longer one must not become a boundary. + + pyannote's standard pattern for an "mhm" during someone's sentence is a + 0.3 s turn whose start and end both fall inside a much longer one. Sorted + by start time, the turn that follows then looks like it succeeds the + backchannel, and comparing against *that* turn's end put the cut in the + middle of the enclosing speaker's utterance -- here at ~22.7 s, in the + middle of A's 10-30 s turn, instead of at the real change at 30 s. + """ + audio = _audio(60, silences=(30,)) + turns = [(0.0, 30.0, "A"), (15.0, 15.4, "B"), (30.0, 60.0, "A")] + # A resumes after B's backchannel -- there is no speaker change at all. + assert _turn_gap_split(audio, turns) is None + + +def test_change_after_a_backchannel_cuts_at_the_enclosing_turns_end(): + """With a real change after the nested turn, the cut belongs at its end.""" + audio = _audio(60, silences=(30,)) + turns = [(0.0, 30.0, "A"), (15.0, 15.4, "B"), (30.0, 60.0, "C")] + cut = _turn_gap_split(audio, turns) + assert cut is not None + assert 29.0 <= cut / SAMPLE_RATE <= 31.0 diff --git a/tests/test_voxtral_pin.py b/tests/test_voxtral_pin.py new file mode 100644 index 00000000..9f8bb038 --- /dev/null +++ b/tests/test_voxtral_pin.py @@ -0,0 +1,57 @@ +"""The installed Voxtral stack is the one the engine was validated against. + +`environments/requirements_voxtral_macOS_arm64.txt` pins mlx, mlx-lm and +mlx-voxtral to exact versions, and every measurement in VOXTRAL.md and docs/ +describes those versions -- but nothing used to check what is actually in the +environment. That gap is not cosmetic: mlx-voxtral 0.0.5 computes the log-Mel +per 30 s chunk and 0.0.6 over the whole audio, so a developer left on the older +pin feeds the encoder a different input from the one every documented number was +measured on, and no test says a word. The smoke test cannot catch it either -- it +compares the two decode paths of whatever happens to be installed. + +The version checks skip where the optional Apple-Silicon stack is absent, which +is how Linux CI and every non-Voxtral checkout see them; the check that the +pins are still exact is a plain file read and runs everywhere. +""" +import pathlib +import re + +import pytest + +from importlib.metadata import PackageNotFoundError, version + +REQUIREMENTS = (pathlib.Path(__file__).resolve().parent.parent + / "environments" / "requirements_voxtral_macOS_arm64.txt") + +PIN = re.compile(r"^([A-Za-z0-9._-]+)==([A-Za-z0-9.]+)\s*$") + + +def _pins(): + """{package: version} for the `name==version` lines, comments ignored.""" + out = {} + for line in REQUIREMENTS.read_text().splitlines(): + m = PIN.match(line) + if m: + out[m.group(1)] = m.group(2) + return out + + +def test_the_requirements_file_actually_pins_the_stack(): + """A guard on the guard: if the pins were loosened to `>=`, the test below + would pass vacuously by having nothing left to compare.""" + pins = _pins() + assert {"mlx", "mlx-lm", "mlx-voxtral"} <= set(pins), \ + f"exact pins missing from {REQUIREMENTS.name}: found {sorted(pins)}" + + +@pytest.mark.parametrize("package", ["mlx", "mlx-lm", "mlx-voxtral"]) +def test_the_installed_version_matches_the_pin(package): + pinned = _pins()[package] + try: + installed = version(package) + except PackageNotFoundError: + pytest.skip(f"{package} not installed (optional Apple-Silicon stack)") + assert installed == pinned, ( + f"{package} {installed} installed, {REQUIREMENTS.name} pins {pinned}. " + f"The engine and its measurements were validated against the pin; " + f"re-run `pip install -r environments/{REQUIREMENTS.name}`.") diff --git a/tests/test_voxtral_safety_guards.py b/tests/test_voxtral_safety_guards.py new file mode 100644 index 00000000..6d69e6f3 --- /dev/null +++ b/tests/test_voxtral_safety_guards.py @@ -0,0 +1,227 @@ +"""The guard rails before model loading -- and those of the salvage cut. + +What they have in common is that a mistake here does not crash: it sends the +machine into swap, never lets the job finish, or removes speech from the +transcript, without the log looking any different from a good run. +""" +import numpy as np +import pytest + +from noScribe import voxtral_engine as v +from noScribe.voxtral_engine import ( + HARD_MIN_CHUNK_SEC, + SAMPLE_RATE, + _model_kind, + _salvage_prefix, + _transcribe_guarded, +) + + +class _StopRun(Exception): + """The stubbed model constructor raises this: proves that the run got + exactly as far as model loading and not one step further.""" + + +def _tiny_wav(tmp_path): + import soundfile as sf + p = tmp_path / "t.wav" + sf.write(p, np.zeros(2 * 16000, dtype="float32"), 16000) + return str(p) + + +def _sized(monkeypatch, tmp_path, ram=32.0, **kw): + """Drive transcribe() up to model loading and collect the log lines.""" + def stop(repo): + raise _StopRun + + monkeypatch.setattr(v, "_Voxtral", stop) + monkeypatch.setattr(v, "_total_ram_gb", lambda: ram) + logs = [] + with pytest.raises(_StopRun): + v.transcribe(_tiny_wav(tmp_path), need_timestamps=False, + log_cb=lambda lvl, msg: logs.append((lvl, msg)), **kw) + return logs + + +# --------------------------------------------------------------------------- # +# Memory profile of a build +# --------------------------------------------------------------------------- # +def test_a_24b_build_without_a_bit_width_is_metered_conservatively(): + """`small` is the 4-bit profile and therefore the cheapest of the three 24B + entries. An unquantised or 6-bit folder without a bit width in its name + landed on it and got passes several hundred seconds long for weights that + need 25-48 GB -- the run then swaps endlessly instead of being refused.""" + assert _model_kind("models/Voxtral-Small-24B-2507") == "small8" + assert _model_kind("my-voxtral-24b") == "small8" + # With an explicit bit width everything stays as it was. + assert _model_kind("voxtral-small-4bit") == "small" + assert _model_kind("voxtral-small-6bit") == "small6" + assert _model_kind("voxtral-small-8bit") == "small8" + # The mini side was never affected: without a bit width it hits the more + # expensive bf16 profile, i.e. the safe direction. + assert _model_kind("models/Voxtral-Mini-3B-2507") == "mini" + assert _model_kind("voxtral-mini-8bit") == "mini8" + + +def test_an_unquantised_source_release_is_refused_by_path_too(monkeypatch, tmp_path): + """The block only compared the full Hub repo ID. A local copy under + models/ is a file path, though -- so exactly the build the block is meant + to stop got past it.""" + def stop(repo): + raise _StopRun + + monkeypatch.setattr(v, "_Voxtral", stop) + monkeypatch.setattr(v, "_total_ram_gb", lambda: 64.0) + wav = _tiny_wav(tmp_path) + for repo in ("mistralai/Voxtral-Small-24B-2507", + "models/Voxtral-Small-24B-2507", + "/srv/builds/Voxtral-Mini-3B-2507"): + with pytest.raises(ValueError, match="unquantised source release"): + v.transcribe(wav, voxtral_repo=repo, need_timestamps=False) + + +# --------------------------------------------------------------------------- # +# Pinned chunk_sec +# --------------------------------------------------------------------------- # +def test_a_sub_second_pinned_chunk_sec_cannot_collapse_the_passes(monkeypatch, tmp_path): + """int() turns anything below 1 s into 0, and max(1, 0 * SAMPLE_RATE) + turns that into a pass of ONE sample: a 2-minute file fell apart into 2401 + Voxtral decodes of 50 ms each, and the job never finished. Until now the + floor applied only on the automatic path.""" + logs = _sized(monkeypatch, tmp_path, chunk_sec=0.5, + voxtral_repo="models/voxtral-mini-8bit") + assert any(lvl == "warn" and "below the" in msg and "minimum" in msg + for lvl, msg in logs), logs + # ...and the old, misleading justification is gone. + assert not any("more context than the model has" in msg for _, msg in logs), logs + + +def test_a_pinned_chunk_sec_below_the_floor_is_lifted(monkeypatch, tmp_path): + logs = _sized(monkeypatch, tmp_path, chunk_sec=30, + voxtral_repo="models/voxtral-mini-8bit") + assert any(str(HARD_MIN_CHUNK_SEC) in msg for _, msg in logs), logs + + +def test_a_sane_pinned_chunk_sec_is_left_alone(monkeypatch, tmp_path): + """Counter-check: a usable value must not trigger a warning.""" + logs = _sized(monkeypatch, tmp_path, chunk_sec=600, + voxtral_repo="models/voxtral-mini-8bit") + assert not any(lvl == "warn" and "voxtral_chunk_sec" in msg + for lvl, msg in logs), logs + + +# --------------------------------------------------------------------------- # +# What the salvage cut may rely on +# --------------------------------------------------------------------------- # +LOOP_TEXT = ("Wir haben gestern lange über die eigentlichen Ziele gesprochen. " + "Danach kam ziemlich unvermittelt die Frage nach den Werten auf. " + "Ich fand diese Diskussion ausgesprochen aufschlussreich. " + + "dass das, " * 40).strip() + + +def _stamps(words, real_tail, step=4.0): + """One word every `step` seconds -- far enough apart that the prefix lies + above SALVAGE_MIN_PREFIX_SEC and the shortness bound does not fire first. + `real_tail` decides whether the LAST word was really aligned or is only + an estimate.""" + out = [{"word": w, "start": i * step, "end": (i + 1) * step, "prob": -0.1} + for i, w in enumerate(words)] + if not real_tail: + out[-1] = dict(out[-1], prob=0.0) + return out + + +def test_salvage_refuses_a_cut_taken_from_an_invented_timestamp(): + """The check used any() over the whole prefix -- a single genuinely + aligned word was enough. The cut, however, is taken exclusively on the + LAST timestamp. When that time was guessed (evenly spread or interpolated + words at the end), the resume point landed a measured 27 s too late, and + the speech in between fell out of the transcript.""" + audio = np.zeros(200 * SAMPLE_RATE, dtype=np.float32) + + good, why = _salvage_prefix( + LOOP_TEXT, audio, lambda w, a: _stamps(w, real_tail=True)) + assert good is not None and why is None + + bad, why = _salvage_prefix( + LOOP_TEXT, audio, lambda w, a: _stamps(w, real_tail=False)) + assert bad is None + assert "not really aligned" in why, why + + +def test_salvage_still_refuses_a_fully_spread_alignment(): + """The older case is kept, together with its justification in the log.""" + audio = np.zeros(200 * SAMPLE_RATE, dtype=np.float32) + spread = lambda w, a: [{"word": x, "start": float(i), "end": float(i + 1), + "prob": 0.0} for i, x in enumerate(w)] + out, why = _salvage_prefix(LOOP_TEXT, audio, spread) + # Evenly spread times are pure guesswork; the justification has to name + # that, otherwise a refused salvage cannot be told in the log from a rung + # that never ran. + assert out is None and "even guess" in why + + +def test_a_degenerate_prefix_is_not_shipped_as_a_success(): + """The `rest_sec < 1.0` exit was the only success exit of the ladder + without a degeneracy check: a prefix that is still looping itself passed + as a finished result and never escalated.""" + audio = np.zeros(200 * SAMPLE_RATE, dtype=np.float32) + # A prefix whose sentence repeats verbatim (the compression arm of the + # detector), followed by a short cycle loop. + sentence = "Und dann sagte sie genau dasselbe noch ein weiteres Mal dazu. " + text = (sentence * 60 + "dass das, " * 40).strip() + + calls = [] + + class _Vox: + def transcribe_array(self, audio, language, max_new_tokens=4096, + repetition_penalty=1.0, token_cb=None, + temperature=0.0, seed=None, info=None): + calls.append(round(len(audio) / SAMPLE_RATE)) + return text if len(calls) == 1 else "Ein sauberer zweiter Versuch." + + def _align_to_the_very_end(words, window): + # Last word really aligned, but practically at the window end -> + # rest_sec < 1.0, i.e. the exit in question. + n = len(words) + step = (len(window) / SAMPLE_RATE) / n + return [{"word": w, "start": i * step, "end": (i + 1) * step, + "prob": -0.1} for i, w in enumerate(words)] + + logs = [] + out = _transcribe_guarded(_Vox(), audio, "de", + lambda lvl, msg: logs.append(msg), "Pass 1/1", + align_cb=_align_to_the_very_end) + assert not v._looks_degenerate(out), out[:120] + assert len(calls) > 1, "the ladder did not carry on after the prefix" + assert any("still looks degenerate" in m for m in logs), logs + + +def test_the_salvage_alignment_runs_on_a_cleared_mlx_cache(): + """The prefix rung aligns right after the looping decode -- typically the + longest of the job -- and the aligner's forward runs on the same GPU. The + main path frees the dead MLX buffers before its alignment; this one did + not, which is exactly the co-residency MIN_HEADROOM_GB is meant to rule out.""" + events = [] + + class _Mx: + def clear_cache(self): + events.append("clear") + + class _Vox: + _mx = _Mx() + + def transcribe_array(self, audio, language, max_new_tokens=4096, + repetition_penalty=1.0, token_cb=None, + temperature=0.0, seed=None, info=None): + events.append("decode") + return LOOP_TEXT if events.count("decode") == 1 else "Ein sauberer Rest." + + def _align(words, window): + events.append("align") + return _stamps(words, real_tail=True) + + audio = np.zeros(200 * SAMPLE_RATE, dtype=np.float32) + _transcribe_guarded(_Vox(), audio, "de", None, "Pass 1/1", align_cb=_align) + assert "align" in events, events + assert events[events.index("align") - 1] == "clear", events diff --git a/tests/test_voxtral_smoke.py b/tests/test_voxtral_smoke.py new file mode 100644 index 00000000..0a5176ab --- /dev/null +++ b/tests/test_voxtral_smoke.py @@ -0,0 +1,87 @@ +"""Integration smoke test for the Voxtral stack. + +Guards the whole pipeline -- mlx-voxtral model load, VoxtralProcessor, the Whisper +encoder, _merge_input_embeddings, and our generate_step decode path -- against a +silent break when the (pinned) mlx / mlx-lm / mlx-voxtral versions or the model +format shift under an environment change. It does NOT assert specific German text +(no bundled/private audio); it asserts the invariant that actually matters: + + the fast greedy path is byte-identical to the library generate() path, + +on this machine's real model and library versions, plus "runs without error and +returns a str". Deterministic synthetic audio -- no files, no private data. +(The two paths intentionally diverge only on a single-token repetition loop -- +see _consume_tokens -- which this benign tone signal does not trigger.) + +Skipped unless mlx is importable AND the local mini-8bit build is present, so it +runs only where someone deliberately has the model (e.g. the dev machine) and is +silently skipped everywhere else. +""" +import os +import pytest + +pytest.importorskip("mlx.core") +pytest.importorskip("mlx_voxtral") + +_MODEL = os.path.join(os.path.dirname(os.path.dirname(__file__)), + "models", "voxtral-mini-8bit") +pytestmark = pytest.mark.skipif( + not os.path.isdir(_MODEL), + reason="local models/voxtral-mini-8bit not present (smoke test is opt-in)", +) + + +def _synthetic_audio(seconds=3.0): + """A short, quiet, deterministic 16 kHz mono signal -- enough to exercise the + encoder + decoder without needing an audio file.""" + import numpy as np + from noScribe.voxtral_engine import SAMPLE_RATE + t = np.arange(int(seconds * SAMPLE_RATE), dtype=np.float32) / SAMPLE_RATE + # a couple of soft tones; content is irrelevant, determinism is not + return (0.05 * np.sin(2 * np.pi * 220 * t) + + 0.03 * np.sin(2 * np.pi * 330 * t)).astype(np.float32) + + +@pytest.fixture(scope="module") +def voxtral(): + """One loaded model for the whole file. Loading per test would hold two + 6 GB builds at once on a machine the suite is expected to run on.""" + from noScribe.voxtral_engine import _Voxtral + return _Voxtral(_MODEL) + + +def test_voxtral_stack_runs_and_fast_matches_library(voxtral): + from noScribe.voxtral_engine import SAMPLE_RATE + + v = voxtral + audio = _synthetic_audio() + + # fast path (generate_step) via the public method + fast = v.transcribe_array(audio, "de") + assert isinstance(fast, str) + + # library reference path (same greedy settings) + inp = v.proc.apply_transcrition_request(audio=audio, language="de", + sampling_rate=SAMPLE_RATE) + mi = {"input_ids": inp.input_ids, "input_features": inp.input_features} + out = v.model.generate(**mi, max_new_tokens=4096, temperature=0.0, + repetition_penalty=1.0) + ref = v.proc.decode(out[0, inp.input_ids.shape[1]:], + skip_special_tokens=True).strip() + + assert fast == ref, ( + "fast generate_step path diverged from library generate() -- a pinned " + "mlx/mlx-lm/mlx-voxtral upgrade may have changed decode semantics.\n" + f"fast: {fast[:200]!r}\nref : {ref[:200]!r}" + ) + + +def test_the_penalty_rung_still_reaches_the_library(voxtral): + """The retry ladder's repetition-penalty rung is the one production path + that calls model.generate(), and the only caller passing `stop_tokens=`. + Nothing else here exercises it, so an upstream rename of that keyword would + surface at runtime -- on a retry, i.e. on a pass that was already going + wrong. Content is irrelevant; that the call survives the pin is not.""" + out = voxtral.transcribe_array(_synthetic_audio(), "de", + repetition_penalty=1.2) + assert isinstance(out, str) diff --git a/tests/test_voxtral_transcribe_guards.py b/tests/test_voxtral_transcribe_guards.py new file mode 100644 index 00000000..d41fda50 --- /dev/null +++ b/tests/test_voxtral_transcribe_guards.py @@ -0,0 +1,328 @@ +"""transcribe() end to end with the model and the aligners stubbed out. + +What these guard is plumbing between the pieces rather than any one of them: +what reaches the model as its language, which aligner survives an eviction, +what an empty file or a missing download turns into. Each failed without an +error of its own -- a wrong prompt, memory not coming back, or a message +naming an environment variable instead of the problem. +""" +import gc +import weakref + +import numpy as np +import pytest + +sf = pytest.importorskip("soundfile") + +from noScribe import voxtral_engine as v # noqa: E402 +from noScribe.voxtral_engine import SAMPLE_RATE # noqa: E402 +from test_loop_breaker import ENGLISH, GERMAN # noqa: E402 + + +def _wav(tmp_path, seconds): + p = tmp_path / "t.wav" + sf.write(p, np.zeros(int(seconds * SAMPLE_RATE), dtype="float32"), SAMPLE_RATE) + return str(p) + + +class _Vox: + """The model: canned texts in call order; records the language asked for.""" + + def __init__(self, *texts): + self.texts = list(texts) + self.languages = [] + + def transcribe_array(self, audio, language, max_new_tokens=4096, **kw): + self.languages.append(language) + return self.texts[min(len(self.languages) - 1, len(self.texts) - 1)] + + +class _FakeAligner: + """An aligner that sits on the GPU, so evicting it asks for the memory back.""" + device = "mps" + loaded = [] + + def __init__(self, model): + self.model = model + _FakeAligner.loaded.append(weakref.ref(self)) + + def align_words(self, words, audio, t_offset=0.0, depth=0): + return [{"word": w, "start": t_offset + i * 0.5, + "end": t_offset + i * 0.5 + 0.4, "prob": 0.9} + for i, w in enumerate(words)] + + +@pytest.fixture +def stubbed(monkeypatch, tmp_path): + """transcribe() with a fake model, fake aligners and plenty of RAM; the + repo is a local directory so nothing is looked up on the hub.""" + _FakeAligner.loaded = [] + monkeypatch.setattr(v, "_total_ram_gb", lambda: 64.0) + monkeypatch.setattr(v, "_Aligner", _FakeAligner) + monkeypatch.setattr(v, "_unfetchable", lambda model: False) # no network in tests + repo = tmp_path / "voxtral-mini-8bit" + repo.mkdir() + + def run(vox, seconds, **kw): + monkeypatch.setattr(v, "_Voxtral", lambda r: vox) + return v.transcribe(_wav(tmp_path, seconds), voxtral_repo=str(repo), + chunk_sec=v.HARD_MIN_CHUNK_SEC, **kw) + return run + + +# --------------------------------------------------------------------------- # +# The language the caller asked for +# --------------------------------------------------------------------------- # +@pytest.mark.parametrize("spelled", ["auto", "Multilingual", ""]) +def test_auto_reaches_the_model_as_no_language(stubbed, spelled): + """"auto" was cut to its first two letters like any code and became the + pinned language "au": written into the prompt as lang:au, used as the + target of the translated-pass guard, and it froze the aligner on the + multilingual model instead of following the text.""" + loaded = [] + orig = v._AlignerPool._load + + def spy(self, model, remember=True): + loaded.append(model) + return orig(self, model, remember) + + vox = _Vox(GERMAN, ENGLISH, GERMAN) + with pytest.MonkeyPatch.context() as mp: + mp.setattr(v._AlignerPool, "_load", spy) + stubbed(vox, 130, language=spelled) + assert vox.languages and set(vox.languages) == {None}, vox.languages + assert v.ALIGN_MODELS["de"] in loaded and v.ALIGN_MODELS["en"] in loaded, loaded + + +# --------------------------------------------------------------------------- # +# An evicted aligner really goes +# --------------------------------------------------------------------------- # +def test_an_evicted_aligner_is_not_held_by_the_chunk_loop(stubbed, monkeypatch): + """transcribe() kept the previous chunk's aligner in a local until the next + one replaced it -- and the pool evicts before it loads, so at the moment + of eviction that local was still the last reference. _release_gpu_memory + then freed nothing (its docstring has the measurement: 2.04 GB stayed + resident while a local held the model).""" + alive_at_release = [] + + def release(): + gc.collect() + alive_at_release.append( + sum(1 for r in _FakeAligner.loaded if r() is not None)) + + monkeypatch.setattr(v._AlignerPool, "MAX_CACHED", 1) + monkeypatch.setattr(v._AlignerPool, "_release_gpu_memory", staticmethod(release)) + stubbed(_Vox(GERMAN, ENGLISH, GERMAN), 130, language=None) + assert alive_at_release, "no aligner was ever evicted" + assert alive_at_release == [0] * len(alive_at_release), alive_at_release + + +# --------------------------------------------------------------------------- # +# An empty file +# --------------------------------------------------------------------------- # +def test_an_empty_file_is_an_empty_transcript(monkeypatch, tmp_path): + """Zero samples got as far as the log-Mel features, after the model load, + and died there with "[as_strided] Negative dimensions not allowed". + Whisper returns no segments for such a file; so does this, without loading + anything.""" + def no_load(repo): + raise AssertionError("the model was loaded for an empty file") + + monkeypatch.setattr(v, "_Voxtral", no_load) + segments, info = v.transcribe(_wav(tmp_path, 0), voxtral_repo="voxtral-mini-8bit") + assert segments == [] + assert info["duration"] == 0.0 + + +# --------------------------------------------------------------------------- # +# ram_reserve_gb=0 +# --------------------------------------------------------------------------- # +def test_a_zero_ram_reserve_is_not_the_default(monkeypatch): + """0 is falsy, so an explicit "hold nothing back" silently became the 7 GB + default. The GUI maps its 0 to None before it gets here, so its default is + unaffected; a direct caller asking for 0 gets 0.""" + monkeypatch.setattr(v, "_total_ram_gb", lambda: 24.0) + repo = "voxtral-small-4bit" + default = v._auto_chunk_sec(repo, None, None) + none_held = v._auto_chunk_sec(repo, None, 0) + assert none_held > default + assert none_held == int(v.max_safe_chunk_sec(repo)) + + +# --------------------------------------------------------------------------- # +# A model that is not downloaded, with no internet +# --------------------------------------------------------------------------- # +@pytest.fixture +def hub(monkeypatch): + """The state of the hub as _load_from_hub sees it: whether the model is in + the cache, whether the hub answers, whether downloads are switched off.""" + def set_state(missing=True, reachable=False, offline=False): + monkeypatch.setattr(v, "_needs_download", lambda repo: missing) + monkeypatch.setattr(v, "_hub_reachable", lambda: reachable) + monkeypatch.setattr(v, "_hub_offline", lambda: offline) + return set_state + + +_OFFLINE = ("Cannot find an appropriate cached snapshot folder for the specified " + "revision on the local disk and outgoing traffic has been disabled. To " + "enable repo look-ups and downloads online, set 'HF_HUB_OFFLINE=0' as " + "environment variable.") + + +def test_a_missing_voxtral_model_says_so_offline(monkeypatch, tmp_path, hub): + """The job failed with huggingface_hub's own text, which tells the user to + set an environment variable. What they need to know is that the model has + not been downloaded and that it takes one online run.""" + hub_errors = pytest.importorskip("huggingface_hub.utils") + + def offline(repo): + raise hub_errors.LocalEntryNotFoundError(_OFFLINE) + + hub(missing=True, reachable=False) + monkeypatch.setattr(v, "_total_ram_gb", lambda: 64.0) + monkeypatch.setattr(v, "_Voxtral", offline) + repo = v.VOXTRAL_MODELS["voxtral-mini-8bit"] + with pytest.raises(RuntimeError, match="not downloaded yet") as err: + v.transcribe(_wav(tmp_path, 5), voxtral_repo=repo, need_timestamps=False) + assert repo in str(err.value) and "5.6 GB" in str(err.value) + assert isinstance(err.value.__cause__, hub_errors.LocalEntryNotFoundError) + + +@pytest.mark.parametrize("error", [ + OSError("Can't load feature extractor for 'x'. If you were trying to load it " + "from 'https://huggingface.co/models', make sure you don't have a " + "local directory with the same name."), # transformers, offline + TimeoutError("The read operation timed out"), # a download that stalled +]) +def test_a_missing_model_the_hub_cannot_deliver_says_so(monkeypatch, hub, error): + """Offline, transformers raises a bare OSError for an aligner, with nothing + chained that says why; a download that stalls ends in an error that is no + OSError at all. Whether the model could be fetched is asked of the hub.""" + def failing(model): + raise error + + hub(missing=True, reachable=False) + monkeypatch.setattr(v, "_Aligner", failing) + with pytest.raises(RuntimeError, match="alignment model .* not downloaded yet"): + v._AlignerPool("de", None).aligner_for(GERMAN) + + +def test_a_load_that_fails_while_the_hub_answers_says_so_itself(monkeypatch, hub): + """A stale token (401), an unwritable cache or a full disk: the hub is + there, and "connect once" would be the wrong advice. transformers wraps + the first two into a bare OSError, and hf_xet reports the last without an + errno, so none of them can be told apart by the exception.""" + def failing(model): + raise OSError("There was a specific connection error ... 401 Unauthorized") + + hub(missing=True, reachable=True) + monkeypatch.setattr(v, "_Aligner", failing) + with pytest.raises(OSError, match="401") as err: + v._AlignerPool("de", None).aligner_for(GERMAN) + assert not isinstance(err.value, RuntimeError) + + +def test_a_cached_model_that_fails_to_load_says_so_itself(monkeypatch, hub): + def broken(model): + raise OSError("corrupt weights") + + hub(missing=False, reachable=False) + monkeypatch.setattr(v, "_Aligner", broken) + with pytest.raises(OSError, match="corrupt weights"): + v._AlignerPool("de", None).aligner_for(GERMAN) + + +def test_offline_by_setting_says_so(monkeypatch, hub): + """With HF_HUB_OFFLINE set by the user, connecting would not help.""" + def offline(model): + raise OSError("Can't load feature extractor") + + hub(missing=True, reachable=True, offline=True) + monkeypatch.setattr(v, "_Aligner", offline) + with pytest.raises(RuntimeError, match="HF_HUB_OFFLINE"): + v._AlignerPool("de", None).aligner_for(GERMAN) + + +def test_a_chunk_whose_aligner_cannot_be_fetched_uses_the_one_in_use(monkeypatch): + """Offline on Auto, a chunk that reads as English while only the German + aligner was ever downloaded ended the whole job after its pass had been + decoded. The aligner already loaded aligns it instead, with a warning.""" + monkeypatch.setattr(v, "_Aligner", _FakeAligner) + monkeypatch.setattr(v, "_unfetchable", lambda model: model == v.ALIGN_MODELS["en"]) + logs = [] + pool = v._AlignerPool(None, lambda level, msg: logs.append((level, msg))) + assert pool.aligner_for(GERMAN).model == v.ALIGN_MODELS["de"] + assert pool.aligner_for(ENGLISH).model == v.ALIGN_MODELS["de"] + assert any(level == "warn" and v.ALIGN_MODELS["en"] in msg for level, msg in logs) + + +def test_a_set_language_whose_aligner_cannot_be_fetched_fails_before_decoding( + stubbed, monkeypatch): + """With the language set, its aligner is known before the first pass: the + job used to decode a whole pass and only then fail on the aligner.""" + monkeypatch.setattr(v, "_unfetchable", lambda model: model == v.ALIGN_MODELS["de"]) + vox = _Vox(GERMAN) + with pytest.raises(RuntimeError, match="alignment model .* not downloaded yet"): + stubbed(vox, 70, language="de") + assert vox.languages == [] + + +def test_a_first_download_is_announced(monkeypatch, hub): + """A 6 GB download with nothing in the log looks like a hang -- and one + that cannot happen, offline, is not announced.""" + repo = v.VOXTRAL_MODELS["voxtral-mini-8bit"] + logs = [] + + def load(missing, offline): + logs.clear() + hub(missing=missing, reachable=True, offline=offline) + v._load_from_hub(lambda: "model", "Voxtral model", repo, 5.6, + lambda lvl, msg: logs.append(msg)) + return logs + + assert any("Downloading" in m and "5.6 GB" in m for m in load(missing=True, offline=False)) + assert load(missing=False, offline=False) == [] + assert load(missing=True, offline=True) == [] + + +def test_needs_download_asks_the_cache(monkeypatch, tmp_path): + huggingface_hub = pytest.importorskip("huggingface_hub") + monkeypatch.setattr(huggingface_hub, "try_to_load_from_cache", + lambda repo, filename: None) + assert v._needs_download("some/repo") + monkeypatch.setattr(huggingface_hub, "try_to_load_from_cache", + lambda repo, filename: "/cache/config.json") + assert not v._needs_download("some/repo") + assert not v._needs_download(str(tmp_path)) # a local directory + + +def test_a_download_that_stalls_says_so(monkeypatch, hub): + """When the connection drops during the first download, huggingface_hub + gives up with httpx's own error -- no OSError, so it slipped past the + rewording and the user read "The read operation timed out".""" + httpx = pytest.importorskip("httpx") + + def stalled(model): + raise httpx.ReadTimeout("The read operation timed out") + + hub(missing=True, reachable=False) + monkeypatch.setattr(v, "_Aligner", stalled) + with pytest.raises(RuntimeError, match="not downloaded yet"): + v._AlignerPool("de", None).aligner_for(GERMAN) + + +def test_a_refusal_names_what_the_model_really_needs(monkeypatch): + """The refusal quoted the peak without the headroom it is checked with, so + an 18 GB Mac read "needs about 17 GB ... does not fit in 18 GB".""" + monkeypatch.setattr(v, "_total_ram_gb", lambda: 18.0) + with pytest.raises(MemoryError) as err: + v.max_safe_chunk_sec("voxtral-small-4bit") + assert f"about {v.min_ram_gb('voxtral-small-4bit'):.0f} GB" in str(err.value) + + +def test_a_renamed_model_points_at_one_that_is_offered(): + """A model saved under its old name was silently replaced by the first + Whisper model; the rename table has to lead to a build that exists.""" + m = pytest.importorskip("noScribe.main") + for old, new in m.RENAMED_MODELS.items(): + assert new in v.VOXTRAL_MODELS and old not in v.VOXTRAL_MODELS diff --git a/tests/test_voxtral_worker.py b/tests/test_voxtral_worker.py new file mode 100644 index 00000000..7d67a98b --- /dev/null +++ b/tests/test_voxtral_worker.py @@ -0,0 +1,139 @@ +"""The Voxtral worker's queue contract, as `main.py` consumes it. + +`voxtral_proc_entrypoint` is the spawn target the GUI talks to through one +queue. The contract it must keep (see the worker's docstring): a finished run +ends in `{"type": "result", "ok": True, "info": ...}`; any exception ends in +`ok: False` with the exception's type and message in `error` and the traceback +in `trace`, so the GUI can show one and log the other; `log` and `progress` +puts that fail are swallowed, because losing a log line must not fail a job; +but a `segment` put that fails is deliberately NOT swallowed -- a transcript +silently truncated while the job reports success is worse than a failed job. +""" +import pytest + +from noScribe import voxtral_engine, voxtral_mp_worker + + +class _Queue: + """Records puts; raises on the message types listed in `broken`.""" + + def __init__(self, broken=()): + self.items = [] + self.broken = set(broken) + + def put(self, msg): + if msg.get("type") in self.broken: + raise OSError("queue closed") + self.items.append(msg) + + +def _fake_transcribe(segments=({"start": 0.0, "end": 1.0, "text": "hi", "words": None},)): + def transcribe(**kw): + kw["log_cb"]("info", "hello") + kw["progress_cb"](42) + for seg in segments: + kw["segment_cb"](seg) + return list(segments), {"duration": 1.0, "language": "de"} + return transcribe + + +def test_ok_run_forwards_log_progress_segments_and_info(monkeypatch): + monkeypatch.setattr(voxtral_engine, "transcribe", _fake_transcribe()) + q = _Queue() + voxtral_mp_worker.voxtral_proc_entrypoint({"audio_path": "x.wav"}, q) + assert [m["type"] for m in q.items] == ["log", "progress", "segment", "result"] + assert q.items[0] == {"type": "log", "level": "info", "msg": "hello"} + assert q.items[1] == {"type": "progress", "pct": 42} + assert q.items[2]["segment"]["text"] == "hi" + assert q.items[3] == {"type": "result", "ok": True, + "info": {"duration": 1.0, "language": "de"}} + + +def test_exception_becomes_a_failed_result_with_type_and_trace(monkeypatch): + def boom(**kw): + raise ValueError("no such model") + monkeypatch.setattr(voxtral_engine, "transcribe", boom) + q = _Queue() + voxtral_mp_worker.voxtral_proc_entrypoint({"audio_path": "x.wav"}, q) + assert len(q.items) == 1 + res = q.items[0] + assert res["type"] == "result" and res["ok"] is False + assert res["error"] == "ValueError: no such model" + assert "ValueError: no such model" in res["trace"] + + +def test_broken_log_and_progress_puts_do_not_fail_the_job(monkeypatch): + monkeypatch.setattr(voxtral_engine, "transcribe", _fake_transcribe()) + q = _Queue(broken={"log", "progress"}) + voxtral_mp_worker.voxtral_proc_entrypoint({"audio_path": "x.wav"}, q) + assert [m["type"] for m in q.items] == ["segment", "result"] + assert q.items[-1]["ok"] is True + + +def test_broken_segment_put_fails_the_job_instead_of_truncating(monkeypatch): + monkeypatch.setattr(voxtral_engine, "transcribe", _fake_transcribe()) + q = _Queue(broken={"segment"}) + voxtral_mp_worker.voxtral_proc_entrypoint({"audio_path": "x.wav"}, q) + assert q.items[-1]["type"] == "result" and q.items[-1]["ok"] is False + assert q.items[-1]["error"].startswith("OSError") + + +def test_args_are_passed_through_by_name(monkeypatch): + seen = {} + def transcribe(**kw): + seen.update(kw) + return [], {} + monkeypatch.setattr(voxtral_engine, "transcribe", transcribe) + args = {"audio_path": "a.wav", "language_code": "de", "need_timestamps": False, + "voxtral_repo": "r", "chunk_sec": 120, "corrections_path": "c.yml", + "speaker_names": ["Mona"], "ram_reserve_gb": 4, + "speaker_turns": [[0.0, 1.0, "S1"]]} + voxtral_mp_worker.voxtral_proc_entrypoint(args, _Queue()) + assert seen["audio_path"] == "a.wav" + assert seen["language"] == "de" + assert seen["need_timestamps"] is False + assert seen["voxtral_repo"] == "r" + assert seen["chunk_sec"] == 120 + assert seen["corrections_path"] == "c.yml" + assert seen["speaker_names"] == ["Mona"] + assert seen["ram_reserve_gb"] == 4 + assert seen["speaker_turns"] == [[0.0, 1.0, "S1"]] + + +@pytest.mark.parametrize("inherited", [None, "0", "false"]) +def test_hub_telemetry_is_off_before_the_engine_is_imported(inherited): + """huggingface_hub reads HF_HUB_DISABLE_TELEMETRY once, at import, and the + engine pulls it in (model downloads, the aligner). A clean interpreter in + which importing voxtral_engine reports the value it was imported under, + through the worker's own error path.""" + import os + import subprocess + import sys + import textwrap + from pathlib import Path + probe = textwrap.dedent(""" + import importlib.abc, importlib.machinery, os, queue, sys + + class Report(importlib.abc.MetaPathFinder, importlib.abc.Loader): + def find_spec(self, name, path, target=None): + if name == "noScribe.voxtral_engine": + return importlib.machinery.ModuleSpec(name, self) + def create_module(self, spec): + return None + def exec_module(self, module): + raise RuntimeError("telemetry=%r" % os.environ.get("HF_HUB_DISABLE_TELEMETRY")) + + sys.meta_path.insert(0, Report()) + from noScribe.voxtral_mp_worker import voxtral_proc_entrypoint + q = queue.Queue() + voxtral_proc_entrypoint({}, q) + print(q.get_nowait()["error"]) + """) + env = dict(os.environ) + env.pop("HF_HUB_DISABLE_TELEMETRY", None) + if inherited is not None: + env["HF_HUB_DISABLE_TELEMETRY"] = inherited + proc = subprocess.run([sys.executable, "-c", probe], env=env, capture_output=True, text=True, + cwd=Path(__file__).resolve().parent.parent) + assert proc.returncode == 0, proc.stderr + assert "telemetry='1'" in proc.stdout, proc.stdout diff --git a/tests/test_word_prob_format.py b/tests/test_word_prob_format.py new file mode 100644 index 00000000..19a40edd --- /dev/null +++ b/tests/test_word_prob_format.py @@ -0,0 +1,64 @@ +"""`prob` on a word is a probability, not a log probability. + +`voxtral_engine`'s module docstring promises the segment shape it streams is +compatible with `whisper_mp_worker`'s, and that worker puts faster-whisper's +`word.probability` -- a value in [0, 1] -- into this field. The forced aligner +gets its scores from `noScribe.ctc_align.merge_tokens` (torchaudio's before it), +which returns log probabilities (<= 0, measured -11.96 .. -0.0014), so the two +paths disagreed on what the field meant. + +0.0 has a second job here: it is the sentinel for a word that was spread evenly +or interpolated rather than actually aligned, and the salvage guard tests +exactly that. Converting with exp() keeps the sentinel intact and, as a bonus, +stops a genuine score of exactly 0.0 from colliding with it -- it now maps to +1.0. + +No `importorskip` here on purpose: `voxtral_engine` imports stdlib only at +module level and these tests never load a model, so they run on the Linux CI +too -- which has no `transformers` and would otherwise skip the file that pins +this contract. +""" +import math + +import pytest + +from noScribe.voxtral_engine import _Aligner + + +class _Span: + def __init__(self, token, start, end, score): + self.token, self.start, self.end, self.score = token, start, end, score + + +def _aligner(): + """An _Aligner without loading a model -- only the stamp maths is used. + + Same shape as the stubs in test_forced_align_cap.py and + test_salvage_prefix_alignment.py. + """ + al = object.__new__(_Aligner) + al.blank = 0 + return al + + +def test_log_scores_become_probabilities(): + # Two words, one token each, with log-probabilities the aligner really emits. + spans = [_Span(5, 0, 10, math.log(0.5)), _Span(7, 10, 20, math.log(0.25))] + out = _aligner()._stamps_from_spans(spans, ["a", "b"], [0, 1], fps=100.0, + t_start=0.0, t_end=1.0) + assert out is not None + assert [w["prob"] for w in out] == [pytest.approx(0.5), pytest.approx(0.25)] + + +def test_a_perfect_score_does_not_look_like_the_unaligned_sentinel(): + """log p == 0.0 means certainty; it must not read as "never aligned".""" + spans = [_Span(5, 0, 10, 0.0)] + out = _aligner()._stamps_from_spans(spans, ["a"], [0], fps=100.0, + t_start=0.0, t_end=1.0) + assert out[0]["prob"] == pytest.approx(1.0) + + +def test_spread_words_keep_the_zero_sentinel(): + """_spread marks words it could not align with prob 0.0; that must stay.""" + out = _aligner()._spread(["a", "b"], [0.0] * 1600, 0.0) + assert [w["prob"] for w in out] == [0.0, 0.0] diff --git a/tests/test_worker_import_lightweight.py b/tests/test_worker_import_lightweight.py index 73551267..b703ca55 100644 --- a/tests/test_worker_import_lightweight.py +++ b/tests/test_worker_import_lightweight.py @@ -15,8 +15,9 @@ REPO = str(Path(__file__).resolve().parent.parent) -# Both are ctx.Process targets, so both are re-imported in a spawn child. -WORKER_MODULES = ["noScribe.pyannote_mp_worker", "noScribe.whisper_mp_worker"] +# All three are ctx.Process targets, so all are re-imported in a spawn child. +WORKER_MODULES = ["noScribe.pyannote_mp_worker", "noScribe.whisper_mp_worker", + "noScribe.voxtral_mp_worker"] def _without_comments(text): From 186e364797f2cd702d1ddebc5e4fbfc0edb0a665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=A4mmerer?= Date: Sun, 20 Sep 2026 15:19:50 +0200 Subject: [PATCH 4/5] Add the tools that build other Voxtral bit widths locally and re-derive the loop-detection thresholds from logs quantize_voxtral.py produces the shipped layout (weights quantised, audio encoder and projector left in bf16); calibrate_loop_detection.py recomputes the thresholds the engine's comments cite. Co-Authored-By: Claude Fable 5.1 Co-Authored-By: Claude Opus 5.5 --- tools/calibrate_loop_detection.py | 152 +++++++++++++ tools/quantize_voxtral.py | 346 ++++++++++++++++++++++++++++++ 2 files changed, 498 insertions(+) create mode 100644 tools/calibrate_loop_detection.py create mode 100644 tools/quantize_voxtral.py diff --git a/tools/calibrate_loop_detection.py b/tools/calibrate_loop_detection.py new file mode 100644 index 00000000..0b8ca72d --- /dev/null +++ b/tools/calibrate_loop_detection.py @@ -0,0 +1,152 @@ +"""Re-measure the repetition-loop thresholds against real noScribe transcripts. + +The thresholds in voxtral_engine (DEGENERATE_CYCLE_REPEATS, and the +DEGENERATE_COMPRESSION_RATIO net behind it) are not guesses -- they sit in the +gap between two measured populations: what clean German transcripts do, and +what a pass that collapsed into a repetition loop does. That gap has to be +re-checked whenever a threshold moves or a new model lands, and the only honest +source for it is finished transcripts. + +Those live in noScribe's own log files, which record the transcript as it is +written. They contain real interview material, so nothing here is committed: +the script reads the log directory in place and prints numbers. + + python tools/calibrate_loop_detection.py # default log dir + python tools/calibrate_loop_detection.py --logs PATH # somewhere else + python tools/calibrate_loop_detection.py --list # per-chunk detail + +A threshold is well-placed when "highest clean" and "lowest flagged" stay far +apart. If a clean chunk creeps up towards the threshold, that is a false +positive waiting to happen -- look at the chunk before raising the number, +because a genuine loop of a new shape looks the same from here. +""" +import argparse +import os +import re +import sys +import zlib +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +from noScribe.voxtral_engine import ( # noqa: E402 + DEGENERATE_COMPRESSION_RATIO, + DEGENERATE_CYCLE_REPEATS, + _longest_cycle_repeats, + _looks_degenerate, +) + +CHUNK_MARKER = re.compile(r"Transcribing chunk (\d+)/") +# The GUI writes these into the log around the transcript; they are not model +# output and would otherwise show up as a repeating cycle of their own. +GUI_TIMESTAMP = re.compile(r"\[\d\d:\d\d:\d\d\]") +MIN_WORDS = 200 # shorter blocks are not a representative pass + + +def short(name, width=40): + """Keep the END of a long log name: transcripts of the same series differ + in their suffix ("... day_1" / "... day_2"), so cutting the tail makes two + different files look like one.""" + return name if len(name) <= width else "\u2026" + name[-(width - 1):] + + +def default_log_dir(): + if sys.platform == "darwin": + return Path.home() / "Library/Application Support/noScribe/log" + if os.name == "nt": + return Path(os.environ.get("APPDATA", "")) / "noScribe/log" + return Path.home() / ".config/noScribe/log" + + +def chunks(log_dir): + """Yield (log name, chunk number, transcript text) per transcribed chunk.""" + for f in sorted(Path(log_dir).glob("*.log")): + lines = f.read_text(errors="replace").splitlines() + marks = [(int(m.group(1)), i) for i, ln in enumerate(lines) + if (m := CHUNK_MARKER.search(ln))] + starts = [i for _, i in marks[1:]] + [len(lines)] + for (number, start), end in zip(marks, starts): + text = GUI_TIMESTAMP.sub(" ", "\n".join(lines[start:end])) + if len(text.split()) >= MIN_WORDS: + yield f.name, number, text + + +def cycle_sample(words, max_k=8): + """The words behind the count -- so a flagged chunk can be eyeballed + without opening the log. The count itself always comes from the engine's + own _longest_cycle_repeats, so the two cannot drift apart.""" + best = (0, 0, 0) + for k in range(1, max_k + 1): + run = 0 + for i in range(k, len(words)): + run = run + 1 if words[i] == words[i - k] else 0 + if run > best[0]: + best = (run, k, i) + run, _, end = best + return " ".join(words[end - run:end])[:60] + + +def compression_ratio(text): + raw = text.encode("utf-8") + return len(raw) / max(1, len(zlib.compress(raw))) + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--logs", type=Path, default=default_log_dir(), + help="directory holding noScribe's log files (default: the app's log dir)") + ap.add_argument("--list", action="store_true", help="one line per chunk") + args = ap.parse_args() + + if not args.logs.is_dir(): + sys.exit(f"No log directory at {args.logs} -- pass --logs PATH.") + + clean, flagged = [], [] + for name, number, text in chunks(args.logs): + words = text.split() + repeats = _longest_cycle_repeats(words) + sample = cycle_sample(words) + row = (repeats, compression_ratio(text), len(words), name, number, sample) + (flagged if _looks_degenerate(text) else clean).append(row) + if args.list: + mark = "LOOP" if _looks_degenerate(text) else " " + print(f"{mark} {short(name):40} chunk {number:2} " + f"{len(words):5} words cycle x{repeats:<4} ratio {row[1]:.2f}") + + if not clean and not flagged: + sys.exit(f"No transcribed chunks found in {args.logs}.") + + if args.list: + print() + print(f"chunks measured : {len(clean) + len(flagged)} from {args.logs}") + print(f"thresholds : cycle repeats >= {DEGENERATE_CYCLE_REPEATS}, " + f"compression ratio > {DEGENERATE_COMPRESSION_RATIO}") + print() + + if clean: + top = max(clean) + print(f"clean : {len(clean):3} chunks, highest cycle count {top[0]} " + f"({short(top[3])} chunk {top[4]})") + if flagged: + low = min(flagged) + print(f"flagged : {len(flagged):3} chunks, lowest cycle count {low[0]} " + f"({short(low[3])} chunk {low[4]})") + for repeats, _, _, name, number, sample in sorted(flagged, reverse=True): + print(f" x{repeats:<4} {short(name):40} chunk {number:2} {sample!r}") + + print() + if clean and flagged: + gap = min(f[0] for f in flagged) - max(c[0] for c in clean) + print(f"gap between the populations: {gap} repeats " + f"(threshold {DEGENERATE_CYCLE_REPEATS} sits inside it)" + if gap > 0 else + "POPULATIONS OVERLAP -- a clean chunk cycles as often as a loop; " + "the cycle count alone cannot separate them any more.") + elif not flagged: + print("Nothing flagged. Either the corpus is clean or the threshold is " + "too high -- check the highest clean count above.") + + +if __name__ == "__main__": + main() diff --git a/tools/quantize_voxtral.py b/tools/quantize_voxtral.py new file mode 100644 index 00000000..7409c417 --- /dev/null +++ b/tools/quantize_voxtral.py @@ -0,0 +1,346 @@ +"""Build a quantised Voxtral model for the noScribe Voxtral engine. + +Quantised weights are much faster and need far less memory than the bf16 +release, which in turn allows longer passes. The 3B model at 8 bit is +word-for-word identical to bf16 on our test material at roughly five times the +speed, so it is the recommended build. + +mlx_voxtral ships its own `scripts/quantize_voxtral.py`, but its argparse +restricts --bits to 2/4/8. MLX supports 4/5/6/8 and mlx_lm's quantize_model +accepts any of them, so this is the same pipeline with the restriction lifted. + +Both Voxtral releases are Apache-2.0, so the resulting weights may be shared. + +A Voxtral build has three parts that can carry different precision, and they +behave very differently: + + * the audio encoder (audio_tower + projector, ~0.6B params in BOTH model + sizes) runs ONCE per pass, so its precision barely affects speed; + * lm_head (vocab x hidden, 0.4B in the 3B model / 0.7B in the 24B) runs once + per GENERATED TOKEN, so its precision costs real time; + * the language model body dominates size, and its bit width is the main + quality/memory trade. + +They are therefore separate options rather than one "mode". + +Usage: + python tools/quantize_voxtral.py \ + [bits] [group] [mode] [--lm-head-bits N] [--encoder-bits N] + + group: 32, 64 (default) or 128 -- the only sizes MLX supports, and it must + divide the last dimension of every tensor being quantised. Both + Voxtral releases satisfy all three; a checkpoint that does not is + refused before quantising, because mlx_lm would otherwise skip the + offending tensors silently and leave them dense. + + mode: uniform -- every quantizable tensor at [bits] (default) + mixed -- audio tower, projector and lm_head two bits higher + (only meaningful below 8 bit) + dense-audio -- audio tower, projector and lm_head stay bf16, the + language model is quantised to [bits] + dense-encoder -- only the audio tower and projector stay bf16; + lm_head is quantised too, so the acoustic path is the + only thing that varies + + --lm-head-bits / --encoder-bits override the width the mode would pick for + that part ("dense" = leave in bf16). Use them to vary ONE part at a time: + a build that raises the encoder and lowers lm_head at the same time cannot + tell you which change did what. + +Examples (from the repository root, with the venv active): + # recommended: 3B at 8 bit, ~5 GB, needs ~13 GB RAM to run + python tools/quantize_voxtral.py mistralai/Voxtral-Mini-3B-2507 \ + models/voxtral-mini-8bit 8 + + # 24B at 6 bit, ~20 GB, needs ~28 GB RAM to run (32 GB machine: short passes) + python tools/quantize_voxtral.py mistralai/Voxtral-Small-24B-2507 \ + models/voxtral-small-6bit 6 + + # 24B as shipped (voxtral-small-4bit): 4-bit body and lm_head, bf16 ear + python tools/quantize_voxtral.py mistralai/Voxtral-Small-24B-2507 \ + models/voxtral-small-4bit 4 64 dense-encoder --lm-head-bits 4 + +The source weights are downloaded to the Hugging Face cache on first use +(3B: ~9 GB, 24B: ~48 GB). Conversion itself is quick and memory-light -- +loading and quantising stay lazy, only saving evaluates the graph. +""" +import sys, time, json, shutil +from pathlib import Path + +# MLX and mlx_voxtral are imported *after* the argument checks below, not here. +# They exist only on Apple Silicon, so importing them first turned every +# misspelled argument into a ModuleNotFoundError traceback on Windows and Linux +# -- and made the argument checks untestable anywhere else (they run in CI). + +SUPPORTED_BITS = (4, 5, 6, 8) # MLX affine quantisation +SUPPORTED_GROUPS = (32, 64, 128) # mx.quantize accepts no others +MODES = ("uniform", "mixed", "dense-audio", "dense-encoder") + +# Validate EVERYTHING before touching the network: a typo discovered after the +# multi-GB source download is a typo that cost an hour. +argv, overrides = [], {} +_it = iter(sys.argv[1:]) +for a in _it: + if a in ("--lm-head-bits", "--encoder-bits"): + try: + overrides[a] = next(_it) + except StopIteration: + sys.exit(f"{a} needs a value (a bit width or 'dense')") + elif a in ("-h", "--help"): + print(__doc__.strip()) + sys.exit(0) + elif a.startswith("--"): + sys.exit(f"unknown option {a}") + else: + argv.append(a) + +if len(argv) < 2: + sys.exit(__doc__.split("Usage:")[1].split("Examples")[0].strip()) +src = argv[0] +out = Path(argv[1]) +bits = int(argv[2]) if len(argv) > 2 else 6 +group = int(argv[3]) if len(argv) > 3 else 64 +mode = argv[4] if len(argv) > 4 else "uniform" + + +def _width(flag): + """Parse an override: a supported bit width, or 'dense' (stay bf16).""" + v = overrides.get(flag) + if v is None: + return None + if v.lower() == "dense": + return False # False = do not quantize (bf16) + try: + w = int(v) + except ValueError: + sys.exit(f"{flag} must be a number or 'dense', got {v!r}") + if w not in SUPPORTED_BITS: + sys.exit(f"{flag} must be one of {SUPPORTED_BITS} or 'dense', got {w}") + return w + + +if bits not in SUPPORTED_BITS: + sys.exit(f"bits must be one of {SUPPORTED_BITS}, got {bits}") +if group not in SUPPORTED_GROUPS: + # Checked here, before the download, because MLX rejects everything else + # and the rejection would otherwise arrive an hour into a 48 GB fetch. + sys.exit(f"group must be one of {SUPPORTED_GROUPS}, got {group}") +if mode not in MODES: + sys.exit(f"mode must be one of {MODES}, got {mode!r}") +if mode == "mixed" and bits >= 8 and "--lm-head-bits" not in overrides \ + and "--encoder-bits" not in overrides: + sys.exit("mixed with 8 bits is identical to uniform (the boost caps at 8) " + "-- use uniform, or dense-audio to go beyond 8 bit") +# The mixed boost snaps to the next supported width (5+2=7 is not). +boost_bits = bits + 2 if bits + 2 in SUPPORTED_BITS else 8 +lm_head_bits = _width("--lm-head-bits") +encoder_bits = _width("--encoder-bits") + +if out.exists(): + sys.exit(f"output dir {out} already exists") + +# Only now the Apple-Silicon stack, so that every check above works (and is +# testable) on a machine that does not have it, and reports a missing install +# as a sentence rather than a traceback. +try: + import mlx.core as mx + from mlx_voxtral import load_voxtral_model + from mlx_voxtral.quantization import ( + quantize_model, save_model, save_config, compute_bits_per_weight, + ) +except ImportError as exc: + sys.exit(f"this tool needs the Apple-Silicon Voxtral stack (pip install -r " + f"environments/requirements_voxtral_macOS_arm64.txt): {exc}") +# Build into a scratch dir and rename at the very end, so an interrupted or +# failed run can never leave a half-written build that the model picker +# (which keys on config.json) would offer as usable. +tmp_out = out.parent / (out.name + ".partial") +if tmp_out.exists(): + shutil.rmtree(tmp_out) +tmp_out.mkdir(parents=True) + +def rss_gb(): + import resource + return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024**3 + + +t0 = time.time() +print(f"loading {src} (lazy, bfloat16) ...", flush=True) +model, config = load_voxtral_model(src, dtype=mx.bfloat16, lazy=True) +if "quantization" in config: + sys.exit("source model is already quantized - use the original weights") +print(f" loaded, RSS {rss_gb():.1f} GB, MLX peak {mx.get_peak_memory()/1e9:.1f} GB", flush=True) + +SENSITIVE = ("audio_tower.", "multi_modal_projector.", "lm_head") + + +def _quantizable(path, module): + if "embed_positions" in path or "pos_emb" in path: + return False + return hasattr(module, "to_quantized") + + +def uniform(path, module, *rest): + return _quantizable(path, module) + + +def mixed(path, module, *rest): + """Give the parts that carry the acoustic evidence extra bits: the audio + encoder, the projector that feeds its output to the language model, and + the output layer. + + This is a *simplified* variant of mlx_voxtral's own + voxtral_mixed_quantization_predicate: the library additionally boosts the + MLP projections of the first and last two language-model layers and adapts + group sizes per tensor. That boost was not carried over because the part + that is worth bits is the encoder, and the sweeps that establish it are in + docs/voxtral-quantisation.md. The per-tensor group adaptation was not + carried over for a different reason: it does not work. mlx_lm's + quantize_model filters on the *global* group size before any custom + predicate is consulted, so a + tensor the group does not divide is skipped whatever a predicate returns + for it. The group check before the quantize_model call below covers that + case instead, by refusing. + """ + if not _quantizable(path, module): + return False + if any(x in path for x in SENSITIVE): + return {"group_size": group, "bits": boost_bits} + return {"group_size": group, "bits": bits} + + +def dense_encoder(path, module, *rest): + """Keep only the audio encoder and its projector in bf16; quantise the + whole language model including lm_head to [bits]. + + The isolating variant of dense-audio: it varies precision in the acoustic + path *alone*, so a quality difference cannot be credited to a dense output + layer (which for the 24B model is 1.3 GB on its own). Cheap, because the + encoder is a small fraction of a large model -- the point of the test is + whether a 24B language model is held back by a lossy ear. + """ + if not _quantizable(path, module): + return False + if "audio_tower." in path or "multi_modal_projector." in path: + return False + return {"group_size": group, "bits": bits} + + +def dense_audio(path, module, *rest): + """Quantise the language model, keep the acoustic path in bf16 -- the + audio encoder AND the projector and lm_head (everything in SENSITIVE). + + The only way to go *beyond* 8 bit for these parts, since affine + quantisation cannot express more. + """ + if not _quantizable(path, module): + return False + if any(x in path for x in SENSITIVE): + return False + return {"group_size": group, "bits": bits} + + +_base = {"uniform": uniform, "mixed": mixed, "dense-audio": dense_audio, + "dense-encoder": dense_encoder}[mode] + +ENCODER = ("audio_tower.", "multi_modal_projector.") + + +def predicate(path, module, *rest): + """The mode's choice, with per-part overrides applied last. + + Kept as one wrapper rather than four edited predicates so that "vary one + part, hold the others" is the same operation whatever the base mode is. + """ + result = _base(path, module, *rest) + if not _quantizable(path, module): + return result + if lm_head_bits is not None and "lm_head" in path: + return False if lm_head_bits is False else {"group_size": group, "bits": lm_head_bits} + if encoder_bits is not None and any(x in path for x in ENCODER): + return False if encoder_bits is False else {"group_size": group, "bits": encoder_bits} + return result + + +# Refuse a group size that does not divide every tensor we mean to quantise. +# +# This is not defensive noise, and it cannot be delegated. mlx_lm's +# quantize_model wraps our predicate in one of its own that starts with +# +# if module.weight.shape[-1] % group_size != 0: return False +# +# against the *global* group size -- so a tensor it does not divide is skipped +# silently, before our predicate is ever asked, and stays bf16 with no message. +# The build then looks finished: it loads, it transcribes, it is simply bigger +# and slower than the tables in docs/voxtral-quantisation.md say, and nothing +# names the layer that was left out. mlx_voxtral's own predicate has a fallback +# to group_size 32 for exactly this case; driven through quantize_model it is +# dead code, because the wrapper has already skipped the tensor (verified in +# tests/test_quantize_group_guard.py). Both Voxtral releases divide by all three +# supported group sizes, so this never fires today -- it exists so that a +# checkpoint with a different hidden dimension stops here, loudly, instead of +# producing a quietly mixed build. +skipped = [] +for _path, _module in model.named_modules(): + if not _path or not hasattr(_module, "to_quantized"): + continue + if not predicate(_path, _module): + continue # meant to stay dense; not our problem + _last = _module.weight.shape[-1] + if _last % group: + skipped.append((_path, _last)) +if skipped: + shutil.rmtree(tmp_out) + _lines = "\n".join(f" {p} (last dimension {n})" for p, n in skipped[:10]) + _more = f"\n ... and {len(skipped) - 10} more" if len(skipped) > 10 else "" + _works = [g for g in SUPPORTED_GROUPS if all(n % g == 0 for _, n in skipped)] + sys.exit( + f"group size {group} does not divide {len(skipped)} tensor(s) that this " + f"mode quantises:\n{_lines}{_more}\n" + + (f"Use group {' or '.join(map(str, _works))} instead." + if _works else + "No supported group size divides all of them; this checkpoint needs " + "a per-tensor group size, which mlx_lm's quantize_model cannot " + "express.") + + "\nRefusing rather than letting those tensors be skipped silently.") + +print(f"quantizing to {bits} bit (group size {group}, mode {mode}) ...", flush=True) +qmodel, qconfig = quantize_model( + model, config, group_size=group, bits=bits, quant_predicate=predicate, +) +print(f" quantized, RSS {rss_gb():.1f} GB, MLX peak {mx.get_peak_memory()/1e9:.1f} GB", flush=True) +try: + print(f" average bits per weight: {compute_bits_per_weight(qmodel):.2f}", flush=True) +except Exception: + pass + +print(f"saving to {out} ...", flush=True) +save_model(tmp_out, qmodel, donate_model=True) +save_config(qconfig, tmp_out / "config.json") + +# Tokenizer / processor files the loader needs alongside the weights. For a +# hub id the snapshot is already in the HF cache (load_voxtral_model fetched +# it above), so download_model only resolves the path. +if Path(src).exists(): + srcdir = Path(src) +else: + from mlx_voxtral.utils.model_loading import download_model + srcdir = Path(download_model(src)) +copied = [] +for name in ("tekken.json", "params.json", "preprocessor_config.json", + "tokenizer_config.json", "tokenizer.json", "chat_template.json"): + f = srcdir / name + if f.is_file(): + shutil.copy2(f, tmp_out / name) + copied.append(name) +# Without a tokenizer the build looks complete (config.json exists, so the +# model picker offers it) but fails cryptically at load -- refuse to produce it. +if not any(n in copied for n in ("tekken.json", "tokenizer.json")): + shutil.rmtree(tmp_out) + sys.exit(f"no tokenizer file found in {srcdir} -- refusing to write a " + f"build that cannot be loaded") + +tmp_out.rename(out) +size = sum(p.stat().st_size for p in out.glob("*.safetensors")) / 1e9 +print(f"done in {time.time()-t0:.0f}s | {size:.1f} GB | RSS peak {rss_gb():.1f} GB | " + f"MLX peak {mx.get_peak_memory()/1e9:.1f} GB", flush=True) From 3daca1bf53c2517598541cf91270856d33dd3a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=A4mmerer?= Date: Wed, 23 Sep 2026 15:13:22 +0200 Subject: [PATCH 5/5] Document the Voxtral engine for users, and keep the measurements behind its decisions together with the scripts that made them VOXTRAL.md is the user documentation. docs/ holds what the code comments cannot carry: which build ships and why, the comparison with six other ASR engines, the log-Mel clamp floor, the audio path, the numpy Viterbi, and a conditional brief for moving to mlx-audio should mlx-voxtral ever go quiet. Co-Authored-By: Claude Fable 5.1 Co-Authored-By: Claude Opus 5.5 --- VOXTRAL.md | 249 ++++++++ docs/migration-mlx-audio.md | 340 +++++++++++ docs/other-asr-engines.md | 433 ++++++++++++++ docs/scripts/adjudicate.py | 74 +++ docs/scripts/audio_audit.py | 109 ++++ docs/scripts/audio_filters.py | 86 +++ docs/scripts/audit_long.py | 84 +++ docs/scripts/batch_probe.py | 118 ++++ docs/scripts/bitmatrix.py | 27 + docs/scripts/bootstrap_cer.py | 191 ++++++ docs/scripts/build_reference.py | 118 ++++ docs/scripts/cli_check.py | 69 +++ docs/scripts/decisive.py | 41 ++ docs/scripts/encoder_diff.py | 195 +++++++ docs/scripts/engines/README.md | 75 +++ docs/scripts/engines/cohere_asr_wer.py | 201 +++++++ docs/scripts/engines/nemotron_asr_wer.py | 189 ++++++ docs/scripts/engines/parakeet_wer.py | 131 +++++ docs/scripts/engines/qwen_asr_wer.py | 198 +++++++ docs/scripts/engines/transcribe_cpp_wer.py | 125 ++++ docs/scripts/engines/vibevoice_wer.py | 171 ++++++ docs/scripts/find_passage.py | 102 ++++ docs/scripts/fleurs.py | 89 +++ docs/scripts/fleurs_gain.py | 147 +++++ docs/scripts/fleurs_noise.py | 102 ++++ docs/scripts/fleurs_quiet.py | 178 ++++++ docs/scripts/fleurs_resample.py | 122 ++++ docs/scripts/fleurs_stream.py | 130 +++++ docs/scripts/fleurs_window.py | 101 ++++ docs/scripts/locate_passage.py | 68 +++ docs/scripts/loop410.py | 21 + docs/scripts/mel_outlier_gap.py | 91 +++ docs/scripts/mel_stream.py | 132 +++++ docs/scripts/mixed_vs_uniform.py | 43 ++ docs/scripts/pair_cer.py | 92 +++ docs/scripts/preproc_cer.py | 70 +++ docs/scripts/preproc_mel.py | 71 +++ docs/scripts/preproc_variants.py | 154 +++++ docs/scripts/rep_matrix.py | 39 ++ docs/scripts/split_retry.py | 36 ++ docs/scripts/three_way_diff.py | 57 ++ docs/scripts/viterbi_bench.py | 442 ++++++++++++++ docs/scripts/viterbi_bench2.py | 172 ++++++ docs/scripts/viterbi_bench3.py | 127 ++++ docs/scripts/voxpopuli_floor.py | 191 ++++++ docs/scripts/voxpopuli_sparse.py | 125 ++++ docs/scripts/voxpopuli_spike.py | 149 +++++ docs/scripts/wer.py | 102 ++++ docs/scripts/whisper_spike_streams.py | 103 ++++ docs/scripts/whisper_vs_voxtral.py | 63 ++ docs/viterbi-numpy-brief.md | 555 ++++++++++++++++++ docs/voxtral-audio-preprocessing.md | 273 +++++++++ docs/voxtral-benchmarks.md | 291 ++++++++++ docs/voxtral-mel-clamp-floor.md | 329 +++++++++++ docs/voxtral-quantisation.md | 642 +++++++++++++++++++++ 55 files changed, 8633 insertions(+) create mode 100644 VOXTRAL.md create mode 100644 docs/migration-mlx-audio.md create mode 100644 docs/other-asr-engines.md create mode 100644 docs/scripts/adjudicate.py create mode 100644 docs/scripts/audio_audit.py create mode 100644 docs/scripts/audio_filters.py create mode 100644 docs/scripts/audit_long.py create mode 100644 docs/scripts/batch_probe.py create mode 100644 docs/scripts/bitmatrix.py create mode 100644 docs/scripts/bootstrap_cer.py create mode 100644 docs/scripts/build_reference.py create mode 100644 docs/scripts/cli_check.py create mode 100644 docs/scripts/decisive.py create mode 100644 docs/scripts/encoder_diff.py create mode 100644 docs/scripts/engines/README.md create mode 100644 docs/scripts/engines/cohere_asr_wer.py create mode 100644 docs/scripts/engines/nemotron_asr_wer.py create mode 100644 docs/scripts/engines/parakeet_wer.py create mode 100644 docs/scripts/engines/qwen_asr_wer.py create mode 100644 docs/scripts/engines/transcribe_cpp_wer.py create mode 100644 docs/scripts/engines/vibevoice_wer.py create mode 100644 docs/scripts/find_passage.py create mode 100644 docs/scripts/fleurs.py create mode 100644 docs/scripts/fleurs_gain.py create mode 100644 docs/scripts/fleurs_noise.py create mode 100644 docs/scripts/fleurs_quiet.py create mode 100644 docs/scripts/fleurs_resample.py create mode 100644 docs/scripts/fleurs_stream.py create mode 100644 docs/scripts/fleurs_window.py create mode 100644 docs/scripts/locate_passage.py create mode 100644 docs/scripts/loop410.py create mode 100644 docs/scripts/mel_outlier_gap.py create mode 100644 docs/scripts/mel_stream.py create mode 100644 docs/scripts/mixed_vs_uniform.py create mode 100644 docs/scripts/pair_cer.py create mode 100644 docs/scripts/preproc_cer.py create mode 100644 docs/scripts/preproc_mel.py create mode 100644 docs/scripts/preproc_variants.py create mode 100644 docs/scripts/rep_matrix.py create mode 100644 docs/scripts/split_retry.py create mode 100644 docs/scripts/three_way_diff.py create mode 100644 docs/scripts/viterbi_bench.py create mode 100644 docs/scripts/viterbi_bench2.py create mode 100644 docs/scripts/viterbi_bench3.py create mode 100644 docs/scripts/voxpopuli_floor.py create mode 100644 docs/scripts/voxpopuli_sparse.py create mode 100644 docs/scripts/voxpopuli_spike.py create mode 100644 docs/scripts/wer.py create mode 100644 docs/scripts/whisper_spike_streams.py create mode 100644 docs/scripts/whisper_vs_voxtral.py create mode 100644 docs/viterbi-numpy-brief.md create mode 100644 docs/voxtral-audio-preprocessing.md create mode 100644 docs/voxtral-benchmarks.md create mode 100644 docs/voxtral-mel-clamp-floor.md create mode 100644 docs/voxtral-quantisation.md diff --git a/VOXTRAL.md b/VOXTRAL.md new file mode 100644 index 00000000..199b3713 --- /dev/null +++ b/VOXTRAL.md @@ -0,0 +1,249 @@ +# Voxtral transcription engine (Apple Silicon) + +An optional alternative to faster-whisper, using Mistral's **Voxtral** models via +[`mlx-voxtral`](https://pypi.org/project/mlx-voxtral/) on the Apple Silicon GPU. + +On German / Swiss-German interview and podcast audio it is, in our tests, +**more accurate and more readable than Whisper** — it gets technical terms right +where Whisper mis-hears them (e.g. *Wortfindungsstörungen*, not +*Gottfindungsstörungen*), spells consistently, and produces fluent, readable +sentences instead of literal disfluent strings. With the 3B model it runs +several times **faster than realtime**; the 24B build is slower than the +recording (see the table below). Left to itself it falls into repetition loops +on hard audio more often than Whisper does, which is why the engine detects a +loop and repairs the pass instead of shipping the damage. + +## Install (macOS, Apple Silicon) + +```bash +pip install -r environments/requirements_voxtral_macOS_arm64.txt +``` + +The models `voxtral-mini-8bit` and `voxtral-small-4bit` then appear in the +model dropdown, each with the RAM it needs. They are downloaded on first use. + +### A note on the pinned dependencies + +`mlx`, `mlx-lm` and `mlx-voxtral` are pinned to exact versions. +[`mlx-voxtral`](https://github.com/mzbac/mlx.voxtral) went untouched between +2025-08 and 2026-08, so the pin started life as a deliberate freeze; the author +has since relicensed it to plain MIT and merged all four fixes this engine had +reported, so it is a normal version constraint again. `mlx-lm` is pinned because +decoding drives `generate_step` directly. `tests/test_voxtral_pin.py` checks that +the installed versions are the pinned ones. + +The pin is `0.0.6`. One of those fixes moves this engine's output — the log-Mel +is now computed over the whole audio rather than per 30-second chunk, changing +the encoder input on most frames — and it was measured over 92 minutes of paired +audio to cost nothing at the transcript (dWER +0.02 [−0.19, +0.24]); see +[docs/voxtral-mel-clamp-floor.md](docs/voxtral-mel-clamp-floor.md), section 8. + +The engine also swaps the processor's feature extractor for its own +(`_PercentileFloorFeatures`): the library clamps the log-Mel at `log_max − 8` +with the maximum taken over the whole input, so one loud transient — a door +slam — raises the floor for the entire pass and costs a measured +1.52 WER +points at 28 dB; the engine takes the floor from a percentile of the +spectrogram instead, which removes that and costs nothing on clean material. +The spectrogram itself is the library's (built from its own window, STFT and +filter bank), and `tests/test_mel_floor.py` pins it bit for bit to the +library's path at percentile 100. The same document has the measurements. + +The coupling to `mlx-voxtral` is small and listed, so the pin is a known +liability with a measured exit route: five library calls (`load_voxtral_model`, +`VoxtralProcessor`, `apply_transcrition_request`, `model.generate` for the +repetition-penalty rung, `proc.decode`), the four audio primitives behind the +feature extractor, three `mlx-lm` imports for the decode loop, and a handful +of attribute reaches. Everything else in `voxtral_engine.py` — chunking, loop +detection, the temperature ladder, forced alignment, prefix salvage — is this +project's own and does not depend on which package loads the weights. If the +pin ever breaks against a newer MLX, the replacement is +[`mlx-audio`](https://github.com/Blaizzy/mlx-audio); +[docs/migration-mlx-audio.md](docs/migration-mlx-audio.md) records what was +verified about it (it does not load the published quantised builds, and fails +silently doing so; it carried the same stop-token defect this engine already +works around, fixed by this project's pull request on 2026-09-02 and not in a +release as of 0.5.1) and the steps and acceptance criteria for the move. + +A frozen (PyInstaller) build does not bundle the MLX stack, so a packaged app +shows no Voxtral models; `is_available()` checks for the packages and the +feature stays inert without them. Voxtral is for running from source on an +Apple Silicon Mac. + +## Language & word-timestamp quality + +Word timestamps come from a CTC forced aligner: a wav2vec2 model produces +per-frame emissions (transformers, on the GPU), and a numpy Viterbi in +`noScribe/ctc_align.py` turns them into word boundaries — torchaudio's +`forced_align` kernel is no longer used, see +[docs/viterbi-numpy-brief.md](docs/viterbi-numpy-brief.md) for why. The +aligner model is chosen **per chunk from the transcribed text itself**: when one language dominates the +chunk (function-word analysis; non-Latin scripts are recognised directly), +the char-native model for that language is used -- so "Auto" gets the same +alignment quality as an explicit language choice. Mixed speech with a clear +majority language (e.g. German with English phrases) uses the majority +model, which also anchors the minority-language words; only text without a +dominant language falls back to the romanised multilingual aligner +(MMS-300M, 1130 languages). If an explicitly selected language contradicts +what the transcript looks like, a warning is logged. + +## Which model + +Two builds, both quantised on Apple Silicon and published so they download on +first use. They appear in the model menu only on Apple Silicon Macs — MLX cannot +run anywhere else — and Whisper (`precise`) stays the default because it runs on +every platform. The menu shows what each build needs, because picking one that +does not fit does not fail loudly: the machine starts swapping and the run stops +making progress. Builds that cannot fit are refused before a run starts. + +| Build | Size | Needs | Notes | +|---|---:|---:|---| +| `voxtral-mini-8bit` (3B) | 6 GB | ~13 GB | **recommended** — runs on a 16 GB Mac, faster than realtime, reproduces the bf16 transcript at ~4.5× the speed | +| `voxtral-small-4bit` (24B) | 15 GB | ~23 GB | for clean, read-aloud audio and material heavy with names; ~2× realtime, runs on a 32 GB Mac | + +**Which of the two?** It depends on the recording. On clean, read-aloud speech +the 24B model is clearly better (FLEURS German 2.8 % against 4.9 % word error). +On conversation — four hand-corrected passages of interview, podcast and video +call — Mini was as good or better on every one, and the 24B model looped where +Mini did not. For interviews and podcasts, pick Mini: it is also four times +faster and runs on a 16 GB Mac. The measurements, including the comparison +against Whisper, are in +[docs/voxtral-quantisation.md](docs/voxtral-quantisation.md). + +Both builds keep the **audio encoder in bf16** and quantise the language model +and `lm_head` — Mini to 8 bit, Small to 4. The encoder runs once per pass, so its +precision costs no speed, but compressing it measurably costs accuracy on +difficult audio. `lm_head` runs once per generated token and is left quantised +for that reason. Small ships its body at 4 bit so that it fits in 32 GB; a 6-bit body measured no +better, and an 8-bit one (~34 GB) was not measured. + +They download on first use from Hugging Face +([mini](https://huggingface.co/MarkusKaemmerer/Voxtral-Mini-3B-2507-8bit-dense-encoder), +[small](https://huggingface.co/MarkusKaemmerer/Voxtral-Small-24B-2507-4bit-dense-encoder)); +the weights are Apache-2.0. Other bit widths (4/5/6-bit, or a bf16 encoder on a +lower-bit body) can be made locally in a few seconds with +`tools/quantize_voxtral.py` — see the script's header and +[docs/voxtral-quantisation.md](docs/voxtral-quantisation.md). + +## How it works + +Voxtral produces clean text but no timestamps, so noScribe uses two paths: + +- **Fast path** – plain text only. Used only for `.txt` output without + speakers, timestamps or pauses — ideal for the "just give me a clean + transcript" case (e.g. course summaries). Its segment times are + approximations, which is why formats that embed audio-sync anchors never + use it. +- **Long path** – word timestamps are recovered by CTC forced alignment + against a language-matched wav2vec2 model (the WhisperX approach) and + grouped into subtitle-sized cues. Used automatically for `.html` (its + anchors drive the editor's click-to-play audio sync), `.vtt` subtitles, + visible timestamps, speaker detection and pause marking. + +Note: Voxtral has no prompt/hotword hook, so the "Disfluencies" option cannot +steer it (a log line says so). Voxtral Mini naturally smooths most fillers; +Voxtral Small stays closer to the exact wording. + +## Long audio & memory + +Voxtral transcribes each pass in a single `generate()` call whose peak memory +grows roughly linearly with the pass length (flash attention + a KV cache, *not* +O(T²)). Files up to one pass long therefore go through in one piece; longer ones +are split. + +A pass is capped at **10 minutes**, whatever the machine could hold. That is the +longest Voxtral has been *measured* on: the widely quoted 30/40 minutes is a +capacity calculation (12.5 Hz frame rate against a 32k context), while the +paper's own long-form ASR protocol segments one-hour earnings calls "into +shorter, 10 minute variants" ([arXiv:2507.13264](https://arxiv.org/abs/2507.13264)). +Past that this project has recorded two distinct failures — whole passes coming +back translated, and passes returning without their opening — so the cap is a +refusal to run the model twice as far out as anyone has measured it, not a fix +for either (both are near-ties that a slightly shorter window would not dodge). + +Below the cap the per-pass length is chosen from installed RAM so the estimated +generate peak stays within physical memory (compute on swapped-out MLX buffers +would thrash and never finish). The one-off model-load spike is allowed to swap +— it frees before transcription starts. The sizing, a little above the measured +peaks: mini ≈ 6.5 GB + ~0.4 GB/min, small ≈ 16.5 GB + ~0.4 GB/min. Rough +per-pass lengths: + +| RAM | mini-8bit | small-4bit | +|----:|:---------:|:----------:| +| 16 GB | ~7 min | won't run (refused) | +| 24 GB | 10 min | ~1 min | +| 32 GB+ | 10 min | 10 min | + +When a file is longer than one pass it is split into **equal, pause-aligned +passes**: each cut is snapped to a real speaker pause found in a wide window +(searching backward, since a shorter pass is always memory-safe), and a short +lead-in overlap is carried across the seam and de-duplicated by timestamp — so a +pass never splits a word and boundaries are effectively lossless. + +The first pass is additionally checked for a dropped opening: a window +occasionally returns without its first seconds of speech, silently, so a short +head of the same audio is decoded and whatever is missing is spliced back. Later +passes are not checked: on the long path they carry a lead-in overlap that the +previous pass already transcribed, and probing every pass was measured at ~10 % +of each decode. (The fast path has no overlap, so there a later pass is simply +unguarded.) When the check finds something, the log says so. + +This is not rare enough to skip: on a raw Zoom recording, 5 of 64 windows cut at +300 s and 600 s came back missing their opening, once losing 18 words of fluent +speech. It depends on the recording — read-aloud benchmark audio never shows it. +Measurements in [docs/voxtral-benchmarks.md](docs/voxtral-benchmarks.md), §5. + +To pin the length yourself, set `voxtral_chunk_sec:` (seconds) in `config.yml` +(`0` = automatic). If other apps need RAM, raise `voxtral_ram_reserve_gb:` +instead — that is the knob the automatic length is computed against. Raising it past 10 minutes +is refused — see the cap above. + +## Correcting brand / product / programme names + +Voxtral has no hotword support, so it mis-hears proper names. Maintain a simple +find/replace list at: + +``` +/voxtral_corrections.yml +``` + +(macOS: `~/Library/Application Support/noScribe/voxtral_corrections.yml`) + +Speaker names entered in the app are corrected in the same step, but only +where the model wrote a name as it sounds and spelled it another way ("Mohna" +or "Mona", "Steffy" or "Steffi"). A word that sounds different ("Muna") may be +someone else and is left alone — put it in the list above if it is not — and +so is any word the macOS dictionary of the transcript's language knows: "Mohn" +next to a speaker called Mon, or a real name spelled differently ("Marcus" next +to "Markus"), which may be someone else. Where the system has no dictionary for +the language, or one that accepts every word, names are left as the model wrote +them. + +```yaml +- to: VitaFlor + from: [vitaflor, "vita flor", "flor-öl", "flor-öle"] +- to: Sonvita + from: [sonvida, sonvieda] +``` + +Matches are whole-word and case-insensitive. The file is created empty (with +commented examples) on first use — add your recurring brand, product and +programme names (e.g. from earlier podcast transcripts). + +Why this and not a prompt: the transcription request has exactly one text slot +(`lang:xx`), and putting terms there was measured to act as a decode +perturbation rather than a vocabulary hint — on three clips it fixed one term, +ignored another and broke a third that the plain request had got right. +Voxtral's chat mode *does* use a term list, but it is not a verbatim +transcriber: one of four clips came back as a 12-word answer instead of an +84-word transcript, and another wrote the instruction into the text. A term +Voxtral does not know is therefore fixed after the fact, here. + +## Author + +The Voxtral integration for noScribe (engine, forced alignment, quantised +model builds) was created by **[Markus Kämmerer](https://markus-kaemmerer.de)** +· [Instagram @markuskaemmerer](https://www.instagram.com/markuskaemmerer/). + +It was written with [Claude Code](https://claude.com/claude-code): the design +decisions, the measurements and what to make of them are the author's; Claude +wrote and reviewed much of the code and the write-ups under that direction. diff --git a/docs/migration-mlx-audio.md b/docs/migration-mlx-audio.md new file mode 100644 index 00000000..5565cb2f --- /dev/null +++ b/docs/migration-mlx-audio.md @@ -0,0 +1,340 @@ +# Brief: move the Voxtral engine from mlx-voxtral to mlx-audio + +This is a conditional work order, not a plan of record: nothing here is +scheduled, and it only becomes relevant if one of the triggers below fires. + +Facts re-verified 2026-08-22 against `mlx-voxtral` 0.0.6, `mlx-audio` 0.5.0 and +`mlx` 0.32.1, the two upstream reports re-checked 2026-09-02 against `mlx-audio` +0.5.1 in a throwaway venv, and the whole brief audited 2026-09-03 against +`mlx-audio` main (`b809500`, the merge of #901) — that audit added the log-Mel +floor, which the brief had missed, and reversed the `mx.metal` argument. Each +fact states how it was checked, so you can re-check rather than trust. + +## When this becomes relevant + +Not now. `mlx-voxtral` is MIT, actively maintained again, and carries every fix +this project reported — which is the point: it shipped under a "Personal Use +License" (MIT plus a ban on commercial use) that noScribe's GPL-3.0 could not +carry, and that licence, not the code, was what once made this migration urgent. +The author relicensed within a day of being asked. Do the migration only if one of these happens: + +* **mlx-voxtral goes quiet again** and a defect turns up that nobody upstream will + fix. +* **An MLX or mlx-lm bump breaks it.** The exposure is `mlx-lm`, but not for the + reason this brief used to give. `mlx-lm` has had no release since 0.31.3 + (2026-04-22) although `main` is active (checked 2026-09-03); the engine drives + decoding through its `generate_step`, `KVCache` and `make_sampler`, so a + release that changes those, or a stale release that stops installing against + a newer `mlx`, breaks the decode loop. mlx-audio dropped `mlx-lm` as a core + dependency on 2026-08-09 and vendors a snapshot of 0.31.3 under + `mlx_audio/lm`, so the move would make the engine independent of `mlx-lm` + releases. That is the argument; the `mx.metal` one does not hold — see fact 4. +* **A second engine is worth shipping.** `mlx-audio` carries Voxtral alongside a + dozen other ASR models behind one API, so the move pays for itself in one go + rather than one engine at a time. What those models are worth is measured in + `docs/other-asr-engines.md`; what its aligner models are worth, in + `docs/viterbi-numpy-brief.md`. + +If none of those is true, close the task. + +## Scope + +In: `noScribe/voxtral_engine.py`, `tools/quantize_voxtral.py`, +`environments/requirements_voxtral_macOS_arm64.txt`, the affected tests, +re-publishing the two quantised builds, and **the log-Mel percentile floor** +(`_PercentileFloorFeatures`, `docs/voxtral-mel-clamp-floor.md`) — it is built on +the library's STFT helpers and has to be rebuilt on whatever computes the +features afterwards. Step 3 says how. + +Out: chunking, loop detection, the temperature ladder, forced alignment, prefix +salvage. None of that touches the library. **Do not refactor them while you are in +there.** + +## The coupling is six calls + +Verified with `grep -rn "mlx_voxtral\|mlx_lm" noScribe/ tools/ tests/`. Line numbers +drift — grep rather than trust them: + +| where | what | +|---|---| +| `_Voxtral.__init__` | `load_voxtral_model`, `VoxtralProcessor` | +| `_Voxtral.__init__` | `proc.feature_extractor = _PercentileFloorFeatures()` — swaps the log-Mel path | +| `_PercentileFloorFeatures.__call__` | `mlx_voxtral.audio_processing`: `stft_mlx`, `get_mel_filters`, `hanning`, `pad_to_multiple` and the frame constants | +| `_Voxtral.transcribe_array` | `proc.apply_transcrition_request(...)` (note the upstream typo) | +| `_Voxtral.transcribe_array` | `model.generate(...)` — the non-greedy fallback only | +| `_Voxtral.transcribe_array` | `proc.decode(...)` | +| `tools/quantize_voxtral.py` | `mlx_voxtral.quantization`, `utils.model_loading.download_model` | + +Plus `_merged_embeddings` and `_LMAdapter`, both of which likely become unnecessary +— see below. + +The floor rows were missing until 2026-09-03: the brief was re-verified on +2026-08-22 and the floor landed on 2026-08-23. It is the one piece of the engine +that reaches *into* the library's signal path rather than calling its API, and +it is why "the same transcript through old and new" (step 5) is only a valid +check once the floor sits on both sides. Under mlx-audio the features do not +come from an MLX STFT at all: its `generate()` takes `input_features` from +transformers' `VoxtralProcessor`, computed in torch on the CPU with the plain +maximum floor. Two ways to keep ours, pick one in step 3: subclass or wrap +transformers' extractor, or port the window, STFT and filter bank to numpy and +hand `input_features` straight to `Model.stream_generate`, which accepts them. +Either way `tests/test_mel_floor.py` changes its reference: it pins bit-identity +at pct=100 against mlx-voxtral's path today, and would pin it against the +transformers extractor instead. That the two are interchangeable was measured +on 2026-08-22, when mlx-voxtral 0.0.6 moved to the whole-file log-Mel: on 300 s +of podcast material its features differ from transformers' by at most 0.00017 +(against 1.30 between 0.0.5 and 0.0.6). Recorded here because no other +document carries it. + +## Verified facts you must not re-derive + +**1. mlx-audio will not load the published builds, and it says nothing.** + +Loading `models/voxtral-mini-8bit` through `mlx_audio.stt.utils.load_model` yields +**0 quantised modules and 405 dense `Linear`**, 211 of them in the language model, +with no exception. Mechanism, all in `mlx_audio/utils.py`: + +* `apply_quantization`'s `get_class_predicate` looks up `p in quantization` and + otherwise falls back to `f"{p}.scales" in weights`. +* Our config and weights use `language_model.layers.0…`; mlx-audio's module tree is + `language_model.model.layers.0…` (its `LanguageModel.__init__` does + `self.model = LlamaModel(config)`). Neither lookup matches. +* `sanitize()` only transposes conv weights — it does not remap the prefix. +* `load_model(..., strict=False)` is the **default**, so the mismatched keys are + skipped silently and the language model stays at its initial values. + +The audio tower does match and does load, which is why a naive smoke test looks +half-plausible. Reported as +[Blaizzy/mlx-audio#902](https://github.com/Blaizzy/mlx-audio/issues/902). **Re-tested +on 0.5.1 (2026-09-02): reproduces unchanged and still silently.** 0 quantised +modules, 211 dense `Linear` in the language model, and `q_proj.weight` comes back +float32 with std 0.0104 — random initialisation, not our 8-bit weights. Nothing on +stdout; the only thing on stderr is an unrelated transformers tokenizer notice. +Issue still open and unanswered. Check again before relying on a warning. + +**2. Re-quantising is format-only. It cannot change quality or speed.** + +MLX's affine quantisation is data-free — scale and bias come from each group's own +min and max, no calibration set, no randomness. Verified: `mx.quantize` on the same +tensor twice returns bit-identical results, and so does a fresh copy of it. +mlx-audio's predicate is `not p.startswith("audio_tower")`, which selects exactly +the set the current builds carry: **213 modules, all 8 bit, group size 64, affine, +encoder dense**. Same weights in, same tensors out; only the keys change. + +So the measurement tables in `docs/voxtral-quantisation.md` still describe a +re-quantised build. **Do not re-run the bit sweeps.** + +**3. What could change output, and therefore must be checked:** + +* mlx-audio's `_merge_input_embeddings` scatters without promoting dtype, where + `_merged_embeddings` promotes deliberately — and on the current build that + promotion is needed. Measured on `voxtral-mini-8bit`: `embed_tokens` returns + bfloat16, but the projector returns float32, because the log-Mel features are + float32 (transformers' processor, which mlx-audio uses, returns them as float32 + too, and mlx-audio does not cast them) and MLX promotes the bf16 weights' + output to the input's dtype. Scattering float32 into the bf16 array rounds + every audio embedding to bf16 (1.0001 → 1.0). Keep the promotion in any + migration; the failure is invisible in the text and shows up only as different + logits. +* **Stop tokens: fixed upstream, keep resolving them anyway.** mlx-audio carried + `_VOXTRAL_EOS_TOKEN_IDS = [2, 4, 32000]`, where 32000 is not a pad token but the + ordinary text token `" Capital"`, so a transcript containing that word was + truncated silently — clean prose that merely ended early. Our + [Blaizzy/mlx-audio#901](https://github.com/Blaizzy/mlx-audio/pull/901) fixed it to + `[2, 4, 11]`, merged 2026-09-02 and **first released in 0.5.2** (2026-09-07; + 0.5.1 of 2026-08-31 still carries 32000), so the `mlx-audio>=` floor is 0.5.2. **Do not rely on the library default even then**: + resolve the ids from the processor, as `_resolve_stop_tokens` does, and pass them + explicitly on both decode paths. +* mlx-audio vendors its own `generate_step` (`mlx_audio.lm.generate`) rather than + using `mlx_lm`'s. Same chunked prefill (`prefill_step_size=2048`), so the ~18 % + peak saving survives — but `MEM_MODEL` is calibrated against the current path and + **must be re-measured**. +* **`AutoProcessor` and `AutoTokenizer` resolve different tokenizer backends for + this build.** On transformers 5.16.1, `AutoTokenizer.from_pretrained` picks + `MistralCommonBackend` and reads `tekken.json`; loading the same directory through + mlx-audio's `AutoProcessor` took the fast-tokenizer path instead + (`tokenization_auto.py` swaps the class when `_use_mistral_format` is false), which + announced itself only as a regex warning. A different backend means differently + encoded prompts, and this build ships no `tokenizer.json` for a fast backend to + read. **Assert the tokenizer class after loading**, the same way step 5 asserts the + quantised-module count. (The regex warning itself is a false positive — see the + comment above `transformers>=5` in + `environments/requirements_voxtral_macOS_arm64.txt`.) +* **The penalty rung has no parameter to land on.** mlx-audio's Voxtral + `generate()` exposes temperature, top-p, top-k and min-p but no + `repetition_penalty`, so the fallback `model.generate(repetition_penalty=...)` + call has no counterpart. The vendored loop takes `logits_processors`, and + `mlx_audio.lm.sample_utils.make_logits_processors(repetition_penalty=..., + repetition_context_size=...)` builds the same processor `mlx_lm` does — drive + the rung through `stream_generate`/`generate_step` with that, and pass the + stop tokens yourself, because `stream_generate` only breaks on + `tokenizer.eos_token_ids`. +* **Its `generate()` defaults are a smoke-test trap, not an engine concern:** + `max_tokens=128` and `language="en"`. A quick "does it transcribe" call with + the defaults truncates at 128 tokens and prompts for English. The engine + never goes through `generate()`; it will drive `stream_generate` directly. +* Voxtral is one of three STT models in mlx-audio that use `AutoProcessor` + (`mms` and `qwen2_audio` are the others, as of main 2026-09-03), and for + Voxtral it resolves to transformers' `VoxtralProcessor`, which **requires + torch — which `pip install mlx-audio` does not install.** Confirmed on 0.5.1: a clean install + loads the module fine and then dies in `post_load_hook` with + `ImportError: VoxtralProcessor requires the PyTorch library`. So torch is an + undeclared dependency of this path and must go in the requirements explicitly. + noScribe has torch for the diarizer and the aligner, so this is not a blocker, but + it changes + the worker's import graph — re-check `tests/test_worker_import_lightweight.py` + and the PyInstaller specs. mlx-audio also hard-requires `sounddevice` and + `miniaudio` (native, PortAudio); the STT import path does not pull them, but pip + installs them, so the frozen build likely needs excludes. **Prove that with a + throwaway PyInstaller build — never infer frozen behaviour from source.** + +**4. The move does not reduce the `mx.metal` exposure; it enlarges it.** + +Measured on `mlx` 0.32.1 with warnings forced on: `mx.metal.is_available()` +emits nothing and has no documented replacement; what is deprecated ("will be +removed in a future version") is `mx.metal.device_info()` and the +`mx.metal.get_*_memory()` family, replaced by the same names on `mx`. `mlx-lm` +0.31.3 calls `mx.metal.is_available()` at nine sites and none of the deprecated +ones — its `wired_limit` already reads `mx.device_info()`. mlx-audio calls +`is_available()` at seven sites and `mx.metal.device_info()` at two, one of +them `mlx_audio/stt/utils.py:wired_limit`, the context manager Voxtral's +`stream_generate` runs inside. So whichever package the engine sits on, a +removal of `mx.metal.is_available` breaks it, and only mlx-audio also breaks +on the removal that is actually announced. Do not cite `mx.metal` as a reason +to move. `mlx` itself is at 0.32.2 (2026-08-25); the pin stays at 0.32.1 until +bumped deliberately. + +## The alignment path is not affected — but know this before you touch it + +Out of scope here, and stated so you do not go looking: **the German forced-aligner +model already loads through mlx-audio**, via `mms/mms.py`, which wraps the same +`Wav2Vec2Model` encoder and adds the `lm_head` a `Wav2Vec2ForCTC` checkpoint +carries. It needs no code change, only `model_type: "mms"` in a converted config, +and it reproduces the torch emissions exactly — identical argmax on every frame, +max |Δ| 0.00068. + +That is not a reason to bundle it in. It is independent of which package loads +Voxtral, the Viterbi half is numpy since 2026-08-23 (`noScribe/ctc_align.py`, which +mlx-audio neither has nor needs to provide), and the aligner already runs on the GPU +where the speed was. A migration that also rewrites alignment cannot be shown to have +changed nothing. + +## Do this first: try to avoid re-publishing at all + +Before re-quantising 6 GB and 25 GB and making every user re-download, evaluate a +key remap in mlx-audio's Voxtral `sanitize()` that accepts the mlx-voxtral layout +(`language_model.X` → `language_model.model.X`). If that works it is a small +upstream contribution, it fixes the same problem for every other published +mlx-voxtral build, and it removes the largest single cost from this migration. It +is already offered in #902. + +Send it as a PR to mlx-audio and see. If it is rejected or takes too long, fall +back to re-quantising. + +Checked 2026-09-03: nobody else has sent that PR, #902 has had no reply beyond +a tracker bot, and there is no ready-made build to fall back on either — +`mlx-community` publishes only a bf16 Voxtral-Mini for mlx-audio, and the 8-bit +Mini and Small builds other people uploaded are in the mlx-voxtral layout, so +they load exactly as badly as ours. The remap is the only route that avoids +re-publishing. + +## Steps + +1. Install `mlx-audio[stt]` **in a throwaway venv first** and confirm the facts + above still hold against whatever version is current. Do not touch the project + venv until the approach is settled — verify with `pip freeze` before and after + that it comes back byte-identical. +2. Try the `sanitize()` remap route. If it works, the build story is solved. +3. Rewrite the six call sites. Expect `_LMAdapter` to become unnecessary — + mlx-audio's `Model.__call__(input_ids, input_features, cache)` already returns + logits — and `_merged_embeddings` likewise, since its merge is already a single + scatter. Delete them only after the equality check in step 5 passes. **Rebuild + the percentile floor on the new feature path** (the two options are under + *The coupling is six calls*) and repoint `tests/test_mel_floor.py`'s + bit-identity reference at the transformers extractor. Do this before step 5: + without it the equality check measures the floor, not the migration. +4. If re-quantising is needed: `python -m mlx_audio.convert -q --q-bits 8` already + carries what `tools/quantize_voxtral.py` exists for — the group-size guard + (`weight.shape[-1] % 64`) and the `not audio_tower` predicate are built into + its `build_quant_predicate` — so try it before rewriting the tool, and keep + only what it cannot do. Rebuild `voxtral-mini-8bit` and `voxtral-small-4bit`, + re-publish, update the model URLs. +5. **Equality check before anything else is believed:** the same audio through the + old and new paths must produce the same transcript, **with the percentile + floor active on both sides**. `tests/test_voxtral_smoke.py` + asserts fast == library today; extend it to assert **the number of quantised + modules is non-zero**, because the failure mode in fact 1 is silent. +6. Re-measure `MEM_MODEL` (see `docs/voxtral-quantisation.md`, *Decode path and + memory*) and update the entries. +7. Re-run the two hand-corrected references and FLEURS with + `docs/scripts/wer.py` and `docs/scripts/fleurs.py`. Expect the numbers to match + the tables. **If they do not, something in fact 3 is biting — find it, do not + update the tables.** +8. Update the dependency notes in `VOXTRAL.md` and + `environments/requirements_voxtral_macOS_arm64.txt`, and the *Re-quantising + with another tool changes nothing* section of `docs/voxtral-quantisation.md` + if the build tables move. + +## Acceptance + +* Transcript identical to the current engine on the same audio, or the difference + explained and measured. +* `venv/bin/python3 -m pytest tests/ -q` green, including the new + quantised-module-count assertion. +* WER/CER on both references and FLEURS within noise of the published tables. +* A throwaway PyInstaller build starts and transcribes. +* The loaded processor's tokenizer class is asserted, not assumed. +* No `mlx_voxtral` left in the shipped code: + `grep -rn mlx_voxtral noScribe/ tools/ tests/ --include="*.py"` empty. The five + measurement scripts under `docs/scripts/` that import it reproduce numbers + taken on the old path; they keep the import and get a one-line note saying + so. Rewriting them would be re-measuring, which step 7 covers with the two + scripts that matter. + +## Traps + +* **Never trust a silent load.** Assert quantised-module count, not just "it + loaded". +* **Do not re-run the bit sweeps.** Fact 2 says they still hold; re-running them is + days of compute for a known answer. +* **Do not touch the pinned venv** until the approach is settled. +* **Do not infer frozen behaviour.** Build it. +* **Do not delete `_merged_embeddings`' dtype guard reflexively.** It costs nothing + and protects against a build whose dtypes disagree. +* Read the docstrings in `voxtral_engine.py` before changing a constant. Several + encode a defect that was expensive to find. + +## Two things mlx-audio has that are not this migration + +Surveyed 2026-09-03 so they are not rediscovered. Neither is a reason to add +mlx-audio as a *second* dependency next to mlx-voxtral: both jobs are small +enough for the torch stack noScribe already carries, and the added package +brings `miniaudio`, `sounddevice` (native) and a `transformers>=5.14` floor +into the worker's import graph for nothing the GPU is needed for. + +* **Language identification** (`mlx_audio.lid`): `facebook/mms-lid-256` + (wav2vec2, ships safetensors, loads directly) and VoxLingua107-ECAPA + (community conversions only). Where this would matter is not here but in the + language-flip guard of `voxtral_engine.py`: its uncovered case is "first + chunk translated, no loop, language on Auto", because `want_lang` is then + unknown. Audio LID over the first minute would supply it without any text. + It helps the *guard*, not the model — `lang:de` in the prompt was measured + not to prevent the flip. transformers 5.16.1 already has + `Wav2Vec2ForSequenceClassification` for the same checkpoint, on torch. +* **Silero VAD** (`vad/silero_vad`, `get_speech_timestamps`), as an + alternative to the energy-based cut points in `_quiet_runs`. Unmeasured, and + the lost start of a pass was shown to depend on where the window *ends*, not + on where it is cut (`docs/voxtral-benchmarks.md`, section 5), so do not + expect a better cut to buy that. + +Everything else was already known: no Viterbi anywhere in the tree, the +Qwen3 aligner collapses at length (`docs/viterbi-numpy-brief.md`), sortformer +caps at four speakers, the eval normaliser is English +only, and Voxtral Realtime is fully ported there but was ruled out on Mistral's +own WER figures (`docs/other-asr-engines.md`). + +## Rollback + +The current state is committed. If the migration stalls, +the pins are stable and nothing is broken — revert. The measurement documents +describe the pre-migration state accurately. diff --git a/docs/other-asr-engines.md b/docs/other-asr-engines.md new file mode 100644 index 00000000..3eb05814 --- /dev/null +++ b/docs/other-asr-engines.md @@ -0,0 +1,433 @@ +# Other ASR engines, measured against Voxtral + +Is anything else better for German interview audio yet? Seven engines have been +measured against the shipped `voxtral-mini-8bit` build, and none has replaced +it. This file is the record, so the same candidate is not re-evaluated from +scratch every time it trends. Which *Voxtral* build to ship is a different +question and lives in [voxtral-quantisation.md](voxtral-quantisation.md). + +## How these numbers are made + +One harness, one metric, for every engine: the scripts under +`docs/scripts/engines/` reuse `norm` and `wer` from `docs/scripts/wer.py` +unchanged, so a row here is comparable with a row there. +`docs/scripts/engines/README.md` has the shape they share and how to add one. + +Three yardsticks, deliberately unequal: + +* **Hard passage** — 422 words, two minutes, hand-corrected: overlapping speech, + crosstalk, brand names, a dialect speaker. The hardest thing here. +* **Second reference** — 859 words, five minutes, a video call, hand-corrected: + real conversation, cleaner recording. +* **FLEURS German** — 100 read-aloud recordings, 25 minutes, public benchmark. + +The reason for all three is the finding that keeps repeating below: **a model +can top the read-aloud benchmark and be unusable for interview work.** Speeds +measured on MPS are indicative only — the same clip has measured 2.04x cold and +4.45x warm in one session. + +> **A note on the second reference.** It was produced by hand-correcting this +> engine's own draft, so scores on it flatter Voxtral by construction. The +> figure below (1.98 % / 1.09 %) is the honest one, re-scored 2026-08-22 under +> mlx-voxtral 0.0.6. An earlier **0.81 % / 0.64 %** circulated and should not +> be quoted: it was the residual distance to the very draft the reference was +> corrected from — two substitutions on 859 words of conversational audio was +> implausibly good, and that was the tell. Comparisons keep their direction +> either way, with a smaller multiplier. + +## The three tables + +**Hard passage, 422 words.** Ranked by word error. + +| Engine | WER | CER | Sub | Del | Ins | Speed | Commas/100w | +|---|---:|---:|---:|---:|---:|---:|---:| +| **voxtral-mini-8bit** | **4.27 %** | **3.39 %** | 10 | 8 | 0 | 6.8–7.2x | 10.87 | +| transcribe.cpp Q8_0, `--language de` | 4.98 % | 3.83 % | 10 | 10 | 1 | 7.11x | — | +| transcribe.cpp Q8_0, auto | 5.45 % | 3.83 % | 11 | 10 | 2 | 7.10x | — | +| whisper-fast | 8.06 % | 3.34 % | 22 | 4 | 8 | 2.43x | 10.62 | +| cohere-transcribe | 10.43 % | 3.69 % | 25 | 4 | 15 | **16.58x** | 11.74 | +| qwen3-asr-1.7b, 60 s chunks | 10.90 % | 4.33 % | 29 | 5 | 12 | — | 8.73 | +| qwen3-asr-1.7b, one pass | 11.14 % | 4.08 % | 31 | 4 | 12 | — | 0 | +| qwen3-asr-1.7b, auto language | 11.85 % | 4.33 % | 32 | 5 | 13 | — | — | +| vibevoice-asr | 13.03 % | 6.98 % | 34 | 2 | 19 | 0.66x | **11.34** | +| nemotron-3.5-asr, `de-DE` | 14.45 % | 6.54 % | 47 | 3 | 11 | 5.15x | 8.60 | +| parakeet-tdt-0.6b-v3, beam 5 | 14.69 % | 6.49 % | 49 | 6 | 7 | 9.13x | — | +| parakeet-tdt-0.6b-v3, greedy | 18.01 % | 9.88 % | 49 | 20 | 7 | 26.47x | — | + +**Second reference, 859 words.** + +| Engine | WER | CER | Sub | Del | Ins | Speed | +|---|---:|---:|---:|---:|---:|---:| +| **voxtral-mini-8bit** | **1.98 %** | **1.09 %** | 12 | 4 | 1 | 7.97x | +| transcribe.cpp Q8_0, `--language de` | 2.10 % | 1.33 % | 12 | 5 | 1 | 8.25x | +| parakeet-tdt-0.6b-v3, beam 5 | 8.50 % | 5.18 % | 34 | 15 | 24 | 10.30x | +| cohere-transcribe | 9.20 % | 6.12 % | — | — | — | **27.93x** | +| parakeet-tdt-0.6b-v3, greedy | 10.59 % | 6.81 % | 36 | 14 | 41 | 41.75x | +| qwen3-asr-1.7b, 60 s chunks | 10.83 % | 6.69 % | 36 | 16 | 41 | — | +| qwen3-asr-1.7b, one pass | 12.11 % | 7.01 % | 43 | 17 | 44 | — | +| vibevoice-asr | 12.34 % | 7.58 % | 35 | 10 | 61 | 0.87x | +| nemotron-3.5-asr, `de-DE` | 17.69 % | 9.28 % | 63 | 20 | 69 | 7.69x | + +**FLEURS German, 100 recordings.** The ranking inverts almost completely. + +| Engine | WER | CER | Speed | +|---|---:|---:|---:| +| **qwen3-asr-1.7b** | **3.96 %** | **1.23 %** | — | +| whisper-precise | 4.05 % | 1.40 % | 4.11x | +| cohere-transcribe | 4.55 % | 1.85 % | 13.51x | +| voxtral-mini-8bit | 4.81 % | 1.44 % | 7.64x | +| parakeet-tdt-0.6b-v3, greedy | 4.81 % | 2.15 % | **44.45x** | +| vibevoice-asr | 8.26 % | 5.84 % | — | +| nemotron-3.5-asr, `de-DE` | 11.00 % | 5.80 % | 7.95x | + +Read the three together and the pattern is the whole point: on FLEURS the field +is separated by tenths of a point and Voxtral is mid-table; on real conversation +the same field spreads over an order of magnitude and the order reverses. + +--- + +## transcribe.cpp's Voxtral — on par, and that is the finding + +[transcribe.cpp](https://github.com/handy-computer/transcribe.cpp) is a GGML +speech-to-text library (MIT) carrying Voxtral alongside fifteen other families, +with Metal, CUDA, Vulkan and HIP backends. It is the only route we know to +*this* model on hardware MLX cannot reach — which is the whole reason to measure +it, since the engine is otherwise Apple-Silicon-only. Built from source (Metal), +`Voxtral-Mini-3B-2507-Q8_0.gguf` from the project's own GGUF repo, with our own +build re-run the same day so both columns come from one machine and one pin. + +**Identical substitution counts on both passages** (10 and 12), the difference +sitting in one or two extra deletions — inside the ±1.8 to ±2.5 CER points a +passage this size can resolve. The two engines are indistinguishable on this +material, at the same speed. + +**The expected failure did not appear.** [Issue #82](https://github.com/handy-computer/transcribe.cpp/issues/82) +reports Voxtral's Tekken tokenizer as unimplemented there, with the loader +falling back to qwen2 pretokenization and producing German word-level garbles +("Publikum" → "Pubikom"). That is the defect this measurement was designed to +catch, and on 1281 words of German it did not show: a near-miss scan over every +produced word absent from the reference turns up three pairs, all ordinary +mishearings (`geworden` for `geboren`, `schokopourridge` for `schokoporridge`) +rather than dropped-letter garbles. The issue may be real on other material or +other quants; it is not visible here, at Q8_0, on this audio. + +So the quality objection to a cross-platform Voxtral does not survive contact +with the measurement. What remains is engine-level, not model-level, and is +recorded nowhere else, so here it is: transcribe.cpp advertises +`TRANSCRIBE_TIMESTAMPS_NONE` for Voxtral; there is no repetition/loop defence on +its causal-LM path (the only compression-ratio gate in that tree is Whisper's +2.4, which we measured as too coarse); and a C API cannot hand us the logits +processor the loop breaker rides on. + +## Parakeet-TDT — rejected + +`nvidia/parakeet-tdt-0.6b-v3`: a FastConformer encoder with a Token-and-Duration +Transducer decoder, 0.6B parameters, CC-BY-4.0, 25 European languages including +German, and — unlike anything else here — distributed as MLX, ONNX, CoreML and +GGUF, so it would run on Windows and Linux CPUs too. A transducer also cannot do +several things this engine defends against: it emits tokens bound to audio +frames, so repetition loops, whole-chunk language drift and a dropped head are +structurally impossible, and word timestamps fall out of the predicted durations +instead of needing a CTC aligner. Measured via `parakeet-mlx` 0.5.2 at bf16. + +**On FLEURS it ties Voxtral to the second decimal, and on conversation it is +four to five times behind.** That is the benchmark's whole worth here. + +**The failure has a name: uncontrolled code-switching.** v3 is multilingual with +no language conditioning — no language token, so `transcribe()` has no +`language` argument. When the acoustics get ambiguous it writes German function +words as their English homophones: *und* as "and", *wenn* as "when", *es ist* as +"it is", *gut* as "good", *ja* as "yeah". Counted: **14 English function-word +tokens in 407 words** on the hard passage, **0 in 882 words** on the cleaner +second reference. It is triggered by audio quality, and no input suppresses it. + +The error *profile* is the second problem: 49 substitutions against Voxtral's 10 +on the same passage. Parakeet replaces where Voxtral omits, and a wrong word +survives proof-reading in a way a missing one does not — the same argument that +decided against `whisper-precise`. Beam search (width 5) is worth having if the +model is ever revisited: it cuts deletions from 20 to 6 and roughly 3 WER +points, for two thirds of the throughput. + +## Nemotron 3.5 ASR — the language prompt works, the recognition does not + +`nvidia/nemotron-3.5-asr-streaming-0.6b` (2026-09): a cache-aware streaming +FastConformer-RNNT, 0.6B parameters, OpenMDW-1.1, 40 language-locales — the +multilingual successor in Parakeet's line, with the one thing Parakeet lacked: a +language prompt, a one-hot vector joined to every encoder frame. Measured via +mlx-audio (`stt/models/nemotron_asr`, `mlx-community/nemotron-3.5-asr-streaming-0.6b`, +bf16) at its largest chunk, 1120 ms; 560 ms and automatic language detection land +within a point either way. + +**The prompt cures the code-switching.** Zero English function words on either +reference (Parakeet: 14 in 407 on the hard passage), and it demonstrably reaches +the model — prompted `en-US`, the hard passage collapses to 51 % WER with 163 +deletions. + +**What is left is Parakeet's error profile without the English.** 47 +substitutions against 3 deletions on the hard passage: wrong inflections +(`isst` → `ist`, `die` → `der`), split or misheard compounds and brand names, +invented replacement words. It writes no digits and assembles numbers wrongly +(1940 as "eintausend hundertvierzig"), keeps fillers and stutters (15 `äh`/`ähm` +on the second reference; without them it would score 16.2 % instead of 17.7 %), +and sets a third fewer commas than Whisper or Voxtral. On conversation it ends +level with Parakeet on the hard passage and last on the second reference, and it +is also last on FLEURS — the model card's 8.31 % there uses a normalisation that +maps number words to digits, ours does not. Discussion #11 on the model page +reports the same for German; NVIDIA points to word boosting, LM fusion or +fine-tuning. Worth a second look only if a German fine-tune appears. + +## Qwen3-ASR-1.7B — wins the benchmark, loses the job + +`Qwen/Qwen3-ASR-1.7B-hf` is the Voxtral principle at half the size: an audio +encoder in front of a Qwen3-Omni language model, Apache-2.0, 30 languages. Two +things make it easier to try than anything else here — **transformers supports it +natively** (`AutoModelForMultimodalLM`, no new dependency in this venv) — and it +takes an explicit language as well as a free-form context prompt. + +On FLEURS German it is the best model this project has measured, ahead of +Whisper and of the shipped Voxtral build in both words and characters. On the +two conversational references it is two to six times behind. The cause is not +code-switching — there is none, and forcing the language buys only 0.7 WER +points over auto-detect. It simply hears this material less well. + +Three findings that outlive the verdict: + +**Punctuation collapses on long input, and chunking fixes it.** Fed the whole +120 s or 300 s clip, the model returns text with *zero* commas and *zero* full +stops. Cut into ~60 s windows it punctuates normally — 8.73 and 10.33 commas per +100 words, against Whisper's 10.62 and Voxtral's 10.87. A length sweep puts the +usable band at roughly 30–90 s. The card advertises long audio; for German prose +output it does not hold, and any engine built on this model would have to chunk +far more aggressively than Voxtral does. + +**The context prompt is a real hotword mechanism — the thing Voxtral lacks.** +With `Vocabulary: …` in the system message, "Vita Flor" becomes "VitaFlor", +"Son Vita" becomes "Sonvita" and a mangled compound comes back correct, on a +controlled 30 s clip with no other change to the text. Over the full passage it +costs nothing in word error (10.90 % either way) and 0.05 points of character +error. This is exactly what `voxtral_corrections.yml` exists to work around, and +it is the one capability that would argue for the model. + +**But it is paid for in punctuation:** the same vocabulary hint halves comma +density, 8.73 to 4.28 per 100 words. A term list behaves as a decode +perturbation here too, just a cheaper one than in Voxtral — the same phenomenon +as the `repetition_penalty` default, and a reminder to measure punctuation +whenever a decode-level knob is turned. + +Two implementation notes for anyone who picks this up. The `prompt=` argument +documented on `apply_transcription_request` **does not exist** in transformers +5.15.0.dev0 — it lands in `**kwargs` and is dropped with a warning; the context +has to go into a system message next to the language, which is what the chat +template concatenates anyway. And an MLX port would be the honest place to +measure throughput. + +## VibeVoice-ASR — the architecture works, the recognition does not + +`microsoft/VibeVoice-ASR-HF` is not another engine behind the same seam. It does +ASR, diarization and timestamping in **one pass** and emits speaker-attributed +segments directly — noScribe's whole pipeline collapsed into one model. MIT, +16.7 GB in bf16, transformers-native, with 4-bit and 8-bit MLX ports from +mlx-community. + +FLEURS is arguably the wrong test for it — its design point is an hour of +multi-speaker audio, not a 15-second clip, and one of the 100 clips came back as +nothing but a `[Silence]` tag. Take the 8.26 % as a lower bound rather than a +verdict. + +**The diarization, however, is the real thing.** Frame by frame against +noScribe's own pyannote pipeline on the same two clips, 10 ms resolution, best +speaker permutation: + +| | speakers | segments | timeline labelled | agreement with pyannote | +|---|---:|---:|---:|---:| +| hard passage | 2 vs 2 | 16 vs 22 | 100 % vs 96.4 % | **98.2 %** | +| second reference | 2 vs 2 | 19 vs 52 | 97.1 % vs 93.3 % | **97.5 %** | + +Same speaker count, and near-total agreement on who is speaking — from a model +that produced the transcript in the same forward pass. The segmentation is much +coarser (16 and 19 turns against 22 and 52), a problem for subtitle cues but not +for speaker attribution. It also punctuates better than anything else measured +here (11.34 and 12.57 commas per 100 words) and labels non-speech explicitly +(`[Silence]`, `[Human Sounds]`); those tags are stripped before scoring, and +leaving them in costs 3.3 WER points on FLEURS. + +**Not adopted, and the reason is only recognition.** Three times Voxtral's word +error on the hard passage, six times on the second reference, and 0.66–0.87x +realtime on MPS — slower than the two-stage pipeline it would replace, in which +pyannote is cheap and Voxtral runs at 6.6x. But the architecture is validated, +and that is worth writing down: **a single model really can deliver speaker, +time and text at pyannote-grade diarization quality.** The thing to watch is a +model of this shape that hears German conversation as well as Voxtral does. +Nothing here suggests that is far off. + +## Cohere Transcribe — the best challenger so far, still not close enough + +`CohereLabs/cohere-transcribe-03-2026`, from the leaderboard below: ~2B +parameters, Apache-2.0, 3.9 GB, transformers-native, best open-weight model on +the leaderboard's English long-form tab. Measured at bf16 on MPS, language +forced to `de`. + +**On the hard passage its character error is within noise of Voxtral's** — 3.69 +against 3.39, where this project's noise floor is ~0.15 points and a single +build's word-error interval is ±3. By the measure that tracks what was *heard* +rather than how it was spelled, a 2B model at 16x realtime is level with the 3B +Voxtral build at 6.8x, on the hardest audio here. That is the best result any +challenger has produced. + +**The second reference decides it anyway:** 9.20 % against 1.98 %, character +error 6.12 against 1.09 — a factor of five, far outside anything the error bars +cover, on the larger of the two references. + +Three limitations from its own model card, all of which matter for an engine: + +* **No timestamps, no diarization.** The tokenizer knows `<|timestamp|>` and + `<|diarize|>` and the decoder prompt accepts them — leftovers of the training + format. Setting them changes nothing useful (the diarize arm just truncates, + 307 words against 426), and the card says the model does not feature either. + Word timestamps would still need the CTC aligner. +* **No language detection**, and explicitly inconsistent on code-switched audio. + noScribe's "Auto" would have to be resolved before the engine is called. +* **It hallucinates on silence** and wants a VAD or noise gate in front. Visible + here: the first window opens with a header of its own, `Input transcript + corrected:`, once per file, regardless of the context slot. Stripped before + scoring; leaving it in costs 0.7 WER points. + +Not adopted. But it is the first challenger where the gap is about a specific +weakness rather than the whole model, and its ecosystem is the broadest of +anything here — transformers, vLLM, mlx-audio, ONNX, GGUF, a Rust port and a +WebGPU demo. Worth re-measuring when Cohere ships a successor. + +*Practical note:* the repo is gated (click-through) and its Xet transfer fails +with `Unable to parse string as hex hash value`. `HF_HUB_DISABLE_XET=1` in front +of the download falls back to plain HTTP and works. + +## Voxtral-Mini-4B-Realtime — ruled out on the vendor's own numbers + +The streaming **Voxtral-Mini-4B-Realtime** model (Awni Hannun's +[voxmlx](https://github.com/awni/voxmlx) runs it with a bounded rotating KV +cache) was considered as a low-memory option, then ruled out on Mistral's own +published figures: on German FLEURS it scores 6.19 % WER at its 480 ms setting +and 4.15 % even at 2.4 s delay — worse than the offline Voxtral Mini 3B +(3.54 %), a smaller model. The causal/streaming architecture trades look-ahead +for latency, and on hard conversational audio the gap would only widen. Its +advantages — sub-500 ms latency, bounded memory — are irrelevant to offline file +transcription. + +**`voxtral-mini-2602` ("Transcribe 2")** is ruled out for a different reason: +still API-only, and therefore unusable for confidential interviews. + +## What the Open ASR Leaderboard says (German tab, checked 2026-08-20) + +The leaderboard has a German tab fed by `hf-audio/multilingual_evals` +(`multilingual_de.csv`) — an independent check on everything above. Ranked by +FLEURS German WER, Common Voice alongside: + +| Model | FLEURS | MCV | RTFx | +|---|---:|---:|---:| +| microsoft/azure-speech-06-2026 *(API)* | 1.93 | 1.88 | — | +| elevenlabs/scribe_v2 *(API)* | 2.30 | 2.19 | — | +| assemblyai/universal-3-pro *(API)* | 2.42 | 2.76 | — | +| reson8/resonant-1 *(API)* | 2.56 | 3.01 | — | +| **mistralai/Voxtral-Small-24B-2507** | **2.61** | 3.19 | 83 | +| openai/whisper-large-v3 | 3.20 | 4.79 | 328 | +| CohereLabs/cohere-transcribe-03-2026 | 3.33 | **2.87** | 607 | +| Qwen/Qwen3-ASR-1.7B-hf | 3.35 | 4.60 | 369 | +| nvidia/canary-1b-v2 | 3.43 | 4.69 | 1308 | +| **mistralai/Voxtral-Mini-3B-2507** | **3.64** | 5.29 | 150 | +| microsoft/Phi-4-multimodal-instruct | 3.99 | 4.25 | 123 | +| nvidia/parakeet-tdt-0.6b-v3 | 4.16 | 4.07 | 3363 | +| microsoft/VibeVoice-ASR-HF | 7.44 | 20.97 | 114 | + +**Voxtral-Small is the best open-weight model on German here.** Everything above +it reports no RTFx, which on this leaderboard marks a proprietary API. The engine +this project settled on is not a compromise pick; it is the top of the open field +for this language. + +It also cross-checks the harness, though not perfectly. The broad ordering on +FLEURS matches ours — Qwen3-ASR ahead of Voxtral-Mini, VibeVoice far behind — +but Parakeet is one place *behind* Voxtral-Mini there while our own run has the +two tied at 4.81 %, and the absolute values differ by more than normalisation +alone would explain: Voxtral-Mini 3.64 against our 4.81, Cohere 3.33 against our +4.55, both more than a point apart, while Qwen (0.61) and Parakeet (0.65) sit +close. Our 4-bit Voxtral-Small scores 2.82 against the leaderboard's 2.61 for +the unquantised model, so the quantisation costs about 0.2 points on clean +audio, which is the same story the build sweeps tell. + +**`CohereLabs/cohere-transcribe-03-2026` was the one candidate the table added +that we had not seen** — best Common Voice German of any open model listed at +2.87, ahead of Voxtral-Small's 3.19, and best open model outright on the English +long-form tab. It has since been measured; see above, and note how little the +leaderboard predicted: two Common Voice points ahead of Voxtral-Small, and a +factor of five behind Voxtral-Mini on a real conversation. The long-form tab is +**English only**, so it says nothing about German conversation — the gap this +project keeps running into has no public benchmark at all. + +## Surveyed, not measured + +Scaling up within Parakeet's own family does not help: `parakeet-tdt-1.1b` and +`canary-qwen-2.5b` are English-only. `nvidia/canary-1b-v2` is the real step up — +25 languages, CC-BY-4.0, 4.40 % against Parakeet's 5.04 % on NVIDIA's own FLEURS +German figure — but it is an attention encoder-decoder rather than a transducer, +so it gives back the structural guarantees that made the family interesting, and +0.6 FLEURS points say nothing about conversational audio. +`OpenMOSS-Team/MOSS-Transcribe-Diarize` does VibeVoice's joint trick and is more +popular, but supports only Chinese and English. + +Inside mlx-audio, two German-capable ASR models are unmeasured: `canary` and +`granite_speech` (`nemotron_asr` is measured above). The rest of its +`stt/models/` carry no German at all, and `mega_asr` is a router over Qwen3-ASR +rather than a model. After seven comparisons the pattern is stable enough to +predict the outcome: these models tie on FLEURS and lose on real conversation. + +## Reproducing + +```bash +# Parakeet (needs `pip install parakeet-mlx`, which is NOT in the requirements -- +# it pulls only dacite on top of what is already installed, and mlx 0.32.1 +# satisfies its floor, so it can be added and removed without disturbing the +# pinned Voxtral stack) +python docs/scripts/engines/parakeet_wer.py ref \ + Audiotest2/referenz/hart_780-900_REFERENZ.txt \ + Audiotest2/referenz/hart_780-900.wav 5 # trailing 5 = beam width +python docs/scripts/engines/parakeet_wer.py fleurs 100 + +# Nemotron 3.5 ASR (needs mlx-audio from git main -- nemotron_asr came after the +# 0.5.5 release -- and mlx-audio wants mlx 0.32.2 over the pinned 0.32.1, so use +# a throwaway venv, not this one). arm = language[@right context]: de-DE is +# 1120 ms chunks, de-DE@6 560 ms; auto and en-US for the cross-checks +python docs/scripts/engines/nemotron_asr_wer.py ref \ + Audiotest2/referenz/hart_780-900_REFERENZ.txt \ + Audiotest2/referenz/hart_780-900.wav de-DE +python docs/scripts/engines/nemotron_asr_wer.py fleurs 100 de-DE + +# Qwen3-ASR (no extra dependency -- transformers 5.13+ has it) +python docs/scripts/engines/qwen_asr_wer.py ref \ + Audiotest2/referenz/hart_780-900_REFERENZ.txt \ + Audiotest2/referenz/hart_780-900.wav chunk60 +python docs/scripts/engines/qwen_asr_wer.py ref \ + Audiotest2/referenz/hart_780-900_REFERENZ.txt \ + Audiotest2/referenz/hart_780-900.wav "vocab:VitaFlor, Sonvita" +python docs/scripts/engines/qwen_asr_wer.py fleurs 100 + +# VibeVoice-ASR: same yardsticks, plus its own diarization +python docs/scripts/engines/vibevoice_wer.py ref \ + Audiotest2/referenz/hart_780-900_REFERENZ.txt \ + Audiotest2/referenz/hart_780-900.wav +python docs/scripts/engines/vibevoice_wer.py fleurs 100 + +# Cohere Transcribe (gated repo -- accept on the model page first; its Xet +# transfer is broken, so HF_HUB_DISABLE_XET=1 for the download) +python docs/scripts/engines/cohere_asr_wer.py tokens +python docs/scripts/engines/cohere_asr_wer.py ref \ + Audiotest2/referenz/hart_780-900_REFERENZ.txt \ + Audiotest2/referenz/hart_780-900.wav plain +python docs/scripts/engines/cohere_asr_wer.py fleurs 100 + +# transcribe.cpp (build it first; audio must already be 16 kHz mono WAV) +python docs/scripts/engines/transcribe_cpp_wer.py \ + \ + Audiotest2/referenz/hart_780-900_REFERENZ.txt \ + Audiotest2/referenz/hart_780-900.wav --lang de +``` + +Start `venv/bin/python3`, not `python`. diff --git a/docs/scripts/adjudicate.py b/docs/scripts/adjudicate.py new file mode 100644 index 00000000..f752451f --- /dev/null +++ b/docs/scripts/adjudicate.py @@ -0,0 +1,74 @@ +"""List the spots where two arms hear differently -- with a timestamp. + +Why: a reference produced by correcting ONE transcript leans towards that +transcript. Errors overlooked while correcting stand in the reference as truth +afterwards -- and favour exactly the arm the draft came from. Every other arm +is penalised for the same spot even when it hears it correctly. + +That cannot be computed away, but it can be settled cheaply: there are only a +handful of spots where the arms diverge at all. This script lists them with an +approximate timestamp and what the reference says there, so that one can +listen to those spots specifically instead of the whole passage again. + +The timestamp is an estimate: word position times mean word duration. It +points to the neighbourhood, not the second. + +The arm transcripts are read from /tmp/wer_.txt, the scratch files that +wer.py, preproc_cer.py or pair_cer.py leave behind. + + python docs/scripts/adjudicate.py [arm ...] +""" +import difflib +import pathlib +import sys + +REPO = pathlib.Path(__file__).resolve().parents[2] +sys.path.insert(0, str(REPO / "docs" / "scripts")) +from wer import norm, OVERLAP + +CONTEXT = 4 + + +def load(label): + return norm(open(f"/tmp/wer_{label}.txt", encoding="utf-8").read()) + + +def stamp(pos, total, dur): + s = int(pos / max(1, total) * dur) + return f"{s//60:02d}:{s%60:02d}" + + +def main(): + ref_path, dur = sys.argv[1], float(sys.argv[2]) + a_label, others = sys.argv[3], sys.argv[4:] + raw = open(ref_path, encoding="utf-8").read() + ref = norm(OVERLAP.sub(" ", raw)) + a = load(a_label) + + for b_label in others: + b = load(b_label) + sm = difflib.SequenceMatcher(None, a, b, autojunk=False) + ops = [o for o in sm.get_opcodes() if o[0] != "equal"] + print(f"\n{'='*74}\n{a_label} vs {b_label}: {len(ops)} spots\n") + for tag, i1, i2, j1, j2 in ops: + # Where is the spot in the reference? Searched via the shared + # context before it, so the reference words can stand alongside. + pre = a[max(0, i1 - CONTEXT):i1] + k = -1 + for s in range(len(ref) - len(pre) + 1): + if pre and ref[s:s + len(pre)] == pre: + k = s + len(pre) + break + ref_span = " ".join(ref[k:k + max(1, i2 - i1)]) if k >= 0 else "?" + print(f" ~{stamp(i1, len(a), dur)} …{' '.join(pre)} ▸") + print(f" {a_label:14s} {' '.join(a[i1:i2]) or '(nothing)'!r}") + print(f" {b_label:14s} {' '.join(b[j1:j2]) or '(nothing)'!r}") + print(f" {'REFERENCE':14s} {ref_span!r}") + print(f" then: {' '.join(a[i2:i2+CONTEXT])}") + print("\nWhich one is right is for the ear to decide. Where the reference follows") + print("the first arm although the second is right, the reference leans towards") + print("the first -- and the comparison is biased in its favour.") + + +if __name__ == "__main__": + main() diff --git a/docs/scripts/audio_audit.py b/docs/scripts/audio_audit.py new file mode 100644 index 00000000..ffd35ad0 --- /dev/null +++ b/docs/scripts/audio_audit.py @@ -0,0 +1,109 @@ +"""Audit what the real source files actually look like before we touch them. + +Answers, per file: how hot is it, does it clip, does it clip only *after* +resampling, is there DC, how loud is it in LUFS. These are the numbers that +decide whether headroom or normalisation is worth anything. + + python docs/scripts/audio_audit.py [file ...] # default: Audiotest/* +""" +import sys, glob, pathlib +import numpy as np +import av +import soxr +import torch +import torchaudio + +REPO = pathlib.Path(__file__).resolve().parents[2] +SR = 16000 + + +def decode_float(path, max_sec=None): + """Decode to mono float64 at the file's native rate, without any int step. + + Mono is (L+R)/2 -- the same downmix noScribe/audio/convert.py performs, so + the numbers below describe the signal our pipeline actually sees. + """ + c = av.open(str(path)) + st = c.streams.audio[0] + layout = "stereo" if st.channels > 1 else "mono" + res = av.audio.resampler.AudioResampler(format="fltp", layout=layout, rate=st.rate) + buf = [] + for frame in c.decode(st): + for fr in res.resample(frame): + a = fr.to_ndarray() + buf.append(a.mean(axis=0) if a.shape[0] > 1 else a[0]) + if max_sec and sum(len(b) for b in buf) > st.rate * max_sec: + break + rate = st.rate + c.close() + return np.concatenate(buf).astype(np.float64), rate + + +def true_peak(x, sr, oversample=4): + """Inter-sample peak, the level a reconstruction filter would actually see.""" + up = soxr.resample(x, sr, sr * oversample, quality="VHQ") + return float(np.abs(up).max()) + + +def lufs(x, sr): + w = torch.from_numpy(x.astype(np.float32)).unsqueeze(0) + try: + return float(torchaudio.functional.loudness(w, sr)) + except Exception: + return float("nan") + + +def audit(path): + x, sr = decode_float(path) + y = soxr.resample(x, sr, SR, quality="VHQ") # ideal resample, float + n = len(x) + return dict( + name=pathlib.Path(path).name, + sr=sr, + sec=n / sr, + dc=float(x.mean()), + rms_db=20 * np.log10(float(np.sqrt((x ** 2).mean())) + 1e-30), + peak=float(np.abs(x).max()), + tpeak=true_peak(x, sr), + over=int(np.sum(np.abs(x) > 1.0)), + peak16k=float(np.abs(y).max()), + over16k=int(np.sum(np.abs(y) > 1.0)), + lufs=lufs(x, sr), + hf=10 * np.log10(hf_share(x, sr) + 1e-30), + ) + + +def hf_share(x, sr): + """Energy above 8 kHz as a fraction of the total -- what 16 kHz discards.""" + if sr <= 16000: + return 0.0 + n = 1 << 16 + acc = tot = 0.0 + fr = np.fft.rfftfreq(n, 1 / sr) + m = fr >= 8000 + for h in range(0, min(len(x) - n, sr * 300), n): + S = np.abs(np.fft.rfft(x[h:h + n] * np.hanning(n))) ** 2 + acc += S[m].sum(); tot += S.sum() + return acc / tot if tot else 0.0 + + +def main(paths): + print(f"{'file':16s} {'sr':>6s} {'sec':>7s} {'DC':>9s} {'RMS':>7s} " + f"{'peak':>6s} {'true':>6s} {'>1.0':>7s} {'pk@16k':>7s} {'>1@16k':>7s} " + f"{'LUFS':>7s} {'>8kHz':>7s}") + for p in paths: + try: + r = audit(p) + except Exception as exc: + print(f"{pathlib.Path(p).name[:16]:16s} -- {exc}") + continue + print(f"{r['name'][:16]:16s} {r['sr']:6d} {r['sec']:7.1f} {r['dc']:+9.2e} " + f"{r['rms_db']:7.1f} {r['peak']:6.3f} {r['tpeak']:6.3f} {r['over']:7d} " + f"{r['peak16k']:7.3f} {r['over16k']:7d} {r['lufs']:7.1f} {r['hf']:7.1f}") + + +if __name__ == "__main__": + args = sys.argv[1:] or (sorted(glob.glob(str(REPO / "Audiotest" / "*.m4a"))) + + sorted(glob.glob(str(REPO / "Audiotest2" / "*.m4a"))) + + [str(REPO / "Audiotest2" / "referenz" / "hart_780-900.wav")]) + main(args) diff --git a/docs/scripts/audio_filters.py b/docs/scripts/audio_filters.py new file mode 100644 index 00000000..b408574b --- /dev/null +++ b/docs/scripts/audio_filters.py @@ -0,0 +1,86 @@ +"""Run a libavfilter chain over a 16 kHz mono float array. + +Everything noScribe already depends on ships these filters, so a leveller or a +denoiser in front of the encoder costs no new dependency -- only the question of +whether it helps, which the scripts next to this one measure. + + y = apply_filter(x, "speechnorm=e=12.5:r=0.0001:l=1") +""" +from fractions import Fraction + +import numpy as np +import av + +SR = 16000 + +# Named chains, so the measurement scripts and the write-up refer to the same +# settings. Defaults are the filters' own unless a reason is given. +CHAINS = { + "raw": None, + # speechnorm's r is the per-sample rise rate; the default 0.0001 needs many + # seconds to lift a quiet passage, which is exactly the case under test, so + # a faster rise is used here. + "speechnorm": "speechnorm=e=12.5:r=0.002:l=1", + "dynaudnorm": "dynaudnorm=f=150:g=9:p=0.9:m=20", + # loudnorm runs its internals at 192 kHz, so it has to be brought back. + "loudnorm16": "loudnorm=I=-16:TP=-1.5:LRA=7,aresample=16000", + "acompressor": "acompressor=threshold=0.05:ratio=4:attack=20:release=250:makeup=2", + "highpass60": "highpass=f=60:poles=2", + "afftdn": "afftdn=nr=12:nf=-40", + "arnndn": None, # needs a model file; left unset on purpose +} + + +def apply_filter(x, chain, sr=SR): + """Return the filtered signal as float32, same sample rate.""" + if not chain: + return np.ascontiguousarray(x, dtype=np.float32) + graph = av.filter.Graph() + src = graph.add_abuffer(format="fltp", sample_rate=sr, layout="mono", + time_base=Fraction(1, sr)) + prev = src + for part in chain.split(","): + name, _, args = part.partition("=") + node = graph.add(name.strip(), args.strip() or None) + prev.link_to(node) + prev = node + sink = graph.add("abuffersink") + prev.link_to(sink) + graph.configure() + + frame = av.AudioFrame.from_ndarray( + np.ascontiguousarray(np.asarray(x, dtype=np.float32)[np.newaxis, :]), + format="fltp", layout="mono") + frame.sample_rate = sr + frame.time_base = Fraction(1, sr) + frame.pts = 0 + graph.push(frame) + graph.push(None) + + out = [] + while True: + try: + out.append(graph.pull().to_ndarray().ravel()) + except (av.error.EOFError, av.error.BlockingIOError): + break + return (np.concatenate(out).astype(np.float32) if out + else np.zeros(0, dtype=np.float32)) + + +if __name__ == "__main__": + # Smoke test: a two-level signal shows what each chain does to the quiet part. + t = np.arange(SR * 4) / SR + loud = 0.5 * np.sin(2 * np.pi * 300 * t[:SR * 2]) + quiet = 0.5 * 10 ** (-25 / 20) * np.sin(2 * np.pi * 300 * t[:SR * 2]) + x = np.concatenate([loud, quiet]).astype(np.float32) + print(f"{'chain':14s} {'len':>7s} {'laut':>8s} {'leise':>8s} {'Abstand':>9s}") + for name, chain in CHAINS.items(): + if name == "arnndn": + continue + y = apply_filter(x, chain) + if len(y) < len(x) // 2: + print(f"{name:14s} {len(y):7d} (leer)") + continue + a = np.abs(y[:SR * 2]).max(); b = np.abs(y[SR * 2:]).max() + print(f"{name:14s} {len(y):7d} {20*np.log10(a+1e-12):8.1f} " + f"{20*np.log10(b+1e-12):8.1f} {20*np.log10(a/(b+1e-12)):9.1f} dB") diff --git a/docs/scripts/audit_long.py b/docs/scripts/audit_long.py new file mode 100644 index 00000000..36d1391e --- /dev/null +++ b/docs/scripts/audit_long.py @@ -0,0 +1,84 @@ +"""Streaming audit for long recordings, and a per-minute level profile. + +audio_audit.py holds the whole file in memory, which a five-hour Zoom recording +will not tolerate. This one streams: overall peak / DC / clipping counts, plus a +per-minute loudness profile, because on unmastered material the interesting +question is not the average level but how far the quiet minutes sit below the +loud ones. That spread is what a single global gain cannot fix. + + python docs/scripts/audit_long.py [minutes_per_bucket] +""" +import pathlib +import sys + +import numpy as np +import av +import soxr +import torch +import torchaudio + +SR = 16000 + + +def stream_mono(path, chunk_sec=60): + """Yield (t0, mono float64 at 16 kHz) minute by minute, downmixed (L+R)/2.""" + c = av.open(str(path)) + st = c.streams.audio[0] + layout = "stereo" if st.channels > 1 else "mono" + res = av.audio.resampler.AudioResampler(format="fltp", layout=layout, rate=st.rate) + buf, t0, need = [], 0.0, st.rate * chunk_sec + for frame in c.decode(st): + for fr in res.resample(frame): + a = fr.to_ndarray() + buf.append(a.mean(axis=0) if a.shape[0] > 1 else a[0]) + if sum(len(b) for b in buf) >= need: + x = np.concatenate(buf).astype(np.float64) + yield t0, x[:need], st.rate + buf = [x[need:]] + t0 += chunk_sec + if buf: + x = np.concatenate(buf).astype(np.float64) + if len(x): + yield t0, x, st.rate + c.close() + + +def lufs(x, sr): + if len(x) < sr * 0.5: + return float("nan") + w = torch.from_numpy(np.ascontiguousarray(x, dtype=np.float32)).unsqueeze(0) + try: + return float(torchaudio.functional.loudness(w, sr)) + except Exception: + return float("nan") + + +def main(): + path = sys.argv[1] + bucket = float(sys.argv[2]) if len(sys.argv) > 2 else 1.0 + peak = 0.0 + over = n = 0 + dc = 0.0 + prof = [] + for t0, x, rate in stream_mono(path, chunk_sec=int(60 * bucket)): + peak = max(peak, float(np.abs(x).max())) + over += int(np.sum(np.abs(x) > 1.0)) + dc += float(x.sum()) + n += len(x) + prof.append((t0, lufs(x, rate), float(np.abs(x).max()), + 20 * np.log10(float(np.sqrt((x ** 2).mean())) + 1e-30))) + print(f"# {pathlib.Path(path).name}: {n / rate / 60:.1f} min") + print(f"# Peak {peak:.4f} Samples>1.0 {over} DC {dc / max(1, n):+.2e}") + L = np.array([p[1] for p in prof], dtype=float) + L = L[np.isfinite(L)] + if len(L): + q = np.percentile(L, [5, 25, 50, 75, 95]) + print(f"# LUFS pro {bucket:g} min: Median {q[2]:.1f}, " + f"p5 {q[0]:.1f}, p95 {q[4]:.1f}, Spanne p95-p5 {q[4]-q[0]:.1f} dB") + print(f"\n{'t/min':>7s} {'LUFS':>7s} {'peak':>7s} {'RMS dB':>8s}") + for t0, lo, pk, rms in prof: + print(f"{t0/60:7.0f} {lo:7.1f} {pk:7.3f} {rms:8.1f}") + + +if __name__ == "__main__": + main() diff --git a/docs/scripts/batch_probe.py b/docs/scripts/batch_probe.py new file mode 100644 index 00000000..6970f269 --- /dev/null +++ b/docs/scripts/batch_probe.py @@ -0,0 +1,118 @@ +"""Batched greedy decode: B equal-length Voxtral passes decoded together. + +Equal-length audio gives equal-length prompts, so no padding and no mask +change is needed. Compares against production (_fast_generate, one pass at a +time) on wall time and tokens/s, and checks that every batched row is +token-identical. Result: docs/voxtral-benchmarks.md §7 -- 1.16x at B=2, 1.11x +at B=4, every row identical; decode is not bandwidth-bound on an M1 Max. + + python docs/scripts/batch_probe.py <16 kHz wav> [B ...] + +The wav must hold at least max(B) passes. +""" +import sys, time +import pathlib +REPO = pathlib.Path(__file__).resolve().parents[2] # docs/scripts/x.py -> repo root +sys.path.insert(0, str(REPO)) + +import numpy as np +import soundfile as sf +import mlx.core as mx +from mlx_lm.models.cache import KVCache +from noScribe.voxtral_engine import _Voxtral + +wav, sec, Bs = sys.argv[1], float(sys.argv[2]), [int(b) for b in sys.argv[3:]] +audio, sr = sf.read(wav, dtype="float32") +n = int(sec * sr) +vox = _Voxtral(str(REPO / "models/voxtral-mini-8bit")) +lm = vox._lm_adapter +stops = set(vox._STOP_TOKENS) +PREFILL = 2048 # generate_step's default prefill_step_size +MAX_NEW = 4096 + + +def inputs(i): + chunk = audio[i * n:(i + 1) * n] + inp = vox.proc.apply_transcrition_request(audio=chunk, language=None, sampling_rate=sr) + return {"input_ids": inp.input_ids, "input_features": inp.input_features} + + +def batched_greedy(mis): + """Prefill all rows in PREFILL-sized steps, then decode one token per row per + step until every row has hit a stop token.""" + embeds = mx.concatenate([vox._merged_embeddings(mi) for mi in mis], axis=0) + mx.eval(embeds) + B, L, _ = embeds.shape + cache = [KVCache() for _ in range(len(vox.model.language_model.layers))] + t0 = time.perf_counter() + pos = 0 + while L - pos > 1: # like generate_step: all but the last position + step = min(PREFILL, L - 1 - pos) + lm(None, cache=cache, input_embeddings=embeds[:, pos:pos + step]) + mx.eval([c.state for c in cache]) + pos += step + logits = lm(None, cache=cache, input_embeddings=embeds[:, pos:]) + y = mx.argmax(logits[:, -1, :], axis=-1) + mx.eval(y) + t1 = time.perf_counter() + out = [[] for _ in range(B)] + done = [False] * B + steps = 0 + while not all(done) and steps < MAX_NEW: + nxt = mx.argmax(lm(y[:, None], cache=cache)[:, -1, :], axis=-1) + mx.async_eval(nxt) + for b, t in enumerate(y.tolist()): + if not done[b]: + if t in stops: + done[b] = True + else: + out[b].append(t) + y = nxt + steps += 1 + t2 = time.perf_counter() + return out, t1 - t0, t2 - t1, steps + + +mx.eval(vox._merged_embeddings(inputs(0))) # warm-up (kernels, weights) +maxB = max(Bs) +mis = [inputs(i) for i in range(maxB)] +print(f"passes of {sec:.0f}s, prompt lengths {[mi['input_ids'].shape[1] for mi in mis]}", flush=True) + +# Production baseline: one pass at a time through _fast_generate. +prod, prod_t = [], [] +for mi in mis: + t0 = time.perf_counter() + toks = np.array(vox._fast_generate(mi, MAX_NEW))[0].tolist() + prod_t.append(time.perf_counter() - t0) + prod.append(toks) +print(f"production B=1: {sum(prod_t):.1f}s for {maxB} passes, " + f"{sum(map(len, prod))} tokens", flush=True) + +# Own loop at B=1: separates the loop's own effect from batching. +own1 = [] +for i, mi in enumerate(mis[:1]): + out, tp, td, _ = batched_greedy([mi]) + own1.append(out[0]) + print(f"own loop B=1 pass0: prefill {tp:.1f}s decode {td:.1f}s " + f"{len(out[0]) / td:.1f} tok/s, identical to production: {out[0] == prod[0]}", flush=True) + +for B in Bs: + mx.clear_cache(); mx.reset_peak_memory() + t0 = time.perf_counter() + out, tp, td, steps = batched_greedy(mis[:B]) + wall = time.perf_counter() - t0 + same = [out[b] == prod[b] for b in range(B)] + first_diff = [] + for b in range(B): + k = next((j for j, (a, c) in enumerate(zip(out[b], prod[b])) if a != c), None) + first_diff.append(k if k is not None else (None if len(out[b]) == len(prod[b]) else min(len(out[b]), len(prod[b])))) + ntok = sum(map(len, out)) + print(f"B={B}: wall {wall:.1f}s (prefill {tp:.1f}, decode {td:.1f}, {steps} steps) vs " + f"production {sum(prod_t[:B]):.1f}s -> {sum(prod_t[:B]) / wall:.2f}x; " + f"{ntok / td:.1f} tok/s total; peak {mx.get_peak_memory() / 1e9:.1f} GB; " + f"identical rows {same}; first differing token {first_diff}", flush=True) + for b in range(B): + if not same[b]: + a = vox.proc.decode(out[b], skip_special_tokens=True).split() + c = vox.proc.decode(prod[b], skip_special_tokens=True).split() + print(f" row {b}: words batched {len(a)} vs production {len(c)}", flush=True) diff --git a/docs/scripts/bitmatrix.py b/docs/scripts/bitmatrix.py new file mode 100644 index 00000000..79f7024a --- /dev/null +++ b/docs/scripts/bitmatrix.py @@ -0,0 +1,27 @@ +"""Speed + fidelity across bit widths of the mini build. + +Transcribes one 150-s cut (/tmp/ab_150.wav, a scratch file the user provides) +with the bf16 release and locally quantised 8/6/4-bit builds and reports +realtime factor, peak memory and word agreement with bf16. The hypothesis +under test, raised when the builds were made: MLX's 6-bit matmul kernels may +be slower than the 4- and 8-bit ones, so 6 bit could lose in speed what it +saves in size. Feeds the bit-width tables in docs/voxtral-quantisation.md; +docs/voxtral-benchmarks.md §9 lists it. +""" +import sys, time, difflib, soundfile as sf, mlx.core as mx +import pathlib +REPO = pathlib.Path(__file__).resolve().parents[2] # docs/scripts/x.py -> repo root +sys.path.insert(0, str(REPO)) +from noScribe.voxtral_engine import _Voxtral + +a,_ = sf.read('/tmp/ab_150.wav', dtype='float32') +ref=None +for name, path in (("bf16","models/voxtral-mini"), ("8-bit","/tmp/mini8"), + ("6-bit","/tmp/mini6"), ("4-bit","/tmp/mini4")): + vox=_Voxtral(path); mx.reset_peak_memory() + t0=time.time(); t=vox.transcribe_array(a,'de',max_new_tokens=3512); el=time.time()-t0 + if ref is None: ref=t; sim=100.0 + else: sim=difflib.SequenceMatcher(None, t.split(), ref.split()).ratio()*100 + print(f"### {name:5s} {len(t.split()):4d} W | {150/el:5.2f}x | Peak {mx.get_peak_memory()/1e9:5.1f} GB | " + f"word-identical to bf16 {sim:6.2f}%", flush=True) + del vox; mx.clear_cache() diff --git a/docs/scripts/bootstrap_cer.py b/docs/scripts/bootstrap_cer.py new file mode 100644 index 00000000..d70de038 --- /dev/null +++ b/docs/scripts/bootstrap_cer.py @@ -0,0 +1,191 @@ +"""Bootstrap confidence intervals for WER and CER on the reference passage. + +The reference is 422 words / 2034 characters, so a single character is 0.049 CER +points. That makes it easy to over-read a 0.05-point difference between builds. +This script replaces the rule of thumb ("below ~0.15 points is noise") with a +measured interval, and reports the paired difference between two builds -- +which is the quantity that actually decides whether a build change did anything. + +Input is the transcripts that docs/scripts/wer.py leaves in /tmp/wer_.txt. + + python docs/scripts/wer.py [build ...] + python docs/scripts/bootstrap_cer.py [build ...] + +Method: align hypothesis to reference with a Levenshtein DP that keeps +backpointers, attribute every edit to the reference position it lands on, bucket +those positions into blocks, then resample blocks with replacement. Blocks +rather than single tokens because errors are correlated within a phrase -- a +token-level bootstrap would understate the interval. +""" +import random +import sys +import pathlib + +REPO = pathlib.Path(__file__).resolve().parents[2] +sys.path.insert(0, str(REPO / "docs" / "scripts")) +from wer import norm, OVERLAP # same normalisation as the scoring script + +RESAMPLES = 10000 +WORD_BLOCK = 20 # reference words per bootstrap block +CHAR_BLOCK = 100 # reference characters per bootstrap block +SEED = 20260727 # fixed: the interval must not move between runs + + +def align_costs(ref, hyp): + """Edit count attributed to each reference index. + + Returns a list `cost` of len(ref): cost[i] is how many edits the cheapest + alignment charges at reference position i. Insertions are charged to the + reference position they precede, so every edit lands somewhere in the + reference and the totals still sum to the edit distance. + """ + n, m = len(ref), len(hyp) + # d[i][j] = distance; move[i][j] in {"eq","sub","del","ins"} + d = [[0] * (m + 1) for _ in range(n + 1)] + mv = [[""] * (m + 1) for _ in range(n + 1)] + for i in range(1, n + 1): + d[i][0] = i + mv[i][0] = "del" + for j in range(1, m + 1): + d[0][j] = j + mv[0][j] = "ins" + for i in range(1, n + 1): + ri = ref[i - 1] + di, dp = d[i], d[i - 1] + mi = mv[i] + for j in range(1, m + 1): + if ri == hyp[j - 1]: + di[j] = dp[j - 1] + mi[j] = "eq" + else: + sub, dele, ins = dp[j - 1], dp[j], di[j - 1] + best = min(sub, dele, ins) + di[j] = best + 1 + mi[j] = "sub" if best == sub else ("del" if best == dele else "ins") + cost = [0] * max(1, n) + i, j = n, m + while i > 0 or j > 0: + step = mv[i][j] + if step == "eq": + i, j = i - 1, j - 1 + elif step == "sub": + cost[i - 1] += 1 + i, j = i - 1, j - 1 + elif step == "del": + cost[i - 1] += 1 + i -= 1 + else: # insertion: charge it to the reference position it sits before + cost[min(i, n - 1)] += 1 + j -= 1 + return cost + + +def blocks(cost, size): + """(errors, reference units) per block.""" + out = [] + for s in range(0, len(cost), size): + chunk = cost[s:s + size] + out.append((sum(chunk), len(chunk))) + return out + + +def rate(blks): + """The observed error rate over the unsampled blocks, in percent. + + This is the point estimate. It is NOT the middle of the bootstrap interval: + the resampling distribution can sit off-centre, so the midpoint is a + different quantity and would disagree with the per-build numbers. + """ + e = sum(x for x, _ in blks) + u = sum(x for _, x in blks) + return e / u * 100 if u else 0.0 + + +def boot(blks, rng): + """Bootstrap distribution of the error rate over blocks.""" + n = len(blks) + dist = [] + for _ in range(RESAMPLES): + e = u = 0 + for _ in range(n): + be, bu = blks[rng.randrange(n)] + e += be + u += bu + dist.append(e / u * 100 if u else 0.0) + dist.sort() + return dist + + +def ci(dist): + lo = dist[int(0.025 * len(dist))] + hi = dist[int(0.975 * len(dist)) - 1] + return lo, hi + + +def paired(blks_a, blks_b, rng): + """Bootstrap the DIFFERENCE a-b on the same resampled blocks. + + Paired, because both builds are scored on the same passage: the shared + difficulty of the material cancels and the interval is far tighter than + comparing two independent intervals. + """ + n = len(blks_a) + dist = [] + for _ in range(RESAMPLES): + ea = ua = eb = ub = 0 + for _ in range(n): + k = rng.randrange(n) + ea += blks_a[k][0]; ua += blks_a[k][1] + eb += blks_b[k][0]; ub += blks_b[k][1] + dist.append((ea / ua - eb / ub) * 100 if ua and ub else 0.0) + dist.sort() + return dist + + +raw = open(sys.argv[1], encoding="utf-8").read() +ref_words = norm(OVERLAP.sub(" ", raw)) +ref_chars = list("".join(ref_words)) +names = [p.rstrip("/").split("/")[-1] for p in sys.argv[2:]] + +print(f"# Reference {len(ref_words)} words / {len(ref_chars)} chars") +print(f"# {RESAMPLES} resamples, blocks: {WORD_BLOCK} words / {CHAR_BLOCK} chars, seed {SEED}\n") + +table = {} +for name in names: + path = f"/tmp/wer_{name}.txt" + try: + hyp_words = norm(open(path, encoding="utf-8").read()) + except FileNotFoundError: + print(f"!! {path} missing -- run docs/scripts/wer.py first") + continue + hyp_chars = list("".join(hyp_words)) + wb = blocks(align_costs(ref_words, hyp_words), WORD_BLOCK) + cb = blocks(align_costs(ref_chars, hyp_chars), CHAR_BLOCK) + table[name] = (wb, cb) + rng = random.Random(SEED) + wd, cd = boot(wb, rng), boot(cb, rng) + wl, wh = ci(wd) + cl, ch = ci(cd) + print(f"{name:32s} WER {rate(wb):5.2f}% [{wl:5.2f}, {wh:5.2f}]" + f" CER {rate(cb):5.2f}% [{cl:5.2f}, {ch:5.2f}]") + +if len(table) > 1: + print("\nPaired differences (95% interval; if it contains 0, the " + "difference is not demonstrable):") + keys = list(table) + for a in range(len(keys)): + for b in range(a + 1, len(keys)): + ka, kb = keys[a], keys[b] + rng = random.Random(SEED) + wd = paired(table[ka][0], table[kb][0], rng) + cd = paired(table[ka][1], table[kb][1], rng) + wl, wh = ci(wd) + cl, ch = ci(cd) + wsig = " " if wl <= 0 <= wh else "*" + csig = " " if cl <= 0 <= ch else "*" + print(f" {ka} - {kb}") + dw = rate(table[ka][0]) - rate(table[kb][0]) + dc = rate(table[ka][1]) - rate(table[kb][1]) + print(f" dWER {dw:+5.2f} [{wl:+5.2f}, {wh:+5.2f}] {wsig}" + f" dCER {dc:+5.2f} [{cl:+5.2f}, {ch:+5.2f}] {csig}") + print("\n* = interval excludes 0") diff --git a/docs/scripts/build_reference.py b/docs/scripts/build_reference.py new file mode 100644 index 00000000..4b0de4f8 --- /dev/null +++ b/docs/scripts/build_reference.py @@ -0,0 +1,118 @@ +"""Prepare a passage for hand correction. + +Produces three files following the convention of Audiotest2/referenz/: + + .wav the passage, through the real production path + (noScribe/audio/convert.py), i.e. exactly what the + engine will later get to hear + _ENTWURF.txt raw transcript of the current build, one line, as the + starting point for the correction (ENTWURF = draft) + _PEGEL.txt per-second level and the quiet stretches derived from + it -- so that while correcting it is clear where + listening closely pays off, and so that those stretches + can later be scored separately (PEGEL = level) + +What the script CANNOT do: correct. That takes ears. The draft is the start of +the work, not the result. + +Convention of the reference (see docs/scripts/wer.py): + //text// spoken simultaneously by the second voice; a transcript that + leaves it out is not wrong and is counted separately + + python docs/scripts/build_reference.py [build] +""" +import pathlib +import sys + +import numpy as np +import soundfile as sf + +REPO = pathlib.Path(__file__).resolve().parents[2] +sys.path.insert(0, str(REPO)) +sys.path.insert(0, str(REPO / "docs" / "scripts")) +from find_passage import production_wav, SR + +QUIET_REL_DB = 10.0 # this far below the loud level counts as a "quiet stretch" +MIN_QUIET_SEC = 2 # shorter dips are breaths, not passages + + +def level_profile(seg): + n = len(seg) // SR + r = seg[:n * SR].reshape(n, SR).astype(np.float64) + return 20 * np.log10(np.sqrt((r ** 2).mean(axis=1)) + 1e-12) + + +def quiet_runs(db, floor, loud): + """Contiguous seconds that are speech but markedly quieter.""" + speech = db > floor + 12 + quiet = speech & (db < loud - QUIET_REL_DB) + runs, start = [], None + for i, q in enumerate(list(quiet) + [False]): + if q and start is None: + start = i + elif not q and start is not None: + if i - start >= MIN_QUIET_SEC: + runs.append((start, i)) + start = None + return runs + + +def main(): + src, t0, t1, base = sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), sys.argv[4] + build = sys.argv[5] if len(sys.argv) > 5 else "models/voxtral-mini-8bit" + + wav = production_wav(src) + x, sr = sf.read(wav, dtype="float32") + assert sr == SR, sr + seg = np.ascontiguousarray(x[t0 * SR:t1 * SR]) + + out = pathlib.Path(base) + out.parent.mkdir(parents=True, exist_ok=True) + sf.write(out.with_suffix(".wav"), seg, SR, subtype="PCM_16") + + db = level_profile(seg) + # The noise floor has to come from the WHOLE recording, not from the + # passage: a speech-dense passage has hardly any silence, so its own 10th + # percentile sits in the middle of the quiet speech. With the passage + # percentile as the floor, exactly what matters here would drop out of the + # speech mask -- the spread would then look much smaller than it is. + floor = float(np.percentile(level_profile(x), 10)) + speech = db > floor + 12 + loud = float(np.percentile(db[speech], 90)) if speech.any() else floor + runs = quiet_runs(db, floor, loud) + spread = (loud - float(np.percentile(db[speech], 10))) if speech.any() else 0.0 + + from noScribe.voxtral_engine import _Voxtral + vox = _Voxtral(build) + text = vox.transcribe_array(seg, "de", + max_new_tokens=int((t1 - t0) * 20) + 512).strip() + open(f"{base}_ENTWURF.txt", "w", encoding="utf-8").write(text) + + with open(f"{base}_PEGEL.txt", "w", encoding="utf-8") as f: + f.write(f"# {pathlib.Path(src).name} {t0}-{t1}s, {t1-t0}s, " + f"draft {len(text.split())} words, build {build}\n") + f.write(f"# noise floor {floor:.1f} dB, loud speech level (p90) {loud:.1f} dB, " + f"spread p90-p10 {spread:.1f} dB, speech share {speech.mean()*100:.0f}%\n") + f.write(f"# Quiet stretches: speech more than {QUIET_REL_DB:.0f} dB below p90, " + f"at least {MIN_QUIET_SEC} s in a row. " + f"Seconds relative to the start of the passage.\n#\n") + f.write(f"# {len(runs)} quiet stretches, " + f"{sum(b-a for a, b in runs)} s in total " + f"({sum(b-a for a, b in runs)/max(1,len(db))*100:.0f}% of the passage):\n") + for a, b in runs: + f.write(f"# {a//60:02d}:{a%60:02d} - {b//60:02d}:{b%60:02d} " + f"({b-a:3d}s, {db[a:b].mean():.1f} dB, " + f"{loud-db[a:b].mean():.1f} dB below loud)\n") + f.write("#\n# second\tdB\tspeech\tquiet\n") + for i, v in enumerate(db): + q = any(a <= i < b for a, b in runs) + f.write(f"{i}\t{v:.1f}\t{int(speech[i])}\t{int(q)}\n") + + print(f"{out.with_suffix('.wav')} {t1-t0}s") + print(f"{base}_ENTWURF.txt {len(text.split())} words") + print(f"{base}_PEGEL.txt spread {spread:.1f} dB, " + f"{len(runs)} quiet stretches / {sum(b-a for a, b in runs)}s") + + +if __name__ == "__main__": + main() diff --git a/docs/scripts/cli_check.py b/docs/scripts/cli_check.py new file mode 100644 index 00000000..6e90bddd --- /dev/null +++ b/docs/scripts/cli_check.py @@ -0,0 +1,69 @@ +"""Assertions on the three transcript files a noScribe CLI smoke run leaves behind. + +Reads /tmp/out_mapped.vtt, /tmp/out_unmapped.html and /tmp/out_short.txt -- +scratch files the user produces with three CLI invocations -- and checks the +output-format and speaker-name plumbing: mapped names reach the VTT voices, +S00/S01 survive without --speaker-names, no HTML leaks into the txt path. A +format check, not a measurement; listed under docs/voxtral-benchmarks.md §9. +""" +import os, re, sys + +def rd(p): + return open(p, encoding="utf-8").read() if os.path.exists(p) else None + +fails = [] + +# --- A: mapped VTT --- +vtt = rd("/tmp/out_mapped.vtt") +print("=== A: /tmp/out_mapped.vtt ===") +if not vtt: + fails.append("A: vtt missing") +else: + cues = re.findall(r"\d\d:\d\d:\d\d\.\d\d\d --> \d\d:\d\d:\d\d\.\d\d\d\n(.*?)(?:\n\n|\Z)", vtt, re.S) + voices = re.findall(r"]+)>", vtt) + texts = [re.sub(r"]+>", "", c).strip() for c in cues] + lens = [len(t) for t in texts if t] + print(f" starts WEBVTT: {vtt.startswith('WEBVTT')} cues: {len(cues)} voices: {sorted(set(voices))}") + print(f" cue text len: max={max(lens) if lens else 0} avg={sum(lens)//len(lens) if lens else 0}") + for t in texts[:3]: + print(f" cue: {t[:70]!r}") + if not vtt.startswith("WEBVTT"): fails.append("A: no WEBVTT header") + if len(cues) < 3: fails.append(f"A: too few cues ({len(cues)})") + if not any(v in ("Mona", "Lena") for v in voices): + fails.append(f"A: mapped names not in voices {set(voices)}") + if any(v in ("S00", "S01") for v in voices): + fails.append(f"A: unmapped S00/S01 leaked into voices {set(voices)}") + if lens and max(lens) > 300: + fails.append(f"A: cue too large ({max(lens)} chars) -- not subtitle-sized") + +# --- B: unmapped HTML (old S00/S01 behavior) --- +htm = rd("/tmp/out_unmapped.html") +print("\n=== B: /tmp/out_unmapped.html ===") +if not htm: + fails.append("B: html missing") +else: + anchors = re.findall(r'name="ts_[\d.]+_[\d.]+_([^"]*)"', htm) + print(f"

tags: {htm.count(' paragraphs") + if len(anchors) < 3: fails.append(f"B: too few ts_ anchors ({len(anchors)})") + if not any(re.fullmatch(r"S\d\d", s) for s in body_speakers): + fails.append(f"B: expected S00/S01 labels (old behavior), got {body_speakers}") + if any(n in htm for n in ("Mona", "Lena")): + fails.append("B: real names present despite no --speaker-names") + +# --- C: short-path TXT --- +txt = rd("/tmp/out_short.txt") +print("\n=== C: /tmp/out_short.txt ===") +if not txt: + fails.append("C: txt missing") +else: + words = len(txt.split()) + tags = len(re.findall(r"<[a-zA-Z/][^>]*>", txt)) + print(f" words: {words} html-tags: {tags}") + print(f" head: {txt[:120]!r}") + if words < 50: fails.append(f"C: too little text ({words} words)") + if tags > 0: fails.append(f"C: raw HTML tags leaked into txt ({tags})") + +print("\n===== FORMAT/SPEAKER RESULT:", "ALL OK" if not fails else f"FAILS: {fails}", "=====") +sys.exit(0 if not fails else 1) diff --git a/docs/scripts/decisive.py b/docs/scripts/decisive.py new file mode 100644 index 00000000..44e41a9f --- /dev/null +++ b/docs/scripts/decisive.py @@ -0,0 +1,41 @@ +"""Isolate repetition_penalty and pass length at realistic length. + +Transcribes the first 600 s of a recording (/tmp/first600.wav, a scratch cut +the user provides) with the mini build three ways -- 600 s at rep=1.2 (the +library default at the time), 600 s at rep=1.0, 2x300 s at rep=1.0 -- and +counts commas and sentence ends per 100 words against the Whisper reference +(10.62 / 7.10). Showed that the penalty, not the pass length, strips the +punctuation; feeds the rep=1.0 default and RETRY_REPETITION_PENALTIES in +noScribe/voxtral_engine.py, docs/voxtral-benchmarks.md §1 and §6. +""" +import sys, re, soundfile as sf +from pathlib import Path +sys.path.insert(0, str(Path(__file__).resolve().parents[2])) +from noScribe.voxtral_engine import _Voxtral, SAMPLE_RATE +audio,_ = sf.read('/tmp/first600.wav', dtype='float32') +vox = _Voxtral('models/voxtral-mini') + +def one(a, lang, rep, maxnew): + inp = vox.proc.apply_transcrition_request(audio=a, language=lang, sampling_rate=SAMPLE_RATE) + mi = {"input_ids": inp.input_ids, "input_features": inp.input_features} + if getattr(inp,"attention_mask",None) is not None: mi["attention_mask"]=inp.attention_mask + out = vox.model.generate(**mi, max_new_tokens=maxnew, temperature=0.0, repetition_penalty=rep) + return vox.proc.decode(out[0, inp.input_ids.shape[1]:], skip_special_tokens=True).strip() + +def stats(t): + w=len(t.split()); return w, t.count(',')/w*100, len(re.findall(r'[.!?]',t))/w*100 + +runs = {} +runs['a 600s rep=1.2 (default)'] = one(audio,'de',1.2,4096) +print('a done', flush=True) +runs['b 600s rep=1.0'] = one(audio,'de',1.0,4096) +print('b done', flush=True) +half = len(audio)//2 +runs['c 2x300s rep=1.0'] = one(audio[:half],'de',1.0,4096)+' '+one(audio[half:],'de',1.0,4096) +print('c done', flush=True) + +print("\n=== WHISPER reference: commas 10.62 | sentence ends 7.10 (per 100 w) ===") +for k,t in runs.items(): + w,c,p = stats(t) + print(f"{k:26s} {w:5d} w | commas {c:5.2f} | sentence ends {p:5.2f}") +import json; json.dump(runs, open('/tmp/decisive.json','w'), ensure_ascii=False, indent=1) diff --git a/docs/scripts/encoder_diff.py b/docs/scripts/encoder_diff.py new file mode 100644 index 00000000..f65b681e --- /dev/null +++ b/docs/scripts/encoder_diff.py @@ -0,0 +1,195 @@ +"""Reference-free paired diff between two builds over hours of real audio. + +The question is whether the audio encoder's precision changes what the model +HEARS or only how it SPELLS. A hand-corrected reference answers that on two +minutes of audio; this answers it on hours, without needing one. + +Method: transcribe the same audio with both builds in identical fixed windows, +then classify every difference between the two transcripts by how similar the +differing spans are at character level: + + orthographic short span, characters nearly the same ("Vita Flor"/"VitaFlor") + lexical characters differ (a different word was heard) + cascade long span -- under greedy decoding one flipped token makes the + text diverge for a while; that is not an encoder effect and is + counted separately rather than as a mishearing + +If the encoder's bit width only produces orthographic differences, its precision +is not buying recognition quality. + + python docs/scripts/encoder_diff.py [build ...] \ + --audio [file ...] + +Every build is compared against the first one. Each build transcribes each file +exactly once, so adding a third build costs one more pass, not three. + +Windows are cut at a fixed length so both builds see byte-identical input; no +pause-alignment, no retry ladder, nothing that could differ between runs. +""" +import os +import sys +import json +import pathlib +import tempfile +import time +from difflib import SequenceMatcher + +import numpy as np +import soundfile as sf +import mlx.core as mx + +REPO = pathlib.Path(__file__).resolve().parents[2] +sys.path.insert(0, str(REPO)) +sys.path.insert(0, str(REPO / "docs" / "scripts")) +from noScribe.voxtral_engine import _Voxtral +from wer import norm + +WINDOW_SEC = 600 # fits mini8 on 32 GB with room to spare (~10 GB peak) +CASCADE_WORDS = 8 # longer differing spans are greedy divergence, not hearing +ORTHO_SIM = 0.80 # character similarity at or above this = same word, spelled differently +LEXICAL_SIM = 0.50 # below this = a different word + + +def load_16k(path): + """Mono 16 kHz float32, the way the engine feeds the model.""" + import av + container = av.open(str(path)) + stream = next(s for s in container.streams if s.type == "audio") + resampler = av.AudioResampler(format="fltp", layout="mono", rate=16000) + parts = [] + for frame in container.decode(stream): + for out in resampler.resample(frame): + parts.append(out.to_ndarray().reshape(-1)) + for out in resampler.resample(None): + parts.append(out.to_ndarray().reshape(-1)) + return np.concatenate(parts).astype("float32") if parts else np.zeros(0, "float32") + + +def transcribe_windows(build, audio, cache): + """Transcribe fixed windows, caching per (build, window) so a re-run is free.""" + key = str(build) + if key in cache: + return cache[key] + vox = _Voxtral(build) + out = [] + step = WINDOW_SEC * 16000 + for w, start in enumerate(range(0, len(audio), step)): + seg = audio[start:start + step] + if len(seg) < 16000: # ignore a sub-second tail + continue + dur = len(seg) / 16000 + t0 = time.time() + out.append(vox.transcribe_array(seg, "de", max_new_tokens=int(dur * 20) + 512)) + print(f" {key.split('/')[-1]:28s} Fenster {w + 1} " + f"({dur:.0f}s) in {time.time() - t0:.0f}s", flush=True) + del vox + mx.clear_cache() + cache[key] = out + return out + + +def classify(a_span, b_span): + n = max(len(a_span), len(b_span)) + if n > CASCADE_WORDS: + return "cascade", 0.0 + if not a_span or not b_span: + # One side emitted nothing. That is not a mishearing of some other + # word, it is an omission -- a different failure, and one that matters: + # a build that drops words is worse even if it never misspells. + return "omission", 0.0 + sim = SequenceMatcher(None, "".join(a_span), "".join(b_span)).ratio() + if sim >= ORTHO_SIM: + return "orthographic", sim + if sim < LEXICAL_SIM: + return "lexical", sim + return "ambiguous", sim + + +def compare(text_a, text_b): + a, b = norm(text_a), norm(text_b) + sm = SequenceMatcher(None, a, b, autojunk=False) + events = [] + for tag, i1, i2, j1, j2 in sm.get_opcodes(): + if tag == "equal": + continue + a_span, b_span = a[i1:i2], b[j1:j2] + kind, sim = classify(a_span, b_span) + events.append({"kind": kind, "sim": round(sim, 3), + "a": " ".join(a_span), "b": " ".join(b_span)}) + return events, len(a), len(b) + + +split = sys.argv.index("--audio") +builds = sys.argv[1:split] +files = sys.argv[split + 1:] +short = [b.rstrip("/").split("/")[-1] for b in builds] +ref_build, ref_name = builds[0], short[0] + +# results[other_build] = (events, words_ref, words_other) +results = {b: ([], 0, 0) for b in builds[1:]} +for path in files: + print(f"\n== {path}", flush=True) + audio = load_16k(path) + print(f" {len(audio) / 16000 / 60:.1f} min, " + f"{-(-len(audio) // (WINDOW_SEC * 16000))} Fenster", flush=True) + cache = {} + wins = {b: transcribe_windows(b, audio, cache) for b in builds} + for b in builds[1:]: + ev, wr, wo = results[b] + for ta, tb in zip(wins[ref_build], wins[b]): + e, na, nb = compare(ta, tb) + ev += e + wr += na + wo += nb + results[b] = (ev, wr, wo) + +ACOUSTIC = ("lexical", "omission") +dump = {"reference": ref_name, "files": files, "window_sec": WINDOW_SEC, + "thresholds": {"cascade_words": CASCADE_WORDS, "ortho_sim": ORTHO_SIM, + "lexical_sim": LEXICAL_SIM}, + "comparisons": {}} + +for b in builds[1:]: + events, words_r, words_o = results[b] + name_b = b.rstrip("/").split("/")[-1] + counts = {} + for e in events: + counts[e["kind"]] = counts.get(e["kind"], 0) + 1 + decisive = sum(counts.get(k, 0) for k in ("orthographic", "ambiguous") + ACOUSTIC) + acoustic = sum(counts.get(k, 0) for k in ACOUSTIC) + + print(f"\n{'=' * 72}") + print(f"{ref_name} vs {name_b}") + print(f"{words_r} / {words_o} words, {len(events)} differing spots\n") + for kind in ("orthographic", "ambiguous", "lexical", "omission", "cascade"): + n = counts.get(kind, 0) + share = (f"{n / decisive * 100:5.1f}% of the classifiable" + if decisive and kind != "cascade" else "") + print(f" {kind:14s} {n:5d} {n / max(1, words_r) * 1000:6.2f} per 1000 words {share}") + if decisive: + print(f"\n -> acoustic (lexical+omission): {acoustic}/{decisive} = " + f"{acoustic / decisive * 100:.1f}% of the classifiable spots, " + f"{acoustic / max(1, words_r) * 1000:.2f} per 1000 words") + print(" If this share is small, the encoder precision only changes the " + "spelling, not what was heard.") + + print("\n Beispiele je Klasse (bis zu 6):") + for kind in ("lexical", "omission", "ambiguous", "orthographic"): + ex = [e for e in events if e["kind"] == kind][:6] + if ex: + print(f" -- {kind}") + for e in ex: + print(f" sim {e['sim']:.2f} {ref_name[-5:]}: {e['a'][:55]!r} " + f"{name_b[-5:]}: {e['b'][:55]!r}") + + dump["comparisons"][name_b] = {"words_ref": words_r, "words_other": words_o, + "counts": counts, "events": events} + +# NOT into the repository: the event list quotes the audio verbatim, and the +# audio is real interview material. Only the aggregate counts printed above are +# safe to copy into docs/. Override the location with NOSCRIBE_MESS_DIR. +out_dir = pathlib.Path(os.environ.get("NOSCRIBE_MESS_DIR", tempfile.gettempdir())) +out_dir.mkdir(parents=True, exist_ok=True) +out = out_dir / f"encoder_diff_{ref_name}.json" +out.write_text(json.dumps(dump, ensure_ascii=False, indent=1)) +print(f"\nRohdaten (ausserhalb des Repos): {out}") diff --git a/docs/scripts/engines/README.md b/docs/scripts/engines/README.md new file mode 100644 index 00000000..978deea9 --- /dev/null +++ b/docs/scripts/engines/README.md @@ -0,0 +1,75 @@ +# Scoring other people's ASR engines + +Six throwaway-shaped scripts that turned out to be worth keeping, because the +question "is there something better than Voxtral for German interviews yet?" +keeps coming back and rebuilding the harness each time is the expensive part. + +## What they have in common + +Each script scores one foreign engine on the same three yardsticks the Voxtral +builds are measured against, and reuses `norm()` and `wer()` from +`../wer.py` **verbatim** — pulled in by cutting that file before `main()` and +dropping its Voxtral import, so a comparison never silently changes the metric: + +| yardstick | what it is | +|---|---| +| `Audiotest2/referenz/hart_780-900` | 422 words, 2 min, crosstalk and product names | +| `Audiotest2/referenz/zoom_9890-10190` | 859 words, 5 min, video call | +| FLEURS German, 100 recordings | the external, clean, read-aloud yardstick | + +The audio is private and lives outside the repository. + +The results and what they mean are in +[`../../other-asr-engines.md`](../../other-asr-engines.md). The short version, +confirmed six times over now: **FLEURS ranks +these models in an order that real German conversation does not.** Use these +scripts to find candidates, never to decide. + +## The scripts + +| script | model | needs | +|---|---|---| +| `parakeet_wer.py` | `nvidia/parakeet-tdt-0.6b-v3` | `pip install parakeet-mlx` | +| `nemotron_asr_wer.py` | `nvidia/nemotron-3.5-asr-streaming-0.6b` | mlx-audio from git main, in a throwaway venv | +| `qwen_asr_wer.py` | `Qwen/Qwen3-ASR-1.7B-hf` | nothing | +| `vibevoice_wer.py` | `microsoft/VibeVoice-ASR-HF` | nothing | +| `cohere_asr_wer.py` | `CohereLabs/cohere-transcribe-03-2026` | nothing (gated repo — accept on the model page first) | +| `transcribe_cpp_wer.py` | Voxtral Q8_0 GGUF via transcribe.cpp | the `transcribe` CLI, built from source | + +"nothing" means transformers already supports the architecture natively, so the +venv does not have to be touched at all. `parakeet-mlx` is the one exception: it +adds only `dacite` on top of what is installed and its `mlx>=0.22.1` floor is +satisfied by the pinned 0.32.1, so it can be installed and removed without +disturbing the Voxtral stack. Verify with `pip freeze` before and after — it +should come back byte-identical. + +**None of these belong in `environments/`.** They are for answering a question, +not for running noScribe. + +## Usage + +```bash +venv/bin/python3 docs/scripts/engines/