import { parseBearerString } from "https://esm.town/v/andreterron/parseBearerString";
import { API_URL } from "https://esm.town/v/std/API_URL?v=5";
import { OpenAIUsage } from "https://esm.town/v/std/OpenAIUsage";
import { RateLimit } from "npm:@rlimit/http";
const client = new OpenAIUsage();
const allowedPathnames = [
"/",
"/v1/chat/completions",
];
const rlimit = new RateLimit({
namespace: Deno.env.get("rlimit_namespace"),
maximum: 10,
interval: "1m",
});
type User = { id: string; username: string; tier: string };
export default async function(req: Request): Promise<Response> {
const { pathname, search } = new URL(req.url);
if (!allowedPathnames.includes(pathname)) {
return new Response("Path not supported", { status: 404 });
}
const authHeader = req.headers.get("Proxy-Authorization") || req.headers.get("Authorization");
const token = authHeader ? parseBearerString(authHeader) : undefined;
const meRes = await fetch(`${API_URL}/v1/me`, { headers: { Authorization: `Bearer ${token}` } });
if (!meRes.ok) {
return new Response("Unauthorized", { status: 401 });
}
const user: User = await meRes.json();
const { ok } = await rlimit.check(`user:${user.id}`);
if (!ok) {
return new Response("Too Many Requests", { status: 429 });
}
const url = new URL("." + pathname, "https://api.openai.com");
url.search = search;
const headers = new Headers(req.headers);
headers.set("Host", url.hostname);
headers.set("Authorization", `Bearer ${Deno.env.get("OPENAI_API_KEY")}`);
headers.set("OpenAI-Organization", Deno.env.get("OPENAI_API_ORG"));
const modifiedBody = await limitFreeModel(req, user);
client.writeUsage({
userId: user.id,
handle: user.username,
tokens: 0,
tier: user.tier,
model: modifiedBody.model,
}).catch((e) => {
console.error(e);
});
const openAIRes = await fetch(url, {
method: req.method,
headers,
body: JSON.stringify(modifiedBody),
redirect: "manual",
});
const res = new Response(openAIRes.body, openAIRes);
res.headers.delete("openai-organization");
return res;
}
const isExpensiveModel = (model?: string) => {
if (!model) return false;
return model.includes("gpt-4") && !model.includes("-mini")
}
async function limitFreeModel(req: Request, user: User) {
const input = await req.json();
let model = "gpt-4o-mini";
if (user.tier == "pro" && isExpensiveModel(input.model)) {
const count = await client.recentGpt4Usage(user.id);
if (count <= 10) {
model = input.model;
}
}
return {
...input,
model,
};
}