fix(agent): route finish_reason='length' with blank content to length recovery
When an LLM response arrives with finish_reason='length' and has_tool_calls but blank text content (e.g. the model spent its whole output budget on a tool call whose closing tag was truncated), the runner dropped the tool calls and then misrouted the blank response into the empty-response retry branch. Retrying the same prompt cannot recover from output-budget exhaustion, so every retry hit the same length ceiling and the turn ended in the generic apology. The length-recovery branch was gated on 'finish_reason == length and not is_blank_text(clean)', so a blank-but-truncated turn could never reach it. - The empty-response retry branch now excludes finish_reason == 'length' (in addition to 'error'). - The length-recovery branch no longer requires non-blank content, so a blank-but-truncated turn enters recovery and appends build_length_recovery_message (which handles a blank tail safely). Adds a regression test asserting the length-recovery path is taken; it fails on the unfixed code and passes with the fix. Fixes #5133
This commit is contained in:
@@ -575,7 +575,7 @@ class AgentRunner:
|
||||
)
|
||||
|
||||
clean = hook.finalize_content(context, response.content)
|
||||
if response.finish_reason != "error" and is_blank_text(clean):
|
||||
if response.finish_reason not in ("error", "length") and is_blank_text(clean):
|
||||
empty_content_retries += 1
|
||||
if empty_content_retries < _MAX_EMPTY_RETRIES:
|
||||
logger.warning(
|
||||
@@ -608,7 +608,7 @@ class AgentRunner:
|
||||
original_content = response.content
|
||||
clean = hook.finalize_content(context, response.content)
|
||||
|
||||
if response.finish_reason == "length" and not is_blank_text(clean):
|
||||
if response.finish_reason == "length":
|
||||
if len(length_recovery_parts) < _MAX_LENGTH_RECOVERIES:
|
||||
length_recovery_parts.append(
|
||||
_restore_outer_whitespace(clean or "", original_content)
|
||||
|
||||
Reference in New Issue
Block a user