Hear it working first
The SDK’s terminal voice loop records from your microphone and prints partial and final transcripts. It speaks each final transcript back through Rime text-to-speech. It needs a Rime API key and SoX.
How a stream works
Your code must meet two deadlines on each stream:- A stream fails after 15 seconds without audio. Send audio through pauses, and include silence.
- A stream must finish within 120 seconds of connecting.
Where audio flows
WebSocket API cannot set the Authorization header that carries the key.
Decode the audio to headerless PCM16 before you pass it to the SDK. PCM16 is signed 16-bit little-endian samples, with stereo channels interleaved. The SDK does not decode other formats.
Send audio as you capture it
Give each utterance its own queue. Your capture code puts audio frames in the queue as they arrive, and the SDK takes the frames out at the other end. Capture never waits for the network, because audio that you capture while the stream connects stays in the queue until Rime accepts the stream. If your capture path drops silent frames, put frames of zeros in the queue in their place. This function transcribes one utterance from a queue. It returns the final transcript and the request ID. In each tab, the comments in the second code block show where to connect your capture and turn detection.- Python
- Node.js
stream_utterance.py
call_soon_threadsafe method safely passes their frames to the event loop from any thread:- Input format: declare the format that your capture produces. The example uses 48 kHz mono, a common rate for browser and WebRTC audio. For telephone audio, use 8000. The SDK converts 8, 16, 24, or 48 kHz audio, mono or stereo, to the 16 kHz mono that the service reads. Because the SDK converts from the rate that you declare, a wrong rate distorts the audio, and the SDK does not raise an error.
- Timeout: a
timeoutof 120 stops the call on the client side at the service limit. Python and Node.js both take the value in seconds. The timeout clock includes the time that the SDK waits for your audio source. - Update loop: do not do slow work in the update loop, such as waiting for your language model. A slow loop also slows the stream, through backpressure.
Open each stream when speech starts
Start the stream for each utterance when your voice activity detector reports speech. Do not start it earlier. A stream that opens early costs more than it saves:- It holds service capacity from the time Rime accepts it until it ends, even when nobody speaks.
- Its 120-second clock starts when it connects.
- Silence before speech counts toward the stream’s limits. For each partial transcript, the model recognizes all of the stream’s audio again. Leading silence uses model context that the speech needs.
- Create one client per process. Pass it to every utterance, as the examples do. A new client for each utterance must open its own connection first.
- Keep a short pre-roll. A voice activity detector reports speech a short time after the speech starts. By then, the first syllable is past. Keep the most recent audio in a short rolling buffer. Put its contents in the new queue before any other frame.
Show partials and act on the final
Each partial transcript is the full transcript so far, so replace the text on screen with each partial. If you append partials, words repeat. The service also does not send an update for each audio chunk. For example, the quickstart’s run got three partials in a 9-second call, then the final transcript. Send only the final transcript to your language model. Partials can still change, and only the final transcript covers all of the audio. Even an empty final transcript is a successful result. It means that the audio had no recognizable speech, and your agent decides whether to respond.End the utterance
End the audio source when your turn detector decides that the speaker has finished:
Ending the source does not cancel the stream. The SDK sends the remaining audio, and Rime recognizes that audio and sends the final transcript. Read updates until the final transcript arrives, and then start a new stream for the next utterance.
Split long turns at pauses
For each partial transcript, the model reads all of the stream’s audio again. So a long turn runs out of model context before the stream’s 120 seconds end. The turn then fails with a resource-limit error and no final transcript. When one speaker talks for a long time, split the turn into several utterances:- Find the first pause after about a minute of speech. Split the turn at that pause. Do this even when the pause is too short to end the turn for your turn detector.
- To split, start the next stream on a new queue. Switch your capture code to the new queue, and then end the old utterance. This order keeps every frame of audio in one of the two streams.
- While a later part streams, show the earlier final transcripts, followed by the current partial. When the turn ends, join the final transcripts in order, with a space between them. Send the joined text to your language model.
Cancel when the caller hangs up
Cancel the stream when the caller hangs up during a sentence, or when your agent no longer needs the utterance. Also stop capturing audio for it. A cancelled stream never produces a final transcript.How to cancel in each SDK
How to cancel in each SDK
Common mistakes
Most of these mistakes do not raise an error. Each row links to the section that covers it:Call the API directly
To call the WebSocket or gRPC API without the SDK, start at Choose how to connect, which compares the transports and the output contracts. The WebSocket and gRPC references give each message and audio rule, and the WebSocket reference shows how to transcribe a long recording.Next
Recognition settings
Switch to word-for-word text, or add the names that your callers say.
Speak the reply
Choose a starter that streams your agent’s reply through Rime text-to-speech.

