From 6abf7642caa66239f3f3e75454811f95aaf50a26 Mon Sep 17 00:00:00 2001 From: xiangpei <xiangpei@timesnew.cn> Date: 星期三, 16 四月 2025 17:49:48 +0800 Subject: [PATCH] 聊天对接实现打字机效果 --- src/components/AiChat.vue | 141 ++++++++++++++++++++++++++-------------------- 1 files changed, 79 insertions(+), 62 deletions(-) diff --git a/src/components/AiChat.vue b/src/components/AiChat.vue index 6a3f58e..547eda8 100644 --- a/src/components/AiChat.vue +++ b/src/components/AiChat.vue @@ -95,16 +95,21 @@ top_k: 3, score_threshold: 2, history: [ + ], stream: true, model: "Qwen25-32B-Instruct", - temperature: 0.7, - max_tokens: 64, + temperature: 1.15, + max_tokens: 512, prompt_name: "default", return_direct: false }, msgIndex: null, - msgRole: null + msgRole: null, + chunkRef: null, // ai鍥炵瓟涓存椂鏁版嵁 + haveThinkStart: false, // ai鍥炵瓟鍝嶅簲鏁版嵁鏄惁鏈塼hink鏍囩 + haveThinkEnd: false, // ai鍥炵瓟鍝嶅簲鏁版嵁鏄惁鏈塼hink鏍囩 + controller: null, // ai鍥炵瓟璇锋眰鎺у埗鍣紝鍙互鏍规嵁涓氬姟涓柇璇锋眰 }; }, methods: { @@ -156,77 +161,89 @@ return roleNames[role]; }, - // 鍙戦�佹秷鎭� async sendMessage() { if (this.inputMessage.trim() === '') return; - // 娣诲姞鐢ㄦ埛娑堟伅 - this.messages.push({ - role: 'user', - content: this.inputMessage, - }); - this.messages.push({ - role: 'assistant', - content: '' - }) - this.sendMsgForm.query = this.inputMessage - // 娓呯┖杈撳叆妗� + // 鍒濆鍖栫姸鎬� + this.chunkRef = ''; + this.haveThinkStart = false; + this.haveThinkEnd = false; + this.controller = new AbortController(); + + // 娣诲姞鐢ㄦ埛娑堟伅鍜屽崰浣嶅姪鐞嗘秷鎭� + this.messages.push( + { role: 'user', content: this.inputMessage }, + { role: 'assistant', content: '' } + ); + this.sendMsgForm.query = this.inputMessage; this.inputMessage = ''; + const assistantIndex = this.messages.length - 1; + try { + const response = await fetch('/api/chat/kb_chat', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + "Accept": 'text/event-stream', + 'Cache-Control': 'no-cache, no-transform' + }, + signal: this.controller.signal, + body: JSON.stringify(this.sendMsgForm) + }); - 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(); + if (!response.ok) throw new Error(`HTTP ${response.status}`); - // 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(); - }) + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let jsonBuffer = ''; + + // eslint-disable-next-line no-constant-condition + while (true) { + const { done, value } = await reader.read(); + if (done) { + this.sendMsgForm.history.push({ role: 'user', content: this.inputMessage }) + this.sendMsgForm.history.push({ role: 'assistant', content: this.messages[assistantIndex].content }) + break; + } + + const chunk = decoder.decode(value, { stream: true }); + const lines = chunk.split('\n'); + + for (const line of lines) { + if (!line.startsWith('data:')) continue; + + const rawData = line.slice(5).trim(); + if (rawData === '[DONE]') continue; + + try { + jsonBuffer += rawData; + const data = JSON.parse(jsonBuffer); + jsonBuffer = ''; + let content = ''; + if (data.docs) { + for (let i = 0; i < data.docs.length; i++) { + content += data.docs[i] } + } else { + content = data?.choices?.[0]?.delta?.content || ''; } - + if (!content) continue; + // 瀹炴椂鏇存柊 + this.messages[assistantIndex].content += content; + this.messages[assistantIndex].content = this.messages[assistantIndex].content.replace(/<\/?think>/g, ''); + await this.$nextTick(); + } catch { + // JSON涓嶅畬鏁达紝绛夊緟涓嬫鏁版嵁 + continue; } } } - + } catch (error) { + if (error.name !== 'AbortError') { + console.error('Request failed:', error); + this.messages[assistantIndex].content = '璇锋眰寮傚父'; + } } - // sendKbMsg(this.sendMsgForm).then(res => { - // console.log(res, "鎷垮埌鍥炲浜嗭紒锛�") - // this.sendMsgForm.history.push({ - // role: 'assistant', - // content: JSON.parse(res.data).choices[0].message.content, - // }); - // }) - }, + } }, }; -- Gitblit v1.8.0