mirror of
https://github.com/Yidadaa/ChatGPT-Next-Web.git
synced 2025-08-30 10:06:54 +08:00
Compare commits
8 Commits
feature/gl
...
feature/ch
Author | SHA1 | Date | |
---|---|---|---|
|
ba8e2414c6 | ||
|
fe7f726c4b | ||
|
6b914b7ced | ||
|
175c52b13b | ||
|
0db21cf836 | ||
|
5b1a759f86 | ||
|
ab158bf042 | ||
|
14e3b409cc |
@@ -32,7 +32,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with Claude, GPT
|
||||
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
|
||||
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
|
||||
|
||||
[<img src="https://vercel.com/button" alt="Deploy on Vercel" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [<img src="https://img.shields.io/badge/BT_Deploy-Install-20a53a" alt="BT Deply Install" height="30">](https://www.bt.cn/new/download.html)
|
||||
[<img src="https://vercel.com/button" alt="Deploy on Vercel" height="30">](https://vercel.com/new/clone?repository-url=https://github.com/Dogtiti/ChatGPT-Next-Web-EarlyBird&env=OPENAI_API_KEY&env=CODE&project-name=nextchat-earlyBird&repository-name=NextChat-EarlyBird) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [<img src="https://img.shields.io/badge/BT_Deploy-Install-20a53a" alt="BT Deply Install" height="30">](https://www.bt.cn/new/download.html)
|
||||
|
||||
[<img src="https://github.com/user-attachments/assets/903482d4-3e87-4134-9af1-f2588fa90659" height="60" width="288" >](https://monica.im/?utm=nxcrp)
|
||||
|
||||
@@ -477,7 +477,7 @@ If you want to add a new translation, read this [document](./docs/translation.md
|
||||
|
||||
## Donation
|
||||
|
||||
[Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa)
|
||||
[Buy Me a Coffee](https://1kafei.com/dogtiti)
|
||||
|
||||
## Special Thanks
|
||||
|
||||
|
@@ -33,7 +33,7 @@
|
||||
|
||||
1. 准备好你的 [OpenAI API Key](https://platform.openai.com/account/api-keys);
|
||||
2. 点击右侧按钮开始部署:
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&env=GOOGLE_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web),直接使用 Github 账号登录即可,记得在环境变量页填入 API Key 和[页面访问密码](#配置页面访问密码) CODE;
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/Dogtiti/ChatGPT-Next-Web-EarlyBird&env=OPENAI_API_KEY&env=CODE&project-name=nextchat-earlyBird&repository-name=NextChat-EarlyBird),直接使用 Github 账号登录即可,记得在环境变量页填入 API Key 和[页面访问密码](#配置页面访问密码) CODE;
|
||||
3. 部署完毕后,即可开始使用;
|
||||
4. (可选)[绑定自定义域名](https://vercel.com/docs/concepts/projects/domains/add-a-domain):Vercel 分配的域名 DNS 在某些区域被污染了,绑定自定义域名即可直连。
|
||||
|
||||
|
@@ -30,7 +30,7 @@
|
||||
|
||||
1. [OpenAI API Key](https://platform.openai.com/account/api-keys)を準備する;
|
||||
2. 右側のボタンをクリックしてデプロイを開始:
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&env=GOOGLE_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) 、GitHubアカウントで直接ログインし、環境変数ページにAPI Keyと[ページアクセスパスワード](#設定ページアクセスパスワード) CODEを入力してください;
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/Dogtiti/ChatGPT-Next-Web-EarlyBird&env=OPENAI_API_KEY&env=CODE&project-name=nextchat-earlyBird&repository-name=NextChat-EarlyBird) 、GitHubアカウントで直接ログインし、環境変数ページにAPI Keyと[ページアクセスパスワード](#設定ページアクセスパスワード) CODEを入力してください;
|
||||
3. デプロイが完了したら、すぐに使用を開始できます;
|
||||
4. (オプション)[カスタムドメインをバインド](https://vercel.com/docs/concepts/projects/domains/add-a-domain):Vercelが割り当てたドメインDNSは一部の地域で汚染されているため、カスタムドメインをバインドすると直接接続できます。
|
||||
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "@/app/api/auth";
|
||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||
|
||||
const serverConfig = getServerSideConfig();
|
||||
|
||||
@@ -89,7 +89,7 @@ async function request(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.Alibaba as string,
|
||||
|
@@ -9,7 +9,7 @@ import {
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "./auth";
|
||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
||||
|
||||
const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]);
|
||||
@@ -122,7 +122,7 @@ async function request(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.Anthropic as string,
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "@/app/api/auth";
|
||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||
import { getAccessToken } from "@/app/utils/baidu";
|
||||
|
||||
const serverConfig = getServerSideConfig();
|
||||
@@ -104,7 +104,7 @@ async function request(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.Baidu as string,
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "@/app/api/auth";
|
||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||
|
||||
const serverConfig = getServerSideConfig();
|
||||
|
||||
@@ -88,7 +88,7 @@ async function request(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.ByteDance as string,
|
||||
|
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
|
||||
import { getServerSideConfig } from "../config/server";
|
||||
import { OPENAI_BASE_URL, ServiceProvider } from "../constant";
|
||||
import { cloudflareAIGatewayUrl } from "../utils/cloudflare";
|
||||
import { getModelProvider, isModelNotavailableInServer } from "../utils/model";
|
||||
import { getModelProvider, isModelAvailableInServer } from "../utils/model";
|
||||
|
||||
const serverConfig = getServerSideConfig();
|
||||
|
||||
@@ -118,14 +118,15 @@ export async function requestOpenai(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
[
|
||||
ServiceProvider.OpenAI,
|
||||
ServiceProvider.Azure,
|
||||
jsonBody?.model as string, // support provider-unspecified model
|
||||
],
|
||||
ServiceProvider.OpenAI as string,
|
||||
) ||
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.Azure as string,
|
||||
)
|
||||
) {
|
||||
return NextResponse.json(
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "@/app/api/auth";
|
||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||
|
||||
const serverConfig = getServerSideConfig();
|
||||
|
||||
@@ -89,7 +89,7 @@ async function request(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.ChatGLM as string,
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "@/app/api/auth";
|
||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||
// iflytek
|
||||
|
||||
const serverConfig = getServerSideConfig();
|
||||
@@ -89,7 +89,7 @@ async function request(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.Iflytek as string,
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "@/app/api/auth";
|
||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||
|
||||
const serverConfig = getServerSideConfig();
|
||||
|
||||
@@ -88,7 +88,7 @@ async function request(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.Moonshot as string,
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "@/app/api/auth";
|
||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||
|
||||
const serverConfig = getServerSideConfig();
|
||||
|
||||
@@ -88,7 +88,7 @@ async function request(req: NextRequest) {
|
||||
|
||||
// not undefined and is false
|
||||
if (
|
||||
isModelNotavailableInServer(
|
||||
isModelAvailableInServer(
|
||||
serverConfig.customModels,
|
||||
jsonBody?.model as string,
|
||||
ServiceProvider.XAI as string,
|
||||
|
@@ -21,10 +21,9 @@ import {
|
||||
SpeechOptions,
|
||||
} from "../api";
|
||||
import { getClientConfig } from "@/app/config/client";
|
||||
import { getMessageTextContent, isVisionModel } from "@/app/utils";
|
||||
import { getMessageTextContent } from "@/app/utils";
|
||||
import { RequestPayload } from "./openai";
|
||||
import { fetch } from "@/app/utils/stream";
|
||||
import { preProcessImageContent } from "@/app/utils/chat";
|
||||
|
||||
interface BasePayload {
|
||||
model: string;
|
||||
@@ -155,12 +154,9 @@ export class ChatGLMApi implements LLMApi {
|
||||
}
|
||||
|
||||
async chat(options: ChatOptions) {
|
||||
const visionModel = isVisionModel(options.config.model);
|
||||
const messages: ChatOptions["messages"] = [];
|
||||
for (const v of options.messages) {
|
||||
const content = visionModel
|
||||
? await preProcessImageContent(v.content)
|
||||
: getMessageTextContent(v);
|
||||
const content = getMessageTextContent(v);
|
||||
messages.push({ role: v.role, content });
|
||||
}
|
||||
|
||||
@@ -172,6 +168,7 @@ export class ChatGLMApi implements LLMApi {
|
||||
providerName: options.config.providerName,
|
||||
},
|
||||
};
|
||||
|
||||
const modelType = this.getModelType(modelConfig.model);
|
||||
const requestPayload = this.createPayload(messages, modelConfig, options);
|
||||
const path = this.path(this.getModelPath(modelType));
|
||||
|
@@ -60,18 +60,9 @@ export class GeminiProApi implements LLMApi {
|
||||
extractMessage(res: any) {
|
||||
console.log("[Response] gemini-pro response: ", res);
|
||||
|
||||
const getTextFromParts = (parts: any[]) => {
|
||||
if (!Array.isArray(parts)) return "";
|
||||
|
||||
return parts
|
||||
.map((part) => part?.text || "")
|
||||
.filter((text) => text.trim() !== "")
|
||||
.join("\n\n");
|
||||
};
|
||||
|
||||
return (
|
||||
getTextFromParts(res?.candidates?.at(0)?.content?.parts) ||
|
||||
getTextFromParts(res?.at(0)?.candidates?.at(0)?.content?.parts) ||
|
||||
res?.candidates?.at(0)?.content?.parts.at(0)?.text ||
|
||||
res?.at(0)?.candidates?.at(0)?.content?.parts.at(0)?.text ||
|
||||
res?.error?.message ||
|
||||
""
|
||||
);
|
||||
@@ -232,10 +223,7 @@ export class GeminiProApi implements LLMApi {
|
||||
},
|
||||
});
|
||||
}
|
||||
return chunkJson?.candidates
|
||||
?.at(0)
|
||||
?.content.parts?.map((part: { text: string }) => part.text)
|
||||
.join("\n\n");
|
||||
return chunkJson?.candidates?.at(0)?.content.parts.at(0)?.text;
|
||||
},
|
||||
// processToolMessage, include tool_calls message and tool call results
|
||||
(
|
||||
|
@@ -22,6 +22,7 @@ import {
|
||||
MIN_SIDEBAR_WIDTH,
|
||||
NARROW_SIDEBAR_WIDTH,
|
||||
Path,
|
||||
PLUGINS,
|
||||
REPO_URL,
|
||||
} from "../constant";
|
||||
|
||||
@@ -31,12 +32,6 @@ import dynamic from "next/dynamic";
|
||||
import { showConfirm, Selector } from "./ui-lib";
|
||||
import clsx from "clsx";
|
||||
|
||||
const DISCOVERY = [
|
||||
{ name: Locale.Plugin.Name, path: Path.Plugins },
|
||||
{ name: "Stable Diffusion", path: Path.Sd },
|
||||
{ name: Locale.SearchChat.Page.Title, path: Path.SearchChat },
|
||||
];
|
||||
|
||||
const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
|
||||
loading: () => null,
|
||||
});
|
||||
@@ -224,7 +219,7 @@ export function SideBarTail(props: {
|
||||
export function SideBar(props: { className?: string }) {
|
||||
useHotKey();
|
||||
const { onDragStart, shouldNarrow } = useDragSideBar();
|
||||
const [showDiscoverySelector, setshowDiscoverySelector] = useState(false);
|
||||
const [showPluginSelector, setShowPluginSelector] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const config = useAppConfig();
|
||||
const chatStore = useChatStore();
|
||||
@@ -259,21 +254,21 @@ export function SideBar(props: { className?: string }) {
|
||||
icon={<DiscoveryIcon />}
|
||||
text={shouldNarrow ? undefined : Locale.Discovery.Name}
|
||||
className={styles["sidebar-bar-button"]}
|
||||
onClick={() => setshowDiscoverySelector(true)}
|
||||
onClick={() => setShowPluginSelector(true)}
|
||||
shadow
|
||||
/>
|
||||
</div>
|
||||
{showDiscoverySelector && (
|
||||
{showPluginSelector && (
|
||||
<Selector
|
||||
items={[
|
||||
...DISCOVERY.map((item) => {
|
||||
...PLUGINS.map((item) => {
|
||||
return {
|
||||
title: item.name,
|
||||
value: item.path,
|
||||
};
|
||||
}),
|
||||
]}
|
||||
onClose={() => setshowDiscoverySelector(false)}
|
||||
onClose={() => setShowPluginSelector(false)}
|
||||
onSelection={(s) => {
|
||||
navigate(s[0], { state: { fromHome: true } });
|
||||
}}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import md5 from "spark-md5";
|
||||
import { DEFAULT_MODELS, DEFAULT_GA_ID } from "../constant";
|
||||
import { isGPT4Model } from "../utils/model";
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
@@ -128,12 +127,20 @@ export const getServerSideConfig = () => {
|
||||
|
||||
if (disableGPT4) {
|
||||
if (customModels) customModels += ",";
|
||||
customModels += DEFAULT_MODELS.filter((m) => isGPT4Model(m.name))
|
||||
customModels += DEFAULT_MODELS.filter(
|
||||
(m) =>
|
||||
(m.name.startsWith("gpt-4") || m.name.startsWith("chatgpt-4o") || m.name.startsWith("o1")) &&
|
||||
!m.name.startsWith("gpt-4o-mini"),
|
||||
)
|
||||
.map((m) => "-" + m.name)
|
||||
.join(",");
|
||||
if (defaultModel && isGPT4Model(defaultModel)) {
|
||||
if (
|
||||
(defaultModel.startsWith("gpt-4") ||
|
||||
defaultModel.startsWith("chatgpt-4o") ||
|
||||
defaultModel.startsWith("o1")) &&
|
||||
!defaultModel.startsWith("gpt-4o-mini")
|
||||
)
|
||||
defaultModel = "";
|
||||
}
|
||||
}
|
||||
|
||||
const isStability = !!process.env.STABILITY_API_KEY;
|
||||
|
@@ -305,9 +305,6 @@ export const VISION_MODEL_REGEXES = [
|
||||
/qwen2-vl/,
|
||||
/gpt-4-turbo(?!.*preview)/, // Matches "gpt-4-turbo" but not "gpt-4-turbo-preview"
|
||||
/^dall-e-3$/, // Matches exactly "dall-e-3"
|
||||
/glm-4v-plus/,
|
||||
/glm-4v/,
|
||||
/glm-4v-flash/,
|
||||
];
|
||||
|
||||
export const EXCLUDE_VISION_MODEL_REGEXES = [/claude-3-5-haiku-20241022/];
|
||||
@@ -600,6 +597,11 @@ export const internalAllowedWebDavEndpoints = [
|
||||
];
|
||||
|
||||
export const DEFAULT_GA_ID = "G-89WN60ZK2E";
|
||||
export const PLUGINS = [
|
||||
{ name: "Plugins", path: Path.Plugins },
|
||||
{ name: "Stable Diffusion", path: Path.Sd },
|
||||
{ name: "Search Chat", path: Path.SearchChat },
|
||||
];
|
||||
|
||||
export const SAAS_CHAT_URL = "https://nextchat.dev/chat";
|
||||
export const SAAS_CHAT_UTM_URL = "https://nextchat.dev/chat?utm=github";
|
||||
|
@@ -176,7 +176,7 @@ const cn = {
|
||||
},
|
||||
},
|
||||
Lang: {
|
||||
Name: "Language", // 注意:如果要添加新的翻译,请不要翻译此值,将它保留为 `Language`
|
||||
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
||||
All: "所有语言",
|
||||
},
|
||||
Avatar: "头像",
|
||||
@@ -630,7 +630,7 @@ const cn = {
|
||||
Sysmessage: "你是一个助手",
|
||||
},
|
||||
SearchChat: {
|
||||
Name: "搜索聊天记录",
|
||||
Name: "搜索",
|
||||
Page: {
|
||||
Title: "搜索聊天记录",
|
||||
Search: "输入搜索关键词",
|
||||
|
@@ -485,7 +485,7 @@ const tw = {
|
||||
},
|
||||
},
|
||||
SearchChat: {
|
||||
Name: "搜尋聊天記錄",
|
||||
Name: "搜尋",
|
||||
Page: {
|
||||
Title: "搜尋聊天記錄",
|
||||
Search: "輸入搜尋關鍵詞",
|
||||
|
@@ -202,52 +202,3 @@ export function isModelAvailableInServer(
|
||||
const modelTable = collectModelTable(DEFAULT_MODELS, customModels);
|
||||
return modelTable[fullName]?.available === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the model name is a GPT-4 related model
|
||||
*
|
||||
* @param modelName The name of the model to check
|
||||
* @returns True if the model is a GPT-4 related model (excluding gpt-4o-mini)
|
||||
*/
|
||||
export function isGPT4Model(modelName: string): boolean {
|
||||
return (
|
||||
(modelName.startsWith("gpt-4") ||
|
||||
modelName.startsWith("chatgpt-4o") ||
|
||||
modelName.startsWith("o1")) &&
|
||||
!modelName.startsWith("gpt-4o-mini")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a model is not available on any of the specified providers in the server.
|
||||
*
|
||||
* @param {string} customModels - A string of custom models, comma-separated.
|
||||
* @param {string} modelName - The name of the model to check.
|
||||
* @param {string|string[]} providerNames - A string or array of provider names to check against.
|
||||
*
|
||||
* @returns {boolean} True if the model is not available on any of the specified providers, false otherwise.
|
||||
*/
|
||||
export function isModelNotavailableInServer(
|
||||
customModels: string,
|
||||
modelName: string,
|
||||
providerNames: string | string[],
|
||||
): boolean {
|
||||
// Check DISABLE_GPT4 environment variable
|
||||
if (
|
||||
process.env.DISABLE_GPT4 === "1" &&
|
||||
isGPT4Model(modelName.toLowerCase())
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const modelTable = collectModelTable(DEFAULT_MODELS, customModels);
|
||||
|
||||
const providerNamesArray = Array.isArray(providerNames)
|
||||
? providerNames
|
||||
: [providerNames];
|
||||
for (const providerName of providerNamesArray) {
|
||||
const fullName = `${modelName}@${providerName.toLowerCase()}`;
|
||||
if (modelTable?.[fullName]?.available === true) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@@ -1,80 +0,0 @@
|
||||
import { isModelNotavailableInServer } from "../app/utils/model";
|
||||
|
||||
describe("isModelNotavailableInServer", () => {
|
||||
test("test model will return false, which means the model is available", () => {
|
||||
const customModels = "";
|
||||
const modelName = "gpt-4";
|
||||
const providerNames = "OpenAI";
|
||||
const result = isModelNotavailableInServer(
|
||||
customModels,
|
||||
modelName,
|
||||
providerNames,
|
||||
);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test("test model will return true when model is not available in custom models", () => {
|
||||
const customModels = "-all,gpt-4o-mini";
|
||||
const modelName = "gpt-4";
|
||||
const providerNames = "OpenAI";
|
||||
const result = isModelNotavailableInServer(
|
||||
customModels,
|
||||
modelName,
|
||||
providerNames,
|
||||
);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should respect DISABLE_GPT4 setting", () => {
|
||||
process.env.DISABLE_GPT4 = "1";
|
||||
const result = isModelNotavailableInServer("", "gpt-4", "OpenAI");
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should handle empty provider names", () => {
|
||||
const result = isModelNotavailableInServer("-all,gpt-4", "gpt-4", "");
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should be case insensitive for model names", () => {
|
||||
const result = isModelNotavailableInServer("-all,GPT-4", "gpt-4", "OpenAI");
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("support passing multiple providers, model unavailable on one of the providers will return true", () => {
|
||||
const customModels = "-all,gpt-4@google";
|
||||
const modelName = "gpt-4";
|
||||
const providerNames = ["OpenAI", "Azure"];
|
||||
const result = isModelNotavailableInServer(
|
||||
customModels,
|
||||
modelName,
|
||||
providerNames,
|
||||
);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
// FIXME: 这个测试用例有问题,需要修复
|
||||
// test("support passing multiple providers, model available on one of the providers will return false", () => {
|
||||
// const customModels = "-all,gpt-4@google";
|
||||
// const modelName = "gpt-4";
|
||||
// const providerNames = ["OpenAI", "Google"];
|
||||
// const result = isModelNotavailableInServer(
|
||||
// customModels,
|
||||
// modelName,
|
||||
// providerNames,
|
||||
// );
|
||||
// expect(result).toBe(false);
|
||||
// });
|
||||
|
||||
test("test custom model without setting provider", () => {
|
||||
const customModels = "-all,mistral-large";
|
||||
const modelName = "mistral-large";
|
||||
const providerNames = modelName;
|
||||
const result = isModelNotavailableInServer(
|
||||
customModels,
|
||||
modelName,
|
||||
providerNames,
|
||||
);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user