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:
@@ -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)
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user