tries to disable quarantined aws keys
This commit is contained in:
parent
994b30dcce
commit
edc0d094e2
|
@ -65,7 +65,7 @@ type ErrorGeneratorOptions = {
|
||||||
format: APIFormat | "unknown";
|
format: APIFormat | "unknown";
|
||||||
title: string;
|
title: string;
|
||||||
message: string;
|
message: string;
|
||||||
obj?: object;
|
obj?: Record<string, any>;
|
||||||
reqId: string | number | object;
|
reqId: string | number | object;
|
||||||
model?: string;
|
model?: string;
|
||||||
statusCode?: number;
|
statusCode?: number;
|
||||||
|
@ -95,6 +95,23 @@ export function tryInferFormat(body: any): APIFormat | "unknown" {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// avoid leaking upstream hostname on dns resolution error
|
||||||
|
function redactHostname(options: ErrorGeneratorOptions): ErrorGeneratorOptions {
|
||||||
|
if (!options.message.includes("getaddrinfo")) return options;
|
||||||
|
|
||||||
|
const redacted = { ...options };
|
||||||
|
redacted.message = "Could not resolve hostname";
|
||||||
|
|
||||||
|
if (typeof redacted.obj?.error === "object") {
|
||||||
|
redacted.obj = {
|
||||||
|
...redacted.obj,
|
||||||
|
error: { message: "Could not resolve hostname" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return redacted;
|
||||||
|
}
|
||||||
|
|
||||||
export function sendErrorToClient({
|
export function sendErrorToClient({
|
||||||
options,
|
options,
|
||||||
req,
|
req,
|
||||||
|
@ -104,27 +121,26 @@ export function sendErrorToClient({
|
||||||
req: express.Request;
|
req: express.Request;
|
||||||
res: express.Response;
|
res: express.Response;
|
||||||
}) {
|
}) {
|
||||||
const { format: inputFormat } = options;
|
const redactedOpts = redactHostname(options);
|
||||||
|
const { format: inputFormat } = redactedOpts;
|
||||||
|
|
||||||
// This is an error thrown before we know the format of the request, so we
|
|
||||||
// can't send a response in the format the client expects.
|
|
||||||
const format =
|
const format =
|
||||||
inputFormat === "unknown" ? tryInferFormat(req.body) : inputFormat;
|
inputFormat === "unknown" ? tryInferFormat(req.body) : inputFormat;
|
||||||
if (format === "unknown") {
|
if (format === "unknown") {
|
||||||
return res.status(options.statusCode || 400).json({
|
return res.status(redactedOpts.statusCode || 400).json({
|
||||||
error: options.message,
|
error: redactedOpts.message,
|
||||||
details: options.obj,
|
details: redactedOpts.obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const completion = buildSpoofedCompletion({ ...options, format });
|
const completion = buildSpoofedCompletion({ ...redactedOpts, format });
|
||||||
const event = buildSpoofedSSE({ ...options, format });
|
const event = buildSpoofedSSE({ ...redactedOpts, format });
|
||||||
const isStreaming =
|
const isStreaming =
|
||||||
req.isStreaming || req.body.stream === true || req.body.stream === "true";
|
req.isStreaming || req.body.stream === true || req.body.stream === "true";
|
||||||
|
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent) {
|
||||||
res.setHeader("x-oai-proxy-error", options.title);
|
res.setHeader("x-oai-proxy-error", redactedOpts.title);
|
||||||
res.setHeader("x-oai-proxy-error-status", options.statusCode || 500);
|
res.setHeader("x-oai-proxy-error-status", redactedOpts.statusCode || 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isStreaming) {
|
if (isStreaming) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* This file is fucking horrendous, sorry */
|
/* This file is fucking horrendous, sorry */
|
||||||
|
// TODO: extract all per-service error response handling into its own modules
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import * as http from "http";
|
import * as http from "http";
|
||||||
import { config } from "../../../config";
|
import { config } from "../../../config";
|
||||||
|
@ -225,7 +226,7 @@ const handleUpstreamErrors: ProxyResHandlerWithBody = async (
|
||||||
break;
|
break;
|
||||||
case "anthropic":
|
case "anthropic":
|
||||||
case "aws":
|
case "aws":
|
||||||
await handleAnthropicBadRequestError(req, errorPayload);
|
await handleAnthropicAwsBadRequestError(req, errorPayload);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assertNever(service);
|
assertNever(service);
|
||||||
|
@ -247,7 +248,9 @@ const handleUpstreamErrors: ProxyResHandlerWithBody = async (
|
||||||
);
|
);
|
||||||
keyPool.update(req.key!, { allowsMultimodality: false });
|
keyPool.update(req.key!, { allowsMultimodality: false });
|
||||||
await reenqueueRequest(req);
|
await reenqueueRequest(req);
|
||||||
throw new RetryableError("Claude request re-enqueued because key does not support multimodality.");
|
throw new RetryableError(
|
||||||
|
"Claude request re-enqueued because key does not support multimodality."
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
keyPool.disable(req.key!, "revoked");
|
keyPool.disable(req.key!, "revoked");
|
||||||
errorPayload.proxy_note = `Assigned API key is invalid or revoked, please try again.`;
|
errorPayload.proxy_note = `Assigned API key is invalid or revoked, please try again.`;
|
||||||
|
@ -347,7 +350,7 @@ const handleUpstreamErrors: ProxyResHandlerWithBody = async (
|
||||||
throw new HttpError(statusCode, errorPayload.error?.message);
|
throw new HttpError(statusCode, errorPayload.error?.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handleAnthropicBadRequestError(
|
async function handleAnthropicAwsBadRequestError(
|
||||||
req: Request,
|
req: Request,
|
||||||
errorPayload: ProxiedErrorPayload
|
errorPayload: ProxiedErrorPayload
|
||||||
) {
|
) {
|
||||||
|
@ -382,11 +385,13 @@ async function handleAnthropicBadRequestError(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDisabled = error?.message?.match(/organization has been disabled/i);
|
const isDisabled =
|
||||||
|
error?.message?.match(/organization has been disabled/i) ||
|
||||||
|
error?.message?.match(/^operation not allowed/i);
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
req.log.warn(
|
req.log.warn(
|
||||||
{ key: req.key?.hash, message: error?.message },
|
{ key: req.key?.hash, message: error?.message },
|
||||||
"Anthropic key has been disabled."
|
"Anthropic/AWS key has been disabled."
|
||||||
);
|
);
|
||||||
keyPool.disable(req.key!, "revoked");
|
keyPool.disable(req.key!, "revoked");
|
||||||
errorPayload.proxy_note = `Assigned key has been disabled. (${error?.message})`;
|
errorPayload.proxy_note = `Assigned key has been disabled. (${error?.message})`;
|
||||||
|
@ -512,7 +517,7 @@ async function handleOpenAIRateLimitError(
|
||||||
// keyPool.markRateLimited(req.key!);
|
// keyPool.markRateLimited(req.key!);
|
||||||
// break;
|
// break;
|
||||||
default:
|
default:
|
||||||
errorPayload.proxy_note = `This is likely a temporary error with OpenAI. Try again in a few seconds.`;
|
errorPayload.proxy_note = `This is likely a temporary error with the API. Try again in a few seconds.`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return errorPayload;
|
return errorPayload;
|
||||||
|
|
Loading…
Reference in New Issue