3513c45a02
The ZAI (Zhipu) provider emits 'Weekly/Monthly Limit Exhausted. Your limit will reset at YYYY-MM-DD HH:MM:SS' when the coding-plan subscription quota is hit. None of the existing quota regex patterns (/quota.?exceeded/, /usage\s+limit/, /exhausted\s+your\s+capacity/, /credit\s+balance.*too\s+low/, etc.) match the 'Limit Exhausted' phrasing, so the runtime-fallback never fires and the user is stuck on the dead model. Add /limit\s+exhausted/i to both pattern lists that gate fallback dispatch: - RETRYABLE_ERROR_PATTERNS in constants.ts (text-pattern path used by extractStatusCode + retryable scan) - classifyErrorType quota_exceeded branch in error-classifier.ts (typed classification path used by isRetryableError) The pattern is intentionally narrow: it requires the literal token 'Limit' followed by whitespace then 'Exhausted'. It matches the ZAI weekly, monthly, and combined Weekly/Monthly variants but does not collide with unrelated phrases such as 'context limit' or 'rate limit' that already have their own dedicated patterns. Regression coverage added to quota-error-classifier.regression.test.ts: - 'Weekly/Monthly Limit Exhausted. Your limit will reset at 2026-05-20 15:43:27' -> quota_exceeded + retryable=true - 'Weekly Limit Exhausted. Your limit will reset at 2026-05-28 10:30:00' -> quota_exceeded + retryable=true Verification: 11/11 quota-error-classifier.regression.test.ts pass (was 9 pass + 2 fail pre-fix). Broader runtime-fallback suite goes from 135/196 pass to 137/198 pass (the 61 pre-existing failures are unrelated to this change and reproduce on a clean upstream/dev checkout). bun run typecheck clean.
68 lines
1.8 KiB
TypeScript
68 lines
1.8 KiB
TypeScript
/**
|
|
* Runtime Fallback Hook - Constants
|
|
*
|
|
* Default values and configuration constants for the runtime fallback feature.
|
|
*/
|
|
|
|
import type { RuntimeFallbackConfig } from "../../config"
|
|
|
|
/**
|
|
* Default configuration values for runtime fallback
|
|
*/
|
|
export const DEFAULT_CONFIG: Required<RuntimeFallbackConfig> = {
|
|
enabled: false,
|
|
retry_on_errors: [429, 500, 502, 503, 504],
|
|
max_fallback_attempts: 3,
|
|
cooldown_seconds: 60,
|
|
timeout_seconds: 30,
|
|
notify_on_fallback: true,
|
|
}
|
|
|
|
/**
|
|
* Error patterns that indicate rate limiting or temporary failures
|
|
* These are checked in addition to HTTP status codes
|
|
*/
|
|
export const RETRYABLE_ERROR_PATTERNS = [
|
|
/rate.?limit/i,
|
|
/too.?many.?requests/i,
|
|
/quota\s+will\s+reset\s+after/i,
|
|
/quota.?exceeded/i,
|
|
/exceeded.*quota/i,
|
|
/usage\s*quota/i,
|
|
/exhausted\s+your\s+capacity/i,
|
|
/limit\s+exhausted/i,
|
|
/all\s+credentials\s+for\s+model/i,
|
|
/cool(?:ing)?\s+down/i,
|
|
/model.{0,20}?not.{0,10}?supported/i,
|
|
/model_not_supported/i,
|
|
/service.?unavailable/i,
|
|
/overloaded/i,
|
|
/temporarily.?unavailable/i,
|
|
/try.?again/i,
|
|
/(?:^|\s)429(?:\s|$)/,
|
|
/(?:^|\s)503(?:\s|$)/,
|
|
/(?:^|\s)529(?:\s|$)/,
|
|
/使用上限/,
|
|
/频率限制/,
|
|
/请求过于频繁/,
|
|
/暂时不可用/,
|
|
/服务不可用/,
|
|
/请稍后重试/,
|
|
]
|
|
|
|
/**
|
|
* Hook name for identification and logging
|
|
*/
|
|
export const HOOK_NAME = "runtime-fallback"
|
|
|
|
/**
|
|
* First-prompt watchdog: how long to wait for the first sign of progress
|
|
* (assistant text/reasoning/finish) from a subagent session before assuming
|
|
* the provider is silently stuck and dispatching the configured fallback.
|
|
*
|
|
* Tuned to be longer than typical first-token latency (well under 30s in
|
|
* practice) yet much shorter than the 30-minute outer poll timeout that
|
|
* would otherwise be the only safety net.
|
|
*/
|
|
export const DEFAULT_FIRST_PROMPT_WATCHDOG_MS = 90_000
|