Azure Speech
Continuous speech recognition from Azure AI Speech with AzureSTTService, over the Speech service's WebSocket protocol.
Overview
AzureSTTService streams the user's audio to Azure AI Speech and turns the results into
transcription frames: an InterimTranscriptionFrame for each hypothesis while the user speaks,
and a TranscriptionFrame (with finalized: true) for each recognized phrase.
Upstream drives Azure through the native Speech SDK. pipedog speaks the Speech service's own WebSocket protocol (the one the SDKs use) on the continuous "conversation" endpoint, so no SDK is needed and the service runs on every runtime.
Upstream page
Pipecat's documentation for the same service.
Source
packages/azure/src/stt.ts and speech_protocol.ts.
Azure docs
Recognition languages, segmentation and custom models.
Installation
bun add @pipedog/azureimport { AzureSTTService } from "@pipedog/azure/stt";Prerequisites
- An Azure AI Speech (or Azure AI Foundry) resource, with its key and region from the portal's Keys and Endpoint page.
- Pass the key as
apiKeyand the region (e.g."eastus") asregion. The examples read them fromAZURE_SPEECH_API_KEYandAZURE_SPEECH_REGION.
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new AzureSTTService.Settings({...}). Any
setting you leave out keeps the default below.
| Setting | Type | Default | Description |
|---|---|---|---|
language | Language | string | Language.EN_US | Recognition language, sent as an Azure locale ("en-US"). |
profanity | "raw" | "masked" | "removed" | null | null (Azure: "masked") | How profanity appears in transcripts. "raw" helps non-English deployments where Azure masks ordinary words. An unknown value throws. |
segmentationSilenceTimeoutMs | number | null | null (Azure: 500) | Silence, in ms (100 to 5000), inside a phrase before Azure finalizes it. Lower finalizes sooner; higher keeps hesitant speech in one transcript. |
Usage
import { AzureSTTService } from "@pipedog/azure/stt";
import { STTUpdateSettingsFrame } from "@pipedog/core/frames/frames";
import { Language } from "@pipedog/core/transcriptions/language";
const stt = new AzureSTTService({
apiKey: process.env.AZURE_SPEECH_API_KEY ?? "",
region: process.env.AZURE_SPEECH_REGION ?? "eastus",
settings: new AzureSTTService.Settings({
language: Language.EN_US,
profanity: "raw",
segmentationSilenceTimeoutMs: 800,
}),
});Change settings mid-call with an STTUpdateSettingsFrame. A change to language, profanity or
segmentationSilenceTimeoutMs reconnects with the new settings:
// Queue it on the worker, or push it from a processor upstream of the service.
// The service reconnects with the new language.
const update = new STTUpdateSettingsFrame({
delta: new AzureSTTService.Settings({ language: Language.ES_ES }),
});A Private Link endpoint or custom domain replaces the region, and endpointId selects a Custom
Speech model:
const privateStt = new AzureSTTService({
apiKey: process.env.AZURE_SPEECH_API_KEY ?? "",
privateEndpoint: "wss://my-speech.cognitiveservices.azure.com/",
endpointId: process.env.AZURE_CUSTOM_SPEECH_ENDPOINT_ID ?? null,
});Notes
- The connection opens when the pipeline starts, at the pipeline's input sample rate (16-bit mono
PCM). If Azure closes it (its session limit, an error), the next audio reopens it, and an error
close is reported as an
ErrorFrame. TranscriptionFrames carryfinalized: true: Azure's phrase result is the final one for the utterance, so turn-stop strategies can act on it without waiting for VAD.- Results are requested in Azure's
simpleformat; the raw result is on the frame'sresult.
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/azure/package.json: ✅ means the compat smoke test passes on that runtime.
AzureSTTService opens its WebSocket with core's websocketConnect and works on
Bun, Node, Deno and Cloudflare Workers. The key is sent both as the Ocp-Apim-Subscription-Key
handshake header and as a query parameter, as the Speech SDK does, so it also authenticates where
a WebSocket can't send headers. Pass webSocketFactory to open the socket some other way.
Differences from upstream: there is no Speech SDK SpeechConfig; _speechConfig is a small
record of the language, profanity option and properties, and the connection opens in start()
rather than setup(). The deprecated language constructor option isn't ported; use
settings.language.
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).
AWS SageMaker (bidirectional streaming)
SageMakerBidiClient, the HTTP/2 bidirectional streaming client for speech models hosted on Amazon SageMaker endpoints.
Whisper
Speech-to-text with Whisper. WhisperSTTService and WhisperSTTServiceMLX run Whisper locally on ONNX Runtime, and BaseWhisperSTTService is the base for Whisper-compatible APIs.