Skip to main content
A voice session orchestrates VAD, STT, LLM, and TTS as one pipeline. The SDK owns microphone capture, turn detection, and playback; the app consumes a stream of typed events.

Overview

Creating a session

One call names the three models and hands back a VoiceSession. The session downloads and loads what it was given, makes sure a VAD is resident, and wires the pipeline.
ModelRef is ExpressibleByStringLiteral, so a bare string works:
The tts ref takes an optional voice, which selects a voice inside the TTS model rather than naming a model:
Set downloadIfNeeded: false to fail rather than download when a model is missing.

VoiceSession

start() is the only thing that opens the microphone. Subscribing to events on its own is safe, so you can wire the UI before capture begins.
say(_:) speaks a line outside the turn loop, useful for a greeting. interrupt() stops the agent mid-utterance. close() stops capture, tears down the pipeline, and releases native resources.

VoiceEvent

AgentState is .listening, .thinking, or .speaking. .userTranscribed arrives repeatedly with isFinal: false while the user is mid-sentence, then once with isFinal: true. .agentResponse arrives per token, so append rather than replace. Recoverable component trouble comes through .error with recoverable: true and the session keeps running. An unrecoverable failure throws into the stream instead.

Turn handling

Only endpointing.minDelayMs reaches the native compose ABI today. maxDelayMs and the interruption settings are accepted, logged, and ignored until the ABI carries them.

Generation options

Pass LlmOptions to shape what the agent says.
Keeping maxOutputTokens low matters here in a way it does not for chat: every token becomes speech the user waits through.

Complete voice assistant

Session lifecycle

Hold one session at a time. start() is idempotent, so a second call on a running session does nothing, but two sessions mean two microphone drivers. Cancel the event task before closing:
close() never throws and is safe to call more than once.

Error handling

Session creation does the downloading and loading, so most failures surface here rather than during the conversation.

Best practices

createSession downloads and loads three models. Doing that when the user taps the microphone is a visible stall. Create it during onboarding or on view appear.
Read session.events and set the task up first, then call start(). Subscribing does not open the mic, so nothing is lost.
Calls and other apps interrupt the audio session. Close the session and create a new one when the interruption ends.
Tear the session down in onDisappear. A live microphone driver outlives the view otherwise.

VAD

Voice activity detection

STT

Speech-to-text