The Free Encyclopedia

Parakeet ASR Setup

When theLAB moved ADI's live transcription — and the Scribe meeting recorder — off Whisper, the replacement was NVIDIA NeMo Parakeet. On English it's fast and accurate, with noticeably lower latency than Whisper for the same job, which is exactly what a real-time voice loop needs.

The trick that makes it a drop-in is the wrapper: Parakeet runs behind an OpenAI-compatible transcription endpoint. Any client that already speaks OpenAI STT just swaps the base URL and keeps working. In ADI it lives on the host apexlabs-voicebox at http://<host>:9292/v1/audio/transcriptions, on the private Tailscale net.

01Know what the endpoint wants

The contract is the standard OpenAI shape. POST multipart/form-data to /v1/audio/transcriptions with a file field carrying the audio blob and response_format=json. The response comes back as JSON containing a single text field.

Audio expectations are simple: 16 kHz mono WAV transcribes cleanly. That's not a coincidence — the wake/capture stage upstream produces exactly 16 kHz mono frames, so the WAV you hand Parakeet is already in its preferred format with no resampling.

Why OpenAI-compatible The whole point is that nothing downstream had to be rewritten. Point an existing OpenAI-style STT client at :9292/v1 and you're done — same multipart request, same {text} response. Swapping Whisper for Parakeet became a base-URL change, not a client rewrite.

02Hit it directly with curl

Before wiring anything into the app, confirm the endpoint is alive with a raw request. Record or grab a 16 kHz mono chunk.wav and post it.

# straight at Parakeet on apexlabs-voicebox (Tailscale net)
curl -s http://apexlabs-voicebox:9292/v1/audio/transcriptions \
    -F [email protected] \
    -F response_format=json
# → {"text": "your spoken words come back here"}

If that returns a populated text, the model and endpoint are healthy and every other layer is just plumbing.

03The PHP proxy

Clients don't talk to Parakeet directly. ADI's browser recorder POSTs each WAV chunk to a small PHP proxy, live-transcribe.php, which forwards it to the endpoint and hands back the {text}. The proxy reads the configured stt_base_url from settings and falls back to PARAKEET_TRANSCRIBE_URL when that setting is unset — so the host can be re-pointed without touching code.

The forwarding itself is a CURLFile mirroring the curl call above:

<?php
// $tmp = the uploaded 16 kHz mono WAV chunk on disk
$base = $sttBaseUrl ?: getenv('PARAKEET_TRANSCRIBE_URL');
$cf   = new CURLFile($tmp, 'audio/wav', 'chunk.wav');

$ch = curl_init($base);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POSTFIELDS     => [
        'file'            => $cf,
        'response_format' => 'json',
    ],
]);
$resp = curl_exec($ch);
$text = json_decode($resp, true)['text'] ?? '';
The chunk loop The full real-time path is: the browser records a short audio chunk → POSTs the WAV to live-transcribe.php → the proxy forwards to Parakeet → {text} flows back and is appended to the live transcript. Keep chunks small for responsiveness; Parakeet's low latency keeps the loop feeling live.

Verify it took

  • The direct curl against :9292/v1/audio/transcriptions returns a non-empty text.
  • The proxy returns the same words for the same chunk — confirms stt_base_url / PARAKEET_TRANSCRIBE_URL resolves to the live host.
  • A 16 kHz mono WAV transcribes cleanly; if accuracy drops, check the chunk really is 16 kHz mono and not stereo or resampled.

Parakeet is the middle stage of ADI's voice stack: the wake word feeds it 16 kHz frames upstream, and the transcribed text drives the LLM downstream. See the fully-local voice stack for how the pieces fit together end to end.