Manage agent sessions

A session is one conversation with an agent. init() returns a VoiceAgentSession: you subscribe to its events, mute or unmute the microphone, wait for it to end, and close it. This page is for developers taking an agent past the first demo. It assumes you have an agent running, see the quickstart. The session works the same way in Python, in Node.js, and in the browser, where initSession() returns it.

Lifecycle

Lifecycle

A session connects, exchanges audio and events while the conversation runs, then ends. Mute the microphone with set_input_enabled(False), wait for the end with wait(), and always call close() when you're done.

await session.set_input_enabled(False)  # mute
await session.set_input_enabled(True)   # unmute

await session.wait()   # blocks until disconnected
await session.close()  # close the session, should always be called

init() creates the session with POST /v1/audio/sessions, and close() removes it with DELETE /v1/audio/sessions/{id}. To call these endpoints yourself, use client.audio.sessions.create(...) and client.audio.sessions.close(session_id).

Events

Events

Register a handler with session.on(event, handler).

EventPayloadYour app should
connectedsession_idMark the session as ready.
disconnectedsession_id, reasonStop audio and clean up.
transcriptid, role, content, finalShow partial transcripts live (final is false), and keep the final ones.
audiobytesPlay the agent's audio yourself, if you handle audio manually. See Custom audio.
audio_tracktrack, sourceIn the browser, attach the media track.
agent_state_changedstate (listening, thinking, speaking, and more)Update the interface.
tool_call_startedid, nameShow that the agent is working. The arguments aren't known yet.
tool_call_endedid, name, arguments, resultShow the result.
unhandled_toolname, argumentsLog it: the agent called a client tool that has no handler.
errormessage, fatal, code, sourceReport it. If fatal is true, the session is closing, see Errors and cleanup.

disconnected has no reason after a normal close. Otherwise, reason is user_interrupt, start_failed, audio_input_error, audio_output_error, or error.

from mistralai.extra.voice_agents import (
    VoiceAgentErrorEvent,
    VoiceAgentStateChangedEvent,
    VoiceAgentToolCallEndedEvent,
    VoiceAgentToolCallStartedEvent,
    VoiceAgentTranscriptEvent,
)

def on_transcript(event: VoiceAgentTranscriptEvent) -> None:
    print(f"{event.role}: {event.content}")

def on_tool_call_started(event: VoiceAgentToolCallStartedEvent) -> None:
    print(f"tool call started: {event.name}")

def on_tool_call_ended(event: VoiceAgentToolCallEndedEvent) -> None:
    print(f"tool call ended: {event.name} -> {event.result!r}")

def on_agent_state_changed(event: VoiceAgentStateChangedEvent) -> None:
    print(f"state: {event.state}")

def on_error(event: VoiceAgentErrorEvent) -> None:
    print(f"error (fatal={event.fatal}): {event.message}")

session.on("transcript", on_transcript)
session.on("tool_call_started", on_tool_call_started)
session.on("tool_call_ended", on_tool_call_ended)
session.on("agent_state_changed", on_agent_state_changed)
session.on("error", on_error)

session.on() returns a function that unsubscribes the handler. You can also call session.off() with the original handler:

unsubscribe = session.on("transcript", on_transcript)

unsubscribe()  # stop receiving
# or, equivalently:
session.off("transcript", on_transcript)
Errors and cleanup

Errors and cleanup

Every error carries fatal:

  • Recoverable errors (fatal is false) leave the session open. Log them and continue.
  • Fatal errors close the session: wait() returns, and audio stops. Startup failures and unexpected disconnections are fatal.

Register an error handler to decide what your app does. Without one, the SDK logs the error (Python logging, console.error in Node.js), so errors are never silent. Because init() starts the session before it returns, a startup failure also raises VoiceAgentConnectionException, with the error in error_event (Python) or errorEvent (TypeScript).

In both cases, call close() in a finally block, as in the quickstart, so the session is always released.

Going further

Going further