Просмотр исходного кода

retry anthropic overloaded errors

Dax Raad 4 месяцев назад
Родитель
Сommit
9eabbe2e76

+ 2 - 2
packages/opencode/src/session/processor.ts

@@ -333,9 +333,9 @@ export namespace SessionProcessor {
               error: e,
               error: e,
             })
             })
             const error = MessageV2.fromError(e, { providerID: input.providerID })
             const error = MessageV2.fromError(e, { providerID: input.providerID })
-            if (error?.name === "APIError" && error.data.isRetryable) {
+            if ((error?.name === "APIError" && error.data.isRetryable) || error.data.message.includes("Overloaded")) {
               attempt++
               attempt++
-              const delay = SessionRetry.delay(error, attempt)
+              const delay = SessionRetry.delay(attempt, error.name === "APIError" ? error : undefined)
               SessionStatus.set(input.sessionID, {
               SessionStatus.set(input.sessionID, {
                 type: "retry",
                 type: "retry",
                 attempt,
                 attempt,

+ 24 - 22
packages/opencode/src/session/retry.ts

@@ -19,32 +19,34 @@ export namespace SessionRetry {
     })
     })
   }
   }
 
 
-  export function delay(error: MessageV2.APIError, attempt: number) {
-    const headers = error.data.responseHeaders
-    if (headers) {
-      const retryAfterMs = headers["retry-after-ms"]
-      if (retryAfterMs) {
-        const parsedMs = Number.parseFloat(retryAfterMs)
-        if (!Number.isNaN(parsedMs)) {
-          return parsedMs
+  export function delay(attempt: number, error?: MessageV2.APIError) {
+    if (error) {
+      const headers = error.data.responseHeaders
+      if (headers) {
+        const retryAfterMs = headers["retry-after-ms"]
+        if (retryAfterMs) {
+          const parsedMs = Number.parseFloat(retryAfterMs)
+          if (!Number.isNaN(parsedMs)) {
+            return parsedMs
+          }
         }
         }
-      }
 
 
-      const retryAfter = headers["retry-after"]
-      if (retryAfter) {
-        const parsedSeconds = Number.parseFloat(retryAfter)
-        if (!Number.isNaN(parsedSeconds)) {
-          // convert seconds to milliseconds
-          return Math.ceil(parsedSeconds * 1000)
-        }
-        // Try parsing as HTTP date format
-        const parsed = Date.parse(retryAfter) - Date.now()
-        if (!Number.isNaN(parsed) && parsed > 0) {
-          return Math.ceil(parsed)
+        const retryAfter = headers["retry-after"]
+        if (retryAfter) {
+          const parsedSeconds = Number.parseFloat(retryAfter)
+          if (!Number.isNaN(parsedSeconds)) {
+            // convert seconds to milliseconds
+            return Math.ceil(parsedSeconds * 1000)
+          }
+          // Try parsing as HTTP date format
+          const parsed = Date.parse(retryAfter) - Date.now()
+          if (!Number.isNaN(parsed) && parsed > 0) {
+            return Math.ceil(parsed)
+          }
         }
         }
-      }
 
 
-      return RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1)
+        return RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1)
+      }
     }
     }
 
 
     return Math.min(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1), RETRY_MAX_DELAY_NO_HEADERS)
     return Math.min(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1), RETRY_MAX_DELAY_NO_HEADERS)

+ 9 - 9
packages/opencode/test/session/retry.test.ts

