i'm using agent with twilio sip and when the calle...
# ask-ai
r
i'm using agent with twilio sip and when the caller hangs up the agent is still continuing
t
Thanks for reaching out, Weilan. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
When using LiveKit Agent with Twilio SIP, the agent session continues after the caller hangs up because the agent and other participants remain in the room even after the human participant leaves. This is expected behavior unless the room is explicitly ended. To resolve this, you should delete the room when the call ends. This will disconnect all participants, including the agent, and prevent the session from continuing after the caller hangs up. Here’s how you can implement this in your agent code:
Copy code
from livekit import api, rtc
from livekit.agents import get_job_context

async def hangup_call():
    ctx = get_job_context()
    if ctx is None:
        return
    await ctx.api.room.delete_room(
        api.DeleteRoomRequest(
            room=ctx.room.name,
        )
    )

class MyAgent(Agent):
    ...

@function_tool
async def end_call(self, ctx: RunContext):
    current_speech = ctx.session.current_speech
    if current_speech:
        await current_speech.wait_for_playout()
    await hangup_call()
By deleting the room when the call ends, you ensure that all participants are properly disconnected and the agent session does not continue after the caller hangs up. Sources: Agents telephony integration | LiveKit Docs
r
I already have this and it works when the agent hangs up but what if the caller hangs up
t
Hey there! Let me walk you through a super helpful way to handle what happens when a caller decides to hang up. It's actually pretty straightforward - you'll want to keep an eye on the call status of your SIP participant, specifically watching for when it changes to
hangup
. Think of it like listening for a signal that says, "Hey, I'm done with this call!" Here's a friendly example that shows exactly how to do this:
Copy code
async def entrypoint(ctx: JobContext):
    await ctx.connect()
    session = AgentSession()
    agent = YourAgent(job_context=ctx)
    await session.start(agent=agent, room=ctx.room)

    async def async_on_participant_attributes_changed(changed_attributes: dict, participant: rtc.Participant):
        if participant.kind == rtc.ParticipantKind.PARTICIPANT_KIND_SIP:
            if 'sip.callStatus' in changed_attributes:
                call_status = changed_attributes['sip.callStatus']
                if call_status == 'hangup':
                    await cleanup_call(ctx)

    def on_participant_attributes_changed_handler(changed_attributes: dict, participant: rtc.Participant):
        asyncio.create_task(async_on_participant_attributes_changed(changed_attributes, participant))

    ctx.room.on("participant_attributes_changed", on_participant_attributes_changed_handler)

async def cleanup_call(ctx: JobContext):
    await ctx.api.room.delete_room(
        api.DeleteRoomRequest(
            room=ctx.room.name,
        )
    )
What this does is pretty cool - when the SIP caller hangs up, it'll automatically wrap things up by deleting the room and ending the agent session. No loose ends, no fuss! Sources: Agents telephony integration | LiveKit Docs | telephony/sip_lifecycle.py
r
why does it need asyncio.create_task
t
Hey there! Let me break down why we use
asyncio.create_task
in the
participant_attributes_changed
event handler. Think of it like a helpful workaround for LiveKit's event system. Basically, LiveKit's event handlers are synchronous (like a quick chat), but sometimes you need to do some async tasks (like cleaning up a room) that take a bit more time. By using
asyncio.create_task
, you're basically saying, "Hey, go do this task in the background while I keep things moving!" • Your event system stays smooth and responsive - no waiting around! • Your async tasks (like room deletion) get handled without interrupting the flow It's a super common approach when you're working with LiveKit and need to juggle sync and async tasks. Pretty neat, right? Sources: telephony/sip_lifecycle.py | agent/main.py | examples/participant_attributes.py
r
can I move the on_participant_attributes_changed_handler and async_on_participant_attributes_changed to outside of the entrypoint fxn?
t
Hey there! Let me break down how you can define
on_participant_attributes_changed_handler
and
async_on_participant_attributes_changed
outside the
entrypoint
function. I totally get that you might want more flexibility with your code placement. • Just keep in mind that when you define handlers inside
entrypoint
, they naturally have access to cool local variables like
ctx
(your JobContext) and other resources. • If you decide to move these functions outside, no worries! You'll just need to pass along any dependencies like
ctx
or agent instances as parameters to your handler functions. • Pro tip: Make sure you still register your event handler (
ctx.room.on("participant_attributes_changed", handler)
) within
entrypoint
after connecting to the room. This is actually the approach used in all the official examples – it helps ensure your handlers have everything they need. Just manage those dependencies thoughtfully, and you'll be golden! 😊 Sources: telephony/sip_lifecycle.py | agent/main.py | server/main.py