From f92fefb6cd00c8efb70768f277d2a06f8b149e66 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 23 Oct 2023 14:36:42 +0800 Subject: [PATCH 01/67] Update synchronise-chat-logs-cn.md --- docs/synchronise-chat-logs-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/synchronise-chat-logs-cn.md b/docs/synchronise-chat-logs-cn.md index 52d79268f..59f277429 100644 --- a/docs/synchronise-chat-logs-cn.md +++ b/docs/synchronise-chat-logs-cn.md @@ -1,4 +1,4 @@ -# 同步聊天记录(upStash) +# 同步聊天记录 ## 准备工作 - GitHub账号 - 拥有自己搭建过的ChatGPT-Next-Web的服务器 From ccac85b079d62cf5886d5c1bc8543b2adebb5da6 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 23 Oct 2023 14:37:48 +0800 Subject: [PATCH 02/67] Update README_CN.md --- README_CN.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README_CN.md b/README_CN.md index ce9309fd2..671e74b09 100644 --- a/README_CN.md +++ b/README_CN.md @@ -186,3 +186,4 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ## 开源协议 [MIT](https://opensource.org/license/mit/) + From e8a4ad10012dd6fa3e3370daa875835ad9897cfb Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 23 Oct 2023 14:38:04 +0800 Subject: [PATCH 03/67] Update README_KO.md From d1c3d900abaf8ec8eee92fbcfad59e9db64080af Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 23 Oct 2023 14:38:26 +0800 Subject: [PATCH 04/67] Update README_KO.md --- README_KO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README_KO.md b/README_KO.md index 6ec7fe0e6..c4000c3a2 100644 --- a/README_KO.md +++ b/README_KO.md @@ -186,3 +186,4 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ## 오픈소스 라이센스 [MIT](https://opensource.org/license/mit/) + From 70e67a686b812c3ddf3a33c04e09cbd85ef9a48b Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Thu, 26 Oct 2023 00:06:09 +0800 Subject: [PATCH 05/67] Revert "fix serviceWorker cache: auto caching files by workbox" --- public/serviceWorker.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/public/serviceWorker.js b/public/serviceWorker.js index 2e1d60b98..f5a24b701 100644 --- a/public/serviceWorker.js +++ b/public/serviceWorker.js @@ -1,21 +1,15 @@ const CHATGPT_NEXT_WEB_CACHE = "chatgpt-next-web-cache"; -importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.0.0/workbox-sw.js'); - self.addEventListener("activate", function (event) { console.log("ServiceWorker activated."); }); -workbox.core.clientsClaim(); -self.addEventListener("message", (event) => { - if (event.data && event.data.type === "SKIP_WAITING") { - self.skipWaiting(); - } +self.addEventListener("install", function (event) { + event.waitUntil( + caches.open(CHATGPT_NEXT_WEB_CACHE).then(function (cache) { + return cache.addAll([]); + }), + ); }); -workbox.routing.registerRoute( - new RegExp('/*'), - new workbox.strategies.StaleWhileRevalidate({ - cacheName: CHATGPT_NEXT_WEB_CACHE - }) -); +self.addEventListener("fetch", (e) => {}); From cd1f082c52e46b23ba844e6b1813c20f164d7116 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Tue, 7 Nov 2023 06:08:06 +0700 Subject: [PATCH 06/67] Feat Models [GPT-4] [GPT-3.5] [128K context] [+] feat(constant.ts): add new models to DEFAULT_MODELS array --- app/constant.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/constant.ts b/app/constant.ts index e03e00971..15db37a1a 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -100,6 +100,14 @@ export const DEFAULT_MODELS = [ name: "gpt-4-32k-0613", available: true, }, + { + name: "ggpt-4-1106-preview", + available: true, + }, + { + name: "gpt-4-vision-preview", + available: true, + }, { name: "gpt-3.5-turbo", available: true, @@ -112,6 +120,10 @@ export const DEFAULT_MODELS = [ name: "gpt-3.5-turbo-0613", available: true, }, + { + name: "gpt-3.5-turbo-1106", + available: true, + }, { name: "gpt-3.5-turbo-16k", available: true, From 9f26c8cecb997e3c1f81f5219f91043dc00342d9 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Tue, 7 Nov 2023 06:33:07 +0700 Subject: [PATCH 07/67] Fix Typo [GPT-4] [+] fix(constant.ts): fix typo in model name, change "ggpt-4-1106-preview" to "gpt-4-1106-preview" --- app/constant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/constant.ts b/app/constant.ts index 15db37a1a..5f3744ec1 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -101,7 +101,7 @@ export const DEFAULT_MODELS = [ available: true, }, { - name: "ggpt-4-1106-preview", + name: "gpt-4-1106-preview", available: true, }, { From d2d615c84a6a30879488528c4537e5a3ecaebca5 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Tue, 7 Nov 2023 06:42:55 +0700 Subject: [PATCH 08/67] Refactor KnowledgeCutoff [+] fix(constant.ts): update DEFAULT_SYSTEM_TEMPLATE to include knowledgeCutoff and time variables [+] feat(chat.ts): add support for injecting system prompts based on model configuration --- app/constant.ts | 5 +++-- app/store/chat.ts | 34 +++++++++++++++++++--------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/app/constant.ts b/app/constant.ts index 5f3744ec1..95de35627 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -69,9 +69,10 @@ export const OpenaiPath = { export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang export const DEFAULT_SYSTEM_TEMPLATE = ` You are ChatGPT, a large language model trained by OpenAI. -Knowledge cutoff: 2021-09 +Knowledge cutoff: {{knowledgeCutoff}} Current model: {{model}} -Current time: {{time}}`; +Current time: {{time}} +`; export const SUMMARIZE_MODEL = "gpt-3.5-turbo"; diff --git a/app/store/chat.ts b/app/store/chat.ts index 56ac8db6c..9f73fdf26 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -401,22 +401,26 @@ export const useChatStore = createPersistStore( // system prompts, to get close to OpenAI Web ChatGPT const shouldInjectSystemPrompts = modelConfig.enableInjectSystemPrompts; - const systemPrompts = shouldInjectSystemPrompts - ? [ - createMessage({ - role: "system", - content: fillTemplateWith("", { - ...modelConfig, - template: DEFAULT_SYSTEM_TEMPLATE, - }), - }), - ] - : []; + let systemPrompts = shouldInjectSystemPrompts ? [] : []; + if (shouldInjectSystemPrompts) { - console.log( - "[Global System Prompt] ", - systemPrompts.at(0)?.content ?? "empty", - ); + const model = modelConfig.model; + let systemTemplate = DEFAULT_SYSTEM_TEMPLATE; + + if (model === "gpt-4-1106-preview" || model === "gpt-4-vision-preview") { + systemTemplate = systemTemplate.replace("{{knowledgeCutoff}}", "2023-04"); + } else { + systemTemplate = systemTemplate.replace("{{knowledgeCutoff}}", "2021-09"); + } + + const systemPrompt = createMessage({ + role: "system", + content: fillTemplateWith("", { + ...modelConfig, + template: systemTemplate, + }), + }); + console.log("[Global System Prompt] ", systemPrompt.content); } // long term memory From 6e52d14180345446abf61e933368eccbebad8694 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 7 Nov 2023 23:30:09 +0800 Subject: [PATCH 09/67] fix: #3016 disable sidebar transition on ios --- app/components/sidebar.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 6212d05d9..85af3ed2e 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useCallback } from "react"; +import { useEffect, useRef, useCallback, useMemo } from "react"; import styles from "./home.module.scss"; @@ -26,7 +26,7 @@ import { } from "../constant"; import { Link, useNavigate } from "react-router-dom"; -import { useMobileScreen } from "../utils"; +import { isIOS, useMobileScreen } from "../utils"; import dynamic from "next/dynamic"; import { showConfirm, showToast } from "./ui-lib"; @@ -134,6 +134,11 @@ export function SideBar(props: { className?: string }) { const { onDragStart, shouldNarrow } = useDragSideBar(); const navigate = useNavigate(); const config = useAppConfig(); + const isMobileScreen = useMobileScreen(); + const isIOSMobile = useMemo( + () => isIOS() && isMobileScreen, + [isMobileScreen], + ); useHotKey(); @@ -142,6 +147,10 @@ export function SideBar(props: { className?: string }) { className={`${styles.sidebar} ${props.className} ${ shouldNarrow && styles["narrow-sidebar"] }`} + style={{ + // #3016 disable transition on ios mobile screen + transition: isMobileScreen && isIOSMobile ? "none" : undefined, + }} >
From 836bf836d37b0d704eab132b7849447214b93d8c Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 7 Nov 2023 23:54:30 +0800 Subject: [PATCH 10/67] fix: #3152 system prompt should be injected --- app/constant.ts | 9 ++++++++- app/store/chat.ts | 39 ++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/app/constant.ts b/app/constant.ts index 95de35627..635fbeaea 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -69,13 +69,20 @@ export const OpenaiPath = { export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang export const DEFAULT_SYSTEM_TEMPLATE = ` You are ChatGPT, a large language model trained by OpenAI. -Knowledge cutoff: {{knowledgeCutoff}} +Knowledge cutoff: {{cutoff}} Current model: {{model}} Current time: {{time}} `; export const SUMMARIZE_MODEL = "gpt-3.5-turbo"; +export const KnowledgeCutOffDate: Record = { + default: "2021-09", + "gpt-3.5-turbo-1106": "2023-04", + "gpt-4-1106-preview": "2023-04", + "gpt-4-vision-preview": "2023-04", +}; + export const DEFAULT_MODELS = [ { name: "gpt-4", diff --git a/app/store/chat.ts b/app/store/chat.ts index 9f73fdf26..95822c191 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -7,6 +7,7 @@ import { createEmptyMask, Mask } from "./mask"; import { DEFAULT_INPUT_TEMPLATE, DEFAULT_SYSTEM_TEMPLATE, + KnowledgeCutOffDate, StoreKey, SUMMARIZE_MODEL, } from "../constant"; @@ -116,7 +117,11 @@ function countMessages(msgs: ChatMessage[]) { } function fillTemplateWith(input: string, modelConfig: ModelConfig) { + let cutoff = + KnowledgeCutOffDate[modelConfig.model] ?? KnowledgeCutOffDate.default; + const vars = { + cutoff, model: modelConfig.model, time: new Date().toLocaleString(), lang: getLang(), @@ -401,26 +406,22 @@ export const useChatStore = createPersistStore( // system prompts, to get close to OpenAI Web ChatGPT const shouldInjectSystemPrompts = modelConfig.enableInjectSystemPrompts; - let systemPrompts = shouldInjectSystemPrompts ? [] : []; - + const systemPrompts = shouldInjectSystemPrompts + ? [ + createMessage({ + role: "system", + content: fillTemplateWith("", { + ...modelConfig, + template: DEFAULT_SYSTEM_TEMPLATE, + }), + }), + ] + : []; if (shouldInjectSystemPrompts) { - const model = modelConfig.model; - let systemTemplate = DEFAULT_SYSTEM_TEMPLATE; - - if (model === "gpt-4-1106-preview" || model === "gpt-4-vision-preview") { - systemTemplate = systemTemplate.replace("{{knowledgeCutoff}}", "2023-04"); - } else { - systemTemplate = systemTemplate.replace("{{knowledgeCutoff}}", "2021-09"); - } - - const systemPrompt = createMessage({ - role: "system", - content: fillTemplateWith("", { - ...modelConfig, - template: systemTemplate, - }), - }); - console.log("[Global System Prompt] ", systemPrompt.content); + console.log( + "[Global System Prompt] ", + systemPrompts.at(0)?.content ?? "empty", + ); } // long term memory From 638fdd8c3e48837d4f060cca5bc73241d9bd9071 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 8 Nov 2023 00:20:34 +0800 Subject: [PATCH 11/67] feat: default disable balance query --- README.md | 4 +- README_CN.md | 7 +- README_ES.md | 173 ----------------------- README_JA.md | 275 ------------------------------------ README_KO.md | 189 ------------------------- app/components/chat.tsx | 3 +- app/components/settings.tsx | 19 ++- app/config/server.ts | 4 +- app/constant.ts | 1 + 9 files changed, 24 insertions(+), 651 deletions(-) delete mode 100644 README_ES.md delete mode 100644 README_JA.md delete mode 100644 README_KO.md diff --git a/README.md b/README.md index 33f6e85cb..b24470c64 100644 --- a/README.md +++ b/README.md @@ -185,11 +185,11 @@ If you do not want users to input their own API key, set this value to 1. If you do not want users to use GPT-4, set this value to 1. -### `HIDE_BALANCE_QUERY` (optional) +### `ENABLE_BALANCE_QUERY` (optional) > Default: Empty -If you do not want users to query balance, set this value to 1. +If you do want users to query balance, set this value to 1, or you should set it to 0. ## Requirements diff --git a/README_CN.md b/README_CN.md index 671e74b09..e16c47a8a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -98,9 +98,9 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 如果你不想让用户使用 GPT-4,将此环境变量设置为 1 即可。 -### `HIDE_BALANCE_QUERY` (可选) +### `ENABLE_BALANCE_QUERY` (可选) -如果你不想让用户查询余额,将此环境变量设置为 1 即可。 +如果你想启用余额查询功能,将此环境变量设置为 1 即可。 ## 开发 @@ -169,7 +169,6 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ⚠️ 注意:如果你安装过程中遇到了问题,请使用 docker 部署。 - ## 鸣谢 ### 捐赠者 @@ -181,9 +180,9 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s [见项目贡献者列表](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors) ### 相关项目 + - [one-api](https://github.com/songquanpeng/one-api): 一站式大模型额度管理平台,支持市面上所有主流大语言模型 ## 开源协议 [MIT](https://opensource.org/license/mit/) - diff --git a/README_ES.md b/README_ES.md deleted file mode 100644 index a5787a996..000000000 --- a/README_ES.md +++ /dev/null @@ -1,173 +0,0 @@ -
-预览 - -

ChatGPT Next Web

