From 73e12b1fdd0e23a6b59a0d6c6dc397a34c166b44 Mon Sep 17 00:00:00 2001 From: Redon <790348264@qq.com> Date: Thu, 23 Mar 2023 16:23:57 +0800 Subject: [PATCH 01/44] =?UTF-8?q?fix:=20=E7=A7=BB=E5=8A=A8=E7=AB=AF?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=E4=BC=9A=E8=AF=9D=E5=85=B3=E9=97=AD=E4=BE=A7?= =?UTF-8?q?=E8=BE=B9=E6=A0=8F=20(#813)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/chat/layout/sider/index.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/chat/layout/sider/index.vue b/src/views/chat/layout/sider/index.vue index 524d3c0..2792301 100644 --- a/src/views/chat/layout/sider/index.vue +++ b/src/views/chat/layout/sider/index.vue @@ -18,6 +18,8 @@ const collapsed = computed(() => appStore.siderCollapsed) function handleAdd() { chatStore.addHistory({ title: 'New Chat', uuid: Date.now(), isEdit: false }) + if (isMobile.value) + appStore.setSiderCollapsed(true) } function handleUpdateCollapsed() { From 9081b22ce9768ff459216e07954998e00f174868 Mon Sep 17 00:00:00 2001 From: ChenZhaoYu <790348264@qq.com> Date: Thu, 23 Mar 2023 16:56:59 +0800 Subject: [PATCH 02/44] =?UTF-8?q?perf:=20=E7=A7=BB=E5=8A=A8=E7=AB=AF?= =?UTF-8?q?=E5=88=A0=E9=99=A4=20chat=20=E6=97=B6=EF=BC=8C=E4=BE=A7?= =?UTF-8?q?=E8=BE=B9=E6=A0=8F=E5=BA=94=E8=AF=A5=E6=94=B6=E8=B5=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/chat/layout/sider/List.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/chat/layout/sider/List.vue b/src/views/chat/layout/sider/List.vue index 2a282e0..0f5ddae 100644 --- a/src/views/chat/layout/sider/List.vue +++ b/src/views/chat/layout/sider/List.vue @@ -32,6 +32,8 @@ function handleEdit({ uuid }: Chat.History, isEdit: boolean, event?: MouseEvent) function handleDelete(index: number, event?: MouseEvent | TouchEvent) { event?.stopPropagation() chatStore.deleteHistory(index) + if (isMobile.value) + appStore.setSiderCollapsed(true) } function handleEnter({ uuid }: Chat.History, isEdit: boolean, event: KeyboardEvent) { From 57a1d6e3cd0683aa14fc57bfc6b36126ad65d071 Mon Sep 17 00:00:00 2001 From: Kid <44045911+kidonng@users.noreply.github.com> Date: Thu, 23 Mar 2023 20:43:47 +0800 Subject: [PATCH 03/44] fix: ESM error (#826) --- service/package.json | 2 +- service/src/chatgpt/index.ts | 4 +++- service/tsup.config.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/service/package.json b/service/package.json index 4089b9e..d457c7b 100644 --- a/service/package.json +++ b/service/package.json @@ -16,7 +16,7 @@ "scripts": { "start": "esno ./src/index.ts", "dev": "esno watch ./src/index.ts", - "prod": "esno ./build/index.js", + "prod": "node ./build/index.mjs", "build": "pnpm clean && tsup", "clean": "rimraf build", "lint": "eslint .", diff --git a/service/src/chatgpt/index.ts b/service/src/chatgpt/index.ts index 62e4b03..c702e6a 100644 --- a/service/src/chatgpt/index.ts +++ b/service/src/chatgpt/index.ts @@ -3,7 +3,7 @@ import 'isomorphic-fetch' import type { ChatGPTAPIOptions, ChatMessage, SendMessageOptions } from 'chatgpt' import { ChatGPTAPI, ChatGPTUnofficialProxyAPI } from 'chatgpt' import { SocksProxyAgent } from 'socks-proxy-agent' -import { HttpsProxyAgent } from 'https-proxy-agent' +import httpsProxyAgent from 'https-proxy-agent' import fetch from 'node-fetch' import axios from 'axios' import { sendResponse } from '../utils' @@ -11,6 +11,8 @@ import { isNotEmptyString } from '../utils/is' import type { ApiModel, ChatContext, ChatGPTUnofficialProxyAPIOptions, ModelConfig } from '../types' import type { RequestOptions } from './types' +const { HttpsProxyAgent } = httpsProxyAgent + dotenv.config() const ErrorCodeMessage: Record = { diff --git a/service/tsup.config.ts b/service/tsup.config.ts index 4f24c6a..534f06d 100644 --- a/service/tsup.config.ts +++ b/service/tsup.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ entry: ['src/index.ts'], outDir: 'build', target: 'es2020', - format: ['cjs'], + format: ['esm'], splitting: false, sourcemap: true, minify: false, From 634c879108175aa044ac171a17b7c745d078b6e3 Mon Sep 17 00:00:00 2001 From: ChenZhaoYu <790348264@qq.com> Date: Thu, 23 Mar 2023 20:51:20 +0800 Subject: [PATCH 04/44] =?UTF-8?q?perf:=20=E8=87=AA=E5=8A=A8=E7=84=A6?= =?UTF-8?q?=E7=82=B9=E6=97=B6=E7=A7=BB=E5=8A=A8=E7=AB=AF=E4=B8=8A=E7=9A=84?= =?UTF-8?q?=E4=B8=8D=E4=BE=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/chat/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/chat/index.vue b/src/views/chat/index.vue index 2984f62..c37466a 100644 --- a/src/views/chat/index.vue +++ b/src/views/chat/index.vue @@ -454,7 +454,7 @@ const footerClass = computed(() => { onMounted(() => { scrollToBottom() - if (inputRef.value) + if (inputRef.value && !isMobile.value) inputRef.value?.focus() }) From 9b0d7dbee82c71ca16212253f33d6190dc93c56c Mon Sep 17 00:00:00 2001 From: ChenZhaoYu <790348264@qq.com> Date: Thu, 23 Mar 2023 21:12:36 +0800 Subject: [PATCH 05/44] =?UTF-8?q?fix:=20=E7=A7=BB=E5=8A=A8=E7=AB=AF?= =?UTF-8?q?=E7=84=A6=E7=82=B9=E4=B8=8D=E8=A7=A6=E5=8F=91=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/chat/components/Message/index.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/views/chat/components/Message/index.vue b/src/views/chat/components/Message/index.vue index 239ec25..541dde1 100644 --- a/src/views/chat/components/Message/index.vue +++ b/src/views/chat/components/Message/index.vue @@ -7,6 +7,7 @@ import { SvgIcon } from '@/components/common' import { copyText } from '@/utils/format' import { useIconRender } from '@/hooks/useIconRender' import { t } from '@/locales' +import { useBasicLayout } from '@/hooks/useBasicLayout' interface Props { dateTime?: string @@ -25,6 +26,8 @@ const props = defineProps() const emit = defineEmits() +const { isMobile } = useBasicLayout() + const { iconRender } = useIconRender() const textRef = ref() @@ -113,7 +116,12 @@ function handleRegenerate() { > - + From 902321026b1112861c19d1b5437b3ba440fb7b9a Mon Sep 17 00:00:00 2001 From: ChenZhaoYu <790348264@qq.com> Date: Fri, 24 Mar 2023 08:14:51 +0800 Subject: [PATCH 06/44] =?UTF-8?q?perf:=20=E6=9C=AC=E5=9C=B0=E5=9C=B0?= =?UTF-8?q?=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 54f675a..9a7a52f 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ # Glob API URL VITE_GLOB_API_URL=/api -VITE_APP_API_BASE_URL=http://localhost:3002/ +VITE_APP_API_BASE_URL=http://127.0.0.1:3002/ # Whether long replies are supported, which may result in higher API fees VITE_GLOB_OPEN_LONG_REPLY=false From fb8ad3790dd994191fcf881051d0d78c94be467a Mon Sep 17 00:00:00 2001 From: gitgitgogogo Date: Sat, 25 Mar 2023 08:30:15 +0800 Subject: [PATCH 07/44] =?UTF-8?q?fix:=20=E5=8F=8D=E5=90=91=E4=BB=A3?= =?UTF-8?q?=E7=90=86=E9=99=90=E6=B5=81=E5=A4=B1=E6=95=88=20(#863)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://docs.colyseus.io/zh_hk/colyseus/how-to/rate-limit/ 使用nginx限流会只识别为服务器ip,需启用trust proxy --- service/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/service/src/index.ts b/service/src/index.ts index b011039..28041d1 100644 --- a/service/src/index.ts +++ b/service/src/index.ts @@ -82,5 +82,6 @@ router.post('/verify', async (req, res) => { app.use('', router) app.use('/api', router) +app.set('trust proxy', 1) app.listen(3002, () => globalThis.console.log('Server is running on port 3002')) From 639152f9872f92b71e13ebdaa6fe03eceddc2e11 Mon Sep 17 00:00:00 2001 From: yi-ge Date: Sat, 25 Mar 2023 17:01:12 +0800 Subject: [PATCH 08/44] docs: update README.md, add long reply feature --- README.en.md | 4 ++++ README.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.en.md b/README.en.md index 555ec7d..c1c1511 100644 --- a/README.en.md +++ b/README.en.md @@ -304,6 +304,10 @@ Q: Why doesn't the frontend have a typewriter effect? A: One possible reason is that after Nginx reverse proxying, buffering is turned on, and Nginx will try to buffer a certain amount of data from the backend before sending it to the browser. Please try adding `proxy_buffering off;` after the reverse proxy parameter and then reloading Nginx. Other web server configurations are similar. +Q: The content returned is incomplete? + +A: There is a length limit for the content returned by the API each time. You can modify the `VITE_GLOB_OPEN_LONG_REPLY` field in the `.env` file under the root directory, set it to `true`, and rebuild the front-end to enable the long reply feature, which can return the full content. It should be noted that using this feature may bring more API usage fees. + ## Contributing Please read the [Contributing Guidelines](./CONTRIBUTING.en.md) before contributing. diff --git a/README.md b/README.md index f0cdc6b..c507d44 100644 --- a/README.md +++ b/README.md @@ -310,6 +310,10 @@ Q: 前端没有打字机效果? A: 一种可能原因是经过 Nginx 反向代理,开启了 buffer,则 Nginx 会尝试从后端缓冲一定大小的数据再发送给浏览器。请尝试在反代参数后添加 `proxy_buffering off;`,然后重载 Nginx。其他 web server 配置同理。 +Q: 内容返回不完整? + +A: 接口每次返回的内容是有长度限制的,可以修改根目录下`.env`文件中的`VITE_GLOB_OPEN_LONG_REPLY`字段,将其设置为`true`,重新编译前端即可开启长回复功能,即可返回全部的内容。需要注意的是,使用此功能可能带来较多的API使用费用。 + ## 参与贡献 贡献之前请先阅读 [贡献指南](./CONTRIBUTING.md) From a3944f86b7b80b0ce63b9818ab9139550f67cc48 Mon Sep 17 00:00:00 2001 From: zaimoe <31397840+zaiMoe@users.noreply.github.com> Date: Sat, 25 Mar 2023 17:23:57 +0800 Subject: [PATCH 09/44] fix: missing VITE_GLOB_API_URL when docker build, fixed #690 #717 # (#877) --- .dockerignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.dockerignore b/.dockerignore index def59e5..b3cea57 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,3 +4,4 @@ node_modules Dockerfile .* */.* +!.env From c3f431118b186bed950a4d673a5e85be2c976e23 Mon Sep 17 00:00:00 2001 From: ChenZhaoYu <790348264@qq.com> Date: Sun, 26 Mar 2023 10:08:01 +0800 Subject: [PATCH 10/44] chore: update docs --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c507d44..1062ea0 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ 1. 你应该首先使用 `API` 方式 2. 使用 `API` 时,如果网络不通,那是国内被墙了,你需要自建代理,绝对不要使用别人的公开代理,那是危险的。 3. 使用 `accessToken` 方式时反向代理将向第三方暴露您的访问令牌,这样做应该不会产生任何不良影响,但在使用这种方法之前请考虑风险。 -4. 使用 `accessToken` 时,不管你是国内还是国外的机器,都会使用代理。默认代理为 [acheong08](https://github.com/acheong08) 大佬的 `https://bypass.duti.tech/api/conversation`,这不是后门也不是监听,除非你有能力自己翻过 `CF` 验证,用前请知悉。[社区代理](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy)(注意:只有这两个是推荐,其他第三方来源,请自行甄别) +4. 使用 `accessToken` 时,不管你是国内还是国外的机器,都会使用代理。默认代理为 [acheong08](https://github.com/acheong08) 大佬的 `https://bypass.churchless.tech/api/conversation`,这不是后门也不是监听,除非你有能力自己翻过 `CF` 验证,用前请知悉。[社区代理](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy)(注意:只有这两个是推荐,其他第三方来源,请自行甄别) 5. 把项目发布到公共网络时,你应该设置 `AUTH_SECRET_KEY` 变量添加你的密码访问权限,你也应该修改 `index.html` 中的 `title`,防止被关键词搜索到。 切换方式: @@ -165,7 +165,7 @@ pnpm dev `ACCESS_TOKEN` 可用: - `OPENAI_ACCESS_TOKEN` 和 `OPENAI_API_KEY` 二选一,同时存在时,`OPENAI_API_KEY` 优先 -- `API_REVERSE_PROXY` 设置反向代理,可选,默认:`https://bypass.duti.tech/api/conversation`,[社区](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy)(注意:只有这两个是推荐,其他第三方来源,请自行甄别) +- `API_REVERSE_PROXY` 设置反向代理,可选,默认:`https://bypass.churchless.tech/api/conversation`,[社区](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy)(注意:只有这两个是推荐,其他第三方来源,请自行甄别) 通用: @@ -310,10 +310,6 @@ Q: 前端没有打字机效果? A: 一种可能原因是经过 Nginx 反向代理,开启了 buffer,则 Nginx 会尝试从后端缓冲一定大小的数据再发送给浏览器。请尝试在反代参数后添加 `proxy_buffering off;`,然后重载 Nginx。其他 web server 配置同理。 -Q: 内容返回不完整? - -A: 接口每次返回的内容是有长度限制的,可以修改根目录下`.env`文件中的`VITE_GLOB_OPEN_LONG_REPLY`字段,将其设置为`true`,重新编译前端即可开启长回复功能,即可返回全部的内容。需要注意的是,使用此功能可能带来较多的API使用费用。 - ## 参与贡献 贡献之前请先阅读 [贡献指南](./CONTRIBUTING.md) From 799af867393bcf71e389ba24921bfb00165c8000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=9D=89=28Shan=20Wu=29?= Date: Mon, 27 Mar 2023 10:49:42 +0300 Subject: [PATCH 11/44] perf: add localization for sider (#911) --- src/locales/en-US.ts | 2 ++ src/locales/zh-CN.ts | 2 ++ src/locales/zh-TW.ts | 2 ++ src/views/chat/layout/sider/index.vue | 4 ++-- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 357b5ec..2d6f7bc 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -28,6 +28,7 @@ export default { unauthorizedTips: 'Unauthorized, please verify first.', }, chat: { + newChatButton: 'New Chat', placeholder: 'Ask me anything...(Shift + Enter = line break)', placeholderMobile: 'Ask me anything...', copy: 'Copy', @@ -70,6 +71,7 @@ export default { balance: 'API Balance', }, store: { + siderButton: 'Prompt Store', local: 'Local', online: 'Online', title: 'Title', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index d2ebd00..7eac3d6 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -28,6 +28,7 @@ export default { unauthorizedTips: '未经授权,请先进行验证。', }, chat: { + newChatButton: '新建聊天', placeholder: '来说点什么吧...(Shift + Enter = 换行)', placeholderMobile: '来说点什么...', copy: '复制', @@ -70,6 +71,7 @@ export default { balance: 'API余额', }, store: { + siderButton: '提示词商店', local: '本地', online: '在线', title: '标题', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 68f4b27..4ae0bff 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -28,6 +28,7 @@ export default { unauthorizedTips: '未經授權,請先進行驗證。', }, chat: { + newChatButton: '新建對話', placeholder: '來說點什麼...(Shift + Enter = 換行)', placeholderMobile: '來說點什麼...', copy: '複製', @@ -70,6 +71,7 @@ export default { balance: 'API余額', }, store: { + siderButton: '提示詞商店', local: '本機', online: '線上', title: '標題', diff --git a/src/views/chat/layout/sider/index.vue b/src/views/chat/layout/sider/index.vue index 2792301..5ea203a 100644 --- a/src/views/chat/layout/sider/index.vue +++ b/src/views/chat/layout/sider/index.vue @@ -73,7 +73,7 @@ watch(
- New chat + {{ $t('chat.newChatButton') }}
@@ -81,7 +81,7 @@ watch(
- Prompt Store + {{ $t('store.siderButton') }}
From 32ad20416c1888f53544cb60465268f16295692b Mon Sep 17 00:00:00 2001 From: weifeng <76556787+weifeng12x@users.noreply.github.com> Date: Mon, 27 Mar 2023 15:58:54 +0800 Subject: [PATCH 12/44] chore: Update README.md (#880) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update README.md docker-compose 文件中注释掉未使用的配置节点,因为 留xxx 会导代码读取节点内容值为xxx,而不是期望的默认值,导致代码运行异常 增加 对模型可选参数的备注 * style: eslint fix --------- Co-authored-by: ChenZhaoYu <790348264@qq.com> --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1062ea0..d07ced3 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,8 @@ services: OPENAI_ACCESS_TOKEN: xxx # API接口地址,可选,设置 OPENAI_API_KEY 时可用 OPENAI_API_BASE_URL: xxx - # API模型,可选,设置 OPENAI_API_KEY 时可用 + # API模型,可选,设置 OPENAI_API_KEY 时可用,https://platform.openai.com/docs/models + # gpt-4, gpt-4-0314, gpt-4-32k, gpt-4-32k-0314, gpt-3.5-turbo, gpt-3.5-turbo-0301, text-davinci-003, text-davinci-002, code-davinci-002 OPENAI_API_MODEL: xxx # 反向代理,可选 API_REVERSE_PROXY: xxx From e2eeee455a40b2de350f9e2342302a4ae1863e83 Mon Sep 17 00:00:00 2001 From: KingTwinkle <75203489+KingTwinkle@users.noreply.github.com> Date: Tue, 28 Mar 2023 09:34:15 +0800 Subject: [PATCH 13/44] fix: local import error and NModal not as expected (#938) --- src/components/common/PromptStore/index.vue | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/common/PromptStore/index.vue b/src/components/common/PromptStore/index.vue index cbee4b0..93bbe5a 100644 --- a/src/components/common/PromptStore/index.vue +++ b/src/components/common/PromptStore/index.vue @@ -147,7 +147,7 @@ const clearPromptTemplate = () => { message.success(t('common.clearSuccess')) } -const importPromptTemplate = () => { +const importPromptTemplate = (from = 'online') => { try { const jsonData = JSON.parse(tempPromptValue.value) let key = '' @@ -168,7 +168,7 @@ const importPromptTemplate = () => { } for (const i of jsonData) { - if (!('key' in i) || !('value' in i)) + if (!(key in i) || !(value in i)) throw new Error(t('store.importError')) let safe = true for (const j of promptList.value) { @@ -191,6 +191,8 @@ const importPromptTemplate = () => { catch { message.error('JSON 格式错误,请检查 JSON 格式') } + if (from === 'local') + showModal.value = !showModal.value } // 模板导出 @@ -469,7 +471,7 @@ const dataSource = computed(() => { block type="primary" :disabled="inputStatus" - @click="() => { importPromptTemplate() }" + @click="() => { importPromptTemplate('local') }" > {{ t('common.import') }} From b579d24d198c674250ae0fbda0207746899ef5a9 Mon Sep 17 00:00:00 2001 From: assassinliujie <68693675+assassinliujie@users.noreply.github.com> Date: Tue, 28 Mar 2023 09:40:20 +0800 Subject: [PATCH 14/44] pref: message output optimization (#935) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update index.ts 修改后端,让保留打字机效果的同时优化前后端之间传输的内容,节省流量和性能 * Update index.vue 修改前端,和之前修改的后端匹配,保留打字机效果同时优化性能和流量传输 * chore: lint fix --------- Co-authored-by: ChenZhaoYu <790348264@qq.com> --- service/src/index.ts | 12 ++- src/views/chat/index.vue | 174 ++++++++++++++++++++------------------- 2 files changed, 98 insertions(+), 88 deletions(-) diff --git a/service/src/index.ts b/service/src/index.ts index 28041d1..f2e0e68 100644 --- a/service/src/index.ts +++ b/service/src/index.ts @@ -29,8 +29,16 @@ router.post('/chat-process', [auth, limiter], async (req, res) => { message: prompt, lastContext: options, process: (chat: ChatMessage) => { - res.write(firstChunk ? JSON.stringify(chat) : `\n${JSON.stringify(chat)}`) - firstChunk = false + if (firstChunk) { + res.write(`${JSON.stringify(chat)}t1h1i4s5i1s4a1s9i1l9l8y1s0plit`) + firstChunk = false + } + else { + let tmp = chat.delta + if (!(chat.delta)) + tmp = '' + res.write(tmp) + } }, systemMessage, }) diff --git a/src/views/chat/index.vue b/src/views/chat/index.vue index c37466a..c511921 100644 --- a/src/views/chat/index.vue +++ b/src/views/chat/index.vue @@ -107,7 +107,9 @@ async function onConversation() { scrollToBottom() try { - let lastText = '' + const magicSplit = 't1h1i4s5i1s4a1s9i1l9l8y1s0plit' + let renderText = '' + let firstTime = true const fetchChatAPIOnce = async () => { await fetchChatAPIProcess({ prompt: message, @@ -117,42 +119,49 @@ async function onConversation() { const xhr = event.target const { responseText } = xhr // Always process the final line - const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2) - let chunk = responseText - if (lastIndex !== -1) - chunk = responseText.substring(lastIndex) - try { - const data = JSON.parse(chunk) - updateChat( - +uuid, - dataSources.value.length - 1, - { - dateTime: new Date().toLocaleString(), - text: lastText + data.text ?? '', - inversion: false, - error: false, - loading: false, - conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id }, - requestOptions: { prompt: message, options: { ...options } }, - }, - ) - if (openLongReply && data.detail.choices[0].finish_reason === 'length') { - options.parentMessageId = data.id - lastText = data.text - message = '' - return fetchChatAPIOnce() + const splitIndexBegin = responseText.search(magicSplit) + if (splitIndexBegin !== -1) { + const splitIndexEnd = splitIndexBegin + magicSplit.length + + const firstChunk = responseText.substring(0, splitIndexBegin) + const deltaText = responseText.substring(splitIndexEnd) + try { + const data = JSON.parse(firstChunk) + if (firstTime) { + firstTime = false + renderText = data.text ?? '' + } + else { + renderText = deltaText ?? '' + } + updateChat( + +uuid, + dataSources.value.length - 1, + { + dateTime: new Date().toLocaleString(), + text: renderText, + inversion: false, + error: false, + loading: false, + conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id }, + requestOptions: { prompt: message, ...options }, + }, + ) + + if (openLongReply && data.detail.choices[0].finish_reason === 'length') { + options.parentMessageId = data.id + message = '' + return fetchChatAPIOnce() + } + } + catch (error) { + // } - - scrollToBottomIfAtBottom() - } - catch (error) { - // } }, }) } - await fetchChatAPIOnce() } catch (error: any) { @@ -237,7 +246,9 @@ async function onRegenerate(index: number) { ) try { - let lastText = '' + const magicSplit = 't1h1i4s5i1s4a1s9i1l9l8y1s0plit' + let renderText = '' + let firstTime = true const fetchChatAPIOnce = async () => { await fetchChatAPIProcess({ prompt: message, @@ -247,35 +258,45 @@ async function onRegenerate(index: number) { const xhr = event.target const { responseText } = xhr // Always process the final line - const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2) - let chunk = responseText - if (lastIndex !== -1) - chunk = responseText.substring(lastIndex) - try { - const data = JSON.parse(chunk) - updateChat( - +uuid, - index, - { - dateTime: new Date().toLocaleString(), - text: lastText + data.text ?? '', - inversion: false, - error: false, - loading: false, - conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id }, - requestOptions: { prompt: message, ...options }, - }, - ) - if (openLongReply && data.detail.choices[0].finish_reason === 'length') { - options.parentMessageId = data.id - lastText = data.text - message = '' - return fetchChatAPIOnce() + const splitIndexBegin = responseText.search(magicSplit) + if (splitIndexBegin !== -1) { + const splitIndexEnd = splitIndexBegin + magicSplit.length + + const firstChunk = responseText.substring(0, splitIndexBegin) + const deltaText = responseText.substring(splitIndexEnd) + try { + const data = JSON.parse(firstChunk) + if (firstTime) { + firstTime = false + renderText = data.text ?? '' + } + else { + renderText = deltaText ?? '' + } + updateChat( + +uuid, + index, + { + dateTime: new Date().toLocaleString(), + text: renderText, + inversion: false, + error: false, + loading: false, + conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id }, + requestOptions: { prompt: message, ...options }, + }, + ) + + if (openLongReply && data.detail.choices[0].finish_reason === 'length') { + options.parentMessageId = data.id + message = '' + return fetchChatAPIOnce() + } + } + catch (error) { + // } - } - catch (error) { - // } }, }) @@ -467,20 +488,13 @@ onUnmounted(() => {