astonishing-salesclerk-9701
12/15/2025, 7:14 AMtall-airline-16756
12/15/2025, 7:14 AMtall-airline-16756
12/15/2025, 7:15 AMlast_spoke timestamp whenever the user starts speaking.
• Run a background monitor that checks elapsed time and disconnects the room when it exceeds 120 seconds.
2) Minimal example (async pattern)
import asyncio
import time
SILENCE_THRESHOLD = 120 # 2 minutes
async def entrypoint(ctx):
user_last_spoke = time.time()
@agent.on("user_started_speaking")
def on_user_started_speaking(_msg):
nonlocal user_last_spoke
user_last_spoke = time.time()
async def monitor():
while True:
if time.time() - user_last_spoke > SILENCE_THRESHOLD:
try:
await ctx.room.disconnect()
except Exception:
pass
break
await asyncio.sleep(1)
monitor_task = asyncio.create_task(monitor())
await agent.start(ctx.room, participant)
async def on_shutdown():
if not monitor_task.done():
monitor_task.cancel()
ctx.add_shutdown_callback(on_shutdown)
3) Notes and options
• Poll interval (here 1s) can be increased to reduce CPU usage.
• If you need more robust detection, combine this with voice-activity or LLM-based voicemail detection.
• Tell me how you trigger speech events in your setup (server-side agent, client SDK, or webhook) and I’ll adapt this snippet to your environment. Sources: How to detect and handle silence during calls | Agents Framework | livekit/livekit_composite | DeepWikiastonishing-salesclerk-9701
12/15/2025, 7:22 AMtall-airline-16756
12/15/2025, 7:22 AM@server.rtc_session(), you can use the session's user-state events to detect when the user stops speaking and start a 2‑minute timer that disconnects the room if the user doesn't speak again.
Do you want a concise runnable snippet adapted to your code (Python) or a short explanation of the event/timer flow?
Sources: Events and error handling | LiveKit docs | Agent server options | LiveKit docs | Job lifecycle | LiveKit docsastonishing-salesclerk-9701
12/15/2025, 7:22 AMtall-airline-16756
12/15/2025, 7:22 AM@server.rtc_session() that speaks once, then monitors user activity and disconnects the room if the user stays silent for 2 minutes.
Important points first:
• We use UserStateChangedEvent (session events) to detect when the user finishes speaking (new_state == "listening").
• When that happens we start a 120s timer; if the user does not start speaking again before the timer ends, we call ctx.room.disconnect().
• The monitor task is cancelled on shutdown to avoid leaks.
Code (adapt to your imports and environment as needed):
import asyncio
from livekit.agents import server, AgentSession, JobContext
from livekit.agents.events import UserStateChangedEvent
SILENCE_TIMEOUT = 120.0 # seconds
@server.rtc_session()
async def my_agent(ctx: JobContext):
# create and start your AgentSession (configure STT/LLM/TTS as required)
session = AgentSession(...) # fill with your session config
await session.start(room=ctx.room)
async def silence_monitor():
try:
while True:
# Wait for a user_state_changed event
event: UserStateChangedEvent = await session.wait_for_event("user_state_changed")
# If user moved to "listening" (finished speaking), start timeout
if getattr(event, "new_state", None) == "listening":
# Wait the silence timeout while allowing interruption if user starts speaking
try:
await asyncio.wait_for(_wait_for_user_speaking(), timeout=SILENCE_TIMEOUT)
# If _wait_for_user_speaking returns, user spoke again; loop continues
continue
except asyncio.TimeoutError:
# No speech for SILENCE_TIMEOUT -> disconnect
await ctx.room.disconnect()
return
except asyncio.CancelledError:
return
async def _wait_for_user_speaking():
# Await the next event where user starts speaking
while True:
ev: UserStateChangedEvent = await session.wait_for_event("user_state_changed")
if getattr(ev, "new_state", None) == "speaking":
return
monitor_task = asyncio.create_task(silence_monitor())
# Ensure monitor_task is cancelled on shutdown
@ctx.on("shutdown")
def _on_shutdown():
monitor_task.cancel()
# Optional: have the agent speak once
await session.generate_reply(instructions="Hello — I will end the call if you remain silent for 2 minutes.")
# Wait for session to finish (or be disconnected)
await session.aclose()
If you’d like, I can:
• adapt this snippet to your exact AgentSession constructor arguments, or
• provide a variant that polls session.user_state instead of using events.astonishing-salesclerk-9701
12/15/2025, 7:37 AMtall-airline-16756
12/15/2025, 7:38 AMJobContext doesn't have an on method. Use ctx.add_shutdown_callback(...) to register shutdown cleanup. Below is a corrected, concise snippet replacing the @ctx.on("shutdown") usage with ctx.add_shutdown_callback, and ensuring the monitor task is cancelled cleanly.
import asyncio
from livekit.agents import server, AgentSession, JobContext
from livekit.agents.events import UserStateChangedEvent
SILENCE_TIMEOUT = 120.0 # seconds
@server.rtc_session()
async def my_agent(ctx: JobContext):
session = AgentSession(...) # fill with your session config
await session.start(room=ctx.room)
async def _wait_for_user_speaking():
while True:
ev: UserStateChangedEvent = await session.wait_for_event("user_state_changed")
if getattr(ev, "new_state", None) == "speaking":
return
async def silence_monitor():
try:
while True:
ev: UserStateChangedEvent = await session.wait_for_event("user_state_changed")
if getattr(ev, "new_state", None) == "listening":
try:
await asyncio.wait_for(_wait_for_user_speaking(), timeout=SILENCE_TIMEOUT)
continue # user spoke again
except asyncio.TimeoutError:
await ctx.room.disconnect()
return
except asyncio.CancelledError:
return
monitor_task = asyncio.create_task(silence_monitor())
# Correct shutdown registration
def _cleanup():
if not monitor_task.done():
monitor_task.cancel()
ctx.add_shutdown_callback(_cleanup)
# Optional greeting
await session.generate_reply(instructions="Hello — I will end the call if you remain silent for 2 minutes.")
await session.aclose()
If you want, I can:
• adapt this to your actual AgentSession(...) config, or
• provide a variant that polls session.user_state instead of waiting for events. Which do you prefer?astonishing-salesclerk-9701
12/15/2025, 7:43 AMtall-airline-16756
12/15/2025, 7:43 AMwait_for_event and ctx.on aren't available, use @session.on("user_state_changed") to reset a single asyncio timer task. Below is a minimal, corrected Python example for @server.rtc_session() that:
• has the agent speak once,
• starts a 120s inactivity timer after speaking,
• cancels & restarts the timer when user speaks,
• calls ctx.room.disconnect() when timer expires,
• uses ctx.add_shutdown_callback to cancel the timer on shutdown.
Adapt the AgentSession constructor and imports to your project.
import asyncio
from livekit.agents import server, AgentSession, JobContext, UserStateChangedEvent
SILENCE_TIMEOUT = 120.0 # seconds
@server.rtc_session()
async def my_agent(ctx: JobContext):
session = AgentSession(...) # fill in your config
await session.start(room=ctx.room)
inactivity_task: asyncio.Task | None = None
def start_inactivity_timer():
nonlocal inactivity_task
if inactivity_task and not inactivity_task.done():
inactivity_task.cancel()
inactivity_task = asyncio.create_task(_inactivity_wait())
async def _inactivity_wait():
try:
await asyncio.sleep(SILENCE_TIMEOUT)
await ctx.room.disconnect()
except asyncio.CancelledError:
return
@session.on("user_state_changed")
def _on_user_state_changed(ev: UserStateChangedEvent):
# user started speaking -> reset timer so we don't disconnect
if getattr(ev, "new_state", None) == "speaking":
if inactivity_task and not inactivity_task.done():
inactivity_task.cancel()
# user finished speaking -> start timer
elif getattr(ev, "new_state", None) == "listening":
start_inactivity_timer()
# optional: handle away state immediately
elif getattr(ev, "new_state", None) == "away":
asyncio.create_task(ctx.room.disconnect())
# ensure cleanup on shutdown
def _cleanup():
nonlocal inactivity_task
if inactivity_task and not inactivity_task.done():
inactivity_task.cancel()
ctx.add_shutdown_callback(_cleanup)
# have the agent speak, then start the inactivity timer
await session.generate_reply(instructions="Hello — I will end the call if you remain silent for 2 minutes.")
start_inactivity_timer()
await session.aclose()
If you want, tell me your AgentSession constructor args (STT/TTS/LLM names) and I’ll plug them into this snippet.
Sources: Events and error handling | LiveKit docs | Agent session | LiveKit docsastonishing-salesclerk-9701
12/15/2025, 7:59 AMtall-airline-16756
12/15/2025, 7:59 AMastonishing-salesclerk-9701
12/15/2025, 11:37 AMtall-airline-16756
12/15/2025, 11:37 AM