How to detect and handle silence during calls
Learn how to automatically end calls after periods of silence using the built-in user away timeout and custom retry mechanisms.
Last Updated:
There are times when you may need to automatically end a call after a period of silence, such as when a user becomes unresponsive or walks away from a call. This guide explains how to implement silence detection and automatic call termination.
Using the built-in user away timeout
LiveKit Agents has a built-in user_away_timeout feature that detects when a user has been inactive. When the timeout is reached, the user state changes to "away". You can configure this timeout and listen for the user_state_changed event:
1import asyncio2import logging34from livekit.agents import (5Agent,6AgentServer,7AgentSession,8JobContext,9UserStateChangedEvent,10cli,11)12from livekit.plugins import cartesia, deepgram, openai, silero1314logger = logging.getLogger("silence-detection-agent")1516server = AgentServer()1718@server.rtc_session(agent_name="silence-detection-agent")19async def entrypoint(ctx: JobContext):20session = AgentSession(21vad=silero.VAD.load(),22llm=openai.LLM(model="gpt-4o-mini"),23stt=deepgram.STT(),24tts=cartesia.TTS(),25user_away_timeout=5.0, # seconds of silence before user is marked "away"26)2728@session.on("user_state_changed")29def _user_state_changed(ev: UserStateChangedEvent):30if ev.new_state == "away":31logger.info("User has been silent for too long, shutting down session")32session.shutdown()3334await session.start(35agent=Agent(instructions="You are a helpful assistant."),36room=ctx.room37)3839if __name__ == "__main__":40cli.run_app(server)
Implementing custom silence detection with retries
For more advanced scenarios, such as prompting the user before disconnecting, you can implement a retry mechanism:
1import asyncio2import logging34from livekit.agents import (5Agent,6AgentServer,7AgentSession,8JobContext,9UserStateChangedEvent,10cli,11)12from livekit.plugins import cartesia, deepgram, openai, silero1314logger = logging.getLogger("silence-detection-agent")1516server = AgentServer()1718@server.rtc_session(agent_name="silence-detection-agent")19async def entrypoint(ctx: JobContext):20session = AgentSession(21vad=silero.VAD.load(),22llm=openai.LLM(model="gpt-4o-mini"),23stt=deepgram.STT(),24tts=cartesia.TTS(),25user_away_timeout=12.5, # seconds before user is marked "away"26)2728inactivity_task: asyncio.Task | None = None2930async def user_presence_task():31# Try to ping the user 3 times, if we get no answer, close the session32for _ in range(3):33await session.generate_reply(34instructions="The user has been inactive. Politely check if the user is still present."35)36await asyncio.sleep(10)3738logger.info("No response after multiple attempts, shutting down")39session.shutdown()4041@session.on("user_state_changed")42def _user_state_changed(ev: UserStateChangedEvent):43nonlocal inactivity_task44if ev.new_state == "away":45inactivity_task = asyncio.create_task(user_presence_task())46return4748# User is now speaking or listening, cancel the inactivity task49if inactivity_task is not None:50inactivity_task.cancel()51inactivity_task = None5253await session.start(54agent=Agent(instructions="You are a helpful assistant."),55room=ctx.room56)5758if __name__ == "__main__":59cli.run_app(server)
The user_state_changed event provides a UserStateChangedEvent with the following possible states:
"speaking"- VAD detected the user has started speaking"listening"- VAD detected the user has stopped speaking"away"- The user hasn't responded for the configureduser_away_timeoutduration
Related: Answering machine detection
For outbound calling scenarios where you need to detect answering machines or voicemail systems, see the telephony documentation for dedicated detection methods. Silence detection can complement these approaches but isn't a reliable standalone method for voicemail detection.