Zeli AvatarDeveloper docs
v0.1.0

Guides / Handling events

Handling events

The client is event-driven. Register handlers with the @client.on(...) decorator or client.add_listener(...). Handlers may be sync or async, and a handler that raises never breaks the SDK — the error is routed to your ERROR handlers.

Registering handlers

from zeli import ZeliEvent
 
@client.on(ZeliEvent.SESSION_READY)
async def on_ready(info):
    print("ready:", info.session_id)
 
# Equivalent, without the decorator:
def on_error(err):
    print("error:", err)
 
client.add_listener(ZeliEvent.ERROR, on_error)
client.remove_listener(ZeliEvent.ERROR, on_error)

The events

For the complete list of the 14 ZeliEvent members and their payload models, see Events & models.

EventPayloadFires when
CONNECTION_ESTABLISHEDThe 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_idThe avatar starts speaking.
AVATAR_SPEECH_ENDEDcorrelation_idThe avatar stops speaking.
TALK_STREAM_INTERRUPTEDA barge-in interrupted playback.
EMOTION_DETECTEDEmotionEventThe server classified an utterance's emotion.
CONNECTION_CLOSEDcode, reasonThe session ended.
SERVER_WARNINGmessageThe server sent a non-fatal warning.
ERRORerrorA server-side or handler error occurred.
USER_SPEECH_* are reserved

USER_SPEECH_STARTED / USER_SPEECH_ENDED are defined on the enum for a future mic-input path but are not currently emitted by the client.

Streaming a transcript

MESSAGE_STREAM_EVENT_RECEIVED fires per spoken clause as the reply is delivered, so you can render captions in real time:

@client.on(ZeliEvent.MESSAGE_STREAM_EVENT_RECEIVED)
async def on_chunk(event):
    print(event.content, end=" ", flush=True)  # clause-by-clause
    if event.end_of_speech:
        print()
 
@client.on(ZeliEvent.MESSAGE_RECEIVED)
async def on_final(message):
    print(f"[final] {message.role.value}: {message.content}")

Reacting to speech state

@client.on(ZeliEvent.AVATAR_SPEECH_STARTED)
async def on_speaking(correlation_id):
    ui.set_speaking(True)
 
@client.on(ZeliEvent.AVATAR_SPEECH_ENDED)
async def on_done(correlation_id):
    ui.set_speaking(False)

Message history

The client accumulates the transcript for you:

for msg in client.get_message_history():
    print(f"{msg.role.value}: {msg.content}")

get_message_history() returns a copy — mutating it won't affect the client.

Zeli Avatar · real-time avatars over WebRTC · self-hostable · AU data residency · source