Transcription · Stream transcripts with VAD and end-of-turn events4 / 7
  1. 01
  2. 02
  3. 03
  4. 05
  5. 06
  6. 07

Stream transcripts with VAD and end-of-turn events

Example on GitHub(packages/sdk/examples/transcription/whispercpp-microphone-conversation.ts)

Now that we can stream transcripts from a microphone, we're going to surface the VAD and end-of-turn events the engine already tracks.

transcribeStream returns a duplex session. Audio goes in via session.write(chunk), and the session yields a discriminated union of events: text chunks, voice-activity state, and turn boundaries. A real-time voice assistant builds on top of those three event types.

Setting emitVadEvents: true and an endOfTurnSilenceMs of 800 surfaces VAD and end-of-turn events. The session open looks like this:

const session = await transcribeStream({
  modelId,
  emitVadEvents: true,
  endOfTurnSilenceMs: 800,
});

void (async () => {
  for await (const chunk of audioStream()) {
    session.write(chunk);
  }
})();

Three event types on the same duplex stream, one for await + switch is the canonical consume pattern:

for await (const event of session) {
  switch (event.type) {
    case "text":
      console.log(`> ${event.text.trim()}`);
      break;
    case "vad":
      console.log(`▸ [vad] speaking=${event.speaking} probability=${event.probability.toFixed(2)}`);
      break;
    case "endOfTurn":
      console.log(`▸ [endOfTurn] silence ${event.silenceDurationMs}ms\n`);
      break;
  }
}

The vad event fires while the speaker is talking. The endOfTurn event fires after the speaker pauses for endOfTurnSilenceMs milliseconds. That silence window is the conversation-equivalent of "they're done; now I can answer."

Note: endOfTurn measures silence from the VAD, not from a parakeet EOU token. Pair the Whisper model with VAD_SILERO_5_1_2 in modelConfig.vadModelSrc so the silence window is measured accurately.

Put it to the test

  1. Open transcribeStream({ modelId, emitVadEvents: true, endOfTurnSilenceMs: 800 }), pipe the mic stream's Float32Array frames into session.write(chunk) in a background task, then iterate for await (const event of session).
  2. Switch on event.type. Console.log text events as transcript lines, vad events as VAD ticks, and endOfTurn events as turn boundaries.
index.ts

$ Run your code to see results

$