mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(agent): classify 429 'overloaded' bodies as overloaded, not rate_limit
Z.AI / Zhipu reuse HTTP 429 for server-wide overload. The 429 status path classified these unconditionally as rate_limit with should_rotate_credential=True, so an overloaded provider exhausted the credential pool after two errors — fatal for a single-key user, who has nothing to rotate to. The credential is valid; the server is just busy. Disambiguate the 429 body against a shared _OVERLOADED_PATTERNS list and route overload language to FailoverReason.overloaded (retryable, no rotation), matching the existing 503/529 path and the message-only path (#52890). Genuine rate limits (no overload language) still rotate. Extracted the inline overloaded tuple #52890 added into the shared _OVERLOADED_PATTERNS constant so the status-code and message paths use one list. Closes #14038.
This commit is contained in:
parent
16192103f4
commit
38e7bd8a08
2 changed files with 63 additions and 5 deletions
|
|
@ -386,6 +386,30 @@ class TestClassifyApiError:
|
|||
assert result.retryable is True
|
||||
assert result.should_rotate_credential is False
|
||||
|
||||
def test_429_with_overloaded_body_is_overloaded_not_rate_limit(self):
|
||||
"""Z.AI / Zhipu reuse HTTP 429 for server-wide overload. The credential
|
||||
is valid — the server is just busy — so it must classify as overloaded
|
||||
(back off + retry the same key), NOT rate_limit (which would rotate and
|
||||
exhaust the pool, doing nothing for a single-key user). (#14038)"""
|
||||
e = MockAPIError(
|
||||
"The service may be temporarily overloaded, please try again later",
|
||||
status_code=429,
|
||||
)
|
||||
result = classify_api_error(e, provider="zai")
|
||||
assert result.reason == FailoverReason.overloaded
|
||||
assert result.retryable is True
|
||||
assert result.should_rotate_credential is False
|
||||
|
||||
def test_429_normal_rate_limit_still_rotates(self):
|
||||
"""Guard: a genuine 429 rate limit (no overload language) must still
|
||||
classify as rate_limit and rotate the credential. (#14038)"""
|
||||
e = MockAPIError(
|
||||
"Rate limit exceeded: too many requests", status_code=429
|
||||
)
|
||||
result = classify_api_error(e, provider="zai")
|
||||
assert result.reason == FailoverReason.rate_limit
|
||||
assert result.should_rotate_credential is True
|
||||
|
||||
# ── 5xx that are actually request-validation errors ──
|
||||
# Some OpenAI-compatible gateways (e.g. codex.nekos.me) return
|
||||
# request-validation failures with a 5xx status. These are
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue