bumpy-florist-37019
12/21/2025, 12:34 AMsession.say() inside tts_node(), what guarantees exist about suppression of further yields? Is there an integrated mechanism to prevent tts_node() from continuing and yielding after a session.say() call?tall-airline-16756
12/21/2025, 12:34 AMtall-airline-16756
12/21/2025, 12:35 AMsession.say() inside tts_node() automatically suppresses further yields or stops the node from continuing.
Short answer: It does not. There is no built‑in guarantee that session.say() will halt the current tts_node() execution or prevent further yields.
What this means and what you can do:
• session.say() schedules speech (returns a SpeechHandle) via the agent's speech queue, but scheduling is independent of the tts_node() control flow.
• If you want tts_node() to stop yielding after calling session.say(), implement that yourself:
◦ Return early from tts_node() immediately after session.say().
◦ Or set and check a control flag (e.g., should_stop) before each yield and exit when set.
◦ If you need to cancel in‑flight speech, use the interruption/cancel mechanisms provided by the speech system.
If you want, tell me which behavior you want (e.g., immediate stop, finish current chunk then stop, or cancel scheduled speech) and I’ll suggest a specific code pattern.
Sources: Pipeline nodes and hooks | LiveKit docs | Agent speech and audio | LiveKit docsbumpy-florist-37019
12/21/2025, 12:36 AMsession.say() is invoked inside the same agent turn, how does LiveKit’s internal TTS pipeline reconcile generator yields vs explicit TTS calls? Is there a recommended pattern to avoid double speech?tall-airline-16756
12/21/2025, 12:36 AMtts_node() generator yields with session.say() invoked in the same agent turn, and how to avoid double/overlapping speech.
Short answer: LiveKit does not automatically reconcile them. If session.say() is called while tts_node() is yielding, both can produce audio unless you coordinate them.
Recommended patterns to avoid double speech
• Use one mechanism per turn:
◦ Prefer tts_node() for LLM-driven replies.
◦ Use session.say() only for injected/short phrases (greetings, interrupts).
• Return or stop the generator after calling `session.say()`:
◦ Call return from tts_node() or set a control flag checked before yielding further frames.
• Interrupt/cancel current speech before calling `session.say()`:
◦ Call session.interrupt() or interrupt the active SpeechHandle, then await cancellation if needed, then call session.say().
• Chain via separate turns when injecting speech:
◦ Interrupt → session.say() → start a new generate_reply() if follow-up LLM output is needed.
• Use pre-synthesized audio to bypass TTS:
◦ Call session.say(audio=...) with prepared frames so tts_node() is not involved.
If you tell me which behavior you want (immediate stop, finish current chunk then stop, or cancel queued speech), I’ll provide a concise code pattern you can plug into your tts_node() implementation.
Sources: Agent speech and audio | LiveKit docs | Pipeline nodes and hooks | LiveKit docs | Interrupt User | LiveKit docs | Turns overview | LiveKit docs | Text and transcriptions | LiveKit docs