f
Hii
t
Thanks for reaching out, Vishalli. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Hi — great to hear from you! 👋 How can I help today? A few examples of things I can assist with: • Troubleshooting a room or connection issue • SDK setup (web, iOS, Android) or examples • Authentication, tokens, or server setup • Features like recording, tracks, or participant management • Billing, limits, or account questions If you’re not sure where to start, tell me: • What platform or SDK you’re using (Web, iOS, Android, or REST/API) • Any error messages, room ID, or example code snippets Share a bit more and I’ll guide you step‑by‑step.
f
i am facing one issue on warm transfer the merge of call is not happening agent is saying the summary and all to other party but as as soon as it triedto merge call disconnected i am using warm trnafser task import json import logging import os from livekit.agents import RunContext, function_tool, get_job_context from livekit.agents.beta.workflows import WarmTransferTask logger = logging.getLogger("inspra_main/func/warm_transfer") def fn_add_warm_transfer(self, name: str, instructions: str, phone: str): """ Registers a tool that performs a warm transfer using LiveKit's WarmTransferTask. The agent places the caller on hold, dials the target, briefs them with a conversation summary, then moves the target into the caller's room. """ schema = { "type": "function", "name": name, "description": instructions, "strict": True, "parameters": { "type": "object", "properties": { "summary": { "type": "string", "description": "A short summary of the call context or conversation.", } }, "required": ["summary"], "additionalProperties": False, }, } async def warm_transfer_handler(raw_arguments: dict[str, object], context: RunContext): job_ctx = get_job_context() summary = raw_arguments.get("summary", "") logger.info(f"Starting warm transfer to {phone} with summary: {summary}") try: # Get trunk ID: from dispatch (outbound) or from Redis workspace config (inbound) trunk_id = getattr(self.attributes, "trunk_id", None) if not trunk_id: redis_client = getattr(self, "redis_client", None) inspra_phone = getattr(self.attributes, "inspra_phone", None) if redis_client and inspra_phone: try: ws_json = redis_client.get(f"phoneNumber:{inspra_phone}") if ws_json: ws_config = json.loads(ws_json) trunk_id = ws_config.get("outboundTrunkId") except Exception as e: logger.warning(f"Failed to fetch outboundTrunkId from Redis: {e}") if not trunk_id: trunk_id = os.environ.get("LIVEKIT_SIP_OUTBOUND_TRUNK", "") if not trunk_id: logger.error("No SIP trunk ID available for warm transfer") await self.session.say("I'm sorry, I can't transfer the call right now.") return logger.info(f"Using trunk {trunk_id} to warm transfer to {phone}") await self.session.say("No worries. Let me connect you with one of our team members. Just hold on a moment.") # Mark on_hold so the inactivity detector in main.py doesn't # kill the room while the caller is waiting for the merge. self.on_hold = True logger.info(f"on_hold set to True, starting WarmTransferTask to {phone}") try: result = await WarmTransferTask( target_phone_number=phone, sip_trunk_id=trunk_id, chat_ctx=self.session.history, ) logger.info(f"WarmTransferTask returned successfully: {result}") except Exception as wt_err: logger.error(f"WarmTransferTask raised exception: {wt_err}", exc_info=True) raise finally: logger.info(f"on_hold set back to False (was {self.on_hold})") self.on_hold = False except Exception as e: logger.error(f"Unexpected error during warm transfer: {e}", exc_info=True) try: await self.session.say( "Sorry, all the specialists are busy right now, " "but we've noted your details and someone will call you back shortly." ) except RuntimeError: logger.info("Session already closing, caller likely hung up") return function_tool(warm_transfer_handler, raw_schema=schema)
t
Thanks — I can see the issue you’re describing: the agent can brief the other party, but as soon as the merge should happen the call disconnects. I reviewed your handler and the WarmTransferTask usage and put together focused troubleshooting steps and quick fixes you can try next. Key areas to check and quick remedies • Verify the SIP trunk ID actually resolves and is valid ◦ Log the resolved value before dialing:
Copy code
<http://logger.info|logger.info>(f"Resolved trunk_id: '{trunk_id}' (type: {type(trunk_id)})")
• If
trunk_id
is empty, the merge may fail when the second leg is created. Ensure the trunk is an outbound SIP trunk and has outbound permissions. • Ensure you pass a proper ChatContext, not raw session history ◦ WarmTransferTask expects a
ChatContext
object. Passing
self.session.history
(a raw list) can cause unexpected behavior. Use
self.chat_ctx
or construct a `ChatContext`:
Copy code
from livekit.agents.llm import ChatContext
    chat_ctx = ChatContext(messages=list(self.session.history))
    result = await WarmTransferTask(..., chat_ctx=chat_ctx)
