elegant-judge-25223
04/01/2026, 8:31 AMtall-airline-16756
04/01/2026, 8:32 AMtall-airline-16756
04/01/2026, 8:32 AMconversation_item_added event and llm_node for user messages, and the differences when preemptive_generation is false vs true. Below I’ll walk through both flows and summarize the key differences and consequences.
When preemptive_generation = false
1. User turn completes:
◦ The user speaks, STT (speech-to-text) transcribes the audio.
◦ The final transcript is committed to the conversation history.
1. conversation_item_added fires:
◦ The user message is added and the conversation_item_added event is emitted for that finalized user message.
1. llm_node runs:
◦ The llm_node is invoked after the message is finalized and receives the chat_ctx that includes the finalized user message.
◦ Generation starts from a stable, final input; typically only one llm_node call per user turn.
Consequences
• Linear and deterministic flow: event → LLM.
• No speculative responses based on partial transcripts.
• Slightly higher perceived latency because generation waits for end-of-turn.
When preemptive_generation = true
1. User begins speaking:
◦ Partial transcriptions arrive while the user is still speaking.
1. llm_node may run preemptively:
◦ The system can invoke llm_node speculatively using the partial transcript (before the user turn is finalized).
◦ This can produce an early/streaming agent response.
1. Finalization and `conversation_item_added`:
◦ When the user finishes, the final transcript is committed and conversation_item_added fires for the finalized user message.
1. Correction/restart if needed:
◦ If the preemptive generation was based on an incomplete or incorrect partial transcript, that speculative generation is canceled and llm_node is invoked again with the final chat_ctx.
◦ This can result in multiple llm_node invocations for a single user turn (preemptive + final).
Consequences
• llm_node can run before conversation_item_added.
• Lower latency when speculation is correct, but extra work and possible cancellations if partial transcript differs from final.
• conversation_item_added remains tied to committing the final user message — preemptive generation does not emit that event earlier.
Key differences (short)
• Order: with preemptive=false, conversation_item_added → llm_node. With preemptive=true, llm_node can run before conversation_item_added.
• Calls per turn: preemptive=false typically one call; preemptive=true can be multiple.
• Input to `llm_node`: preemptive=false uses finalized user message; preemptive=true may use partial transcript and later the finalized message.
• Effect on latency and correctness: preemptive helps latency but risks re-generation if partial was wrong.
Helpful pointers / next steps I can help with
• If you want, I can show a sequence diagram or a short example timeline (timestamps and events) for each mode.
• If you’re debugging a case where the agent restarted generation unexpectedly, share a brief timeline (partial transcripts, final transcript, and observed llm_node calls) and I can help pinpoint whether preemptive generation caused the re-run.
Would you like a timeline example or a diagram for one of your specific sessions?
Sources: Agent session | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation | Agent speech and audio | LiveKit Documentation, with interactive elementselegant-judge-25223
04/01/2026, 8:39 AMtall-airline-16756
04/01/2026, 8:39 AMconversation_item_added handler to run based on the user message and to run before any TTS output for that user message. Below I’ll explain how to achieve that in both preemptive_generation = false and preemptive_generation = true modes, and give practical guidance you can apply.
Short answer
• If you need your condition to evaluate on the finalized user message before any TTS is produced for that message, rely on the conversation_item_added event (which fires when the final transcript is committed) or the on_user_turn_completed lifecycle hook. That event/hook happens before agent-side TTS of the user message and before the agent’s response generation when preemptive_generation is false.
• With preemptive_generation = true, speculative llm_node runs may start before conversation_item_added, so to guarantee you act before any TTS of the final user message, run your condition in conversation_item_added / on_user_turn_completed (not in speculative code paths). If you also need to intercept before any speculative TTS triggered by preemptive generation, you must detect and handle the speculative path (see details below).
Detailed explanation and recommended implementation
1) Where to run your condition (guaranteed-to-be-finalized user text and before TTS)
• Use conversation_item_added for the user message, or on_user_turn_completed if you have that lifecycle hook available. Both run after the final transcript is committed to chat history and before normal agent processing of that final message.
• When conversation_item_added fires for the finalized user message, agent-side TTS for the user message itself will not have been emitted yet. This is the right place to evaluate conditions that must be enforced before any TTS that consumes the finalized text.
2) Behavior when preemptive_generation = false
• Flow: STT → final transcript commit → conversation_item_added (your condition runs) → llm_node invoked → agent response generation → agent TTS for the agent response.
• Because no speculative generation happens, evaluating your condition in conversation_item_added guarantees it runs before any agent generation/TTS tied to that user message.
3) Behavior when preemptive_generation = true
• Flow can include: STT (partial) → speculative llm_node runs using partial transcript → speculative agent output may start streaming; later STT final commit → conversation_item_added fires for finalized user message → final llm_node run may be triggered or speculative run canceled.
• Important points:
◦ llm_node (speculative) can run before conversation_item_added. If your condition only lives in conversation_item_added, it will not prevent speculative generation that already started.
◦ However, conversation_item_added still fires when the final transcript is committed. Running your condition there will let you evaluate the finalized user message and, if needed, cancel or modify subsequent processing (for example cancel final TTS or re-route the flow).
◦ If you must prevent any TTS that might be triggered by speculative generation, you need to detect the speculative path early and either:
▪︎ Disable/prevent speculative generation globally for the sessions where you require this guarantee, or
▪︎ Add a check in the speculative generation path that evaluates the same condition against partial transcripts and prevents starting TTS when the partial transcript meets your blocking criteria (accepting that partial transcripts may be incomplete/more likely to false-trigger).
4) Practical strategies you can use
• Preferred (safe) approach: Keep preemptive_generation = false for sessions where you require deterministic pre-TTS checks. Then evaluate your condition in conversation_item_added or on_user_turn_completed. This is the simplest way to guarantee ordering.
• If you must use preemptive_generation = true for latency reasons:
◦ Implement the same condition in two places:
▪︎ a) Early/speculative check that runs on partial transcripts before starting speculative TTS (accepting higher false positives), and
▪︎ b) Final check in conversation_item_added that runs on the finalized transcript and cancels or adjusts any speculative outputs if needed.
◦ Use a cancelation mechanism to stop speculative outputs when final transcript differs or when final condition says to block.
• If the condition depends on the fully finalized wording (e.g., exact phrase matching, entity resolution), prefer waiting for conversation_item_added and then cancel/restart the flow rather than attempting to rely on partial transcripts.
5) Example minimal flow for required behavior
1. Turn starts:
◦ If `preemptive_generation=false`: no speculative work; wait.
◦ If `preemptive_generation=true`: speculative path may begin (optional).
1. Final transcript committed → `conversation_item_added`:
◦ Evaluate your condition on the finalized message.
◦ If condition = true and you want to prevent TTS related to that message:
▪︎ Cancel any in-progress speculative TTS/generation.
▪︎ Prevent scheduling of agent TTS (block or alter downstream actions).
◦ If condition = false, allow normal processing to continue.
6) Debugging tips
• Log timestamps for: partial transcripts, speculative llm_node starts, conversation_item_added, final llm_node starts, and TTS start events. This will show if speculative TTS began before your condition ran.
• If you observe unexpected TTS before conversation_item_added, you likely have speculative generation enabled and no early check to block it.
Would you like:
• A short sequence diagram/timeline example for your specific session logs? If so, share a snippet of timestamps (partial transcript events, conversation_item_added, and TTS start) and I’ll map the events to show where to insert the checks.
• Or code-like pseudocode for where to place the condition and how to cancel speculative outputs?, with interactive elementselegant-judge-25223
04/01/2026, 10:22 AMtall-airline-16756
04/01/2026, 10:23 AMtts_node to be invoked while your custom llm_node is awaiting a non-LLM task (for example, a RAG lookup, external API call, or other async work) instead of actively streaming LLM output. Correct?
A few clarifying questions so I can give a precise answer and actionable guidance:
• Are you seeing the tts_node start producing audio for the user message, or audio for an agent reply? Which speaker is the TTS for?
• Is your llm_node explicitly awaiting a background task (RAG, external API, DB call) before it returns any LLM response tokens, or is it blocked in some other way?
• Do you have preemptive_generation enabled for this session?
• Do you have logs or timestamps showing when:
◦ the llm_node began and when it awaited,
◦ the conversation_item_added (final user message) event,
◦ and when the tts_node started?
Sharing a short timeline (3–5 lines) will help me map the ordering.
What I can help with once you share those details
• Explain expected ordering of events for both preemptive_generation = true/false and where TTS is normally triggered.
• Identify likely reasons why TTS might start while llm_node is still awaiting (for example: speculative/early TTS tied to partial transcripts, a separate TTS trigger path, or an implementation detail in your custom nodes).
• Show where to add checks/cancellations (in conversation_item_added, speculative path, or inside tts_node) so TTS only runs when you want it to.
• Provide short pseudocode examples to ensure your llm_node and tts_node coordinate properly.
If you prefer, paste a short snippet of the event ordering or logs you’re seeing and I’ll map the flow and recommend concrete fixes.
, with interactive elementselegant-judge-25223
04/01/2026, 10:26 AMtall-airline-16756
04/01/2026, 10:27 AMelegant-judge-25223
04/01/2026, 11:27 AM