@@ -13,49 +13,49 @@ function apiError(headers?: Record<string, string>): MessageV2.APIError {
 describe("session.retry.delay", () => {
 describe("session.retry.delay", () => {
   test("caps delay at 30 seconds when headers missing", () => {
   test("caps delay at 30 seconds when headers missing", () => {
     const error = apiError()
     const error = apiError()
-    const delays = Array.from({ length: 10 }, (_, index) => SessionRetry.delay(error, index + 1))
+    const delays = Array.from({ length: 10 }, (_, index) => SessionRetry.delay(index + 1, error))
     expect(delays).toStrictEqual([2000, 4000, 8000, 16000, 30000, 30000, 30000, 30000, 30000, 30000])
     expect(delays).toStrictEqual([2000, 4000, 8000, 16000, 30000, 30000, 30000, 30000, 30000, 30000])
   })
   })
 
 
   test("prefers retry-after-ms when shorter than exponential", () => {
   test("prefers retry-after-ms when shorter than exponential", () => {
     const error = apiError({ "retry-after-ms": "1500" })
     const error = apiError({ "retry-after-ms": "1500" })
-    expect(SessionRetry.delay(error, 4)).toBe(1500)
+    expect(SessionRetry.delay(4, error)).toBe(1500)
   })
   })
 
 
   test("uses retry-after seconds when reasonable", () => {
   test("uses retry-after seconds when reasonable", () => {
     const error = apiError({ "retry-after": "30" })
     const error = apiError({ "retry-after": "30" })
-    expect(SessionRetry.delay(error, 3)).toBe(30000)
+    expect(SessionRetry.delay(3, error)).toBe(30000)
   })
   })
 
 
   test("accepts http-date retry-after values", () => {
   test("accepts http-date retry-after values", () => {
     const date = new Date(Date.now() + 20000).toUTCString()
     const date = new Date(Date.now() + 20000).toUTCString()
     const error = apiError({ "retry-after": date })
     const error = apiError({ "retry-after": date })
-    const d = SessionRetry.delay(error, 1)
+    const d = SessionRetry.delay(1, error)
     expect(d).toBeGreaterThanOrEqual(19000)
     expect(d).toBeGreaterThanOrEqual(19000)
     expect(d).toBeLessThanOrEqual(20000)
     expect(d).toBeLessThanOrEqual(20000)
   })
   })
 
 
   test("ignores invalid retry hints", () => {
   test("ignores invalid retry hints", () => {
     const error = apiError({ "retry-after": "not-a-number" })
     const error = apiError({ "retry-after": "not-a-number" })
-    expect(SessionRetry.delay(error, 1)).toBe(2000)
+    expect(SessionRetry.delay(1, error)).toBe(2000)
   })
   })
 
 
   test("ignores malformed date retry hints", () => {
   test("ignores malformed date retry hints", () => {
     const error = apiError({ "retry-after": "Invalid Date String" })
     const error = apiError({ "retry-after": "Invalid Date String" })
-    expect(SessionRetry.delay(error, 1)).toBe(2000)
+    expect(SessionRetry.delay(1, error)).toBe(2000)
   })
   })
 
 
   test("ignores past date retry hints", () => {
   test("ignores past date retry hints", () => {
     const pastDate = new Date(Date.now() - 5000).toUTCString()
     const pastDate = new Date(Date.now() - 5000).toUTCString()
     const error = apiError({ "retry-after": pastDate })
     const error = apiError({ "retry-after": pastDate })
-    expect(SessionRetry.delay(error, 1)).toBe(2000)
+    expect(SessionRetry.delay(1, error)).toBe(2000)
   })
   })
 
 
   test("uses retry-after values even when exceeding 10 minutes with headers", () => {
   test("uses retry-after values even when exceeding 10 minutes with headers", () => {
     const error = apiError({ "retry-after": "50" })
     const error = apiError({ "retry-after": "50" })
-    expect(SessionRetry.delay(error, 1)).toBe(50000)
+    expect(SessionRetry.delay(1, error)).toBe(50000)
 
 
     const longError = apiError({ "retry-after-ms": "700000" })
     const longError = apiError({ "retry-after-ms": "700000" })
-    expect(SessionRetry.delay(longError, 1)).toBe(700000)
+    expect(SessionRetry.delay(1, longError)).toBe(700000)
   })
   })
 })
 })