• Or simply:
chat_ctx=self.chat_ctx
if available. • Prevent the room/session from being deleted while transfer runs ◦ You set
self.on_hold = True
which is good, but confirm no other inactivity watchdog or background task still deletes the room. Cancel or pause any inactivity timer during the transfer so the room stays alive until merge completes. Add clear begin/end logs around the await so you can see if control ever returns:
Copy code
<http://logger.info|logger.info>("BEGIN WarmTransferTask")
    result = await WarmTransferTask(...)
    <http://logger.info|logger.info>("END WarmTransferTask")
• Confirm the human agent actually answers (not voicemail) and the second leg is established ◦ Log the WarmTransferTask result and check for fields like
human_agent_identity
. If that is absent, the second leg never connected and merge won’t occur:
Copy code
<http://logger.info|logger.info>(f"WarmTransferTask result: {result}")
    if not getattr(result, "human_agent_identity", None):
        logger.warning("Second leg didn't connect — merge aborted")
• Check trunk capabilities and SIP behavior that affect merges ◦ The trunk must allow outbound bridging and support session progression needed for a media merge (SIP REFER / re-INVITE / session updates depending on flow). If the provider or trunk blocks these, the merge can fail even though dialing worked. • Watch for runtime/job termination or resource limits ◦ If your agent process is killed (timeout, memory, or container restart) during the transfer, the merge will never complete. If your END log never appears, investigate job timeouts or OOMs on the agent host. Suggested minimal changes to test quickly 1. Add logs to confirm trunk_id, entry/exit of WarmTransferTask, and the Task result. 2. Switch
chat_ctx=self.session.history
→
chat_ctx=self.chat_ctx
(or build a ChatContext) and retry. 3. Temporarily disable any inactivity detector while the transfer runs to rule out premature room deletion. 4. If possible, test the trunk by making a standalone SIP outbound call (outside WarmTransferTask) and confirm bridging behavior. If you still see disconnects, please share: • The log output around the WarmTransferTask call (BEGIN/END/result) — sanitized if needed. • The
trunk_id
value (or confirm it was non-empty and the trunk type). • Whether the human agent actually answers or goes to voicemail. I’ll help interpret the logs and next steps once you share them. If you’d like, I can also walk through a manual test flow (creating a SIP participant then attempting a merge) to isolate whether dialing or merging is failing. Sources: Preserving Caller ID During SIP Transfers in LiveKit Agents | sip | livekit/livekit_composite | DeepWiki | examples/frontdesk | livekit/livekit_composite | DeepWiki | Agents telephony integration | LiveKit Documentation | WarmTransferTask | LiveKit Documentation
f
trunk id is valid
t
Thanks — that helps. Since the trunk ID is valid, let’s focus on the next highest‑likelihood causes and narrow down diagnostics so we can pinpoint where the merge is failing. Quick checklist to confirm (please run these and share results/logs): 1. Confirm ChatContext type ◦ Are you passing
self.chat_ctx
to WarmTransferTask, or
self.session.history
?
WarmTransferTask
expects a
ChatContext
. ◦ If you’re using
self.session.history
, try replacing it with
self.chat_ctx
or build a
ChatContext
and retry. 1. Add minimal entry/exit/result logging around the call and paste the logs here ◦ Add these logs exactly as shown and run a failing warm transfer so we can see whether the task finishes or is interrupted:
Copy code
<http://logger.info|logger.info>("BEGIN WarmTransferTask")
     result = await WarmTransferTask(
         target_phone_number=phone,
         sip_trunk_id=trunk_id,
         chat_ctx=self.chat_ctx,  # or constructed ChatContext
     )
     <http://logger.info|logger.info>(f"END WarmTransferTask, result: {result}")
