Skip to content

feat(agent): configurable content_response_policy for content-without-tool-call responses (#3992)#3997

Open
Code-weaver1 wants to merge 1 commit into
OpenHands:mainfrom
Code-weaver1:feat/content-response-policy
Open

feat(agent): configurable content_response_policy for content-without-tool-call responses (#3992)#3997
Code-weaver1 wants to merge 1 commit into
OpenHands:mainfrom
Code-weaver1:feat/content-response-policy

Conversation

@Code-weaver1

Copy link
Copy Markdown

HUMAN:

  • A human has tested these changes.

AGENT:

Investigated, implemented, and unit-tested by an AI agent (Claude Code) operating under
Solomon Okello's direction; every claim below is backed by the included tests.


Why

#3992: ResponseDispatchMixin dispatches asymmetrically — an EMPTY response gets a
corrective nudge and the loop continues, but a CONTENT response (prose, no tool call)
sets execution_status = FINISHED unconditionally. Weaker/local models narrate a step
in prose before emitting its tool call, so they get silently terminated mid-task with
exit 0. Three earlier attempts to change the default (#1303, #1304, #1385) didn't merge —
plain content is a legitimate completion signal for strong models. So this PR follows
the issue's second suggested direction: make the policy configurable, default
unchanged
.

Summary

  • New AgentBase field content_response_policy: Literal["finish", "nudge"], default
    "finish" — zero behavior change unless opted in.
  • Under "nudge": the prose message is emitted (preserved in history), a corrective
    nudge is sent (pointing at the finish tool as the explicit completion path), and the
    loop continues. Runaway prose loops are bounded by conversation-level stuck detection —
    the same mechanism that already bounds the EMPTY-response nudge (per the existing
    docstring on _send_corrective_nudge).
  • _send_corrective_nudge gains an optional text kwarg so the content-case feedback
    can differ from the empty-case feedback (the model did produce a message; telling it
    otherwise would be false).

Issue Number

Fixes #3992

How to Test

uv run pytest tests/sdk/agent/test_response_dispatch.py -q

Three new tests:

  • test_content_response_nudge_policy_continues_loop — prose turn under "nudge": not
    FINISHED, agent message preserved, nudge follows, nudge text points at finish
  • test_content_response_default_policy_is_finish — guards the no-behavior-change
    contract
  • test_finish_tool_still_finishes_under_nudge_policy — explicit completion still works

The pre-existing test_content_response_sets_finished continues to pass unchanged,
which is the other half of the compatibility guarantee.

…-tool-call responses

CONTENT responses (prose, no tool call) currently set FINISHED unconditionally,
silently terminating weaker/local models that narrate a step before its tool
call (OpenHands#3992). EMPTY responses, by contrast, get a corrective nudge.

Add content_response_policy to AgentBase: 'finish' (default, unchanged) or
'nudge' (emit the message, send corrective feedback pointing at the finish
tool, continue the loop). Runaway prose loops are bounded by conversation-level
stuck detection, the same mechanism bounding the existing empty-response nudge.

Fixes OpenHands#3992
@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: I have assigned @neubig as a reviewer based on the repository MAINTAINERS file. Thanks in advance for the help!

This comment was posted by an AI agent (OpenHands) on behalf of the user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Asymmetric handling of content-without-tool-call responses terminates agents driven by weaker/local models

2 participants