Zeli AvatarDeveloper docs

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.

ValueHandler receivesFires when
CONNECTION_ESTABLISHEDnoneThe media connection is up.
SESSION_READYSessionInfoThe control-channel handshake completed.
MESSAGE_RECEIVEDMessageA user or assistant message is finalized.
MESSAGE_STREAM_EVENT_RECEIVEDMessageStreamEventAn incremental transcript chunk arrives.
MESSAGE_HISTORY_UPDATEDlist[Message]The transcript changed.
AVATAR_SPEECH_STARTEDcorrelation_id: str | NoneThe avatar starts speaking.
AVATAR_SPEECH_ENDEDcorrelation_id: str | NoneThe avatar stops speaking.
TALK_STREAM_INTERRUPTEDcorrelation_id: str | NoneA barge-in interrupted playback. None when an older server does not echo an id.
EMOTION_DETECTEDEmotionEventReserved, not currently emitted. No server sends an emotion frame.
CREDENTIAL_STATElive: boolThe server stated whether the credential now on the control socket still works.
SESSION_EXPIREDSessionExpiredEventThe server retired the session on one of its two clocks. A close follows immediately.
UNKNOWN_FRAMEUnknownFrameEventA control frame this SDK has no branch for. Ignored, but no longer in silence.
CONNECTION_CLOSEDcode: ConnectionCloseCode, reason: str | NoneThe session ended. reason carries the server's own sentence when it gave one.
SERVER_WARNINGmessage: strThe server sent a non-fatal warning.
ERRORerrorA 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.

ValueHandler receivesFires when
VOICE_READYVoiceReadyThe server has speech recognition attached and is listening. The only proof a microphone session works.
VOICE_ENDPOINTVoiceEndpointBeside each revision of what was heard: how complete the turn looks and how long the server will wait. Telemetry.
VOICE_STATEVoiceStatethinking brackets the reply; speaking False with bargein True is somebody taking the floor back.
VOICE_TOOLVoiceToolWhile a tool runs, not once it is over.
VOICE_ACTIONVoiceActionThe 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_ENDEDVoiceSessionEndedThe box ended the session, after the persona's goodbye. Final: do not reconnect.
VOICE_METRICVoiceMetricThe first audio of the reply is ready, with how long it took.
VOICE_NOTICEnotice: strThe voice account this session speaks on changed, including back to normal, where the notice is empty and the warning should be cleared.
VOICE_CLOSEDVoiceClosedThe microphone socket ended, with the server's own close code and sentence.
Where the user's own speech arrives

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.

The credential renews itself on the socket

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.

ValueHandler receivesFires when
TOOL_CALL_STARTEDToolCallStartedThe persona decided to call one of your tools.
TOOL_CALLToolCallThe call itself, with args, timeout_seconds and handled, which is False when no handler is registered for the name.
TOOL_CALL_COMPLETEDToolCallCompletedThe call finished, with duration_ms. For fire_and_forget that means sent, not carried out.
TOOL_CALL_FAILEDToolCallFailedThe 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.

FieldTypeDescription
idstrStable message id.
roleMessageRoleUSER, ASSISTANT, or SYSTEM.
contentstrFull text.
timestampstrOptional timestamp.
interruptedboolWhether the message was cut off.

MessageStreamEvent

An incremental transcript chunk (partial reply as it's spoken).

FieldTypeDescription
idstrId of the message being assembled.
roleMessageRoleUsually ASSISTANT.
contentstrThis chunk's text.
content_indexintOrder of this chunk within the message.
end_of_speechboolWhether this is the final chunk.
interruptedboolWhether playback was interrupted.
correlation_idstr | NoneTies the chunk to the originating command.
tonestr | NoneDelivery tone for this clause. A reply can change tone partway through, so the tone travels with the words.
continuesboolThis 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

FieldTypeDescription
emotionstrClassified emotion for the utterance.
correlation_idstr | NoneOriginating 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.

FieldTypeDescription
reasonstridle or max_length today. A string rather than an enum, so a newer server's reason arrives in the server's own words.
messagestrThe sentence the server gave, written for a person to read.
secondsfloatThe limit that was crossed, so a caller can raise it.
codeintThe 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.

FieldTypeDescription
typestrThe frame's type, or "" when it carried none.
framedictThe frame exactly as it arrived.

SessionInfo

Delivered with SESSION_READY.

FieldTypeDescription
session_idstrServer-issued session id.
avatarstr | NoneThe avatar the server actually attached, straight off session_ready.
toneslist[str]Tone variants available for this avatar, e.g. ["neutral", "confident"].
ice_serverslist[dict]Always empty. Reserved; nothing populates it today.
warningslist[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:

ValueMeaning
normalClean shutdown.
server_closedThe server ended the session.
webrtc_failureThe media transport failed.
signalling_failureThe signalling/handshake failed.
timeoutAn operation or the connection timed out.
errorAn unspecified error ended the session.
Zeli Avatar · real-time avatars over WebRTC · self-hostable · AU data residency · source