Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions agent/conversation_compression.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,15 @@ def check_compression_model_feasibility(agent: Any) -> None:
# above guarantees aux_context >= MINIMUM_CONTEXT_LENGTH,
# so the new threshold is always >= 64K.
#
# The compression summariser sends a single user-role
# prompt (no system prompt, no tools) to the aux model, so
# new_threshold == aux_context is safe: the request is
# the raw messages plus a small summarisation instruction.
# Apply an 80 % safety margin on the aux model's context
# window: the summarisation prompt template, system
# instructions, and tool schemas consume part of the
# context, so reserving 20 % headroom avoids passing a
# request that exceeds the aux model's actual capacity
# (see issue #53008 — a too-high threshold combined with
# no headroom can produce an infinite compression loop).
old_threshold = threshold
new_threshold = aux_context
new_threshold = max(int(aux_context * 0.8), MINIMUM_CONTEXT_LENGTH)
agent.context_compressor.threshold_tokens = new_threshold
# Keep threshold_percent in sync so future main-model
# context_length changes (update_model) re-derive from a
Expand All @@ -285,7 +288,7 @@ def check_compression_model_feasibility(agent: Any) -> None:
agent.context_compressor.threshold_percent = (
new_threshold / main_ctx
)
safe_pct = int((aux_context / main_ctx) * 100) if main_ctx else 50
safe_pct = int((new_threshold / main_ctx) * 100) if main_ctx else 50
# Build human-readable "model (provider)" labels for both
# the main model and the compression model so users can
# tell at a glance which provider each side is actually
Expand Down
24 changes: 23 additions & 1 deletion agent/turn_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ def build_turn_context(
f">= {_compressor.threshold_tokens:,} threshold. "
"This may take a moment."
)
_preflight_original_tokens = _preflight_tokens
for _pass in range(3):
_orig_len = len(messages)
_orig_tokens = _preflight_tokens
Expand Down Expand Up @@ -428,7 +429,28 @@ def build_turn_context(
if not _compressor.should_compress(_preflight_tokens):
break

# Plugin hook: pre_llm_call (context injected into user message, not system prompt).
# Guard against an infinite compression loop when the aux
# model is too small to compress the current session
# effectively (issue #53008). Token reduction below 10 %
# means every turn will re-trigger compression without
# making progress — raise the threshold to break the cycle.
_reduction = 1.0 - (_preflight_tokens / _preflight_original_tokens)
if _reduction < 0.10 and _compressor.should_compress(_preflight_tokens):
_old_threshold = _compressor.threshold_tokens
_compressor.threshold_tokens = max(
_preflight_tokens + 1,
int(_old_threshold * 1.5),
)
logger.warning(
"Preflight compression ineffective "
"(%.1f%% token reduction — aux model may be too "
"small for current session). "
"Raising threshold %s → %s to prevent "
"compression loop.",
_reduction * 100,
f"{_old_threshold:,}",
f"{_compressor.threshold_tokens:,}",
)
plugin_user_context = ""
try:
from hermes_cli.plugins import invoke_hook as _invoke_hook
Expand Down
Loading