mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-20 15:33:54 +00:00
fix(context): preserve transient quota retry behavior
This commit is contained in:
parent
c72f4576b9
commit
202ad1b8c9
2 changed files with 35 additions and 8 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue