diff --git a/src/shared/retry-status-utils.ts b/src/shared/retry-status-utils.ts index 3b93ddf02..c4514b1c8 100644 --- a/src/shared/retry-status-utils.ts +++ b/src/shared/retry-status-utils.ts @@ -1,19 +1,51 @@ -export function normalizeRetryStatusMessage(message: string): string { - return message - .replace(/\[retrying in [^\]]*attempt\s*#\d+\]/gi, "[retrying]") - .replace(/retrying in\s+[^(]*attempt\s*#\d+/gi, "retrying") - .replace(/\s+/g, " ") - .trim() - .toLowerCase() +const RETRY_COUNTDOWN_PATTERN = /\[\s*retrying\s+in[^\]]*\]/gi + +function collapseWhitespace(value: string): string { + return value.toLowerCase().replace(/\s+/g, " ").trim() } -export function extractRetryAttempt(statusAttempt: unknown, message: string): string { - if (typeof statusAttempt === "number" && Number.isFinite(statusAttempt)) { - return String(statusAttempt) +export function extractRetryAttempt(attempt: number | undefined, message: string): number | "?" { + if (typeof attempt === "number" && Number.isFinite(attempt)) { + return attempt } - const attemptMatch = message.match(/attempt\s*#\s*(\d+)/i) - if (attemptMatch?.[1]) { - return attemptMatch[1] - } - return "?" + + const parsedAttempt = message.match(/attempt\s*#\s*(\d+)/i)?.[1] + return parsedAttempt ? Number.parseInt(parsedAttempt, 10) : "?" +} + +export function extractRetryStatusModel(message: string): string | undefined { + return message.match(/model\s+([a-z0-9._/-]+)(?=\s+(?:are|is)\b)/i)?.[1]?.toLowerCase() +} + +export function normalizeRetryStatusMessage(message: string): string { + const normalizedMessage = collapseWhitespace(message.replace(RETRY_COUNTDOWN_PATTERN, " ")) + if (!normalizedMessage) { + return "retry" + } + + if (/all\s+credentials\s+for\s+model|cool(?:ing)?\s+down|cooldown|exhausted\s+your\s+capacity/.test(normalizedMessage)) { + return "cooldown" + } + + if (/too\s+many\s+requests/.test(normalizedMessage)) { + return "too-many-requests" + } + + if (/quota\s+will\s+reset\s+after|quota\s*exceeded/.test(normalizedMessage)) { + return "quota" + } + + if (/usage\s+limit\s+has\s+been\s+reached|limit\s+reached/.test(normalizedMessage)) { + return "usage-limit" + } + + if (/rate\s+limit/.test(normalizedMessage)) { + return "rate-limit" + } + + if (/service.?unavailable|temporarily.?unavailable|overloaded/.test(normalizedMessage)) { + return "service-unavailable" + } + + return normalizedMessage }