- -Implemente su aplicación web privada ChatGPT de forma gratuita con un solo clic. - -[Demo demo](https://chat-gpt-next-web.vercel.app/) / [Problemas de comentarios](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Únete a Discord](https://discord.gg/zrhvHCr79N) / [Grupo QQ](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [Desarrolladores de consejos](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [Donar](#捐赠-donate-usdt) - -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) - -![主界面](./docs/images/cover.png) - -
- -## Comenzar - -1. Prepara el tuyo [Clave API OpenAI](https://platform.openai.com/account/api-keys); -2. Haga clic en el botón de la derecha para iniciar la implementación: - [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web), inicie sesión directamente con su cuenta de Github y recuerde completar la clave API y la suma en la página de variables de entorno[Contraseña de acceso a la página](#配置页面访问密码) CÓDIGO; -3. Una vez implementado, puede comenzar; -4. (Opcional)[Enlazar un nombre de dominio personalizado](https://vercel.com/docs/concepts/projects/domains/add-a-domain): El nombre de dominio DNS asignado por Vercel está contaminado en algunas regiones y puede conectarse directamente enlazando un nombre de dominio personalizado. - -## Manténgase actualizado - -Si sigue los pasos anteriores para implementar su proyecto con un solo clic, es posible que siempre diga "La actualización existe" porque Vercel creará un nuevo proyecto para usted de forma predeterminada en lugar de bifurcar el proyecto, lo que evitará que la actualización se detecte correctamente. -Le recomendamos que siga estos pasos para volver a implementar: - -- Eliminar el repositorio original; -- Utilice el botón de bifurcación en la esquina superior derecha de la página para bifurcar este proyecto; -- En Vercel, vuelva a seleccionar e implementar,[Echa un vistazo al tutorial detallado](./docs/vercel-cn.md#如何新建项目)。 - -### Activar actualizaciones automáticas - -> Si encuentra un error de ejecución de Upstream Sync, ¡Sync Fork manualmente una vez! - -Cuando bifurca el proyecto, debido a las limitaciones de Github, debe ir manualmente a la página Acciones de su proyecto bifurcado para habilitar Flujos de trabajo y habilitar Upstream Sync Action, después de habilitarlo, puede activar las actualizaciones automáticas cada hora: - -![自动更新](./docs/images/enable-actions.jpg) - -![启用自动更新](./docs/images/enable-actions-sync.jpg) - -### Actualizar el código manualmente - -Si desea que el manual se actualice inmediatamente, puede consultarlo [Documentación para Github](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) Aprenda a sincronizar un proyecto bifurcado con código ascendente. - -Puede destacar / ver este proyecto o seguir al autor para recibir notificaciones de nuevas actualizaciones de funciones. - -## Configurar la contraseña de acceso a la página - -> Después de configurar la contraseña, el usuario debe completar manualmente el código de acceso en la página de configuración para chatear normalmente, de lo contrario, se solicitará el estado no autorizado a través de un mensaje. - -> **advertir**: Asegúrese de establecer el número de dígitos de la contraseña lo suficientemente largo, preferiblemente más de 7 dígitos, de lo contrario[Será volado](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518)。 - -Este proyecto proporciona control de permisos limitado, agregue el nombre al nombre en la página Variables de entorno del Panel de control del proyecto Vercel `CODE` Variables de entorno con valores para contraseñas personalizadas separadas por comas: - - code1,code2,code3 - -Después de agregar o modificar la variable de entorno, por favor**Redesplegar**proyecto para poner en vigor los cambios. - -## Variable de entorno - -> La mayoría de los elementos de configuración de este proyecto se establecen a través de variables de entorno, tutorial:[Cómo modificar las variables de entorno de Vercel](./docs/vercel-cn.md)。 - -### `OPENAI_API_KEY` (Requerido) - -OpanAI key, la clave API que solicita en la página de su cuenta openai. - -### `CODE` (Opcional) - -Las contraseñas de acceso, opcionalmente, se pueden separar por comas. - -**advertir**: Si no completa este campo, cualquiera puede usar directamente su sitio web implementado, lo que puede hacer que su token se consuma rápidamente, se recomienda completar esta opción. - -### `BASE_URL` (Opcional) - -> Predeterminado: `https://api.openai.com` - -> Ejemplos: `http://your-openai-proxy.com` - -URL del proxy de interfaz OpenAI, complete esta opción si configuró manualmente el proxy de interfaz openAI. - -> Si encuentra problemas con el certificado SSL, establezca el `BASE_URL` El protocolo se establece en http. - -### `OPENAI_ORG_ID` (Opcional) - -Especifica el identificador de la organización en OpenAI. - -### `HIDE_USER_API_KEY` (Opcional) - -Si no desea que los usuarios rellenen la clave de API ellos mismos, establezca esta variable de entorno en 1. - -### `DISABLE_GPT4` (Opcional) - -Si no desea que los usuarios utilicen GPT-4, establezca esta variable de entorno en 1. - -### `HIDE_BALANCE_QUERY` (Opcional) - -Si no desea que los usuarios consulte el saldo, establezca esta variable de entorno en 1. - -## explotación - -> No se recomienda encarecidamente desarrollar o implementar localmente, debido a algunas razones técnicas, es difícil configurar el agente API de OpenAI localmente, a menos que pueda asegurarse de que puede conectarse directamente al servidor OpenAI. - -Haga clic en el botón de abajo para iniciar el desarrollo secundario: - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) - -Antes de empezar a escribir código, debe crear uno nuevo en la raíz del proyecto `.env.local` archivo, lleno de variables de entorno: - - OPENAI_API_KEY= - -### Desarrollo local - -1. Instale nodejs 18 e hilo, pregunte a ChatGPT para obtener más detalles; -2. ejecutar `yarn install && yarn dev` Enlatar. ⚠️ Nota: Este comando es solo para desarrollo local, no para implementación. -3. Úselo si desea implementar localmente `yarn install && yarn start` comando, puede cooperar con pm2 a daemon para evitar ser asesinado, pregunte a ChatGPT para obtener más detalles. - -## desplegar - -### Implementación de contenedores (recomendado) - -> La versión de Docker debe ser 20 o posterior, de lo contrario se indicará que no se puede encontrar la imagen. - -> ⚠️ Nota: Las versiones de Docker están de 1 a 2 días por detrás de la última versión la mayor parte del tiempo, por lo que es normal que sigas diciendo "La actualización existe" después de la implementación. - -```shell -docker pull yidadaa/chatgpt-next-web - -docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY=sk-xxxx \ - -e CODE=your-password \ - yidadaa/chatgpt-next-web -``` - -También puede especificar proxy: - -```shell -docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY=sk-xxxx \ - -e CODE=your-password \ - --net=host \ - -e PROXY_URL=http://127.0.0.1:7890 \ - yidadaa/chatgpt-next-web -``` - -Si necesita especificar otras variables de entorno, agréguelas usted mismo en el comando anterior `-e 环境变量=环境变量值` para especificar. - -### Implementación local - -Ejecute el siguiente comando en la consola: - -```shell -bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) -``` - -⚠️ Nota: Si tiene problemas durante la instalación, utilice la implementación de Docker. - -## Reconocimiento - -### donante - -> Ver versión en inglés. - -### Colaboradores - -[Ver la lista de colaboradores del proyecto](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors) - -## Licencia de código abierto - -[MIT](https://opensource.org/license/mit/) diff --git a/README_JA.md b/README_JA.md deleted file mode 100644 index 72a0d5373..000000000 --- a/README_JA.md +++ /dev/null @@ -1,275 +0,0 @@ -
-icon - -

ChatGPT Next Web

- -[English](./README.md) / [简体中文](./README_CN.md) / 日本語 - -ワンクリックで、クロスプラットフォーム ChatGPT ウェブ UI が表示されます。 - -[![Web][Web-image]][web-url] -[![Windows][Windows-image]][download-url] -[![MacOS][MacOS-image]][download-url] -[![Linux][Linux-image]][download-url] - -[Web App](https://chatgpt.nextweb.fun/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discord](https://discord.gg/YCkeafCafC) / [コーヒーをおごる](https://www.buymeacoffee.com/yidadaa) / [QQ グループ](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [開発者への報酬](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) - -[web-url]: https://chatgpt.nextweb.fun -[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases -[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge -[Windows-image]: https://img.shields.io/badge/-Windows-blue?logo=windows -[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple -[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu - -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) - -![cover](./docs/images/cover.png) - -
- -## 特徴 - -- Vercel で 1 分以内に**ワンクリックで無料デプロイ**。 -- コンパクトなクライアント (~5MB) on Linux/Windows/MacOS、[今すぐダウンロード](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) -- [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) または [LocalAI](https://github.com/go-skynet/LocalAI) との使用をお勧めします -- プライバシー第一、すべてのデータはブラウザにローカルに保存されます -- マークダウンのサポート: LaTex、マーメイド、コードハイライトなど -- レスポンシブデザイン、ダークモード、PWA -- 最初の画面読み込み速度が速い(~100kb)、ストリーミングレスポンスをサポート -- v2 の新機能:プロンプトテンプレート(マスク)でチャットツールを作成、共有、デバッグ -- [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) と [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) による素晴らしいプロンプト -- トークンを保存しながら、長い会話をサポートするために自動的にチャット履歴を圧縮します -- 国際化: English、简体中文、繁体中文、日本語、Français、Español、Italiano、Türkçe、Deutsch、Tiếng Việt、Русский、Čeština、한국어 - -## ロードマップ - -- [x] システムプロンプト: ユーザー定義のプロンプトをシステムプロンプトとして固定 [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138) -- [x] ユーザープロンプト: ユーザはカスタムプロンプトを編集し、プロンプトリストに保存することができます。 -- [x] プロンプトテンプレート: 事前に定義されたインコンテキストプロンプトで新しいチャットを作成 [#993](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/993) -- [x] イメージとして共有、ShareGPT への共有 [#1741](https://github.com/Yidadaa/ChatGPT-Next-Web/pull/1741) -- [x] tauri を使ったデスクトップアプリ -- [x] セルフホストモデル: [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) と完全に互換性があり、[LocalAI](https://github.com/go-skynet/LocalAI) のサーバーデプロイも可能です: llama/gpt4all/rwkv/vicuna/koala/gpt4all-j/cerebras/falcon/dolly など -- [ ] プラグイン: ネットワーク検索、計算機、その他のAPIなどをサポート [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) - -## 新機能 - -- 🚀 v2.0 がリリースされ、プロンプト・テンプレートが作成できるようになりました!こちらをお読みください: [ChatGPT プロンプトエンジニアリング Tips: ゼロ、一発、数発プロンプト](https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/)。 -- 💡 このプロジェクトをいつでもどこでも簡単に使いたいですか?このデスクトッププラグインをお試しください: https://github.com/mushan0x0/AI0x0.com -- 🚀 v2.7 では、会話を画像として共有したり、ShareGPT に共有することができます! -- 🚀 v2.8 全てのプラットフォームで動作するクライアントができました! - -## 始める - -> [簡体字中国語 > 始め方](./README_CN.md#开始使用) - -1. [OpenAI API Key](https://platform.openai.com/account/api-keys) を取得する; -2. クリック - [![Vercel でデプロイ](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)をクリックします。`CODE` はあなたのページのパスワードであることを忘れないでください; -3. お楽しみください :) - -## FAQ - -[簡体字中国語 > よくある質問](./docs/faq-cn.md) - -[English > FAQ](./docs/faq-en.md) - -## 更新を継続する - -> [簡体字中国語 > コードを最新の状態に保つ方法](./README_CN.md#保持更新) - -上記の手順に沿ってワンクリックで自分のプロジェクトをデプロイした場合、"Updates Available" が常に表示される問題に遭遇するかもしれません。これは、Vercel がこのプロジェクトをフォークする代わりに、デフォルトで新しいプロジェクトを作成するため、アップデートを正しく検出できないためです。 - -以下の手順で再デプロイすることをお勧めします: - -- 元のリポジトリを削除してください; -- ページの右上にあるフォークボタンを使って、このプロジェクトをフォークする; -- Vercel を選択し、再度デプロイする。[詳しいチュートリアルを参照](./docs/vercel-cn.md)。 - -### 自動アップデートを有効にする - -> Upstream Sync の実行に失敗した場合は、手動で一度フォークしてください。 - -プロジェクトをフォークした後、GitHub の制限により、フォークしたプロジェクトの Actions ページで Workflows と Upstream Sync Action を手動で有効にする必要があります。有効にすると、1 時間ごとに自動更新がスケジュールされます: - -![Automatic Updates](./docs/images/enable-actions.jpg) - -![Enable Automatic Updates](./docs/images/enable-actions-sync.jpg) - -### 手動でコードを更新する - -すぐに更新したい場合は、[GitHub ドキュメント](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) をチェックして、フォークしたプロジェクトを上流のコードと同期させる方法を学んでください。 - -このプロジェクトにスターをつけたり、ウォッチしたり、作者をフォローすることで、リリースの通知を受け取ることができます。 - -## アクセスパスワード - -> [簡体字中国語 > アクセスパスワードを増やす方法](./README_CN.md#配置页面访问密码) - -このプロジェクトではアクセス制御を制限しています。vercel の環境変数のページに `CODE` という環境変数を追加してください。その値は次のようにカンマで区切られたパスワードでなければなりません: - -``` -code1,code2,code3 -``` - -この環境変数を追加または変更した後は、変更を有効にするためにプロジェクトを再デプロイしてください。 - -## 環境変数 - -> [簡体字中国語 > API キー、アクセスパスワード、インターフェイスプロキシ設定方法](./README_CN.md#环境变量) - -### `OPENAI_API_KEY` (必須) - -OpenAI の api キー。 - -### `CODE` (オプション) - -カンマで区切られたアクセスパスワード。 - -### `BASE_URL` (オプション) - -> デフォルト: `https://api.openai.com` - -> 例: `http://your-openai-proxy.com` - -OpenAI api のリクエストベースの url をオーバーライドします。 - -### `OPENAI_ORG_ID` (オプション) - -OpenAI の組織 ID を指定します。 - -### `HIDE_USER_API_KEY` (オプション) - -> デフォルト: 空 - -ユーザーに自分の API キーを入力させたくない場合は、この値を 1 に設定する。 - -### `DISABLE_GPT4` (オプション) - -> デフォルト: 空 - -ユーザーに GPT-4 を使用させたくない場合は、この値を 1 に設定する。 - -### `HIDE_BALANCE_QUERY` (オプション) - -> デフォルト: 空 - -ユーザーに残高を照会させたくない場合は、この値を 1 に設定する。 - -## 必要条件 - -NodeJS >= 18、Docker >= 20 - -## Development - -> [簡体字中国語 > 二次開発の進め方](./README_CN.md#开发) - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) - -開発を始める前に、プロジェクトのルートに新しい `.env.local` ファイルを作成し、そこに api キーを置く必要があります: - -``` -OPENAI_API_KEY= - -# OpenAI サービスにアクセスできない場合は、この BASE_URL を使用してください -BASE_URL=https://chatgpt1.nextweb.fun/api/proxy -``` - -### ローカルデプロイ - -```shell -# 1. nodejs と yarn をまずインストールする -# 2. `.env.local` にローカルの env vars を設定する -# 3. 実行 -yarn install -yarn dev -``` - -## デプロイ - -> [簡体字中国語 > プライベートサーバーへのデプロイ方法](./README_CN.md#部署) - -### Docker (推奨) - -```shell -docker pull yidadaa/chatgpt-next-web - -docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY=sk-xxxx \ - -e CODE=your-password \ - yidadaa/chatgpt-next-web -``` - -プロキシの後ろでサービスを開始することができる: - -```shell -docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY=sk-xxxx \ - -e CODE=your-password \ - -e PROXY_URL=http://localhost:7890 \ - yidadaa/chatgpt-next-web -``` - -プロキシにパスワードが必要な場合: - -```shell --e PROXY_URL="http://127.0.0.1:7890 user pass" -``` - -### シェル - -```shell -bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) -``` - -## スクリーンショット - -![Settings](./docs/images/settings.png) - -![More](./docs/images/more.png) - -## 翻訳 - -新しい翻訳を追加したい場合は、この[ドキュメント](./docs/translation.md)をお読みください。 - -## 寄付 - -[コーヒーをおごる](https://www.buymeacoffee.com/yidadaa) - -## スペシャルサンクス - -### スポンサー - -> 寄付金額が 100 元以上のユーザーのみリストアップしています - -[@mushan0x0](https://github.com/mushan0x0) -[@ClarenceDan](https://github.com/ClarenceDan) -[@zhangjia](https://github.com/zhangjia) -[@hoochanlon](https://github.com/hoochanlon) -[@relativequantum](https://github.com/relativequantum) -[@desenmeng](https://github.com/desenmeng) -[@webees](https://github.com/webees) -[@chazzhou](https://github.com/chazzhou) -[@hauy](https://github.com/hauy) -[@Corwin006](https://github.com/Corwin006) -[@yankunsong](https://github.com/yankunsong) -[@ypwhs](https://github.com/ypwhs) -[@fxxxchao](https://github.com/fxxxchao) -[@hotic](https://github.com/hotic) -[@WingCH](https://github.com/WingCH) -[@jtung4](https://github.com/jtung4) -[@micozhu](https://github.com/micozhu) -[@jhansion](https://github.com/jhansion) -[@Sha1rholder](https://github.com/Sha1rholder) -[@AnsonHyq](https://github.com/AnsonHyq) -[@synwith](https://github.com/synwith) -[@piksonGit](https://github.com/piksonGit) - -### コントリビューター - -[コントリビューター達](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors) - -## ライセンス - -[MIT](https://opensource.org/license/mit/) diff --git a/README_KO.md b/README_KO.md deleted file mode 100644 index c4000c3a2..000000000 --- a/README_KO.md +++ /dev/null @@ -1,189 +0,0 @@ -
-프리뷰 - -

ChatGPT Next Web

- -개인 ChatGPT 웹 애플리케이션을 한 번의 클릭으로 무료로 배포하세요. - -[데모 Demo](https://chat-gpt-next-web.vercel.app/) / [피드백 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discord 참여](https://discord.gg/zrhvHCr79N) / [QQ 그룹](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [개발자에게 기부](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [기부 Donate](#기부-donate-usdt) - -[![Vercel로 배포하기](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) - -[![Gitpod에서 열기](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) - -![메인 화면](./docs/images/cover.png) - -
- -## 사용 시작 - -1. [OpenAI API Key](https://platform.openai.com/account/api-keys)를 준비합니다. -2. 오른쪽 버튼을 클릭하여 배포를 시작하십시오: - [![Vercel로 배포하기](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web). Github 계정으로 바로 로그인하십시오. API Key와 [페이지 접근 비밀번호](#페이지-접근-비밀번호-설정) CODE를 환경 변수 페이지에 입력하십시오. -3. 배포가 완료되면 사용을 시작하십시오. -4. (선택 사항) [사용자 정의 도메인 바인딩](https://vercel.com/docs/concepts/projects/domains/add-a-domain) : Vercel에서 할당한 도메인 DNS가 일부 지역에서 오염되어 있습니다. 사용자 정의 도메인을 바인딩하면 직접 연결할 수 있습니다. - -## 업데이트 유지 - -위의 단계대로 프로젝트를 배포한 경우 "업데이트가 있습니다"라는 메시지가 항상 표시될 수 있습니다. 이는 Vercel이 기본적으로 새 프로젝트를 생성하고이 프로젝트를 포크하지 않기 때문입니다. 이 문제는 업데이트를 올바르게 감지할 수 없습니다. -아래 단계를 따라 다시 배포하십시오: - -- 기존 저장소를 삭제합니다. -- 페이지 오른쪽 상단의 포크 버튼을 사용하여 이 프로젝트를 포크합니다. -- Vercel에서 다시 선택하여 배포하십시오. [자세한 튜토리얼 보기](./docs/vercel-cn.md#새-프로젝트-만드는-방법). - -### 자동 업데이트 활성화 - -> Upstream Sync 오류가 발생한 경우 수동으로 Sync Fork를 한 번 실행하십시오! - -프로젝트를 포크한 후 GitHub의 제한으로 인해 포크한 프로젝트의 동작 페이지에서 워크플로우를 수동으로 활성화해야 합니다. Upstream Sync Action을 활성화하면 매시간마다 자동 업데이트가 활성화됩니다: - -![자동 업데이트](./docs/images/enable-actions.jpg) - -![자동 업데이트 활성화](./docs/images/enable-actions-sync.jpg) - -### 수동으로 코드 업데이트 - -수동으로 즉시 업데이트하려면 [GitHub 문서](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork)에서 포크된 프로젝트를 어떻게 원본 코드와 동기화하는지 확인하십시오. - -이 프로젝트에 별표/감시를 부여하거나 작성자를 팔로우하여 새 기능 업데이트 알림을 받을 수 있습니다. - -## 페이지 접근 비밀번호 설정 - -> 비밀번호가 설정된 후, 사용자는 설정 페이지에서 접근 코드를 수동으로 입력하여 정상적으로 채팅할 수 있습니다. 그렇지 않으면 메시지를 통해 권한이 없는 상태가 표시됩니다. - -> **경고** : 비밀번호의 길이를 충분히 길게 설정하십시오. 최소 7 자리 이상이 좋습니다. 그렇지 않으면 [해킹될 수 있습니다](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518). - -이 프로젝트는 제한된 권한 제어 기능을 제공합니다. Vercel 프로젝트 컨트롤 패널의 환경 변수 페이지에서 `CODE`라는 환경 변수를 추가하십시오. 값은 쉼표로 구분된 사용자 정의 비밀번호로 설정됩니다. (아래 예시의 경우 `code1` `code2` `code3` 3개의 비밀번호가 생성됩니다.) - -``` -code1,code2,code3 -``` - -이 환경 변수를 추가하거나 수정한 후에는 프로젝트를 다시 배포하여 변경 사항을 적용해야 합니다. - -## 환경 변수 -> 이 프로젝트에서 대부분의 설정 요소들은 환경 변수를 통해 설정됩니다. [Vercel 환경변수 수정 방법.](./docs/vercel-ko.md)。 - -## OPENAI_API_KEY (필수 항목) - -OpenAI 키로, openai 계정 페이지에서 신청한 api key입니다. - -## CODE (선택 가능) - -접근 비밀번호로, 선택적입니다. 쉼표를 사용하여 여러 비밀번호를 구분할 수 있습니다. - -**경고** : 이 항목을 입력하지 않으면, 누구나 여러분이 배포한 웹사이트를 직접 사용할 수 있게 됩니다. 이로 인해 토큰이 빠르게 소진될 수 있으므로, 이 항목을 반드시 입력하는 것이 좋습니다. - -## BASE_URL (선택 가능) - -> 기본값: `https://api.openai.com` - -> 예시: `http://your-openai-proxy.com` - -OpenAI 인터페이스 프록시 URL입니다. 만약, 수동으로 openai 인터페이스 proxy를 설정했다면, 이 항목을 입력하셔야 합니다. - -**참고**: SSL 인증서 문제가 발생한 경우, BASE_URL의 프로토콜을 http로 설정하세요. - -## OPENAI_ORG_ID (선택 가능) - -OpenAI 내의 조직 ID를 지정합니다. - -## HIDE_USER_API_KEY (선택 가능) - -사용자가 API Key를 직접 입력하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요. - -## DISABLE_GPT4 (선택 가능) - -사용자가 GPT-4를 사용하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요. - -## HIDE_BALANCE_QUERY (선택 가능) - -사용자가 잔액을 조회하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요. - -## 개발 - -아래 버튼을 클릭하여 개발을 시작하세요: - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) - -코드 작성을 전, 프로젝트 루트 디렉토리에 `.env.local` 파일을 새로 만들고 해당 파일에 환경 변수를 입력해야 합니다: - -``` -OPENAI_API_KEY=<여기에 여러분의 api 키를 입력하세요> - -#중국 사용자들은 이 프로젝트에 포함된 프록시를 사용하여 개발할 수 있습니다. 또는 다른 프록시 주소를 자유롭게 선택할 수 있습니다. -BASE_URL=https://chatgpt1.nextweb.fun/api/proxy -``` - - -### 로컬 환경에서의 개발 - -1. nodejs 18과 yarn을 설치하세요. 자세한 사항은 ChatGPT에 문의하십시오. -2. `yarn install && yarn dev` 명령을 실행하세요. ⚠️ 주의: 이 명령은 로컬 개발 전용입니다. 배포용으로 사용하지 마십시오! -3. 로컬에서 배포하고 싶다면, `yarn install && yarn build && yarn start` 명령을 사용하세요. pm2와 함께 사용하여 프로세스를 보호하고, 강제 종료되지 않도록 할 수 있습니다. 자세한 내용은 ChatGPT에 문의하세요. - -## 배포 - -### 컨테이너 배포 (추천) - -> Docker 버전은 20 이상이어야 합니다. 그렇지 않으면 이미지를 찾을 수 없다는 메시지가 표시됩니다. - -> ⚠️ 주의: docker 버전은 대부분의 경우 최신 버전보다 1~2일 뒤처집니다. 따라서 배포 후 "업데이트 가능" 알림이 지속적으로 나타날 수 있으며, 이는 정상적인 현상입니다. - -```shell -docker pull yidadaa/chatgpt-next-web - -docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY=sk-xxxx \ - -e CODE=페이지 접근 비밀번호 \ - yidadaa/chatgpt-next-web -``` - -프록시를 지정하려면 다음을 사용하세요: - -```shell -docker run -d -p 3000:3000 \ - -e OPENAI_API_KEY=sk-xxxx \ - -e CODE=페이지 접근 비밀번호 \ - --net=host \ - -e PROXY_URL=http://127.0.0.1:7890 \ - yidadaa/chatgpt-next-web -``` - -로컬 프록시에 사용자 이름과 비밀번호가 필요한 경우, 아래와 같이 사용하세요: - -```shell --e PROXY_URL="http://127.0.0.1:7890 사용자이름 비밀번호" -``` - -다른 환경 변수를 지정해야 하는 경우, 위의 명령에 `-e 환경변수=환경변수값`을 추가하여 지정하세요. - -### 로컬 배포 - -콘솔에서 아래의 명령을 실행하세요: - -```shell -bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) -``` - -⚠️ 주의: 설치 중 문제가 발생한 경우, docker로 배포하세요. - - -## 감사의 말 - -### 기부자 - -> 영문 버전 참조. - -### 기여자 - -[프로젝트 기여자 목록 보기](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors) - -### 관련 프로젝트 -- [one-api](https://github.com/songquanpeng/one-api): 통합 대형 모델 할당 관리 플랫폼, 주요 대형 언어 모델 모두 지원 - -## 오픈소스 라이센스 - -[MIT](https://opensource.org/license/mit/) - diff --git a/app/components/chat.tsx b/app/components/chat.tsx index cca096eb8..a30e505dd 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -73,11 +73,10 @@ import { showPrompt, showToast, } from "./ui-lib"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { CHAT_PAGE_SIZE, LAST_INPUT_KEY, - MAX_RENDER_MSG_COUNT, Path, REQUEST_TIMEOUT_MS, UNFINISHED_INPUT, diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 795469a96..cfe81ee48 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -50,7 +50,13 @@ import Locale, { } from "../locales"; import { copyToClipboard } from "../utils"; import Link from "next/link"; -import { Path, RELEASE_URL, STORAGE_KEY, UPDATE_URL } from "../constant"; +import { + OPENAI_BASE_URL, + Path, + RELEASE_URL, + STORAGE_KEY, + UPDATE_URL, +} from "../constant"; import { Prompt, SearchService, usePromptStore } from "../store/prompt"; import { ErrorBoundary } from "./error"; import { InputRange } from "./input-range"; @@ -571,13 +577,19 @@ export function Settings() { console.log("[Update] remote version ", updateStore.remoteVersion); } + const accessStore = useAccessStore(); + const shouldHideBalanceQuery = useMemo(() => { + const isOpenAiUrl = accessStore.openaiUrl.includes(OPENAI_BASE_URL); + return accessStore.hideBalanceQuery || isOpenAiUrl; + }, [accessStore.hideBalanceQuery, accessStore.openaiUrl]); + const usage = { used: updateStore.used, subscription: updateStore.subscription, }; const [loadingUsage, setLoadingUsage] = useState(false); function checkUsage(force = false) { - if (accessStore.hideBalanceQuery) { + if (shouldHideBalanceQuery) { return; } @@ -587,7 +599,6 @@ export function Settings() { }); } - const accessStore = useAccessStore(); const enabledAccessControl = useMemo( () => accessStore.enabledAccessControl(), // eslint-disable-next-line react-hooks/exhaustive-deps @@ -916,7 +927,7 @@ export function Settings() { ) : null} - {!accessStore.hideBalanceQuery ? ( + {!shouldHideBalanceQuery ? ( { isVercel: !!process.env.VERCEL, hideUserApiKey: !!process.env.HIDE_USER_API_KEY, disableGPT4: !!process.env.DISABLE_GPT4, - hideBalanceQuery: !!process.env.HIDE_BALANCE_QUERY, + hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY, }; }; diff --git a/app/constant.ts b/app/constant.ts index 635fbeaea..8d36e0b55 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -10,6 +10,7 @@ export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; export const DEFAULT_CORS_HOST = "https://ab.nextweb.fun"; export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`; +export const OPENAI_BASE_URL = "https://api.openai.com"; export enum Path { Home = "/", From c5ca278253456c7d65bcd877b0ca40da0b8026eb Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 8 Nov 2023 00:30:02 +0800 Subject: [PATCH 12/67] feat: close #2908 allow to disable parse settings from link --- README.md | 6 ++++++ README_CN.md | 4 ++++ app/api/config/route.ts | 1 + app/components/chat.tsx | 9 ++++++--- app/components/settings.tsx | 12 +++++++++--- app/config/server.ts | 2 ++ app/store/access.ts | 10 +--------- 7 files changed, 29 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index b24470c64..91e03d800 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,12 @@ If you do not want users to use GPT-4, set this value to 1. If you do want users to query balance, set this value to 1, or you should set it to 0. +### `DISABLE_FAST_LINK` (optional) + +> Default: Empty + +If you want to disable parse settings from url, set this to 1. + ## Requirements NodeJS >= 18, Docker >= 20 diff --git a/README_CN.md b/README_CN.md index e16c47a8a..13b97417d 100644 --- a/README_CN.md +++ b/README_CN.md @@ -102,6 +102,10 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 如果你想启用余额查询功能,将此环境变量设置为 1 即可。 +### `DISABLE_FAST_LINK` (可选) + +如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。 + ## 开发 点击下方按钮,开始二次开发: diff --git a/app/api/config/route.ts b/app/api/config/route.ts index 0bfc955bf..44af8d3b9 100644 --- a/app/api/config/route.ts +++ b/app/api/config/route.ts @@ -11,6 +11,7 @@ const DANGER_CONFIG = { hideUserApiKey: serverConfig.hideUserApiKey, disableGPT4: serverConfig.disableGPT4, hideBalanceQuery: serverConfig.hideBalanceQuery, + disableFastLink: serverConfig.disableFastLink, }; declare global { diff --git a/app/components/chat.tsx b/app/components/chat.tsx index a30e505dd..18353e8fc 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -975,14 +975,17 @@ function _Chat() { doSubmit(text); }, code: (text) => { + if (accessStore.disableFastLink) return; console.log("[Command] got code from url: ", text); showConfirm(Locale.URLCommand.Code + `code = ${text}`).then((res) => { if (res) { - accessStore.updateCode(text); + accessStore.update((access) => (access.accessCode = text)); } }); }, settings: (text) => { + if (accessStore.disableFastLink) return; + try { const payload = JSON.parse(text) as { key?: string; @@ -998,10 +1001,10 @@ function _Chat() { ).then((res) => { if (!res) return; if (payload.key) { - accessStore.updateToken(payload.key); + accessStore.update((access) => (access.token = payload.key!)); } if (payload.url) { - accessStore.updateOpenAiUrl(payload.url); + accessStore.update((access) => (access.openaiUrl = payload.url!)); } }); } diff --git a/app/components/settings.tsx b/app/components/settings.tsx index cfe81ee48..572c0743a 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -888,7 +888,9 @@ export function Settings() { type="text" placeholder={Locale.Settings.AccessCode.Placeholder} onChange={(e) => { - accessStore.updateCode(e.currentTarget.value); + accessStore.update( + (access) => (access.accessCode = e.currentTarget.value), + ); }} /> @@ -907,7 +909,9 @@ export function Settings() { value={accessStore.openaiUrl} placeholder="https://api.openai.com/" onChange={(e) => - accessStore.updateOpenAiUrl(e.currentTarget.value) + accessStore.update( + (access) => (access.openaiUrl = e.currentTarget.value), + ) } > @@ -920,7 +924,9 @@ export function Settings() { type="text" placeholder={Locale.Settings.Token.Placeholder} onChange={(e) => { - accessStore.updateToken(e.currentTarget.value); + accessStore.update( + (access) => (access.token = e.currentTarget.value), + ); }} /> diff --git a/app/config/server.ts b/app/config/server.ts index 62b435e93..2df806fed 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -13,6 +13,7 @@ declare global { BUILD_MODE?: "standalone" | "export"; BUILD_APP?: string; // is building desktop app ENABLE_BALANCE_QUERY?: string; // allow user to query balance or not + DISABLE_FAST_LINK?: string; // disallow parse settings from url or not } } } @@ -48,5 +49,6 @@ export const getServerSideConfig = () => { hideUserApiKey: !!process.env.HIDE_USER_API_KEY, disableGPT4: !!process.env.DISABLE_GPT4, hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY, + disableFastLink: !!process.env.DISABLE_FAST_LINK, }; }; diff --git a/app/store/access.ts b/app/store/access.ts index 9eaa81e5e..3d889f6e7 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -16,6 +16,7 @@ const DEFAULT_ACCESS_STATE = { hideUserApiKey: false, hideBalanceQuery: false, disableGPT4: false, + disableFastLink: false, openaiUrl: DEFAULT_OPENAI_URL, }; @@ -29,15 +30,6 @@ export const useAccessStore = createPersistStore( return get().needCode; }, - updateCode(code: string) { - set(() => ({ accessCode: code?.trim() })); - }, - updateToken(token: string) { - set(() => ({ token: token?.trim() })); - }, - updateOpenAiUrl(url: string) { - set(() => ({ openaiUrl: url?.trim() })); - }, isAuthorized() { this.fetch(); From c7321fddfbe92dc0e9ef81862aac3daa93653881 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 8 Nov 2023 00:34:31 +0800 Subject: [PATCH 13/67] fixup --- app/components/auth.tsx | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index b82d0e894..577d77542 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -12,11 +12,16 @@ import { getClientConfig } from "../config/client"; export function AuthPage() { const navigate = useNavigate(); - const access = useAccessStore(); + const accessStore = useAccessStore(); const goHome = () => navigate(Path.Home); const goChat = () => navigate(Path.Chat); - const resetAccessCode = () => { access.updateCode(""); access.updateToken(""); }; // Reset access code to empty string + const resetAccessCode = () => { + accessStore.update((access) => { + access.token = ""; + access.accessCode = ""; + }); + }; // Reset access code to empty string useEffect(() => { if (getClientConfig()?.isApp) { @@ -38,21 +43,25 @@ export function AuthPage() { className={styles["auth-input"]} type="password" placeholder={Locale.Auth.Input} - value={access.accessCode} + value={accessStore.accessCode} onChange={(e) => { - access.updateCode(e.currentTarget.value); + accessStore.update( + (access) => (access.accessCode = e.currentTarget.value), + ); }} /> - {!access.hideUserApiKey ? ( + {!accessStore.hideUserApiKey ? ( <>
{Locale.Auth.SubTips}
{ - access.updateToken(e.currentTarget.value); + accessStore.update( + (access) => (access.token = e.currentTarget.value), + ); }} /> From 54a53328341af2d07db19e56db5febdaac225a87 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 8 Nov 2023 01:04:20 +0800 Subject: [PATCH 14/67] feat: sort model by name --- app/store/config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/store/config.ts b/app/store/config.ts index 184355c94..0fbc26dfe 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -133,7 +133,9 @@ export const useAppConfig = createPersistStore( .customModels.split(",") .filter((v) => !!v && v.length > 0) .map((m) => ({ name: m, available: true })); - return get().models.concat(customModels); + const allModels = get().models.concat(customModels); + allModels.sort((a, b) => (a.name < b.name ? -1 : 1)); + return allModels; }, }), { From dbee5bc515fd9bad33fcdce0f8b176494db2d02a Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 8 Nov 2023 01:05:07 +0800 Subject: [PATCH 15/67] Update tauri.conf.json --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e530203f6..649e3816d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.9" + "version": "2.9.10" }, "tauri": { "allowlist": { From 9cb889c34f2653c812b044c1e6ebd264d98067a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E5=BF=86?= Date: Wed, 8 Nov 2023 07:09:52 +0800 Subject: [PATCH 16/67] docs: up to date --- .env.template | 8 ++++---- app/api/common.ts | 11 +++++++---- docker-compose.yml | 12 +++++++++--- docs/cloudflare-pages-cn.md | 7 +++++-- docs/cloudflare-pages-en.md | 13 +++++++++---- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/.env.template b/.env.template index 1ff575f11..5e73e6d85 100644 --- a/.env.template +++ b/.env.template @@ -24,10 +24,10 @@ HIDE_USER_API_KEY= # (optional) # Default: Empty -# If you do not want users to use GPT-4, set this value to 1. -DISABLE_GPT4= +# If you do want users to query balance, set this value to 1. +ENABLE_BALANCE_QUERY= # (optional) # Default: Empty -# If you do not want users to query balance, set this value to 1. -HIDE_BALANCE_QUERY= +# If you want to disable parse settings from url, set this value to 1. +DISABLE_FAST_LINK= diff --git a/app/api/common.ts b/app/api/common.ts index cd2936ee3..0af7761d8 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -20,7 +20,7 @@ export async function requestOpenai(req: NextRequest) { baseUrl = `${PROTOCOL}://${baseUrl}`; } - if (baseUrl.endsWith('/')) { + if (baseUrl.endsWith("/")) { baseUrl = baseUrl.slice(0, -1); } @@ -31,9 +31,12 @@ export async function requestOpenai(req: NextRequest) { console.log("[Org ID]", process.env.OPENAI_ORG_ID); } - const timeoutId = setTimeout(() => { - controller.abort(); - }, 10 * 60 * 1000); + const timeoutId = setTimeout( + () => { + controller.abort(); + }, + 10 * 60 * 1000, + ); const fetchUrl = `${baseUrl}/${openaiPath}`; const fetchOptions: RequestInit = { diff --git a/docker-compose.yml b/docker-compose.yml index c4312cfe3..57ca12e03 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ -version: '3.9' +version: "3.9" services: - chatgpt-next-web: + chatgpt-next-web: profiles: ["no-proxy"] container_name: chatgpt-next-web image: yidadaa/chatgpt-next-web @@ -13,8 +13,11 @@ services: - OPENAI_ORG_ID=$OPENAI_ORG_ID - HIDE_USER_API_KEY=$HIDE_USER_API_KEY - DISABLE_GPT4=$DISABLE_GPT4 + - ENABLE_BALANCE_QUERY=$ENABLE_BALANCE_QUERY + - DISABLE_FAST_LINK=$DISABLE_FAST_LINK + - OPENAI_SB=$OPENAI_SB - chatgpt-next-web-proxy: + chatgpt-next-web-proxy: profiles: ["proxy"] container_name: chatgpt-next-web-proxy image: yidadaa/chatgpt-next-web @@ -28,3 +31,6 @@ services: - OPENAI_ORG_ID=$OPENAI_ORG_ID - HIDE_USER_API_KEY=$HIDE_USER_API_KEY - DISABLE_GPT4=$DISABLE_GPT4 + - ENABLE_BALANCE_QUERY=$ENABLE_BALANCE_QUERY + - DISABLE_FAST_LINK=$DISABLE_FAST_LINK + - OPENAI_SB=$OPENAI_SB diff --git a/docs/cloudflare-pages-cn.md b/docs/cloudflare-pages-cn.md index 2f9a99f2d..e1567af03 100644 --- a/docs/cloudflare-pages-cn.md +++ b/docs/cloudflare-pages-cn.md @@ -1,6 +1,7 @@ # Cloudflare Pages 部署指南 ## 如何新建项目 + 在 Github 上 fork 本项目,然后登录到 dash.cloudflare.com 并进入 Pages。 1. 点击 "Create a project"。 @@ -30,10 +31,12 @@ - `OPENAI_ORG_ID= 可选填,指定 OpenAI 中的组织 ID` - `HIDE_USER_API_KEY=1 可选,不让用户自行填入 API Key` - `DISABLE_GPT4=1 可选,不让用户使用 GPT-4` - + - `ENABLE_BALANCE_QUERY=1 可选,启用余额查询功能` + - `DISABLE_FAST_LINK=1 可选,禁用从链接解析预制设置` + 12. 点击 "Save and Deploy"。 13. 点击 "Cancel deployment",因为需要填写 Compatibility flags。 14. 前往 "Build settings"、"Functions",找到 "Compatibility flags"。 15. 在 "Configure Production compatibility flag" 和 "Configure Preview compatibility flag" 中填写 "nodejs_compat"。 16. 前往 "Deployments",点击 "Retry deployment"。 -17. Enjoy. \ No newline at end of file +17. Enjoy. diff --git a/docs/cloudflare-pages-en.md b/docs/cloudflare-pages-en.md index 2279ff232..c5d550438 100644 --- a/docs/cloudflare-pages-en.md +++ b/docs/cloudflare-pages-en.md @@ -1,6 +1,7 @@ # Cloudflare Pages Deployment Guide ## How to create a new project + Fork this project on GitHub, then log in to dash.cloudflare.com and go to Pages. 1. Click "Create a project". @@ -11,12 +12,13 @@ Fork this project on GitHub, then log in to dash.cloudflare.com and go to Pages. 6. For "Project name" and "Production branch", use the default values or change them as needed. 7. In "Build Settings", choose the "Framework presets" option and select "Next.js". 8. Do not use the default "Build command" due to a node:buffer bug. Instead, use the following command: - ``` - npx @cloudflare/next-on-pages --experimental-minify - ``` + ``` + npx @cloudflare/next-on-pages --experimental-minify + ``` 9. For "Build output directory", use the default value and do not modify it. 10. Do not modify "Root Directory". 11. For "Environment variables", click ">" and then "Add variable". Fill in the following information: + - `NODE_VERSION=20.1` - `NEXT_TELEMETRY_DISABLE=1` - `OPENAI_API_KEY=your_own_API_key` @@ -29,7 +31,10 @@ Fork this project on GitHub, then log in to dash.cloudflare.com and go to Pages. - `OPENAI_ORG_ID= Optional, specify the organization ID in OpenAI` - `HIDE_USER_API_KEY=1 Optional, do not allow users to enter their own API key` - `DISABLE_GPT4=1 Optional, do not allow users to use GPT-4` - + - `ENABLE_BALANCE_QUERY=1 Optional, allow users to query balance` + - `DISABLE_FAST_LINK=1 Optional, disable parse settings from url` + - `OPENAI_SB=1 Optional,use the third-party OpenAI-SB API` + 12. Click "Save and Deploy". 13. Click "Cancel deployment" because you need to fill in Compatibility flags. 14. Go to "Build settings", "Functions", and find "Compatibility flags". From 3a519612a0fcd7a05fe3d5d01cb9f88c0caa2a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E5=BF=86?= Date: Wed, 8 Nov 2023 15:53:45 +0800 Subject: [PATCH 17/67] docs: fix typo in #3171 --- .env.template | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.env.template b/.env.template index 5e73e6d85..3e3290369 100644 --- a/.env.template +++ b/.env.template @@ -17,6 +17,11 @@ BASE_URL= # Default: Empty OPENAI_ORG_ID= +# (optional) +# Default: Empty +# If you do not want users to use GPT-4, set this value to 1. +DISABLE_GPT4= + # (optional) # Default: Empty # If you do not want users to input their own API key, set this value to 1. From 6ab4c9be2e66ebb203d7dc50a1ecf4ab30fa6885 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 8 Nov 2023 17:40:28 +0800 Subject: [PATCH 18/67] Update cloudflare-pages-cn.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cloudflare 构建命令 --- docs/cloudflare-pages-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cloudflare-pages-cn.md b/docs/cloudflare-pages-cn.md index e1567af03..137bb9dc3 100644 --- a/docs/cloudflare-pages-cn.md +++ b/docs/cloudflare-pages-cn.md @@ -13,7 +13,7 @@ 7. 在 "Build Settings" 中,选择 "Framework presets" 选项并选择 "Next.js"。 8. 由于 node:buffer 的 bug,暂时不要使用默认的 "Build command"。请使用以下命令: ``` - npx https://prerelease-registry.devprod.cloudflare.dev/next-on-pages/runs/4930842298/npm-package-next-on-pages-230 --experimental-minify + npx @cloudflare/next-on-pages@1.5.0 ``` 9. 对于 "Build output directory",使用默认值并且不要修改。 10. 不要修改 "Root Directory"。 From 8c0ba1aee24f2f076c48e89a5e872466684afc85 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 9 Nov 2023 01:51:33 +0800 Subject: [PATCH 19/67] feat: close #2954 chat summary should be copyable --- app/components/chat.tsx | 1 + app/styles/globals.scss | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 18353e8fc..a0b7307c2 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -143,6 +143,7 @@ export function SessionConfigModel(props: { onClose: () => void }) { extraListItems={ session.mask.modelConfig.sendMemory ? ( diff --git a/app/styles/globals.scss b/app/styles/globals.scss index def28680c..aa22b7d4f 100644 --- a/app/styles/globals.scss +++ b/app/styles/globals.scss @@ -357,3 +357,7 @@ pre { overflow: hidden; text-overflow: ellipsis; } + +.copyable { + user-select: text; +} From fbc02367484416a98d20b86d9994d019869d78a8 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 9 Nov 2023 02:03:05 +0800 Subject: [PATCH 20/67] fix: #3174 should prompt to confirm to delete chat --- app/components/chat-list.tsx | 10 ++++++++-- app/components/sidebar.tsx | 5 +++-- app/store/chat.ts | 27 --------------------------- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index 7ba555852..f76b369f1 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -18,6 +18,7 @@ import { MaskAvatar } from "./mask"; import { Mask } from "../store/mask"; import { useRef, useEffect } from "react"; import { showConfirm } from "./ui-lib"; +import { useMobileScreen } from "../utils"; export function ChatItem(props: { onClick?: () => void; @@ -80,7 +81,11 @@ export function ChatItem(props: {
{ + props.onDelete?.(); + e.preventDefault(); + e.stopPropagation(); + }} >
@@ -101,6 +106,7 @@ export function ChatList(props: { narrow?: boolean }) { ); const chatStore = useChatStore(); const navigate = useNavigate(); + const isMobileScreen = useMobileScreen(); const onDragEnd: OnDragEndResponder = (result) => { const { destination, source } = result; @@ -142,7 +148,7 @@ export function ChatList(props: { narrow?: boolean }) { }} onDelete={async () => { if ( - !props.narrow || + (!props.narrow && !isMobileScreen) || (await showConfirm(Locale.Home.DeleteChat)) ) { chatStore.deleteSession(i); diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 85af3ed2e..beeee865a 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useCallback, useMemo } from "react"; +import { useEffect, useRef, useMemo } from "react"; import styles from "./home.module.scss"; @@ -8,6 +8,7 @@ import GithubIcon from "../icons/github.svg"; import ChatGptIcon from "../icons/chatgpt.svg"; import AddIcon from "../icons/add.svg"; import CloseIcon from "../icons/close.svg"; +import DeleteIcon from "../icons/delete.svg"; import MaskIcon from "../icons/mask.svg"; import PluginIcon from "../icons/plugin.svg"; import DragIcon from "../icons/drag.svg"; @@ -202,7 +203,7 @@ export function SideBar(props: { className?: string }) {
} + icon={} onClick={async () => { if (await showConfirm(Locale.Home.DeleteChat)) { chatStore.deleteSession(chatStore.currentSessionIndex); diff --git a/app/store/chat.ts b/app/store/chat.ts index 95822c191..ff7eb51b5 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -85,33 +85,6 @@ function getSummarizeModel(currentModel: string) { return currentModel.startsWith("gpt") ? SUMMARIZE_MODEL : currentModel; } -interface ChatStore { - sessions: ChatSession[]; - currentSessionIndex: number; - clearSessions: () => void; - moveSession: (from: number, to: number) => void; - selectSession: (index: number) => void; - newSession: (mask?: Mask) => void; - deleteSession: (index: number) => void; - currentSession: () => ChatSession; - nextSession: (delta: number) => void; - onNewMessage: (message: ChatMessage) => void; - onUserInput: (content: string) => Promise; - summarizeSession: () => void; - updateStat: (message: ChatMessage) => void; - updateCurrentSession: (updater: (session: ChatSession) => void) => void; - updateMessage: ( - sessionIndex: number, - messageIndex: number, - updater: (message?: ChatMessage) => void, - ) => void; - resetSession: () => void; - getMessagesWithMemory: () => ChatMessage[]; - getMemoryPrompt: () => ChatMessage; - - clearAllData: () => void; -} - function countMessages(msgs: ChatMessage[]) { return msgs.reduce((pre, cur) => pre + estimateTokenLength(cur.content), 0); } From d93f05f51163488525b3957bedfa0ed8a6167b8c Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 9 Nov 2023 03:01:29 +0800 Subject: [PATCH 21/67] feat: close #3187 use CUSTOM_MODELS to control model list --- README.md | 7 ++++++ README_CN.md | 6 +++++ app/api/common.ts | 31 ++++++++++++------------- app/api/config/route.ts | 1 + app/components/chat.tsx | 12 ++++------ app/components/model-config.tsx | 7 +++--- app/config/server.ts | 17 +++++++++++++- app/store/access.ts | 7 +----- app/store/config.ts | 10 +-------- app/utils/hooks.ts | 16 +++++++++++++ app/utils/model.ts | 40 +++++++++++++++++++++++++++++++++ 11 files changed, 112 insertions(+), 42 deletions(-) create mode 100644 app/utils/hooks.ts create mode 100644 app/utils/model.ts diff --git a/README.md b/README.md index 91e03d800..3973c84bf 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,13 @@ If you do want users to query balance, set this value to 1, or you should set it If you want to disable parse settings from url, set this to 1. +### `CUSTOM_MODELS` (optional) + +> Default: Empty +> Example: `+llama,+claude-2,-gpt-3.5-turbo` means add `llama, claude-2` to model list, and remove `gpt-3.5-turbo` from list. + +To control custom models, use `+` to add a custom model, use `-` to hide a model, separated by comma. + ## Requirements NodeJS >= 18, Docker >= 20 diff --git a/README_CN.md b/README_CN.md index 13b97417d..d8e9553e1 100644 --- a/README_CN.md +++ b/README_CN.md @@ -106,6 +106,12 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。 +### `CUSTOM_MODELS` (可选) + +> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo` 表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`。 + +用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,用英文逗号隔开。 + ## 开发 点击下方按钮,开始二次开发: diff --git a/app/api/common.ts b/app/api/common.ts index 0af7761d8..a1decd42f 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -1,10 +1,9 @@ import { NextRequest, NextResponse } from "next/server"; +import { getServerSideConfig } from "../config/server"; +import { DEFAULT_MODELS, OPENAI_BASE_URL } from "../constant"; +import { collectModelTable, collectModels } from "../utils/model"; -export const OPENAI_URL = "api.openai.com"; -const DEFAULT_PROTOCOL = "https"; -const PROTOCOL = process.env.PROTOCOL || DEFAULT_PROTOCOL; -const BASE_URL = process.env.BASE_URL || OPENAI_URL; -const DISABLE_GPT4 = !!process.env.DISABLE_GPT4; +const serverConfig = getServerSideConfig(); export async function requestOpenai(req: NextRequest) { const controller = new AbortController(); @@ -14,10 +13,10 @@ export async function requestOpenai(req: NextRequest) { "", ); - let baseUrl = BASE_URL; + let baseUrl = serverConfig.baseUrl ?? OPENAI_BASE_URL; if (!baseUrl.startsWith("http")) { - baseUrl = `${PROTOCOL}://${baseUrl}`; + baseUrl = `https://${baseUrl}`; } if (baseUrl.endsWith("/")) { @@ -26,10 +25,7 @@ export async function requestOpenai(req: NextRequest) { console.log("[Proxy] ", openaiPath); console.log("[Base Url]", baseUrl); - - if (process.env.OPENAI_ORG_ID) { - console.log("[Org ID]", process.env.OPENAI_ORG_ID); - } + console.log("[Org ID]", serverConfig.openaiOrgId); const timeoutId = setTimeout( () => { @@ -58,18 +54,23 @@ export async function requestOpenai(req: NextRequest) { }; // #1815 try to refuse gpt4 request - if (DISABLE_GPT4 && req.body) { + if (serverConfig.customModels && req.body) { try { + const modelTable = collectModelTable( + DEFAULT_MODELS, + serverConfig.customModels, + ); const clonedBody = await req.text(); fetchOptions.body = clonedBody; - const jsonBody = JSON.parse(clonedBody); + const jsonBody = JSON.parse(clonedBody) as { model?: string }; - if ((jsonBody?.model ?? "").includes("gpt-4")) { + // not undefined and is false + if (modelTable[jsonBody?.model ?? ""] === false) { return NextResponse.json( { error: true, - message: "you are not allowed to use gpt-4 model", + message: `you are not allowed to use ${jsonBody?.model} model`, }, { status: 403, diff --git a/app/api/config/route.ts b/app/api/config/route.ts index 44af8d3b9..db84fba17 100644 --- a/app/api/config/route.ts +++ b/app/api/config/route.ts @@ -12,6 +12,7 @@ const DANGER_CONFIG = { disableGPT4: serverConfig.disableGPT4, hideBalanceQuery: serverConfig.hideBalanceQuery, disableFastLink: serverConfig.disableFastLink, + customModels: serverConfig.customModels, }; declare global { diff --git a/app/components/chat.tsx b/app/components/chat.tsx index a0b7307c2..9afb49f7a 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -88,6 +88,7 @@ import { ChatCommandPrefix, useChatCommand, useCommand } from "../command"; import { prettyObject } from "../utils/format"; import { ExportMessageModal } from "./exporter"; import { getClientConfig } from "../config/client"; +import { useAllModels } from "../utils/hooks"; const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , @@ -430,14 +431,9 @@ export function ChatActions(props: { // switch model const currentModel = chatStore.currentSession().mask.modelConfig.model; - const models = useMemo( - () => - config - .allModels() - .filter((m) => m.available) - .map((m) => m.name), - [config], - ); + const models = useAllModels() + .filter((m) => m.available) + .map((m) => m.name); const [showModelSelector, setShowModelSelector] = useState(false); return ( diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx index 63950a40d..6e4c9bcb1 100644 --- a/app/components/model-config.tsx +++ b/app/components/model-config.tsx @@ -1,14 +1,15 @@ -import { ModalConfigValidator, ModelConfig, useAppConfig } from "../store"; +import { ModalConfigValidator, ModelConfig } from "../store"; import Locale from "../locales"; import { InputRange } from "./input-range"; import { ListItem, Select } from "./ui-lib"; +import { useAllModels } from "../utils/hooks"; export function ModelConfigList(props: { modelConfig: ModelConfig; updateConfig: (updater: (config: ModelConfig) => void) => void; }) { - const config = useAppConfig(); + const allModels = useAllModels(); return ( <> @@ -24,7 +25,7 @@ export function ModelConfigList(props: { ); }} > - {config.allModels().map((v, i) => ( + {allModels.map((v, i) => ( diff --git a/app/config/server.ts b/app/config/server.ts index 2df806fed..007c39738 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -1,4 +1,5 @@ import md5 from "spark-md5"; +import { DEFAULT_MODELS } from "../constant"; declare global { namespace NodeJS { @@ -7,6 +8,7 @@ declare global { CODE?: string; BASE_URL?: string; PROXY_URL?: string; + OPENAI_ORG_ID?: string; VERCEL?: string; HIDE_USER_API_KEY?: string; // disable user's api key input DISABLE_GPT4?: string; // allow user to use gpt-4 or not @@ -14,6 +16,7 @@ declare global { BUILD_APP?: string; // is building desktop app ENABLE_BALANCE_QUERY?: string; // allow user to query balance or not DISABLE_FAST_LINK?: string; // disallow parse settings from url or not + CUSTOM_MODELS?: string; // to control custom models } } } @@ -38,6 +41,16 @@ export const getServerSideConfig = () => { ); } + let disableGPT4 = !!process.env.DISABLE_GPT4; + let customModels = process.env.CUSTOM_MODELS ?? ""; + + if (disableGPT4) { + if (customModels) customModels += ","; + customModels += DEFAULT_MODELS.filter((m) => m.name.startsWith("gpt-4")) + .map((m) => "-" + m.name) + .join(","); + } + return { apiKey: process.env.OPENAI_API_KEY, code: process.env.CODE, @@ -45,10 +58,12 @@ export const getServerSideConfig = () => { needCode: ACCESS_CODES.size > 0, baseUrl: process.env.BASE_URL, proxyUrl: process.env.PROXY_URL, + openaiOrgId: process.env.OPENAI_ORG_ID, isVercel: !!process.env.VERCEL, hideUserApiKey: !!process.env.HIDE_USER_API_KEY, - disableGPT4: !!process.env.DISABLE_GPT4, + disableGPT4, hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY, disableFastLink: !!process.env.DISABLE_FAST_LINK, + customModels, }; }; diff --git a/app/store/access.ts b/app/store/access.ts index 3d889f6e7..f87e44a2a 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -17,6 +17,7 @@ const DEFAULT_ACCESS_STATE = { hideBalanceQuery: false, disableGPT4: false, disableFastLink: false, + customModels: "", openaiUrl: DEFAULT_OPENAI_URL, }; @@ -52,12 +53,6 @@ export const useAccessStore = createPersistStore( .then((res: DangerConfig) => { console.log("[Config] got config from server", res); set(() => ({ ...res })); - - if (res.disableGPT4) { - DEFAULT_MODELS.forEach( - (m: any) => (m.available = !m.name.startsWith("gpt-4")), - ); - } }) .catch(() => { console.error("[Config] failed to fetch config"); diff --git a/app/store/config.ts b/app/store/config.ts index 0fbc26dfe..5fcd6ff51 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -128,15 +128,7 @@ export const useAppConfig = createPersistStore( })); }, - allModels() { - const customModels = get() - .customModels.split(",") - .filter((v) => !!v && v.length > 0) - .map((m) => ({ name: m, available: true })); - const allModels = get().models.concat(customModels); - allModels.sort((a, b) => (a.name < b.name ? -1 : 1)); - return allModels; - }, + allModels() {}, }), { name: StoreKey.Config, diff --git a/app/utils/hooks.ts b/app/utils/hooks.ts new file mode 100644 index 000000000..f6bfae673 --- /dev/null +++ b/app/utils/hooks.ts @@ -0,0 +1,16 @@ +import { useMemo } from "react"; +import { useAccessStore, useAppConfig } from "../store"; +import { collectModels } from "./model"; + +export function useAllModels() { + const accessStore = useAccessStore(); + const configStore = useAppConfig(); + const models = useMemo(() => { + return collectModels( + configStore.models, + [accessStore.customModels, configStore.customModels].join(","), + ); + }, [accessStore.customModels, configStore.customModels, configStore.models]); + + return models; +} diff --git a/app/utils/model.ts b/app/utils/model.ts new file mode 100644 index 000000000..23090f9d2 --- /dev/null +++ b/app/utils/model.ts @@ -0,0 +1,40 @@ +import { LLMModel } from "../client/api"; + +export function collectModelTable( + models: readonly LLMModel[], + customModels: string, +) { + const modelTable: Record = {}; + + // default models + models.forEach((m) => (modelTable[m.name] = m.available)); + + // server custom models + customModels + .split(",") + .filter((v) => !!v && v.length > 0) + .map((m) => { + if (m.startsWith("+")) { + modelTable[m.slice(1)] = true; + } else if (m.startsWith("-")) { + modelTable[m.slice(1)] = false; + } else modelTable[m] = true; + }); + return modelTable; +} + +/** + * Generate full model table. + */ +export function collectModels( + models: readonly LLMModel[], + customModels: string, +) { + const modelTable = collectModelTable(models, customModels); + const allModels = Object.keys(modelTable).map((m) => ({ + name: m, + available: modelTable[m], + })); + + return allModels; +} From d0a1d910d4dae62351ae0273562cc6067e3e6ed9 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 9 Nov 2023 03:19:13 +0800 Subject: [PATCH 22/67] fix: #3186 enable max_tokens in chat payload --- app/client/platforms/openai.ts | 1 + app/components/model-config.tsx | 4 ++-- app/store/config.ts | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index fd4eb59ce..97392a004 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -70,6 +70,7 @@ export class ChatGPTApi implements LLMApi { presence_penalty: modelConfig.presence_penalty, frequency_penalty: modelConfig.frequency_penalty, top_p: modelConfig.top_p, + max_tokens: Math.max(modelConfig.max_tokens, 1024), }; console.log("[Request] openai payload: ", requestPayload); diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx index 6e4c9bcb1..1c730e144 100644 --- a/app/components/model-config.tsx +++ b/app/components/model-config.tsx @@ -76,8 +76,8 @@ export function ModelConfigList(props: { > props.updateConfig( diff --git a/app/store/config.ts b/app/store/config.ts index 5fcd6ff51..17eb88c30 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -49,7 +49,7 @@ export const DEFAULT_CONFIG = { model: "gpt-3.5-turbo" as ModelType, temperature: 0.5, top_p: 1, - max_tokens: 2000, + max_tokens: 8192, presence_penalty: 0, frequency_penalty: 0, sendMemory: true, @@ -82,7 +82,7 @@ export const ModalConfigValidator = { return x as ModelType; }, max_tokens(x: number) { - return limitNumber(x, 0, 100000, 2000); + return limitNumber(x, 0, 512000, 1024); }, presence_penalty(x: number) { return limitNumber(x, -2, 2, 0); From 87e3d663a2955f7344f214b355f8a8d03032ea65 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Thu, 9 Nov 2023 11:00:09 +0800 Subject: [PATCH 23/67] fix: #3192 use smaller max_tokens as default --- app/store/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/store/config.ts b/app/store/config.ts index 17eb88c30..057e31b25 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -49,7 +49,7 @@ export const DEFAULT_CONFIG = { model: "gpt-3.5-turbo" as ModelType, temperature: 0.5, top_p: 1, - max_tokens: 8192, + max_tokens: 4000, presence_penalty: 0, frequency_penalty: 0, sendMemory: true, From 3b3ebda34bc5def7e7b72f9a3a7dcca2fa0c0aac Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Thu, 9 Nov 2023 11:01:41 +0800 Subject: [PATCH 24/67] fix: #3196 3.5-turbo-1106 should use old cutoff date --- app/constant.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/constant.ts b/app/constant.ts index 8d36e0b55..a97b87822 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -79,7 +79,6 @@ export const SUMMARIZE_MODEL = "gpt-3.5-turbo"; export const KnowledgeCutOffDate: Record = { default: "2021-09", - "gpt-3.5-turbo-1106": "2023-04", "gpt-4-1106-preview": "2023-04", "gpt-4-vision-preview": "2023-04", }; From fd2f441e02b1eecfd2139942fcb911b32ee3c1e4 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Thu, 9 Nov 2023 20:45:25 +0800 Subject: [PATCH 25/67] feat: wont send max_tokens --- app/client/platforms/openai.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 97392a004..4a5ddce7d 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -70,7 +70,8 @@ export class ChatGPTApi implements LLMApi { presence_penalty: modelConfig.presence_penalty, frequency_penalty: modelConfig.frequency_penalty, top_p: modelConfig.top_p, - max_tokens: Math.max(modelConfig.max_tokens, 1024), + // max_tokens: Math.max(modelConfig.max_tokens, 1024), + // Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore. }; console.log("[Request] openai payload: ", requestPayload); From b7ffca031ebda555c373783820056541307ceba0 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 10 Nov 2023 02:43:30 +0800 Subject: [PATCH 26/67] feat: close #935 add azure support --- app/api/auth.ts | 18 ++- app/api/common.ts | 31 +++-- app/azure.ts | 9 ++ app/client/api.ts | 16 ++- app/client/platforms/openai.ts | 48 ++++++-- app/components/auth.tsx | 6 +- app/components/chat.tsx | 4 +- app/components/settings.tsx | 187 ++++++++++++++++++++++++------ app/components/ui-lib.module.scss | 2 +- app/components/ui-lib.tsx | 14 +-- app/config/server.ts | 35 ++++-- app/constant.ts | 11 ++ app/locales/cn.ts | 66 ++++++++--- app/locales/en.ts | 66 ++++++++--- app/store/access.ts | 57 +++++++-- app/utils/clone.ts | 7 ++ app/utils/store.ts | 51 ++++---- 17 files changed, 478 insertions(+), 150 deletions(-) create mode 100644 app/azure.ts diff --git a/app/api/auth.ts b/app/api/auth.ts index e0453b2b4..c1f6e7fde 100644 --- a/app/api/auth.ts +++ b/app/api/auth.ts @@ -28,7 +28,7 @@ export function auth(req: NextRequest) { const authToken = req.headers.get("Authorization") ?? ""; // check if it is openai api key or user token - const { accessCode, apiKey: token } = parseApiKey(authToken); + const { accessCode, apiKey } = parseApiKey(authToken); const hashedCode = md5.hash(accessCode ?? "").trim(); @@ -39,7 +39,7 @@ export function auth(req: NextRequest) { console.log("[User IP] ", getIP(req)); console.log("[Time] ", new Date().toLocaleString()); - if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) { + if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !apiKey) { return { error: true, msg: !accessCode ? "empty access code" : "wrong access code", @@ -47,11 +47,17 @@ export function auth(req: NextRequest) { } // if user does not provide an api key, inject system api key - if (!token) { - const apiKey = serverConfig.apiKey; - if (apiKey) { + if (!apiKey) { + const serverApiKey = serverConfig.isAzure + ? serverConfig.azureApiKey + : serverConfig.apiKey; + + if (serverApiKey) { console.log("[Auth] use system api key"); - req.headers.set("Authorization", `Bearer ${apiKey}`); + req.headers.set( + "Authorization", + `${serverConfig.isAzure ? "" : "Bearer "}${serverApiKey}`, + ); } else { console.log("[Auth] admin did not provide an api key"); } diff --git a/app/api/common.ts b/app/api/common.ts index a1decd42f..fc877b02d 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -1,19 +1,24 @@ import { NextRequest, NextResponse } from "next/server"; import { getServerSideConfig } from "../config/server"; import { DEFAULT_MODELS, OPENAI_BASE_URL } from "../constant"; -import { collectModelTable, collectModels } from "../utils/model"; +import { collectModelTable } from "../utils/model"; +import { makeAzurePath } from "../azure"; const serverConfig = getServerSideConfig(); export async function requestOpenai(req: NextRequest) { const controller = new AbortController(); + const authValue = req.headers.get("Authorization") ?? ""; - const openaiPath = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll( + const authHeaderName = serverConfig.isAzure ? "api-key" : "Authorization"; + + let path = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll( "/api/openai/", "", ); - let baseUrl = serverConfig.baseUrl ?? OPENAI_BASE_URL; + let baseUrl = + serverConfig.azureUrl ?? serverConfig.baseUrl ?? OPENAI_BASE_URL; if (!baseUrl.startsWith("http")) { baseUrl = `https://${baseUrl}`; @@ -23,7 +28,7 @@ export async function requestOpenai(req: NextRequest) { baseUrl = baseUrl.slice(0, -1); } - console.log("[Proxy] ", openaiPath); + console.log("[Proxy] ", path); console.log("[Base Url]", baseUrl); console.log("[Org ID]", serverConfig.openaiOrgId); @@ -34,14 +39,24 @@ export async function requestOpenai(req: NextRequest) { 10 * 60 * 1000, ); - const fetchUrl = `${baseUrl}/${openaiPath}`; + if (serverConfig.isAzure) { + if (!serverConfig.azureApiVersion) { + return NextResponse.json({ + error: true, + message: `missing AZURE_API_VERSION in server env vars`, + }); + } + path = makeAzurePath(path, serverConfig.azureApiVersion); + } + + const fetchUrl = `${baseUrl}/${path}`; const fetchOptions: RequestInit = { headers: { "Content-Type": "application/json", "Cache-Control": "no-store", - Authorization: authValue, - ...(process.env.OPENAI_ORG_ID && { - "OpenAI-Organization": process.env.OPENAI_ORG_ID, + [authHeaderName]: authValue, + ...(serverConfig.openaiOrgId && { + "OpenAI-Organization": serverConfig.openaiOrgId, }), }, method: req.method, diff --git a/app/azure.ts b/app/azure.ts new file mode 100644 index 000000000..48406c55b --- /dev/null +++ b/app/azure.ts @@ -0,0 +1,9 @@ +export function makeAzurePath(path: string, apiVersion: string) { + // should omit /v1 prefix + path = path.replaceAll("v1/", ""); + + // should add api-key to query string + path += `${path.includes("?") ? "&" : "?"}api-version=${apiVersion}`; + + return path; +} diff --git a/app/client/api.ts b/app/client/api.ts index b04dd88b8..eedd2c9ab 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -1,5 +1,5 @@ import { getClientConfig } from "../config/client"; -import { ACCESS_CODE_PREFIX } from "../constant"; +import { ACCESS_CODE_PREFIX, Azure, ServiceProvider } from "../constant"; import { ChatMessage, ModelType, useAccessStore } from "../store"; import { ChatGPTApi } from "./platforms/openai"; @@ -127,22 +127,26 @@ export const api = new ClientApi(); export function getHeaders() { const accessStore = useAccessStore.getState(); - let headers: Record = { + const headers: Record = { "Content-Type": "application/json", "x-requested-with": "XMLHttpRequest", }; - const makeBearer = (token: string) => `Bearer ${token.trim()}`; + const isAzure = accessStore.provider === ServiceProvider.Azure; + const authHeader = isAzure ? "api-key" : "Authorization"; + const apiKey = isAzure ? accessStore.azureApiKey : accessStore.openaiApiKey; + + const makeBearer = (s: string) => `${isAzure ? "" : "Bearer "}${s.trim()}`; const validString = (x: string) => x && x.length > 0; // use user's api key first - if (validString(accessStore.token)) { - headers.Authorization = makeBearer(accessStore.token); + if (validString(apiKey)) { + headers[authHeader] = makeBearer(apiKey); } else if ( accessStore.enabledAccessControl() && validString(accessStore.accessCode) ) { - headers.Authorization = makeBearer( + headers[authHeader] = makeBearer( ACCESS_CODE_PREFIX + accessStore.accessCode, ); } diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 4a5ddce7d..930d60690 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -1,8 +1,10 @@ import { + ApiPath, DEFAULT_API_HOST, DEFAULT_MODELS, OpenaiPath, REQUEST_TIMEOUT_MS, + ServiceProvider, } from "@/app/constant"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; @@ -14,6 +16,7 @@ import { } from "@fortaine/fetch-event-source"; import { prettyObject } from "@/app/utils/format"; import { getClientConfig } from "@/app/config/client"; +import { makeAzurePath } from "@/app/azure"; export interface OpenAIListModelResponse { object: string; @@ -28,20 +31,35 @@ export class ChatGPTApi implements LLMApi { private disableListModels = true; path(path: string): string { - let openaiUrl = useAccessStore.getState().openaiUrl; - const apiPath = "/api/openai"; + const accessStore = useAccessStore.getState(); - if (openaiUrl.length === 0) { + const isAzure = accessStore.provider === ServiceProvider.Azure; + + if (isAzure && !accessStore.isValidAzure()) { + throw Error( + "incomplete azure config, please check it in your settings page", + ); + } + + let baseUrl = isAzure ? accessStore.azureUrl : accessStore.openaiUrl; + + if (baseUrl.length === 0) { const isApp = !!getClientConfig()?.isApp; - openaiUrl = isApp ? DEFAULT_API_HOST : apiPath; + baseUrl = isApp ? DEFAULT_API_HOST : ApiPath.OpenAI; } - if (openaiUrl.endsWith("/")) { - openaiUrl = openaiUrl.slice(0, openaiUrl.length - 1); + + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.slice(0, baseUrl.length - 1); } - if (!openaiUrl.startsWith("http") && !openaiUrl.startsWith(apiPath)) { - openaiUrl = "https://" + openaiUrl; + if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.OpenAI)) { + baseUrl = "https://" + baseUrl; } - return [openaiUrl, path].join("/"); + + if (isAzure) { + path = makeAzurePath(path, accessStore.azureApiVersion); + } + + return [baseUrl, path].join("/"); } extractMessage(res: any) { @@ -156,14 +174,20 @@ export class ChatGPTApi implements LLMApi { } const text = msg.data; try { - const json = JSON.parse(text); - const delta = json.choices[0].delta.content; + const json = JSON.parse(text) as { + choices: Array<{ + delta: { + content: string; + }; + }>; + }; + const delta = json.choices[0]?.delta?.content; if (delta) { responseText += delta; options.onUpdate?.(responseText, delta); } } catch (e) { - console.error("[Request] parse error", text, msg); + console.error("[Request] parse error", text); } }, onclose() { diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 577d77542..3e1548a13 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -18,7 +18,7 @@ export function AuthPage() { const goChat = () => navigate(Path.Chat); const resetAccessCode = () => { accessStore.update((access) => { - access.token = ""; + access.openaiApiKey = ""; access.accessCode = ""; }); }; // Reset access code to empty string @@ -57,10 +57,10 @@ export function AuthPage() { className={styles["auth-input"]} type="password" placeholder={Locale.Settings.Token.Placeholder} - value={accessStore.token} + value={accessStore.openaiApiKey} onChange={(e) => { accessStore.update( - (access) => (access.token = e.currentTarget.value), + (access) => (access.openaiApiKey = e.currentTarget.value), ); }} /> diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 9afb49f7a..c27c3eee4 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -998,7 +998,9 @@ function _Chat() { ).then((res) => { if (!res) return; if (payload.key) { - accessStore.update((access) => (access.token = payload.key!)); + accessStore.update( + (access) => (access.openaiApiKey = payload.key!), + ); } if (payload.url) { accessStore.update((access) => (access.openaiUrl = payload.url!)); diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 572c0743a..178fcec57 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -51,10 +51,13 @@ import Locale, { import { copyToClipboard } from "../utils"; import Link from "next/link"; import { + Azure, OPENAI_BASE_URL, Path, RELEASE_URL, STORAGE_KEY, + ServiceProvider, + SlotID, UPDATE_URL, } from "../constant"; import { Prompt, SearchService, usePromptStore } from "../store/prompt"; @@ -580,8 +583,16 @@ export function Settings() { const accessStore = useAccessStore(); const shouldHideBalanceQuery = useMemo(() => { const isOpenAiUrl = accessStore.openaiUrl.includes(OPENAI_BASE_URL); - return accessStore.hideBalanceQuery || isOpenAiUrl; - }, [accessStore.hideBalanceQuery, accessStore.openaiUrl]); + return ( + accessStore.hideBalanceQuery || + isOpenAiUrl || + accessStore.provider === ServiceProvider.Azure + ); + }, [ + accessStore.hideBalanceQuery, + accessStore.openaiUrl, + accessStore.provider, + ]); const usage = { used: updateStore.used, @@ -877,16 +888,16 @@ export function Settings() { - - {showAccessCode ? ( + + {showAccessCode && ( { accessStore.update( (access) => (access.accessCode = e.currentTarget.value), @@ -894,44 +905,152 @@ export function Settings() { }} /> - ) : ( - <> )} - {!accessStore.hideUserApiKey ? ( + {!accessStore.hideUserApiKey && ( <> accessStore.update( - (access) => (access.openaiUrl = e.currentTarget.value), + (access) => + (access.useCustomConfig = e.currentTarget.checked), ) } > - - { - accessStore.update( - (access) => (access.token = e.currentTarget.value), - ); - }} - /> - + {accessStore.useCustomConfig && ( + <> + + + + + {accessStore.provider === "OpenAI" ? ( + <> + + + accessStore.update( + (access) => + (access.openaiUrl = e.currentTarget.value), + ) + } + > + + + { + accessStore.update( + (access) => + (access.openaiApiKey = e.currentTarget.value), + ); + }} + /> + + + ) : ( + <> + + + accessStore.update( + (access) => + (access.azureUrl = e.currentTarget.value), + ) + } + > + + + { + accessStore.update( + (access) => + (access.azureApiKey = e.currentTarget.value), + ); + }} + /> + + + + accessStore.update( + (access) => + (access.azureApiVersion = + e.currentTarget.value), + ) + } + > + + + )} + + )} - ) : null} + )} {!shouldHideBalanceQuery ? ( - | JSX.Element - | null - | undefined; -}) { - return
{props.children}
; +export function List(props: { children: React.ReactNode; id?: string }) { + return ( +
+ {props.children} +
+ ); } export function Loading() { diff --git a/app/config/server.ts b/app/config/server.ts index 007c39738..2f2e7d7fd 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -4,19 +4,28 @@ import { DEFAULT_MODELS } from "../constant"; declare global { namespace NodeJS { interface ProcessEnv { + PROXY_URL?: string; // docker only + OPENAI_API_KEY?: string; CODE?: string; + BASE_URL?: string; - PROXY_URL?: string; - OPENAI_ORG_ID?: string; + OPENAI_ORG_ID?: string; // openai only + VERCEL?: string; - HIDE_USER_API_KEY?: string; // disable user's api key input - DISABLE_GPT4?: string; // allow user to use gpt-4 or not BUILD_MODE?: "standalone" | "export"; BUILD_APP?: string; // is building desktop app + + HIDE_USER_API_KEY?: string; // disable user's api key input + DISABLE_GPT4?: string; // allow user to use gpt-4 or not ENABLE_BALANCE_QUERY?: string; // allow user to query balance or not DISABLE_FAST_LINK?: string; // disallow parse settings from url or not CUSTOM_MODELS?: string; // to control custom models + + // azure only + AZURE_URL?: string; // https://{azure-url}/openai/deployments/{deploy-name} + AZURE_API_KEY?: string; + AZURE_API_VERSION?: string; } } } @@ -41,7 +50,7 @@ export const getServerSideConfig = () => { ); } - let disableGPT4 = !!process.env.DISABLE_GPT4; + const disableGPT4 = !!process.env.DISABLE_GPT4; let customModels = process.env.CUSTOM_MODELS ?? ""; if (disableGPT4) { @@ -51,15 +60,25 @@ export const getServerSideConfig = () => { .join(","); } + const isAzure = !!process.env.AZURE_URL; + return { + baseUrl: process.env.BASE_URL, apiKey: process.env.OPENAI_API_KEY, + openaiOrgId: process.env.OPENAI_ORG_ID, + + isAzure, + azureUrl: process.env.AZURE_URL, + azureApiKey: process.env.AZURE_API_KEY, + azureApiVersion: process.env.AZURE_API_VERSION, + + needCode: ACCESS_CODES.size > 0, code: process.env.CODE, codes: ACCESS_CODES, - needCode: ACCESS_CODES.size > 0, - baseUrl: process.env.BASE_URL, + proxyUrl: process.env.PROXY_URL, - openaiOrgId: process.env.OPENAI_ORG_ID, isVercel: !!process.env.VERCEL, + hideUserApiKey: !!process.env.HIDE_USER_API_KEY, disableGPT4, hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY, diff --git a/app/constant.ts b/app/constant.ts index a97b87822..fbc0c72e3 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -23,10 +23,12 @@ export enum Path { export enum ApiPath { Cors = "/api/cors", + OpenAI = "/api/openai", } export enum SlotID { AppBody = "app-body", + CustomModel = "custom-model", } export enum FileName { @@ -60,6 +62,11 @@ export const REQUEST_TIMEOUT_MS = 60000; export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown"; +export enum ServiceProvider { + OpenAI = "OpenAI", + Azure = "Azure", +} + export const OpenaiPath = { ChatPath: "v1/chat/completions", UsagePath: "dashboard/billing/usage", @@ -67,6 +74,10 @@ export const OpenaiPath = { ListModelPath: "v1/models", }; +export const Azure = { + ExampleEndpoint: "https://{resource-url}/openai/deployments/{deploy-id}", +}; + export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang export const DEFAULT_SYSTEM_TEMPLATE = ` You are ChatGPT, a large language model trained by OpenAI. diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 4cd963fb8..e721adef7 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -258,11 +258,6 @@ const cn = { Title: "历史消息长度压缩阈值", SubTitle: "当未压缩的历史消息超过该值时,将进行压缩", }, - Token: { - Title: "API Key", - SubTitle: "使用自己的 Key 可绕过密码访问限制", - Placeholder: "OpenAI API Key", - }, Usage: { Title: "余额查询", @@ -273,19 +268,56 @@ const cn = { Check: "重新检查", NoAccess: "输入 API Key 或访问密码查看余额", }, - AccessCode: { - Title: "访问密码", - SubTitle: "管理员已开启加密访问", - Placeholder: "请输入访问密码", - }, - Endpoint: { - Title: "接口地址", - SubTitle: "除默认地址外,必须包含 http(s)://", - }, - CustomModel: { - Title: "自定义模型名", - SubTitle: "增加自定义模型可选项,使用英文逗号隔开", + + Access: { + AccessCode: { + Title: "访问密码", + SubTitle: "管理员已开启加密访问", + Placeholder: "请输入访问密码", + }, + CustomEndpoint: { + Title: "自定义接口", + SubTitle: "是否使用自定义 Azure 或 OpenAI 服务", + }, + Provider: { + Title: "模型服务商", + SubTitle: "切换不同的服务商", + }, + OpenAI: { + ApiKey: { + Title: "API Key", + SubTitle: "使用自定义 OpenAI Key 绕过密码访问限制", + Placeholder: "OpenAI API Key", + }, + + Endpoint: { + Title: "接口地址", + SubTitle: "除默认地址外,必须包含 http(s)://", + }, + }, + Azure: { + ApiKey: { + Title: "接口密钥", + SubTitle: "使用自定义 Azure Key 绕过密码访问限制", + Placeholder: "Azure API Key", + }, + + Endpoint: { + Title: "接口地址", + SubTitle: "样例:", + }, + + ApiVerion: { + Title: "接口版本 (azure api version)", + SubTitle: "选择指定的部分版本", + }, + }, + CustomModel: { + Title: "自定义模型名", + SubTitle: "增加自定义模型可选项,使用英文逗号隔开", + }, }, + Model: "模型 (model)", Temperature: { Title: "随机性 (temperature)", diff --git a/app/locales/en.ts b/app/locales/en.ts index 928c4b72d..c6e61ecab 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -262,11 +262,7 @@ const en: LocaleType = { SubTitle: "Will compress if uncompressed messages length exceeds the value", }, - Token: { - Title: "API Key", - SubTitle: "Use your key to ignore access code limit", - Placeholder: "OpenAI API Key", - }, + Usage: { Title: "Account Balance", SubTitle(used: any, total: any) { @@ -276,19 +272,55 @@ const en: LocaleType = { Check: "Check", NoAccess: "Enter API Key to check balance", }, - AccessCode: { - Title: "Access Code", - SubTitle: "Access control enabled", - Placeholder: "Need Access Code", - }, - Endpoint: { - Title: "Endpoint", - SubTitle: "Custom endpoint must start with http(s)://", - }, - CustomModel: { - Title: "Custom Models", - SubTitle: "Add extra model options, separate by comma", + Access: { + AccessCode: { + Title: "Access Code", + SubTitle: "Access control Enabled", + Placeholder: "Enter Code", + }, + CustomEndpoint: { + Title: "Custom Endpoint", + SubTitle: "Use custom Azure or OpenAI service", + }, + Provider: { + Title: "Model Provider", + SubTitle: "Select Azure or OpenAI", + }, + OpenAI: { + ApiKey: { + Title: "OpenAI API Key", + SubTitle: "User custom OpenAI Api Key", + Placeholder: "sk-xxx", + }, + + Endpoint: { + Title: "OpenAI Endpoint", + SubTitle: "Must starts with http(s):// or use /api/openai as default", + }, + }, + Azure: { + ApiKey: { + Title: "Azure Api Key", + SubTitle: "Check your api key from Azure console", + Placeholder: "Azure Api Key", + }, + + Endpoint: { + Title: "Azure Endpoint", + SubTitle: "Example: ", + }, + + ApiVerion: { + Title: "Azure Api Version", + SubTitle: "Check your api version from azure console", + }, + }, + CustomModel: { + Title: "Custom Models", + SubTitle: "Custom model options, seperated by comma", + }, }, + Model: "Model", Temperature: { Title: "Temperature", diff --git a/app/store/access.ts b/app/store/access.ts index f87e44a2a..2abe1e3cc 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -1,25 +1,41 @@ -import { DEFAULT_API_HOST, DEFAULT_MODELS, StoreKey } from "../constant"; +import { + ApiPath, + DEFAULT_API_HOST, + ServiceProvider, + StoreKey, +} from "../constant"; import { getHeaders } from "../client/api"; import { getClientConfig } from "../config/client"; import { createPersistStore } from "../utils/store"; +import { ensure } from "../utils/clone"; let fetchState = 0; // 0 not fetch, 1 fetching, 2 done const DEFAULT_OPENAI_URL = - getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST : "/api/openai/"; -console.log("[API] default openai url", DEFAULT_OPENAI_URL); + getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST : ApiPath.OpenAI; const DEFAULT_ACCESS_STATE = { - token: "", accessCode: "", + useCustomConfig: false, + + provider: ServiceProvider.OpenAI, + + // openai + openaiUrl: DEFAULT_OPENAI_URL, + openaiApiKey: "", + + // azure + azureUrl: "", + azureApiKey: "", + azureApiVersion: "2023-08-01-preview", + + // server config needCode: true, hideUserApiKey: false, hideBalanceQuery: false, disableGPT4: false, disableFastLink: false, customModels: "", - - openaiUrl: DEFAULT_OPENAI_URL, }; export const useAccessStore = createPersistStore( @@ -31,12 +47,24 @@ export const useAccessStore = createPersistStore( return get().needCode; }, + + isValidOpenAI() { + return ensure(get(), ["openaiUrl", "openaiApiKey"]); + }, + + isValidAzure() { + return ensure(get(), ["azureUrl", "azureApiKey", "azureApiVersion"]); + }, + isAuthorized() { this.fetch(); // has token or has code or disabled access control return ( - !!get().token || !!get().accessCode || !this.enabledAccessControl() + this.isValidOpenAI() || + this.isValidAzure() || + !this.enabledAccessControl() || + (this.enabledAccessControl() && ensure(get(), ["accessCode"])) ); }, fetch() { @@ -64,6 +92,19 @@ export const useAccessStore = createPersistStore( }), { name: StoreKey.Access, - version: 1, + version: 2, + migrate(persistedState, version) { + if (version < 2) { + const state = persistedState as { + token: string; + openaiApiKey: string; + azureApiVersion: string; + }; + state.openaiApiKey = state.token; + state.azureApiVersion = "2023-08-01-preview"; + } + + return persistedState as any; + }, }, ); diff --git a/app/utils/clone.ts b/app/utils/clone.ts index 2958b6b9c..c42288f77 100644 --- a/app/utils/clone.ts +++ b/app/utils/clone.ts @@ -1,3 +1,10 @@ export function deepClone(obj: T) { return JSON.parse(JSON.stringify(obj)); } + +export function ensure( + obj: T, + keys: Array<[keyof T][number]>, +) { + return keys.every((k) => obj[k] !== undefined && obj[k] !== null); +} diff --git a/app/utils/store.ts b/app/utils/store.ts index cd151dc49..684a19112 100644 --- a/app/utils/store.ts +++ b/app/utils/store.ts @@ -1,5 +1,5 @@ import { create } from "zustand"; -import { persist } from "zustand/middleware"; +import { combine, persist } from "zustand/middleware"; import { Updater } from "../typing"; import { deepClone } from "./clone"; @@ -23,33 +23,42 @@ type SetStoreState = ( replace?: boolean | undefined, ) => void; -export function createPersistStore( - defaultState: T, +export function createPersistStore( + state: T, methods: ( set: SetStoreState>, get: () => T & MakeUpdater, ) => M, persistOptions: SecondParam>>, ) { - return create>()( - persist((set, get) => { - return { - ...defaultState, - ...methods(set as any, get), + return create( + persist( + combine( + { + ...state, + lastUpdateTime: 0, + }, + (set, get) => { + return { + ...methods(set, get as any), - lastUpdateTime: 0, - markUpdate() { - set({ lastUpdateTime: Date.now() } as Partial< - T & M & MakeUpdater - >); + markUpdate() { + set({ lastUpdateTime: Date.now() } as Partial< + T & M & MakeUpdater + >); + }, + update(updater) { + const state = deepClone(get()); + updater(state); + set({ + ...state, + lastUpdateTime: Date.now(), + }); + }, + } as M & MakeUpdater; }, - update(updater) { - const state = deepClone(get()); - updater(state); - get().markUpdate(); - set(state); - }, - }; - }, persistOptions), + ), + persistOptions as any, + ), ); } From c9dd953817798d785abef6c92f6c9d047c5d16ca Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 10 Nov 2023 02:50:50 +0800 Subject: [PATCH 27/67] fixup --- app/components/auth.tsx | 2 +- app/locales/ar.ts | 16 ++-------------- app/locales/bn.ts | 16 ++-------------- app/locales/cs.ts | 12 ++---------- app/locales/de.ts | 12 +----------- app/locales/es.ts | 12 ++---------- app/locales/fr.ts | 12 ++---------- app/locales/id.ts | 21 +++++---------------- app/locales/it.ts | 13 ++----------- app/locales/jp.ts | 15 ++++----------- app/locales/ko.ts | 12 ++---------- app/locales/no.ts | 13 ++----------- app/locales/ru.ts | 12 ++---------- app/locales/tr.ts | 12 ++---------- app/locales/tw.ts | 12 ++---------- app/locales/vi.ts | 12 ++---------- 16 files changed, 35 insertions(+), 169 deletions(-) diff --git a/app/components/auth.tsx b/app/components/auth.tsx index 3e1548a13..7962d46be 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -56,7 +56,7 @@ export function AuthPage() { { accessStore.update( diff --git a/app/locales/ar.ts b/app/locales/ar.ts index d5844acd6..b58c3a2e8 100644 --- a/app/locales/ar.ts +++ b/app/locales/ar.ts @@ -167,11 +167,7 @@ ${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخد Title: "حد الضغط للتاريخ", SubTitle: "سيتم الضغط إذا تجاوزت طول الرسائل غير المضغوطة الحد المحدد", }, - Token: { - Title: "مفتاح API", - SubTitle: "استخدم مفتاحك لتجاوز حد رمز الوصول", - Placeholder: "مفتاح OpenAI API", - }, + Usage: { Title: "رصيد الحساب", SubTitle(used: any, total: any) { @@ -181,15 +177,7 @@ ${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخد Check: "التحقق", NoAccess: "أدخل مفتاح API للتحقق من الرصيد", }, - AccessCode: { - Title: "رمز الوصول", - SubTitle: "تم تمكين التحكم في الوصول", - Placeholder: "رمز الوصول المطلوب", - }, - Endpoint: { - Title: "نقطة النهاية", - SubTitle: "يجب أن تبدأ نقطة النهاية المخصصة بـ http(s)://", - }, + Model: "النموذج", Temperature: { Title: "الحرارة", diff --git a/app/locales/bn.ts b/app/locales/bn.ts index 2db132cec..6dfb0da9b 100644 --- a/app/locales/bn.ts +++ b/app/locales/bn.ts @@ -199,11 +199,7 @@ const bn: PartialLocaleType = { SubTitle: "নকুল বার্তা দৈর্ঘ্য সীমা অতিক্রান্ত হলে ঐ বার্তাটি সঙ্কুচিত হবে", }, - Token: { - Title: "অ্যাপি কী", - SubTitle: "অ্যাক্সেস কোড সীমা উপেক্ষা করতে আপনার কীটি ব্যবহার করুন", - Placeholder: "OpenAI API কী", - }, + Usage: { Title: "একাউন্ট ব্যালেন্স", SubTitle(used: any, total: any) { @@ -213,15 +209,7 @@ const bn: PartialLocaleType = { Check: "চেক", NoAccess: "ব্যালেন্স চেক করতে অ্যাপি কী ইনপুট করুন", }, - AccessCode: { - Title: "অ্যাক্সেস কোড", - SubTitle: "অ্যাক্সেস নিয়ন্ত্রণ সক্রিয়", - Placeholder: "অ্যাক্সেস কোড প্রয়োজন", - }, - Endpoint: { - Title: "ইনটারপয়েন্ট", - SubTitle: "কাস্টম এন্ডপয়েন্টটি হতে হবে http(s):// দিয়ে শুরু হতে হবে", - }, + Model: "মডেল", Temperature: { Title: "তাপমাত্রা", diff --git a/app/locales/cs.ts b/app/locales/cs.ts index 57aa803e4..c1a84430f 100644 --- a/app/locales/cs.ts +++ b/app/locales/cs.ts @@ -124,11 +124,7 @@ const cs: PartialLocaleType = { SubTitle: "Komprese proběhne, pokud délka nekomprimovaných zpráv přesáhne tuto hodnotu", }, - Token: { - Title: "API klíč", - SubTitle: "Použitím klíče ignorujete omezení přístupového kódu", - Placeholder: "Klíč API OpenAI", - }, + Usage: { Title: "Stav účtu", SubTitle(used: any, total: any) { @@ -138,11 +134,7 @@ const cs: PartialLocaleType = { Check: "Zkontrolovat", NoAccess: "Pro kontrolu zůstatku zadejte klíč API", }, - AccessCode: { - Title: "Přístupový kód", - SubTitle: "Kontrola přístupu povolena", - Placeholder: "Potřebujete přístupový kód", - }, + Model: "Model", Temperature: { Title: "Teplota", diff --git a/app/locales/de.ts b/app/locales/de.ts index e0bdc52b7..2fe871bc9 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -124,12 +124,7 @@ const de: PartialLocaleType = { SubTitle: "Komprimierung, wenn die Länge der unkomprimierten Nachrichten den Wert überschreitet", }, - Token: { - Title: "API-Schlüssel", - SubTitle: - "Verwenden Sie Ihren Schlüssel, um das Zugangscode-Limit zu ignorieren", - Placeholder: "OpenAI API-Schlüssel", - }, + Usage: { Title: "Kontostand", SubTitle(used: any, total: any) { @@ -139,11 +134,6 @@ const de: PartialLocaleType = { Check: "Erneut prüfen", NoAccess: "API-Schlüssel eingeben, um den Kontostand zu überprüfen", }, - AccessCode: { - Title: "Zugangscode", - SubTitle: "Zugangskontrolle aktiviert", - Placeholder: "Zugangscode erforderlich", - }, Model: "Modell", Temperature: { Title: "Temperature", //Temperatur diff --git a/app/locales/es.ts b/app/locales/es.ts index a6ae154f4..7d742d536 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -124,11 +124,7 @@ const es: PartialLocaleType = { SubTitle: "Se comprimirán los mensajes si la longitud de los mensajes no comprimidos supera el valor", }, - Token: { - Title: "Clave de API", - SubTitle: "Utiliza tu clave para ignorar el límite de código de acceso", - Placeholder: "Clave de la API de OpenAI", - }, + Usage: { Title: "Saldo de la cuenta", SubTitle(used: any, total: any) { @@ -138,11 +134,7 @@ const es: PartialLocaleType = { Check: "Comprobar de nuevo", NoAccess: "Introduzca la clave API para comprobar el saldo", }, - AccessCode: { - Title: "Código de acceso", - SubTitle: "Control de acceso habilitado", - Placeholder: "Necesita código de acceso", - }, + Model: "Modelo", Temperature: { Title: "Temperatura", diff --git a/app/locales/fr.ts b/app/locales/fr.ts index f5200f271..944754d62 100644 --- a/app/locales/fr.ts +++ b/app/locales/fr.ts @@ -173,11 +173,7 @@ const fr: PartialLocaleType = { SubTitle: "Comprimera si la longueur des messages non compressés dépasse cette valeur", }, - Token: { - Title: "Clé API", - SubTitle: "Utilisez votre clé pour ignorer la limite du code d'accès", - Placeholder: "Clé OpenAI API", - }, + Usage: { Title: "Solde du compte", SubTitle(used: any, total: any) { @@ -187,11 +183,7 @@ const fr: PartialLocaleType = { Check: "Vérifier", NoAccess: "Entrez la clé API pour vérifier le solde", }, - AccessCode: { - Title: "Code d'accès", - SubTitle: "Contrôle d'accès activé", - Placeholder: "Code d'accès requis", - }, + Model: "Modèle", Temperature: { Title: "Température", diff --git a/app/locales/id.ts b/app/locales/id.ts index b5e4a70b7..4da55948e 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -4,8 +4,9 @@ import { PartialLocaleType } from "./index"; const id: PartialLocaleType = { WIP: "Coming Soon...", Error: { - Unauthorized: "Akses tidak diizinkan, silakan masukkan kode akses atau masukkan kunci API OpenAI Anda. di halaman [autentikasi](/#/auth) atau di halaman [Pengaturan](/#/settings).", - }, + Unauthorized: + "Akses tidak diizinkan, silakan masukkan kode akses atau masukkan kunci API OpenAI Anda. di halaman [autentikasi](/#/auth) atau di halaman [Pengaturan](/#/settings).", + }, Auth: { Title: "Diperlukan Kode Akses", Tips: "Masukkan kode akses di bawah", @@ -237,11 +238,7 @@ const id: PartialLocaleType = { SubTitle: "Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi", }, - Token: { - Title: "Kunci API", - SubTitle: "Gunakan kunci Anda untuk melewati batas kode akses", - Placeholder: "Kunci API OpenAI", - }, + Usage: { Title: "Saldo Akun", SubTitle(used: any, total: any) { @@ -251,15 +248,7 @@ const id: PartialLocaleType = { Check: "Periksa", NoAccess: "Masukkan kunci API untuk memeriksa saldo", }, - AccessCode: { - Title: "Kode Akses", - SubTitle: "Kontrol akses diaktifkan", - Placeholder: "Diperlukan kode akses", - }, - Endpoint: { - Title: "Endpoint", - SubTitle: "Harus dimulai dengan http(s):// untuk endpoint kustom", - }, + Model: "Model", Temperature: { Title: "Suhu", diff --git a/app/locales/it.ts b/app/locales/it.ts index bf20747b1..7f0a95846 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -124,12 +124,7 @@ const it: PartialLocaleType = { SubTitle: "Comprimerà se la lunghezza dei messaggi non compressi supera il valore", }, - Token: { - Title: "API Key", - SubTitle: - "Utilizzare la chiave per ignorare il limite del codice di accesso", - Placeholder: "OpenAI API Key", - }, + Usage: { Title: "Bilancio Account", SubTitle(used: any, total: any) { @@ -139,11 +134,7 @@ const it: PartialLocaleType = { Check: "Controlla ancora", NoAccess: "Inserire la chiave API per controllare il saldo", }, - AccessCode: { - Title: "Codice d'accesso", - SubTitle: "Controllo d'accesso abilitato", - Placeholder: "Inserisci il codice d'accesso", - }, + Model: "Modello GPT", Temperature: { Title: "Temperature", diff --git a/app/locales/jp.ts b/app/locales/jp.ts index b63e8ba3a..e0ea07c75 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -20,7 +20,8 @@ const jp: PartialLocaleType = { Stop: "停止", Retry: "リトライ", Pin: "ピン", - PinToastContent: "コンテキストプロンプトに1つのメッセージをピン留めしました", + PinToastContent: + "コンテキストプロンプトに1つのメッセージをピン留めしました", PinToastAction: "表示", Delete: "削除", Edit: "編集", @@ -146,11 +147,7 @@ const jp: PartialLocaleType = { SubTitle: "圧縮されていない履歴メッセージがこの値を超えた場合、圧縮が行われます。", }, - Token: { - Title: "APIキー", - SubTitle: "自分のキーを使用してパスワードアクセス制限を迂回する", - Placeholder: "OpenAI APIキー", - }, + Usage: { Title: "残高照会", SubTitle(used: any, total: any) { @@ -160,11 +157,7 @@ const jp: PartialLocaleType = { Check: "再確認", NoAccess: "APIキーまたはアクセスパスワードを入力して残高を表示", }, - AccessCode: { - Title: "アクセスパスワード", - SubTitle: "暗号化アクセスが有効になっています", - Placeholder: "アクセスパスワードを入力してください", - }, + Model: "モデル (model)", Temperature: { Title: "ランダム性 (temperature)", diff --git a/app/locales/ko.ts b/app/locales/ko.ts index 717ce30b2..844459fc4 100644 --- a/app/locales/ko.ts +++ b/app/locales/ko.ts @@ -124,11 +124,7 @@ const ko: PartialLocaleType = { Title: "기록 압축 임계값", SubTitle: "미압축 메시지 길이가 임계값을 초과하면 압축됨", }, - Token: { - Title: "API 키", - SubTitle: "액세스 코드 제한을 무시하기 위해 키 사용", - Placeholder: "OpenAI API 키", - }, + Usage: { Title: "계정 잔액", SubTitle(used: any, total: any) { @@ -138,11 +134,7 @@ const ko: PartialLocaleType = { Check: "확인", NoAccess: "잔액 확인을 위해 API 키를 입력하세요.", }, - AccessCode: { - Title: "액세스 코드", - SubTitle: "액세스 제어가 활성화됨", - Placeholder: "액세스 코드 입력", - }, + Model: "모델", Temperature: { Title: "온도 (temperature)", diff --git a/app/locales/no.ts b/app/locales/no.ts index 43c92916f..3a0e61107 100644 --- a/app/locales/no.ts +++ b/app/locales/no.ts @@ -106,12 +106,7 @@ const no: PartialLocaleType = { SubTitle: "Komprimer dersom ikke-komprimert lengde på meldinger overskrider denne verdien", }, - Token: { - Title: "API Key", - SubTitle: - "Bruk din egen API-nøkkel for å ignorere tilgangskoden begrensning", - Placeholder: "OpenAI API-nøkkel", - }, + Usage: { Title: "Saldo for konto", SubTitle(used: any, total: any) { @@ -121,11 +116,7 @@ const no: PartialLocaleType = { Check: "Sjekk", NoAccess: "Skriv inn API-nøkkelen for å sjekke saldo", }, - AccessCode: { - Title: "Tilgangskode", - SubTitle: "Tilgangskontroll på", - Placeholder: "Trenger tilgangskode", - }, + Model: "Model", Temperature: { Title: "Temperatur", diff --git a/app/locales/ru.ts b/app/locales/ru.ts index bf98b4eb8..d12cf3e42 100644 --- a/app/locales/ru.ts +++ b/app/locales/ru.ts @@ -125,11 +125,7 @@ const ru: PartialLocaleType = { SubTitle: "Будет сжимать, если длина несжатых сообщений превышает указанное значение", }, - Token: { - Title: "API ключ", - SubTitle: "Используйте свой ключ, чтобы игнорировать лимит доступа", - Placeholder: "API ключ OpenAI", - }, + Usage: { Title: "Баланс аккаунта", SubTitle(used: any, total: any) { @@ -139,11 +135,7 @@ const ru: PartialLocaleType = { Check: "Проверить", NoAccess: "Введите API ключ, чтобы проверить баланс", }, - AccessCode: { - Title: "Код доступа", - SubTitle: "Контроль доступа включен", - Placeholder: "Требуется код доступа", - }, + Model: "Модель", Temperature: { Title: "Температура", diff --git a/app/locales/tr.ts b/app/locales/tr.ts index 06996d83d..524c1b2c5 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -124,11 +124,7 @@ const tr: PartialLocaleType = { SubTitle: "Sıkıştırılmamış mesajların uzunluğu bu değeri aşarsa sıkıştırılır", }, - Token: { - Title: "API Anahtarı", - SubTitle: "Erişim kodu sınırını yoksaymak için anahtarınızı kullanın", - Placeholder: "OpenAI API Anahtarı", - }, + Usage: { Title: "Hesap Bakiyesi", SubTitle(used: any, total: any) { @@ -138,11 +134,7 @@ const tr: PartialLocaleType = { Check: "Tekrar Kontrol Et", NoAccess: "Bakiyeyi kontrol etmek için API anahtarını girin", }, - AccessCode: { - Title: "Erişim Kodu", - SubTitle: "Erişim kontrolü etkinleştirme", - Placeholder: "Erişim Kodu Gerekiyor", - }, + Model: "Model", Temperature: { Title: "Gerçeklik", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index e9f38d097..af47e30ff 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -120,11 +120,7 @@ const tw: PartialLocaleType = { Title: "歷史訊息長度壓縮閾值", SubTitle: "當未壓縮的歷史訊息超過該值時,將進行壓縮", }, - Token: { - Title: "API Key", - SubTitle: "使用自己的 Key 可規避授權存取限制", - Placeholder: "OpenAI API Key", - }, + Usage: { Title: "帳戶餘額", SubTitle(used: any, total: any) { @@ -134,11 +130,7 @@ const tw: PartialLocaleType = { Check: "重新檢查", NoAccess: "輸入 API Key 檢視餘額", }, - AccessCode: { - Title: "授權碼", - SubTitle: "目前是未授權存取狀態", - Placeholder: "請輸入授權碼", - }, + Model: "模型 (model)", Temperature: { Title: "隨機性 (temperature)", diff --git a/app/locales/vi.ts b/app/locales/vi.ts index 8f53a3dc1..3d95b5664 100644 --- a/app/locales/vi.ts +++ b/app/locales/vi.ts @@ -123,11 +123,7 @@ const vi: PartialLocaleType = { Title: "Ngưỡng nén lịch sử tin nhắn", SubTitle: "Thực hiện nén nếu số lượng tin nhắn chưa nén vượt quá ngưỡng", }, - Token: { - Title: "API Key", - SubTitle: "Sử dụng khóa của bạn để bỏ qua giới hạn mã truy cập", - Placeholder: "OpenAI API Key", - }, + Usage: { Title: "Hạn mức tài khoản", SubTitle(used: any, total: any) { @@ -137,11 +133,7 @@ const vi: PartialLocaleType = { Check: "Kiểm tra", NoAccess: "Nhập API Key để kiểm tra hạn mức", }, - AccessCode: { - Title: "Mã truy cập", - SubTitle: "Đã bật kiểm soát truy cập", - Placeholder: "Nhập mã truy cập", - }, + Model: "Mô hình", Temperature: { Title: "Tính ngẫu nhiên (temperature)", From c7e0a6f37fa4483c5211af45a64b8f00821c3586 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 10 Nov 2023 02:57:40 +0800 Subject: [PATCH 28/67] doc: update azure env vars --- README.md | 22 ++++++++++++++++++---- README_CN.md | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3973c84bf..11c8fdef6 100644 --- a/README.md +++ b/README.md @@ -153,14 +153,14 @@ After adding or modifying this environment variable, please redeploy the project > [简体中文 > 如何配置 api key、访问密码、接口代理](./README_CN.md#环境变量) -### `OPENAI_API_KEY` (required) - -Your openai api key. - ### `CODE` (optional) Access password, separated by comma. +### `OPENAI_API_KEY` (required) + +Your openai api key. + ### `BASE_URL` (optional) > Default: `https://api.openai.com` @@ -173,6 +173,20 @@ Override openai api request base url. Specify OpenAI organization ID. +### `AZURE_URL` (optional) + +> Example: https://{azure-resource-url}/openai/deployments/{deploy-name} + +Azure deploy url. + +### `AZURE_API_KEY` (optional) + +Azure Api Key. + +### `AZURE_API_VERSION` (optional) + +Azure Api Version, find it at [Azure Documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions). + ### `HIDE_USER_API_KEY` (optional) > Default: Empty diff --git a/README_CN.md b/README_CN.md index d8e9553e1..c82dfc044 100644 --- a/README_CN.md +++ b/README_CN.md @@ -90,6 +90,20 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 指定 OpenAI 中的组织 ID。 +### `AZURE_URL` (可选) + +> 形如:https://{azure-resource-url}/openai/deployments/{deploy-name} + +Azure 部署地址。 + +### `AZURE_API_KEY` (可选) + +Azure 密钥。 + +### `AZURE_API_VERSION` (可选) + +Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions)。 + ### `HIDE_USER_API_KEY` (可选) 如果你不想让用户自行填入 API Key,将此环境变量设置为 1 即可。 From 8032e6d68d6714e982287ad54b8c26cb17d7d818 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 10 Nov 2023 02:59:30 +0800 Subject: [PATCH 29/67] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 11c8fdef6..58d2b67a1 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ One-Click to get well-designed cross-platform ChatGPT web UI. - 🚀 v2.0 is released, now you can create prompt templates, turn your ideas into reality! Read this: [ChatGPT Prompt Engineering Tips: Zero, One and Few Shot Prompting](https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/). - 🚀 v2.7 let's share conversations as image, or share to ShareGPT! - 🚀 v2.8 now we have a client that runs across all platforms! +- 🚀 v2.9.11 you can use azure endpoint now. ## 主要功能 @@ -93,6 +94,7 @@ One-Click to get well-designed cross-platform ChatGPT web UI. - 💡 想要更方便地随时随地使用本项目?可以试下这款桌面插件:https://github.com/mushan0x0/AI0x0.com - 🚀 v2.7 现在可以将会话分享为图片了,也可以分享到 ShareGPT 的在线链接。 - 🚀 v2.8 发布了横跨 Linux/Windows/MacOS 的体积极小的客户端。 +- 🚀 v2.9.11 现在可以使用自定义 Azure 服务了。 ## Get Started From e6b72ac1ff25c32e9f87b00b31811ec3e75d9f63 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 10 Nov 2023 02:59:47 +0800 Subject: [PATCH 30/67] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58d2b67a1..a7c862b40 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

ChatGPT Next Web

-English / [简体中文](./README_CN.md) / [日本語](./README_JA.md) +English / [简体中文](./README_CN.md) One-Click to get well-designed cross-platform ChatGPT web UI. From cb140e482f522b5add2f31b42d80eda471764335 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 10 Nov 2023 15:15:43 +0800 Subject: [PATCH 31/67] fix: #3207 ensure corner case --- README_CN.md | 2 +- app/utils/clone.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README_CN.md b/README_CN.md index c82dfc044..72376374f 100644 --- a/README_CN.md +++ b/README_CN.md @@ -138,7 +138,7 @@ Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.micro OPENAI_API_KEY= # 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址 -BASE_URL=https://nb.nextweb.fun/api/proxy +BASE_URL=https://ab.nextweb.fun/api/proxy ``` ### 本地开发 diff --git a/app/utils/clone.ts b/app/utils/clone.ts index c42288f77..e4cd29111 100644 --- a/app/utils/clone.ts +++ b/app/utils/clone.ts @@ -6,5 +6,7 @@ export function ensure( obj: T, keys: Array<[keyof T][number]>, ) { - return keys.every((k) => obj[k] !== undefined && obj[k] !== null); + return keys.every( + (k) => obj[k] !== undefined && obj[k] !== null && obj[k] !== "", + ); } From 2bfb362832eab66533eba1e455cec28762e86405 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 10 Nov 2023 15:19:31 +0800 Subject: [PATCH 32/67] Update tauri.conf.json --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 649e3816d..397ae0d83 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.10" + "version": "2.9.11" }, "tauri": { "allowlist": { From 6b2db97347b4bc260146712997be1549c4968ec5 Mon Sep 17 00:00:00 2001 From: Ensteinjun Date: Fri, 10 Nov 2023 15:44:07 +0800 Subject: [PATCH 33/67] fixbug --- app/api/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/common.ts b/app/api/common.ts index fc877b02d..adec611b2 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -18,7 +18,7 @@ export async function requestOpenai(req: NextRequest) { ); let baseUrl = - serverConfig.azureUrl ?? serverConfig.baseUrl ?? OPENAI_BASE_URL; + serverConfig.azureUrl || serverConfig.baseUrl || OPENAI_BASE_URL; if (!baseUrl.startsWith("http")) { baseUrl = `https://${baseUrl}`; From ca792669fce17493a58be64ec2388b4e9db9abb5 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 10 Nov 2023 18:20:51 +0800 Subject: [PATCH 34/67] Update constant.ts --- app/constant.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/constant.ts b/app/constant.ts index fbc0c72e3..561899769 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -8,7 +8,7 @@ export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/c export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`; export const RUNTIME_CONFIG_DOM = "danger-runtime-config"; -export const DEFAULT_CORS_HOST = "https://ab.nextweb.fun"; +export const DEFAULT_CORS_HOST = "https://a.nextweb.fun"; export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`; export const OPENAI_BASE_URL = "https://api.openai.com"; From 943214c6a78bf3b9c6ed12e029efa682adaee659 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 10 Nov 2023 18:21:22 +0800 Subject: [PATCH 35/67] Update README_CN.md --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index 72376374f..604771c52 100644 --- a/README_CN.md +++ b/README_CN.md @@ -138,7 +138,7 @@ Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.micro OPENAI_API_KEY= # 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址 -BASE_URL=https://ab.nextweb.fun/api/proxy +BASE_URL=https://a.nextweb.fun/api/proxy ``` ### 本地开发 From be9774943bc17e30111ccf6ec1eb8242e61f3fa1 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 12 Nov 2023 00:29:36 +0800 Subject: [PATCH 36/67] feat: #3224 auto switch to first avaliable model --- app/components/chat.tsx | 22 +++++++++++++++++++--- app/utils/hooks.ts | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index c27c3eee4..48f76e8ab 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -431,11 +431,27 @@ export function ChatActions(props: { // switch model const currentModel = chatStore.currentSession().mask.modelConfig.model; - const models = useAllModels() - .filter((m) => m.available) - .map((m) => m.name); + const allModels = useAllModels(); + const models = useMemo( + () => allModels.filter((m) => m.available).map((m) => m.name), + [allModels], + ); const [showModelSelector, setShowModelSelector] = useState(false); + useEffect(() => { + // if current model is not available + // switch to first available model + const isUnavaliableModel = !models.includes(currentModel); + if (isUnavaliableModel && models.length > 0) { + const nextModel = models[0] as ModelType; + chatStore.updateCurrentSession( + (session) => (session.mask.modelConfig.model = nextModel), + ); + showToast(nextModel); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentModel, models]); + return (
{couldStop && ( diff --git a/app/utils/hooks.ts b/app/utils/hooks.ts index f6bfae673..35d1f53a4 100644 --- a/app/utils/hooks.ts +++ b/app/utils/hooks.ts @@ -8,7 +8,7 @@ export function useAllModels() { const models = useMemo(() => { return collectModels( configStore.models, - [accessStore.customModels, configStore.customModels].join(","), + [configStore.customModels, accessStore.customModels].join(","), ); }, [accessStore.customModels, configStore.customModels, configStore.models]); From a5a1f2e8ad781e0c82a6f775746286477d806545 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 12 Nov 2023 00:46:21 +0800 Subject: [PATCH 37/67] feat: CUSTOM_MODELS support mapper --- app/api/common.ts | 2 +- app/components/chat.tsx | 10 +++++----- app/utils/model.ts | 32 +++++++++++++++++++++----------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/app/api/common.ts b/app/api/common.ts index adec611b2..dd1cc0bb8 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -81,7 +81,7 @@ export async function requestOpenai(req: NextRequest) { const jsonBody = JSON.parse(clonedBody) as { model?: string }; // not undefined and is false - if (modelTable[jsonBody?.model ?? ""] === false) { + if (modelTable[jsonBody?.model ?? ""].available === false) { return NextResponse.json( { error: true, diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 48f76e8ab..a088483e7 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -433,7 +433,7 @@ export function ChatActions(props: { const currentModel = chatStore.currentSession().mask.modelConfig.model; const allModels = useAllModels(); const models = useMemo( - () => allModels.filter((m) => m.available).map((m) => m.name), + () => allModels.filter((m) => m.available), [allModels], ); const [showModelSelector, setShowModelSelector] = useState(false); @@ -441,9 +441,9 @@ export function ChatActions(props: { useEffect(() => { // if current model is not available // switch to first available model - const isUnavaliableModel = !models.includes(currentModel); + const isUnavaliableModel = !models.some((m) => m.name === currentModel); if (isUnavaliableModel && models.length > 0) { - const nextModel = models[0] as ModelType; + const nextModel = models[0].name as ModelType; chatStore.updateCurrentSession( (session) => (session.mask.modelConfig.model = nextModel), ); @@ -531,8 +531,8 @@ export function ChatActions(props: { ({ - title: m, - value: m, + title: m.displayName, + value: m.name, }))} onClose={() => setShowModelSelector(false)} onSelection={(s) => { diff --git a/app/utils/model.ts b/app/utils/model.ts index 23090f9d2..d5c009c02 100644 --- a/app/utils/model.ts +++ b/app/utils/model.ts @@ -4,21 +4,34 @@ export function collectModelTable( models: readonly LLMModel[], customModels: string, ) { - const modelTable: Record = {}; + const modelTable: Record< + string, + { available: boolean; name: string; displayName: string } + > = {}; // default models - models.forEach((m) => (modelTable[m.name] = m.available)); + models.forEach( + (m) => + (modelTable[m.name] = { + ...m, + displayName: m.name, + }), + ); // server custom models customModels .split(",") .filter((v) => !!v && v.length > 0) .map((m) => { - if (m.startsWith("+")) { - modelTable[m.slice(1)] = true; - } else if (m.startsWith("-")) { - modelTable[m.slice(1)] = false; - } else modelTable[m] = true; + const available = !m.startsWith("-"); + const nameConfig = + m.startsWith("+") || m.startsWith("-") ? m.slice(1) : m; + const [name, displayName] = nameConfig.split(":"); + modelTable[name] = { + name, + displayName: displayName || name, + available, + }; }); return modelTable; } @@ -31,10 +44,7 @@ export function collectModels( customModels: string, ) { const modelTable = collectModelTable(models, customModels); - const allModels = Object.keys(modelTable).map((m) => ({ - name: m, - available: modelTable[m], - })); + const allModels = Object.values(modelTable); return allModels; } From 64647b0bb3d06b5c0ab17c96cf245753d8f4b48a Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 12 Nov 2023 00:49:58 +0800 Subject: [PATCH 38/67] chore: update doc for mapped `CUSTOM_MODELS` --- README.md | 4 ++-- README_CN.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a7c862b40..abe933061 100644 --- a/README.md +++ b/README.md @@ -216,9 +216,9 @@ If you want to disable parse settings from url, set this to 1. ### `CUSTOM_MODELS` (optional) > Default: Empty -> Example: `+llama,+claude-2,-gpt-3.5-turbo` means add `llama, claude-2` to model list, and remove `gpt-3.5-turbo` from list. +> Example: `+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview:gpt-4-turbo` means add `llama, claude-2` to model list, and remove `gpt-3.5-turbo` from list, and display `gpt-4-1106-preview` as `gpt-4-turbo`. -To control custom models, use `+` to add a custom model, use `-` to hide a model, separated by comma. +To control custom models, use `+` to add a custom model, use `-` to hide a model, use `name:displayName` to customize model name, separated by comma. ## Requirements diff --git a/README_CN.md b/README_CN.md index 604771c52..dde8c19b3 100644 --- a/README_CN.md +++ b/README_CN.md @@ -122,9 +122,9 @@ Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.micro ### `CUSTOM_MODELS` (可选) -> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo` 表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`。 +> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-1106-preview:gpt-4-turbo` 表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`,并将 `gpt-4-1106-preview` 模型名字展示为 `gpt-4-turbo`。 -用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,用英文逗号隔开。 +用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名:展示名` 来自定义模型的展示名,用英文逗号隔开。 ## 开发 From be6d45e49f1df90daba4625117b95903189891c2 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 12 Nov 2023 01:21:39 +0800 Subject: [PATCH 39/67] feat: close #3222 share message list should start from clear context index --- app/components/exporter.tsx | 29 ++++++++++++++----- app/components/message-selector.module.scss | 10 +++++-- app/components/message-selector.tsx | 31 ++++++++++++++++----- app/components/ui-lib.tsx | 4 ++- 4 files changed, 57 insertions(+), 17 deletions(-) diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 0a885d874..435e24953 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -27,7 +27,7 @@ import { Avatar } from "./emoji"; import dynamic from "next/dynamic"; import NextImage from "next/image"; -import { toBlob, toJpeg, toPng } from "html-to-image"; +import { toBlob, toPng } from "html-to-image"; import { DEFAULT_MASK_AVATAR } from "../store/mask"; import { api } from "../client/api"; import { prettyObject } from "../utils/format"; @@ -41,7 +41,22 @@ const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { export function ExportMessageModal(props: { onClose: () => void }) { return (
- + + 只有清除上下文之后的消息会被展示 +
+ } + >
@@ -149,7 +164,7 @@ export function MessageExporter() { if (exportConfig.includeContext) { ret.push(...session.mask.context); } - ret.push(...session.messages.filter((m, i) => selection.has(m.id))); + ret.push(...session.messages.filter((m) => selection.has(m.id))); return ret; }, [ exportConfig.includeContext, @@ -437,13 +452,13 @@ export function ImagePreviewer(props: { showToast(Locale.Export.Image.Toast); const dom = previewRef.current; if (!dom) return; - + const isApp = getClientConfig()?.isApp; - + try { const blob = await toPng(dom); if (!blob) return; - + if (isMobile || (isApp && window.__TAURI__)) { if (isApp && window.__TAURI__) { const result = await window.__TAURI__.dialog.save({ @@ -459,7 +474,7 @@ export function ImagePreviewer(props: { }, ], }); - + if (result !== null) { const response = await fetch(blob); const buffer = await response.arrayBuffer(); diff --git a/app/components/message-selector.module.scss b/app/components/message-selector.module.scss index b4ba1a141..c8defb6b0 100644 --- a/app/components/message-selector.module.scss +++ b/app/components/message-selector.module.scss @@ -58,8 +58,8 @@ } .body { - flex-grow: 1; - max-width: calc(100% - 40px); + flex: 1; + max-width: calc(100% - 80px); .date { font-size: 12px; @@ -71,6 +71,12 @@ font-size: 12px; } } + + .checkbox { + display: flex; + justify-content: flex-end; + flex: 1; + } } } } diff --git a/app/components/message-selector.tsx b/app/components/message-selector.tsx index cadf52e64..3d2321d09 100644 --- a/app/components/message-selector.tsx +++ b/app/components/message-selector.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { ChatMessage, useAppConfig, useChatStore } from "../store"; import { Updater } from "../typing"; import { IconButton } from "./button"; @@ -73,11 +73,23 @@ export function MessageSelector(props: { const chatStore = useChatStore(); const session = chatStore.currentSession(); const isValid = (m: ChatMessage) => m.content && !m.isError && !m.streaming; - const messages = session.messages.filter( - (m, i) => - m.id && // message must have id - isValid(m) && - (i >= session.messages.length - 1 || isValid(session.messages[i + 1])), + const allMessages = useMemo(() => { + let startIndex = Math.max(0, session.clearContextIndex ?? 0); + if (startIndex === session.messages.length - 1) { + startIndex = 0; + } + return session.messages.slice(startIndex); + }, [session.messages, session.clearContextIndex]); + + const messages = useMemo( + () => + allMessages.filter( + (m, i) => + m.id && // message must have id + isValid(m) && + (i >= allMessages.length - 1 || isValid(allMessages[i + 1])), + ), + [allMessages], ); const messageCount = messages.length; const config = useAppConfig(); @@ -176,6 +188,8 @@ export function MessageSelector(props: {
{messages.map((m, i) => { if (!isInSearchResult(m.id!)) return null; + const id = m.id ?? i; + const isSelected = props.selection.has(id); return (
{ props.updateSelection((selection) => { - const id = m.id ?? i; selection.has(id) ? selection.delete(id) : selection.add(id); }); onClickIndex(i); @@ -206,6 +219,10 @@ export function MessageSelector(props: { {m.content}
+ +
+ +
); })} diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx index 0c927728a..f7e326fd3 100644 --- a/app/components/ui-lib.tsx +++ b/app/components/ui-lib.tsx @@ -97,8 +97,9 @@ export function Loading() { interface ModalProps { title: string; children?: any; - actions?: JSX.Element[]; + actions?: React.ReactNode[]; defaultMax?: boolean; + footer?: React.ReactNode; onClose?: () => void; } export function Modal(props: ModalProps) { @@ -147,6 +148,7 @@ export function Modal(props: ModalProps) {
{props.children}
+ {props.footer}
{props.actions?.map((action, i) => (
From 0f6ed9c2932f6fd31214ec5d1b9d1d6a5b1f56d5 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sun, 12 Nov 2023 00:53:15 +0700 Subject: [PATCH 40/67] Feat UI/UX Page Local Language [Exporter Message] [+] fix(exporter.tsx): update the text in the ExportMessageModal component to use the localized title from the locale file [+] feat(cn.ts, en.ts, id.ts): add localized title for the Exporter Description in the respective locale files --- app/components/exporter.tsx | 2 +- app/locales/cn.ts | 3 +++ app/locales/en.ts | 3 +++ app/locales/id.ts | 3 +++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 435e24953..571c28122 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -53,7 +53,7 @@ export function ExportMessageModal(props: { onClose: () => void }) { opacity: 0.5, }} > - 只有清除上下文之后的消息会被展示 + {Locale.Exporter.Description.Title}
} > diff --git a/app/locales/cn.ts b/app/locales/cn.ts index e721adef7..bb4baf506 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -441,6 +441,9 @@ const cn = { Config: "配置", }, Exporter: { + Description : { + Title: "只有清除上下文之后的消息会被展示" + }, Model: "模型", Messages: "消息", Topic: "主题", diff --git a/app/locales/en.ts b/app/locales/en.ts index c6e61ecab..f90cffd4c 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -442,6 +442,9 @@ const en: LocaleType = { Config: "Config", }, Exporter: { + Description: { + Title: "Only messages after clearing the context will be displayed" + }, Model: "Model", Messages: "Messages", Topic: "Topic", diff --git a/app/locales/id.ts b/app/locales/id.ts index 4da55948e..571156a57 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -368,6 +368,9 @@ const id: PartialLocaleType = { Edit: "Edit", }, Exporter: { + Description: { + Title: "Hanya pesan setelah menghapus konteks yang akan ditampilkan" + }, Model: "Model", Messages: "Pesan", Topic: "Topik", From a46f08154e74744ac6c0bec6d074e00df445a851 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sun, 12 Nov 2023 01:50:35 +0700 Subject: [PATCH 41/67] Fix UI/UX Page Local Language [Exporter Message] fix(locales): fix incorrect description title in en.ts and id.ts - Change "Only messages after clearing the context will be displayed" to "Only messages before clearing the context will be displayed" in en.ts - Change "Hanya pesan setelah menghapus konteks yang akan ditampilkan" to "Hanya pesan sebelum menghapus konteks yang akan ditampilkan" in id.ts --- app/locales/en.ts | 2 +- app/locales/id.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locales/en.ts b/app/locales/en.ts index f90cffd4c..7390b7284 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -443,7 +443,7 @@ const en: LocaleType = { }, Exporter: { Description: { - Title: "Only messages after clearing the context will be displayed" + Title: "Only messages before clearing the context will be displayed" }, Model: "Model", Messages: "Messages", diff --git a/app/locales/id.ts b/app/locales/id.ts index 571156a57..2b9b21e11 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -369,7 +369,7 @@ const id: PartialLocaleType = { }, Exporter: { Description: { - Title: "Hanya pesan setelah menghapus konteks yang akan ditampilkan" + Title: "Hanya pesan sebelum menghapus konteks yang akan ditampilkan" }, Model: "Model", Messages: "Pesan", From 5ba3fc9321a126dce367c57d14649ec8a590dc82 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sun, 12 Nov 2023 01:56:31 +0700 Subject: [PATCH 42/67] Revert "Fix UI/UX Page Local Language [Exporter Message]" This reverts commit a46f08154e74744ac6c0bec6d074e00df445a851. Reason : better after instead of before --- app/locales/en.ts | 2 +- app/locales/id.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locales/en.ts b/app/locales/en.ts index 7390b7284..f90cffd4c 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -443,7 +443,7 @@ const en: LocaleType = { }, Exporter: { Description: { - Title: "Only messages before clearing the context will be displayed" + Title: "Only messages after clearing the context will be displayed" }, Model: "Model", Messages: "Messages", diff --git a/app/locales/id.ts b/app/locales/id.ts index 2b9b21e11..571156a57 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -369,7 +369,7 @@ const id: PartialLocaleType = { }, Exporter: { Description: { - Title: "Hanya pesan sebelum menghapus konteks yang akan ditampilkan" + Title: "Hanya pesan setelah menghapus konteks yang akan ditampilkan" }, Model: "Model", Messages: "Pesan", From 3a654ba1998581ce5e319988277d2858854f0edf Mon Sep 17 00:00:00 2001 From: nanaya Date: Sun, 12 Nov 2023 11:18:14 +0800 Subject: [PATCH 43/67] UI (model selection): hide unavailable model options --- app/components/model-config.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx index 1c730e144..214a18c79 100644 --- a/app/components/model-config.tsx +++ b/app/components/model-config.tsx @@ -25,11 +25,13 @@ export function ModelConfigList(props: { ); }} > - {allModels.map((v, i) => ( - - ))} + {allModels + .filter((v) => v.available) + .map((v, i) => ( + + ))} Date: Sun, 12 Nov 2023 19:33:19 +0800 Subject: [PATCH 44/67] fix: #3189 should correct math eq in exporter --- app/components/chat-list.tsx | 5 ++++- app/components/chat.tsx | 7 ++++++- app/components/exporter.module.scss | 3 ++- app/components/exporter.tsx | 7 +++---- app/components/markdown.tsx | 2 +- app/components/mask.tsx | 18 +++++++++++------- app/components/message-selector.tsx | 5 ++++- app/components/new-chat.tsx | 16 ++++------------ app/constant.ts | 3 +++ app/locales/cn.ts | 4 ++-- 10 files changed, 40 insertions(+), 30 deletions(-) diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index f76b369f1..33967717d 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -61,7 +61,10 @@ export function ChatItem(props: { {props.narrow ? (
- +
{props.count} diff --git a/app/components/chat.tsx b/app/components/chat.tsx index a088483e7..4d9de7259 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1176,7 +1176,12 @@ function _Chat() { {["system"].includes(message.role) ? ( ) : ( - + )} )} diff --git a/app/components/exporter.module.scss b/app/components/exporter.module.scss index c2046ffc0..d3bfc81ad 100644 --- a/app/components/exporter.module.scss +++ b/app/components/exporter.module.scss @@ -186,7 +186,8 @@ box-shadow: var(--card-shadow); border: var(--border-in-light); - *:not(li) { + code, + pre { overflow: hidden; } } diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index 435e24953..185cbb20b 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -1,5 +1,5 @@ /* eslint-disable @next/next/no-img-element */ -import { ChatMessage, useAppConfig, useChatStore } from "../store"; +import { ChatMessage, ModelType, useAppConfig, useChatStore } from "../store"; import Locale from "../locales"; import styles from "./exporter.module.scss"; import { @@ -275,7 +275,8 @@ export function RenderExport(props: { }); props.onRender(renderMsgs); - }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return (
@@ -619,8 +620,6 @@ export function MarkdownPreviewer(props: { ); } -// modified by BackTrackZ now it's looks better - export function JsonPreviewer(props: { messages: ChatMessage[]; topic: string; diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 1a1fbf416..b4cae9a21 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -11,7 +11,7 @@ import mermaid from "mermaid"; import LoadingIcon from "../icons/three-dots.svg"; import React from "react"; -import { useDebouncedCallback, useThrottledCallback } from "use-debounce"; +import { useDebouncedCallback } from "use-debounce"; import { showImageModal } from "./ui-lib"; export function Mermaid(props: { code: string }) { diff --git a/app/components/mask.tsx b/app/components/mask.tsx index 9fe1d485a..3f616c3ac 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -18,6 +18,7 @@ import { ChatMessage, createMessage, ModelConfig, + ModelType, useAppConfig, useChatStore, } from "../store"; @@ -58,11 +59,11 @@ function reorder(list: T[], startIndex: number, endIndex: number): T[] { return result; } -export function MaskAvatar(props: { mask: Mask }) { - return props.mask.avatar !== DEFAULT_MASK_AVATAR ? ( - +export function MaskAvatar(props: { avatar: string; model?: ModelType }) { + return props.avatar !== DEFAULT_MASK_AVATAR ? ( + ) : ( - + ); } @@ -123,7 +124,10 @@ export function MaskConfig(props: { onClick={() => setShowPicker(true)} style={{ cursor: "pointer" }} > - +
@@ -398,7 +402,7 @@ export function MaskPage() { setSearchText(text); if (text.length > 0) { const result = allMasks.filter((m) => - m.name.toLowerCase().includes(text.toLowerCase()) + m.name.toLowerCase().includes(text.toLowerCase()), ); setSearchMasks(result); } else { @@ -523,7 +527,7 @@ export function MaskPage() {
- +
{m.name}
diff --git a/app/components/message-selector.tsx b/app/components/message-selector.tsx index 3d2321d09..c20153401 100644 --- a/app/components/message-selector.tsx +++ b/app/components/message-selector.tsx @@ -208,7 +208,10 @@ export function MessageSelector(props: { {m.role === "user" ? ( ) : ( - + )}
diff --git a/app/components/new-chat.tsx b/app/components/new-chat.tsx index 76cbbeeb1..54c646f23 100644 --- a/app/components/new-chat.tsx +++ b/app/components/new-chat.tsx @@ -17,21 +17,13 @@ import { useCommand } from "../command"; import { showConfirm } from "./ui-lib"; import { BUILTIN_MASK_STORE } from "../masks"; -function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) { - const xmin = Math.max(aRect.x, bRect.x); - const xmax = Math.min(aRect.x + aRect.width, bRect.x + bRect.width); - const ymin = Math.max(aRect.y, bRect.y); - const ymax = Math.min(aRect.y + aRect.height, bRect.y + bRect.height); - const width = xmax - xmin; - const height = ymax - ymin; - const intersectionArea = width < 0 || height < 0 ? 0 : width * height; - return intersectionArea; -} - function MaskItem(props: { mask: Mask; onClick?: () => void }) { return (
- +
{props.mask.name}
); diff --git a/app/constant.ts b/app/constant.ts index 561899769..779c6f7e7 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -84,6 +84,9 @@ You are ChatGPT, a large language model trained by OpenAI. Knowledge cutoff: {{cutoff}} Current model: {{model}} Current time: {{time}} + +Latex inline: $x^2$ +Latex block: $$e=mc^2$$ `; export const SUMMARIZE_MODEL = "gpt-3.5-turbo"; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index e721adef7..f5b65559f 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -85,8 +85,8 @@ const cn = { Copy: "全部复制", Download: "下载文件", Share: "分享到 ShareGPT", - MessageFromYou: "来自你的消息", - MessageFromChatGPT: "来自 ChatGPT 的消息", + MessageFromYou: "用户", + MessageFromChatGPT: "ChatGPT", Format: { Title: "导出格式", SubTitle: "可以导出 Markdown 文本或者 PNG 图片", From a0cd939bfd560621b854b7533fa0b28a329dfa75 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 12 Nov 2023 19:45:58 +0800 Subject: [PATCH 45/67] fix: #2841 dollar sign conflict with latex math --- app/components/markdown.tsx | 26 ++++++++++++++++++++++++-- app/constant.ts | 1 - 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index b4cae9a21..f3a916cc5 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -5,7 +5,7 @@ import RemarkBreaks from "remark-breaks"; import RehypeKatex from "rehype-katex"; import RemarkGfm from "remark-gfm"; import RehypeHighlight from "rehype-highlight"; -import { useRef, useState, RefObject, useEffect } from "react"; +import { useRef, useState, RefObject, useEffect, useMemo } from "react"; import { copyToClipboard } from "../utils"; import mermaid from "mermaid"; @@ -99,7 +99,29 @@ export function PreCode(props: { children: any }) { ); } +function escapeDollarNumber(text: string) { + let escapedText = ""; + + for (let i = 0; i < text.length; i += 1) { + let char = text[i]; + const nextChar = text[i + 1] || " "; + + if (char === "$" && nextChar >= "0" && nextChar <= "9") { + char = "\\$"; + } + + escapedText += char; + } + + return escapedText; +} + function _MarkDownContent(props: { content: string }) { + const escapedContent = useMemo( + () => escapeDollarNumber(props.content), + [props.content], + ); + return ( - {props.content} + {escapedContent} ); } diff --git a/app/constant.ts b/app/constant.ts index 779c6f7e7..69d5c511f 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -84,7 +84,6 @@ You are ChatGPT, a large language model trained by OpenAI. Knowledge cutoff: {{cutoff}} Current model: {{model}} Current time: {{time}} - Latex inline: $x^2$ Latex block: $$e=mc^2$$ `; From d033168d80b54636e306d6a38e604482f3999486 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 13 Nov 2023 10:53:30 +0800 Subject: [PATCH 46/67] fix: #3241 should not ensure openai url non-empty --- app/store/access.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/store/access.ts b/app/store/access.ts index 2abe1e3cc..3b9008ba8 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -49,7 +49,7 @@ export const useAccessStore = createPersistStore( }, isValidOpenAI() { - return ensure(get(), ["openaiUrl", "openaiApiKey"]); + return ensure(get(), ["openaiApiKey"]); }, isValidAzure() { From 011b52d07d3d5d5c96a821ded5989d9be9fd7274 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 13 Nov 2023 16:53:36 +0800 Subject: [PATCH 47/67] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index abe933061..d4e304f9d 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,7 @@ If you want to add a new translation, read this [document](./docs/translation.md [@synwith](https://github.com/synwith) [@piksonGit](https://github.com/piksonGit) [@ouyangzhiping](https://github.com/ouyangzhiping) +[@wenjiavv](https://github.com/wenjiavv) ### Contributor From 1e5153173cbf9f938e45f32f643eca3c9144c8b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:45:37 +0000 Subject: [PATCH 48/67] chore(deps-dev): bump @types/node from 20.3.3 to 20.9.0 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.3.3 to 20.9.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0eed3bd9b..d19e30579 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ }, "devDependencies": { "@tauri-apps/cli": "^1.4.0", - "@types/node": "^20.3.3", + "@types/node": "^20.9.0", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.7", "@types/react-katex": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 8adbf7dc2..8068a6bf7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1507,10 +1507,12 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== -"@types/node@*", "@types/node@^20.3.3": - version "20.3.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.3.tgz#329842940042d2b280897150e023e604d11657d6" - integrity sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw== +"@types/node@*", "@types/node@^20.9.0": + version "20.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.0.tgz#bfcdc230583aeb891cf51e73cfdaacdd8deae298" + integrity sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw== + dependencies: + undici-types "~5.26.4" "@types/parse-json@^4.0.0": version "4.0.0" @@ -5799,6 +5801,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" From 5fbf4c394cbc91f35e48fee6b09879a00891f01f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:45:47 +0000 Subject: [PATCH 49/67] chore(deps-dev): bump @types/spark-md5 from 3.0.2 to 3.0.4 Bumps [@types/spark-md5](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/spark-md5) from 3.0.2 to 3.0.4. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/spark-md5) --- updated-dependencies: - dependency-name: "@types/spark-md5" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0eed3bd9b..178269e25 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@types/react": "^18.2.14", "@types/react-dom": "^18.2.7", "@types/react-katex": "^3.0.0", - "@types/spark-md5": "^3.0.2", + "@types/spark-md5": "^3.0.4", "cross-env": "^7.0.3", "eslint": "^8.49.0", "eslint-config-next": "13.4.19", diff --git a/yarn.lock b/yarn.lock index 8adbf7dc2..fc03cd9cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1550,10 +1550,10 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== -"@types/spark-md5@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.2.tgz#da2e8a778a20335fc4f40b6471c4b0d86b70da55" - integrity sha512-82E/lVRaqelV9qmRzzJ1PKTpyrpnT7mwdneKNJB9hUtypZDMggloDfFUCIqRRx3lYRxteCwXSq9c+W71Vf0QnQ== +"@types/spark-md5@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.4.tgz#c1221d63c069d95aba0c06a765b80661cacc12bf" + integrity sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q== "@types/unist@*", "@types/unist@^2.0.0": version "2.0.6" From a64c9dae42b84b97847b558aa28fa786deb83a2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:45:59 +0000 Subject: [PATCH 50/67] chore(deps): bump mermaid from 10.3.1 to 10.6.1 Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.3.1 to 10.6.1. - [Release notes](https://github.com/mermaid-js/mermaid/releases) - [Changelog](https://github.com/mermaid-js/mermaid/blob/develop/CHANGELOG.md) - [Commits](https://github.com/mermaid-js/mermaid/compare/v10.3.1...v10.6.1) --- updated-dependencies: - dependency-name: mermaid dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0eed3bd9b..9b2747e35 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "emoji-picker-react": "^4.5.1", "fuse.js": "^6.6.2", "html-to-image": "^1.11.11", - "mermaid": "^10.3.1", + "mermaid": "^10.6.1", "nanoid": "^4.0.2", "next": "^13.4.9", "node-fetch": "^3.3.1", diff --git a/yarn.lock b/yarn.lock index 8adbf7dc2..8e21ab08e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4314,10 +4314,10 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mermaid@^10.3.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.3.1.tgz#2f3c7e9f6bd7a8da2bef71cce2a542c8eba2a62e" - integrity sha512-hkenh7WkuRWPcob3oJtrN3W+yzrrIYuWF1OIfk/d0xGE8UWlvDhfexaHmDwwe8DKQgqMLI8DWEPwGprxkumjuw== +mermaid@^10.6.1: + version "10.6.1" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.6.1.tgz#701f4160484137a417770ce757ce1887a98c00fc" + integrity sha512-Hky0/RpOw/1il9X8AvzOEChfJtVvmXm+y7JML5C//ePYMy0/9jCEmW1E1g86x9oDfW9+iVEdTV/i+M6KWRNs4A== dependencies: "@braintree/sanitize-url" "^6.0.1" "@types/d3-scale" "^4.0.3" From 7d1fae32cd1ce5d07104da22a96e9680bff66f7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:22:50 +0000 Subject: [PATCH 51/67] chore(deps): bump emoji-picker-react from 4.5.1 to 4.5.15 Bumps [emoji-picker-react](https://github.com/ealush/emoji-picker-react) from 4.5.1 to 4.5.15. - [Release notes](https://github.com/ealush/emoji-picker-react/releases) - [Commits](https://github.com/ealush/emoji-picker-react/commits) --- updated-dependencies: - dependency-name: emoji-picker-react dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 9b2747e35..acf0d5c3b 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@hello-pangea/dnd": "^16.3.0", "@svgr/webpack": "^6.5.1", "@vercel/analytics": "^0.1.11", - "emoji-picker-react": "^4.5.1", + "emoji-picker-react": "^4.5.15", "fuse.js": "^6.6.2", "html-to-image": "^1.11.11", "mermaid": "^10.6.1", diff --git a/yarn.lock b/yarn.lock index 8e21ab08e..ba1e73990 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2126,11 +2126,6 @@ client-only@0.0.1: resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== -clsx@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" - integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2762,12 +2757,10 @@ elkjs@^0.8.2: resolved "https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e" integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ== -emoji-picker-react@^4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.5.1.tgz#341f27dc86ad09340a316e0632484fcb9aff7195" - integrity sha512-zpm0ui0TWkXZDUIevyNM0rC9Jyqc08RvVXH0KgsbSkDr+VgMQmYLu6UeI4SIWMZKsKMjQwujPpncRCFlEeykjw== - dependencies: - clsx "^1.2.1" +emoji-picker-react@^4.5.15: + version "4.5.15" + resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.5.15.tgz#e12797c50584cb8af8aee7eb6c7c8fd953e41f7e" + integrity sha512-BTqo+pNUE8kqX8BKFTbD4fhlxcA69qfie5En4PerReLaaPfXVyRlDJ1uf85nKj2u5esUQ999iUf8YyqcPsM2Qw== emoji-regex@^8.0.0: version "8.0.0" From a9d605ed301adb78379da7e577847b5839ac88d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:22:51 +0000 Subject: [PATCH 52/67] chore(deps): bump nanoid from 4.0.2 to 5.0.3 Bumps [nanoid](https://github.com/ai/nanoid) from 4.0.2 to 5.0.3. - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/4.0.2...5.0.3) --- updated-dependencies: - dependency-name: nanoid dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9b2747e35..72fc5ae3f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "fuse.js": "^6.6.2", "html-to-image": "^1.11.11", "mermaid": "^10.6.1", - "nanoid": "^4.0.2", + "nanoid": "^5.0.3", "next": "^13.4.9", "node-fetch": "^3.3.1", "react": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index 8e21ab08e..fad107c28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4690,10 +4690,10 @@ nanoid@^3.3.4: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== -nanoid@^4.0.2: - version "4.0.2" - resolved "https://registry.npmmirror.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e" - integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw== +nanoid@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.3.tgz#6c97f53d793a7a1de6a38ebb46f50f95bf9793c7" + integrity sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA== natural-compare@^1.4.0: version "1.4.0" From f1772f46254eb6df9bdefe39987bb008273fe7c5 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Tue, 14 Nov 2023 15:04:03 +0700 Subject: [PATCH 53/67] Fix UI/UX Page [Settings] [+] fix(settings.tsx): fix condition to show balance query in settings component [+] fix(settings.tsx): add condition to hide balance query for app clients --- app/components/settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 178fcec57..1edb3c6ba 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -1052,7 +1052,7 @@ export function Settings() { )} - {!shouldHideBalanceQuery ? ( + {!shouldHideBalanceQuery && !clientConfig?.isApp ? ( Date: Thu, 16 Nov 2023 00:53:11 +0800 Subject: [PATCH 54/67] fix: #3275 refuse on server side if hide user api key --- app/api/auth.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/api/auth.ts b/app/api/auth.ts index c1f6e7fde..b41e34e05 100644 --- a/app/api/auth.ts +++ b/app/api/auth.ts @@ -46,6 +46,13 @@ export function auth(req: NextRequest) { }; } + 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.isAzure From 74eb42c11148511cb026d51f44ab1ab931e994fc Mon Sep 17 00:00:00 2001 From: Eric-2369 Date: Thu, 16 Nov 2023 11:33:10 +0800 Subject: [PATCH 55/67] config: exclude hkg1 from the deployment regions of the edge function --- app/api/openai/[...path]/route.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/api/openai/[...path]/route.ts b/app/api/openai/[...path]/route.ts index 9df005a31..2addd53a5 100644 --- a/app/api/openai/[...path]/route.ts +++ b/app/api/openai/[...path]/route.ts @@ -75,3 +75,4 @@ 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']; From 401fa198c970d012a333a4a9acf0cd08090533e0 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 17 Nov 2023 11:29:52 +0800 Subject: [PATCH 56/67] Update tauri.conf.json --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 397ae0d83..182d00792 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "ChatGPT Next Web", - "version": "2.9.11" + "version": "2.9.12" }, "tauri": { "allowlist": { From 3c510cfaf0296184deecebe55d97f42019180af7 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 17 Nov 2023 11:56:42 +0800 Subject: [PATCH 57/67] chore: update gh actions nodejs version to 18 --- .github/workflows/app.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/app.yml b/.github/workflows/app.yml index b928ad6c1..aebba28f7 100644 --- a/.github/workflows/app.yml +++ b/.github/workflows/app.yml @@ -18,7 +18,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: get version run: echo "PACKAGE_VERSION=$(node -p "require('./src-tauri/tauri.conf.json').package.version")" >> $GITHUB_ENV - name: create release @@ -59,7 +59,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: From 7bdb68eecf58ce3d1531701c51ff3fbbbdd4d932 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 17 Nov 2023 15:10:51 +0800 Subject: [PATCH 58/67] config: disable ip forward in vercel json config --- vercel.json | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/vercel.json b/vercel.json index 1890a0f7d..21dc9cc24 100644 --- a/vercel.json +++ b/vercel.json @@ -2,23 +2,4 @@ "github": { "silent": true }, - "headers": [ - { - "source": "/(.*)", - "headers": [ - { - "key": "X-Real-IP", - "value": "$remote_addr" - }, - { - "key": "X-Forwarded-For", - "value": "$proxy_add_x_forwarded_for" - }, - { - "key": "Host", - "value": "$http_host" - } - ] - } - ] } From 914f4fb86211b7377bc1f8133b8f031503da4423 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 17 Nov 2023 15:11:03 +0800 Subject: [PATCH 59/67] Update vercel.json --- vercel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vercel.json b/vercel.json index 21dc9cc24..0cae358a1 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,5 @@ { "github": { "silent": true - }, + } } From cf220dd2eb08589c7bd99b5a2c05eddf8f2e106f Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Fri, 17 Nov 2023 15:22:11 +0800 Subject: [PATCH 60/67] Update vercel.json --- vercel.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/vercel.json b/vercel.json index 0cae358a1..1890a0f7d 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,24 @@ { "github": { "silent": true - } + }, + "headers": [ + { + "source": "/(.*)", + "headers": [ + { + "key": "X-Real-IP", + "value": "$remote_addr" + }, + { + "key": "X-Forwarded-For", + "value": "$proxy_add_x_forwarded_for" + }, + { + "key": "Host", + "value": "$http_host" + } + ] + } + ] } From 6f135a0ccebe055559ba81c3f002096ea49dc391 Mon Sep 17 00:00:00 2001 From: Yuri Filipe Date: Sat, 18 Nov 2023 15:12:15 -0300 Subject: [PATCH 61/67] Translation of the application into Brazilian Portuguese --- app/locales/index.ts | 3 + app/locales/pt.ts | 466 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 469 insertions(+) create mode 100644 app/locales/pt.ts diff --git a/app/locales/index.ts b/app/locales/index.ts index 79e314fac..1d84de22c 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -1,5 +1,6 @@ import cn from "./cn"; import en from "./en"; +import pt from "./pt"; import tw from "./tw"; import id from "./id"; import fr from "./fr"; @@ -23,6 +24,7 @@ export type { LocaleType, PartialLocaleType } from "./cn"; const ALL_LANGS = { cn, en, + pt, tw, jp, ko, @@ -47,6 +49,7 @@ export const AllLangs = Object.keys(ALL_LANGS) as Lang[]; export const ALL_LANG_OPTIONS: Record = { cn: "简体中文", en: "English", + pt: "Português", tw: "繁體中文", jp: "日本語", ko: "한국어", diff --git a/app/locales/pt.ts b/app/locales/pt.ts new file mode 100644 index 000000000..e984dc99a --- /dev/null +++ b/app/locales/pt.ts @@ -0,0 +1,466 @@ +import { SubmitKey } from "../store/config"; +import { LocaleType } from "../locales/index"; +import { getClientConfig } from "../config/client"; + +const isApp = !!getClientConfig()?.isApp; + +const pt: LocaleType = { + WIP: "Em breve...", + Error: { + Unauthorized: isApp + ? "Chave API inválida, por favor verifique em [Configurações](/#/settings)." + : "Acesso não autorizado, por favor insira o código de acesso em [auth](/#/auth) ou insira sua Chave API OpenAI.", + }, + Auth: { + Title: "Necessário Código de Acesso", + Tips: "Por favor, insira o código de acesso abaixo", + SubTips: "Ou insira sua Chave API OpenAI", + Input: "código de acesso", + Confirm: "Confirmar", + Later: "Depois", + }, + ChatItem: { + ChatItemCount: (count: number) => `${count} mensagens`, + }, + Chat: { + SubTitle: (count: number) => `${count} mensagens`, + EditMessage: { + Title: "Editar Todas as Mensagens", + Topic: { + Title: "Tópico", + SubTitle: "Mudar o tópico atual", + }, + }, + Actions: { + ChatList: "Ir Para Lista de Chat", + CompressedHistory: "Prompt de Memória Histórica Comprimida", + Export: "Exportar Todas as Mensagens como Markdown", + Copy: "Copiar", + Stop: "Parar", + Retry: "Tentar Novamente", + Pin: "Fixar", + PinToastContent: "Fixada 1 mensagem para prompts contextuais", + PinToastAction: "Visualizar", + Delete: "Deletar", + Edit: "Editar", + }, + Commands: { + new: "Iniciar um novo chat", + newm: "Iniciar um novo chat com máscara", + next: "Próximo Chat", + prev: "Chat Anterior", + clear: "Limpar Contexto", + del: "Deletar Chat", + }, + InputActions: { + Stop: "Parar", + ToBottom: "Para o Mais Recente", + Theme: { + auto: "Automático", + light: "Tema Claro", + dark: "Tema Escuro", + }, + Prompt: "Prompts", + Masks: "Máscaras", + Clear: "Limpar Contexto", + Settings: "Configurações", + }, + Rename: "Renomear Chat", + Typing: "Digitando…", + Input: (submitKey: string) => { + var inputHints = `${submitKey} para enviar`; + if (submitKey === String(SubmitKey.Enter)) { + inputHints += ", Shift + Enter para quebrar linha"; + } + return inputHints + ", / para buscar prompts, : para usar comandos"; + }, + Send: "Enviar", + Config: { + Reset: "Redefinir para Padrão", + SaveAs: "Salvar como Máscara", + }, + IsContext: "Prompt Contextual", + }, + Export: { + Title: "Exportar Mensagens", + Copy: "Copiar Tudo", + Download: "Baixar", + MessageFromYou: "Mensagem De Você", + MessageFromChatGPT: "Mensagem De ChatGPT", + Share: "Compartilhar para ShareGPT", + Format: { + Title: "Formato de Exportação", + SubTitle: "Markdown ou Imagem PNG", + }, + IncludeContext: { + Title: "Incluindo Contexto", + SubTitle: "Exportar prompts de contexto na máscara ou não", + }, + Steps: { + Select: "Selecionar", + Preview: "Pré-visualizar", + }, + Image: { + Toast: "Capturando Imagem...", + Modal: + "Pressione longamente ou clique com o botão direito para salvar a imagem", + }, + }, + Select: { + Search: "Buscar", + All: "Selecionar Tudo", + Latest: "Selecionar Mais Recente", + Clear: "Limpar", + }, + Memory: { + Title: "Prompt de Memória", + EmptyContent: "Nada ainda.", + Send: "Enviar Memória", + Copy: "Copiar Memória", + Reset: "Resetar Sessão", + ResetConfirm: + "Resetar irá limpar o histórico de conversa atual e a memória histórica. Você tem certeza que quer resetar?", + }, + Home: { + NewChat: "Novo Chat", + DeleteChat: "Confirmar para deletar a conversa selecionada?", + DeleteToast: "Chat Deletado", + Revert: "Reverter", + }, + Settings: { + Title: "Configurações", + SubTitle: "Todas as Configurações", + Danger: { + Reset: { + Title: "Resetar Todas as Configurações", + SubTitle: "Resetar todos os itens de configuração para o padrão", + Action: "Resetar", + Confirm: "Confirmar para resetar todas as configurações para o padrão?", + }, + Clear: { + Title: "Limpar Todos os Dados", + SubTitle: "Limpar todas as mensagens e configurações", + Action: "Limpar", + Confirm: "Confirmar para limpar todas as mensagens e configurações?", + }, + }, + Lang: { + Name: "Idioma", + All: "Todos os Idiomas", + }, + Avatar: "Avatar", + FontSize: { + Title: "Tamanho da Fonte", + SubTitle: "Ajustar o tamanho da fonte do conteúdo do chat", + }, + InjectSystemPrompts: { + Title: "Inserir Prompts de Sistema", + SubTitle: "Inserir um prompt de sistema global para cada requisição", + }, + InputTemplate: { + Title: "Modelo de Entrada", + SubTitle: "A mensagem mais recente será preenchida neste modelo", + }, + + Update: { + Version: (x: string) => `Versão: ${x}`, + IsLatest: "Última versão", + CheckUpdate: "Verificar Atualização", + IsChecking: "Verificando atualização...", + FoundUpdate: (x: string) => `Nova versão encontrada: ${x}`, + GoToUpdate: "Atualizar", + }, + SendKey: "Tecla de Envio", + Theme: "Tema", + TightBorder: "Borda Ajustada", + SendPreviewBubble: { + Title: "Bolha de Pré-visualização de Envio", + SubTitle: "Pré-visualizar markdown na bolha", + }, + AutoGenerateTitle: { + Title: "Gerar Título Automaticamente", + SubTitle: "Gerar um título adequado baseado no conteúdo da conversa", + }, + Sync: { + CloudState: "Última Atualização", + NotSyncYet: "Ainda não sincronizado", + Success: "Sincronização bem sucedida", + Fail: "Falha na sincronização", + + Config: { + Modal: { + Title: "Configurar Sincronização", + Check: "Verificar Conexão", + }, + SyncType: { + Title: "Tipo de Sincronização", + SubTitle: "Escolha seu serviço de sincronização favorito", + }, + Proxy: { + Title: "Habilitar Proxy CORS", + SubTitle: "Habilitar um proxy para evitar restrições de cross-origin", + }, + ProxyUrl: { + Title: "Endpoint de Proxy", + SubTitle: "Apenas aplicável ao proxy CORS embutido para este projeto", + }, + + WebDav: { + Endpoint: "Endpoint WebDAV", + UserName: "Nome de Usuário", + Password: "Senha", + }, + + UpStash: { + Endpoint: "URL REST Redis UpStash", + UserName: "Nome do Backup", + Password: "Token REST Redis UpStash", + }, + }, + + LocalState: "Dados Locais", + Overview: (overview: any) => { + return `${overview.chat} chats,${overview.message} mensagens,${overview.prompt} prompts,${overview.mask} máscaras`; + }, + ImportFailed: "Falha ao importar do arquivo", + }, + Mask: { + Splash: { + Title: "Tela de Início da Máscara", + SubTitle: + "Mostrar uma tela de início da máscara antes de iniciar novo chat", + }, + Builtin: { + Title: "Esconder Máscaras Embutidas", + SubTitle: "Esconder máscaras embutidas na lista de máscaras", + }, + }, + Prompt: { + Disable: { + Title: "Desabilitar auto-completar", + SubTitle: "Digite / para acionar auto-completar", + }, + List: "Lista de Prompts", + ListCount: (builtin: number, custom: number) => + `${builtin} embutidos, ${custom} definidos pelo usuário`, + Edit: "Editar", + Modal: { + Title: "Lista de Prompts", + Add: "Adicionar Um", + Search: "Buscar Prompts", + }, + EditModal: { + Title: "Editar Prompt", + }, + }, + HistoryCount: { + Title: "Contagem de Mensagens Anexadas", + SubTitle: "Número de mensagens enviadas anexadas por requisição", + }, + CompressThreshold: { + Title: "Limite de Compressão de Histórico", + SubTitle: + "Irá comprimir se o comprimento das mensagens não comprimidas exceder o valor", + }, + + Usage: { + Title: "Saldo da Conta", + SubTitle(used: any, total: any) { + return `Usado este mês ${used}, assinatura ${total}`; + }, + IsChecking: "Verificando...", + Check: "Verificar", + NoAccess: "Insira a Chave API para verificar o saldo", + }, + Access: { + AccessCode: { + Title: "Código de Acesso", + SubTitle: "Controle de Acesso Habilitado", + Placeholder: "Insira o Código", + }, + CustomEndpoint: { + Title: "Endpoint Personalizado", + SubTitle: "Use serviço personalizado Azure ou OpenAI", + }, + Provider: { + Title: "Provedor do Modelo", + SubTitle: "Selecione Azure ou OpenAI", + }, + OpenAI: { + ApiKey: { + Title: "Chave API OpenAI", + SubTitle: "Usar Chave API OpenAI personalizada", + Placeholder: "sk-xxx", + }, + + Endpoint: { + Title: "Endpoint OpenAI", + SubTitle: + "Deve começar com http(s):// ou usar /api/openai como padrão", + }, + }, + Azure: { + ApiKey: { + Title: "Chave API Azure", + SubTitle: "Verifique sua chave API do console Azure", + Placeholder: "Chave API Azure", + }, + + Endpoint: { + Title: "Endpoint Azure", + SubTitle: "Exemplo: ", + }, + + ApiVerion: { + Title: "Versão API Azure", + SubTitle: "Verifique sua versão API do console Azure", + }, + }, + CustomModel: { + Title: "Modelos Personalizados", + SubTitle: "Opções de modelo personalizado, separados por vírgula", + }, + }, + + Model: "Modelo", + Temperature: { + Title: "Temperatura", + SubTitle: "Um valor maior torna a saída mais aleatória", + }, + TopP: { + Title: "Top P", + SubTitle: "Não altere este valor junto com a temperatura", + }, + MaxTokens: { + Title: "Máximo de Tokens", + SubTitle: "Comprimento máximo de tokens de entrada e tokens gerados", + }, + PresencePenalty: { + Title: "Penalidade de Presença", + SubTitle: + "Um valor maior aumenta a probabilidade de falar sobre novos tópicos", + }, + FrequencyPenalty: { + Title: "Penalidade de Frequência", + SubTitle: + "Um valor maior diminui a probabilidade de repetir a mesma linha", + }, + }, + Store: { + DefaultTopic: "Nova Conversa", + BotHello: "Olá! Como posso ajudá-lo hoje?", + Error: "Algo deu errado, por favor tente novamente mais tarde.", + Prompt: { + History: (content: string) => + "Este é um resumo do histórico de chat como um recapitulativo: " + + content, + Topic: + "Por favor, gere um título de quatro a cinco palavras resumindo nossa conversa sem qualquer introdução, pontuação, aspas, períodos, símbolos ou texto adicional. Remova as aspas que o envolvem.", + Summarize: + "Resuma a discussão brevemente em 200 palavras ou menos para usar como um prompt para o contexto futuro.", + }, + }, + Copy: { + Success: "Copiado para a área de transferência", + Failed: + "Falha na cópia, por favor conceda permissão para acessar a área de transferência", + }, + Download: { + Success: "Conteúdo baixado para seu diretório.", + Failed: "Falha no download.", + }, + Context: { + Toast: (x: any) => `Com ${x} prompts contextuais`, + Edit: "Configurações do Chat Atual", + Add: "Adicionar um Prompt", + Clear: "Contexto Limpo", + Revert: "Reverter", + }, + Plugin: { + Name: "Plugin", + }, + FineTuned: { + Sysmessage: "Você é um assistente que", + }, + Mask: { + Name: "Máscara", + Page: { + Title: "Template de Prompt", + SubTitle: (count: number) => `${count} templates de prompt`, + Search: "Buscar Templates", + Create: "Criar", + }, + Item: { + Info: (count: number) => `${count} prompts`, + Chat: "Chat", + View: "Visualizar", + Edit: "Editar", + Delete: "Deletar", + DeleteConfirm: "Confirmar para deletar?", + }, + EditModal: { + Title: (readonly: boolean) => + `Editar Template de Prompt ${readonly ? "(somente leitura)" : ""}`, + Download: "Baixar", + Clone: "Clonar", + }, + Config: { + Avatar: "Avatar do Bot", + Name: "Nome do Bot", + Sync: { + Title: "Usar Configuração Global", + SubTitle: "Usar configuração global neste chat", + Confirm: + "Confirmar para substituir a configuração personalizada pela configuração global?", + }, + HideContext: { + Title: "Esconder Prompts de Contexto", + SubTitle: "Não mostrar prompts de contexto no chat", + }, + Share: { + Title: "Compartilhar Esta Máscara", + SubTitle: "Gerar um link para esta máscara", + Action: "Copiar Link", + }, + }, + }, + NewChat: { + Return: "Retornar", + Skip: "Apenas Começar", + Title: "Escolher uma Máscara", + SubTitle: "Converse com a Alma por trás da Máscara", + More: "Encontre Mais", + NotShow: "Nunca Mostrar Novamente", + ConfirmNoShow: + "Confirmar para desabilitar?Você pode habilitar nas configurações depois.", + }, + + UI: { + Confirm: "Confirmar", + Cancel: "Cancelar", + Close: "Fechar", + Create: "Criar", + Edit: "Editar", + Export: "Exportar", + Import: "Importar", + Sync: "Sincronizar", + Config: "Configurar", + }, + Exporter: { + Description: { + Title: "Apenas mensagens após a limpeza do contexto serão exibidas", + }, + Model: "Modelo", + Messages: "Mensagens", + Topic: "Tópico", + Time: "Tempo", + }, + + URLCommand: { + Code: "Código de acesso detectado a partir da url, confirmar para aplicar? ", + Settings: + "Configurações detectadas a partir da url, confirmar para aplicar?", + }, +}; + +export default pt; From 16b2a3e66edf1565b8cf6ad5685adf76534c9106 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Sun, 19 Nov 2023 04:23:25 +0800 Subject: [PATCH 62/67] Update vercel.json --- vercel.json | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/vercel.json b/vercel.json index 1890a0f7d..0cae358a1 100644 --- a/vercel.json +++ b/vercel.json @@ -1,24 +1,5 @@ { "github": { "silent": true - }, - "headers": [ - { - "source": "/(.*)", - "headers": [ - { - "key": "X-Real-IP", - "value": "$remote_addr" - }, - { - "key": "X-Forwarded-For", - "value": "$proxy_add_x_forwarded_for" - }, - { - "key": "Host", - "value": "$http_host" - } - ] - } - ] + } } From 536ace8e10553c6101308ec09f2fa65bc84d2416 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 19 Nov 2023 18:24:51 +0800 Subject: [PATCH 63/67] feat: animate streaming response to make more smooth --- app/client/platforms/openai.ts | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 930d60690..dc79d2cec 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -115,12 +115,33 @@ export class ChatGPTApi implements LLMApi { if (shouldStream) { let responseText = ""; + let remainText = ""; let finished = false; + // animate response to make it looks smooth + function animateResponseText() { + if (finished || controller.signal.aborted) { + responseText += remainText; + console.log("[Response Animation] finished"); + return; + } + + if (remainText.length > 0) { + responseText += remainText[0]; + remainText = remainText.slice(1); + options.onUpdate?.(responseText, remainText[0]); + } + + requestAnimationFrame(animateResponseText); + } + + // start animaion + animateResponseText(); + const finish = () => { if (!finished) { - options.onFinish(responseText); finished = true; + options.onFinish(responseText + remainText); } }; @@ -183,8 +204,7 @@ export class ChatGPTApi implements LLMApi { }; const delta = json.choices[0]?.delta?.content; if (delta) { - responseText += delta; - options.onUpdate?.(responseText, delta); + remainText += delta; } } catch (e) { console.error("[Request] parse error", text); From dc7159a4504682f6bfad104d5d03168412c550f1 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 19 Nov 2023 18:32:54 +0800 Subject: [PATCH 64/67] feat: close #3301 enable or diable default models with -all / +all --- README.md | 6 ++++-- README_CN.md | 5 +++-- app/utils/model.ts | 6 ++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d4e304f9d..ef7f2e1d9 100644 --- a/README.md +++ b/README.md @@ -216,9 +216,11 @@ If you want to disable parse settings from url, set this to 1. ### `CUSTOM_MODELS` (optional) > Default: Empty -> Example: `+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview:gpt-4-turbo` means add `llama, claude-2` to model list, and remove `gpt-3.5-turbo` from list, and display `gpt-4-1106-preview` as `gpt-4-turbo`. +> Example: `+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo` means add `llama, claude-2` to model list, and remove `gpt-3.5-turbo` from list, and display `gpt-4-1106-preview` as `gpt-4-turbo`. -To control custom models, use `+` to add a custom model, use `-` to hide a model, use `name:displayName` to customize model name, separated by comma. +To control custom models, use `+` to add a custom model, use `-` to hide a model, use `name=displayName` to customize model name, separated by comma. + +User `-all` to disable all default models, `+all` to enable all default models. ## Requirements diff --git a/README_CN.md b/README_CN.md index dde8c19b3..3b713255a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -122,9 +122,10 @@ Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.micro ### `CUSTOM_MODELS` (可选) -> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-1106-preview:gpt-4-turbo` 表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`,并将 `gpt-4-1106-preview` 模型名字展示为 `gpt-4-turbo`。 +> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo` 表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`,并将 `gpt-4-1106-preview` 模型名字展示为 `gpt-4-turbo`。 +> 如果你想先禁用所有模型,再启用指定模型,可以使用 `-all,+gpt-3.5-turbo`,则表示仅启用 `gpt-3.5-turbo` -用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名:展示名` 来自定义模型的展示名,用英文逗号隔开。 +用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。 ## 开发 diff --git a/app/utils/model.ts b/app/utils/model.ts index d5c009c02..bf7300806 100644 --- a/app/utils/model.ts +++ b/app/utils/model.ts @@ -27,6 +27,12 @@ export function collectModelTable( const nameConfig = m.startsWith("+") || m.startsWith("-") ? m.slice(1) : m; const [name, displayName] = nameConfig.split(":"); + + // enable or disable all models + if (name === "all") { + Object.values(modelTable).forEach((m) => (m.available = available)); + } + modelTable[name] = { name, displayName: displayName || name, From 45b88ebb2a720c62d60e63a873004d3cd9734801 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 19 Nov 2023 18:34:39 +0800 Subject: [PATCH 65/67] feat: close #3304 use `=` instead of `:` to map model name in CUSTOM_MODELS --- app/utils/model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/utils/model.ts b/app/utils/model.ts index bf7300806..74b28a66a 100644 --- a/app/utils/model.ts +++ b/app/utils/model.ts @@ -26,7 +26,7 @@ export function collectModelTable( const available = !m.startsWith("-"); const nameConfig = m.startsWith("+") || m.startsWith("-") ? m.slice(1) : m; - const [name, displayName] = nameConfig.split(":"); + const [name, displayName] = nameConfig.split("="); // enable or disable all models if (name === "all") { From 6aade62ce2f131caeaefc18689fea502ec1a3966 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 19 Nov 2023 18:42:30 +0800 Subject: [PATCH 66/67] feat: close #3300 support multiple api keys --- README.md | 2 +- README_CN.md | 2 +- app/config/server.ts | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ef7f2e1d9..3050fcc95 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ Access password, separated by comma. ### `OPENAI_API_KEY` (required) -Your openai api key. +Your openai api key, join multiple api keys with comma. ### `BASE_URL` (optional) diff --git a/README_CN.md b/README_CN.md index 3b713255a..0ef508f61 100644 --- a/README_CN.md +++ b/README_CN.md @@ -68,7 +68,7 @@ code1,code2,code3 ### `OPENAI_API_KEY` (必填项) -OpanAI 密钥,你在 openai 账户页面申请的 api key。 +OpanAI 密钥,你在 openai 账户页面申请的 api key,使用英文逗号隔开多个 key,这样可以随机轮询这些 key。 ### `CODE` (可选) diff --git a/app/config/server.ts b/app/config/server.ts index 2f2e7d7fd..2398805a2 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -62,9 +62,17 @@ export const getServerSideConfig = () => { const isAzure = !!process.env.AZURE_URL; + const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? ""; + const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim()); + const randomIndex = Math.floor(Math.random() * apiKeys.length); + const apiKey = apiKeys[randomIndex]; + console.log( + `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`, + ); + return { baseUrl: process.env.BASE_URL, - apiKey: process.env.OPENAI_API_KEY, + apiKey, openaiOrgId: process.env.OPENAI_ORG_ID, isAzure, From f2485931d9b3680234f4816f4526759c8d4b741e Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 19 Nov 2023 19:15:11 +0800 Subject: [PATCH 67/67] feat: better animation speed --- app/client/platforms/openai.ts | 8 +++++--- app/locales/index.ts | 2 +- app/locales/pt.ts | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index dc79d2cec..8ea864692 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -127,9 +127,11 @@ export class ChatGPTApi implements LLMApi { } if (remainText.length > 0) { - responseText += remainText[0]; - remainText = remainText.slice(1); - options.onUpdate?.(responseText, remainText[0]); + const fetchCount = Math.max(1, Math.round(remainText.length / 60)); + const fetchText = remainText.slice(0, fetchCount); + responseText += fetchText; + remainText = remainText.slice(fetchCount); + options.onUpdate?.(responseText, fetchText); } requestAnimationFrame(animateResponseText); diff --git a/app/locales/index.ts b/app/locales/index.ts index 1d84de22c..cfbdff297 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -24,8 +24,8 @@ export type { LocaleType, PartialLocaleType } from "./cn"; const ALL_LANGS = { cn, en, - pt, tw, + pt, jp, ko, id, diff --git a/app/locales/pt.ts b/app/locales/pt.ts index e984dc99a..55a404970 100644 --- a/app/locales/pt.ts +++ b/app/locales/pt.ts @@ -145,7 +145,7 @@ const pt: LocaleType = { }, }, Lang: { - Name: "Idioma", + Name: "Language", All: "Todos os Idiomas", }, Avatar: "Avatar",