fix: prevent already_sent from swallowing empty responses after tool calls (#10531)

When a model (e.g. mimo-v2-pro) streams intermediate text alongside tool
calls ("Let me search for that") but then returns empty after processing
tool results, the stream consumer already_sent flag is True from the
earlier text delivery.  The gateway suppression check
(already_sent=True, failed=False → return None) would swallow the final
response, leaving the user staring at silence after the search.

Two changes:

1. gateway/run.py return path: skip already_sent suppression when the
   final_response is "(empty)" or empty — the user needs to know the
   agent finished even if streaming sent partial content earlier.

2. gateway/run.py response handler: convert the internal "(empty)"
   sentinel to a user-friendly warning instead of delivering the raw
   sentinel string.

Tests added for all empty/None/sentinel cases plus preserved existing
suppression behavior for normal non-empty responses.
This commit is contained in:
Teknium
2026-04-15 14:26:45 -07:00
committed by GitHub
parent a9197f9bb1
commit e36c804bc2
2 changed files with 87 additions and 2 deletions

View File

@@ -3874,6 +3874,18 @@ class GatewayRunner:
pass
response = agent_result.get("final_response") or ""
# Convert the agent's internal "(empty)" sentinel into a
# user-friendly message. "(empty)" means the model failed to
# produce visible content after exhausting all retries (nudge,
# prefill, empty-retry, fallback). Sending the raw sentinel
# looks like a bug; a short explanation is more helpful.
if response == "(empty)":
response = (
"⚠️ The model returned no response after processing tool "
"results. This can happen with some models — try again or "
"rephrase your question."
)
agent_messages = agent_result.get("messages", [])
_response_time = time.time() - _msg_start_time
_api_calls = agent_result.get("api_calls", 0)
@@ -9400,9 +9412,19 @@ class GatewayRunner:
# BUT: never suppress delivery when the agent failed — the error
# message is new content the user hasn't seen, and it must reach
# them even if streaming had sent earlier partial output.
#
# Also never suppress when the final response is "(empty)" — this
# means the model failed to produce content after tool calls (common
# with mimo-v2-pro, GLM-5, etc.). The stream consumer may have
# sent intermediate text ("Let me search for that…") alongside the
# tool call, setting already_sent=True, but that text is NOT the
# final answer. Suppressing delivery here leaves the user staring
# at silence. (#10xxx — "agent stops after web search")
_sc = stream_consumer_holder[0]
if _sc and isinstance(response, dict) and not response.get("failed"):
if (
_final = response.get("final_response") or ""
_is_empty_sentinel = not _final or _final == "(empty)"
if not _is_empty_sentinel and (
getattr(_sc, "final_response_sent", False)
or getattr(_sc, "already_sent", False)
):