fix(gateway): suppress tool-progress bubbles after interrupt (#16034)
When the LLM response carries N parallel tool calls, the agent fires N tool.started events back-to-back before its interrupt check runs. A user sending /stop mid-batch would see the '⚡ Interrupting current task' ack followed by a trail of 🔍 web_search bubbles for the remaining events in the batch — making the interrupt feel ignored. progress_callback and the drain loop in send_progress_messages now check agent.is_interrupted (via agent_holder[0], the existing cross-scope handle). Events that arrive after interrupt are dropped at both the queueing and rendering stages. The '⚡ Interrupting' message is sent through a separate adapter path and is unaffected.
This commit is contained in:
@@ -9370,6 +9370,22 @@ class GatewayRunner:
|
||||
if event_type not in ("tool.started",):
|
||||
return
|
||||
|
||||
# Suppress tool-progress bubbles once the user has sent `stop`.
|
||||
# When the LLM response carries N parallel tool calls, the agent
|
||||
# fires N "tool.started" events back-to-back before checking for
|
||||
# interrupts — without this guard, a late `stop` still renders
|
||||
# all N as 🔍 bubbles, making the interrupt feel ignored.
|
||||
# (agent lives in run_sync's scope; agent_holder[0] is the shared
|
||||
# handle across nested scopes — see line ~9607.)
|
||||
try:
|
||||
_agent_for_interrupt = agent_holder[0] if agent_holder else None
|
||||
if _agent_for_interrupt is not None and getattr(
|
||||
_agent_for_interrupt, "is_interrupted", False
|
||||
):
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# "new" mode: only report when tool changes
|
||||
if progress_mode == "new" and tool_name == last_tool[0]:
|
||||
return
|
||||
@@ -9476,6 +9492,22 @@ class GatewayRunner:
|
||||
|
||||
raw = progress_queue.get_nowait()
|
||||
|
||||
# Drain silently when interrupted: events queued in the
|
||||
# window between tool parse and interrupt processing
|
||||
# should not render as bubbles. The "⚡ Interrupting
|
||||
# current task" message is sent separately and is the
|
||||
# last progress-flavored bubble the user should see.
|
||||
try:
|
||||
_agent_for_interrupt = agent_holder[0] if agent_holder else None
|
||||
if _agent_for_interrupt is not None and getattr(
|
||||
_agent_for_interrupt, "is_interrupted", False
|
||||
):
|
||||
# Drop this event and continue draining.
|
||||
await asyncio.sleep(0)
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Handle dedup messages: update last line with repeat counter
|
||||
if isinstance(raw, tuple) and len(raw) == 3 and raw[0] == "__dedup__":
|
||||
_, base_msg, count = raw
|
||||
|
||||
Reference in New Issue
Block a user