Skip to content

fix(agent): detect thinking-timeout for reasoning models and surface actionable guidance instead of misleading file-write advice - #52272

Closed
DavidMetcalfe wants to merge 1 commit into
NousResearch:mainfrom
DavidMetcalfe:fix/a2-thinking-timeout-guidance
Closed

fix(agent): detect thinking-timeout for reasoning models and surface actionable guidance instead of misleading file-write advice#52272
DavidMetcalfe wants to merge 1 commit into
NousResearch:mainfrom
DavidMetcalfe:fix/a2-thinking-timeout-guidance

Conversation

@DavidMetcalfe

Copy link
Copy Markdown
Contributor

Summary

Two-part fix for issue #52271 (reasoning-model thinking-timeout UX).

Part 1 — Classifier override at agent/error_classifier.py:720-738: A transport disconnect on a reasoning model — even on a large session — now routes to FailoverReason.timeout instead of context_overflow. Without this, large-session reasoning-model disconnects route to the context-compression branch and silently delete conversation history on a phantom context-length error.

Part 2 — Detection + guidance via new agent/thinking_timeout_guidance.py + integration at agent/conversation_loop.py:3488-3567: When a known reasoning model (NVIDIA Nemotron 3 Ultra, OpenAI o1/o3, Anthropic Opus 4.x thinking, DeepSeek R1, Qwen QwQ, xAI Grok reasoning) hits a transport-kill, the user sees reasoning-specific guidance with three actionable workarounds in priority order:

  1. Set providers.<provider>.models.<model>.stale_timeout_seconds: 900 in ~/.hermes/config.yaml (Hermes's built-in floor is already 600s for known reasoning models after PR feat(agent): per-reasoning-model stale-timeout floor (Nemotron 3 Ultra, OpenAI o1/o3, Opus 4.x thinking, DeepSeek R1, Qwen QwQ, xAI Grok reasoning) #52238 lands; raise further if upstream is even tighter).
  2. Lower reasoning_budget or set reasoning_effort: medium on this model if the provider supports it.
  3. Use a smaller / faster reasoning model if the task doesn't require deep thinking.

The new guidance takes precedence (if/elif) over the existing _is_stream_drop block. A reasoning-model user with a transport-kill message now sees actionable advice instead of the misleading "try execute_code with Python's open() for large files" advice (which is correct for the unrelated large-file-write stream-drop case but actively wrong for the thinking-timeout case).

Test Plan

  • python3 -m pytest tests/agent/test_thinking_timeout_guidance.py tests/agent/test_error_classifier.py tests/agent/test_non_stream_stale_timeout.py tests/agent/test_stream_read_timeout_floor.py tests/agent/test_local_stream_timeout.py tests/hermes_cli/test_timeouts.py tests/run_agent/test_primary_runtime_restore.py tests/run_agent/test_streaming.py tests/agent/test_model_metadata.py -q478 passed, zero failures, zero regressions.
  • ruff check agent/error_classifier.py agent/conversation_loop.py agent/thinking_timeout_guidance.py agent/reasoning_timeouts.py tests/agent/test_thinking_timeout_guidance.py — clean.
  • Cross-vendor dual review via agy -p:
    • Gemini 3.5 Flash (Medium) — passed: true, zero blockers, zero should-fix, zero nits (after Round 1 BLOCKER fix).
    • GPT-OSS 120B (Medium) — passed: true, zero blockers, zero should-fix, one nit (test placement — adopted at tests/agent/test_thinking_timeout_guidance.py).
  • Regression guards: 6 parametrized tests confirm non-reasoning models (gpt-4o, claude-3-5-sonnet-20240620, llama-3.3-70b-instruct, qwen2-72b-instruct, x-ai/grok-3, olmo-1) still route to context_overflow on large sessions. 4 parametrized gates confirm non-timeout classifier reasons (billing, rate_limit, auth, context_overflow, format_error, provider_policy_blocked, content_policy_blocked, thinking_signature, unknown) never trigger the guidance. 5 parametrized cases confirm non-transport messages (insufficient credits, rate limit exceeded, invalid API key, context length exceeded, tool call argument malformed) never trigger it.
  • Guidance message regression guard: New message does NOT contain "execute_code" or "open()" — the misleading advice is fully replaced, not appended alongside.
  • Why this exists (Layer 1 critical path): The classifier's disconnect+large-session rule at error_classifier.py:718-732 is the load-bearing bug. Gemini Flash's Round 1 review caught it: WITHOUT the classifier override, reasoning-model users on long sessions get routed to the context-compression branch at conversation_loop.py:3037-3149, which calls _compress_context() 3 times, then returns "Context length exceeded: max compression attempts (3) reached" — silently deleting conversation history on a phantom overflow. The Part 2 guidance never fires because the error never reaches the terminal site.

Notes

What this PR does NOT change

Fixes #52271.

…actionable guidance instead of misleading file-write advice

Two-part fix:

Part 1 (classifier override at agent/error_classifier.py:720-738):
A transport disconnect on a reasoning model — even on a large session —
now routes to FailoverReason.timeout instead of context_overflow. Without
this, large-session reasoning-model disconnects route to the compression
branch and silently delete conversation history on a phantom
context-length error. The override is strictly targeted: non-reasoning
models (gpt-4o, claude-3-5-sonnet, llama-3.3-70b, etc.) still route to
context_overflow on large sessions — the existing intentional behavior
for chat models whose proxy doesn't idle-kill during prefill/generation.

Part 2 (new agent/thinking_timeout_guidance.py + integration at
agent/conversation_loop.py:3488-3567):
New is_thinking_timeout() and build_thinking_timeout_guidance() helpers.
When a known reasoning model (NVIDIA Nemotron 3 Ultra, OpenAI o1/o3,
Anthropic Opus 4.x thinking, DeepSeek R1, Qwen QwQ, xAI Grok reasoning)
hits a transport-kill on a small session (classifier says timeout
directly) or after Part 1 routes correctly (large session), the user
now sees reasoning-specific guidance with three actionable workarounds
in priority order:

  1. Set providers.<provider>.models.<model>.stale_timeout_seconds: 900
     in ~/.hermes/config.yaml (Hermes's built-in floor is already 600s
     for known reasoning models; raise further if upstream is even
     tighter).
  2. Lower reasoning_budget or set reasoning_effort: medium on this
     model if the provider supports it.
  3. Use a smaller / faster reasoning model if the task doesn't
     require deep thinking.

The new guidance takes precedence via if/elif over the existing
_is_stream_drop block, so a reasoning-model user with a transport-kill
message sees actionable advice instead of the misleading "try
execute_code with Python's open() for large files" advice (which is
correct for the unrelated large-file-write stream-drop case but
actively wrong for the thinking-timeout case).

