melodic-hamburger-67426
08/06/2025, 11:23 AMSpeechHandle.wait_for_playout()
from inside the function tool end_call
that owns this SpeechHandle. This creates a circular wait: the speech handle is waiting for the function tool
to complete, while the function tool is simultaneously waiting for the speech handle.
To wait for the assistant’s spoken response prior to running this tool, use RunContext.wait_for_playout()
instead.
this error is occuring when trying to hangup the call
from livekit.agents import get_job_context
async def hangup_call():
ctx = get_job_context()
if ctx is None:
# Not running in a job context
return
await ctx.api.room.delete_room(
api.DeleteRoomRequest(
room=ctx.room.name,
)
)
@function_tool
async def end_call(self, ctx: RunContext):
"""Called when the user wants to end the call"""
# let the agent finish speaking
current_speech = ctx.session.current_speech
if current_speech:
await current_speech.wait_for_playout()
await hangup_call()tall-airline-16756
08/06/2025, 11:23 AMtall-airline-16756
08/06/2025, 11:24 AMSpeechHandle.wait_for_playout()
within the function tool creates a circular wait. To resolve this, replace it with RunContext.wait_for_playout()
, which avoids the circular dependency.
Here’s the corrected implementation:
@function_tool()
async def end_call(self, ctx: RunContext):
"""Called when the user wants to end the call"""
<http://logger.info|logger.info>(f"ending the call for {self.participant.identity}")
# Use RunContext.wait_for_playout() instead of SpeechHandle.wait_for_playout()
await ctx.wait_for_playout()
await self.hangup()
This ensures the speech playout completes properly without causing a deadlock.
Sources: livekit-agents/livekit/agents/voice/generation.pymelodic-hamburger-67426
08/06/2025, 11:25 AMtall-airline-16756
08/06/2025, 11:25 AMself.hangup()
is a handy method in the LiveKit framework, usually found in the CallActions
class. It helps end a call by removing a specific participant from the room. Just so you know, it’s a bit different from your custom hangup_call()
function.
Now, your hangup_call()
function is something you’ve created to delete the entire room, which means the call ends for everyone involved. So, while both are about ending calls, self.hangup()
works on the participant level, and hangup_call()
handles things at the room level.
Sources: Agents telephony integration | LiveKit Docs | Agents telephony integration | LiveKit Docs | Making calls using SIP | LiveKit Docs | agent.pymelodic-hamburger-67426
08/06/2025, 11:28 AMtall-airline-16756
08/06/2025, 11:28 AMfrom livekit import api
from livekit.agents import get_job_context
class CallingAgent(Agent):
async def hangup(self):
"""Helper function to hang up the call by deleting the room"""
job_ctx = get_job_context()
if job_ctx is None:
return # Not running in a job context
await job_ctx.api.room.delete_room(
api.DeleteRoomRequest(
room=job_ctx.room.name,
)
)
@function_tool
async def end_call(self, ctx: RunContext):
"""Called when the user wants to end the call"""
# Let the agent finish speaking
current_speech = ctx.session.current_speech
if current_speech:
await current_speech.wait_for_playout()
await self.hangup()
With this in place, your hangup method will be ready to properly handle ending the call. If you need any more help, just let me know!