I want to do a cold sip transfer with a timeout af...
# ask-ai
l
I want to do a cold sip transfer with a timeout after which it comes back to the AI. How to I configure the call to stop ringing after 15 seconds, and to stop the agent from listening and generating speech in the meatime
t
Thanks for reaching out, David. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
I'd love to help you set up a cold SIP transfer with a timeout, making sure the agent stops listening and generating speech during the transfer. Here's how you can do it: • Start by using the
TransferSIPParticipant
API to initiate the transfer. You can set the
ringing_timeout
parameter to 15 seconds so the call stops ringing automatically after that time. • Once the transfer starts, the SIP participant will leave the LiveKit room. This naturally stops the agent from listening and generating speech, as the session ends when the participant leaves. Here's a simple example in Python to guide you:
Copy code
from livekit import api
from google.protobuf.duration_pb2 import Duration

await ctx.api.sip.transfer_sip_participant(
    api.TransferSIPParticipantRequest(
        room_name=ctx.room.name,
        participant_identity=participant_identity,
        transfer_to="tel:+15105550123",
        ringing_timeout=Duration(seconds=15)
    )
)
Also, please make sure your SIP trunk supports SIP REFER for transfers. If you're using Twilio, you'll want to enable PSTN Transfer and set
transfer-mode
to
enable-all
. If you have any questions or need a hand with anything else, just let me know—I'm here to help! Sources: Agents telephony integration | LiveKit Docs | Transferring calls | LiveKit Docs | livekit-agents/livekit/agents/job.py | livekit-api/livekit/api/sip_service.py
l
where is the link to the doc for TransferSIPParticipant
t
I'm happy to help you find the documentation for the
TransferSIPParticipant
API! You can check it out on the official LiveKit SIP APIs page here: TransferSIPParticipant Documentation. This page covers all the details you'll need, including required parameters, optional settings, and authentication requirements. If you have any questions or need a hand with anything else, just let me know—I'm here for you! Sources: SIP APIs | LiveKit Docs | Transferring calls | LiveKit Docs
l
there is no ringing_timeout on it
t
Great question! The
TransferSIPParticipant
API does indeed support setting a ringing timeout through the
ringing_timeout
field in the
TransferSIPParticipantRequest
. This lets you specify how long (for example, 15 seconds) the transfer destination has to answer before the transfer is considered failed. Here’s a friendly example to show you how to include it:
Copy code
from google.protobuf.duration_pb2 import Duration

