Skip to content

feat(examples): add LemonSlice avatar with switchable personas#5834

Merged
theomonnom merged 8 commits into
mainfrom
feat/avatar-example
May 25, 2026
Merged

feat(examples): add LemonSlice avatar with switchable personas#5834
theomonnom merged 8 commits into
mainfrom
feat/avatar-example

Conversation

@theomonnom
Copy link
Copy Markdown
Member

@theomonnom theomonnom commented May 25, 2026

No description provided.

A new example that talks to a LemonSlice video avatar with a roster of
16 personas (California-girl influencer, Pixar fox, anime cat girl, etc).
The playground dropdown fires `set_avatar` RPC; the agent swaps voice +
system prompt live and rotates the avatar mid-conversation, with a
short Bluetooth-pairing-style hold-tone playing during the swap.

Framework changes the example relies on:

  * `BaseAvatarSession.aclose()` now kicks the avatar participant via
    `JobContext.api.room.remove_participant`. Per-provider implementations
    no longer need to roll their own RoomService cleanup.
  * `BaseAvatarSession.wait_for_join(timeout=30.0)` exposes the existing
    `_wait_avatar_join` task so callers can synchronise on the avatar
    being fully joined + its video track published.
  * `utils.wait_for_participant` and `wait_for_track_publication` gained
    optional `timeout` kwargs (mirrors asyncio.wait_for semantics).
  * `lemonslice.AvatarSession.start()` rebinds `agent_session.output.audio`
    BEFORE the slow upstream HTTP call, so audio buffers for the new
    avatar identity instead of getting lost while the session is
    spinning up.

Deployment:
  * `playground.yaml` carries the avatar entry's pixel icon, accent,
    persona dropdown, and the live `agent_id`.
  * `deploy-examples.yml` matrix adds `avatar`.
  * Secrets (`LEMONSLICE_API_KEY`) are set out-of-band once via
    `lk agent update-secrets`, as discussed.
@chenghao-mou chenghao-mou requested a review from a team May 25, 2026 04:51
theomonnom and others added 5 commits May 24, 2026 21:52
…only

Drops out-of-scope hops that were bundled into the original avatar PR:
- framework changes (BaseAvatarSession.aclose remove_participant +
  wait_for_join, wait_for_participant timeout) β†’ moved to a separate PR
- lemonslice plugin output-bind reorder β†’ moved to a separate PR
- playground.yaml hotel-receptionist entry + reformatting + workflow
  matrix entry (avatar is deployed manually for now)
@theomonnom theomonnom merged commit d872421 into main May 25, 2026
21 of 22 checks passed
@theomonnom theomonnom deleted the feat/avatar-example branch May 25, 2026 05:24
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment thread examples/avatar/agent.py

state = State(persona=initial, avatar=make_avatar(initial))
await state.avatar.start(session, room=ctx.room)
await state.avatar.wait_for_join()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ”΄ Calling non-existent method wait_for_join() on AvatarSession causes AttributeError at runtime

The agent calls state.avatar.wait_for_join() at lines 69 and 107, but this method does not exist on lemonslice.AvatarSession or its base class AvatarSession (livekit-agents/livekit/agents/voice/avatar/_types.py:66). The base class has a private _wait_avatar_join() coroutine and a _wait_avatar_join_task attribute, but no public wait_for_join() method. This will raise AttributeError on every agent startup and every persona switch, making the example completely non-functional.

Comparison with existing lemonslice example

The existing lemonslice avatar example at examples/avatar_agents/lemonslice/agent_worker.py:47 simply calls await avatar.start(session, room=ctx.room) without any wait_for_join() call β€” start() already internally creates the _wait_avatar_join_task that waits for the avatar participant to join and publish a video track.

Prompt for agents
The method `wait_for_join()` does not exist on `lemonslice.AvatarSession` or its base class `AvatarSession` (defined in `livekit-agents/livekit/agents/voice/avatar/_types.py`). It is called on lines 69 and 107 of `examples/avatar/agent.py`. The base class has a private `_wait_avatar_join_task` (an asyncio.Task) that is created during `start()` β€” it waits for the avatar participant to join the room and publish a video track. If the intent is to await that internal task, one option is to await `state.avatar._wait_avatar_join_task` directly (though accessing private attributes is fragile). A better approach would be to either (a) add a public `wait_for_join()` method to the base `AvatarSession` class in `_types.py` that awaits `_wait_avatar_join_task`, or (b) remove the `wait_for_join()` calls entirely as the existing lemonslice example (`examples/avatar_agents/lemonslice/agent_worker.py`) does not use it and works fine without it.
Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant