mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-02 23:23:45 +00:00
tweak: add new ContextOverflowError type (#12777)
This commit is contained in:
@@ -7,8 +7,7 @@ import { LSP } from "../lsp"
|
||||
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 { ProviderError } from "@/provider/error"
|
||||
import { iife } from "@/util/iife"
|
||||
import { type SystemError } from "bun"
|
||||
import type { Provider } from "@/provider/provider"
|
||||
@@ -35,6 +34,10 @@ export namespace MessageV2 {
|
||||
}),
|
||||
)
|
||||
export type APIError = z.infer<typeof APIError.Schema>
|
||||
export const ContextOverflowError = NamedError.create(
|
||||
"ContextOverflowError",
|
||||
z.object({ message: z.string(), responseBody: z.string().optional() }),
|
||||
)
|
||||
|
||||
const PartBase = z.object({
|
||||
id: z.string(),
|
||||
@@ -361,6 +364,7 @@ export namespace MessageV2 {
|
||||
NamedError.Unknown.Schema,
|
||||
OutputLengthError.Schema,
|
||||
AbortedError.Schema,
|
||||
ContextOverflowError.Schema,
|
||||
APIError.Schema,
|
||||
])
|
||||
.optional(),
|
||||
@@ -711,13 +715,6 @@ export namespace MessageV2 {
|
||||
return result
|
||||
}
|
||||
|
||||
const isOpenAiErrorRetryable = (e: APICallError) => {
|
||||
const status = e.statusCode
|
||||
if (!status) return e.isRetryable
|
||||
// openai sometimes returns 404 for models that are actually available
|
||||
return status === 404 || e.isRetryable
|
||||
}
|
||||
|
||||
export function fromError(e: unknown, ctx: { providerID: string }) {
|
||||
switch (true) {
|
||||
case e instanceof DOMException && e.name === "AbortError":
|
||||
@@ -751,45 +748,28 @@ export namespace MessageV2 {
|
||||
{ cause: e },
|
||||
).toObject()
|
||||
case APICallError.isInstance(e):
|
||||
const message = iife(() => {
|
||||
let msg = e.message
|
||||
if (msg === "") {
|
||||
if (e.responseBody) return e.responseBody
|
||||
if (e.statusCode) {
|
||||
const err = STATUS_CODES[e.statusCode]
|
||||
if (err) return err
|
||||
}
|
||||
return "Unknown error"
|
||||
}
|
||||
const transformed = ProviderTransform.error(ctx.providerID, e)
|
||||
if (transformed !== msg) {
|
||||
return transformed
|
||||
}
|
||||
if (!e.responseBody || (e.statusCode && msg !== STATUS_CODES[e.statusCode])) {
|
||||
return msg
|
||||
}
|
||||
const parsed = ProviderError.parseAPICallError({
|
||||
providerID: ctx.providerID,
|
||||
error: e,
|
||||
})
|
||||
if (parsed.type === "context_overflow") {
|
||||
return new MessageV2.ContextOverflowError(
|
||||
{
|
||||
message: parsed.message,
|
||||
responseBody: parsed.responseBody,
|
||||
},
|
||||
{ cause: e },
|
||||
).toObject()
|
||||
}
|
||||
|
||||
try {
|
||||
const body = JSON.parse(e.responseBody)
|
||||
// try to extract common error message fields
|
||||
const errMsg = body.message || body.error || body.error?.message
|
||||
if (errMsg && typeof errMsg === "string") {
|
||||
return `${msg}: ${errMsg}`
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return `${msg}: ${e.responseBody}`
|
||||
}).trim()
|
||||
|
||||
const metadata = e.url ? { url: e.url } : undefined
|
||||
return new MessageV2.APIError(
|
||||
{
|
||||
message,
|
||||
statusCode: e.statusCode,
|
||||
isRetryable: ctx.providerID.startsWith("openai") ? isOpenAiErrorRetryable(e) : e.isRetryable,
|
||||
responseHeaders: e.responseHeaders,
|
||||
responseBody: e.responseBody,
|
||||
metadata,
|
||||
message: parsed.message,
|
||||
statusCode: parsed.statusCode,
|
||||
isRetryable: parsed.isRetryable,
|
||||
responseHeaders: parsed.responseHeaders,
|
||||
responseBody: parsed.responseBody,
|
||||
metadata: parsed.metadata,
|
||||
},
|
||||
{ cause: e },
|
||||
).toObject()
|
||||
@@ -797,72 +777,27 @@ export namespace MessageV2 {
|
||||
return new NamedError.Unknown({ message: e.toString() }, { cause: e }).toObject()
|
||||
default:
|
||||
try {
|
||||
const json = iife(() => {
|
||||
if (typeof e === "string") {
|
||||
try {
|
||||
return JSON.parse(e)
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof e === "object" && e !== null) {
|
||||
return e
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
if (json) {
|
||||
const responseBody = JSON.stringify(json)
|
||||
// Handle Responses API mid stream style errors
|
||||
if (json?.type === "error") {
|
||||
switch (json?.error?.code) {
|
||||
case "context_length_exceeded":
|
||||
return new MessageV2.APIError(
|
||||
{
|
||||
message: "Input exceeds context window of this model",
|
||||
isRetryable: false,
|
||||
responseBody,
|
||||
},
|
||||
{
|
||||
cause: e,
|
||||
},
|
||||
).toObject()
|
||||
case "insufficient_quota":
|
||||
return new MessageV2.APIError(
|
||||
{
|
||||
message: "Quota exceeded. Check your plan and billing details.",
|
||||
isRetryable: false,
|
||||
responseBody,
|
||||
},
|
||||
{
|
||||
cause: e,
|
||||
},
|
||||
).toObject()
|
||||
case "usage_not_included":
|
||||
return new MessageV2.APIError(
|
||||
{
|
||||
message:
|
||||
"To use Codex with your ChatGPT plan, upgrade to Plus: https://chatgpt.com/explore/plus.",
|
||||
isRetryable: false,
|
||||
responseBody,
|
||||
},
|
||||
{
|
||||
cause: e,
|
||||
},
|
||||
).toObject()
|
||||
case "invalid_prompt":
|
||||
return new MessageV2.APIError(
|
||||
{
|
||||
message: json?.error?.message || "Invalid prompt.",
|
||||
isRetryable: false,
|
||||
responseBody,
|
||||
},
|
||||
{
|
||||
cause: e,
|
||||
},
|
||||
).toObject()
|
||||
}
|
||||
const parsed = ProviderError.parseStreamError(e)
|
||||
if (parsed) {
|
||||
if (parsed.type === "context_overflow") {
|
||||
return new MessageV2.ContextOverflowError(
|
||||
{
|
||||
message: parsed.message,
|
||||
responseBody: parsed.responseBody,
|
||||
},
|
||||
{ cause: e },
|
||||
).toObject()
|
||||
}
|
||||
return new MessageV2.APIError(
|
||||
{
|
||||
message: parsed.message,
|
||||
isRetryable: parsed.isRetryable,
|
||||
responseBody: parsed.responseBody,
|
||||
},
|
||||
{
|
||||
cause: e,
|
||||
},
|
||||
).toObject()
|
||||
}
|
||||
} catch {}
|
||||
return new NamedError.Unknown({ message: JSON.stringify(e) }, { cause: e }).toObject()
|
||||
|
||||
@@ -59,6 +59,9 @@ export namespace SessionRetry {
|
||||
}
|
||||
|
||||
export function retryable(error: ReturnType<NamedError["toObject"]>) {
|
||||
// DO NOT retry context overflow errors
|
||||
if (MessageV2.ContextOverflowError.isInstance(error)) return undefined
|
||||
|
||||
if (MessageV2.APIError.isInstance(error)) {
|
||||
if (!error.data.isRetryable) return undefined
|
||||
return error.data.message.includes("Overloaded") ? "Provider is overloaded" : error.data.message
|
||||
|
||||
Reference in New Issue
Block a user