Merge pull request #231 from pikasTech/fix/sub2api-weekly-limit-filter

fix: 过滤 Codex weekly limit 账号提示
This commit is contained in:
Lyon
2026-06-09 18:52:52 +08:00
committed by GitHub
4 changed files with 20 additions and 4 deletions
@@ -15,11 +15,11 @@ pool:
durationMinutes: 120 durationMinutes: 120
description: Credential/auth failures should use the longest cooldown. description: Credential/auth failures should use the longest cooldown.
- statusCode: 403 - statusCode: 403
keywords: [forbidden, access denied, quota, billing, capacity, recovered upstream error] keywords: [forbidden, access denied, quota, billing, capacity, weekly limit, less than 10% of your weekly limit left, run /status for a breakdown, recovered upstream error]
durationMinutes: 120 durationMinutes: 120
description: Permission, quota, or account-state failures should use the longest cooldown. description: Permission, quota, or account-state failures should use the longest cooldown.
- statusCode: 429 - statusCode: 429
keywords: [capacity, rate limit, rate_limit, quota, too many requests, overloaded, resource_exhausted, recovered upstream error] keywords: [capacity, rate limit, rate_limit, quota, weekly limit, less than 10% of your weekly limit left, run /status for a breakdown, too many requests, overloaded, resource_exhausted, recovered upstream error]
durationMinutes: 10 durationMinutes: 10
description: Capacity and rate-limit responses are often temporary; start with a ten-minute cooldown and use another account. description: Capacity and rate-limit responses are often temporary; start with a ten-minute cooldown and use another account.
- statusCode: 500 - statusCode: 500
@@ -48,6 +48,14 @@ if (parsed.pool?.defaultTempUnschedulable?.enabled === true) {
const gateway502Rule = rules.find((rule) => rule.statusCode === 502); const gateway502Rule = rules.find((rule) => rule.statusCode === 502);
const gateway502Keywords = new Set((gateway502Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase())); const gateway502Keywords = new Set((gateway502Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase()));
assertCondition(gateway502Keywords.has("recovered upstream error"), "502 temporary-unschedulable rule must catch recovered upstream error wrappers", gateway502Rule); assertCondition(gateway502Keywords.has("recovered upstream error"), "502 temporary-unschedulable rule must catch recovered upstream error wrappers", gateway502Rule);
const accountState403Rule = rules.find((rule) => rule.statusCode === 403);
const quota429Rule = rules.find((rule) => rule.statusCode === 429);
const accountState403Keywords = new Set((accountState403Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase()));
const quota429Keywords = new Set((quota429Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase()));
for (const keyword of ["weekly limit", "less than 10% of your weekly limit left", "run /status for a breakdown"]) {
assertCondition(accountState403Keywords.has(keyword), "403 temporary-unschedulable rule must catch Codex weekly-limit account-state prompts", { keyword, accountState403Rule });
assertCondition(quota429Keywords.has(keyword), "429 temporary-unschedulable rule must catch Codex weekly-limit quota prompts", { keyword, quota429Rule });
}
} }
assertCondition(typeof parsed.localCodex?.responsesSmokeModel === "string" && parsed.localCodex.responsesSmokeModel.length > 0, "localCodex.responsesSmokeModel must be declared for Responses smoke validation", parsed.localCodex); assertCondition(typeof parsed.localCodex?.responsesSmokeModel === "string" && parsed.localCodex.responsesSmokeModel.length > 0, "localCodex.responsesSmokeModel must be declared for Responses smoke validation", parsed.localCodex);
@@ -60,6 +68,7 @@ console.log(JSON.stringify({
"optional WebSocket mode overrides use supported values", "optional WebSocket mode overrides use supported values",
"temporary unschedulable rules are structurally valid when enabled", "temporary unschedulable rules are structurally valid when enabled",
"generic recovered upstream error wrappers are caught by cooldown rules", "generic recovered upstream error wrappers are caught by cooldown rules",
"Codex weekly-limit prompts are caught by account-state and quota cooldown rules",
"Responses smoke model is YAML-declared", "Responses smoke model is YAML-declared",
], ],
})); }));
@@ -27,6 +27,12 @@ assertCondition(rules.every((rule, index) => JSON.stringify(rule.keywords ?? [])
assertCondition(rules.every((rule, index) => rule.description === policy.rules[index]?.description), "rules must preserve policy descriptions", { policy, credentials }); assertCondition(rules.every((rule, index) => rule.description === policy.rules[index]?.description), "rules must preserve policy descriptions", { policy, credentials });
assertCondition(!("pool_mode" in credentials), "pool_mode must not be enabled because it retries the same account instead of cooling it down", credentials); assertCondition(!("pool_mode" in credentials), "pool_mode must not be enabled because it retries the same account instead of cooling it down", credentials);
assertCondition(!("api_key" in credentials) && !("base_url" in credentials), "temporary-unschedulable rendering must not include secrets or endpoints", credentials); assertCondition(!("api_key" in credentials) && !("base_url" in credentials), "temporary-unschedulable rendering must not include secrets or endpoints", credentials);
const accountState403Rule = rules.find((rule) => rule.error_code === 403);
const quota429Rule = rules.find((rule) => rule.error_code === 429);
for (const keyword of ["weekly limit", "less than 10% of your weekly limit left", "run /status for a breakdown"]) {
assertCondition(accountState403Rule?.keywords?.includes(keyword), "403 rendered rule must preserve Codex weekly-limit account-state keyword", { keyword, accountState403Rule });
assertCondition(quota429Rule?.keywords?.includes(keyword), "429 rendered rule must preserve Codex weekly-limit quota keyword", { keyword, quota429Rule });
}
const disabled = renderSub2ApiTempUnschedulableCredentials({ enabled: false, rules: policy.rules }) as { const disabled = renderSub2ApiTempUnschedulableCredentials({ enabled: false, rules: policy.rules }) as {
temp_unschedulable_enabled?: boolean; temp_unschedulable_enabled?: boolean;
@@ -41,6 +47,7 @@ console.log(JSON.stringify({
checks: [ checks: [
"temporary unschedulable policy renders to Sub2API credential field names", "temporary unschedulable policy renders to Sub2API credential field names",
"temporary unschedulable rendering follows the input policy without hard-coded policy gates", "temporary unschedulable rendering follows the input policy without hard-coded policy gates",
"Codex weekly-limit prompt keywords render into 403 and 429 cooldown rules",
"disabled policies clear runtime rules", "disabled policies clear runtime rules",
], ],
})); }));
+2 -2
View File
@@ -660,13 +660,13 @@ export function defaultCodexTempUnschedulablePolicy(): CodexTempUnschedulablePol
}, },
{ {
statusCode: 403, statusCode: 403,
keywords: ["forbidden", "access denied", "quota", "billing", "capacity", "recovered upstream error"], keywords: ["forbidden", "access denied", "quota", "billing", "capacity", "weekly limit", "less than 10% of your weekly limit left", "run /status for a breakdown", "recovered upstream error"],
durationMinutes: 120, durationMinutes: 120,
description: "Permission, quota, or account-state failures should use the longest cooldown.", description: "Permission, quota, or account-state failures should use the longest cooldown.",
}, },
{ {
statusCode: 429, statusCode: 429,
keywords: ["capacity", "rate limit", "rate_limit", "quota", "too many requests", "overloaded", "resource_exhausted", "recovered upstream error"], keywords: ["capacity", "rate limit", "rate_limit", "quota", "weekly limit", "less than 10% of your weekly limit left", "run /status for a breakdown", "too many requests", "overloaded", "resource_exhausted", "recovered upstream error"],
durationMinutes: 10, durationMinutes: 10,
description: "Capacity and rate-limit responses are often temporary; start with a ten-minute cooldown and use another account.", description: "Capacity and rate-limit responses are often temporary; start with a ten-minute cooldown and use another account.",
}, },