steep-balloon-41261
03/12/2026, 6:51 AMtall-airline-16756
03/12/2026, 6:52 AMtall-airline-16756
03/12/2026, 6:52 AMRoomInputOptions.close_on_disconnect, which can be relevant if you want different behavior.
If you want, I can help narrow this down further:
• Do you have the exact voicemail duration that reached the remote system? (or the TTS audio duration)
• Can you share the TTS text length or token counts you expect for a typical message?
• Would you like sample timings (pause lengths and message length) to test against common voicemail systems?
Happy to dig in with those details and help you tune the timing so voicemails finish reliably.
Sources: Agents telephony integration | LiveKit Documentationbetter-sandwich-65834
03/12/2026, 6:54 AMtall-airline-16756
03/12/2026, 6:54 AMbase_agent_call_ending_farewell_instructions)
• Please paste the exact text/content of base_agent_call_ending_farewell_instructions. If it contains an instruction to speak a farewell or produce audio, it could be preempting the voicemail generation when update_instructions(farewell) runs.
4) Are you open to re-applying the VAD fix (turn_detection=None) specifically for the voicemail path?
• Tell me Yes/No. If Yes, I can propose a minimal code change to re-enable turn_detection=None only for outbound/voicemail flows so server-side VAD won’t trigger during playout. If No, we’ll prioritize avoiding session.update while generation is in-flight.
My current read and recommended next step
• Your hypothesis is sound: calling update_instructions(farewell) inside hangup() while voicemail generation was still in-flight is the most likely cause. Azure Realtime session.update can interrupt active responses. The safer change is to await the voicemail generation/playout to finish before calling any session.update / hangup that writes new instructions.
• To confirm and finalize the fix I need the items above (especially item 1 and the farewell text in item 3). Once you provide those I will recommend the exact code change (example: await handle before calling hangup; or defer update_instructions until wait_for_playout returns) and a short test plan.
If you want, I can draft the precise code change (small diff) to:
1. Await the generate_reply/playout handle to complete, then
2. Call update_instructions(farewell) and hangup()
Let me know which artifacts you can provide first (recording/transcript and the farewell text), and whether you’re open to the VAD change. I’ll then give the exact patch and test steps.better-sandwich-65834
03/12/2026, 7:07 AMtall-airline-16756
03/12/2026, 7:07 AMawait handle.wait_for_playout() before calling hangup.
2. Sleep for a configurable drain period (start with 20s as you suggested) — make this configurable so you can tune per carrier.
3. Keep the VAD change (turn_detection=None) for the voicemail path if you’re open to it — it reduces risk of server-side VAD falsely triggering during playout.
4. Wrap in try/finally to ensure turn_detection is restored and hangup still runs on unexpected errors.
If you confirm the recording is silence after "$2900", I’ll provide:
• The exact code diff/patch to implement the two changes and add a configurable PSTN drain value.
• A short test plan with pass/fail criteria you can run against Teams (or other carriers).
Which option do you prefer: confirm the recording now, or paste/attach the Teams file/transcript and I’ll verify it for you?better-sandwich-65834
03/12/2026, 7:12 AMtall-airline-16756
03/12/2026, 7:13 AMwait_for_playout() resolves when the model’s generation is finished and LiveKit has processed the outgoing audio frames internally, not when those frames have finished traversing the SIP → PSTN pipeline and been played out at the remote voicemail endpoint. Deleting the room at response.done discards audio still in-flight, causing the remote recording to end early.
• No ordering changes are required for update_instructions / update_tools — they do not interrupt the active response in your codepath. The important action is ensuring the room is not deleted until the PSTN drain completes.
• Fix: await the playout handle, then sleep long enough to cover the remaining real-time delivery window, then run hangup/teardown. Re-applying the VAD change for voicemail is recommended as a complementary mitigation.
Suggested patch (concept)
• After you `handle = self.session.generate_reply(...)`:
1. Await model playout completion:
◦ await handle.wait_for_playout()
1. Log and sleep for a configurable PSTN drain:
◦ <http://logger.info|logger.info>("Voicemail playout complete, sleeping for PSTN drain")
◦ await asyncio.sleep(self.config.pstn_drain_seconds or 20.0)
1. Proceed with await self.hangup(handle)
Implementation notes
• Make the drain duration configurable (e.g., pstn_drain_seconds) so you can tune per carrier or region; 20s is a good default based on your timing.
• Wrap the generate/await/sleep/hangup in try/finally to ensure cleanup and to restore any temporary VAD settings if you re-enable turn_detection=None for voicemail.
• Keep logs for TTFT, playout completion, and hangup times so you can measure and refine the drain value.
Example test plan
1. Baseline test (current behavior):
◦ Place outbound voicemail to Teams. Record and note the timestamp of beep, last intelligible word, and disconnect.
1. Apply change with pstn_drain_seconds = 20:
◦ Repeat the same voicemail content and compare full recording length and speech completeness.
◦ Expected: remote recording has the full message with no long silence where speech should be.
1. Tunability:
◦ Try 15s and 25s to find minimal safe drain for your carriers.
◦ Run tests across a few carriers (Teams, major PSTN carriers) to ensure compatibility.
1. Edge cases:
◦ Test with short messages (<10s) and very long messages (~45s+) to validate behavior and ensure you don’t unnecessarily prolong calls for short messages.
◦ Test noisy lines or PSTN echo scenarios if VAD is off; reapply the turn_detection=None for the voicemail path and verify no premature interruptions.
Optional: Re-apply server-side VAD mitigation for voicemail
• Re-enabling turn_detection=None for the voicemail path reduces the risk of server-side VAD or PSTN echo causing spurious input_speech_started events during playout. Wrap its use in try/finally so it’s restored immediately after the voicemail flow completes.
If you’d like, I can:
• Draft the exact minimal code diff/patch for the change (including config and try/finally) for you to drop into greetings.py.
• Provide a concise set of log assertions you can add to validate the timing (TTFT, playout done, sleep start/end, hangup timestamp).
Would you like the patch now, or do you want to run the quick confirmation test (play the 21s recording) first?