pipedog is an unofficial community port of Pipecat, not affiliated with Daily.
pipedog
Speech-to-text

ElevenLabs

Speech-to-text with ElevenLabsSTTService (segmented uploads) and ElevenLabsRealtimeSTTService (WebSocket streaming).

Overview

ElevenLabs Scribe transcribes speech. pipedog ports both of Pipecat's ElevenLabs speech-to-text services:

  • ElevenLabsSTTService: uploads each speech segment, cut by the pipeline's VAD, to ElevenLabs' /v1/speech-to-text endpoint and pushes the transcript it gets back.
  • ElevenLabsRealtimeSTTService: streams audio over ElevenLabs' realtime WebSocket API, with partial and committed transcripts. Segments are committed by the pipeline's VAD or by ElevenLabs' own.

Installation

bun add @pipedog/elevenlabs
import {
  CommitStrategy,
  ElevenLabsRealtimeSTTService,
  ElevenLabsSTTService,
} from "@pipedog/elevenlabs/stt";

Prerequisites

  • An ElevenLabs account and an API key from the ElevenLabs dashboard.
  • Pass the key as apiKey. The examples read it from ELEVENLABS_API_KEY.

ElevenLabsSTTService

Configuration

Prop

Type

Settings

Runtime-updatable settings, passed as settings: new ElevenLabsSTTService.Settings({...}). Any setting you leave out keeps the default below. null means the field is not sent, so ElevenLabs uses its own default.

SettingTypeDefaultDescription
modelstring"scribe_v2"Scribe model.
languageLanguage | nullLanguage.ENTranscription language. null lets ElevenLabs detect it.
tagAudioEventsboolean | nullnullInclude audio events such as (laughter) in the transcript.
keytermsstring[] | nullnullTerms to bias transcription towards.
noVerbatimboolean | nullnullDrop filler words, false starts and non-speech sounds. scribe_v2 only.

Usage

stt.ts
import { STTUpdateSettingsFrame } from "@pipedog/core/frames/frames";
import { Language } from "@pipedog/core/transcriptions/language";
import {
  CommitStrategy,
  ElevenLabsRealtimeSTTService,
  ElevenLabsSTTService,
} from "@pipedog/elevenlabs/stt";

const stt = new ElevenLabsSTTService({
  apiKey: process.env.ELEVENLABS_API_KEY ?? "",
  settings: new ElevenLabsSTTService.Settings({
    model: "scribe_v2",
    language: Language.EN,
    keyterms: ["pipedog", "Pipecat"],
  }),
});

Notes

  • A segmented service: it needs VAD in the pipeline (on the user aggregator). Audio between the VAD's start and stop is sent as one WAV upload, padded with a little trailing silence.
  • Transcription runs in the background, so audio keeps flowing while a segment is transcribed.
  • Emits one TranscriptionFrame per segment, with the language code ElevenLabs returns. There are no interim results.

ElevenLabsRealtimeSTTService

Configuration

Prop

Type

Settings

SettingTypeDefaultDescription
modelstring"scribe_v2_realtime"Scribe realtime model.
languageLanguage | nullnull (auto-detect)Transcription language.
keytermsstring[] | nullnullTerms to bias transcription towards.
noVerbatimboolean | nullnullDrop filler words, false starts and non-speech sounds.
vadSilenceThresholdSecsnumber | nullnullSilence before ElevenLabs' VAD commits, 0.3 to 3.0 s. CommitStrategy.VAD only.
vadThresholdnumber | nullnullVAD sensitivity, 0.1 to 0.9 (lower is more sensitive). CommitStrategy.VAD only.
minSpeechDurationMsnumber | nullnullMinimum speech duration, 50 to 2000 ms. CommitStrategy.VAD only.
minSilenceDurationMsnumber | nullnullMinimum silence duration, 50 to 2000 ms. CommitStrategy.VAD only.
filterBackgroundAudioboolean | nullnullFilter background audio before transcription.

Usage

realtime.ts
const realtime = new ElevenLabsRealtimeSTTService({
  apiKey: process.env.ELEVENLABS_API_KEY ?? "",
  settings: new ElevenLabsRealtimeSTTService.Settings({
    language: Language.EN,
    keyterms: ["pipedog"],
  }),
});

With ElevenLabs' VAD committing segments:

// Let ElevenLabs decide when a segment ends instead of the pipeline's VAD.
const serverVad = new ElevenLabsRealtimeSTTService({
  apiKey: process.env.ELEVENLABS_API_KEY ?? "",
  commitStrategy: CommitStrategy.VAD,
  includeTimestamps: true,
  settings: new ElevenLabsRealtimeSTTService.Settings({
    vadSilenceThresholdSecs: 0.8,
    vadThreshold: 0.4,
  }),
});

Change settings mid-call with an STTUpdateSettingsFrame. The service reconnects with the new settings:

// Queue it on the worker, or push it from a processor upstream of the service.
const update = new STTUpdateSettingsFrame({
  delta: new ElevenLabsRealtimeSTTService.Settings({ language: Language.ES }),
});

Event handlers

Inherited from STTService:

EventDescription
on_connectedConnected to ElevenLabs.
on_disconnectedDisconnected from ElevenLabs.
on_connection_errorThe connection failed.

Notes

  • Emits InterimTranscriptionFrame for partial transcripts and TranscriptionFrame for committed ones.
  • With CommitStrategy.MANUAL (the default), a VADUserStoppedSpeakingFrame makes the service send a commit, so the pipeline's VAD decides where segments end. With CommitStrategy.VAD, ElevenLabs decides, using the vad* and min*DurationMs settings.
  • The audio format is chosen from the sample rate: 8000, 16000, 22050, 24000, 44100 or 48000 Hz. Any other rate falls back to pcm_16000 with a warning.
  • The service sends silence as a keepalive after 10 s without audio (checked every 5 s).
  • Error messages from ElevenLabs (authentication, quota, rate limit, throttled commits, session time limit and others) are pushed as errors.
  • baseUrl is a bare host and defaults to "api.elevenlabs.io".

Runtime notes

RuntimeWorksWhat it needs
Bun✅ yesNothing.
Node.js✅ yesNothing.
Deno✅ yesNothing.
Cloudflare Workers✅ yesNothing.

Generated from pipecat.runtimes and pipecat.compat in packages/elevenlabs/package.json: ✅ means the compat smoke test passes on that runtime.

  • ElevenLabsSTTService uploads with fetch and FormData. Upstream's required aiohttp_session is replaced by an optional fetch option (the global fetch by default).
  • ElevenLabsRealtimeSTTService uses the global WebSocket and sends the key as an xi-api-key header on the handshake. That works on Bun, Node and Deno, and on Cloudflare Workers, where the socket is opened through fetch.
  • Not ported: the deprecated InputParams classes and the model / params constructor options. Use settings.

pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.

Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).

On this page