| | |
| | | <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 |
| | |
| | | <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"/> |
| | |
| | | |
| | | <!-- 输入框 --> |
| | | <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" |
| | |
| | | </div> |
| | | </div> |
| | | <div class="send-but-warp"> |
| | | <div class="send-but"> |
| | | <div class="send-but" @click="sendMessage"> |
| | | <svg t="1743144485359" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1261" width="36" height="36"> |
| | | <path fill="#409eff" d="M512 85.333333c234.666667 0 426.666667 192 426.666667 426.666667s-192 426.666667-426.666667 426.666667S85.333333 746.666667 85.333333 512 277.333333 85.333333 512 85.333333z m-6.4 234.666667c-4.266667 2.133333-6.4 2.133333-12.8 8.533333l-153.6 153.6c-12.8 12.8-12.8 32 0 44.8 12.8 12.8 32 12.8 44.8 0l96-96V682.666667c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V430.933333l96 96c12.8 12.8 32 12.8 44.8 0s12.8-32 0-44.8l-153.6-153.6c-6.4-6.4-8.533333-8.533333-12.8-8.533333s-8.533333-2.133333-12.8 0z" p-id="1262"></path> |
| | | </svg> |
| | |
| | | |
| | | <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 |
| | | }, |
| | |
| | | }; |
| | | }, |
| | | methods: { |
| | | // md渲染为html |
| | | getHtml(md) { |
| | | return markdownIt.render(md) |
| | | }, |
| | | changeNetEnable() { |
| | | this.netSearchEnable = ! this.netSearchEnable |
| | | }, |
| | |
| | | e.preventDefault(); // 阻止默认换行行为 |
| | | }, |
| | | handleMouseEnter(msgIndex) { |
| | | console.log("鼠标移入", msgIndex) |
| | | this.msgIndex = msgIndex |
| | | }, |
| | | handleMouseLeave() { |
| | | console.log("鼠标移除") |
| | | this.msgIndex = null |
| | | }, |
| | | // 重新生成 |
| | |
| | | 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> |
| | |
| | | .chat-messages { |
| | | padding: 16px; |
| | | margin-top: 14px; |
| | | overflow-y: auto; |
| | | width: 800px; |
| | | height: 680px; |
| | | } |
| | | |
| | | .message { |
| | |
| | | } |
| | | |
| | | .chat-input { |
| | | padding: 16px; |
| | | background-color: #fff; |
| | | width: 800px; |
| | | width: 100%; |
| | | display: flex; |
| | | flex-direction: row; |
| | | justify-content: center; |