From 689aced342a8c6b41c1f7dcb7594e9fce387112b Mon Sep 17 00:00:00 2001 From: xiangpei <xiangpei@timesnew.cn> Date: 星期三, 16 四月 2025 10:49:02 +0800 Subject: [PATCH] 聊天对接 --- src/api/request.js | 16 ++-- vue.config.js | 2 package-lock.json | 55 +++++++++++++ package.json | 1 src/views/Index.vue | 3 src/api/knowledge.js | 9 ++ src/components/AiChat.vue | 115 +++++++++++++++++++--------- src/components/Login.vue | 2 8 files changed, 153 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index 63ad5f1..4308e26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "clipboard": "^2.0.11", "core-js": "^3.8.3", "element-ui": "^2.15.14", + "markdown-it": "^14.1.0", "vue": "^2.6.14", "vue-router": "^3.5.1", "vuex": "^3.6.2" @@ -5405,7 +5406,6 @@ "version": "4.5.0", "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -7773,6 +7773,15 @@ "dev": true, "license": "MIT" }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz", @@ -8093,6 +8102,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -8108,6 +8140,12 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true, "license": "CC0-1.0" + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" }, "node_modules/media-typer": { "version": "0.3.0", @@ -9916,6 +9954,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz", @@ -11461,6 +11508,12 @@ "node": ">= 0.6" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz", diff --git a/package.json b/package.json index 9362f0d..bdf6baa 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "clipboard": "^2.0.11", "core-js": "^3.8.3", "element-ui": "^2.15.14", + "markdown-it": "^14.1.0", "vue": "^2.6.14", "vue-router": "^3.5.1", "vuex": "^3.6.2" diff --git a/src/api/knowledge.js b/src/api/knowledge.js new file mode 100644 index 0000000..a80d658 --- /dev/null +++ b/src/api/knowledge.js @@ -0,0 +1,9 @@ +import axios from "./request"; + +// 鑾峰彇鐭ヨ瘑搴撳垪琛� +export const getKbList = () => { + return axios({ + url: "knowledge_base/list_knowledge_bases", + method: "GET" + }) +} diff --git a/src/api/request.js b/src/api/request.js index b6d9702..b6b202c 100644 --- a/src/api/request.js +++ b/src/api/request.js @@ -4,7 +4,7 @@ const instance = axios.create({ baseURL: '/api/', - timeout: 50000, + timeout: 500000, // 涓嶆惡甯ookie withCredentials: false, headers: { @@ -26,19 +26,19 @@ // 娣诲姞鍝嶅簲鎷︽埅鍣� instance.interceptors.response.use(function (response) { + console.log("姝e父鍝嶅簲缁撴灉",response) // 澶勭悊鑷畾涔夌姸鎬佺爜 - if(response.data.code === 200) { + if(response.status === 200) { return response; // 楠岃瘉鐮侀敊璇斁琛岋紝浠ヤ究鍒锋柊楠岃瘉鐮� - } else if(response.data.code === 404) { - Message.error(response.data.msg); - } else if (response.data.code === 1998) { - return response; + } else if(response.status === 404) { + Message.error(response.statusText); } else { - Message.error(response.data.msg); - return Promise.reject(response.data.msg); + Message.error(response.statusText); + return Promise.reject(response.statusText); } }, function (error) { + console.log("閿欒鍝嶅簲缁撴灉",error) // 澶勭悊http鐘舵�佺爜 if(error.response.data) { error.message = error.response.data.msg; diff --git a/src/components/AiChat.vue b/src/components/AiChat.vue index e17ef15..6a3f58e 100644 --- a/src/components/AiChat.vue +++ b/src/components/AiChat.vue @@ -1,5 +1,5 @@ <template> - <div style="position: relative;height:calc(100vh - 20px);width: 100%;display: flex;justify-content: center"> + <div style="height:calc(100vh - 20px);width: 100%;display: flex;justify-content: center"> <!-- 鑱婂ぉ娑堟伅鍒楄〃 --> <div class="chat-messages"> <div @@ -11,8 +11,11 @@ <div class="avatar"> <img :src="getAvatar(message.role)" alt="avatar" /> </div> - <div class="content"> - <div class="text" :ref="'msg' + index">{{ message.content }}</div> + <div v-if="!message.content && message.role == 'assistant'" v-loading="!message.content && message.role == 'assistant'"> + + </div> + <div v-else class="content"> + <div class="text" :ref="'msg' + index" v-html="getHtml(message.content)"></div> <div v-if="message.role === 'assistant'"> <div v-show="msgIndex === message" class="msg-op-list"> <el-button class="copy" v-show="false"/> @@ -37,7 +40,7 @@ <!-- 杈撳叆妗� --> <div class="chat-input"> - <div style="position: relative;width: 100%;box-sizing: border-box;"> + <div style="position: relative;width: 800px;box-sizing: border-box;"> <el-input v-model="inputMessage" type="textarea" @@ -74,45 +77,29 @@ <script> import ClipboardJS from 'clipboard'; -import {sendKbMsg} from "@/api/chat"; +// import {sendKbMsg} from "@/api/chat"; import {Message} from "element-ui"; +const markdownIt = require('markdown-it')(); export default { name: 'AiChat', data() { return { + messages: [], // 鐢ㄤ簬椤甸潰灞曠ず鐨勫璇濆垪琛� netSearchEnable: false, - messages: [ - { - role: 'assistant', - content: '浣犲ソ锛佹垜鏄綘鐨� AI 鍔╂墜锛屾湁浠�涔堝彲浠ュ府浣犵殑鍚楋紵', - }, - { - role: 'user', - content: '浣犲ソ锛佹垜鏄綘鐨� AI 鍔╂墜锛屾湁浠�涔堝彲浠ュ府浣犵殑鍚楋紵', - }, - ], inputMessage: '', sendMsgForm: { query: "", mode: "local_kb", - kb_name: "samples", + kb_name: "SouthWest_Neclear_Develepment_KB", top_k: 3, score_threshold: 2, history: [ - { - "content": "鎴戜滑鏉ョ帺鎴愯鎺ラ緳锛屾垜鍏堟潵锛岀敓榫欐椿铏�", - "role": "user" - }, - { - "content": "铏庡ご铏庤剳", - "role": "assistant" - } ], stream: true, - model: "qwen:7b", + model: "Qwen25-32B-Instruct", temperature: 0.7, - max_tokens: 0, + max_tokens: 64, prompt_name: "default", return_direct: false }, @@ -121,6 +108,10 @@ }; }, methods: { + // md娓叉煋涓篽tml + getHtml(md) { + return markdownIt.render(md) + }, changeNetEnable() { this.netSearchEnable = ! this.netSearchEnable }, @@ -129,11 +120,9 @@ e.preventDefault(); // 闃绘榛樿鎹㈣琛屼负 }, handleMouseEnter(msgIndex) { - console.log("榧犳爣绉诲叆", msgIndex) this.msgIndex = msgIndex }, handleMouseLeave() { - console.log("榧犳爣绉婚櫎") this.msgIndex = null }, // 閲嶆柊鐢熸垚 @@ -176,18 +165,69 @@ role: 'user', content: this.inputMessage, }); + this.messages.push({ + role: 'assistant', + content: '' + }) this.sendMsgForm.query = this.inputMessage // 娓呯┖杈撳叆妗� this.inputMessage = ''; - sendKbMsg(this.sendMsgForm).then(res => { - console.log(res, "鎷垮埌鍥炲浜嗭紒锛�") - this.messages.push({ - role: 'assistant', - content: '杩欐槸涓�鏉� AI 鍥炲銆�', - }); - }) + const response = await fetch('/api/chat/kb_chat', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(this.sendMsgForm) + }); + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + // eslint-disable-next-line no-constant-condition + while (true) { + const { done, value } = await reader.read(); + if (done) break; + console.log("缁撴潫寰幆") + const chunk = decoder.decode(value, { stream: false }); + const dataList = chunk.split('\r\n\r\n') + console.log("鑾峰彇鍒颁簡娴佸紡鍝嶅簲鏁版嵁", dataList) + if (dataList && dataList.length > 0) { + for (const data of dataList) { + if (data.startsWith("data: ")) { + const jsonStr = data.slice(6).trim(); + if (jsonStr) { + const json = JSON.parse(jsonStr); + if (json.docs && json.docs.length > 0) { + console.log("1", json) + json.docs.forEach(str => { + this.messages[this.messages.length - 1].content += str + // 寮哄埗Vue鏇存柊瑙嗗浘 + this.$forceUpdate(); + }) + } else if (json.choices && json.choices.length > 0) { + console.log("2", json) + json.choices.forEach(choice => { + this.messages[this.messages.length - 1].content += choice.delta.content + // 寮哄埗Vue鏇存柊瑙嗗浘 + this.$forceUpdate(); + }) + } + } + + } + } + } + + } + // sendKbMsg(this.sendMsgForm).then(res => { + // console.log(res, "鎷垮埌鍥炲浜嗭紒锛�") + // this.sendMsgForm.history.push({ + // role: 'assistant', + // content: JSON.parse(res.data).choices[0].message.content, + // }); + // }) }, + }, }; </script> @@ -229,9 +269,7 @@ .chat-messages { padding: 16px; margin-top: 14px; - overflow-y: auto; width: 800px; - height: 680px; } .message { @@ -280,9 +318,8 @@ } .chat-input { - padding: 16px; background-color: #fff; - width: 800px; + width: 100%; display: flex; flex-direction: row; justify-content: center; diff --git a/src/components/Login.vue b/src/components/Login.vue index fcdaea1..48f24c9 100644 --- a/src/components/Login.vue +++ b/src/components/Login.vue @@ -39,7 +39,7 @@ name: "LoginView", data() { return { - open: true, + open: false, loginForm: { username: '', pwd: '' diff --git a/src/views/Index.vue b/src/views/Index.vue index d88fddf..670d93d 100644 --- a/src/views/Index.vue +++ b/src/views/Index.vue @@ -261,6 +261,9 @@ } .right { width: 1600px; + height: 100vh; + overflow-y: scroll; + position: relative; } .logo { width: 100%; diff --git a/vue.config.js b/vue.config.js index e42232b..c78c8d6 100644 --- a/vue.config.js +++ b/vue.config.js @@ -4,7 +4,7 @@ devServer: { proxy: { "/api": { - target: 'http://i-1.gpushare.com:10695/',//浠g悊鍦板潃 鍑℃槸浣跨敤/api + target: 'http://i-1.gpushare.com:52574/',//浠g悊鍦板潃 鍑℃槸浣跨敤/api changeOrigin: true,//鍏佽璺ㄥ煙璇锋眰 secure: false, pathRewrite: { //閲嶅啓璺緞 鏇挎崲璇锋眰鍦板潃涓殑鎸囧畾璺緞 -- Gitblit v1.8.0