feat: support gemini pro

This commit is contained in:
Hk-Gosuto
2023-12-20 19:36:59 +08:00
parent 4044891f85
commit ee08ab4070
14 changed files with 817 additions and 26 deletions

View File

@@ -76,3 +76,54 @@ export function auth(req: NextRequest) {
error: false,
};
}
export function googleAuth(req: NextRequest) {
const authToken = req.headers.get("Authorization") ?? "";
// check if it is openai api key or user token
const { accessCode, apiKey } = parseApiKey(authToken);
const hashedCode = md5.hash(accessCode ?? "").trim();
const serverConfig = getServerSideConfig();
console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
console.log("[Auth] got access code:", accessCode);
console.log("[Auth] hashed access code:", hashedCode);
console.log("[User IP] ", getIP(req));
console.log("[Time] ", new Date().toLocaleString());
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !apiKey) {
return {
error: true,
msg: !accessCode ? "empty access code" : "wrong access code",
};
}
if (serverConfig.hideUserApiKey && !!apiKey) {
return {
error: true,
msg: "you are not allowed to access openai with your own api key",
};
}
// if user does not provide an api key, inject system api key
if (!apiKey) {
const serverApiKey = serverConfig.googleApiKey;
if (serverApiKey) {
console.log("[Auth] use system api key");
req.headers.set(
"Authorization",
`${serverConfig.isAzure ? "" : "Bearer "}${serverApiKey}`,
);
} else {
console.log("[Auth] admin did not provide an api key");
}
} else {
console.log("[Auth] use user api key");
}
return {
error: false,
};
}

View File

@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from "next/server";
import { getServerSideConfig } from "../config/server";
import { DEFAULT_MODELS, OPENAI_BASE_URL } from "../constant";
import { DEFAULT_MODELS, GOOGLE_BASE_URL, OPENAI_BASE_URL } from "../constant";
import { collectModelTable } from "../utils/model";
import { makeAzurePath } from "../azure";
@@ -118,3 +118,73 @@ export async function requestOpenai(req: NextRequest) {
clearTimeout(timeoutId);
}
}
export async function requestGoogleGemini(req: NextRequest) {
const controller = new AbortController();
const authValue =
req.headers.get("Authorization")?.replace("Bearer ", "") ?? "";
const authHeaderName = "x-goog-api-key";
console.log(req.nextUrl);
let path = `${req.nextUrl.pathname}`.replaceAll("/api/google/", "");
let baseUrl = serverConfig.googleBaseUrl || GOOGLE_BASE_URL;
if (!baseUrl.startsWith("http")) {
baseUrl = `https://${baseUrl}`;
}
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.slice(0, -1);
}
console.log("[Proxy] ", path);
console.log("[Base Url]", baseUrl);
// this fix [Org ID] undefined in server side if not using custom point
if (serverConfig.openaiOrgId !== undefined) {
console.log("[Org ID]", serverConfig.openaiOrgId);
}
const timeoutId = setTimeout(
() => {
controller.abort();
},
10 * 60 * 1000,
);
const fetchUrl = `${baseUrl}/${path}?alt=sse`;
const fetchOptions: RequestInit = {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
[authHeaderName]: authValue,
},
method: req.method,
body: req.body,
// to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
redirect: "manual",
// @ts-ignore
duplex: "half",
signal: controller.signal,
};
try {
const res = await fetch(fetchUrl, fetchOptions);
// to prevent browser prompt for credentials
const newHeaders = new Headers(res.headers);
newHeaders.delete("www-authenticate");
// to disable nginx buffering
newHeaders.set("X-Accel-Buffering", "no");
return new Response(res.body, {
status: res.status,
statusText: res.statusText,
headers: newHeaders,
});
} finally {
clearTimeout(timeoutId);
}
}

View File

@@ -0,0 +1,69 @@
import { prettyObject } from "@/app/utils/format";
import { NextRequest, NextResponse } from "next/server";
import { auth, googleAuth } from "../../auth";
import { requestGoogleGemini } from "../../common";
async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
console.log("[OpenAI Route] params ", params);
if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
}
const subpath = params.path.join("/");
// if (!ALLOWD_PATH.has(subpath)) {
// console.log("[OpenAI Route] forbidden path ", subpath);
// return NextResponse.json(
// {
// error: true,
// msg: "you are not allowed to request " + subpath,
// },
// {
// status: 403,
// },
// );
// }
const authResult = googleAuth(req);
if (authResult.error) {
return NextResponse.json(authResult, {
status: 401,
});
}
try {
const response = await requestGoogleGemini(req);
return response;
} catch (e) {
console.error("[OpenAI] ", e);
return NextResponse.json(prettyObject(e));
}
}
export const GET = handle;
export const POST = handle;
export const runtime = "edge";
export const preferredRegion = [
"arn1",
"bom1",
"cdg1",
"cle1",
"cpt1",
"dub1",
"fra1",
"gru1",
"hnd1",
"iad1",
"icn1",
"kix1",
"lhr1",
"pdx1",
"sfo1",
"sin1",
"syd1",
];

View File

@@ -123,7 +123,7 @@ export class PDFBrowser extends Tool {
undefined,
runManager?.getChild("vectorstore"),
);
context = formatDocumentsAsString(results, "\n");
context = formatDocumentsAsString(results);
}
const input = `Text:${context}\n\nI need ${