OpenAI
Speech-to-text with OpenAISTTService (segmented, Audio API) and OpenAIRealtimeSTTService (streaming, Realtime API, optional server VAD).
Overview
pipedog ports both of Pipecat's OpenAI speech-to-text services:
OpenAISTTService: transcribes each speech segment with the Audio API (audio.transcriptions). It waits for the user to stop speaking, then sends the segment as a WAV file. Works with the GPT transcription models and Whisper (whisper-1).OpenAIRealtimeSTTService: streams audio over a WebSocket to the Realtime API in transcription-only mode, with interim results. Turns come from a local VAD (the default) or from OpenAI's server-side VAD.
Upstream page
Pipecat's documentation for the same services.
Source
packages/openai/src/stt.ts.
OpenAI docs
Transcription models and languages.
Installation
bun add @pipedog/openaiimport { OpenAIRealtimeSTTService, OpenAISTTService } from "@pipedog/openai/stt";OpenAISTTService builds on BaseWhisperSTTService from @pipedog/whisper, which
@pipedog/openai depends on.
Prerequisites
- An OpenAI account and an API key from the OpenAI dashboard.
- Pass the key as
apiKey.OpenAISTTServicefalls back toOPENAI_API_KEYwhen you leave it out;OpenAIRealtimeSTTServicerequires it. The examples read it fromOPENAI_API_KEY.
OpenAISTTService
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new OpenAISTTService.Settings({...})
(OpenAISTTSettings). Any setting you leave out keeps the default below.
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "gpt-transcribe" | Transcription model, for example "whisper-1". |
language | Language | Language.EN | Transcription language. |
prompt | string | null | null | Text that guides the style, or continues a previous segment. |
temperature | number | null | null | Sampling temperature, 0 to 1. |
keywords | string[] | null | null | Words or phrases to guide transcription. Supported by "gpt-transcribe". |
Usage
import { Language } from "@pipedog/core/transcriptions/language";
import { OpenAISTTService } from "@pipedog/openai/stt";
const stt = new OpenAISTTService({
apiKey: process.env.OPENAI_API_KEY ?? "",
settings: new OpenAISTTService.Settings({
model: "gpt-transcribe",
language: Language.EN,
keywords: ["pipedog", "Pipecat"],
}),
});Whisper, keeping empty transcripts:
const whisper = new OpenAISTTService({
apiKey: process.env.OPENAI_API_KEY ?? "",
settings: new OpenAISTTService.Settings({ model: "whisper-1", temperature: 0 }),
pushEmptyTranscripts: true,
});Notes
- A segmented service: it needs a VAD (in the user aggregator) to mark where each segment ends,
then pushes one
TranscriptionFrameper segment. There are no interim results. pushEmptyTranscripts: truepushes aTranscriptionFrameeven when nothing was transcribed, so the bot can go on when the VAD fired on noise.- With
includeProbMetrics: true, GPT transcription models report per-token logprobs and Whisper models per-segment logprobs. Diarization models report none, with a warning. httpClientreplaces the SDK'sfetch, for example to set a longer request timeout.
OpenAIRealtimeSTTService
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new OpenAIRealtimeSTTService.Settings({...})
(OpenAIRealtimeSTTSettings).
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "gpt-realtime-whisper" | Transcription model. |
language | Language | Language.EN | Transcription language, sent as its ISO-639-1 code. |
prompt | string | null | null | Text that guides the style. Not supported by "gpt-realtime-whisper", so it is dropped (with a warning) for that model. |
noiseReduction | "near_field" | "far_field" | null | null | Noise reduction for close or distant microphones. null turns it off. |
delay | "minimal" | "low" | "medium" | "high" | "xhigh" | null | null | How long the model waits before emitting text, trading latency for accuracy. Only "gpt-realtime-whisper". |
Usage
import { Language } from "@pipedog/core/transcriptions/language";
import { OpenAIRealtimeSTTService } from "@pipedog/openai/stt";
// Local VAD (the default): a VAD in the user aggregator ends each segment.
const stt = new OpenAIRealtimeSTTService({
apiKey: process.env.OPENAI_API_KEY ?? "",
settings: new OpenAIRealtimeSTTService.Settings({
model: "gpt-realtime-whisper",
language: Language.EN,
noiseReduction: "near_field",
delay: "low",
}),
});Server-side VAD. OpenAI detects the turns, so the pipeline needs no local VAD:
// OpenAI detects the turns. Use a model with turn detection, and no local VAD.
const serverVad = new OpenAIRealtimeSTTService({
apiKey: process.env.OPENAI_API_KEY ?? "",
turnDetection: { type: "server_vad", threshold: 0.5 },
settings: new OpenAIRealtimeSTTService.Settings({ model: "gpt-transcribe" }),
});Settings sent mid-call with an STTUpdateSettingsFrame are applied with a session.update on
the open session.
Event handlers
| Event | Description |
|---|---|
on_connected | The WebSocket connected. |
on_disconnected | The WebSocket closed. |
on_connection_error | The connection failed. |
Notes
- Emits
InterimTranscriptionFrames as text arrives and aTranscriptionFramefor each completed segment. turnDetectiondefaults tofalse: a local VAD drives turns, and eachVADUserStoppedSpeakingFramecommits the audio buffer so the server transcribes the segment.- With
turnDetection: { type: "server_vad" }, the service broadcastsProposedUserStartedSpeakingFrameandProposedUserStoppedSpeakingFramefrom the server's speech events, and asks the user aggregator forExternalUserTurnStrategies(unless you pass your ownuserTurnStrategies).shouldInterrupt(defaulttrue) decides whether those turns interrupt the bot. Server VAD needs a model with turn detection:gpt-transcribehas it,gpt-realtime-whisperdoes not. See turn detection. - Audio is sent as 24 kHz 16-bit mono PCM. Other input rates are resampled.
- The Cloudflare Workers example uses this service with server VAD, so the Worker loads no local VAD model.
Runtime notes
| Runtime | Works | What it needs |
|---|---|---|
| Bun | ✅ yes | Nothing. |
| Node.js | ✅ yes | Nothing. |
| Deno | ✅ yes | Nothing. |
| Cloudflare Workers | ✅ yes | Nothing. |
Generated from pipecat.runtimes and pipecat.compat in packages/openai/package.json: ✅ means the compat smoke test passes on that runtime.
OpenAISTTServiceuses theopenaiJS SDK overfetch. It works on Bun, Node, Deno and Cloudflare Workers.OpenAIRealtimeSTTServicesends the key as anAuthorizationheader on the WebSocket handshake. Bun, Node and Deno send it from the globalWebSocket; on Cloudflare Workers core opens the socket throughfetch.- The deprecated
model,language,prompt,temperatureandnoiseReductionconstructor options are not ported. Usesettings.
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).