refator: google
This commit is contained in:
parent
ec95292209
commit
b3cfaf1420
|
@ -1,7 +1,15 @@
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { auth } from "../../auth";
|
import { auth } from "../../auth";
|
||||||
import { getServerSideConfig } from "@/app/config/server";
|
import { getServerSideConfig } from "@/app/config/server";
|
||||||
import { GEMINI_BASE_URL, Google, ModelProvider } from "@/app/constant";
|
import {
|
||||||
|
ApiPath,
|
||||||
|
GEMINI_BASE_URL,
|
||||||
|
Google,
|
||||||
|
ModelProvider,
|
||||||
|
} from "@/app/constant";
|
||||||
|
import { prettyObject } from "@/app/utils/format";
|
||||||
|
|
||||||
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
async function handle(
|
async function handle(
|
||||||
req: NextRequest,
|
req: NextRequest,
|
||||||
|
@ -13,32 +21,6 @@ async function handle(
|
||||||
return NextResponse.json({ body: "OK" }, { status: 200 });
|
return NextResponse.json({ body: "OK" }, { status: 200 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
|
||||||
|
|
||||||
let baseUrl = serverConfig.googleUrl || GEMINI_BASE_URL;
|
|
||||||
|
|
||||||
if (!baseUrl.startsWith("http")) {
|
|
||||||
baseUrl = `https://${baseUrl}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseUrl.endsWith("/")) {
|
|
||||||
baseUrl = baseUrl.slice(0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = `${req.nextUrl.pathname}`.replaceAll("/api/google/", "");
|
|
||||||
|
|
||||||
console.log("[Proxy] ", path);
|
|
||||||
console.log("[Base Url]", baseUrl);
|
|
||||||
|
|
||||||
const timeoutId = setTimeout(
|
|
||||||
() => {
|
|
||||||
controller.abort();
|
|
||||||
},
|
|
||||||
10 * 60 * 1000,
|
|
||||||
);
|
|
||||||
|
|
||||||
const authResult = auth(req, ModelProvider.GeminiPro);
|
const authResult = auth(req, ModelProvider.GeminiPro);
|
||||||
if (authResult.error) {
|
if (authResult.error) {
|
||||||
return NextResponse.json(authResult, {
|
return NextResponse.json(authResult, {
|
||||||
|
@ -49,9 +31,9 @@ async function handle(
|
||||||
const bearToken = req.headers.get("Authorization") ?? "";
|
const bearToken = req.headers.get("Authorization") ?? "";
|
||||||
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
|
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
|
||||||
|
|
||||||
const key = token ? token : serverConfig.googleApiKey;
|
const apiKey = token ? token : serverConfig.googleApiKey;
|
||||||
|
|
||||||
if (!key) {
|
if (!apiKey) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
error: true,
|
error: true,
|
||||||
|
@ -62,10 +44,63 @@ async function handle(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
const response = await request(req, apiKey);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[Google] ", e);
|
||||||
|
return NextResponse.json(prettyObject(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const fetchUrl = `${baseUrl}/${path}?key=${key}${
|
export const GET = handle;
|
||||||
req?.nextUrl?.searchParams?.get("alt") == "sse" ? "&alt=sse" : ""
|
export const POST = handle;
|
||||||
|
|
||||||
|
export const runtime = "edge";
|
||||||
|
export const preferredRegion = [
|
||||||
|
"bom1",
|
||||||
|
"cle1",
|
||||||
|
"cpt1",
|
||||||
|
"gru1",
|
||||||
|
"hnd1",
|
||||||
|
"iad1",
|
||||||
|
"icn1",
|
||||||
|
"kix1",
|
||||||
|
"pdx1",
|
||||||
|
"sfo1",
|
||||||
|
"sin1",
|
||||||
|
"syd1",
|
||||||
|
];
|
||||||
|
|
||||||
|
async function request(req: NextRequest, apiKey: string) {
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
let baseUrl = serverConfig.googleUrl || GEMINI_BASE_URL;
|
||||||
|
|
||||||
|
let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.Google, "");
|
||||||
|
|
||||||
|
if (!baseUrl.startsWith("http")) {
|
||||||
|
baseUrl = `https://${baseUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseUrl.endsWith("/")) {
|
||||||
|
baseUrl = baseUrl.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[Proxy] ", path);
|
||||||
|
console.log("[Base Url]", baseUrl);
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(
|
||||||
|
() => {
|
||||||
|
controller.abort();
|
||||||
|
},
|
||||||
|
10 * 60 * 1000,
|
||||||
|
);
|
||||||
|
const fetchUrl = `${baseUrl}${path}?key=${apiKey}${
|
||||||
|
req?.nextUrl?.searchParams?.get("alt") === "sse" ? "&alt=sse" : ""
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
console.log("[Fetch Url] ", fetchUrl);
|
||||||
const fetchOptions: RequestInit = {
|
const fetchOptions: RequestInit = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
@ -97,22 +132,3 @@ async function handle(
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GET = handle;
|
|
||||||
export const POST = handle;
|
|
||||||
|
|
||||||
export const runtime = "edge";
|
|
||||||
export const preferredRegion = [
|
|
||||||
"bom1",
|
|
||||||
"cle1",
|
|
||||||
"cpt1",
|
|
||||||
"gru1",
|
|
||||||
"hnd1",
|
|
||||||
"iad1",
|
|
||||||
"icn1",
|
|
||||||
"kix1",
|
|
||||||
"pdx1",
|
|
||||||
"sfo1",
|
|
||||||
"sin1",
|
|
||||||
"syd1",
|
|
||||||
];
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Google, REQUEST_TIMEOUT_MS } from "@/app/constant";
|
import { ApiPath, Google, REQUEST_TIMEOUT_MS } from "@/app/constant";
|
||||||
import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
|
import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
|
||||||
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
||||||
import { getClientConfig } from "@/app/config/client";
|
import { getClientConfig } from "@/app/config/client";
|
||||||
|
@ -16,6 +16,34 @@ import {
|
||||||
} from "@/app/utils";
|
} from "@/app/utils";
|
||||||
|
|
||||||
export class GeminiProApi implements LLMApi {
|
export class GeminiProApi implements LLMApi {
|
||||||
|
path(path: string): string {
|
||||||
|
const accessStore = useAccessStore.getState();
|
||||||
|
|
||||||
|
let baseUrl = "";
|
||||||
|
if (accessStore.useCustomConfig) {
|
||||||
|
baseUrl = accessStore.googleUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseUrl.length === 0) {
|
||||||
|
const isApp = !!getClientConfig()?.isApp;
|
||||||
|
baseUrl = isApp
|
||||||
|
? DEFAULT_API_HOST + `/api/proxy/google?key=${accessStore.googleApiKey}`
|
||||||
|
: ApiPath.Google;
|
||||||
|
}
|
||||||
|
if (baseUrl.endsWith("/")) {
|
||||||
|
baseUrl = baseUrl.slice(0, baseUrl.length - 1);
|
||||||
|
}
|
||||||
|
if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.Google)) {
|
||||||
|
baseUrl = "https://" + baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[Proxy Endpoint] ", baseUrl, path);
|
||||||
|
|
||||||
|
let chatPath = [baseUrl, path].join("/");
|
||||||
|
|
||||||
|
chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse";
|
||||||
|
return chatPath;
|
||||||
|
}
|
||||||
extractMessage(res: any) {
|
extractMessage(res: any) {
|
||||||
console.log("[Response] gemini-pro response: ", res);
|
console.log("[Response] gemini-pro response: ", res);
|
||||||
|
|
||||||
|
@ -108,30 +136,13 @@ export class GeminiProApi implements LLMApi {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const accessStore = useAccessStore.getState();
|
|
||||||
|
|
||||||
let baseUrl = "";
|
|
||||||
|
|
||||||
if (accessStore.useCustomConfig) {
|
|
||||||
baseUrl = accessStore.googleUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isApp = !!getClientConfig()?.isApp;
|
|
||||||
|
|
||||||
let shouldStream = !!options.config.stream;
|
let shouldStream = !!options.config.stream;
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
options.onController?.(controller);
|
options.onController?.(controller);
|
||||||
try {
|
try {
|
||||||
if (!baseUrl && isApp) {
|
// https://github.com/google-gemini/cookbook/blob/main/quickstarts/rest/Streaming_REST.ipynb
|
||||||
baseUrl = DEFAULT_API_HOST + "/api/proxy/google/";
|
const chatPath = this.path(Google.ChatPath(modelConfig.model));
|
||||||
}
|
|
||||||
baseUrl = `${baseUrl}/${Google.ChatPath(modelConfig.model)}`.replaceAll(
|
|
||||||
"//",
|
|
||||||
"/",
|
|
||||||
);
|
|
||||||
if (isApp) {
|
|
||||||
baseUrl += `?key=${accessStore.googleApiKey}`;
|
|
||||||
}
|
|
||||||
const chatPayload = {
|
const chatPayload = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(requestPayload),
|
body: JSON.stringify(requestPayload),
|
||||||
|
@ -181,10 +192,6 @@ export class GeminiProApi implements LLMApi {
|
||||||
|
|
||||||
controller.signal.onabort = finish;
|
controller.signal.onabort = finish;
|
||||||
|
|
||||||
// https://github.com/google-gemini/cookbook/blob/main/quickstarts/rest/Streaming_REST.ipynb
|
|
||||||
const chatPath =
|
|
||||||
baseUrl.replace("generateContent", "streamGenerateContent") +
|
|
||||||
(baseUrl.indexOf("?") > -1 ? "&alt=sse" : "?alt=sse");
|
|
||||||
fetchEventSource(chatPath, {
|
fetchEventSource(chatPath, {
|
||||||
...chatPayload,
|
...chatPayload,
|
||||||
async onopen(res) {
|
async onopen(res) {
|
||||||
|
@ -259,7 +266,7 @@ export class GeminiProApi implements LLMApi {
|
||||||
openWhenHidden: true,
|
openWhenHidden: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch(baseUrl, chatPayload);
|
const res = await fetch(chatPath, chatPayload);
|
||||||
clearTimeout(requestTimeoutId);
|
clearTimeout(requestTimeoutId);
|
||||||
const resJson = await res.json();
|
const resJson = await res.json();
|
||||||
if (resJson?.promptFeedback?.blockReason) {
|
if (resJson?.promptFeedback?.blockReason) {
|
||||||
|
@ -285,14 +292,4 @@ export class GeminiProApi implements LLMApi {
|
||||||
async models(): Promise<LLMModel[]> {
|
async models(): Promise<LLMModel[]> {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
path(path: string): string {
|
|
||||||
return "/api/google/" + path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureProperEnding(str: string) {
|
|
||||||
if (str.startsWith("[") && !str.endsWith("]")) {
|
|
||||||
return str + "]";
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,8 @@ export const Azure = {
|
||||||
|
|
||||||
export const Google = {
|
export const Google = {
|
||||||
ExampleEndpoint: "https://generativelanguage.googleapis.com/",
|
ExampleEndpoint: "https://generativelanguage.googleapis.com/",
|
||||||
ChatPath: (modelName: string) => `v1beta/models/${modelName}:generateContent`,
|
ChatPath: (modelName: string) =>
|
||||||
|
`v1beta/models/${modelName}:streamGenerateContent`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Baidu = {
|
export const Baidu = {
|
||||||
|
|
Loading…
Reference in New Issue