Skip to main content
Custom TTS lets you connect Rapida to any WebSocket-based speech synthesis service without writing a new Go transformer. The UI stores provider credentials and assistant options; assistant-api reads them through the custom-tts transformer and maps LLM text packets and provider audio frames with WebSocket DSL rules. Provider identifier: custom-tts

Where it is configured

1

Create the provider credential

In the Rapida dashboard, open Integrations > Models, choose Custom TTS, and create a credential.The backend also accepts snake case keys: api_compatibility and base_url.
2

Select Custom TTS on the assistant

Open the assistant voice settings and select Custom TTS as the text-to-speech provider.
3

Fill the TTS arguments

Set the audio format, query parameters, request rules, and response rules. The argument reference below maps to the UI fields and the transformer option keys.

TTS arguments

The UI stores these keys with the speaker. metadata prefix, but the transformer reads the option keys shown below. The current UI exposes the audio and DSL fields; speak.voice.id, speak.model, and speak.language are optional API/import metadata keys that request rules and query parameters can read when present.

Query parameters

Use speak.ws.query_params when your provider expects configuration in the WebSocket URL. Supported variables:
If your provider needs a voice, model, or language and those values are not present as metadata, set them as static values inside speak.ws.request_rules.

Request rules

Request rules are evaluated for normalized TTS packets produced by Rapida. Supported outbound frame types are binary, json, and text.
Add an interrupt rule if your provider needs an explicit cancel/clear message. Without it, queued provider audio can continue after the user starts speaking.

One-shot synthesis

Use this when the provider synthesizes each text packet immediately.

Two-step synthesis with done

Use this when the provider expects text first and a final flush/done packet.

Response rules

Response rules parse provider WebSocket frames into Rapida audio packets. Supported inbound frame types: Supported emit keys:

Binary audio responses

JSON base64 audio responses


TTS DSL design

Custom TTS uses a JSON-template DSL with three sections: query parameters, request rules, and response rules. The DSL is intentionally small. It does not run scripts, call functions, concatenate strings, perform regex matching, or read environment variables.

TTS frame support

Inbound parsing rules:
  • WebSocket message type 2 is treated as binary.
  • Non-binary messages are parsed as JSON when they contain exactly one valid JSON value.
  • Non-JSON messages are treated as text, but TTS response rules do not support text response frames.

TTS operators

Every operator object must contain only that operator and its required field. Unsupported TTS operators:
  • $frame: "text" and $frame: "json" are not supported for TTS emit rules.
  • $decode supports only base64.

Cast behavior

JSON path behavior

$path uses dot-separated paths.
Objects are traversed by key. Arrays are traversed by numeric index.
Limits:
  • Keys containing a literal dot are not addressable.
  • Request rules can only read from config and packet.
  • Response rules can use $path only with JSON response frames.
  • A missing path in when.path means the rule does not match.
  • A missing path in emit or send.body is an error.

Query parameter rules

speak.ws.query_params must be a flat JSON object. Each value must resolve to a primitive value: string, number, boolean, or null. Nested objects and arrays are rejected unless the object is a DSL expression.
The rendered query parameters are appended to baseUrl. Existing query parameters in baseUrl are preserved unless the same key is rendered by speak.ws.query_params.
TTS request rules can read packet.text, but text is not a supported query parameter variable.

Request rule shape

speak.ws.request_rules is an ordered JSON array. Every matching rule is sent, so one packet can produce multiple WebSocket messages. Body validation depends on send.frame:

TTS request scope

The request scope is the data available to $path in request rules.
For done and interrupt, packet.text is present but may be empty.

Response rule shape

speak.ws.response_rules is an ordered JSON array. The first matching rule is evaluated; later rules are skipped for that frame. Match behavior:

TTS emit keys

Binary audio response

Use this for providers that stream raw audio as binary WebSocket frames.

JSON base64 audio response

Use $decode when the provider returns base64-encoded audio inside JSON.

Text, done, and interrupt recipe

Use this pattern when the provider expects text payloads, an explicit final message, and an explicit cancel message.

Runtime behavior

  • The connection URL is built from baseUrl and speak.ws.query_params.
  • Headers are copied from the credential and are not templated.
  • The transformer opens a connection per active message/context. A new context closes the previous connection.
  • text packets open the WebSocket connection if needed.
  • done and interrupt request rules are optional. If no rule exists for that packet, nothing is sent.
  • On interruption, Rapida sends the optional interrupt rule first, then closes the connection.
  • Audio returned by the provider is interpreted as speak.audio.encoding and speak.audio.sample_rate, then resampled to Rapida’s internal audio format when needed.
  • If no response rule matches an inbound frame, the frame is ignored.
  • If a response emits error, Rapida emits a TTS error packet.
  • If a response emits done, Rapida closes the connection and emits a TTS end packet.

Current TTS limits

  • No regex, contains, starts-with, greater-than, or compound match conditions.
  • No string interpolation or concatenation.
  • No fallback values inside expressions.
  • No dynamic headers or dynamic URL path segments.
  • No text response handling for TTS.
  • No $frame: "json" selector in emit rules.
  • $decode supports only base64.

Backend mapping

assistant-api resolves custom-tts in api/assistant-api/internal/transformer/transformer.go, then dispatches to the WebSocket v1 implementation in api/assistant-api/internal/transformer/custom/tts_websocket_v1. The WebSocket v1 transformer validates:
  • baseUrl is present in the credential.
  • speak.audio.encoding is not empty.
  • speak.audio.sample_rate is positive.
  • speak.ws.request_rules contains at least one text packet rule.
  • speak.ws.response_rules contains at least one rule.

Custom STT

Configure WebSocket speech-to-text.

TTS overview

Transformer interface and supported providers.