Kimi K3 is here. Try it now
transcription

NVIDIA logoNVIDIA Nemotron 3 ASR (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 repository

Example usage

This deployment is a Baseten docker_server model: the Baseten WebSockettransport proxies your connection straight through to the NIM's native Riva Realtime WebSocket API. There is no custom Baseten schema, and you speak Riva's OpenAI-Realtime-style JSON event protocol directly.

Two kinds of transcription messages

As audio streams in, the realtime API returns two kinds of transcription events:

  • Partial results (...transcription.delta) — live, in-progress hypotheses that update continuously as you speak. Ideal for low-latency captions that refine in real time. Each event is an incremental delta, not the full hypothesis — concatenate the deltas within a segment to build the running caption.

  • Final results (...transcription.completed) — stable, punctuated transcripts for each completed segment. These won't change and represent the authoritative output.

Prerequisites

pip install websockets
export BASETEN_API_KEY=...

Input must be a mono, 16-bit, 16 kHz PCM WAV. The server treats the bytes as raw pcm16 at the declared sample rate, so stereo or off-rate input transcribes to garbage (or nothing) — convert it before streaming (see the note under load_pcm).

Running it

python transcribe_to_json.py audio.wav out.json
Input
1import asyncio
2import base64
3import json
4import os
5import sys
6import wave
7
8import websockets
9
10URL = (
11    "wss://model-<id>.api.baseten.co/environments/production"
12    "/websocket?intent=transcription"
13)
14API_KEY = os.getenv("BASETEN_API_KEY")
15MODEL = "cache-aware-parakeet-rnnt-en-US-asr-streaming-sortformer"
16COMPLETED = (
17    "conversation.item."
18    "input_audio_transcription.completed"
19)
20SAMPLE_RATE = 16000
21CHUNK = SAMPLE_RATE * 250 // 1000 * 2  # 250 ms of 16-bit mono PCM
22
23
24async def transcribe(wav_path):
25    with wave.open(wav_path, "rb") as wf:
26        # need mono 16-bit 16 kHz PCM WAV
27        pcm = wf.readframes(wf.getnframes())
28
29    segments = []
30    async with websockets.connect(
31        URL,
32        additional_headers={
33            "Authorization": f"Api-Key {API_KEY}",
34        },
35    ) as ws:
36        await ws.send(
37            json.dumps(
38                {
39                    "type": "transcription_session.update",
40                    "session": {
41                        "input_audio_format": "pcm16",
42                        "input_audio_transcription": {
43                            "model": MODEL,
44                            "language": "en-US",
45                        },
46                        "input_audio_params": {
47                            "sample_rate_hz": SAMPLE_RATE,
48                            "num_channels": 1,
49                        },
50                        "recognition_config": {
51                            "enable_automatic_punctuation": True,
52                        },
53                    },
54                }
55            )
56        )
57
58        for i in range(0, len(pcm), CHUNK):
59            await ws.send(
60                json.dumps(
61                    {
62                        "type": "input_audio_buffer.append",
63                        "audio": base64.b64encode(
64                            pcm[i : i + CHUNK]
65                        ).decode(),
66                    }
67                )
68            )
69            await ws.send(
70                json.dumps({"type": "input_audio_buffer.commit"})
71            )
72        await ws.send(
73            json.dumps({"type": "input_audio_buffer.done"})
74        )
75
76        async for raw in ws:
77            msg = json.loads(raw)
78            if msg.get("type") == COMPLETED:
79                segments.append(msg.get("transcript", ""))
80                if msg.get("is_last_result"):
81                    break
82
83    return {
84        "transcript": " ".join(segments),
85        "segments": segments,
86    }
87
88
89if __name__ == "__main__":
90    wav_path, out_path = sys.argv[1], sys.argv[2]
91    result = asyncio.run(transcribe(wav_path))
92    with open(out_path, "w") as f:
93        json.dump(result, f, indent=2)
94    print(f"wrote {out_path}")
JSON output
1{
2    "transcript": "He hoped there would be stew for dinner. Turnips and carrots and bruised potatoes.",
3    "segments": [
4        "He hoped there would be stew for dinner.",
5        "Turnips and carrots and bruised potatoes."
6    ]
7}

🔥 Trending models