NVIDIA Nemotron 3.5 ASR Streaming 0.6B (English)
Nemotron 3.5 ASR is a streaming speech recognition model built for high-quality English transcription in both low-latency streaming and batch workloads.
Model details
View repositoryExample usage
The realtime WebSocket API returns two kinds of transcription messages as audio streams in:
Partial results (
...transcription.delta) — live, in-progress hypotheses that update continuously as you speak. Ideal for showing low-latency captions that refine in real time.Final results (
...transcription.completed) — stable, punctuated transcripts for each completed segment. These won't change and represent the authoritative output.
1import asyncio, base64, json, wave, websockets
2
3URL = "wss://model-<id>.api.baseten.co/environments/production/websocket?intent=transcription"
4API_KEY = "<baseten-api-key>"
5MODEL = "cache-aware-parakeet-rnnt-en-US-asr-streaming-sortformer" # served ASR model name
6SAMPLE_RATE = 16000
7CHUNK_MS = 250
8
9def load_pcm(path):
10 # Return mono 16-bit PCM at SAMPLE_RATE. The server treats the bytes as raw
11 # pcm16 at the declared rate, so any stereo / non-16kHz input must be
12 # converted here or it transcribes to nothing.
13 with wave.open(path, "rb") as wf:
14 n_ch, width, rate = wf.getnchannels(), wf.getsampwidth(), wf.getframerate()
15 frames = wf.readframes(wf.getnframes())
16 if width != 2:
17 raise ValueError(f"need 16-bit PCM, got {width * 8}-bit")
18 if n_ch == 2:
19 frames = audioop.tomono(frames, 2, 0.5, 0.5)
20 if rate != SAMPLE_RATE:
21 frames, _ = audioop.ratecv(frames, 2, 1, rate, SAMPLE_RATE, None)
22 return frames
23
24async def main(path):
25 pcm = load_pcm(path)
26 chunk = int(SAMPLE_RATE * CHUNK_MS / 1000) * 2 # 2 bytes/sample
27
28 async with websockets.connect(URL, additional_headers={"Authorization": f"Api-Key {API_KEY}"}) as ws:
29 # 1. Configure the session (model, language, audio format, sample rate).
30 await ws.send(json.dumps({
31 "type": "transcription_session.update",
32 "session": {
33 "input_audio_format": "pcm16",
34 "input_audio_transcription": {"model": MODEL, "language": "en-US"},
35 "input_audio_params": {"sample_rate_hz": SAMPLE_RATE, "num_channels": 1},
36 "recognition_config": {
37 "max_alternatives": 1,
38 "enable_automatic_punctuation": True,
39 },
40 },
41 }))
42
43 async def send():
44 # 2. Stream base64 PCM chunks; commit each one to get streaming output.
45 for i in range(0, len(pcm), chunk):
46 b64 = base64.b64encode(pcm[i:i + chunk]).decode()
47 await ws.send(json.dumps({"type": "input_audio_buffer.append", "audio": b64}))
48 await ws.send(json.dumps({"type": "input_audio_buffer.commit"}))
49 await asyncio.sleep(CHUNK_MS / 1000) # pace like real-time mic
50 # 3. Flush + stop (required when sending a finite file).
51 await ws.send(json.dumps({"type": "input_audio_buffer.done"}))
52
53 async def receive():
54 # 4. delta = cumulative interim partial; completed = finalized segment.
55 async for raw in ws:
56 msg = json.loads(raw)
57 t = msg.get("type")
58 if t == "conversation.item.input_audio_transcription.delta":
59 print("partial:", msg.get("delta", ""))
60 elif t == "conversation.item.input_audio_transcription.completed":
61 print("final: ", msg.get("transcript", ""))
62 if msg.get("is_last_result"):
63 break
64
65 await asyncio.gather(send(), receive())
66
67asyncio.run(main("audio.wav"))1{
2 "type": "conversation.item.input_audio_transcription.completed",
3 "event_id": "event_abc123",
4 "item_id": "item_001",
5 "transcript": "And Sequoia, in some sense, is splitting.",
6 "is_last_result": false
7}