fix(core): recover v2 context overflow (#31005)
This commit is contained in:
parent
c814f84c87
commit
820c984d47
18 changed files with 473 additions and 88 deletions
|
|
@ -2,6 +2,7 @@ import { APICallError } from "ai"
|
|||
import { STATUS_CODES } from "http"
|
||||
import { iife } from "@/util/iife"
|
||||
import type { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { isContextOverflow } from "@opencode-ai/llm"
|
||||
|
||||
export class HeaderTimeoutError extends Error {
|
||||
public override readonly name = "ProviderHeaderTimeoutError"
|
||||
|
|
@ -19,30 +20,6 @@ export class ResponseStreamError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
// Adapted from overflow detection patterns in:
|
||||
// https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/overflow.ts
|
||||
const OVERFLOW_PATTERNS = [
|
||||
/prompt is too long/i, // Anthropic
|
||||
/input is too long for requested model/i, // Amazon Bedrock
|
||||
/exceeds the context window/i, // OpenAI (Completions + Responses API message text)
|
||||
/input token count.*exceeds the maximum/i, // Google (Gemini)
|
||||
/maximum prompt length is \d+/i, // xAI (Grok)
|
||||
/reduce the length of the messages/i, // Groq
|
||||
/maximum context length is \d+ tokens/i, // OpenRouter, DeepSeek, vLLM
|
||||
/exceeds the limit of \d+/i, // GitHub Copilot
|
||||
/exceeds the available context size/i, // llama.cpp server
|
||||
/greater than the context length/i, // LM Studio
|
||||
/context window exceeds limit/i, // MiniMax
|
||||
/exceeded model token limit/i, // Kimi For Coding, Moonshot
|
||||
/context[_ ]length[_ ]exceeded/i, // Generic fallback
|
||||
/request entity too large/i, // HTTP 413
|
||||
/context length is only \d+ tokens/i, // vLLM
|
||||
/input length.*exceeds.*context length/i, // vLLM
|
||||
/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error
|
||||
/too large for model with \d+ maximum context length/i, // Mistral
|
||||
/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text
|
||||
]
|
||||
|
||||
function isOpenAiErrorRetryable(e: APICallError) {
|
||||
const status = e.statusCode
|
||||
if (!status) return e.isRetryable
|
||||
|
|
@ -52,15 +29,6 @@ function isOpenAiErrorRetryable(e: APICallError) {
|
|||
|
||||
// Providers not reliably handled in this function:
|
||||
// - z.ai: can accept overflow silently (needs token-count/context-window checks)
|
||||
function isOverflow(message: string) {
|
||||
if (OVERFLOW_PATTERNS.some((p) => p.test(message))) return true
|
||||
|
||||
// Providers/status patterns handled outside of regex list:
|
||||
// - Cerebras: often returns "400 (no body)" / "413 (no body)"
|
||||
// - Mistral: often returns "400 (no body)" / "413 (no body)"
|
||||
return /^4(00|13)\s*(status code)?\s*\(no body\)/i.test(message)
|
||||
}
|
||||
|
||||
function message(providerID: ProviderV2.ID, e: APICallError) {
|
||||
return iife(() => {
|
||||
const msg = e.message
|
||||
|
|
@ -197,7 +165,7 @@ export type ParsedAPICallError =
|
|||
export function parseAPICallError(input: { providerID: ProviderV2.ID; error: APICallError }): ParsedAPICallError {
|
||||
const m = message(input.providerID, input.error)
|
||||
const body = json(input.error.responseBody)
|
||||
if (isOverflow(m) || input.error.statusCode === 413 || body?.error?.code === "context_length_exceeded") {
|
||||
if (isContextOverflow(m) || input.error.statusCode === 413 || body?.error?.code === "context_length_exceeded") {
|
||||
return {
|
||||
type: "context_overflow",
|
||||
message: m,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue