Merge pull request #52084 from NousResearch/salvage/31884-silent-drop-after-stop

fix(gateway): surface retry hint instead of silently dropping turn after /stop (#31884)
This commit is contained in:
kshitij 2026-06-25 00:06:32 +05:30 committed by GitHub
commit de281bcebc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 0 deletions

View file

@ -2332,6 +2332,12 @@ def _normalize_empty_agent_response(
Consolidates the existing ``failed`` handler and adds a catch-all for
the case where the agent did work (api_calls > 0) but returned no text.
Fix for #18765.
Also surfaces a retry hint when the agent never ran at all
(api_calls == 0) for a non-interrupted, non-failed turn -- this is the
silent-drop pattern observed after ``/stop`` where the next user
message hits a stale generation token and returns an empty result,
leaving the platform with nothing to send. (#31884)
"""
if response:
return response
@ -2364,6 +2370,22 @@ def _normalize_empty_agent_response(
"This may be a transient error — try sending your message again."
)
# api_calls == 0, not failed, not interrupted: the agent never ran for
# this turn. This is the post-/stop generation-race pattern where the
# gateway would otherwise silently drop the turn (response=0 chars) and
# the user sees no reply at all. Surface a short retry hint so the
# message isn't lost in silence. (#31884)
if (
api_calls == 0
and not agent_result.get("interrupted")
and not agent_result.get("failed")
and not agent_result.get("partial")
):
return (
"⚠️ Your message wasn't processed (the previous turn was still "
"being cleaned up). Please send it again."
)
return response

View file

@ -415,6 +415,31 @@ class TestGatewaySurfacesNullResponse:
assert result == "Hello!"
def test_silent_drop_after_stop_surfaces_hint(self):
"""Regression for #31884: after /stop, the next user message hits a
stale generation token in _run_agent and returns with api_calls=0,
no failure, no interruption. Without normalization the gateway
silently drops the turn (response=0 chars). Surface a retry hint
so the user knows the message was lost."""
from gateway.run import _normalize_empty_agent_response
agent_result = {
"final_response": "",
"api_calls": 0,
"failed": False,
"interrupted": False,
"partial": False,
}
response = agent_result.get("final_response") or ""
result = _normalize_empty_agent_response(
agent_result, response, history_len=10,
)
assert result, "Silent-drop turn must surface a user-facing hint"
lowered = result.lower()
assert "send it again" in lowered or "try again" in lowered
# ===========================================================================
# Prune: finalize_orphaned_compression_sessions