Spaces:
Paused
Paused
| 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.", | |
| }, | |
| }) | |
| ); | |
| }; | |