mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-20 15:33:54 +00:00
fix(context): harden compression failure feedback
This commit is contained in:
parent
577beeb9b9
commit
1e895f4c17
3 changed files with 44 additions and 18 deletions
|
|
@ -2364,8 +2364,10 @@ This compaction should PRIORITISE preserving all information related to the focu
|
|||
# from a distinct auxiliary summary_model; only a failure on the
|
||||
# main model — or a fallback that also access/quota-fails — makes
|
||||
# the abort stick.
|
||||
_is_auth_error = _is_summary_access_or_quota_error(e)
|
||||
if _is_auth_error:
|
||||
_is_access_or_quota_error = _is_summary_access_or_quota_error(e)
|
||||
if _is_access_or_quota_error:
|
||||
# Keep the established field name for caller compatibility;
|
||||
# it now represents the broader terminal access/quota class.
|
||||
self._last_summary_auth_failure = True
|
||||
if _is_json_decode and not _is_model_not_found and not _is_timeout:
|
||||
logger.error(
|
||||
|
|
@ -3251,16 +3253,14 @@ This compaction should PRIORITISE preserving all information related to the focu
|
|||
# surface a warning.
|
||||
# Default is False (historical behavior).
|
||||
#
|
||||
# EXCEPTION — auth AND transient network failures always abort. A
|
||||
# 401/403 from the summary call means the credential or endpoint is
|
||||
# broken (invalid/blocked key, or a token pointed at the wrong
|
||||
# inference host). A connection/stream-close error means the network
|
||||
# blipped at the compaction moment (#29559). In BOTH cases rotating into
|
||||
# a child session with a placeholder summary on a broken credential
|
||||
# strands the user on a degraded session for zero benefit — every
|
||||
# subsequent call fails the same way. So when the failure was an auth
|
||||
# error we abort regardless of abort_on_summary_failure, preserving
|
||||
# the conversation unchanged until the credential is fixed.
|
||||
# EXCEPTION — terminal access/quota AND transient network failures
|
||||
# always abort. Missing credentials, 401/402/403 access failures, and
|
||||
# confirmed non-resetting quota exhaustion cannot be repaired by
|
||||
# retrying the same summary request. A connection/stream-close error
|
||||
# means the network blipped at the compaction moment (#29559). In all
|
||||
# of these cases, rotating into a child session with a placeholder
|
||||
# summary degrades the conversation for zero benefit. Preserve it
|
||||
# unchanged until access is restored or connectivity recovers.
|
||||
if not summary and (
|
||||
self.abort_on_summary_failure
|
||||
or self._last_summary_auth_failure
|
||||
|
|
@ -3273,11 +3273,12 @@ This compaction should PRIORITISE preserving all information related to the focu
|
|||
if not self.quiet_mode:
|
||||
if self._last_summary_auth_failure:
|
||||
logger.warning(
|
||||
"Summary generation failed with an authentication "
|
||||
"error — aborting compression. %d message(s) preserved "
|
||||
"unchanged; the session was NOT rotated. Check your "
|
||||
"provider credential / inference endpoint, then retry "
|
||||
"with /compress or start fresh with /new.",
|
||||
"Summary generation failed with a terminal access or "
|
||||
"quota error — aborting compression. %d message(s) "
|
||||
"preserved unchanged; the session was NOT rotated. "
|
||||
"Check the provider credential, permission, quota, or "
|
||||
"inference endpoint, then retry with /compress or "
|
||||
"start fresh with /new.",
|
||||
n_skipped,
|
||||
)
|
||||
elif self._last_summary_network_failure:
|
||||
|
|
|
|||
|
|
@ -74,7 +74,10 @@ def summarize_manual_compression(
|
|||
)
|
||||
|
||||
if failure_reason and (aborted or fallback_used):
|
||||
safe_reason = redact_sensitive_text(failure_reason.strip())
|
||||
# This text crosses a user-facing UI boundary. Never let a disabled
|
||||
# global redaction preference expose credentials embedded in provider
|
||||
# exception text.
|
||||
safe_reason = redact_sensitive_text(failure_reason.strip(), force=True)
|
||||
note = f"{note} Reason: {safe_reason}"
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,28 @@ def test_aborted_compression_reports_preserved_messages_and_reason():
|
|||
assert "no API key was found" in feedback["note"]
|
||||
|
||||
|
||||
def test_failure_reason_redaction_is_forced_at_ui_boundary(monkeypatch):
|
||||
messages = _messages(12)
|
||||
fake_secret = "sk-proj-" + "X" * 40
|
||||
state = SimpleNamespace(
|
||||
_last_compress_aborted=True,
|
||||
_last_summary_fallback_used=False,
|
||||
_last_summary_error=f"provider rejected OPENAI_API_KEY={fake_secret}",
|
||||
)
|
||||
monkeypatch.setattr("agent.redact._REDACT_ENABLED", False, raising=False)
|
||||
|
||||
feedback = summarize_manual_compression(
|
||||
messages,
|
||||
list(messages),
|
||||
120_000,
|
||||
120_000,
|
||||
compression_state=state,
|
||||
)
|
||||
|
||||
assert fake_secret not in feedback["note"]
|
||||
assert "OPENAI_API_KEY=" in feedback["note"]
|
||||
|
||||
|
||||
def test_fallback_compression_reports_dropped_message_count():
|
||||
before = _messages(12)
|
||||
after = before[:2] + before[-2:]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue