Rime
Text-to-speech with RimeTTSService (WebSocket, word timestamps), RimeHttpTTSService (HTTP) and RimeV1TTSService (WebSocket v1, protobuf).
Overview
Rime's Coda and Mist models turn text into speech. pipedog ports these Pipecat services:
RimeTTSService: Rime's JSON WebSocket API (/ws3), with word timestamps and one audio context per turn.RimeHttpTTSService: Rime's HTTP API, streaming raw PCM, for when a WebSocket is not needed.RimeV1TTSService: Rime's WebSocket v1 API, which carries protobuf envelopes (binary or proto3 JSON). Interruptions cancel a context without reconnecting, and Rime can accumulate streamed LLM tokens into sentences.
Upstream page
Pipecat's documentation for the same services.
Source
packages/rime/src/tts.ts and tts_v1.ts.
Rime docs
The WebSocket v1 protocol, voices and models.
Installation
bun add @pipedog/rimeimport { RimeHttpTTSService, RimeTTSService } from "@pipedog/rime/tts";
import { RimeV1TTSService } from "@pipedog/rime/tts_v1";Prerequisites
- A Rime account and an API key from the Rime dashboard.
- A voice (
speaker) for the model you use. There is no default voice. - Pass the key as
apiKey. The examples read it fromRIME_API_KEY.
RimeTTSService
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new RimeTTSService.Settings({...}).
RimeHttpTTSService uses the same settings class. Each setting is only sent when set; the
sampling settings go to coda and the mistv2 ones to the other models.
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "coda" | Rime model: coda, or a Mist model such as mistv2. |
voice | string | null | null | Voice (speaker). Set one. |
language | Language | null | null | Language. Sent as a Rime code such as eng. |
segment | string | null | null | "immediate", "bySentence" or "never". |
speedAlpha | number | null | null | Speech speed multiplier. |
inlineSpeedAlpha | string | null | null | Inline speed markup. |
repetitionPenalty, temperature, topP | number | null | null | Coda sampling (wire names repetition_penalty, temperature, top_p). |
timeScaleFactor | number | null | null | Coda playback speed. Above 1.0 slows the audio down. |
reduceLatency, pauseBetweenBrackets, phonemizeBetweenBrackets, noTextNormalization, saveOovs | boolean | null | null | Mist options. |
Usage
import { TTSUpdateSettingsFrame } from "@pipedog/core/frames/frames";
import { RimeTTSService } from "@pipedog/rime/tts";
const tts = new RimeTTSService({
apiKey: process.env.RIME_API_KEY ?? "",
settings: new RimeTTSService.Settings({
model: "coda",
voice: "luna",
timeScaleFactor: 1.0,
}),
});
Text helpers for Rime's markup:
// Text helpers for Rime's markup, for use in text transforms.
const spelled = RimeTTSService.SPELL("A1B2");
const paused = `One moment.${RimeTTSService.PAUSE_TAG(0.5)} Thanks.`;
// Custom pronunciation and inline speed apply to the next message sent.
const pronounced = tts.PRONOUNCE("Say hello", "hello", "h'El0");
const slow = tts.INLINE_SPEED("slowly", 0.7);Change settings mid-call with a TTSUpdateSettingsFrame:
// Queue it on the worker, or push it from a processor upstream of the service.
// Every setting of the WebSocket service is a URL parameter, so a change reconnects.
const update = new TTSUpdateSettingsFrame({
delta: new RimeTTSService.Settings({ voice: "another-voice" }),
});Event handlers
Inherited from TTSService:
| Event | Description |
|---|---|
on_connected | Connected to Rime. |
on_disconnected | Disconnected from Rime. |
on_connection_error | The connection failed. |
on_tts_request | A text chunk is about to be sent for synthesis. |
Notes
- Every setting is a query parameter of the WebSocket URL, so any change reconnects.
- The service does not push the LLM's text frames. It builds
TTSTextFrames from Rime's word timestamps. - Text inside
spell(...)is never split across aggregates. PRONOUNCE()andINLINE_SPEED()add fields to the next message only.
RimeHttpTTSService
Configuration
Prop
Type
Usage
import { RimeHttpTTSService } from "@pipedog/rime/tts";
const httpTts = new RimeHttpTTSService({
apiKey: process.env.RIME_API_KEY ?? "",
settings: new RimeHttpTTSService.Settings({ model: "mistv2", voice: "luna", speedAlpha: 1.1 }),
});
Notes
- It posts to
https://users.rime.ai/v1/rime-ttsand streams the PCM body. There are no word timestamps. - The default
languageis"eng".
RimeV1TTSService
Configuration
Prop
Type
Settings
Passed as settings: new RimeV1TTSService.Settings({...}). model is required at construction.
Each context uses the settings in effect when it starts. A setting the model does not accept is
left out with a warning.
| Setting | Type | Description |
|---|---|---|
model | string | Required. "coda" and "mistv3" select their public endpoints. Changing it later is reported, not applied. |
voice | string | null | Voice (speaker). |
language | Language | null | Sent as a BCP-47 tag such as en. |
timeScaleFactor | number | null | Playback speed. Not for mistv2. |
textLookaheadTokens | number | null | Tokens to collect before audio starts (Coda only). |
pauseBetweenBrackets, phonemizeBetweenBrackets, saveOovs | boolean | null | Mist options. |
Usage
import { TextAggregationMode } from "@pipedog/core/services/tts_service";
import { RimeV1TTSService } from "@pipedog/rime/tts_v1";
const v1 = new RimeV1TTSService({
apiKey: process.env.RIME_API_KEY ?? "",
// "binary" (protobuf) or "json" (proto3 JSON). Binary is the default.
websocketProtocol: "binary",
// Stream LLM tokens and let Rime accumulate them into sentences.
textAggregationMode: TextAggregationMode.TOKEN,
// The model is required: "coda" and "mistv3" select Rime's public endpoints.
settings: new RimeV1TTSService.Settings({ model: "coda", voice: "luna" }),
});Notes
- Pass
websocketUrlfor a custom deployment. The model is never inferred from the URL. - Rime v1 sends a
readyevent before the first context. The service waits up to 10 seconds for it and reports a connection error otherwise. - A failed context is reported with the error's
ErrorCategory, and the service stays usable for the next turn. - Rime v1 does not report word timestamps.
Runtime notes
- The WebSocket services use the global
WebSocketand send the key as anAuthorizationheader on the handshake. That works on Bun, Node and Deno, and on Cloudflare Workers, where the socket is opened throughfetch. RimeV1TTSServicereads the negotiated subprotocol (rime.v1.binaryorrime.v1.json) from the socket and refuses a connection that did not accept it.- The protobuf messages come from Rime's own package,
@rimelabs/api, with@bufbuild/protobuf. Both are plain JavaScript. RimeHttpTTSServiceusesfetch. Upstream's requiredaiohttp_sessionis replaced by an optionalfetchoption.- Not ported: the deprecated
RimeNonJsonTTSServiceand theInputParamsclasses, and thevoiceId,model,paramsandaggregateSentencesconstructor options. - Whole-number floats, such as
PAUSE_TAG(0.5)'s500, print without Python's.0.
| 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/rime/package.json: ✅ means the compat smoke test passes on that runtime.
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).