fierce-zoo-69908
12/19/2025, 2:58 AMtall-airline-16756
12/19/2025, 2:58 AMtall-airline-16756
12/19/2025, 2:58 AMthinking (actively processing), delay deletion until the agent transitions out of thinking. Agents emit state-change events you can listen to.
• Track tool executions explicitly. Emit/set a small “tool-running” flag when you start a long-running tool and clear it when the tool finishes (or on the `function_tools_executed`/tool-finished event). On participant disconnect, skip deletion while the flag is set.
• Use a shutdown/cleanup hook that runs after the agent finishes (for example, an on_shutdown or add_shutdown_callback) to perform room deletion once processing completes.
• If you need immediate cleanup but want to preserve in-progress work, schedule deletion with a short delay and cancel that deletion if the agent finishes before the timer elapses.
If you want, tell me your runtime and whether you use the Agents framework and I’ll provide a small code example showing:
• checking agent state in the disconnect handler, and
• a simple tool-running flag pattern you can use. Sources: Events and error handling | LiveKit docs | Job lifecycle | LiveKit docs | Tool definition and use | LiveKit docsfierce-zoo-69908
12/19/2025, 2:59 AMtall-airline-16756
12/19/2025, 3:00 AMthinking) in the participant_disconnected handler.
• Maintain an explicit tool_running flag (set when a long task starts, cleared when it finishes or when its completion event fires).
• Use a shutdown/cleanup callback or wait for the agent to finish before calling delete_room.
• Optionally schedule a timed fallback delete (with cancellation) to protect against stuck tasks.
Minimal example (Python, Agents framework)
# conceptual example — adapt to your code structure and APIs
class AgentController:
def __init__(self, session, room):
self.session = session # agent session object
self.room = room
self.tool_running = False
self.shutdown_scheduled = None
# register handlers
room.on("participant_disconnected", self.on_participant_disconnected)
session.on("agent_state_changed", self.on_agent_state_changed)
session.on("function_tools_executed", self.on_tool_finished)
async def start_long_tool(self, *args):
self.tool_running = True
try:
# run the long-running tool
await run_long_tool(*args)
finally:
# ensure flag cleared even on error
self.tool_running = False
# if a room deletion was scheduled, cancel or perform it here
await self.maybe_finalize_room()
async def on_tool_finished(self, event):
# event fired when tool completes (if available)
self.tool_running = False
await self.maybe_finalize_room()
async def on_agent_state_changed(self, state_event):
# optional: react to `thinking` -> `idle` transitions
if state_event.new_state != "thinking":
await self.maybe_finalize_room()
async def on_participant_disconnected(self, participant):
# run when a participant leaves
# If the agent is actively processing, delay deletion.
if self.session.current_state == "thinking" or self.tool_running:
# schedule a fallback deletion after timeout (e.g. 30s)
if not self.shutdown_scheduled:
self.shutdown_scheduled = asyncio.create_task(self._fallback_delete(30))
return
# safe to delete immediately
await self.room.delete_room()
async def maybe_finalize_room(self):
# called after tools finish or agent becomes idle
if self.shutdown_scheduled:
# cancel fallback if agent finished early
self.shutdown_scheduled.cancel()
self.shutdown_scheduled = None
# if no participants remain, it's safe to delete
if not self.room.participants:
await self.room.delete_room()
async def _fallback_delete(self, timeout_seconds):
try:
await asyncio.sleep(timeout_seconds)
# re-check before deleting
if not (self.session.current_state == "thinking" or self.tool_running):
await self.room.delete_room()
except asyncio.CancelledError:
return
Notes and best practices
• The Agents framework emits state-change and tool-executed events — use them rather than relying only on timing.
• Always clear the tool_running flag in finally blocks to avoid stuck state.
• A short fallback timeout (e.g., 20–60s) prevents orphaned rooms if something crashes.
• Make your deletion idempotent and tolerant of race conditions (check room existence/participants before deleting).
If you’d like, I can:
• produce a version tailored to your exact session/room API names, or
• show how to wire this into your agent’s add_shutdown_callback instead of a scheduled task. Which would help more?fierce-zoo-69908
12/19/2025, 3:01 AMtall-airline-16756
12/19/2025, 3:01 AMdelete_room() when the human leaves.
• The shutdown callback runs after the room/session teardown to perform final cleanup (save state, logs, DB updates, notify services).
How to use them together safely (Python, Agents):
• In participant_disconnected, do not unconditionally force-delete if the agent is still processing. Check session.current_state == "thinking" or a tool_running flag; if processing, schedule a fallback delete (short timeout) and return.
• When tools finish or agent state becomes idle, call a maybe_finalize_room() that cancels the fallback and calls delete_room() if no participants remain.
• Register ctx.add_shutdown_callback(...) for post-delete cleanup (persist results, release resources).
Your posted pattern is fine: keep the async delete_room task on participant disconnect and also register add_shutdown_callback for shutdown work. If you want, I can provide a compact, integrated version of your exact code wired to check session.current_state and a tool_running flag. Which would you prefer?
Sources: Job lifecycle | LiveKit docs