Instructions to use Scicom-intl/semantic-vad-eot-whisper-small with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Scicom-intl/semantic-vad-eot-whisper-small with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("audio-classification", model="Scicom-intl/semantic-vad-eot-whisper-small")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Scicom-intl/semantic-vad-eot-whisper-small", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Semantic VAD — Whisper-small end-of-turn detector (audio only)
The whisper-small sibling of
Scicom-intl/semantic-vad-eot-whisper-tiny and
…-whisper-base: same recipe, same input contract,
same data, an 88 M-parameter encoder. Given the last 8 seconds of a caller's 16 kHz audio it returns p(end of turn) —
finished speaking vs paused mid-sentence — with no transcript.
88 M parameters · int8 ONNX 95 MB · the most accurate of the three (validation AUC 0.894 vs 0.877 base / 0.859
tiny; offline 0.86 / 0.88 / 0.99 at 0 / 0.2 / 0.6 s into a pause). CPU cost on one thread is ≈ 175–200 ms per
prediction, 78 ms at four threads and 50 ms at eight (measured on a busy 164-core box), so it is the choice for nodes
with cores to spare or for a GPU-served, batched endpoint (STT-API's RemoteEoT backend posts the PCM to a URL and is
the hook for that); the tiny model remains the pick for a single CPU thread per agent.
Results
In a real LiveKit Agents 1.8 pipeline (Silero VAD → turn detector → endpointing, no STT, 300 recorded
telephony turns, LiveKit defaults: VAD silence 0.55 s, min_delay 0.5 s, max_delay 3.0 s):
| turn detector | latency p50 / p90 | turns cut off | finished turns on the fast path | AUC (eot vs hold) |
|---|---|---|---|---|
| VAD only | 0.63 / 0.71 s | 14.3 % | – | – |
| smart-turn-v3, threshold 0.5 | 0.65 / 3.04 s | 10.0 % | 82 % | 0.74 |
| tiny variant, threshold 0.5 | 0.64 / 0.74 s | 10.0 % | 95 % | 0.84 |
| base variant, threshold 0.3 | 0.64 / 0.74 s | 9.7 % | 96 % | 0.88 |
| this model, threshold 0.5 | 0.64 / 0.76 s | 10.3 % | 94 % | 0.89 |
| this model, threshold 0.3 | 0.64 / 0.73 s | 10.7 % | 97 % | 0.89 |
The cleanest separation of the family: mean p(eot) 0.83 on finished turns against 0.30 on mid-turn pauses at the moment LiveKit asks (tiny 0.81 / 0.42), so 0.5 is the natural threshold and 0.3 trades one more cut-off in 300 turns for a 97 % fast path. Per call in the pipeline's single CPU thread it took ≈200 ms on a heavily loaded box, inside LiveKit's 1 s prediction budget every time.
Offline, at fixed cut points relative to the start of each pause (AUC, same 300 turns, every pause):
| cut relative to pause start | −0.4 s | −0.2 s | 0.0 s | +0.2 s | +0.6 s |
|---|---|---|---|---|---|
| smart-turn-v3 | 0.60 | 0.62 | 0.63 | 0.65 | 0.69 |
| tiny variant (int8) | 0.72 | 0.78 | 0.80 | 0.81 | 0.97 |
| base variant (int8) | 0.77 | 0.82 | 0.85 | 0.87 | 0.98 |
| this model (int8) | 0.77 | 0.83 | 0.86 | 0.88 | 0.99 |
Score smoothness along a pause is in line with the family (local std 0.034 over 200 ms, threshold flips 1.0 % per 20 ms step; smart-turn-v3 0.124 / 9.8 %).
Under LiveKit's eot-bench harness (100 ms causal grid over
every pause ≥ 0.1 s, threshold × action_delay × timeout policy sweep; same adapter for all audio models,
scored 0.2 s into each pause for the scalar metrics):
| set | model | cutoff @ 300 ms budget | cutoff @ 600 ms | latency @ 5 % cutoff | latency @ 10 % cutoff | AUC |
|---|---|---|---|---|---|---|
| telephony test, 1 000 turns, English (510 eot / 260 hold spans) | this model | 47.3 % | 25.0 % | 1 722 ms | 1 261 ms | 0.84 |
| tiny variant | 50.8 % | 30.0 % | 2 039 ms | 1 529 ms | 0.80 | |
| smart-turn-v3 | 69.6 % | 35.4 % | 2 269 ms | 1 756 ms | 0.66 | |
| VAD baseline | 77.3 % | 41.9 % | 2 020 ms | 1 610 ms | – | |
| telephony test, 1 000 turns, Malay (485 / 169) | this model | 49.1 % | 25.4 % | 1 903 ms | 1 494 ms | 0.86 |
| tiny variant | 55.0 % | 32.5 % | 2 019 ms | 1 423 ms | 0.81 | |
| smart-turn-v3 | 78.1 % | 39.1 % | 2 635 ms | 2 116 ms | 0.63 | |
| VAD baseline | 79.3 % | 46.2 % | 2 540 ms | 2 060 ms | – | |
| telephony, the 300 benchmark turns, English (188 / 105) | this model | 45.7 % | 21.9 % | 1 530 ms | 1 151 ms | 0.83 |
| tiny variant | 58.1 % | 27.6 % | 1 636 ms | 1 198 ms | 0.77 | |
| smart-turn-v3 | 74.3 % | 30.5 % | 1 649 ms | 1 164 ms | 0.64 | |
| VAD baseline | 77.1 % | 31.4 % | 1 800 ms | 1 510 ms | – | |
| telephony, the 300 benchmark turns, Malay (112 / 35) | this model | 48.6 % | 25.7 % | 1 761 ms | 1 238 ms | 0.85 |
| tiny variant | 57.1 % | 31.4 % | 1 843 ms | 1 482 ms | 0.78 | |
| smart-turn-v3 | 68.6 % | 34.3 % | 2 357 ms | 1 603 ms | 0.63 | |
| VAD baseline | 80.0 % | 34.3 % | 2 410 ms | 1 830 ms | – |
Where the tiny model only ties the VAD timer on latency at a 5 % cutoff budget, this one is ahead of it on every operating point of every subset, and ahead of the tiny model everywhere except latency at 10 % on the Malay 1 000-turn set. The harness asks within the first 100–300 ms of every pause, before an audio model has silence evidence; the extra encoder capacity buys the most exactly there (AUC 0.85 vs 0.80 at the pause start). In the LiveKit pipeline, which asks after the VAD's 0.4–0.55 s of silence, the two are one cut-off turn apart.
Compared with other open detectors (eot-bench, 1 000 telephony turns)
Same harness, same 1 000 random private telephony test turns (both language tags pooled), every open audio-native end-of-turn detector we could run locally; cloud services were not run because the call audio may not leave our infrastructure. Third-party models run through eot-bench's own adapters with the language gate widened to Malay; the text detector on transcripts from our Whisper STT (segment timestamps interpolated to words, ~30 % of these short turns have no transcript); ultraVAD without the assistant context it was designed for (this set has none).
| model | cutoff @ 300 ms | cutoff @ 600 ms | latency @ 5 % cutoff | latency @ 10 % cutoff | AUC |
|---|---|---|---|---|---|
| Semantic-VAD whisper-small v6 (this model) | 45.2 % | 24.5 % | 1 839 ms | 1 226 ms | 0.86 |
| Semantic-VAD whisper-base v6 (repo) | 47.3 % | 25.4 % | 1 812 ms | 1 280 ms | 0.85 |
| Semantic-VAD whisper-tiny v6 (repo) | 52.2 % | 30.8 % | 2 042 ms | 1 503 ms | 0.81 |
LiveKit turn-detector v1-mini (audio-only, livekit-local-inference) |
63.6 % | 35.7 % | 2 156 ms | 1 720 ms | 0.74 |
ultraVAD (fixie-ai/ultraVAD, 0.7 B, no text context) |
71.6 % | 39.6 % | 2 212 ms | 1 784 ms | 0.65 |
smart-turn v3.2 (pipecat-ai/smart-turn-v3) |
73.7 % | 36.6 % | 2 296 ms | 1 860 ms | 0.65 |
| LiveKit text turn-detector v0.4.1-intl (on STT transcripts) | – | – | 2 381 ms | 1 894 ms | 0.45 |
smart-turn v2 (pipecat-ai/smart-turn-v2, 95 M wav2vec2) |
74.1 % | 39.6 % | 2 500 ms | 2 000 ms | 0.62 |
| VAD baseline (silence timer) | 78.1 % | 43.6 % | 2 250 ms | 1 770 ms | – |
Files
| file | what |
|---|---|
onnx/model.int8.onnx |
MatMul-only dynamic int8, 95 MB; max abs Δp vs PyTorch 0.10, mean 0.02 |
onnx/model.fp32.onnx |
fp32 export, 350 MB; max abs Δp vs PyTorch 1e-6 |
onnx/export_report.json |
sizes, parity vs PyTorch, latency at export time |
encoder/ |
fine-tuned Whisper-small encoder, HF format (config.json, model.safetensors, bf16, 169 MB) |
eot_head.pt |
{"state_dict": LayerNorm→Linear(768,256)→GELU→Linear(256,1), "pooling": "last5"} |
eot_window.json / preprocessor_config.json |
the input contract: 8 s window, 80 mel bins, 16 kHz, no mel normalisation, mean of the last 5 encoder frames |
training_summary.json |
best step, validation AUC history |
Input: input_features [batch, 80, 800] float32 — Whisper log-mel of the last 8 s of audio, left-padded
with zeros when shorter, do_normalize=False. Output: probability [batch, 1], already through the
sigmoid.
Usage
Identical to the tiny model — substitute the repo id. In short (ONNX, no torch):
import numpy as np, onnxruntime as ort
from huggingface_hub import hf_hub_download
from transformers import WhisperFeatureExtractor
REPO, SR, WINDOW = "Scicom-intl/semantic-vad-eot-whisper-small", 16000, 8 * 16000
opts = ort.SessionOptions(); opts.intra_op_num_threads = 1
sess = ort.InferenceSession(hf_hub_download(REPO, "onnx/model.int8.onnx"), opts, providers=["CPUExecutionProvider"])
fe = WhisperFeatureExtractor(feature_size=80, sampling_rate=SR, chunk_length=8)
def p_end_of_turn(pcm: np.ndarray) -> float:
"""pcm: float32 in [-1, 1] at 16 kHz, the caller's audio up to *now* (any length)."""
pcm = np.asarray(pcm, dtype=np.float32)
if pcm.size and np.abs(pcm).max() > 1.5: # int16-scale samples -> unit float
pcm = pcm / 32768.0
pcm = pcm[-WINDOW:] if len(pcm) >= WINDOW else np.pad(pcm, (WINDOW - len(pcm), 0))
feats = fe([pcm], sampling_rate=SR, return_tensors="np", padding="max_length", max_length=WINDOW,
truncation=True, do_normalize=False)["input_features"].astype(np.float32)
return float(sess.run(None, {"input_features": feats})[0].reshape(-1)[0])
For LiveKit Agents use it as the backend of STT-API's
SemanticVAD through WhisperEoTOnnx from the
Semantic-VAD repo, exactly as on the tiny model's card.
Use p ≥ 0.5 (measured operating point above; 0.3 for a higher fast-path share). The PyTorch loading snippet (a WhisperEncoder subclass that
accepts the 8 s window + the 3-layer head) is on the tiny model's card and works unchanged with this repo id
(d_model 768).
License
Apache-2.0 (the Whisper encoder it fine-tunes is Apache-2.0). The training data is not released.
Model tree for Scicom-intl/semantic-vad-eot-whisper-small
Base model
openai/whisper-small


