fix(context): preserve missing-key compression history

This commit is contained in:
Gille 2026-07-15 14:20:42 -06:00 committed by kshitij
parent 202ad1b8c9
commit 577beeb9b9
8 changed files with 251 additions and 17 deletions

View file

@ -47,6 +47,11 @@ _SUMMARY_PERMANENT_QUOTA_MARKERS: tuple[str, ...] = (
"out of extra usage",
)
_SUMMARY_MISSING_CREDENTIAL_MARKERS: tuple[str, ...] = (
"no api key was found",
"no api key found",
)
def _is_summary_access_or_quota_error(exc: Exception) -> bool:
"""Return True for non-retryable summary auth, permission, or quota errors."""
@ -57,13 +62,16 @@ def _is_summary_access_or_quota_error(exc: Exception) -> bool:
if classified.reason in {FailoverReason.auth, FailoverReason.auth_permanent}:
return True
err_text = str(exc).lower()
if any(marker in err_text for marker in _SUMMARY_MISSING_CREDENTIAL_MARKERS):
return True
status = getattr(exc, "status_code", None) or getattr(
getattr(exc, "response", None), "status_code", None
)
if status in {401, 402, 403}:
return True
err_text = str(exc).lower()
if classified.reason is FailoverReason.billing:
return any(marker in err_text for marker in _SUMMARY_PERMANENT_QUOTA_MARKERS)
return any(marker in err_text for marker in _SUMMARY_PERMANENT_QUOTA_MARKERS)

View file

@ -4,45 +4,83 @@ from __future__ import annotations
from typing import Any, Sequence
from agent.redact import redact_sensitive_text
def summarize_manual_compression(
before_messages: Sequence[dict[str, Any]],
after_messages: Sequence[dict[str, Any]],
before_tokens: int,
after_tokens: int,
*,
compression_state: Any = None,
) -> dict[str, Any]:
"""Return consistent user-facing feedback for manual compression."""
before_count = len(before_messages)
after_count = len(after_messages)
noop = list(after_messages) == list(before_messages)
aborted = (
compression_state is not None
and getattr(compression_state, "_last_compress_aborted", False) is True
)
fallback_used = (
compression_state is not None
and getattr(compression_state, "_last_summary_fallback_used", False) is True
)
failure_reason = (
getattr(compression_state, "_last_summary_error", None)
if compression_state is not None
else None
)
if not isinstance(failure_reason, str) or not failure_reason.strip():
failure_reason = None
if noop:
if aborted:
headline = f"Compression aborted: {before_count} messages preserved"
elif fallback_used:
headline = (
f"Compressed with fallback: {before_count}{after_count} messages"
)
elif noop:
headline = f"No changes from compression: {before_count} messages"
if after_tokens == before_tokens:
token_line = (
f"Approx request size: ~{before_tokens:,} tokens (unchanged)"
)
else:
token_line = (
f"Approx request size: ~{before_tokens:,}"
f"~{after_tokens:,} tokens"
)
else:
headline = f"Compressed: {before_count}{after_count} messages"
if noop and after_tokens == before_tokens:
token_line = f"Approx request size: ~{before_tokens:,} tokens (unchanged)"
else:
token_line = (
f"Approx request size: ~{before_tokens:,}"
f"~{after_tokens:,} tokens"
)
note = None
if not noop and after_count < before_count and after_tokens > before_tokens:
if aborted:
note = "Summary generation failed; no messages were removed."
elif fallback_used:
dropped_count = getattr(
compression_state, "_last_summary_dropped_count", None
)
if not isinstance(dropped_count, int) or isinstance(dropped_count, bool):
dropped_count = max(before_count - after_count, 0)
note = (
"Summary generation failed; Hermes used limited fallback context "
f"and removed {dropped_count} message(s)."
)
elif not noop and after_count < before_count and after_tokens > before_tokens:
note = (
"Note: fewer messages can still raise this estimate when "
"compression rewrites the transcript into denser summaries."
)
if failure_reason and (aborted or fallback_used):
safe_reason = redact_sensitive_text(failure_reason.strip())
note = f"{note} Reason: {safe_reason}"
return {
"noop": noop,
"aborted": aborted,
"fallback_used": fallback_used,
"headline": headline,
"token_line": token_line,
"note": note,