test(runtime-fallback): cover localized provider errors

This commit is contained in:
YeonGyu-Kim
2026-05-15 22:25:20 +09:00
parent c206b16865
commit 3e9b125fd5
4 changed files with 76 additions and 8 deletions
+6 -8
View File
@@ -41,14 +41,12 @@ export const RETRYABLE_ERROR_PATTERNS = [
/(?:^|\s)429(?:\s|$)/,
/(?:^|\s)503(?:\s|$)/,
/(?:^|\s)529(?:\s|$)/,
// Chinese rate-limit / quota patterns (Zhipu, etc.)
/使用上限/, // "usage limit" — Zhipu: "已达到 5 小时的使用上限"
/频率限制/, // "rate limit" — generic Chinese rate-limit
/请求过于频繁/, // "too many requests" — common Chinese 429 message
/暂时不可用/, // "temporarily unavailable"
/服务不可用/, // "service unavailable"
/请稍后重试/, // "please try again later"
/使用上限/,
/频率限制/,
/请求过于频繁/,
/暂时不可用/,
/服务不可用/,
/请稍后重试/,
]
/**
@@ -59,6 +59,44 @@ describe("runtime-fallback error classifier", () => {
expect(retryable).toBe(true)
})
test("treats localized transient provider messages as retryable", () => {
//#given
const errors = [
{ message: "请求过于频繁,请稍后重试" },
{ message: "服务暂时不可用" },
{ message: "触发频率限制" },
]
//#when
const retryable = errors.map((error) => isRetryableError(error, [429, 503, 529]))
//#then
expect(retryable).toEqual([true, true, true])
})
test("classifies localized quota exhaustion messages as quota_exceeded", () => {
//#given
const errors = [
{ message: "已达到 5 小时的使用上限" },
{ message: "已达到每日调用限制" },
{ message: "额度不足" },
{ message: "账户余额不足" },
{ message: "免费额度已耗尽" },
]
//#when
const classifications = errors.map((error) => classifyErrorType(error))
//#then
expect(classifications).toEqual([
"quota_exceeded",
"quota_exceeded",
"quota_exceeded",
"quota_exceeded",
"quota_exceeded",
])
})
test("classifies ProviderModelNotFoundError as model_not_found", () => {
//#given
const error = {
+31
View File
@@ -216,6 +216,21 @@ describe("model-error-classifier", () => {
expect(result).toBe(true)
})
test("treats localized transient provider messages as retryable", () => {
//#given
const errors = [
{ message: "请求过于频繁,请稍后重试" },
{ message: "服务暂时不可用" },
{ message: "触发频率限制" },
]
//#when
const results = errors.map((error) => shouldRetryError(error))
//#then
expect(results).toEqual([true, true, true])
})
test("treats subscription quota message as non-retryable", () => {
//#given
const error = { message: "Subscription quota exceeded. You can continue using free models." }
@@ -227,6 +242,22 @@ describe("model-error-classifier", () => {
expect(result).toBe(false)
})
test("treats localized quota exhaustion messages as non-retryable stop errors", () => {
//#given
const errors = [
{ message: "已达到 5 小时的使用上限" },
{ message: "额度不足" },
{ message: "账户余额不足" },
{ message: "免费额度已耗尽" },
]
//#when
const results = errors.map((error) => shouldRetryError(error))
//#then
expect(results).toEqual([false, false, false, false])
})
test("treats HTTP 429 rate limit message as retryable", () => {
//#given
const error = { message: "429 Too Many Requests: rate limit reached" }
+1
View File
@@ -67,6 +67,7 @@ const RETRYABLE_MESSAGE_PATTERNS = [
"balance",
"temporarily unavailable",
"try again",
"请稍后重试",
"503",
"502",
"504",