Merge pull request #3891 from wjiuxing/feat/chinese-error-patterns
This commit is contained in:
@@ -41,6 +41,12 @@ export const RETRYABLE_ERROR_PATTERNS = [
|
|||||||
/(?:^|\s)429(?:\s|$)/,
|
/(?:^|\s)429(?:\s|$)/,
|
||||||
/(?:^|\s)503(?:\s|$)/,
|
/(?:^|\s)503(?:\s|$)/,
|
||||||
/(?:^|\s)529(?:\s|$)/,
|
/(?:^|\s)529(?:\s|$)/,
|
||||||
|
/使用上限/,
|
||||||
|
/频率限制/,
|
||||||
|
/请求过于频繁/,
|
||||||
|
/暂时不可用/,
|
||||||
|
/服务不可用/,
|
||||||
|
/请稍后重试/,
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -59,6 +59,44 @@ describe("runtime-fallback error classifier", () => {
|
|||||||
expect(retryable).toBe(true)
|
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", () => {
|
test("classifies ProviderModelNotFoundError as model_not_found", () => {
|
||||||
//#given
|
//#given
|
||||||
const error = {
|
const error = {
|
||||||
|
|||||||
@@ -143,6 +143,11 @@ export function classifyErrorType(error: unknown): string | undefined {
|
|||||||
/payment.?required/i.test(message) ||
|
/payment.?required/i.test(message) ||
|
||||||
/usage\s+limit/i.test(message) ||
|
/usage\s+limit/i.test(message) ||
|
||||||
/credit\s+balance.*too\s+low/i.test(message) ||
|
/credit\s+balance.*too\s+low/i.test(message) ||
|
||||||
|
/使用上限/.test(message) ||
|
||||||
|
/达到.*限制/.test(message) ||
|
||||||
|
/额度.*不足/.test(message) ||
|
||||||
|
/余额.*不足/.test(message) ||
|
||||||
|
/已耗尽/.test(message) ||
|
||||||
isLocalizedQuotaExhaustionMessage(message)
|
isLocalizedQuotaExhaustionMessage(message)
|
||||||
) {
|
) {
|
||||||
return "quota_exceeded"
|
return "quota_exceeded"
|
||||||
|
|||||||
@@ -216,6 +216,21 @@ describe("model-error-classifier", () => {
|
|||||||
expect(result).toBe(true)
|
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", () => {
|
test("treats subscription quota message as non-retryable", () => {
|
||||||
//#given
|
//#given
|
||||||
const error = { message: "Subscription quota exceeded. You can continue using free models." }
|
const error = { message: "Subscription quota exceeded. You can continue using free models." }
|
||||||
@@ -227,6 +242,22 @@ describe("model-error-classifier", () => {
|
|||||||
expect(result).toBe(false)
|
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", () => {
|
test("treats HTTP 429 rate limit message as retryable", () => {
|
||||||
//#given
|
//#given
|
||||||
const error = { message: "429 Too Many Requests: rate limit reached" }
|
const error = { message: "429 Too Many Requests: rate limit reached" }
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ const RETRYABLE_MESSAGE_PATTERNS = [
|
|||||||
"balance",
|
"balance",
|
||||||
"temporarily unavailable",
|
"temporarily unavailable",
|
||||||
"try again",
|
"try again",
|
||||||
|
"请稍后重试",
|
||||||
"503",
|
"503",
|
||||||
"502",
|
"502",
|
||||||
"504",
|
"504",
|
||||||
@@ -74,6 +75,11 @@ const RETRYABLE_MESSAGE_PATTERNS = [
|
|||||||
"529",
|
"529",
|
||||||
"selected provider is forbidden",
|
"selected provider is forbidden",
|
||||||
"provider is forbidden",
|
"provider is forbidden",
|
||||||
|
// Chinese retryable patterns (Zhipu, etc.)
|
||||||
|
"频率限制", // "rate limit"
|
||||||
|
"请求过于频繁", // "too many requests"
|
||||||
|
"暂时不可用", // "temporarily unavailable"
|
||||||
|
"服务不可用", // "service unavailable"
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,6 +112,10 @@ const STOP_MESSAGE_PATTERNS = [
|
|||||||
"in arrears",
|
"in arrears",
|
||||||
"fair use policy",
|
"fair use policy",
|
||||||
"recharge and try",
|
"recharge and try",
|
||||||
|
"使用上限",
|
||||||
|
"额度不足",
|
||||||
|
"余额不足",
|
||||||
|
"已耗尽",
|
||||||
]
|
]
|
||||||
|
|
||||||
const AUTO_RETRY_GATE_PATTERNS = [
|
const AUTO_RETRY_GATE_PATTERNS = [
|
||||||
|
|||||||
Reference in New Issue
Block a user