orange-mechanic-67255
08/18/2025, 4:15 PMasync def async_on_participant_disconnected():
# Save transcription
await self.save_final_transcription()
@ctx.room.on("participant_disconnected")
def on_participant_disconnected(participant):
<http://speech_logger.info|speech_logger.info>("The user has disconnected from the call.")
self._disconnect_task = asyncio.create_task(async_on_participant_disconnected())
async def save_final_transcription(self) -> None:
if self.user:
<http://logger.info|logger.info>(f"Saving call transcription for user_id {self.user.id}")
if len([t.content for t in self.transcription_pieces if t.role == "user"]) == 0:
# if user never replied, switch to previous user status (as if conversation didn't happen)
update_user_status(self.user.id, UserStatus.PENDING.value if self.user.prev_status == UserStatus.QUEUED else self.user.prev_status.value)
else:
# if all questions were answered, ensure user status is completed
if not self.user.missing_questions:
self.user.status = UserStatus.COMPLETED
transcript = save_transcription(self.user.id, self.transcription_pieces, self.call_type.value) # save transcription
update_user_status(self.user.id, self.user.status.value) # update user status
# if user status is still in progress at the end of the call, process the transcription to determine a status
if self.user.status == UserStatus.IN_PROGRESS:
await process_transcription(transcript, self.user.id) # THIS NEVER GETS DONE RUNNING BEFORE EVERYTHING SHUTS DOWN
tall-airline-16756
08/18/2025, 4:15 PMtall-airline-16756
08/18/2025, 4:15 PMparticipant_disconnected
finish up before the session closes, here’s a friendly approach you can try:
• Keep an ear out for the participant_disconnected
event and kick off cleanup tasks using asyncio.create_task
.
• Hold onto all those tasks and await them before closing the session by gathering them at the end.
• You can also set close_on_disconnect=True
in your session settings to have sessions close automatically when participants leave.
Here’s a simple example to illustrate:
async def entrypoint(ctx: JobContext):
session = AgentSession(
stt=deepgram.STT(),
tts=silero.TTS()
)
await session.start(
agent=MyAgent(),
room=ctx.room,
input_options=RoomInputOptions(close_on_disconnect=True)
)
tasks = set()
def on_disconnect(participant: rtc.RemoteParticipant):
async def cleanup_session():
await session.aclose()
print(f"Session closed for participant {participant.identity}")
tasks.add(asyncio.create_task(cleanup_session()))
ctx.room.on("participant_disconnected", on_disconnect)
try:
await asyncio.Future() # Run forever
except asyncio.CancelledError:
pass
finally:
if session:
await session.aclose()
for task in tasks:
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
This way, all your important tasks—like saving transcriptions—get done before the session wraps up. If anything’s unclear or you want to dive deeper, just let me know—I’m here to help!orange-mechanic-67255
08/18/2025, 5:26 PMtall-airline-16756
08/18/2025, 5:26 PMaclose
isn’t quite working as expected for you. No worries—here are some friendly tips to help you sort it out:
• Make Sure Tasks Are Handled Properly: Try tweaking the aclose
method so it waits for all tasks to finish without a timeout. Something like this might do the trick: await asyncio.gather(*tasks, return_exceptions=False)
• Close Resources Explicitly: Double-check that all your resources, like audio and video tracks, are properly closed. For example: if self._stream_response and not self._stream_response.output_stream.closed:
await self._stream_response.output_stream.close()
• Log Any Exceptions: Instead of skipping over exceptions when cancelling tasks, it’s helpful to log them so you can spot any issues: try:
await task
except Exception as e:
logger.error("Task failed during closure", exc_info=e)
Hopefully, these steps will get your calls disconnecting smoothly and tasks wrapping up nicely. If you want to chat more or need a hand with anything else, just let me know! Sources: livekit-agents/livekit/agents/voice/agent_session.py