Python SDK / Events & models
Events & models
The client is event-driven. Subscribe with
@client.on(ZeliEvent.…) and receive typed payloads. This page lists
every event and the models they carry.
from zeli import (
ZeliEvent, Message, MessageRole,
MessageStreamEvent, EmotionEvent, SessionInfo, ConnectionCloseCode,
SessionExpiredEvent, UnknownFrameEvent,
)ZeliEvent
The enum has 22 members: fifteen for the session, and seven more for the microphone socket. See the events guide for payload examples.
| Value | Handler receives | Fires when |
|---|---|---|
CONNECTION_ESTABLISHED | none | The media connection is up. |
SESSION_READY | SessionInfo | The control-channel handshake completed. |
MESSAGE_RECEIVED | Message | A user or assistant message is finalized. |
MESSAGE_STREAM_EVENT_RECEIVED | MessageStreamEvent | An incremental transcript chunk arrives. |
MESSAGE_HISTORY_UPDATED | list[Message] | The transcript changed. |
AVATAR_SPEECH_STARTED | correlation_id: str | None | The avatar starts speaking. |
AVATAR_SPEECH_ENDED | correlation_id: str | None | The avatar stops speaking. |
TALK_STREAM_INTERRUPTED | correlation_id: str | None | A barge-in interrupted playback. None when an older server does not echo an id. |
EMOTION_DETECTED | EmotionEvent | Reserved, not currently emitted. No server sends an emotion frame. |
CREDENTIAL_STATE | live: bool | The server stated whether the credential now on the control socket still works. |
SESSION_EXPIRED | SessionExpiredEvent | The server retired the session on one of its two clocks. A close follows immediately. |
UNKNOWN_FRAME | UnknownFrameEvent | A control frame this SDK has no branch for. Ignored, but no longer in silence. |
CONNECTION_CLOSED | code: ConnectionCloseCode, reason: str | None | The session ended. reason carries the server's own sentence when it gave one. |
SERVER_WARNING | message: str | The server sent a non-fatal warning. |
ERROR | error | A server-side or handler error occurred. |
The microphone socket
These nine arrive on a voice session and nowhere else. The other
seven of that socket's sixteen frames reuse the events above: recognised speech
and the spoken reply are both MESSAGE_STREAM_EVENT_RECEIVED and
MESSAGE_RECEIVED, a failed turn and a troubled socket are both ERROR with
different exception types, and a renewed credential is CREDENTIAL_STATE.
| Value | Handler receives | Fires when |
|---|---|---|
VOICE_READY | VoiceReady | The server has speech recognition attached and is listening. The only proof a microphone session works. |
VOICE_ENDPOINT | VoiceEndpoint | Beside each revision of what was heard: how complete the turn looks and how long the server will wait. Telemetry. |
VOICE_STATE | VoiceState | thinking brackets the reply; speaking False with bargein True is somebody taking the floor back. |
VOICE_TOOL | VoiceTool | While a tool runs, not once it is over. |
VOICE_ACTION | VoiceAction | The persona did something rather than said something: it ended the call, skipped a turn, paused, or changed language. A pause carries paused, max_seconds and, when it ends, reason (a PauseEndReason, UNKNOWN for a word this SDK has never heard of, kept in reason_raw). A language change carries language, applied, and voice_id with voice_label only when the voice changed. Each field is None unless the box sent it. The field table. |
VOICE_SESSION_ENDED | VoiceSessionEnded | The box ended the session, after the persona's goodbye. Final: do not reconnect. |
VOICE_METRIC | VoiceMetric | The first audio of the reply is ready, with how long it took. |
VOICE_NOTICE | notice: str | The voice account this session speaks on changed, including back to normal, where the notice is empty and the warning should be cleared. |
VOICE_CLOSED | VoiceClosed | The microphone socket ended, with the server's own close code and sentence. |
There is still no USER_SPEECH_STARTED or
USER_SPEECH_ENDED, and there will not be: this SDK has no
microphone capture, deliberately, and
the reasoning is on the voice page. What it does have
is the socket. Feed it PCM and the recognised words arrive as
MESSAGE_STREAM_EVENT_RECEIVED with the USER role,
revised as the person talks and settled with
end_of_speech, which is what that event's documentation has always
said it would carry.
A session token lives for minutes, and a WebSocket freezes the credential it
opened with into the handshake, so the server asks for a fresh one on the socket
rather than answering 401. The SDK handles that for you when the client was
built with a token_provider: it asks your backend, sends the
renewed credential back down the same socket, and the media connection is never
touched, so the avatar does not disappear and come back. Without a
token_provider there is nothing to send, and that is reported as a
SERVER_WARNING rather than left silent. Read
client.credential_is_live for the server's last word on it;
None means it has not said anything yet, which is not the same as
False.
Custom tool calls
These four arrive whichever socket the call came in on, the control socket or the
microphone socket, so a caller draws one lifecycle. Answer a call with
client.register_tool_call_handler(name, handler) or the
@client.tool_call_handler(name) decorator; the events are for showing what is
happening. The full contract, the failure reasons and a worked example are under
Tools.
| Value | Handler receives | Fires when |
|---|---|---|
TOOL_CALL_STARTED | ToolCallStarted | The persona decided to call one of your tools. |
TOOL_CALL | ToolCall | The call itself, with args, timeout_seconds and handled, which is False when no handler is registered for the name. |
TOOL_CALL_COMPLETED | ToolCallCompleted | The call finished, with duration_ms. For fire_and_forget that means sent, not carried out. |
TOOL_CALL_FAILED | ToolCallFailed | The call failed. reason is a ToolFailureReason; a reason from a newer server is UNKNOWN with the server's word on raw_reason. |
Message
A finalized message in the transcript.
| Field | Type | Description |
|---|---|---|
id | str | Stable message id. |
role | MessageRole | USER, ASSISTANT, or SYSTEM. |
content | str | Full text. |
timestamp | str | Optional timestamp. |
interrupted | bool | Whether the message was cut off. |
MessageStreamEvent
An incremental transcript chunk (partial reply as it's spoken).
| Field | Type | Description |
|---|---|---|
id | str | Id of the message being assembled. |
role | MessageRole | Usually ASSISTANT. |
content | str | This chunk's text. |
content_index | int | Order of this chunk within the message. |
end_of_speech | bool | Whether this is the final chunk. |
interrupted | bool | Whether playback was interrupted. |
correlation_id | str | None | Ties the chunk to the originating command. |
tone | str | None | Delivery tone for this clause. A reply can change tone partway through, so the tone travels with the words. |
continues | bool | This chunk resumes a turn that was interrupted, so content holds only the new words. Voice only. |
MessageRole
An enum: USER, ASSISTANT, SYSTEM. Access the string via .value.
EmotionEvent
| Field | Type | Description |
|---|---|---|
emotion | str | Classified emotion for the utterance. |
correlation_id | str | None | Originating command id. |
SessionExpiredEvent
Delivered with SESSION_EXPIRED. Two clocks can end a session and reason names
which: an idle window that is the server's own policy, and a ceiling you asked for
with AvatarConfig.max_session_length_seconds. A close frame follows immediately,
so this is the only place a retirement can be told apart from a network drop.
| Field | Type | Description |
|---|---|---|
reason | str | idle or max_length today. A string rather than an enum, so a newer server's reason arrives in the server's own words. |
message | str | The sentence the server gave, written for a person to read. |
seconds | float | The limit that was crossed, so a caller can raise it. |
code | int | The WebSocket close code carried on the close that follows. |
UnknownFrameEvent
Delivered with UNKNOWN_FRAME. The frame is still ignored, so a newer server
cannot break an older client; what this adds is that ignoring it is visible, and
that a caller can act on something the SDK does not model yet.
| Field | Type | Description |
|---|---|---|
type | str | The frame's type, or "" when it carried none. |
frame | dict | The frame exactly as it arrived. |
SessionInfo
Delivered with SESSION_READY.
| Field | Type | Description |
|---|---|---|
session_id | str | Server-issued session id. |
avatar | str | None | The avatar the server actually attached, straight off session_ready. |
tones | list[str] | Tone variants available for this avatar, e.g. ["neutral", "confident"]. |
ice_servers | list[dict] | Always empty. Reserved; nothing populates it today. |
warnings | list[str] | Always empty. Reserved; startup warnings arrive as SERVER_WARNING events instead. |
tones is the only place a caller can discover which values talk(tone=…) will
accept. The set is per-avatar, so it cannot be a constant in the SDK.
ConnectionCloseCode
The code delivered with CONNECTION_CLOSED. An enum with values:
| Value | Meaning |
|---|---|
normal | Clean shutdown. |
server_closed | The server ended the session. |
webrtc_failure | The media transport failed. |
signalling_failure | The signalling/handshake failed. |
timeout | An operation or the connection timed out. |
error | An unspecified error ended the session. |