fix(gateway): widen compression noise filter to all routine status lines

Post-sweep audit of every compression status emission (conversation_loop,
turn_context, conversation_compression): the buffered overflow/attempt-cap
retry chatter (🗜️ 'Context too large…', 'Compressed X → Y, retrying…',
'Context reduced to…'), the #69332-reworded auto-lower notice
("Auto-lowered this session's threshold…"), the aux-provider-unavailable
notice, and the concurrent-compression skip all leaked past
_TELEGRAM_NOISY_STATUS_RE on chat platforms. Add anchored alternatives for
each; the ', retrying'/'— compressing' anchors keep manual /compress
feedback ('Compressed: 30 → 12 messages') and failure/abort notices
visible per the deliberate carve-outs.

Also extract every routine compression status string into importable
template constants in agent/conversation_compression.py (single source of
truth shared by all emission sites), so tests can iterate the actual
emitted wording instead of hand-copied literals.
This commit is contained in:
Teknium 2026-07-22 11:23:27 -07:00
parent a2068c668b
commit 9981242f88
4 changed files with 96 additions and 16 deletions

View file

@ -71,6 +71,58 @@ def _emit_compaction_done(agent: Any) -> None:
logger.debug("status_callback error in compaction completion", exc_info=True)
# ── Routine compression status templates ────────────────────────────────────
# Every ROUTINE (non-failure, non-manual-/compress) compression status line the
# agent emits lives here so the gateway noise filter and its tests can couple
# to the real emitted wording instead of hand-copied literals. These are
# suppressed on human-facing chat platforms by _TELEGRAM_NOISY_STATUS_RE
# (gateway/run.py) — when rewording ANY of them, update that regex and the
# pinned data in tests/gateway/test_telegram_noise_filter.py in the same PR.
# Failure notices (⚠ Compression aborted / empty transcript / codex compaction
# failed) and manual /compress feedback (manual_compression_feedback.py) are
# deliberate carve-outs from silence and must NOT be added here.
PRE_API_COMPRESSION_STATUS_TEMPLATE = (
"📦 Pre-API compression: ~{tokens:,} tokens "
"near the context/output limit. Compacting before the next model call."
)
PREFLIGHT_COMPRESSION_STATUS_TEMPLATE = (
"📦 Preflight compression: ~{tokens:,} tokens "
">= {threshold:,} threshold. This may take a moment."
)
IDLE_COMPACTION_STATUS_TEMPLATE = (
"💤 Resumed after {idle_seconds}s idle — compacting "
"~{tokens:,} tokens before continuing."
)
COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE = (
"🗜️ Context too large (~{tokens:,} tokens) — compressing ({attempt}/{cap})..."
)
COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE = (
"🗜️ Compressed {before}{after} messages, retrying..."
)
COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE = (
"🗜️ Compressed ~{before:,} → ~{after:,} tokens, retrying..."
)
COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE = (
"🗜️ Context reduced to {new_ctx:,} tokens (was {old_ctx:,}), retrying..."
)
# Sample-formatted instances of every routine compression status line, for
# behavioral tests that iterate the ACTUAL emitted wording (formatted from the
# same constants the emission sites use) through the gateway noise filter.
ROUTINE_COMPRESSION_STATUS_SAMPLES = (
COMPACTION_STATUS,
PRE_API_COMPRESSION_STATUS_TEMPLATE.format(tokens=123456),
PREFLIGHT_COMPRESSION_STATUS_TEMPLATE.format(tokens=120000, threshold=100000),
IDLE_COMPACTION_STATUS_TEMPLATE.format(idle_seconds=3600, tokens=120000),
COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE.format(tokens=250000, attempt=1, cap=3),
COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE.format(before=30, after=12),
COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE.format(before=250000, after=120000),
COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE.format(
new_ctx=120000, old_ctx=250000
),
)
def _builtin_memory_prompt_snapshot(agent: Any) -> Optional[Tuple[str, str]]:
"""Return the built-in memory text that can affect a system prompt.

View file

@ -28,7 +28,14 @@ import uuid
from typing import Any, Dict, List, Optional
from agent.codex_responses_adapter import _summarize_user_message_for_log
from agent.conversation_compression import conversation_history_after_compression
from agent.conversation_compression import (
COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE,
COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE,
COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE,
COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE,
PRE_API_COMPRESSION_STATUS_TEMPLATE,
conversation_history_after_compression,
)
from agent.display import KawaiiSpinner
from agent.error_classifier import FailoverReason, classify_api_error
from agent.iteration_budget import IterationBudget
@ -1279,8 +1286,9 @@ def run_conversation(
max_compression_attempts,
)
agent._emit_status(
f"📦 Pre-API compression: ~{request_pressure_tokens:,} tokens "
f"near the context/output limit. Compacting before the next model call."
PRE_API_COMPRESSION_STATUS_TEMPLATE.format(
tokens=request_pressure_tokens
)
)
_last_preflight_pressure = request_pressure_tokens
messages, active_system_prompt = agent._compress_context(
@ -3494,8 +3502,9 @@ def run_conversation(
)
if len(messages) < original_len or old_ctx > _reduced_ctx:
agent._buffer_status(
f"🗜️ Context reduced to {_reduced_ctx:,} tokens "
f"(was {old_ctx:,}), retrying..."
COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE.format(
new_ctx=_reduced_ctx, old_ctx=old_ctx
)
)
time.sleep(2)
_retry.restart_with_compressed_messages = True
@ -3756,9 +3765,9 @@ def run_conversation(
if len(messages) < original_len or (new_tokens > 0 and new_tokens < original_tokens * 0.95):
if len(messages) < original_len:
agent._buffer_status(f"🗜️ Compressed {original_len}{len(messages)} messages, retrying...")
agent._buffer_status(COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE.format(before=original_len, after=len(messages)))
else:
agent._buffer_status(f"🗜️ Compressed ~{original_tokens:,} → ~{new_tokens:,} tokens, retrying...")
agent._buffer_status(COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE.format(before=original_tokens, after=new_tokens))
time.sleep(2) # Brief pause between compression retries
_retry.restart_with_compressed_messages = True
break
@ -3976,7 +3985,7 @@ def run_conversation(
"failed": True,
"compression_exhausted": True,
}
agent._buffer_status(f"🗜️ Context too large (~{approx_tokens:,} tokens) — compressing ({compression_attempts}/{max_compression_attempts})...")
agent._buffer_status(COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE.format(tokens=approx_tokens, attempt=compression_attempts, cap=max_compression_attempts))
original_len = len(messages)
original_tokens = estimate_messages_tokens_rough(messages)
@ -3997,9 +4006,9 @@ def run_conversation(
if len(messages) < original_len or (new_tokens > 0 and new_tokens < original_tokens * 0.95) or (new_ctx and new_ctx < old_ctx):
if len(messages) < original_len:
agent._buffer_status(f"🗜️ Compressed {original_len}{len(messages)} messages, retrying...")
agent._buffer_status(COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE.format(before=original_len, after=len(messages)))
elif new_tokens > 0 and new_tokens < original_tokens * 0.95:
agent._buffer_status(f"🗜️ Compressed ~{original_tokens:,} → ~{new_tokens:,} tokens, retrying...")
agent._buffer_status(COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE.format(before=original_tokens, after=new_tokens))
time.sleep(2) # Brief pause between compression retries
_retry.restart_with_compressed_messages = True
break

View file

@ -31,7 +31,11 @@ import uuid
from dataclasses import dataclass
from typing import Any, Dict, List, Mapping, Optional
from agent.conversation_compression import conversation_history_after_compression
from agent.conversation_compression import (
IDLE_COMPACTION_STATUS_TEMPLATE,
PREFLIGHT_COMPRESSION_STATUS_TEMPLATE,
conversation_history_after_compression,
)
from agent.iteration_budget import IterationBudget
from agent.memory_manager import build_memory_context_block
from agent.model_metadata import (
@ -659,8 +663,9 @@ def build_turn_context(
agent.session_id or "none",
)
agent._emit_status(
f"💤 Resumed after {int(_idle_gap)}s idle — compacting "
f"~{_idle_tokens:,} tokens before continuing."
IDLE_COMPACTION_STATUS_TEMPLATE.format(
idle_seconds=int(_idle_gap), tokens=_idle_tokens
)
)
_idle_input = messages
messages, active_system_prompt = agent._compress_context(
@ -767,9 +772,10 @@ def build_turn_context(
f"{_compressor.context_length:,}",
)
agent._emit_status(
f"📦 Preflight compression: ~{_preflight_tokens:,} tokens "
f">= {_compressor.threshold_tokens:,} threshold. "
"This may take a moment."
PREFLIGHT_COMPRESSION_STATUS_TEMPLATE.format(
tokens=_preflight_tokens,
threshold=_compressor.threshold_tokens,
)
)
# Preflight passes honor the same configured per-turn cap
# (compression.max_attempts) as the loop's compression sites;

View file

@ -79,10 +79,23 @@ _TELEGRAM_NOISY_STATUS_RE = re.compile(
r"|configured\s+compression\s+model\s+.+\s+failed"
r"|no\s+auxiliary\s+llm\s+provider\s+configured"
r"|auto-lowered\s+compression\s+threshold"
# #69332 reworded the auto-lower notice to "Auto-lowered this session's
# threshold to N tokens" — keep both generations covered.
r"|auto-lowered\s+(?:this\s+)?session'?s?\s+threshold"
r"|configured\s+auxiliary\s+compression\s+provider\s+.+\s+unavailable"
r"|skipping\s+concurrent\s+compression"
r"|compacting\s+context\s+[—-]\s+summarizing\s+earlier\s+conversation"
r"|resumed\s+after\s+\d+s\s+idle\s+[—-]\s+compacting"
r"|preflight\s+compression"
r"|pre[- ]api\s+compression"
# Buffered attempt/overflow retry chatter replayed through _emit_status
# when a turn exhausts retries. The ", retrying"/"— compressing" anchors
# keep manual /compress feedback ("Compressed: 30 → 12 messages") and
# failure notices out of the match.
r"|context\s+too\s+large\s+\(~[\d,]+\s+tokens\)\s+[—-]+\s+compressing"
r"|compressed\s+\d[\d,]*\s+(?:→|->)\s+\d[\d,]*\s+messages,\s+retrying"
r"|compressed\s+~[\d,]+\s+(?:→|->)\s+~[\d,]+\s+tokens,\s+retrying"
r"|context\s+reduced\s+to\s+[\d,]+\s+tokens\s+\(was\s+[\d,]+\),\s+retrying"
r"|session\s+compressed\s+\d+\s+times"
r"|rate\s+limited\.\s+waiting\s+\d"
r"|retrying\s+in\s+\d"