Traceback (most recent call last): File "C:\User...
# ask-ai
m
Traceback (most recent call last): File "C:\Users\Administrator\Desktop\venv\Lib\site-packages\livekit\agents\voice\generation.py", line 517, in _traceable_fnc_tool val = await function_callable() ^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\Administrator\Desktop\calling_agent.py", line 1119, in end_call await current_speech.wait_for_playout() File "C:\Users\Administrator\Desktop\venv\Lib\site-packages\livekit\agents\voice\speech_handle.py", line 128, in wait_for_playout raise RuntimeError( RuntimeError: cannot call
SpeechHandle.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()
1
t
Thanks for reaching out, Usama. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
The error occurs because calling
SpeechHandle.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:
Copy code
@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.py
m
what is self.hangup()? is it built in function of my hangup_call()?
t
self.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.py
m
Traceback (most recent call last): File "C:\Users\Administrator\Desktop\Zoe MVP\rova_voiceai_zoe\venv\Lib\site-packages\livekit\agents\voice\generation.py", line 517, in _traceable_fnc_tool val = await function_callable() ^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\Administrator\Desktop\Zoe MVP\rova_voiceai_zoe\app\handlers\outbound_calls\calling_agent.py", line 1127, in end_call await self.hangup() ^^^^^^^^^^^ AttributeError: 'CallingAgent' object has no attribute 'hangup'
t
The error happens because the CallingAgent class doesn’t have a hangup method defined yet. No worries though! You just need to add the hangup method inside your agent class. Here’s a friendly example to help you out:
Copy code
from 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!