feat(examples): add LemonSlice avatar with switchable personas#5834
Conversation
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.
β¦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)
|
|
||
| state = State(persona=initial, avatar=make_avatar(initial)) | ||
| await state.avatar.start(session, room=ctx.room) | ||
| await state.avatar.wait_for_join() |
There was a problem hiding this comment.
π΄ 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.
Was this helpful? React with π or π to provide feedback.
No description provided.