Overview
Creating a session
One call names the three models and hands back aVoiceSession. 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:
tts ref takes an optional voice, which selects a voice inside the TTS model rather than naming a
model:
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
PassLlmOptions to shape what the agent says.
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
Best practices
Create the session ahead of the tap
Create the session ahead of the tap
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.Subscribe before you start
Subscribe before you start
Read
session.events and set the task up first, then call start(). Subscribing does not open
the mic, so nothing is lost.Handle audio interruptions
Handle audio interruptions
Calls and other apps interrupt the audio session. Close the session and create a new one when the
interruption ends.
Close on disappear
Close on disappear
Tear the session down in
onDisappear. A live microphone driver outlives the view otherwise.VAD
Voice activity detection
STT
Speech-to-text