Zeli AvatarDeveloper docs

Concepts / Voices

Voices

The voice is the timbre the avatar speaks with. It's set once on the avatar persona, alongside the language and (for conversational turns) the model that generates the words.

Selecting a voice

Set voice_id on AvatarConfig:

from zeli import AvatarConfig
 
AvatarConfig(
    avatar_id="01-presenter-male__confident",
    voice_id="your-voice-id",
)
voice_idstrOptional

The voice the avatar speaks with. Omit it for the server's configured voice.

language_code is reserved

AvatarConfig.language_code is never transmitted, so setting it changes nothing. The language follows the voice you chose and the words you send. See Configuration for the full list of fields that do and do not reach a server.

Text-to-speech vs. the model

How the words are chosen depends on how you drive the avatar:

  • talk(text) speaks your exact text directly through TTS in the configured voice. The conversational model is not involved.
  • send_message(text) runs the server's conversational model (llm_id), and the avatar speaks the generated reply in the configured voice.
# Exact words, straight to the voice
await session.talk("Reading this verbatim.")
 
# Model generates the words, the voice speaks them
await session.send_message("Introduce yourself in one sentence.")

The conversational model

Set llm_id on the persona to choose the model for this session. It is sent on POST /connect, so the choice is made before the first turn and applies to every turn of the session. Omit it to take the server's own configured model.

from zeli import AvatarConfig
 
AvatarConfig(
    avatar_id="01-presenter-male__confident",
    voice_id="your-voice-id",
    llm_id="au.anthropic.claude-opus-5-5",
)
llm_idstrOptional

Model to answer this session's turns with. The JavaScript SDK spells the same field llmId. Read the ids a server offers from its GET /api/settings under models.

A model can be refused, and that is the point

A model id says both which model and where it runs, and the same weights are reachable at more than one address. So a server handed an id it will not run does not quietly substitute its own: it answers 400 model_refused at the connect and the SDK raises ModelRefusedError (JavaScript: ModelRefusedError), carrying the id you named and the reason. A silent fallback would leave you judging a model you are not talking to, and could move the conversation to another country without saying so.

system_prompt is reserved

AvatarConfig.system_prompt is never transmitted. The system prompt is a server setting rather than a session one: configure it on the server itself through GET / POST /api/settings. The model is the one of those settings a single session may also choose for itself.

Use talk when you already have the words (your own model, a script, or fixed copy) and send_message when you want the server's model to generate them. See Driving the avatar.

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