fix(context): preserve transient quota retry behavior

This commit is contained in:
Alex López 2026-07-11 10:29:46 -04:00 committed by kshitij
parent c72f4576b9
commit 202ad1b8c9
2 changed files with 35 additions and 8 deletions

View file

@ -26,6 +26,7 @@ from typing import Any, Dict, List, Optional
from agent.auxiliary_client import call_llm, _is_connection_error, aux_interrupt_protection
from agent.context_engine import ContextEngine
from agent.error_classifier import FailoverReason, classify_api_error
from agent.model_metadata import (
MINIMUM_CONTEXT_LENGTH,
get_model_context_length,
@ -36,11 +37,7 @@ from agent.redact import redact_sensitive_text
logger = logging.getLogger(__name__)
_SUMMARY_ACCESS_OR_QUOTA_ERROR_SUBSTRINGS: tuple[str, ...] = (
"invalid api key",
"invalid x-api-key",
"unauthorized",
"authentication",
_SUMMARY_PERMANENT_QUOTA_MARKERS: tuple[str, ...] = (
"insufficient_quota",
"quota exceeded",
"quota_exceeded",
@ -54,6 +51,12 @@ _SUMMARY_ACCESS_OR_QUOTA_ERROR_SUBSTRINGS: tuple[str, ...] = (
def _is_summary_access_or_quota_error(exc: Exception) -> bool:
"""Return True for non-retryable summary auth, permission, or quota errors."""
classified = classify_api_error(exc)
if classified.reason is FailoverReason.rate_limit:
return False
if classified.reason in {FailoverReason.auth, FailoverReason.auth_permanent}:
return True
status = getattr(exc, "status_code", None) or getattr(
getattr(exc, "response", None), "status_code", None
)
@ -61,9 +64,9 @@ def _is_summary_access_or_quota_error(exc: Exception) -> bool:
return True
err_text = str(exc).lower()
if "api key" in err_text and ("invalid" in err_text or "blocked" in err_text):
return True
return any(marker in err_text for marker in _SUMMARY_ACCESS_OR_QUOTA_ERROR_SUBSTRINGS)
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)
HISTORICAL_TASK_HEADING = "## Historical Task Snapshot"

View file

@ -860,6 +860,7 @@ class TestAuthFailureAborts:
"billing portal is temporarily unavailable",
"usage limit documentation could not be loaded",
"rate limit exceeded; retry later",
"quota exceeded, please retry after the window resets",
"request timed out",
],
)
@ -904,6 +905,29 @@ class TestAuthFailureAborts:
assert c._last_compress_aborted is True
assert c._last_summary_fallback_used is False
def test_402_quota_with_retry_uses_existing_fallback(self):
"""A reset-window quota remains transient instead of aborting compression."""
err = StubProviderError(
"quota exceeded, please retry after the window resets",
status_code=402,
)
with patch("agent.context_compressor.get_model_context_length", return_value=100000):
c = ContextCompressor(
model="test",
quiet_mode=True,
protect_first_n=2,
protect_last_n=2,
abort_on_summary_failure=False,
)
msgs = self._msgs(12)
with patch("agent.context_compressor.call_llm", side_effect=err):
result = c.compress(msgs, current_tokens=999999, force=True)
assert result != msgs
assert c._last_summary_auth_failure is False
assert c._last_compress_aborted is False
assert c._last_summary_fallback_used is True
def test_generate_summary_flags_auth_failure(self):
with patch("agent.context_compressor.get_model_context_length", return_value=100000):
c = ContextCompressor(model="test", quiet_mode=True)