This message was deleted.
# ask-ai
s
This message was deleted.
t
Thanks for reaching out, Aleh. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m responding to your three questions about interruptions during the opening statement in LiveKit Agents (Python SDK) and how to avoid corrupted chat context. 1) Callbacks / hooks you can use • Use the lifecycle/turn hooks such as
on_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 elements
b
Context: We have a bug where the bot's opening statement (delivered via
generate_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
?
t
Thanks — I’m responding to each of your numbered questions about code-level handling of interruptions and chat-context repair. 1) on_user_turn_completed timing and context repair • Yes —
on_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 elements
b
gpt-realtime model we are using don't have acces to on_user_turn_completed event
Context: We use a realtime STS model (not a regular LLM+TTS pipeline). We confirmed
on_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?
t
Thanks — I’ll address each realtime STS question directly and give concrete, code-level guidance and recommended workarounds given you cannot use
on_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:
Copy code
@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