fix(agent): explain abnormal turn endings instead of blank/partial reply (#34452) - #34848
Merged
Conversation
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
unresolved-attribute |
7 |
First entries
tests/run_agent/test_turn_completion_explainer.py:57: [unresolved-attribute] unresolved-attribute: Unresolved attribute `save_trajectories` on type `AIAgent`
tests/run_agent/test_turn_completion_explainer.py:56: [unresolved-attribute] unresolved-attribute: Unresolved attribute `compression_enabled` on type `AIAgent`
tests/run_agent/test_turn_completion_explainer.py:53: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_cached_system_prompt` on type `AIAgent`
tests/run_agent/test_turn_completion_explainer.py:59: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_fallback_chain` on type `AIAgent`
tests/run_agent/test_turn_completion_explainer.py:54: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_use_prompt_caching` on type `AIAgent`
tests/run_agent/test_turn_completion_explainer.py:168: [unresolved-attribute] unresolved-attribute: Attribute `chat` is not defined on `None` in union `None | Any`
tests/run_agent/test_turn_completion_explainer.py:55: [unresolved-attribute] unresolved-attribute: Unresolved attribute `tool_delay` on type `AIAgent`
✅ Fixed issues: none
Unchanged: 4912 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
When a turn ends abnormally after substantive tool calls (empty content after retries, a partial/truncated stream, exhausted retries, or an iteration/budget limit), the CLI/TUI response area was left blank or showed only a fragment (e.g. "The") with no consolidated reason. The internal turn_exit_reason values (empty_response_exhausted, partial_stream_recovery, etc.) were never surfaced to the user. Add a turn-completion explainer that mirrors the existing file-mutation verifier footer: at turn end, map an abnormal turn_exit_reason to a short, actionable message and either replace the bare "(empty)" sentinel or append the reason after a partial fragment. Normal text_response exits (e.g. a terse "Done.") stay quiet. Gated by display.turn_completion_explainer (default on) with HERMES_TURN_COMPLETION_EXPLAINER env override, matching the file-mutation verifier seam. Closes #34452
PR #34470 adds an explainer suffix to abnormal turn endings (e.g. max_iterations_reached) so users see why the response is short instead of receiving a bare/blank reply. test_tool_call_validation_accepts_dict_arguments runs the agent at max_iterations=3 which hits the explainer path; the existing strict-equality assertion (== "done") no longer matches once the suffix is appended. Switch the assertion to .startswith("done") so the test continues to verify that the models actual text survives intact while leaving the explainer suffix wording owned by conversation_loop (where it belongs). Test now passes (1 passed in 0.88s).
Follow-up to the salvaged #34452 turn-completion explainer: - Register display.turn_completion_explainer: True in DEFAULT_CONFIG so the setting is discoverable, matching the file_mutation_verifier precedent. - Shorten the repeated footer prefix from 'Turn ended without a usable reply: ' to 'No reply: ' so the 10 reason variants don't all open with the same 8-word boilerplate. - Update the 7 assertions that referenced the old prefix.
teknium1
force-pushed
the
hermes/hermes-2454e468
branch
from
May 30, 2026 02:08
3394deb to
c3ce7f6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a turn ends abnormally with no usable reply — empty content after retries, a partial/truncated stream, a still-pending tool result, or an iteration/budget limit — the agent now surfaces a single user-visible line explaining why the reply stopped, instead of a blank box or a bare
(empty)sentinel. Normal short replies (Done.) stay completely quiet.Closes #34452. Salvage of #34470 (@Bartok9), rebased onto current
mainwith two follow-up fixes on top.Changes
run_agent.py:_format_turn_completion_explanation()maps eachturn_exit_reasonto a short, actionable message;_turn_completion_explainer_enabled()is the config/env seam (mirrors_file_mutation_verifier_enabled).agent/conversation_loop.py: at turn end, after the file-mutation verifier footer, replaces the(empty)/blank sentinel with the explanation, or appends it to a short truncated fragment (≤24 chars, no terminating punctuation).text_response(...)exits and long replies are never touched.hermes_cli/config.py: registerdisplay.turn_completion_explainer: TrueinDEFAULT_CONFIG(follow-up — the original PR relied on the absent-key default; this makes it discoverable, matchingfile_mutation_verifier).Turn ended without a usable reply:toNo reply:(follow-up) so the 10 reason variants don't all open with the same 8-word boilerplate.Gated by
display.turn_completion_explainer(default on) withHERMES_TURN_COMPLETION_EXPLAINERoverride. No changes to streaming, recovery, retry/fallback machinery, or theturn_exit_reasonvalues themselves — presentation only, so prompt caching and message-flow invariants are untouched.Validation
(empty)⚠️ No reply: the model returned empty content after retries…The)Thethen silenceThe+⚠️ No reply: streaming stopped early…Done.Done.Done.(unchanged)tests/run_agent/test_turn_completion_explainer.py— 11 passed (formatter map, env/config seam, end-to-end empty-exhausted surfaces the explanation,Done.stays quiet).tests/run_agent/test_dict_tool_call_args.py— 1 passed.tests/run_agent/test_run_agent.pyempty/reasoning/nudge/fallback subset — 35 passed (5 assertions updated from the bare(empty)sentinel to the new explanation).tests/hermes_cli/test_tools_config.py+test_tools_disable_enable.py— 100 passed under the hermetic isolation runner (config-key addition is deep-merge safe, no version bump needed).Contributor authorship preserved: both Bartok9 commits land via rebase merge; the config/wording follow-up is a separate commit.
Infographic