can you explain the relation between the event con...
# ask-ai
e
can you explain the relation between the event conversation_item_added and llm_node for user message for both preemptive_generation as false and true
t
Thanks for reaching out, Harsh. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’ll explain how the conversation_item_added event and the llm_node relate for user messages, and how that changes when preemptive_generation is false versus true. Brief acknowledgement • You asked specifically about the relation between the
conversation_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 elements
e
i have a condition in the conversation_item_added event which triggers based on the user message (ideally i want the condition to be triggered before tts for the user message) can you explain the above with this context
t
Thanks — this is helpful context. You want your condition in the
conversation_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 elements
e
custom tts_node being called when custom llm_node is on await for a task other than llm output, is this expeected
t
Thanks — that’s a good question. I want to make sure I address it correctly: you’re asking whether it’s expected for a custom
tts_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 elements
e
the task in llm_node explicitly awaits it , no the tts_node is not giving a response its awaiting the stream but the point being the tts_node is being initialised before llm_node reaches the llm streaming code, which is affecting the code flow due to custom conditions in tts_node
t
I don't have the answer you're looking for. You could also try asking your question: • in one of the other Slack channels or • to https://deepwiki.com/livekit/livekit_composite which is trained on all LiveKit source code If you find the answer, please post it here to help others!
e
alright, can you answer this With preemptive_generation=True, can two assistant reply pipelines overlap concurrently, or does every new preemptive run abort the previous LLM/TTS pipeline so only one is live? This affects whether a single app-level Event/Future between our llm_node pre-stream await and tts_node is sufficient.