File size: 2,845 Bytes
f81f327 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
import { Request, Response } from "express";
import * as http from "http";
import * as httpProxy from "http-proxy";
import { logger } from "../logger";
import { keys } from "../keys";
/** Handle and rewrite response to proxied requests to OpenAI */
export const handleResponse = (
proxyRes: http.IncomingMessage,
req: Request,
res: Response
) => {
const statusCode = proxyRes.statusCode || 500;
if (statusCode >= 400) {
let body = "";
proxyRes.on("data", (chunk) => (body += chunk));
proxyRes.on("end", () => {
let errorPayload: any = {
error: "Proxy couldn't parse error from OpenAI",
};
const canTryAgain = keys.anyAvailable()
? "You can try again to get a different key."
: "There are no more keys available.";
try {
errorPayload = JSON.parse(body);
} catch (err) {
logger.error({ error: err }, errorPayload.error);
res.json(errorPayload);
return;
}
if (statusCode === 401) {
// Key is invalid or was revoked
logger.warn(
`OpenAI key is invalid or revoked. Keyhash ${req.key?.hash}`
);
keys.disable(req.key!);
const message = `The OpenAI key is invalid or revoked. ${canTryAgain}`;
errorPayload.proxy_note = message;
} else if (statusCode === 429) {
// Rate limit exceeded
// Annoyingly they send this for:
// - Quota exceeded, key is totally dead
// - Rate limit exceeded, key is still good but backoff needed
// - Model overloaded, their server is overloaded
if (errorPayload.error?.type === "insufficient_quota") {
logger.warn(`OpenAI key is exhausted. Keyhash ${req.key?.hash}`);
keys.disable(req.key!);
const message = `The OpenAI key is exhausted. ${canTryAgain}`;
errorPayload.proxy_note = message;
} else {
logger.warn(
{ errorCode: errorPayload.error?.type },
`OpenAI rate limit exceeded or model overloaded. Keyhash ${req.key?.hash}`
);
}
}
res.status(statusCode).json(errorPayload);
});
} else {
// Increment key's usage count
keys.incrementPrompt(req.key?.hash);
Object.keys(proxyRes.headers).forEach((key) => {
res.setHeader(key, proxyRes.headers[key] as string);
});
proxyRes.pipe(res);
}
};
export const onError: httpProxy.ErrorCallback = (err, _req, res) => {
logger.error({ error: err }, "Error proxying to OpenAI");
(res as http.ServerResponse).writeHead(500, {
"Content-Type": "application/json",
});
res.end(
JSON.stringify({
error: {
type: "proxy_error",
message: err.message,
proxy_note:
"Reverse proxy encountered an error before it could reach OpenAI.",
},
})
);
};
|