Skip to content

fix(agent): bound context compression summary stalls (salvage #49905) - #56295

Closed
kshitijk4poor wants to merge 2 commits into
NousResearch:mainfrom
kshitijk4poor:salvage-49905
Closed

fix(agent): bound context compression summary stalls (salvage #49905)#56295
kshitijk4poor wants to merge 2 commits into
NousResearch:mainfrom
kshitijk4poor:salvage-49905

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Summary

Salvage of #49905 by @LeonSGP43 (rebased onto current main + a rebase-follow-up fixing a production gap the rebase exposed). Bounds context-compression summary generation with a hard timeout so a wedged summary call can't block the gateway loop indefinitely (#49768).

The bug

call_llm forwards auxiliary.compression.timeout to the provider client as a socket/read timeout only — it does not bound the caller's wait if the network stack wedges or the SDK ignores it. The synchronous summary call in context_compressor.py was wrapped only in aux_interrupt_protection() (which suppresses interrupts — the opposite of a timeout), so compression summary generation could block the gateway loop forever. Confirmed still present on main.

The fix

@LeonSGP43's fix: run the summary call_llm in a ThreadPoolExecutor(max_workers=1) worker and stop waiting after auxiliary.compression.timeout (future.result(timeout=...), shutdown(wait=False, cancel_futures=True)). On timeout it raises, caught by the existing handler and routed into the deterministic fallback summary path — the gateway stays responsive.

Rebase + production-gap fix (this salvage)

The PR was ~592 commits behind; the compressor drifted (two cosmetic conflicts). Resolved by composing main's aux_interrupt_protection wrapper + defensive message coercion with the PR's timeout call (documented the thread-local interaction honestly: the protection guards caller-thread unpacking, the hard timeout is the stronger guarantee for the wedged-socket case — complementary, not nested).

Fixing the rebase surfaced a real production gap: the hard timeout raises a builtin TimeoutError, but _generate_summary's handler set _is_streaming_closed = _is_connection_error(e) — and _is_connection_error classifies any exception whose type name contains "Timeout" as a network close. That set _last_summary_network_failure=True, making compress() take the network-failure ABORT branch (return messages unchanged) instead of the deterministic fallback — the opposite of the PR's intent. Gated it: _is_streaming_closed = _is_connection_error(e) and not _is_timeout. Mutation-verified against the contributor's own test_compress_hard_timeout_uses_existing_fallback_path (reverting the gate fails it). Genuine streaming-closes don't carry "timeout" wording, so they're unaffected.

Review

Ran hermes-agent-dev + hermes-pr-review Phase 2c — 0 Critical. The executor pattern is correct, the abandoned-thread trade-off is bounded/documented, auxiliary.compression.timeout confirmed in DEFAULT_CONFIG, and the false-suppression edge for the _is_timeout gate was independently checked (genuine mid-stream closes are correctly preserved).

Tests

pytest tests/agent/test_context_compressor.py -q   # 142 passed (CI runs the full suite)

Supersedes #49905. Full credit to @LeonSGP43.

LeonSGP43 and others added 2 commits July 1, 2026 15:49
…bort

Rebase follow-up on the compression hard-timeout salvage. The new hard
timeout raises a builtin TimeoutError, but _generate_summary's handler set
_is_streaming_closed = _is_connection_error(e) — and _is_connection_error
classifies ANY exception whose type name contains 'Timeout' as a network
close. That set _last_summary_network_failure=True, so compress() took the
network-failure ABORT branch (return messages unchanged) instead of the
deterministic-fallback path — the opposite of the PR's intent (a timeout
should fall back to a best-effort summary, not abort compression).

Gate it: _is_streaming_closed = _is_connection_error(e) and not _is_timeout.
Timeouts (our hard-timeout, or 408/429/504) are already handled by the
_is_timeout classification above; only a genuine mid-stream close should
trip the network-failure abort. The contributor's own
test_compress_hard_timeout_uses_existing_fallback_path guards this
(mutation-verified: reverting the gate fails it).
@kshitijk4poor

Copy link
Copy Markdown
Collaborator Author

Withdrawing this salvage for now — closing per maintainer direction to focus the current batch elsewhere. The underlying #49905 (bound context compression summary stalls) remains open and can be re-salvaged later. No changes merged.

auto-merge was automatically disabled July 1, 2026 10:33

Pull request was closed

@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 sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state P2 Medium — degraded but workaround exists labels Jul 1, 2026
@kshitijk4poor
kshitijk4poor deleted the salvage-49905 branch August 5, 2026 07:09
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 P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants