Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7881,6 +7881,7 @@ def run_conversation(
self._thinking_prefill_retries = 0
self._post_tool_empty_retried = False
self._last_content_with_tools = None
self._last_content_tools_all_housekeeping = False
self._mute_post_response = False
self._unicode_sanitization_passes = 0

Expand Down Expand Up @@ -8068,6 +8069,7 @@ def run_conversation(
self._empty_content_retries = 0
self._thinking_prefill_retries = 0
self._last_content_with_tools = None
self._last_content_tools_all_housekeeping = False
self._mute_post_response = False
# Re-estimate after compression
_preflight_tokens = estimate_request_tokens_rough(
Expand Down Expand Up @@ -10130,6 +10132,7 @@ def _stop_spinner():
tc.function.name in _HOUSEKEEPING_TOOLS
for tc in assistant_message.tool_calls
)
self._last_content_tools_all_housekeeping = _all_housekeeping
if _all_housekeeping and self._has_stream_consumers():
self._mute_post_response = True
elif self.quiet_mode:
Expand Down Expand Up @@ -10312,15 +10315,22 @@ def _stop_spinner():
break

# If the previous turn already delivered real content alongside
# tool calls (e.g. "You're welcome!" + memory save), the model
# has nothing more to say. Use the earlier content immediately
# instead of wasting API calls on retries that won't help.
# HOUSEKEEPING tool calls (e.g. "You're welcome!" + memory save),
# the model has nothing more to say. Use the earlier content
# immediately instead of wasting API calls on retries.
# NOTE: Only use this shortcut when ALL tools in that turn were
# housekeeping (memory, todo, etc.). When substantive tools
# were called (terminal, search_files, etc.), the content was
# likely mid-task narration ("I'll scan the directory...") and
# the empty follow-up means the model choked — let the
# post-tool nudge below handle that instead of exiting early.
fallback = getattr(self, '_last_content_with_tools', None)
if fallback:
if fallback and getattr(self, '_last_content_tools_all_housekeeping', False):
_turn_exit_reason = "fallback_prior_turn_content"
logger.info("Empty follow-up after tool calls — using prior turn content as final response")
self._emit_status("↻ Empty response after tool calls — using earlier content as final answer")
self._last_content_with_tools = None
self._last_content_tools_all_housekeeping = False
self._empty_content_retries = 0
# Do NOT modify the assistant message content — the
# old code injected "Calling the X tools..." which
Expand All @@ -10331,13 +10341,18 @@ def _stop_spinner():
break

# ── Post-tool-call empty response nudge ───────────
# The model returned empty after executing tool calls
# but there's no prior-turn content to fall back on.
# The model returned empty after executing tool calls.
# This covers two cases:
# (a) No prior-turn content at all — model went silent
# (b) Prior turn had content + SUBSTANTIVE tools (the
# fallback above was skipped because the content
# was mid-task narration, not a final answer)
# Instead of giving up, nudge the model to continue by
# appending a user-level hint. This is the #9400 case:
# weaker models (GLM-5, etc.) sometimes return empty
# after tool results instead of continuing to the next
# step. One retry with a nudge usually fixes it.
# weaker models (mimo-v2-pro, GLM-5, etc.) sometimes
# return empty after tool results instead of continuing
# to the next step. One retry with a nudge usually
# fixes it.
_prior_was_tool = any(
m.get("role") == "tool"
for m in messages[-5:] # check recent messages
Expand All @@ -10347,6 +10362,10 @@ def _stop_spinner():
and not getattr(self, "_post_tool_empty_retried", False)
):
self._post_tool_empty_retried = True
# Clear stale narration so it doesn't resurface
# on a later empty response after the nudge.
self._last_content_with_tools = None
self._last_content_tools_all_housekeeping = False
logger.info(
"Empty response after tool calls — nudging model "
"to continue processing"
Expand Down
Loading