Verified:
- 478 tests passing across 9 directly-relevant files (49 new + 429
  existing, zero regressions).
- Ruff lint clean on all 4 modified/new files.
- Negative test: 6 parametrized regression guards confirm non-reasoning
  models still route to context_overflow on large sessions; 4
  parametrized gates confirm non-timeout classifier reasons never
  trigger the guidance; 5 parametrized cases confirm non-transport
  messages never trigger it.
- Regression guard: new guidance message does NOT contain
  "execute_code" or "open()" — the misleading advice is fully
  replaced, not appended alongside.
- Cross-vendor dual review via agy -p:
  - Gemini 3.5 Flash (Medium) — passed: true, zero blockers, one
    SHOULD-FIX (vprint block duplication — fixed by extracting
    detection into a helper module).
  - GPT-OSS 120B (Medium) — passed: true, zero blockers, two nits
    (test placement — adopted at tests/agent/test_thinking_timeout_guidance.py;
    primary-model capture — accepted as non-issue per Flash's nit).

Dependency note for maintainers:
This PR includes agent/reasoning_timeouts.py (the reasoning-model
allowlist module from PR NousResearch#52238) because the Layer 1 override is
load-bearing on get_reasoning_stale_timeout_floor(). After PR NousResearch#52238
lands on main, this PR's duplicate agent/reasoning_timeouts.py should
be rebased away. Either PR can land first; the other rebase is
mechanical.

Fixes NousResearch#52271.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jun 25, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Approved

Comprehensive two-part fix for reasoning model thinking-timeout UX. Well-structured with classifier override, detection, and guidance. Good test coverage. The guidance provides actionable workarounds in priority order.

Changes:

  • agent/conversation_loop.py: Thinking-timeout detection and guidance display
  • agent/error_classifier.py: Reasoning model disconnect classification override
  • agent/thinking_timeout_guidance.py: New module with detection and guidance functions
  • tests/agent/test_thinking_timeout_guidance.py: Test coverage

LGTM.

Reviewed by Hermes Agent

@teknium1

Copy link
Copy Markdown
Contributor

Salvaged and merged via #52795 (rebase-merge, your authorship preserved in git log: commit f2024cb on main as DavidMetcalfe). Cherry-picked onto current main. Verified live before merge: bug reproduced on pristine main, fix routes reasoning+disconnect+large-session to FailoverReason.timeout (no phantom compression) while leaving chat models and genuine HTTP-400 overflows untouched; 210/210 targeted tests pass. The bundled agent/reasoning_timeouts.py is the load-bearing allowlist — if #52238 lands first the duplicate is a mechanical rebase-away. Thanks for the thorough writeup and proposed fix.

@teknium1 teknium1 closed this Jun 26, 2026
pai-scaffolde pushed a commit to pai-scaffolde/hermes-agent that referenced this pull request Jun 28, 2026
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

4 participants