fix(shared): add stable retry status dedupe helpers
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, test } from "bun:test"
|
||||||
|
|
||||||
|
import { extractRetryAttempt, extractRetryStatusModel, normalizeRetryStatusMessage } from "./retry-status-utils"
|
||||||
|
|
||||||
|
describe("retry-status-utils", () => {
|
||||||
|
test("extracts retry attempt from explicit status attempt", () => {
|
||||||
|
//#given
|
||||||
|
const attempt = 6
|
||||||
|
|
||||||
|
//#when
|
||||||
|
const result = extractRetryAttempt(attempt, "The usage limit has been reached [retrying in 27s attempt #6]")
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(result).toBe(6)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("extracts retry model from cooldown status text", () => {
|
||||||
|
//#given
|
||||||
|
const message = "All credentials for model claude-opus-4-6 are cooling down [retrying in 7m 56s attempt #1]"
|
||||||
|
|
||||||
|
//#when
|
||||||
|
const result = extractRetryStatusModel(message)
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(result).toBe("claude-opus-4-6")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("normalizes countdown jitter to a stable cooldown class", () => {
|
||||||
|
//#given
|
||||||
|
const firstMessage = "All credentials for model claude-opus-4-6 are cooling down [retrying in 7m 56s attempt #1]"
|
||||||
|
const secondMessage = "All credentials for model claude-opus-4-6 are cooling down [retrying in 7m 55s attempt #1]"
|
||||||
|
|
||||||
|
//#when
|
||||||
|
const firstResult = normalizeRetryStatusMessage(firstMessage)
|
||||||
|
const secondResult = normalizeRetryStatusMessage(secondMessage)
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(firstResult).toBe("cooldown")
|
||||||
|
expect(secondResult).toBe("cooldown")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
const RETRY_COUNTDOWN_PATTERN = /\[\s*retrying\s+in[^\]]*\]/gi
|
||||||
|
|
||||||
|
function collapseWhitespace(value: string): string {
|
||||||
|
return value.toLowerCase().replace(/\s+/g, " ").trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractRetryAttempt(attempt: number | undefined, message: string): number | "?" {
|
||||||
|
if (typeof attempt === "number" && Number.isFinite(attempt)) {
|
||||||
|
return attempt
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user