Views
No views yet
UltravoxModel class drives it with no custom modeling code:| session | input → output |
|---|---|
onnx/audio_encoder* | audio_values [1, samples] (24 kHz mono, −25 dBFS RMS-normalized, length padded to a multiple of 3200) → audio_features [1, frames, 1536] |
onnx/embed_tokens* | input_ids → inputs_embeds |
onnx/decoder_model_merged* | inputs_embeds + KV cache → logits |
<|speech_pad|> (id 151648) per audio frame (frames = ceil(samples/3200));
transformers.js merges audio_features into those positions automatically (audio_token_id in config.json).| component | GGUF (VibeASR.cpp) | this repo | notes |
|---|---|---|---|
| LM projections (196 mats) | I2_S ternary, per-tensor s = 1/mean|W| | identical ternary values in MatMulNBits 4-bit blocks (_q4, _q4f16) and true 2-bit blocks (_bnb4 file) | bit-exact math, kernels everywhere |
| Embeddings / lm_head | Q6_K | 8-bit per-row / per-column | ≥ Q6_K fidelity |
| VAE encoders | I8_S (int8 weights and activations, GELU→ReLU substitution) | int8 per-channel weights, float activations, exact GELU | strictly more accurate |
conversion_report.json.q4f16): encoder 696 MB + embeddings 234 MB + decoder 869 MB ≈ 1.8 GBq4): ≈ 1.98 GBdecoder_model_merged_bnb4.onnx, 726 MB): true I2_S-equivalent; current
onnxruntime CPU 2-bit kernels are much slower than 4-bit — published for experimentation.1import { AutoTokenizer, UltravoxModel, Tensor, TextStreamer } from "@huggingface/transformers";
2
3const model_id = "multimodalart/VibeVoice-ASR-BitNet-ONNX";
4const tokenizer = await AutoTokenizer.from_pretrained(model_id);
5const model = await UltravoxModel.from_pretrained(model_id, {
6 device: "webgpu", // or "wasm"
7 dtype: { audio_encoder: "q4f16", embed_tokens: "q4f16", decoder_model_merged: "q4f16" }, // or all "q4"
8});
9
10// audio: Float32Array, 24 kHz mono, RMS-normalized to -25 dBFS, zero-padded to length % 3200 == 0
11const frames = audio.length / 3200;
12const prompt =
13 `<|im_start|>system\nYou are a helpful assistant that transcribes audio input into text output in JSON format.<|im_end|>\n` +
14 `<|im_start|>user\n<|speech_start|>${"<|speech_pad|>".repeat(frames)}<|speech_end|>\n` +
15 `This is a ${duration} seconds audio, please transcribe it.<|im_end|>\n`;
16// build ids via tokenizer (or splice numeric ids 151644/151645/151646/151647/151648 directly)
17
18const out = await model.generate({
19 ...tokenizer(prompt, { add_special_tokens: false }),
20 audio_values: new Tensor("float32", audio, [1, audio.length]),
21 max_new_tokens: 512,
22 streamer: new TextStreamer(tokenizer, { skip_prompt: true, skip_special_tokens: true }),
23});
24// The model emits "<|im_start|>assistant\n" first — strip it from the decoded text.convert_lm_to_gguf.py ternarization semantics, custom
static-causal-padding ONNX export of the tokenizer encoders, structural exact-ternary
MatMulNBits packing, transcript-level validation against golden references
(VibeASR.cpp prompt format).