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

fix case where opencode wasn't retrying

Aiden Cline 2 месяцев назад
Родитель
Сommit
027d43b5ea

+ 1 - 15
packages/opencode/src/provider/transform.ts

@@ -1,5 +1,4 @@
 import type { APICallError, ModelMessage } from "ai"
-import { STATUS_CODES } from "http"
 import { unique } from "remeda"
 import type { JSONSchema } from "zod/v4/core"
 
@@ -318,19 +317,6 @@ export namespace ProviderTransform {
       )
     }
 
-    if (!error.responseBody || (error.statusCode && message !== STATUS_CODES[error.statusCode])) {
-      return message
-    }
-
-    try {
-      const body = JSON.parse(error.responseBody)
-      // try to extract common error message fields
-      const errMsg = body.message || body.error
-      if (errMsg && typeof errMsg === "string") {
-        return `${message}: ${errMsg}`
-      }
-    } catch {}
-
-    return `${message}: ${error.responseBody}`
+    return message
   }
 }

+ 24 - 1
packages/opencode/src/session/message-v2.ts

@@ -9,6 +9,8 @@ import { Snapshot } from "@/snapshot"
 import { fn } from "@/util/fn"
 import { Storage } from "@/storage/storage"
 import { ProviderTransform } from "@/provider/transform"
+import { STATUS_CODES } from "http"
+import { iife } from "@/util/iife"
 
 export namespace MessageV2 {
   export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({}))
@@ -739,7 +741,28 @@ export namespace MessageV2 {
           { cause: e },
         ).toObject()
       case APICallError.isInstance(e):
-        const message = ProviderTransform.error(ctx.providerID, e)
+        const message = iife(() => {
+          let msg = e.message
+          const transformed = ProviderTransform.error(ctx.providerID, e)
+          if (transformed !== msg) {
+            return transformed
+          }
+          if (!e.responseBody || (e.statusCode && msg !== STATUS_CODES[e.statusCode])) {
+            return msg
+          }
+
+          try {
+            const body = JSON.parse(e.responseBody)
+            // try to extract common error message fields
+            const errMsg = body.message || body.error
+            if (errMsg && typeof errMsg === "string") {
+              return `${msg}: ${errMsg}`
+            }
+          } catch {}
+
+          return `${msg}: ${e.responseBody}`
+        })
+
         return new MessageV2.APIError(
           {
             message,

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

@@ -333,13 +333,14 @@ export namespace SessionProcessor {
               error: e,
             })
             const error = MessageV2.fromError(e, { providerID: input.providerID })
-            if (error?.name === "APIError" && error.data.isRetryable) {
+            const retry = SessionRetry.retryable(error)
+            if (retry !== undefined) {
               attempt++
               const delay = SessionRetry.delay(attempt, error.name === "APIError" ? error : undefined)
               SessionStatus.set(input.sessionID, {
                 type: "retry",
                 attempt,
-                message: error.data.message.includes("Overloaded") ? "Provider is overloaded" : error.data.message,
+                message: retry,
                 next: Date.now() + delay,
               })
               await SessionRetry.sleep(delay, input.abort).catch(() => {})

+ 19 - 0
packages/opencode/src/session/retry.ts

@@ -1,3 +1,4 @@
+import type { NamedError } from "@opencode-ai/util/error"
 import { MessageV2 } from "./message-v2"
 
 export namespace SessionRetry {
@@ -51,4 +52,22 @@ export namespace SessionRetry {
 
     return Math.min(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1), RETRY_MAX_DELAY_NO_HEADERS)
   }
+
+  export function retryable(error: ReturnType<NamedError["toObject"]>) {
+    if (MessageV2.APIError.isInstance(error)) {
+      if (!error.data.isRetryable) return undefined
+      return error.data.message.includes("Overloaded") ? "Provider is overloaded" : error.data.message
+    }
+
+    if (typeof error.data?.message === "string") {
+      try {
+        const json = JSON.parse(error.data.message)
+        if (json.type === "error" && json.error?.type === "too_many_requests") {
+          return "Too Many Requests"
+        }
+      } catch {}
+    }
+
+    return undefined
+  }
 }