mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-27 17:58:07 +00:00
fix(errors): classify throttle messages before token-overflow patterns; add new overflow shapes
Port from anomalyco/opencode#37848 (+ dev-branch twin #37840): expand context-overflow patterns and guard against rate-limit messages that mention tokens. - 'Throttling error: Too many tokens, please wait before trying again.' (AWS Bedrock / proxy shape) classified as context_overflow and routed a healthy session into compression on every throttle. Added 'throttling' to _RATE_LIMIT_PATTERNS, which the message-only path checks BEFORE the overflow list. - 'Input length N exceeds the maximum allowed input length of M tokens.' (Together/Fireworks shape) fell through to unknown — no compression recovery. Added 'maximum allowed input length' to overflow patterns. - 'request_too_large' / 'Request exceeds the maximum size' (Anthropic 413 type re-wrapped without a status code by aggregators/proxies) fell through to unknown. Added to _PAYLOAD_TOO_LARGE_PATTERNS. All three shapes proven live on main before the fix; 265 classifier + bedrock tests and 238 sibling rate-guard/compression tests pass.
This commit is contained in:
parent
c4d1913294
commit
53bfe40a35
2 changed files with 90 additions and 0 deletions
|
|
@ -159,6 +159,14 @@ _RATE_LIMIT_PATTERNS = [
|
|||
"throttlingexception",
|
||||
"too many concurrent requests",
|
||||
"servicequotaexceededexception",
|
||||
# Generic throttle prefix — Bedrock (and some proxies) surface throttling
|
||||
# as "Throttling error: Too many tokens, please wait before trying
|
||||
# again." Without this entry the message falls through to the
|
||||
# context-overflow list (which contains "too many tokens") and the retry
|
||||
# loop compresses a healthy session instead of backing off. Matched
|
||||
# BEFORE _CONTEXT_OVERFLOW_PATTERNS in the message-only path, so the
|
||||
# throttle wins. (port of anomalyco/opencode#37848's exclusion guard)
|
||||
"throttling",
|
||||
]
|
||||
|
||||
# Patterns that indicate provider-side overload, NOT a per-credential rate
|
||||
|
|
@ -212,6 +220,12 @@ _PAYLOAD_TOO_LARGE_PATTERNS = [
|
|||
"request entity too large",
|
||||
"payload too large",
|
||||
"error code: 413",
|
||||
# Anthropic's structured 413 error type. Normally arrives with an HTTP
|
||||
# 413 status (handled by the status path), but aggregators/proxies can
|
||||
# re-wrap it into a plain message with no status attribute — route it to
|
||||
# the same compression recovery. (port of anomalyco/opencode#37848)
|
||||
"request_too_large",
|
||||
"request exceeds the maximum size",
|
||||
]
|
||||
|
||||
# Image-size patterns. Matched against 400 bodies (not 413) because most
|
||||
|
|
@ -298,6 +312,10 @@ _CONTEXT_OVERFLOW_PATTERNS = [
|
|||
"max input token",
|
||||
"input token",
|
||||
"exceeds the maximum number of input tokens",
|
||||
# Together/Fireworks-style: "Input length 131393 exceeds the maximum
|
||||
# allowed input length of 131040 tokens." No other pattern in this list
|
||||
# matches that wording. (port of anomalyco/opencode#37848)
|
||||
"maximum allowed input length",
|
||||
]
|
||||
|
||||
# Model not found patterns
|
||||
|
|
|
|||
|
|
@ -2170,3 +2170,75 @@ class Test408RequestTimeout:
|
|||
assert result.should_fallback is True
|
||||
assert result.should_compress is False
|
||||
|
||||
|
||||
# ── Test: throttle vs overflow disambiguation + new overflow shapes ─────
|
||||
# Port of anomalyco/opencode#37848 (expand context overflow patterns +
|
||||
# rate-limit exclusion guard).
|
||||
|
||||
class TestThrottleVsOverflowDisambiguation:
|
||||
"""Throttle messages that mention tokens must NOT route to compression."""
|
||||
|
||||
def test_bedrock_throttling_too_many_tokens_is_rate_limit(self):
|
||||
# AWS Bedrock (and some proxies) surface throttling as
|
||||
# "Throttling error: Too many tokens, please wait before trying
|
||||
# again." — the "too many tokens" fragment sits in
|
||||
# _CONTEXT_OVERFLOW_PATTERNS, so before the "throttling" rate-limit
|
||||
# pattern this compressed a healthy session on every throttle.
|
||||
e = Exception(
|
||||
"Throttling error: Too many tokens, please wait before trying again."
|
||||
)
|
||||
result = classify_api_error(e, provider="bedrock", model="claude")
|
||||
assert result.reason == FailoverReason.rate_limit
|
||||
assert result.should_compress is False
|
||||
|
||||
def test_plain_too_many_tokens_still_overflow(self):
|
||||
# Without any throttle wording, "Too many tokens" remains a
|
||||
# context-overflow signal (Z.AI / GLM family wording).
|
||||
e = Exception("Too many tokens")
|
||||
result = classify_api_error(e, provider="zai", model="glm-5")
|
||||
assert result.reason == FailoverReason.context_overflow
|
||||
assert result.should_compress is True
|
||||
|
||||
|
||||
class TestExpandedOverflowPatterns:
|
||||
"""New provider overflow wordings route into compression recovery."""
|
||||
|
||||
def test_maximum_allowed_input_length_is_overflow(self):
|
||||
# Together/Fireworks-style wording — matched no pattern before.
|
||||
e = Exception(
|
||||
"Input length 131393 exceeds the maximum allowed input length "
|
||||
"of 131040 tokens."
|
||||
)
|
||||
result = classify_api_error(e, provider="together", model="m")
|
||||
assert result.reason == FailoverReason.context_overflow
|
||||
assert result.should_compress is True
|
||||
|
||||
def test_request_too_large_message_only_is_payload_too_large(self):
|
||||
# Anthropic's structured 413 type re-wrapped by a proxy with no
|
||||
# status attribute — was falling through to `unknown`.
|
||||
e = Exception(
|
||||
'{"error":{"type":"request_too_large",'
|
||||
'"message":"Request exceeds the maximum size"}}'
|
||||
)
|
||||
result = classify_api_error(e, provider="anthropic", model="m")
|
||||
assert result.reason == FailoverReason.payload_too_large
|
||||
assert result.should_compress is True
|
||||
|
||||
def test_longer_than_context_length_still_overflow(self):
|
||||
# Regression guard for wordings that already matched.
|
||||
e = Exception(
|
||||
"The input (516368 tokens) is longer than the model's context "
|
||||
"length (262144 tokens)."
|
||||
)
|
||||
result = classify_api_error(e, provider="openrouter", model="m")
|
||||
assert result.reason == FailoverReason.context_overflow
|
||||
|
||||
def test_configured_context_size_still_overflow(self):
|
||||
e = Exception(
|
||||
"Prompt has 5,958,968 tokens, but the configured context size "
|
||||
"is 256,000 tokens"
|
||||
)
|
||||
result = classify_api_error(e, provider="ollama", model="m")
|
||||
assert result.reason == FailoverReason.context_overflow
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue