mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:20:43 +00:00
refactor: remove old provider error utility path
This commit is contained in:
@@ -1,37 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
assertOkOrThrowProviderError,
|
||||
extractProviderErrorDetail,
|
||||
extractProviderRequestId,
|
||||
} from "./provider-error-utils.js";
|
||||
|
||||
describe("provider error utils", () => {
|
||||
it("formats nested provider error details with request ids", async () => {
|
||||
const response = new Response(
|
||||
JSON.stringify({
|
||||
detail: {
|
||||
message: "Quota exceeded",
|
||||
status: "quota_exceeded",
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 429,
|
||||
headers: { "x-request-id": "req_123" },
|
||||
},
|
||||
);
|
||||
|
||||
await expect(assertOkOrThrowProviderError(response, "Provider API error")).rejects.toThrow(
|
||||
"Provider API error (429): Quota exceeded [code=quota_exceeded] [request_id=req_123]",
|
||||
);
|
||||
});
|
||||
|
||||
it("reads string error fields and fallback request id headers", async () => {
|
||||
const response = new Response(JSON.stringify({ error: "Invalid API key" }), {
|
||||
status: 401,
|
||||
headers: { "request-id": "fallback_req" },
|
||||
});
|
||||
|
||||
expect(await extractProviderErrorDetail(response)).toBe("Invalid API key");
|
||||
expect(extractProviderRequestId(response)).toBe("fallback_req");
|
||||
});
|
||||
});
|
||||
@@ -1,152 +0,0 @@
|
||||
export { asFiniteNumber } from "../shared/number-coercion.js";
|
||||
import { normalizeOptionalString as trimToUndefined } from "../shared/string-coerce.js";
|
||||
export { normalizeOptionalString as trimToUndefined } from "../shared/string-coerce.js";
|
||||
|
||||
export function asBoolean(value: unknown): boolean | undefined {
|
||||
return typeof value === "boolean" ? value : undefined;
|
||||
}
|
||||
|
||||
export function asObject(value: unknown): Record<string, unknown> | undefined {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export function truncateErrorDetail(detail: string, limit = 220): string {
|
||||
return detail.length <= limit ? detail : `${detail.slice(0, limit - 1)}…`;
|
||||
}
|
||||
|
||||
export async function readResponseTextLimited(
|
||||
response: Response,
|
||||
limitBytes = 16 * 1024,
|
||||
): Promise<string> {
|
||||
if (limitBytes <= 0) {
|
||||
return "";
|
||||
}
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let total = 0;
|
||||
let text = "";
|
||||
let reachedLimit = false;
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
if (!value || value.byteLength === 0) {
|
||||
continue;
|
||||
}
|
||||
const remaining = limitBytes - total;
|
||||
if (remaining <= 0) {
|
||||
reachedLimit = true;
|
||||
break;
|
||||
}
|
||||
const chunk = value.byteLength > remaining ? value.subarray(0, remaining) : value;
|
||||
total += chunk.byteLength;
|
||||
text += decoder.decode(chunk, { stream: true });
|
||||
if (total >= limitBytes) {
|
||||
reachedLimit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
text += decoder.decode();
|
||||
} finally {
|
||||
if (reachedLimit) {
|
||||
await reader.cancel().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
export function formatProviderErrorPayload(payload: unknown): string | undefined {
|
||||
const root = asObject(payload);
|
||||
const detailObject = asObject(root?.detail);
|
||||
const subject = asObject(root?.error) ?? detailObject ?? root;
|
||||
if (!subject) {
|
||||
return undefined;
|
||||
}
|
||||
const message =
|
||||
trimToUndefined(subject.message) ??
|
||||
trimToUndefined(subject.detail) ??
|
||||
trimToUndefined(root?.message) ??
|
||||
trimToUndefined(root?.error) ??
|
||||
trimToUndefined(root?.detail);
|
||||
const type = trimToUndefined(subject.type);
|
||||
const code = trimToUndefined(subject.code) ?? trimToUndefined(subject.status);
|
||||
const metadata = [type ? `type=${type}` : undefined, code ? `code=${code}` : undefined]
|
||||
.filter((value): value is string => Boolean(value))
|
||||
.join(", ");
|
||||
if (message && metadata) {
|
||||
return `${truncateErrorDetail(message)} [${metadata}]`;
|
||||
}
|
||||
if (message) {
|
||||
return truncateErrorDetail(message);
|
||||
}
|
||||
if (metadata) {
|
||||
return `[${metadata}]`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function extractProviderErrorDetail(response: Response): Promise<string | undefined> {
|
||||
const rawBody = trimToUndefined(await readResponseTextLimited(response));
|
||||
if (!rawBody) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return formatProviderErrorPayload(JSON.parse(rawBody)) ?? truncateErrorDetail(rawBody);
|
||||
} catch {
|
||||
return truncateErrorDetail(rawBody);
|
||||
}
|
||||
}
|
||||
|
||||
export function extractProviderRequestId(response: Response): string | undefined {
|
||||
return (
|
||||
trimToUndefined(response.headers.get("x-request-id")) ??
|
||||
trimToUndefined(response.headers.get("request-id"))
|
||||
);
|
||||
}
|
||||
|
||||
export function formatProviderHttpErrorMessage(params: {
|
||||
label: string;
|
||||
status: number;
|
||||
detail?: string;
|
||||
requestId?: string;
|
||||
}): string {
|
||||
const { label, status, detail, requestId } = params;
|
||||
return (
|
||||
`${label} (${status})` +
|
||||
(detail ? `: ${detail}` : "") +
|
||||
(requestId ? ` [request_id=${requestId}]` : "")
|
||||
);
|
||||
}
|
||||
|
||||
export async function createProviderHttpError(response: Response, label: string): Promise<Error> {
|
||||
const detail = await extractProviderErrorDetail(response);
|
||||
const requestId = extractProviderRequestId(response);
|
||||
return new Error(
|
||||
formatProviderHttpErrorMessage({
|
||||
label,
|
||||
status: response.status,
|
||||
detail,
|
||||
requestId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function assertOkOrThrowProviderError(
|
||||
response: Response,
|
||||
label: string,
|
||||
): Promise<void> {
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
throw await createProviderHttpError(response, label);
|
||||
}
|
||||
Reference in New Issue
Block a user