mlx-bun 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -32
- package/docs/README.md +58 -42
- package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +3617 -0
- package/docs/archive/investigations/openjarvis-fit.md +198 -0
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
- package/docs/archive/planning/release-notes-v0.4.0.md +45 -0
- package/docs/archive/planning/release-notes-v0.5.0.md +37 -0
- package/docs/design/batching.md +1535 -50
- package/docs/design/decode-speed-program.md +394 -1171
- package/docs/design/generic-model-support.md +195 -3
- package/docs/design/kv-cache.md +538 -225
- package/docs/design/speculative-decoding.md +552 -50
- package/docs/design/turboquant.md +140 -17
- package/docs/design/unified-engine-frontier-plan.md +259 -150
- package/docs/planning/PRODUCT_ROADMAP.md +21 -6
- package/docs/planning/public-readiness-handoff.md +244 -0
- package/docs/planning/public-readiness.md +149 -0
- package/docs/reference/benchmarks.md +3537 -1112
- package/docs/reference/cli.md +102 -2
- package/docs/reference/distribution.md +45 -13
- package/docs/reference/environment.md +159 -13
- package/docs/reference/library-api.md +18 -4
- package/docs/reference/memory.md +1 -1
- package/docs/reference/models.md +28 -7
- package/docs/reference/server-api.md +257 -33
- package/docs/reference/server-config.md +372 -151
- package/docs/reference/troubleshooting.md +13 -21
- package/package.json +2 -2
- package/src/audio/audiotoolbox.ts +112 -0
- package/src/audio/conformer.ts +8 -0
- package/src/audio/mic-capture.ts +98 -0
- package/src/audio/silero-vad.ts +379 -0
- package/src/audio/whisper-decode.ts +774 -0
- package/src/audio/whisper-fast.ts +732 -0
- package/src/audio/whisper-format.ts +73 -0
- package/src/audio/whisper-mel.ts +185 -0
- package/src/audio/whisper-timing.ts +274 -0
- package/src/audio/whisper-tokenizer.ts +239 -0
- package/src/audio/whisper-transcribe.ts +410 -0
- package/src/backends/mlx/assistant-target.ts +46 -0
- package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
- package/src/backends/mlx/autoregressive.ts +22 -1
- package/src/backends/mlx/batch-group.ts +464 -236
- package/src/backends/mlx/cache-attachment.ts +67 -0
- package/src/backends/mlx/cache-layout.ts +31 -0
- package/src/backends/mlx/checkpoint-state.ts +53 -0
- package/src/backends/mlx/continuation-persistence.ts +107 -0
- package/src/backends/mlx/continuation-request.ts +43 -0
- package/src/backends/mlx/continuation.ts +77 -0
- package/src/backends/mlx/draft-target.ts +7 -22
- package/src/backends/mlx/encoder-cache.ts +25 -0
- package/src/backends/mlx/fill-append.ts +21 -8
- package/src/backends/mlx/fill-group.ts +497 -0
- package/src/backends/mlx/full-kv-row-append.ts +32 -0
- package/src/backends/mlx/full-kv-row-donor.ts +54 -0
- package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
- package/src/backends/mlx/gateway-binding.ts +122 -19
- package/src/backends/mlx/kv-codec.ts +9 -0
- package/src/backends/mlx/kv-maintenance.ts +161 -0
- package/src/backends/mlx/kv-tensor-rows.ts +212 -0
- package/src/backends/mlx/mixed-iteration.ts +62 -0
- package/src/backends/mlx/model-prompt.ts +46 -32
- package/src/backends/mlx/model-serving.ts +5 -4
- package/src/backends/mlx/paged-cache-codec.ts +27 -0
- package/src/backends/mlx/prefill-cohort.ts +100 -0
- package/src/backends/mlx/prefill-policy.ts +26 -0
- package/src/backends/mlx/prefill-rows.ts +244 -0
- package/src/backends/mlx/prefill.ts +1 -1
- package/src/backends/mlx/projected-context-rows.ts +72 -0
- package/src/backends/mlx/prompt-input.ts +29 -0
- package/src/backends/mlx/qwen-prompt-input.ts +40 -0
- package/src/backends/mlx/request-state-policy.ts +54 -0
- package/src/backends/mlx/rollback.ts +45 -4
- package/src/backends/mlx/row-sampling.ts +49 -0
- package/src/backends/mlx/serial-executor.ts +51 -73
- package/src/backends/mlx/speculative-group.ts +438 -0
- package/src/backends/mlx/speculative-round.ts +161 -0
- package/src/backends/mlx/speculative.ts +24 -7
- package/src/backends/mlx/state-rows.ts +104 -0
- package/src/backends/mlx/state-views.ts +21 -1
- package/src/backends/mlx/target-layout-capability.ts +35 -0
- package/src/backends/mlx/transitioning-kv-rows.ts +173 -0
- package/src/cli.ts +333 -36
- package/src/contracts/execution.ts +21 -0
- package/src/contracts/object-cache.ts +8 -0
- package/src/contracts/pi-provider.ts +1 -1
- package/src/contracts/pi.ts +3 -1
- package/src/contracts/prefix-cache.ts +33 -0
- package/src/contracts/resources.ts +7 -0
- package/src/contracts/sampling.ts +23 -0
- package/src/contracts/scheduling.ts +27 -0
- package/src/engine/execution-plan.ts +38 -25
- package/src/engine/execution-tasks.ts +44 -0
- package/src/engine/namespaced-cache.ts +13 -0
- package/src/engine/resources.ts +12 -1
- package/src/engine/scheduler.ts +32 -7
- package/src/fill/fill-session.ts +14 -3
- package/src/fit.ts +29 -15
- package/src/generate.ts +105 -195
- package/src/grammar.ts +13 -0
- package/src/inference/draft-acceptance.ts +41 -0
- package/src/inference/draft-output.ts +49 -0
- package/src/inference/prefill.ts +6 -0
- package/src/inference/rollback.ts +2 -2
- package/src/kv-scheme.ts +92 -12
- package/src/kv-store.ts +183 -93
- package/src/lab/README.md +5 -1
- package/src/lab/paged-kv/paged-attention.ts +117 -0
- package/src/lab/paged-kv/paged-kv-rows.ts +111 -0
- package/src/lab/paged-kv/paged-kv.ts +126 -46
- package/src/md-modules.d.ts +5 -0
- package/src/mlx/array.ts +39 -1
- package/src/mlx/ffi.ts +19 -0
- package/src/mlx/normalized-argmax.ts +116 -0
- package/src/mlx/ops.ts +97 -10
- package/src/model/artifact-identity.ts +73 -0
- package/src/model/batched-kv.ts +235 -0
- package/src/model/batched-mask.ts +12 -4
- package/src/model/batched-quant.ts +8 -2
- package/src/model/batched-quantized-kv.ts +144 -0
- package/src/model/batched-rotating-quant.ts +76 -15
- package/src/model/batched-rotating-state.ts +76 -0
- package/src/model/batched-rotating.ts +64 -22
- package/src/model/batched-row-storage.ts +52 -0
- package/src/model/batched-ssm.ts +52 -0
- package/src/model/batched-turboquant-kv.ts +94 -0
- package/src/model/compiled-decode.ts +7 -1
- package/src/model/decoded-kv-donor.ts +12 -0
- package/src/model/delayed-quantized-kv.ts +72 -0
- package/src/model/delayed-rotating-quantized-kv.ts +93 -0
- package/src/model/delayed-turboquant-kv.ts +57 -0
- package/src/model/factory.ts +26 -0
- package/src/model/fingerprint.ts +12 -0
- package/src/model/full-prefill-padding.ts +56 -0
- package/src/model/full-prefill-row.ts +105 -0
- package/src/model/gemma4-base.ts +183 -82
- package/src/model/gemma4.ts +116 -28
- package/src/model/generated/gemma4-12b.ts +4 -1
- package/src/model/generated/gemma4-26b.ts +4 -1
- package/src/model/generated/gemma4-e4b.ts +4 -1
- package/src/model/glm52-cache.ts +80 -4
- package/src/model/glm52-mla.ts +12 -6
- package/src/model/glm52.ts +19 -40
- package/src/model/kv-attention-view.ts +70 -0
- package/src/model/minicpm5.ts +11 -7
- package/src/model/profile.ts +29 -4
- package/src/model/quantized-append-attention.ts +31 -0
- package/src/model/qwen3-delta-state.ts +74 -0
- package/src/model/qwen3-delta.ts +59 -36
- package/src/model/qwen3-mrope.ts +7 -9
- package/src/model/qwen3_5.ts +177 -75
- package/src/model/rotating-kv-donor.ts +35 -0
- package/src/model/rotating-kv-layout.ts +262 -0
- package/src/model/rotating-row-transaction.ts +35 -0
- package/src/model/speculative-rotating-kv.ts +101 -0
- package/src/model/ssm-prefill-padding.ts +39 -0
- package/src/model/support.ts +8 -0
- package/src/model/token-groups.ts +66 -0
- package/src/model/trellis-linear.ts +4 -4
- package/src/model/turboquant-codec.ts +89 -0
- package/src/model/universal/dense.ts +14 -13
- package/src/model/whisper.ts +435 -0
- package/src/native/mic_capture.swift +92 -0
- package/src/native-pack.ts +6 -5
- package/src/pi-web.ts +9 -1
- package/src/preflight.ts +31 -15
- package/src/prompt-cache.ts +306 -86
- package/src/runtime-config.ts +1 -5
- package/src/safetensors.ts +9 -2
- package/src/sampler.ts +37 -25
- package/src/serve/audio-routes.ts +323 -0
- package/src/serve/chat-request.ts +2 -0
- package/src/serve/chat-stage.ts +19 -14
- package/src/serve/completion-executor.ts +9 -1
- package/src/serve/discovery-routes.ts +17 -1
- package/src/serve/generated-token-history.ts +57 -0
- package/src/serve/generation-gateway.ts +88 -38
- package/src/serve/model-binding.ts +7 -1
- package/src/serve/model-host.ts +3 -4
- package/src/serve/preparation.ts +3 -3
- package/src/serve/prompt-response-trace.ts +14 -0
- package/src/serve/request-plan.ts +6 -3
- package/src/serve/request-prep.ts +40 -25
- package/src/serve/text-completion-stage.ts +2 -2
- package/src/serve/transcription-server.ts +64 -0
- package/src/serve/transcription-service.ts +502 -0
- package/src/server.ts +185 -261
- package/src/spec/assistant-rows.ts +93 -0
- package/src/spec/assistant-source.ts +28 -18
- package/src/spec/cache-identity.ts +22 -0
- package/src/spec/deepspec-rows.ts +29 -0
- package/src/spec/deepspec-source.ts +12 -10
- package/src/spec/dflash-source.ts +28 -4
- package/src/spec/drafter.ts +153 -142
- package/src/spec/dspark/deepspec-module.ts +95 -120
- package/src/spec/dspark/module-dflash.ts +126 -170
- package/src/spec/glm52-mtp-graph.ts +29 -0
- package/src/spec/glm52-mtp-rows.ts +132 -0
- package/src/spec/glm52-mtp-source.ts +54 -136
- package/src/spec/glm52-mtp-state.ts +33 -0
- package/src/spec/ngram-source.ts +115 -21
- package/src/spec/projected-draft-rows.ts +73 -0
- package/src/spec/qwen-mtp-module.ts +171 -0
- package/src/spec/qwen-mtp-rows.ts +230 -0
- package/src/spec/qwen-mtp-source.ts +84 -226
- package/src/spec/qwen-mtp-state.ts +30 -0
- package/src/spec/serve-loop.ts +73 -69
- package/src/spec/source.ts +93 -15
- package/src/spec/two-model-rows.ts +62 -0
- package/src/spec/two-model.ts +45 -103
- package/src/ssd-cache.ts +107 -36
- package/src/ssd-durability.ts +29 -27
- package/src/storage/host-buffer.ts +24 -0
- package/src/storage/kv-writer.ts +83 -0
- package/src/storage/kv-writer.worker.js +264 -0
- package/src/storage/retention-policy.ts +37 -0
- package/src/tiered-prompt-cache.ts +45 -0
- package/src/vision/embedder.ts +8 -0
- package/src/vision/prompt.ts +101 -65
- package/src/vision/qwen3vl-prompt.ts +101 -64
- package/src/vision/qwen3vl-tower.ts +11 -0
- package/src/vision/siglip.ts +8 -0
- package/src/web/app.html +12 -0
- package/src/web/app.js +217 -57
- package/src/web/src/chat.ts +4 -1
- package/src/web/src/composer.ts +3 -0
- package/src/web/src/voice.ts +141 -0
- package/src/web/sw.js +1 -1
- package/src/backends/mlx/memory-guard.ts +0 -40
- package/src/spec/prefix-state.ts +0 -45
- /package/docs/{planning → archive/planning}/release-notes-v0.3.0.md +0 -0
|
@@ -23,8 +23,12 @@ Two tiers, one contract:
|
|
|
23
23
|
|
|
24
24
|
- **Tier-0 GENERIC** — one config-driven "universal" module per architecture
|
|
25
25
|
*family*. Runs any covered checkpoint at **L1 parity** (bit-exact vs mlx-lm
|
|
26
|
-
on this machine's GPU). Monolith path only
|
|
27
|
-
|
|
26
|
+
on this machine's GPU). Monolith path only, without compiled decode.
|
|
27
|
+
Standard attention now uses the shared plain/affine KV port; the softcap
|
|
28
|
+
branch retains plain KV. Qualified full-attention targets compose delayed
|
|
29
|
+
affine conversion with standalone and lookup drafting through the same
|
|
30
|
+
cache transaction interface; rotating layouts use that interface for
|
|
31
|
+
per-layer conversion and assistant donors. Built: `src/model/universal/` (dense
|
|
28
32
|
llama-family). Design only: UniversalMoE.
|
|
29
33
|
- **Tier-1 TARGETED** — a model *graduates*: dedicated or generated forward,
|
|
30
34
|
L2 (mlx-optiq) composition where an optiq artifact exists, Lab experiments
|
|
@@ -200,7 +204,9 @@ Key choices:
|
|
|
200
204
|
or missing tensors are a LOAD ERROR naming them, never a silently-wrong
|
|
201
205
|
model.
|
|
202
206
|
- **KV cache** — `KVCache`/`RotatingKVCache` unchanged; `layer_types` /
|
|
203
|
-
`sliding_window` already normalized.
|
|
207
|
+
`sliding_window` already normalized. Standard attention consumes the shared
|
|
208
|
+
cache-attention interface; qualified Llama uniform KV4/KV8 serving and
|
|
209
|
+
generated-cache coverage are described in batching.md.
|
|
204
210
|
- **RuntimeModel surface** — the minimal contract `Qwen3Model` implements
|
|
205
211
|
(config, weightsBytes, loraState, makeCache, forwardHidden,
|
|
206
212
|
logitsFromHidden, forward, generate, loraTargets) so serving/eval/LoRA
|
|
@@ -336,6 +342,72 @@ design-relevant facts; phase logs live in PLAN.md at the named anchor.
|
|
|
336
342
|
- Training reference model for segmented backward and ORPO
|
|
337
343
|
(docs/design/orpo-training.md, orpo-training.md).
|
|
338
344
|
|
|
345
|
+
#### 6.2.1 MiniCPM5-2B investigation
|
|
346
|
+
|
|
347
|
+
Metadata and template inspection completed 2026-09-10. No weights were
|
|
348
|
+
downloaded and no GPU inference was run. This checkpoint is a candidate,
|
|
349
|
+
not an artifact with established parity or local performance results.
|
|
350
|
+
|
|
351
|
+
Start with the official [OpenBMB MLX artifact](https://huggingface.co/openbmb/MiniCPM5-2B-MLX),
|
|
352
|
+
revision `3d00c3da500debfe345e60e25a87ed2669f5b1ee`. Its config uses standard
|
|
353
|
+
Llama blocks, 42 layers, hidden size 2048, intermediate size 6144, 16 query
|
|
354
|
+
and 2 KV heads of dimension 128, untied embeddings, vocab 130560 and
|
|
355
|
+
RoPE theta 5000000. Quantization is affine 4-bit with group size 64.
|
|
356
|
+
Preserve both EOS IDs, `1` and `130073`, and the separate
|
|
357
|
+
`chat_template.jinja`. The release is text-only and Apache-2.0.
|
|
358
|
+
|
|
359
|
+
Current integration findings:
|
|
360
|
+
|
|
361
|
+
- `isMiniCPM5Config` in `src/model/support.ts` matches the older 24-layer,
|
|
362
|
+
hidden-1536 artifact. The 2B config fails that predicate and qualifies for
|
|
363
|
+
generic Llama through `genericArgsFor`; `familyProfile` therefore selects
|
|
364
|
+
the universal graph. Do not widen the predicate before auditing dedicated
|
|
365
|
+
execution, training and cache assumptions against this shape.
|
|
366
|
+
- A CPU-only smoke using the pinned config and template passed
|
|
367
|
+
`loadModelConfig`, `genericArgsFor`, `ChatTemplate.load` and rendering a
|
|
368
|
+
user → assistant tool call → tool result → user conversation. Explicit
|
|
369
|
+
thinking true/false both render correctly; the XML function call survives.
|
|
370
|
+
This checks rendering, not generated tool-call parsing or tokenizer parity.
|
|
371
|
+
- `resolveEnableThinking` applies the MiniCPM no-thinking default through
|
|
372
|
+
the same exact-shape predicate. It does not apply to 2B. With no explicit
|
|
373
|
+
option, this artifact's prompt ends at the assistant header; true appends
|
|
374
|
+
an open think tag, false appends a closed empty think block. Set this
|
|
375
|
+
explicitly in comparisons. Template support for disabling thinking does
|
|
376
|
+
not establish no-thinking quality.
|
|
377
|
+
|
|
378
|
+
Evaluation sequence:
|
|
379
|
+
|
|
380
|
+
1. Pin an 8-bit or BF16 control alongside the official 4-bit candidate.
|
|
381
|
+
Establish tokenizer/prompt identity and same-machine logits against the
|
|
382
|
+
pinned mlx-lm oracle, then test multi-turn tool calls, reasoning splits,
|
|
383
|
+
EOS, cache reuse and batched continuation. Keep quantization quality
|
|
384
|
+
comparisons separate from same-artifact runtime parity.
|
|
385
|
+
2. Compare with MiniCPM5-1B and Gemma e4b on held-out Dreaming segmentation,
|
|
386
|
+
extraction and source-grounded synthesis, plus bounded repository/tool
|
|
387
|
+
tasks. Score boundary/label accuracy and completed tasks, not just valid
|
|
388
|
+
JSON or XML. Use explicit thinking modes and equal token/time budgets.
|
|
389
|
+
3. Record first-answer latency, complete task time, output/reasoning tokens,
|
|
390
|
+
repetition and truncation rates, memory and context scaling. Local
|
|
391
|
+
measurements belong only in `docs/reference/benchmarks.md`, with a named
|
|
392
|
+
quiet machine and the required serving benchmark procedure.
|
|
393
|
+
4. Investigate the [released DSpark draft](https://huggingface.co/openbmb/MiniCPM5-2B-DSpark)
|
|
394
|
+
after ordinary inference passes. Its config declares `Qwen3DSparkModel`,
|
|
395
|
+
five draft layers, block size seven, and target taps `[1,10,20,30,39]`.
|
|
396
|
+
Existing Gemma DSpark support does not establish compatibility. Audit
|
|
397
|
+
the draft's architecture, target taps, weights and verifier before a
|
|
398
|
+
paired task-time comparison.
|
|
399
|
+
|
|
400
|
+
Evidence to use when designing the quality checks:
|
|
401
|
+
[OpenBMB's model card](https://huggingface.co/openbmb/MiniCPM5-2B) distinguishes
|
|
402
|
+
internally reproduced scores from Artificial Analysis results. The
|
|
403
|
+
[independent September 7 evaluation](https://artificialanalysis.ai/articles/openbmb-releases-minicpm5-2b)
|
|
404
|
+
supports strong capability for its size but reports substantial reasoning
|
|
405
|
+
token use and weak absolute terminal-task success. These evaluations do not
|
|
406
|
+
validate MLX 4-bit quality. [Upstream issue 374](https://github.com/OpenBMB/MiniCPM/issues/374)
|
|
407
|
+
reports repetition sensitivity in GGUF coding runs. That is an unreproduced
|
|
408
|
+
cross-runtime hypothesis for our sweep, not evidence of the same MLX failure
|
|
409
|
+
or a reason to change repetition defaults.
|
|
410
|
+
|
|
339
411
|
### 6.3 Qwen3 / Qwen3-MoE / Qwen3.5-family (3.6, 3.8) — `qwen3.ts`, `qwen3-moe.ts`, `qwen3_5.ts`
|
|
340
412
|
|
|
341
413
|
- **Qwen3** (`model_type: qwen3`): dense GQA with per-head q/k RMSNorm
|
|
@@ -581,6 +653,122 @@ non-goals.
|
|
|
581
653
|
|
|
582
654
|
---
|
|
583
655
|
|
|
656
|
+
### 6.7 Whisper (speech-to-text) — `src/model/whisper.ts`, `src/audio/whisper-*.ts`, `src/serve/transcription-*.ts`
|
|
657
|
+
|
|
658
|
+
Audio in → text out, the first encoder-decoder family. Target artifact:
|
|
659
|
+
`mlx-community/whisper-large-v3-turbo` (fp16, 32 encoder / 4 decoder layers,
|
|
660
|
+
128 mel bins, 51 866 vocab). Plan/evidence anchor: PLAN.md "Phase: Whisper
|
|
661
|
+
speech-to-text". Served surface: [server-api.md](../reference/server-api.md#post-v1audiotranscriptions--post-v1audiotranslations-speech-to-text),
|
|
662
|
+
[cli.md `transcribe`](../reference/cli.md#transcribe--speech-to-text).
|
|
663
|
+
|
|
664
|
+
- **Oracle**: mlx-whisper 0.4.3 (`ml-explore/mlx-examples/whisper`) in its
|
|
665
|
+
own venv (`~/Code/mlx-whisper-oracle/.venv`, mlx pinned to
|
|
666
|
+
`MLX_CORE_VERSION`; it drags torch + numba, which must not enter the
|
|
667
|
+
pinned mlx-lm venv). The profile declares `{ l1, mlx-whisper, bit-exact }`
|
|
668
|
+
— no mlx-lm arm exists. Generator `scripts/oracle/gen-whisper-golden.py`
|
|
669
|
+
(`bun scripts/regen.ts whisper`) → `goldens/whisper.json` + untracked
|
|
670
|
+
blobs; gate `tests/parity/whisper.test.ts`: mel (f32), decode-window
|
|
671
|
+
encoder output (f16 raw) and every per-step PRE-filter logit vector (f32)
|
|
672
|
+
bit-exact; text / segment tokens / timestamps identical. Clips: the
|
|
673
|
+
tracked `speech-fox.wav`, whisper.cpp's public-domain `jfk.wav` (sotto's
|
|
674
|
+
vendored checkout, optional), and a synthesized 33 s concatenation that
|
|
675
|
+
crosses a 30 s window.
|
|
676
|
+
- **Front end** (`whisper-mel.ts`): op-for-op `log_mel_spectrogram` on the
|
|
677
|
+
GPU — host reflect pad, `as_strided` frames, periodic Hann, `rfft` 400,
|
|
678
|
+
`|·|²`, slaney filter bank (librosa's float64 → float32 rounding order
|
|
679
|
+
reproduced, bit-exact vs the shipped `mel_filters.npz`), `log10`, 8-decade
|
|
680
|
+
clamp, `(x+4)/4`. 1.5 ms warm for a 41 s padded clip (M1 Max).
|
|
681
|
+
- **Model** (`whisper.ts`): faithful path = the oracle's graph, including
|
|
682
|
+
its quirks that change bits: `nn.gelu` is `mx.compile`d in mlx, and the
|
|
683
|
+
FUSED kernel rounds differently from four separate ops (2 % of conv1
|
|
684
|
+
outputs differ by an ulp) — so the same graph is compiled here
|
|
685
|
+
(`CompiledFunction`, shapeless); attention scales q and k by
|
|
686
|
+
`(D/H)^-0.25` each with fp16 `q@kᵀ`, additive `-inf` fp16 causal mask,
|
|
687
|
+
precise softmax; self-attention K/V are cached un-split `[B, T, D]` and
|
|
688
|
+
concatenated per step; cross-attention K/V are projected once per window
|
|
689
|
+
and kept `[1, 1500, D]` — beam rows attend to them by broadcasting, never
|
|
690
|
+
by copying.
|
|
691
|
+
- **Decoding** (`whisper-decode.ts`): DecodingTask port — sot sequence,
|
|
692
|
+
prompt/prefix budgets (`n_ctx/2 − 1 = 223` prompt tokens), suppress-blank,
|
|
693
|
+
non-speech suppression, timestamp rules, language detection, greedy /
|
|
694
|
+
sampled, MaximumLikelihoodRanker. **Two deliberate divergences from
|
|
695
|
+
mlx-whisper**, both documented in code: (1) the monotonic-timestamp rule
|
|
696
|
+
follows openai-whisper (mlx-whisper's port collects sequence indices and
|
|
697
|
+
slices an empty range, so its rule never fires) — pre-filter logits stay
|
|
698
|
+
bit-exact, `avg_logprob` moves ~1e-4; (2) `compression_ratio` uses Bun's
|
|
699
|
+
zlib, which emits one byte more than CPython's 1.2.12 for the same
|
|
700
|
+
level-6 stream — the 2.4 fallback threshold decision is asserted equal,
|
|
701
|
+
the value within 5 %. Beam search is ours (mlx-whisper raises
|
|
702
|
+
NotImplemented): openai-whisper's BeamSearchDecoder over cache rows, top-k
|
|
703
|
+
on device (`argpartition` on negated log-probs; the `[B, k]` slice is
|
|
704
|
+
materialized before host readback — reading a strided view linearly was a
|
|
705
|
+
real bug), finished sequences collected per audio, patience honoured.
|
|
706
|
+
Checked against whisper.cpp v1.9.3 beam-5 transcripts (identical on the
|
|
707
|
+
JFK clip with and without vocabulary hints).
|
|
708
|
+
- **Transcribe loop** (`whisper-transcribe.ts`): transcribe.py's seek loop —
|
|
709
|
+
30 s windows over the zero-padded mel, temperature fallback ladder gated by
|
|
710
|
+
compression ratio / avg log-prob / no-speech probability, timestamp-driven
|
|
711
|
+
seeking, previous-text conditioning, `clip_timestamps`. Async only to
|
|
712
|
+
yield between windows (streaming, cancellation). Word timestamps (DTW on
|
|
713
|
+
the checkpoint's `alignment_heads`) are not ported yet.
|
|
714
|
+
- **Serving** (`transcription-service.ts`, `audio-routes.ts`,
|
|
715
|
+
`transcription-server.ts`): residency-managed service (load on first
|
|
716
|
+
request, dispose after `--whisper-idle-unload` seconds, FIFO queue, runs
|
|
717
|
+
under the gateway's exclusive lock beside chat), OpenAI multipart/JSON
|
|
718
|
+
routes with `json/verbose_json/text/srt/vtt` and SSE streaming, sotto's
|
|
719
|
+
vocabulary-hint fitting with included/omitted reporting, and a
|
|
720
|
+
transcription-only server for `serve <whisper checkpoint>`. Page-in after
|
|
721
|
+
an explicit unload: 37–45 ms on the M1 Max (weights are mmap'd; the OS
|
|
722
|
+
file cache still holds them).
|
|
723
|
+
- **Tokenizer**: the mlx-community repos ship no tokenizer; ids are identical
|
|
724
|
+
to HF's `openai/whisper-large-v3-turbo` tokenizer.json (checked against
|
|
725
|
+
tiktoken), which the loader resolves from the HF cache by `n_vocab`.
|
|
726
|
+
- **Fast path** (`whisper-fast.ts`, default; `faithful`/`--faithful` selects
|
|
727
|
+
the oracle graph): fused SDPA everywhere (whisper.cpp flash-attn); one
|
|
728
|
+
fused q/k/v gemm per encoder layer; cross-K/V for all four decoder layers
|
|
729
|
+
from ONE matmul pair per window, head-split once (whisper.cpp `kv_cross`,
|
|
730
|
+
vLLM encoder-prefill cross KV); the single-token decoder step — embed, 4
|
|
731
|
+
layers, ln, logits, every logit filter, argmax (greedy) or top-k (beam) —
|
|
732
|
+
is one shapeless `mx.compile`d closure with per-step state as int32 arrays
|
|
733
|
+
(the LLM compiled-decode pattern); greedy runs device-resident with the
|
|
734
|
+
token array flowing into the next step and a one-step-lag host read
|
|
735
|
+
(mlx-whisper's async_eval); beam reorders caches with `take`. Gate:
|
|
736
|
+
`tests/parity/whisper-fast.test.ts` — at most two differing tokens per
|
|
737
|
+
clip vs the oracle/faithful path (one near-tie flips "dog"→"dog." plus
|
|
738
|
+
its timestamp on the 33 s no-conditioning clip). Word timestamps
|
|
739
|
+
(`whisper-timing.ts`, timing.py port: alignment-head cross-attention →
|
|
740
|
+
median filter → DTW in JS → punctuation merge and duration heuristics)
|
|
741
|
+
ride the faithful decoder once per window.
|
|
742
|
+
- **Silero VAD** (`silero-vad.ts`): the v6.2 TorchScript graph ported
|
|
743
|
+
exactly (64-sample inter-chunk context, right reflect pad, STFT-as-conv,
|
|
744
|
+
4 conv blocks, LSTMCell, sigmoid) — whisper.cpp's port drops the context
|
|
745
|
+
and pads both sides — plus `get_speech_timestamps`; STFT/convs/input
|
|
746
|
+
gates batched over all chunks in one MLX graph, the recurrence on the
|
|
747
|
+
host; streaming state carried across chunks (streaming == batch within
|
|
748
|
+
1e-6). Gate: `tests/parity/silero-vad.test.ts` vs the pip reference
|
|
749
|
+
(probabilities within 5e-3 because the ggml weights are fp16; segments
|
|
750
|
+
identical). Used as the service's gate (`vad`) and inside sessions.
|
|
751
|
+
- **Streaming run** (`WhisperStreamingRun`): the seek loop as a resumable
|
|
752
|
+
object; `feed()` transcribes each window with 30 s of audio past the seek
|
|
753
|
+
point, `finish()` drains. Batch = one feed + finish, so parity covers it.
|
|
754
|
+
Sessions (`/v1/audio/sessions`) wrap it with the VAD stream and the
|
|
755
|
+
exclusive lock.
|
|
756
|
+
- **Voice input**: `mlx-bun dictate` (AVAudioEngine sidecar
|
|
757
|
+
`src/native/mic_capture.swift`, hold a keycode or Enter-toggle, print /
|
|
758
|
+
clipboard / System Events keystrokes) and the web composer's hold-to-talk
|
|
759
|
+
mic (`src/web/src/voice.ts`: 16 kHz AudioContext → session chunks → caret
|
|
760
|
+
insert; shown when the `ready` frame carries `transcription`). Both are
|
|
761
|
+
the streaming session over the same server code.
|
|
762
|
+
- **Decode input**: AudioToolbox `ExtAudioFile` over `bun:ffi`
|
|
763
|
+
(`audiotoolbox.ts`) for every non-WAV container — CoreAudio's decoder and
|
|
764
|
+
resampler in-process; WAV keeps the exact PCM parser.
|
|
765
|
+
- **What the measurements say (M1 Max 32 GB, 2026-09-15, benchmarks.md):**
|
|
766
|
+
the encoder is ~85 % of a short clip's time and is bound by its fp16
|
|
767
|
+
gemms at ~7–8 TFLOPS (padding rows to 1536 for tile alignment, fused
|
|
768
|
+
vs unfused GELU, batching two windows: all 0–5 %); decode is ~1.5 ms per
|
|
769
|
+
token. Whisper.cpp's `audio_ctx` truncation is the only lever left on
|
|
770
|
+
the encoder and it is a quality trade (`audio_ctx`, Lab, off).
|
|
771
|
+
|
|
584
772
|
## 7. Top design decisions
|
|
585
773
|
|
|
586
774
|
1. **Explicit descriptor table keyed by model_type (+ vendored
|
|
@@ -627,3 +815,7 @@ non-goals.
|
|
|
627
815
|
- 2026-08-23 — This doc consolidated: audio-input-plan,
|
|
628
816
|
diffusion-gemma-port, minicpm5-decode-megakernel, and the Colibri archive
|
|
629
817
|
summarized here; ports become sections.
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
Non-Qwen grouped TurboQuant drafting, including assistant donor attention, uses
|
|
821
|
+
the shared cache interfaces. See [composition coverage](batching.md#turboquant-donor-attention-and-grouped-draft-composition).
|