From 6abf7642caa66239f3f3e75454811f95aaf50a26 Mon Sep 17 00:00:00 2001
From: xiangpei <xiangpei@timesnew.cn>
Date: 星期三, 16 四月 2025 17:49:48 +0800
Subject: [PATCH] 聊天对接实现打字机效果
---
vue.config.js | 1
package-lock.json | 7 ++
package.json | 1
src/components/AiChat.vue | 141 ++++++++++++++++++++++++++--------------------
4 files changed, 88 insertions(+), 62 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 4308e26..7ab32c5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "ai-client",
"version": "0.1.0",
"dependencies": {
+ "@microsoft/fetch-event-source": "^2.0.1",
"axios": "^1.8.4",
"clipboard": "^2.0.11",
"core-js": "^3.8.3",
@@ -1941,6 +1942,12 @@
"dev": true,
"license": "MIT"
},
+ "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==",
+ "license": "MIT"
+ },
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmmirror.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
diff --git a/package.json b/package.json
index bdf6baa..d709915 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
+ "@microsoft/fetch-event-source": "^2.0.1",
"axios": "^1.8.4",
"clipboard": "^2.0.11",
"core-js": "^3.8.3",
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,
- // });
- // })
- },
+ }
},
};
diff --git a/vue.config.js b/vue.config.js
index c78c8d6..852debf 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -2,6 +2,7 @@
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
+ compress: false,
proxy: {
"/api": {
target: 'http://i-1.gpushare.com:52574/',//浠g悊鍦板潃 鍑℃槸浣跨敤/api
--
Gitblit v1.8.0