Skip to content

fix(agent): classify think-only empty responses before retrying - #4552

Closed
kshitijk4poor wants to merge 1 commit into
NousResearch:mainfrom
kshitijk4poor:fix/think-block-retries
Closed

fix(agent): classify think-only empty responses before retrying#4552
kshitijk4poor wants to merge 1 commit into
NousResearch:mainfrom
kshitijk4poor:fix/think-block-retries

Conversation

@kshitijk4poor

@kshitijk4poor kshitijk4poor commented Apr 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • classify think-only / empty-content responses before spending all 3 retries in the generic empty-content loop
  • treat reasoning-only output from local/custom providers as a possible implicit context-pressure signal and attempt compression before retrying
  • salvage repeated structured reasoning-only payloads earlier, while preserving the normal retry path for one-off thinking-model responses
  • return a more actionable local/custom diagnostic when retries are exhausted with no visible content

Problem

Hermes still hits:

  • ❌ Max retries (3) for empty content exceeded.
  • Error: Model generated only think blocks with no actual response after 3 retries

for a class of responses where the backend returns reasoning-only output with no visible content.

Prior fixes already covered adjacent cases:

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.py had only two behaviors:

  1. retry up to 3 times
  2. salvage reasoning at the very end

It did not use surrounding runtime signals to distinguish:

  • a retryable one-off thinking-model response
  • a deterministic repeated structured-reasoning payload
  • a local/custom resumed session that should compress before retrying

Fix

1. Empty-content classifier

Add _classify_empty_content_response() to inspect:

  • extracted reasoning
  • structured reasoning fields
  • whether the same empty-content signature repeated
  • local/custom endpoint detection
  • resumed-session / large-session / prior context-probe pressure signals

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:

  • wrong /v1 endpoint
  • runtime context window smaller than Hermes expects
  • resumed / large session exceeding the backend's actual context limit

Explicit non-goals

This PR does not:

Tests

Added coverage in tests/test_run_agent.py for:

  • inline think-block salvage regression
  • local resumed reasoning-only response triggering compression before retries are exhausted
  • repeated identical structured reasoning-only payloads salvaging early
  • local/custom retry exhaustion returning actionable diagnostics
  • existing thinking-budget exhaustion paths still behaving correctly

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 -q
  • python -m py_compile run_agent.py tests/test_run_agent.py

Notes:

  • python -m pytest tests/test_run_agent.py tests/test_run_agent_codex_responses.py -q currently hits pre-existing failures on current main in unrelated test_run_agent.py areas (TestBuildApiKwargs, TestInit.test_prompt_caching_claude_openrouter, and TestStreamingApiCall). I did not modify those code paths in this PR.

@teknium1

teknium1 commented Apr 2, 2026

Copy link
Copy Markdown
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!

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.

2 participants