fix(run_agent): call should_compress_preflight() for sub-threshold engines (#20316) - #20424
fix(run_agent): call should_compress_preflight() for sub-threshold engines (#20316)#20424Beandon13 wants to merge 1 commit into
Conversation
…gines (NousResearch#20316) Context engines that override ``should_compress_preflight()`` (e.g. the hermes-lcm plugin's incremental leaf-chunk compaction) never had their hook fired by ``run_conversation`` because the preflight block exited early once the hardcoded ``>= threshold_tokens`` check failed. As a result, ``LCM_DEFERRED_MAINTENANCE_ENABLED=1`` and friends were inert and accumulated raw_backlog debt indefinitely. Add an ``elif`` branch that delegates to the engine's preflight hook when the legacy threshold check does not fire. The default ``ContextEngine.should_compress_preflight()`` returns ``False`` so the built-in ``ContextCompressor`` is unaffected; engines opting in get a chance to ingest messages and request a single ``compress()`` pass for deferred maintenance. Exceptions are swallowed at debug level so a buggy engine cannot break an otherwise-healthy turn. Closes NousResearch#20316
|
Related UX/API concern from an LCM deployment: #25115. This PR wires the preflight hook so alternative context engines can run below the normal threshold. That is exactly the path where host status wording becomes important: if It would be useful if this hook, or a companion |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for identifying the dormant context-engine hook. The premise still holds on current main: agent/turn_context.py:420 only calls _compressor.should_compress(_preflight_tokens), while the optional hook remains defined at agent/context_engine.py:110-116.
Problems
- The implementation is stale:
run_agent.py:5775-5796now forwards to the extracted conversation loop, and the live preflight block isagent/turn_context.py:350-463. GitHub currently reports this branch as conflicting. - A port must preserve the newer deferral, cooldown, and Codex-native guards at
agent/turn_context.py:399-420; the alternate-engine decision should not bypass those protections.
Suggested changes
- Rework the patch against
agent/turn_context.pyand add current-path coverage for a concrete engine whose preflight hook requests below-threshold maintenance, returns false, and raises.
Automated hermes-sweeper review.
| ) | ||
| if _preflight_tokens < self.context_compressor.threshold_tokens: | ||
| break # Under threshold | ||
| elif hasattr(self.context_compressor, "should_compress_preflight"): |
There was a problem hiding this comment.
Current main extracted this preflight block into agent/turn_context.py:350-463; run_agent.py:5775-5796 is now only a forwarder. Please port this branch into the live turn-context path and preserve its current deferral, cooldown, and Codex-native-compaction guards.
…ght flow Relocates the #20424 wiring: the preflight region moved out of run_agent.py into agent/turn_context.py (and through the compression.max_attempts unification, #69315), so the contributor's elif branch is reapplied at its current home as the else arm of the threshold dispatch chain. Integration contracts: - Byte-identical default: the built-in ContextCompressor inherits ContextEngine.should_compress_preflight() -> False, so the default path performs no compression and touches no turn bookkeeping (pinned by test_builtin_compressor_default_sub_threshold_path_unchanged). - Attempt-cap: the engine gets exactly ONE compress() pass per turn, mutually exclusive with the cap-bounded threshold multi-pass loop, so turn-start passes stay within the resolved compression.max_attempts budget in every case. - No-op blocking (#64382 / 377244f): an engine pass that no-ops (_compress_context returns the input list object) neither sets nor clears preflight_compression_blocked and does not re-baseline the flush history — a sub-threshold maintenance no-op proves nothing about over-threshold compressibility. - Engine exceptions are swallowed at debug level; cooldown/defer/ codex-native gates run before the hook is ever consulted. Salvaged from #20424 by @Beandon13. Fixes #20316.
…ght flow Relocates the NousResearch#20424 wiring: the preflight region moved out of run_agent.py into agent/turn_context.py (and through the compression.max_attempts unification, NousResearch#69315), so the contributor's elif branch is reapplied at its current home as the else arm of the threshold dispatch chain. Integration contracts: - Byte-identical default: the built-in ContextCompressor inherits ContextEngine.should_compress_preflight() -> False, so the default path performs no compression and touches no turn bookkeeping (pinned by test_builtin_compressor_default_sub_threshold_path_unchanged). - Attempt-cap: the engine gets exactly ONE compress() pass per turn, mutually exclusive with the cap-bounded threshold multi-pass loop, so turn-start passes stay within the resolved compression.max_attempts budget in every case. - No-op blocking (NousResearch#64382 / 0e73a78): an engine pass that no-ops (_compress_context returns the input list object) neither sets nor clears preflight_compression_blocked and does not re-baseline the flush history — a sub-threshold maintenance no-op proves nothing about over-threshold compressibility. - Engine exceptions are swallowed at debug level; cooldown/defer/ codex-native gates run before the hook is ever consulted. Salvaged from NousResearch#20424 by @Beandon13. Fixes NousResearch#20316.
Summary
run_conversationnow consultsContextEngine.should_compress_preflight()when the request is belowthreshold_tokens, so engines like hermes-lcm can run incremental leaf-chunk compaction (or other deferred maintenance) without waiting for the 75% context fill cutoff.ContextEngine.should_compress_preflight()still returnsFalse— the built-inContextCompressoris unaffected.Closes #20316
Testing