• Share the log lines for BEGIN, any intermediate logs, and END/result (sanitize any sensitive data). 1. Check whether the human answers vs. voicemail ◦ In the logs from step 2, does the Task return a
human_agent_identity
or other indicator that the person answered? If not, the second leg is not establishing and merge will abort. 1. Verify the room isn’t being deleted while transfer runs ◦ Temporarily disable any inactivity watcher or room cleanup that might run while
self.on_hold
is True. ◦ Add logs around your inactivity detector to show it does not trigger during the transfer. 1. Confirm the agent process stays alive for the duration ◦ Check your host/application logs for process restarts, timeouts, or OOM events during the transfer. If your END log never appears, your agent process may have been killed. If you prefer, share these specific items next: • The log output lines for BEGIN/END/result from above (sanitized). • The snippet showing which object you pass as
chat_ctx
. • Any room events around the time of transfer (participant_left, room_deleted) if available. What I’ll look for when you share logs • Whether WarmTransferTask returned successfully or raised an exception. • If it returned, whether it includes a human agent identity or success flag. • Any signs that the room was deleted or the agent process ended mid-transfer. Once you post the logs and confirm which
chat_ctx
you used, I’ll point to the exact next step (e.g., adjust trunk settings, change how we pass chat context, or try a manual SIP participant merge to isolate the failing step).
f
issue is person on the other end can hear bot voice
t
Thanks — that’s a helpful detail. I understand the other party hears the bot’s voice after the transfer instead of the human agent. Let’s narrow down why the bot audio remains live during the merge and how to fix it. Quick checklist and targeted questions 1. Are you stopping or muting the bot’s playback before merge? ◦ Do you explicitly stop the TTS/playback or mute the bot participant prior to calling WarmTransferTask or before you perform the merge? If not, the bot’s audio stream can remain active in the room and be heard by the other party. 1. How is the bot sending audio? (playback vs. mic-forwarding) ◦ Is the bot injecting audio as a playback track (e.g., an audio file / TTS track) or forwarding microphone/audio input from the agent session? Playback tracks need to be stopped or unpublished; microphone tracks may be muted. Tell me which one you use. 1. Which merge flow is used — WarmTransferTask’s automatic merge or a manual create+merge? ◦ Confirm you call WarmTransferTask directly and rely on its merge logic, or if you create a SIP participant and then call a separate merge API. That changes where to stop/unpublish the bot track. What to do immediately (quick fixes to test) • Stop/unpublish the bot’s audio track before initiating the merge or before moving the human into the caller’s room: ◦ If the bot publishes an output audio track, unpublish or stop that track so it’s no longer active. ◦ If you can’t unpublish, mute the bot participant’s audio so the track is silent at merge time. • If using TTS playback, ensure playback ends and the player is destroyed before merging. • Add a short delay between finishing the summary and doing the merge to ensure playback has fully stopped (300–700 ms is often sufficient). Suggested code-level checks • If the bot controls a LiveKit Participant, call the stop/unpublish/mute method on that participant or the track before merge. • If using a local TTS player, ensure the playback promise completes and the audio element is removed/destoyed before WarmTransferTask returns. • Add logs immediately before merge showing active tracks/participants: ◦ Log published tracks for the bot participant. ◦ Log participant states (muted / publishing) for the caller, bot, and agent. Example diagnostics to capture and share • Logs showing: ◦ Bot finished speaking (TTS playback ended) and any stop/unpublish/mute actions. ◦ BEGIN WarmTransferTask and END WarmTransferTask (or any exception). ◦ Room participant list and each participant’s published tracks just before merge. • A minimal snippet of how the bot plays audio and how you stop it (sanitized). If you want, try this quick experiment 1. After the bot finishes the summary, immediately call code to mute/unpublish the bot audio track. 2. Wait 0.5s. 3. Trigger the WarmTransferTask and watch whether the other party still hears the bot. Next steps I can help with • Show exact code examples for stopping/unpublishing/muting the bot based on how you publish audio (give me the snippet that publishes playback or TTS). • Help parse the logs you capture to confirm whether the bot’s track is still active at merge time. • Walk through a manual merge flow if WarmTransferTask needs more explicit track state handling. Please tell me: • How the bot plays audio (TTS playback element, published file, or mic forwarding). • A short log snippet showing bot playback end + BEGIN WarmTransferTask (sanitized). • Whether you can mute/unpublish the bot participant before merging. I’ll give the exact code/commands to stop the bot audio and ensure the human voice is what the other party hears.
f
but when it tries to merg ethe call bot has to ledt the call but it is deleting the session
t
Thanks — that’s clear and I can see why this is blocking your warm transfer. You said the bot must leave the call during merge, but when it leaves the session is deleted and the whole call drops. Let’s focus on why removing the bot is triggering room/session deletion and how to remove the bot gracefully so the human agent and caller remain connected. What I’m going to help with • Diagnose why the session/room is being deleted when the bot leaves. • Give concrete, safe steps to remove/unpublish the bot without terminating the room. • Ask for a few targeted logs/snippets to pinpoint the root cause. Likely causes (short list) • The bot is the last non-SIP participant and your cleanup/inactivity logic deletes the room when only the bot remains or when the bot leaves. • Your code explicitly deletes the session/room when the bot disconnects (e.g., in participant disconnect handlers). • An inactivity watcher or background job sees no active participants/tracks and triggers room deletion even though the SIP leg or human agent still needs to be merged. • The bot’s leave is closing an underlying session object that you use for the entire room (shared session object), not just the bot participant. What I need from you to pinpoint the issue 1. How does the bot “leave” the call in your code? Share the snippet or describe the method you call (e.g.,
session.close()
,
participant.disconnect()
,
unpublish_track()
, or
self.session.end()
). 2. Logs around the time of the merge attempt (sanitized): ◦ Lines showing bot stop/unpublish, BEGIN WarmTransferTask, any participant_left events, and any room_deleted or session.close calls. 1. Do you have an inactivity watcher or explicit cleanup that deletes the room when certain flags change (for example,
on_hold
logic or “if only bot present -> delete room”)? If yes, paste that logic or describe it. 2. Is the bot the same process that runs the WarmTransferTask job (i.e., does the bot process own the job/session lifecycle)? Immediate checks and quick fixes to try (in priority order) 1. Confirm you are only removing the bot participant, not closing the whole session ◦ Replace any call like
session.close()
or
self.session.end()
with the participant-specific removal: ▪︎ Unpublish or stop the bot’s audio track, then call the bot participant’s disconnect/unpublish API (e.g.,
participant.unpublish_track(...)
or
participant.disconnect()
), not the room/session close. ◦ If you must close a session object, ensure it is the bot participant’s session, not the whole room/session object shared by the caller/human agent. 1. Ensure the room deletion/inactivity logic is paused during transfer ◦ When you set
self.on_hold = True
, also pause any background cleanup that deletes rooms based on participant counts or silence. ◦ Add a guard: if WarmTransferTask is in progress, skip room deletion:
Copy code
if warm_transfer_in_progress:
         skip_cleanup()
• Add logging in the cleanup to show why it deleted the room (participant count, inactivity timeout, etc.). 1. Remove the bot’s audio without leaving the room immediately ◦ Unpublish or mute the bot’s audio track first so callers don’t hear the bot: ▪︎ Unpublish: remove the playback track from the bot participant. ▪︎ Mute: mark the bot participant as muted to prevent audio transmission. ◦ After confirming the agent is fully joined and the merge succeeded, remove the bot participant. 1. Add a short delay and checks before deleting the bot participant ◦ After WarmTransferTask reports the agent joined (or after you call merge), wait ~300–700 ms and verify the agent’s participant is present and publishing audio, then remove/unpublish the bot. Example safe flow 1. Bot finishes TTS and stops/unpublishes playback track. 2. Set a flag:
warm_transfer_in_progress = True
and pause cleanup. 3. Call
WarmTransferTask(...)
to dial and merge the agent. 4. On success (check result contains agent identity), confirm agent participant exists and has audio tracks. 5. Unpublish/mute and then disconnect the bot participant. 6. Clear
warm_transfer_in_progress
so cleanup resumes. Diagnostics to capture (please share) • The exact code you call when the bot “leaves” (show the function call). • Logs for these events: bot stop/unpublish, BEGIN WarmTransferTask, WarmTransferTask result, participant_left for bot, room_deleted or session closed. • The room participant list right before the bot leaves (who’s present and their published tracks). If you share the snippet where you remove the bot (or the relevant cleanup handler), I’ll point out the precise change to make so you only remove the bot participant and not the session. If you share the logs, I’ll highlight the exact event that triggers room deletion and the minimal code change to prevent it.