rMLX 0.2.2
Multimodal release. Whisper transcription works end to end (decode correctness
- long-form) behind a new model-agnostic
rmlx transcribeCLI; the dense
Gemma 4 12Bgemma4_unifiedany-to-any architecture is now supported for image
and audio input; the standard Gemma 4 family gains native audio input through
the serve path; and the unified vision color-fidelity bug is fixed. Plus
release-signing and CI-hardening housekeeping. No breaking changes.
Added
rmlx transcribe <audio> --model <snapshot> [--format vtt|srt|json|txt]—
model-agnostic audio transcription CLI, arch-dispatched onconfig.json
(Whisper today, a clean seam for future ASR). Decodes any container to 16 kHz
mono internally (enabledsymphoniaisomp4+aac, so.m4aworks). The HTTP
endpoint and the CLI share one long-form engine. (#119)- Gemma 4 12B unified (
gemma4_unified) image + audio input. The dense
any-to-any 12B has no SigLIP/Conformer tower — vision and audio are
early-fusion via soft tokens projected straight into the shared 48-layer LM.
Faithful encoder-free ports ofGemma4UnifiedVisionEmbedder(host patchify +
3×3 merge →patch_ln1→ quantizedpatch_dense→ factorized 2D pos-emb →
embed_vision) andGemma4UnifiedAudioFeatureExtractor(raw 16 kHz waveform
→ fixed 640-sample frames →embed_audio). Dispatched offis_unified_arch;
the standard e4b/26b/31b SigLIP path is unchanged. (#120) - Gemma 4 native audio input through the serve path. The Conformer
audio_tower+embed_audioprojector + USM feature extractor now load at
startup alongside the vision tower, andinput_audioparts are decoded → mel
→AudioEncoder→ soft tokens scattered at<|audio|>, mirroring the vision
flow. Submitting audio to a model without an audio tower (or combining image +
audio) returns a clear 503 — no silent drop. (#122)
Fixed
- Whisper transcription was empty / garbage. large-v3 has 100 language
slots, shifting every special token +1 vs the v1/v2 layout the constants
assumed — soTOK_TRANSCRIBEpointed at<|translate|>and the
timestamp-begin hard-stop fired on<|notimestamps|>. Corrected the
special-token layout and added the missing in-loop logit filters
(SuppressBlank,SuppressTokensderived generally from the tokenizer, and a
faithfulApplyTimestampRules). Long-form decode bounds are derived from
n_text_ctxat runtime so the positional table can't overflow. Full 48-min
real recording at temp 0 → normalized WER ≈ 0.079, deterministic. (#119) - Gemma 4 12B unified vision color corruption. The encoder-free path read
image soft tokens causally, butgemma4_unifiedconditions each image's
soft tokens with bidirectional attention (the SigLIP path hides this by
pre-integrating the image in its ViT). A per-prefill bidirectional overlay,
keyed off the<start_of_image>/<end_of_image>markers and merged
element-wise into each layer's causal/SWA mask, fixes color naming and layout;
gated onhas_imageso text prefill is untouched. (LayerNorm eps also
corrected to the PyTorchnn.LayerNormdefault 1e-5.) A 100%-uniform
achromatic fill still reads as one level — an inherent property of the
encoder-free projection (patch_ln1normalizes the absolute level away),
documented indocs/MODELS.md. (#127) --probe-smokefalseBrokenPunctLoopon instruction-tuned snapshots.
The probe fed a bare (no-chat-template) instruction; chat models degenerate on
such out-of-distribution input (the mlx-lm reference reproduces it
identically) — a probe artifact, not a 4-bit dequant bug. The smoke seed is
now rendered through the snapshot'schat_template.jinjawhen present, falling
back to the bare seed for base models; each entry point keeps its own canonical
BOS resolver (no hardcoded id). (#121)
Security
- Pin CI actions (
actions/checkout,dtolnay/rust-toolchain,
Swatinem/rust-cache) to commit SHAs, add keyless cosign release signing
(make release-sign), and drop a stale RustSec advisory ignore. (#116)