Files
opencode/packages/ai/test/provider-error.test.ts
2026-07-19 22:22:56 -05:00

77 lines
3.3 KiB
TypeScript

import { describe, expect, test } from "bun:test"
import { isContextOverflow } from "../src"
import { classifyProviderFailure } from "../src/provider-error"
describe("provider error classification", () => {
test("classifies provider token limit messages as context overflow", () => {
const messages = [
"tokens in request more than max tokens allowed",
'{"error":{"type":"request_too_large","message":"Request exceeds the maximum size"}}',
"Requested token count exceeds the model's maximum context length of 131072 tokens.",
"Input length (265330) exceeds model's maximum context length (262144).",
"Input length 131393 exceeds the maximum allowed input length of 131040 tokens.",
"The input (516368 tokens) is longer than the model's context length (262144 tokens).",
"Prompt has 5,958,968 tokens, but the configured context size is 256,000 tokens",
"Too many tokens",
"Token limit exceeded",
]
expect(messages.every(isContextOverflow)).toBe(true)
})
test("does not classify rate limits as context overflow", () => {
const messages = [
"Throttling error: Too many tokens, please wait before trying again.",
"Rate limit exceeded, please retry after 30 seconds.",
"Too many requests. Please slow down.",
]
expect(messages.some(isContextOverflow)).toBe(false)
})
test("classifies V1 plain-text rate limit fallbacks", () => {
expect(
[
"Request rate increased too quickly",
"Rate limit exceeded, please try again later",
"Too many requests, please slow down",
].map((message) => classifyProviderFailure({ message })._tag),
).toEqual(["RateLimit", "RateLimit", "RateLimit"])
})
test("classifies V1 JSON rate limit fallbacks", () => {
expect(
[
'{"type":"error","error":{"type":"too_many_requests"}}',
'{"type":"error","error":{"code":"rate_limit_exceeded"}}',
'{"code":"bad_request","error":{"code":"rate_limit_exceeded"}}',
'{"type":"error","error":{"code":"unknown","type":"too_many_requests"}}',
].map((message) => classifyProviderFailure({ message })._tag),
).toEqual(["RateLimit", "RateLimit", "RateLimit", "RateLimit"])
})
test("classifies V1 overloaded provider codes", () => {
expect(
['{"code":"resource_exhausted"}', '{"code":"service_unavailable"}'].map(
(message) => classifyProviderFailure({ message })._tag,
),
).toEqual(["ProviderInternal", "ProviderInternal"])
})
test("classifies nested provider codes when a top-level code is also present", () => {
expect(
[
'{"code":"bad_request","error":{"code":"usage_not_included"}}',
'{"code":"bad_request","error":{"code":"server_error"}}',
'{"code":"bad_request","error":{"type":"invalid_request_error"}}',
].map((message) => classifyProviderFailure({ message })._tag),
).toEqual(["QuotaExceeded", "ProviderInternal", "InvalidRequest"])
})
test("keeps unknown and malformed provider payloads non-retryable", () => {
expect(classifyProviderFailure({ message: '{"error":{"message":"no_kv_space"}}' })._tag).toBe("UnknownProvider")
expect(classifyProviderFailure({ message: '{"type":"error","error":{"code":123}}' })._tag).toBe("UnknownProvider")
expect(classifyProviderFailure({ message: "not-json" })._tag).toBe("UnknownProvider")
})
})