feat(voice/avatar): kick avatar participant on aclose + wait_for_join helper#5836
Conversation
β¦join BaseAvatarSession.aclose() now calls RoomService.RemoveParticipant on its avatar identity before tearing down its own state, so the avatar's audio + video tracks unpublish synchronously instead of lingering until the upstream provider notices the client is gone. Surfaces failures at warning level β a stuck avatar participant is worth seeing in logs. Adds BaseAvatarSession.wait_for_join() so provider implementations can let callers await actual avatar presence (participant + video track) from start(), and a timeout kwarg on utils.wait_for_participant / wait_for_track_publication so wait_for_join can be bounded.
β¦ck_publication wait_for_join bounds itself with asyncio.wait_for on the task, so the plumbed-through timeout kwarg on the util functions is dead code.
Drop the prescriptive 'providers should call this from start()' note β that's an integration choice for the LemonSlice example, not part of the public contract.
| if self._wait_avatar_join_task is None: | ||
| return |
There was a problem hiding this comment.
π΄ wait_for_join silently returns before avatar joins when room is not yet connected
When start() is called with a disconnected room (lines 110-113), _wait_avatar_join_task is not created immediately β only a connection_state_changed listener is registered. The task is created later in _on_connection_state_changed (line 203-204) when the room actually connects. However, wait_for_join() at line 122-123 returns immediately if _wait_avatar_join_task is None, which silently signals success to the caller even though the avatar has not joined. This violates the method's documented contract to "wait until the avatar participant has joined the room and published its video track."
Prompt for agents
The wait_for_join method returns immediately when _wait_avatar_join_task is None, but this state occurs legitimately when start() was called with a disconnected room (the task is only created later by _on_connection_state_changed). The fix should make wait_for_join able to wait for the task to be created. One approach: introduce an asyncio.Event (e.g. self._avatar_joined_event) that is set at the end of _wait_avatar_join(), and have wait_for_join await that event (with the specified timeout) instead of directly awaiting the task. This handles both the connected and not-yet-connected cases correctly. Alternatively, you could make the task always created in start() (even for disconnected rooms, the task itself would first wait for connection).
Was this helpful? React with π or π to provide feedback.
No description provided.