Nemotron Streaming

Two NVIDIA streaming ASR models share the NemotronStreamingASR Swift target. Both are 600M-parameter cache-aware FastConformer encoders paired with an RNN-T decoder, both emit native punctuation and capitalization as regular BPE tokens, both run on the Apple Neural Engine via CoreML, and the multilingual variant additionally has MLX bundles for GPU-resident inference. Pick the one that matches your application:

VariantCoverageDefault chunkUpstream
Nemotron 3.5 Multilingual40 language-locales (en-US, en-GB, es-ES, fr-FR, de-DE, ja-JP, ko-KR, zh-CN, hi-IN, ar, …)320 msnvidia/nemotron-3.5-asr-streaming-0.6b
Nemotron Speech Streaming (English)English only — smaller bundle, lower default latency160 msnvidia/nemotron-speech-streaming-en-0.6b

Shared properties

Architecture

Three CoreML models pipelined per audio chunk:

ComponentDescription
Encoder24-layer cache-aware FastConformer, 1024 hidden. Takes a 32-frame mel chunk (320 ms default) plus six state tensors — attention KV cache [24, 1, 56, 1024], depthwise conv cache [24, 1, 1024, 8], pre_cache mel loopback, and a language_mask 128-slot one-hot that drives the prompt kernel.
Prompt kernelLinear(1152→2048) → ReLU → Linear(2048→1024) — folds the language one-hot into every encoded frame so the same 600M weights serve all 40 language-locales.
DecoderTwo-layer LSTM prediction network, 640 hidden. Consumes the previous non-blank token, emits an embedding plus updated (h, c) state.
JointFuses encoder and decoder outputs into logits over 13 087 BPE tokens + blank. Punctuation, capitalization, and per-language tags are just more tokens in the BPE vocab — no extra heads.

No EOU head

Unlike Parakeet-EOU, Nemotron does not emit a dedicated end-of-utterance token. Two ways to segment continuous audio into utterances:

  1. External VAD — pair the session with Silero VAD; on sustained silence, call finalize() to commit the current utterance and createSession() for the next one.
  2. Punctuation boundary — when the partial transcript ends in ., ?, or !, treat that as a natural commit cue. No extra model, but depends on the audio actually inducing terminal punctuation.

Bundles

Five published variants of Nemotron-3.5-ASR-Streaming-0.6B, plus the older English-only model on the same Swift target:

VariantOn-diskStreaming peak (M5 Pro)HuggingFace
CoreML INT8 (default)612 MB1238 MBaufklarer/Nemotron-3.5-ASR-Streaming-0.6B-CoreML-INT8
MLX bf161217 MB1474 MBaufklarer/…MLX-bf16
MLX 5-bit (native default)538.6 MB800 MBaufklarer/…MLX-5bit
MLX 8-bit732.6 MB992 MBaufklarer/…MLX-8bit
MLX 4-bit473 MB747 MBaufklarer/…MLX-4bit
English-only (CoreML INT8)~580 MBaufklarer/Nemotron-Speech-Streaming-0.6B-CoreML-INT8

Upstream: nvidia/nemotron-3.5-asr-streaming-0.6b (multilingual) and nvidia/nemotron-speech-streaming-en-0.6b (English-only).

On the actual cache-aware 320 ms path, MLX 5-bit averages 11.53% WER across six FLEURS languages versus 11.01% for MLX 8-bit. The 5-bit default saves about 193 MB on disk and 192 MB of peak RSS; MLX 4-bit remains a research export because its quality loss is larger.

Quick start — native MLX streaming

The Swift runtime supports the published MLX 5-bit and 8-bit bundles. It defaults to 5-bit, keeps every session's caches independent, and serializes inference when sessions share one loaded model. MLX uses the validated 320 ms geometry and currently does not expose word boosting.

import NemotronStreamingASR

let model = try await NemotronStreamingASRMLXModel.fromPretrained(
    variant: .int5
)
let session = try model.createSession(language: "en-US")

for chunk in microphoneChunks16kHz {
    for partial in try session.pushAudio(chunk) {
        print(partial.text)
    }
}
let final = try session.finalize().last?.text

Quick start — CoreML batch transcription

Conforms to SpeechRecognitionModel, so it drops into any code path that takes a generic STT model:

import NemotronStreamingASR

let model = try await NemotronStreamingASRModel.fromPretrained()
let text = try model.transcribeAudio(audioSamples, sampleRate: 16000, language: "en-US")

Quick start — async streaming

for await partial in model.transcribeStream(audio: samples, sampleRate: 16000) {
    if partial.isFinal { print("FINAL: \(partial.text)") }
    else               { print("... \(partial.text)") }
}

Each PartialTranscript carries text, isFinal (true only for the last partial after finalize()), confidence, and a monotonic segmentIndex.

Long-lived session API (mic input)

let session = try model.createSession()

// each mic chunk:
let partials = try session.pushAudio(float32Chunk16kHz)
for p in partials { showPartial(p.text) }   // isFinal is false mid-stream

// when the utterance ends (VAD silence or explicit stop):
let trailing = try session.finalize()
for p in trailing { commit(p.text) }

CLI

speech transcribe recording.wav --engine nemotron --language en-US                    # batch
speech transcribe recording.wav --engine nemotron --language en-US --stream           # streaming final
speech transcribe recording.wav --engine nemotron --language ja-JP --stream --partial # partials, Japanese
speech transcribe meeting.wav   --engine nemotron --language de-DE                    # any of the 40 locales

Nemotron vs Parakeet-EOU

Nemotron Streaming 0.6BParakeet-EOU 120M
Parameters600M120M
Encoder24-layer FastConformer, 1024 hidden17-layer FastConformer, 512 hidden
Decoder2-layer LSTM, RNN-T1-layer LSTM, RNN-T
EOU detectionExternal (VAD or punctuation)Built-in <EOU> token
PunctuationNative inline BPE tokensNo (post-process)
Languages40 language-locales (en-US, en-GB, es-ES, fr-FR, de-DE, ja-JP, ko-KR, zh-CN, hi-IN, ar, …)25 European
Default chunk320 ms320 ms
Bundle size612 MB (CoreML INT8); 538.6 MB (native MLX 5-bit)~150 MB
Pick Nemotron when…

…you need multilingual streaming transcription (any of 40 language-locales) with punctuation and capitalization out of the box, and you're OK segmenting utterances yourself (VAD or punctuation cue). For constrained-device iOS dictation that's English-only with a built-in EOU signal, Parakeet-EOU is still the smaller choice.