Run server-engine file transcriptions through faster-whisper's batched pipeline with VAD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bbcc123124
commit
dc64bb8198
5 files changed
+200
-7
No files matched your search
+36
-7
@@ -9,7 +9,8 @@ stdio — one JSON object per line, no framing beyond that:
|
||||
|
||||
stdin -> {"type": "transcribe", "id": "1", "model": "tiny.en",
|
||||
"audio_path": "/tmp/...", "language": "en" | null,
|
||||
"task": "transcribe" | "translate"}
|
||||
"task": "transcribe" | "translate",
|
||||
"mode": "file" | "live"} # optional, default "file"
|
||||
stdout <- {"type": "segment", "id": "1", "start": 0.0, "end": 4.2,
|
||||
"text": " Hello world."} # 0..n of these
|
||||
stdout <- {"type": "done", "id": "1", "cancelled": false}
|
||||
@@ -24,6 +25,17 @@ container decoding happens here. Requests are processed strictly one at a
|
||||
time; a reader thread keeps consuming stdin meanwhile so `cancel` can
|
||||
interrupt the segment loop of the request in flight.
|
||||
|
||||
Decoding strategy by mode (Decisions.md D-WS5a):
|
||||
"file" faster-whisper's BatchedInferencePipeline with its Silero VAD
|
||||
(vad_filter=True): silence is dropped, speech is cut into <= 30 s
|
||||
chunks and BATCH_SIZE chunks decode per forward pass. Segments come
|
||||
out in bursts (one burst per batch), so a cancel takes effect at the
|
||||
next burst boundary; the Bun side's heartbeat (D-021) covers the
|
||||
longer quiet stretch before the first burst.
|
||||
"live" the plain sequential WhisperModel.transcribe: server-live passes
|
||||
are short (<= 29 s, often a few seconds), so batching has nothing to
|
||||
batch and VAD only adds latency and can clip a short utterance.
|
||||
|
||||
The last-used model stays loaded between requests (WorkBreakdown M5 warm-up:
|
||||
never reload weights per request) and is replaced — not accumulated — when a
|
||||
request names a different model, bounding memory to one model.
|
||||
@@ -47,6 +59,7 @@ import sys
|
||||
import threading
|
||||
|
||||
SAMPLE_RATE = 16000
|
||||
BATCH_SIZE = 8 # D-WS5a: VAD chunks decoded per batched forward pass
|
||||
|
||||
|
||||
def emit(message):
|
||||
@@ -101,6 +114,7 @@ def main():
|
||||
|
||||
cached_name = None
|
||||
cached_model = None
|
||||
cached_pipeline = None # BatchedInferencePipeline wrapping cached_model
|
||||
|
||||
emit({"type": "ready", "version": getattr(faster_whisper, "__version__", "?")})
|
||||
|
||||
@@ -115,26 +129,41 @@ def main():
|
||||
audio_path = request["audio_path"]
|
||||
language = request.get("language") or None
|
||||
task = request.get("task") or "transcribe"
|
||||
mode = request.get("mode") or "file"
|
||||
|
||||
if model_name != cached_name:
|
||||
cached_model = None # release before loading the replacement
|
||||
# Release both before loading the replacement: one model resident.
|
||||
cached_model = cached_pipeline = None
|
||||
cached_name = None
|
||||
cached_model = faster_whisper.WhisperModel(
|
||||
model_name,
|
||||
device=device,
|
||||
compute_type=compute_type,
|
||||
download_root=download_root,
|
||||
)
|
||||
# A thin wrapper (no weights of its own) — shares cached_model.
|
||||
cached_pipeline = faster_whisper.BatchedInferencePipeline(cached_model)
|
||||
cached_name = model_name
|
||||
|
||||
audio = np.fromfile(audio_path, dtype=np.float32)
|
||||
segments, _info = cached_model.transcribe(
|
||||
audio, language=language, task=task
|
||||
)
|
||||
if mode == "live":
|
||||
segments, _info = cached_model.transcribe(
|
||||
audio, language=language, task=task
|
||||
)
|
||||
else:
|
||||
segments, _info = cached_pipeline.transcribe(
|
||||
audio,
|
||||
language=language,
|
||||
task=task,
|
||||
vad_filter=True,
|
||||
batch_size=BATCH_SIZE,
|
||||
)
|
||||
|
||||
was_cancelled = False
|
||||
# `segments` is a generator: inference happens during iteration,
|
||||
# so each segment can stream out (and cancel can cut in) while the
|
||||
# next one is still being decoded.
|
||||
# so segments stream out (and cancel can cut in) while later ones
|
||||
# are still being decoded — per segment in live mode, per batch of
|
||||
# BATCH_SIZE speech chunks in file mode (D-WS5a).
|
||||
for segment in segments:
|
||||
if request_id in cancelled:
|
||||
was_cancelled = True
|
||||
|
||||
Reference in new issue
Block a user