steep-balloon-41261
04/23/2026, 8:38 AMtall-airline-16756
04/23/2026, 8:38 AMtall-airline-16756
04/23/2026, 8:38 AMon_user_turn_completed to inspect and modify chat context before the agent generates its next reply. This is a good place to correct or reset context after an interruption.
• Listen for events like user_interruption_detected or agent_false_interruption to detect interruptions in real time and mark state you can act on in the next turn.
• There’s no single documented before_llm_cb, but you can override the llm_node to insert logic that filters or edits the context immediately before LLM inference.
2) What gets written to chat context on interruption
• The agent’s full generated response is added to chat history even if playback was cut short.
• The user’s transcription (their spoken turn) is also appended immediately after.
• That ordering can cause the LLM to assume the intro was already delivered and skip repeating compliance text.
3) Supported ways to deliver the opening statement so interruptions don’t corrupt context
• The most reliable method: disable interruptions for that first message by setting interruption handling off for the turn (e.g., via turn_handling.interruption.enabled = False on say() / generate_reply()). This prevents barge-in during the mandatory script while still allowing normal conversation afterward.
• Complement that with an explicit marker in chat context (e.g., append a short assistant tag like <opening_delivered>) so subsequent logic can confirm the opening was delivered before moving on.
• Alternatively, in on_user_turn_completed check recent history; if the user interrupted early, regenerate/deliver the full opening before proceeding.
• Consider enabling adaptive interruption handling (if available) to reduce false interruptions from brief backchannel utterances.
If you want, share the relevant snippet of your on_enter() + generate_reply() call and I’ll suggest the exact code changes (example showing how to set turn_handling.interruption.enabled = False and/or append an <opening_delivered> marker).
Sources: Pipeline nodes and hooks | LiveKit Documentation | Turns overview | LiveKit Documentation | Solving unwanted interruptions with Adaptive Interruption Handling | LiveKi..., with interactive elementsbetter-sandwich-65834
04/23/2026, 8:42 AMgenerate_reply()) gets interrupted by the customer saying "Hello". After the interruption, the LLM sees only the partial heard portion in chat context and generates a shortened reply that skips part of the required content.
We cannot use session.say() and cannot disable interruptions. We need a code-level solution.
Questions:
1. on_user_turn_completed for context repair: When the agent's speech is interrupted mid-sentence and the customer speaks, does on_user_turn_completed fire before the next generate_reply()? If yes — can we inspect chat_ctx inside on_user_turn_completed to detect that the last assistant message was truncated (e.g., by checking if it was marked as interrupted), and then modify the chat context (via update_chat_ctx) to replace the truncated message with the full intended text — so the LLM sees the complete intro on the next turn?
2. llm_node override for context injection: Can we override llm_node in a subclass to inspect the incoming ChatContext before inference, detect that the opening statement assistant message is missing or truncated, and prepend the full intro text as an assistant message into the context — so the LLM reasons as if the full intro was already delivered?
3. Detecting interruption in context: Is there a reliable way in code to distinguish between (a) the assistant's first reply being interrupted vs. (b) a normal conversation turn? For example, is there a flag on ChatMessage, an interrupted attribute, or a turn counter we can check inside on_user_turn_completed or llm_node?
4. chat_ctx mutation API: What is the correct API to insert or replace a ChatMessage in the chat context inside on_user_turn_completed? Is it chat_ctx.items, and can we safely mutate that list, or is there a dedicated method like update_chat_ctx?tall-airline-16756
04/23/2026, 8:42 AMon_user_turn_completed fires after the user’s turn ends and before the agent generates its next reply, so it’s the appropriate place to inspect and repair context before the next generate_reply().
• You can read chat_ctx inside on_user_turn_completed and call the session context-update API to replace or insert messages so the LLM sees the corrected history on the next turn. The recommended pattern is: detect the truncated/failed delivery, then call the session’s chat-context update method to write the full assistant message (the intended opening) into history before the next LLM call.
2) Overriding llm_node for context injection
• Yes — overriding llm_node in a subclass to inspect and mutate the ChatContext immediately before inference is supported. Injecting or prepending an assistant message containing the full intro there ensures the LLM reasons with the full opening even if playback was interrupted.
• Use this if you want the fix centralized at inference time rather than in turn lifecycle hooks.
3) Detecting an interruption reliably in code
• There are interruption events you can observe (e.g., user_interruption_detected, agent_false_interruption) and the agent lifecycle provides signals when a user turn was captured while the agent was speaking.
• The agent runtime records that the agent’s previous speech was interrupted; you should detect this via the interruption events or by checking the most recent turn metadata rather than relying on brittle heuristics. Treat presence of a recorded interruption event or an interruption flag on the recent turn as the reliable indicator that the assistant’s playback was cut short.
4) Chat context mutation API and safe usage
• Do not mutate chat_ctx.items directly. Use the session-provided API to update chat context (e.g., update_chat_ctx / session method exposed for modifying chat history) so changes are persisted and visible to downstream components.
• The safe flow is:
a. Inspect chat_ctx (read-only view).
b. Build the replacement or injected ChatMessage (assistant role, full opening text, optional metadata like role: assistant, tags: ["opening_delivered"]).
c. Call the provided chat-context update API to insert or replace the message (this ensures indices/turn bookkeeping and eventing remain correct).
Recommended implementation pattern (concise)
1. In `on_user_turn_completed`:
◦ Check for interruption via the interruption event flag / recent-turn metadata.
◦ If interrupted and the opening isn’t confirmed, call the session chat-context update API to insert the full opening assistant message (and optionally a marker tag).
1. Additionally (optional) override llm_node as a safety net:
◦ Before inference, check ChatContext for the opening marker; if missing, prepend the full opening assistant message for this inference only.
1. Add a short marker to the assistant message (e.g., tags: ["opening_delivered"]) so subsequent logic can quickly detect delivery state.
If you’d like, paste your on_enter() + on_user_turn_completed stubs and I’ll give a focused code snippet showing the exact session API calls to update chat context and a sample llm_node override.
, with interactive elementsbetter-sandwich-65834
04/23/2026, 8:46 AMbetter-sandwich-65834
04/23/2026, 8:47 AMon_user_turn_completed is not available in this setup.
Questions:
1. llm_node in realtime STS: Is llm_node override supported when using a realtime STS model, or does it only apply to the classic LLM+TTS pipeline? If not available, what is the equivalent interception point before the model generates a reply in realtime STS?
2. Available hooks in realtime STS: Which Agent lifecycle methods/hooks ARE available in the realtime STS pipeline for inspecting or modifying context before the next model reply? Specifically: is there any hook that fires after the user speaks and before the model responds — equivalent to on_user_turn_completed?
3. Chat context access in realtime STS: In realtime STS mode, is self.chat_ctx populated and writable? Can we modify it between turns to influence the next model response?
4. Interruption detection: In realtime STS, when the model's speech is interrupted by the user, is there an event or flag we can observe in code (on the agent or session) to detect that the previous assistant turn was cut short?tall-airline-16756
04/23/2026, 8:48 AMon_user_turn_completed, cannot disable interruptions globally, and must use realtime STS.
1) Is llm_node override supported in realtime STS?
• No. llm_node only applies to pipeline (STT → LLM → TTS) agents. Realtime STS models run comprehension + generation internally and do not expose a text-inference node you can override.
2) Which lifecycle hooks are available in realtime STS? Is there an equivalent to on_user_turn_completed?
• Available: on_enter() and on_exit() are available in realtime STS.
• Not available by default: on_user_turn_completed is not available unless you move turn detection out of the model and into the agent (i.e., disable model-managed turn detection and enable agent-side turn detection). If you cannot change turn detection, there is no hook that reliably fires after user speech and before the model responds in pure realtime STS.
3) Is self.chat_ctx populated and writable in realtime STS?
• The agent-side chat context can be populated and you can call the session chat-context update APIs (e.g., add_message() / update_chat_ctx()), but changes may not affect the realtime model unless:
◦ You supply an explicit chat_ctx to `generate_reply()`/the model call where supported, or
◦ You control turn boundaries so the model consumes the updated context on the next turn.
• In pure realtime STS where the model manages turns internally, modifying self.chat_ctx is unreliable as a way to change what the realtime model used for its immediate next reply.
4) Can you detect interruptions in realtime STS?
• Yes: the Agents framework emits interruption-related events (e.g., user_interruption_detected, agent_false_interruption) and you can observe agent state transitions (speaking → listening) to detect interruptions. These events are available even in realtime STS.
• However, there is no guaranteed per-message “interrupted” flag on the model’s internal assistant message that you can mutate; interruption detection is event-based.
Practical, reliable code-level workarounds given these constraints
• Best option (recommended): Keep using realtime STS but enforce the opening statement at playback time using a short, uncancellable play (per-turn playback) and then resume realtime mode.
◦ Use session.say(...) or the equivalent playback API for mandatory text with allow_interruptions=False for just that utterance, then resume realtime STS for the rest of the conversation.
◦ Example pattern:
i. In on_enter(), call a non-interruptible playback: await session.say(opening_text, allow_interruptions=False)
ii. After it completes, switch/enabled normal realtime STS interruption handling and call generate_reply() as usual.
◦ This guarantees the user cannot cut the opening and avoids corrupting the model context.
• If you cannot call say() or cannot disable interruptions even for a single utterance:
◦ Use interruption events to detect the barge-in and proactively repair context:
i. Listen for user_interruption_detected.
ii. When fired during your opening, call update_chat_ctx() to insert the full assistant opening message into the agent chat context (tag it, e.g., tags=["opening_delivered"]).
iii. Immediately attempt a controlled follow-up: call generate_reply(chat_ctx=your_updated_ctx, instructions="Do not repeat opening if already delivered; otherwise play opening.")
◦ Caveats: This is best-effort because realtime STS models may not consume the updated context mid-in-flight. It works more reliably when you control turn detection (see below).
• If you can change turn handling (recommended if feasible):
◦ Disable model-managed turn detection and enable agent-side turn detection (VAD or multilingual turn detection). That gives you:
▪︎ on_user_turn_completed hook
▪︎ Deterministic moments where you can inspect/replace self.chat_ctx (via update_chat_ctx) before the model is invoked
◦ With agent-side turn detection you can reliably:
▪︎ Detect interruption
▪︎ Insert the full assistant opening into chat_ctx
▪︎ Then call generate_reply(chat_ctx=...) so the model sees the corrected history.
Concrete API notes (what to call)
• Do NOT mutate internal lists directly. Use session/context helper methods:
◦ Use self.chat_ctx.add_message(role="assistant", content=...) then await self.update_chat_ctx(self.chat_ctx) to persist changes.
◦ Or build a chat_ctx copy, modify, and pass it into generate_reply(chat_ctx=modified_ctx) if that parameter is supported in your realtime route.
• Listen for interruption events:
@session.on("user_interruption_detected")
def on_interruption(ev):
# mark state so your handler can repair context
Summary recommendation (most reliable)
1. If possible, use a single non-interruptible playback for the compliance opening (session.say(..., allow_interruptions=False)) inside on_enter(). Then switch back to realtime STS for the conversation.
2. If you cannot disable interruptions or call say(), move turn detection to the agent so you get on_user_turn_completed, then repair chat_ctx and call generate_reply(chat_ctx=...).
3. As an emergency fallback, listen for user_interruption_detected, call update_chat_ctx() to insert the full opening, and then call generate_reply(...) — but expect this to be less reliable than the two approaches above.
If you want, paste the exact SDK calls you currently use for generate_reply() and your session setup; I’ll produce a focused code snippet that implements the most feasible workaround for your environment.
Sources: Pipeline nodes and hooks | LiveKit Documentation | Realtime models overview | LiveKit Documentation | Turns overview | LiveKit Documentation | Agent speech and audio | LiveKit Documentation | Agents and handoffs | LiveKit Documentation, with interactive elements