fix(console): parse provider SSE error bodies
This commit is contained in:
parent
a78adb1b09
commit
1e1dc56a09
3 changed files with 61 additions and 1 deletions
|
|
@ -48,6 +48,7 @@ import { localeFromRequest } from "~/lib/language"
|
||||||
import { createModelTpmLimiter } from "./modelTpmLimiter"
|
import { createModelTpmLimiter } from "./modelTpmLimiter"
|
||||||
import { createModelTpsLimiter } from "./modelTpsLimiter"
|
import { createModelTpsLimiter } from "./modelTpsLimiter"
|
||||||
import { accumulateUsage, HOT_WORKSPACES } from "./usageBatcher"
|
import { accumulateUsage, HOT_WORKSPACES } from "./usageBatcher"
|
||||||
|
import { readProviderError } from "./providerError"
|
||||||
|
|
||||||
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
||||||
type RetryOptions = {
|
type RetryOptions = {
|
||||||
|
|
@ -248,7 +249,7 @@ export async function handler(
|
||||||
|
|
||||||
// Handle non-streaming response
|
// Handle non-streaming response
|
||||||
if (!isStream || [400, 404, 429].includes(res.status)) {
|
if (!isStream || [400, 404, 429].includes(res.status)) {
|
||||||
const json = await res.json()
|
const json = res.status === 200 ? await res.json() : await readProviderError(res)
|
||||||
await rateLimiter?.track()
|
await rateLimiter?.track()
|
||||||
const usage = providerInfo.extractUsage(json)
|
const usage = providerInfo.extractUsage(json)
|
||||||
if (usage) {
|
if (usage) {
|
||||||
|
|
|
||||||
33
packages/console/app/src/routes/zen/util/providerError.ts
Normal file
33
packages/console/app/src/routes/zen/util/providerError.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
export async function readProviderError(res: Response) {
|
||||||
|
const body = await res.text()
|
||||||
|
const json = parseJson(body)
|
||||||
|
if (json && typeof json === "object") return json as Record<string, any>
|
||||||
|
return parseProviderErrorBody(body, res.statusText)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseProviderErrorBody(body: string, statusText: string) {
|
||||||
|
const text = body.trim()
|
||||||
|
const sseData = text
|
||||||
|
.split(/\r?\n/)
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.filter((line) => line.startsWith("data:"))
|
||||||
|
.map((line) => line.slice(5).trim())
|
||||||
|
.filter((line) => line && line !== "[DONE]")
|
||||||
|
|
||||||
|
const parsed = sseData.map(parseJson).find((item) => item && typeof item === "object")
|
||||||
|
if (parsed) return parsed as Record<string, any>
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
message: sseData[0] || text || statusText,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJson(value: string) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value) as unknown
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
26
packages/console/app/test/providerError.test.ts
Normal file
26
packages/console/app/test/providerError.test.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { parseProviderErrorBody } from "../src/routes/zen/util/providerError"
|
||||||
|
|
||||||
|
describe("provider error parsing", () => {
|
||||||
|
test("parses SSE error bodies from upstream rate limits", () => {
|
||||||
|
expect(
|
||||||
|
parseProviderErrorBody(
|
||||||
|
'event:error\ndata: {"error":{"type":"rate_limit_error","message":"Too many requests"}}\n\n',
|
||||||
|
"Too Many Requests",
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
error: {
|
||||||
|
type: "rate_limit_error",
|
||||||
|
message: "Too many requests",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("wraps plain text errors in provider error shape", () => {
|
||||||
|
expect(parseProviderErrorBody("overloaded", "Too Many Requests")).toEqual({
|
||||||
|
error: {
|
||||||
|
message: "overloaded",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Add table
Add a link
Reference in a new issue