await ctx.api.sip.transfer_sip_participant(
    api.TransferSIPParticipantRequest(
        room_name="my-room",
        participant_identity="sip-participant",
        transfer_to="<sip:1234@example.com>",
        ringing_timeout=Duration(seconds=15)  # 15-second timeout
    )
)
If you have any more questions or need a hand with this, just let me know—I’m here to help! Sources: rpc/sip.go | pkg/service/sip.go
l
will I reconnect to the call directly when the timeout hits ?
t
I understand that the
TransferSIPParticipant
operation can be a bit tricky. When the ringing timeout is reached during this process, the transfer attempt doesn’t go through, but don’t worry—the original SIP participant stays connected in the current call. So, there’s no need to reconnect since they’re never actually disconnected during the transfer. If the transfer doesn’t succeed because of a timeout, the original call will continue just as it was. Please feel free to reach out if you’d like me to explain anything further or if you have any other questions—I’m here to help! Sources: pkg/sip/service.go | SIP troubleshooting guide | LiveKit Docs
l
you said before that the participant leaves the call as soon as we do the sip transfer
t
Thank you so much for bringing that up! Just to clarify, the original participant actually stays in the call while the transfer is happening. They only leave the LiveKit room and end the session once the transfer goes through successfully. If something goes wrong, like a timeout or another issue, the original participant will remain connected to the call. Please feel free to reach out if you have any more questions or if there’s anything else I can help clarify for you! Sources: Transferring calls | LiveKit Docs | Agents telephony integration | LiveKit Docs | Agents telephony integration | LiveKit Docs | protobufs/livekit_sip.proto | livekit-agents/livekit/agents/job.py
l
{"message": "Failed to transfer call: Protocol message TransferSIPParticipantRequest has no \"set_timeout_seconds\" field.\
mb
t
It looks like the error is happening because the
set_timeout_seconds
field isn’t actually part of the
TransferSIPParticipantRequest
. No worries though! To manage timeouts, you can try these approaches: • Ringing Timeout: You can use the
ringing_timeout
field in your request to set how long the system should wait for the transferred call to be answered—say, 15 seconds as an example. • Context Deadline: Another way is to pass a context with a custom timeout (like
context.WithTimeout
in Go or something similar in other languages) when you call the API. This way, you control how long your client waits for the operation to finish. If you’d like, I’m here to help you set this up or answer any questions you have! Sources: rpc/sip.pb.go | livekit-protocol/src/livekit.rs | sipclient.go | pkg/service/sip.go
Absolutely no problem at all! Please feel free to reach out if you have any more questions or if there's anything else I can help you with. I'm here for you!
l
I need to put this in a async create task otherwise the function call times out after 30 seconds. I want to have a different too call response if the transfer timesout
t
I understand how important it is to handle different responses when a
TransferSIPParticipant
times out, so let me guide you through a friendly approach to manage this smoothly: • Set a Context Deadline: You can use a custom timeout (like
context.WithTimeout
in Go or something similar) to decide how long your client should wait for the operation to finish. • Catch Timeout Errors: It’s helpful to detect specific timeout errors (such as 408 Request Timeout) so you can respond accordingly—maybe notify the user or even try the transfer again. • Use Ringing Timeout: Adjust the
ringing_timeout
field in your request to control how long the system waits for the destination to pick up. Here’s a simple example in Python to get you started:
Copy code
try:
    await ctx.api.sip.transfer_sip_participant(
        api.TransferSIPParticipantRequest(
            room_name="my-room",
            participant_identity="sip-participant",
            transfer_to="tel:+18003310500",
            ringing_timeout=Duration(seconds=15)
        )
    )
except Exception as e:
    if "408" in str(e):
        await ctx.room.local_participant.publish_data("Call transfer timed out. Connecting you to support.")
    else:
        logger.error(f"Unexpected error: {e}")
If you’d like, I’m here to help you implement this or answer any questions along the way! Sources: pkg/sip/service.go | Making calls using SIP | LiveKit Docs | examples/voice_agents/error_callback.py | pkg/service/sip.go | sipclient.go
l
That's fine, but the llm tool call to this code itself will timeout after 30 seconds if I dont run it as a background task. How can I run this sync but not have the llm tool call timing out?
t
To help you avoid the LLM tool call timing out while running
TransferSIPParticipant
, a great approach is to run the transfer as a background task and manage interruptions gracefully. Here’s a friendly step-by-step guide: 1. Run the transfer as a background task: You can use
asyncio.ensure_future()
to start the transfer without blocking the main thread, keeping things smooth. 2. Handle interruptions: By using
run_ctx.speech_handle.wait_if_not_interrupted()
, you wait for the task to finish unless the user interrupts it. 3. Return a response: It’s helpful to provide a fallback message if the transfer times out or gets interrupted, so users stay informed. Here’s an example to illustrate this:
Copy code
@function_tool
async def transfer_sip_participant_tool(self, participant_identity, transfer_to, run_ctx):
    transfer_task = asyncio.ensure_future(
        self.api.sip.transfer_sip_participant(
            api.TransferSIPParticipantRequest(
                room_name=run_ctx.room.name,
                participant_identity=participant_identity,
                transfer_to=transfer_to,
                play_dialtone=True,
            )
        )
    )

    await run_ctx.speech_handle.wait_if_not_interrupted([transfer_task])

    if run_ctx.speech_handle.interrupted:
        transfer_task.cancel()
        return "Transfer was interrupted."

    return "Call transferred successfully."
This way, the LLM tool call won’t block forever and you’ll handle any timeouts or interruptions smoothly. If you have any questions or want to explore this further, just let me know—I’m here to help! Sources: examples/voice_agents/long_running_function.py | sipclient.go | packages/livekit-server-sdk/src/SipClient.ts | Making calls using SIP | LiveKit Docs | sip-refer/agent.py