fix(agent): classify think-only empty responses before retrying - #4552
Closed
kshitijk4poor wants to merge 1 commit into
Closed
fix(agent): classify think-only empty responses before retrying#4552kshitijk4poor wants to merge 1 commit into
kshitijk4poor wants to merge 1 commit into
Conversation
Contributor
|
Merged via PR #4645. Your commit was cherry-picked onto current main with your authorship preserved in git log. Thanks for the contribution @kshitijk4poor! |
2 tasks
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
Problem
Hermes still hits:
❌ Max retries (3) for empty content exceeded.Error: Model generated only think blocks with no actual response after 3 retriesfor a class of responses where the backend returns reasoning-only output with no visible content.
Prior fixes already covered adjacent cases:
<think>fallback salvage (fix(agent): use reasoning content when model wraps entire response in think tags #2124)The remaining gap is when a local/custom backend surfaces implicit overflow or malformed follow-up behavior as a reasoning-only final response instead of an explicit context error. In those cases Hermes was still treating the response as a generic transient empty-content glitch and burning retries.
Root Cause
The empty-content retry path in
run_agent.pyhad only two behaviors:It did not use surrounding runtime signals to distinguish:
Fix
1. Empty-content classifier
Add
_classify_empty_content_response()to inspect:2. Compress before retry for likely implicit overflow
When a local/custom endpoint returns reasoning-only output with no visible content and the session shape suggests context pressure, Hermes now tries
_compress_context()before burning retries.3. Early salvage for repeated structured reasoning-only payloads
If the same structured reasoning-only response repeats unchanged, Hermes now uses the reasoning text directly instead of spending the full retry budget on an obviously stable payload.
This keeps the normal retry path for one-off thinking-model responses, which matches the earlier review feedback on #4467.
4. Better final diagnostic for local/custom providers
When retries still exhaust on local/custom endpoints, the final error now points users toward the likely causes:
/v1endpointExplicit non-goals
This PR does not:
max_tokens/HERMES_MAX_TOKENSmitigation path (feat: support HERMES_MAX_TOKENS env var / config.yaml max_tokens for custom providers #782)think=false([Feature]: Pass Ollama think: false parameter when reasoning_effort: none is set for custom/Ollama providers #3191 / feat(ollama): pass think=false to custom providers when reasoning_effort is none #3197 are adjacent follow-up work)Tests
Added coverage in
tests/test_run_agent.pyfor:Test Results
Passed:
python -m pytest tests/test_run_agent.py -q -k "empty_content_retry_uses_inline_reasoning_as_response or empty_content_local_resumed_session_triggers_compression or empty_content_repeated_structured_reasoning_salvages_early or empty_content_local_custom_error_is_actionable or length_thinking_exhausted_skips_continuation or length_empty_content_detected_as_thinking_exhausted"python -m pytest tests/test_run_agent_codex_responses.py -qpython -m py_compile run_agent.py tests/test_run_agent.pyNotes:
python -m pytest tests/test_run_agent.py tests/test_run_agent_codex_responses.py -qcurrently hits pre-existing failures on currentmainin unrelatedtest_run_agent.pyareas (TestBuildApiKwargs,TestInit.test_prompt_caching_claude_openrouter, andTestStreamingApiCall). I did not modify those code paths in this PR.