diff --git a/composables/useFetchRequest.js b/composables/useFetchRequest.js index 0531269e493201b404e16d952b782ebaeddce17e..46d1fd9cc0a7b6bfba1975d1e865afea9a2e97c0 100644 --- a/composables/useFetchRequest.js +++ b/composables/useFetchRequest.js @@ -1,4 +1,4 @@ -const BASE_URL = 'https://gpu-pod656e861afe3d944d6b3ce77e-7862.node.inscode.run'; +const BASE_URL = 'https://gpu-pod656e861afe3d944d6b3ce77e-7862.node.inscode.run' const useFetchRequest = (baseUrl) => { baseUrl = baseUrl || BASE_URL; const config = { diff --git a/package-lock.json b/package-lock.json index 7bb7b4a907f1e0652cc71a77878b570ac8a72596..881517fa22dbde8f6feb98e8e578ae531d57abc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "hasInstallScript": true, "dependencies": { "@iconify-json/simple-icons": "^1.1.101", + "@microsoft/fetch-event-source": "^2.0.1", "@nuxt/ui": "^2.16.0", "@vite-pwa/nuxt": "^0.7.0", "highlight.js": "^11.9.0", @@ -2590,6 +2591,11 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@microsoft/fetch-event-source": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz", + "integrity": "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==" + }, "node_modules/@mswjs/interceptors": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.27.2.tgz", @@ -3710,6 +3716,8 @@ }, "node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": { "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/napi-wasm/-/napi-wasm-1.1.0.tgz", + "integrity": "sha512-lHwIAJbmLSjF9VDRm9GoVOy9AGp3aIvkjv+Kvz9h16QR3uSVYH78PNQUnT2U4X53mhlnV2M7wrhibQ3GHicDmg==", "inBundle": true, "license": "MIT" }, @@ -16956,6 +16964,11 @@ "tar": "^6.1.11" } }, + "@microsoft/fetch-event-source": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz", + "integrity": "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==" + }, "@mswjs/interceptors": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.27.2.tgz", @@ -17729,6 +17742,8 @@ "dependencies": { "napi-wasm": { "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/napi-wasm/-/napi-wasm-1.1.0.tgz", + "integrity": "sha512-lHwIAJbmLSjF9VDRm9GoVOy9AGp3aIvkjv+Kvz9h16QR3uSVYH78PNQUnT2U4X53mhlnV2M7wrhibQ3GHicDmg==", "bundled": true } } diff --git a/package.json b/package.json index 3e297e33d887ce7fe61e4732fc57d3a0d99d5a40..e3ba9402ab92cd9834bfb4961af23ada2f73cf21 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@iconify-json/simple-icons": "^1.1.101", + "@microsoft/fetch-event-source": "^2.0.1", "@nuxt/ui": "^2.16.0", "@vite-pwa/nuxt": "^0.7.0", "highlight.js": "^11.9.0", diff --git a/pages/search/[id].vue b/pages/search/[id].vue index 8d10996647aa201403e5db27df77d453bc14c12d..6dfdfb7872438b97f56bbe310bc95d275712643a 100644 --- a/pages/search/[id].vue +++ b/pages/search/[id].vue @@ -108,6 +108,7 @@ square trailing-icon="i-heroicons-stop-20-solid" color="gray" + @click="handleStopGenerate" :ui="{ rounded: 'rounded-full' }" /> @@ -172,63 +173,6 @@ const stepColor = (step) => { const data = ref([]) -data.value = [ - { - step: 4, - question: 'Select组件怎样远程搜索', - article: 'ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:', - source: [ - { - repo: 'ViewDesign/ViewUIPlus', - label: 'view-design/ViewUIPlus/tree/master/src/select/index.vue', - link: 'http://github.com/view-design/ViewUIPlus/tree/master/src/select/index.vue' - }, - { - repo: 'ViewDesign/ViewUIPlus', - label: 'view-design/ViewUIPlus/tree/master/src/select/index.vue', - link: 'http://github.com/view-design/ViewUIPlus/tree/master/src/select/index.vue' - }, - { - repo: 'ViewDesign/ViewUIPlus', - label: 'view-design/ViewUIPlus/tree/master/src/select/index.vue', - link: 'http://github.com/view-design/ViewUIPlus/tree/master/src/select/index.vue' - }, - { - repo: 'ViewDesign/ViewUIPlus', - label: 'view-design/ViewUIPlus/tree/master/src/select/index.vue', - link: 'http://github.com/view-design/ViewUIPlus/tree/master/src/select/index.vue' - } - ] - }, - { - step: 1, - question: 'Select组件怎样远程搜索', - article: 'ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:ViewUIPlus 的 Table 组件是一个功能强大的组件,支持多种数据渲染和查看方式。以下是使用 ViewUIPlus Table 组件的一些基本步骤和特性:\n引入Table组件:首先,你需要从 ViewUIPlus 中引入 Table 组件。这可以通过在你的 Vue 3 项目中添加相应的导入语句来完成。例如:', - source: [ - { - repo: 'ViewDesign/ViewUIPlus', - label: 'view-design/ViewUIPlus/tree/master/src/select/index.vue', - link: 'http://github.com/view-design/ViewUIPlus/tree/master/src/select/index.vue' - }, - { - repo: 'ViewDesign/ViewUIPlus', - label: 'view-design/ViewUIPlus/tree/master/src/select/index.vue', - link: 'http://github.com/view-design/ViewUIPlus/tree/master/src/select/index.vue' - }, - { - repo: 'ViewDesign/ViewUIPlus', - label: 'view-design/ViewUIPlus/tree/master/src/select/index.vue', - link: 'http://github.com/view-design/ViewUIPlus/tree/master/src/select/index.vue' - }, - { - repo: 'ViewDesign/ViewUIPlus', - label: 'view-design/ViewUIPlus/tree/master/src/select/index.vue', - link: 'http://github.com/view-design/ViewUIPlus/tree/master/src/select/index.vue' - } - ] - } -] - const recommendQuestions = [ { title: 'ViewUIPlus Select 组件如何按需加载' @@ -247,11 +191,9 @@ const handleContinueAsk = (question) => { asking.value = true; } // 处理generate -let generateFetchObj = null; // 用户取消操作方法 -const marked = 'data:'; +let aiChatController = null; // 用户取消操作方法 +const markedEnd = '[DONE]'; const handleFormFetchData = (fetchData) => { - if (fetchData === 'data:[DONE]') return; - fetchData = fetchData.slice(marked.length , fetchData.length); let message = {}; try { message = JSON.parse(fetchData); @@ -259,48 +201,53 @@ const handleFormFetchData = (fetchData) => { console.log(error) } if (Object.keys(message).length === 0) return; - console.log(`message:`, message) - if (message.data.meta.type === 'answer') { - // step: 4, - // question: 'Select组件怎样远程搜索', - // article: + if (message.meta.type === 'answer') { const index = data.value.length === 0 ? 0 : data.value.length - 1; - data.value[index] = { - step: 4, + data.value[index] = data.value[index] || {}; + Object.assign(data.value[index], { question: state.query, - article: message.data.choices[0].message.content - } + article: message.choices[0].message.content + }) + } else if (message.meta.type === 'log') { + console.log('log', JSON.stringify(message)) + } else if (message.meta.type === 'rephrase_question') { + Object.assign(data.value[index], { + description: message.choices[0].message.content + }) } } -const getHistoryMessage = () => { - const messages = [] - data.value.forEach(item => { - messages.push(...[ - { role: 'user', content: item.question }, - { role: 'assistant', content: item.article } - ]) - }) - return messages; +const handleMessage = (event) => { + if (event.data === markedEnd) { + asking.value = false; + return; + } + handleFormFetchData(event.data) +} +const handleStopGenerate = () => { + asking.value && aiChatController && aiChatController.abort(); + asking.value = false; +} +const handleError = (event) => { + console.log(`error`, event) + handleStopGenerate(); } const generateFetchData = () => { const { repo, id } = state; - const messages = getHistoryMessage(); + const messages = []; messages.push({ role: 'user', content: state.query }); - generateFetchObj = fetchRequest('/v1/chat/completions', { - method: 'post', - headers: { - 'Accept': '*/*' - }, - body: { + aiChatController = new AbortController(); + asking.value = true; + fetchEventSource('/v1/chat/completions', { + params: { repo_path: repo, c_id: id, stream: true, - messages - } - }).then((response) => { - handleFormFetchData(response); - }).finally(() => { - asking.value = false; + messages, + rephrase_question: true + }, + onmessage: handleMessage, + onerror: handleError, + controller: aiChatController }) } onMounted(() => { diff --git a/utils/fetchEventSource.js b/utils/fetchEventSource.js new file mode 100644 index 0000000000000000000000000000000000000000..254be649e9f6328e66e3c2d9ad420dc66d196cd9 --- /dev/null +++ b/utils/fetchEventSource.js @@ -0,0 +1,15 @@ +import { fetchEventSource } from '@microsoft/fetch-event-source' +const BASE_URL = 'https://gpu-pod656e861afe3d944d6b3ce77e-7862.node.inscode.run' +export default (url, { onmessage, onerror, controller, params }) => { + fetchEventSource(`${BASE_URL}${url}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': '*/*' + }, + body: JSON.stringify(params), + onmessage, + onerror, + signal: controller && controller.signal + }); +} \ No newline at end of file