基本功能已完成

This commit is contained in:
2023-05-15 01:24:18 +08:00
parent 65b4d57675
commit b99f837c72
7 changed files with 75 additions and 57 deletions

View File

@@ -10,7 +10,6 @@
}, },
"dependencies": { "dependencies": {
"@kangc/v-md-editor": "^2.3.15", "@kangc/v-md-editor": "^2.3.15",
"axios": "^1.4.0",
"element-plus": "^2.3.4", "element-plus": "^2.3.4",
"sass": "^1.62.1", "sass": "^1.62.1",
"sass-loader": "^13.2.2", "sass-loader": "^13.2.2",

View File

@@ -19,7 +19,8 @@ export default {
return { return {
showSettings: false, showSettings: false,
setting: { setting: {
model: new ChatGPT() model: new ChatGPT(),
stream: true
}, },
} }
}, },

View File

@@ -17,7 +17,7 @@
<thinking-bar v-show="loading"/> <thinking-bar v-show="loading"/>
<chat-content class="fill" :list="chatHistory"/> <chat-content class="fill" :list="chatHistory"/>
<el-row class="mar-t8" align="middle"> <el-row class="mar-t8" align="middle">
<chat-input class="fill mar-r8" v-model="inputText"/> <chat-input class="fill mar-r8" v-model="inputText" @enter="handleSend"/>
<el-button type="primary" @click="handleSend">发送</el-button> <el-button type="primary" @click="handleSend">发送</el-button>
</el-row> </el-row>
</section> </section>
@@ -46,7 +46,7 @@ export default {
methods: { methods: {
handleSend() { handleSend() {
if (!!this.inputText) { if (!!this.inputText) {
const ai = this.settings.model const ai = this.config.model
const myMsg = { const myMsg = {
avatar: USER_AVATAR, avatar: USER_AVATAR,
name: "我", name: "我",
@@ -54,6 +54,7 @@ export default {
msg: this.inputText, msg: this.inputText,
chatType: 0, //信息类型0文字1图片 chatType: 0, //信息类型0文字1图片
uid: "me", //uid uid: "me", //uid
role: "user"
} }
this.chatHistory.push(myMsg) this.chatHistory.push(myMsg)
this.loading = true this.loading = true
@@ -61,63 +62,58 @@ export default {
avatar: ai.avatar, avatar: ai.avatar,
name: ai.name, name: ai.name,
time: dayjs().format("YYYY-MM-DD HH:mm:ss"), time: dayjs().format("YYYY-MM-DD HH:mm:ss"),
msg: '', msg: "",
chatType: 0, //信息类型0文字1图片 chatType: 0, //信息类型0文字1图片
uid: "ai", //uid uid: "ai", //uid
role: "assistant"
} }
this.chatHistory.push(aiMsg) this.chatHistory.push(aiMsg)
ai.chat(this.chatHistory).finally(() => this.loading = false) if (this.config.stream) {
ai.chatStream(this.chatHistory).then(reader => this.streamOutput(reader, this.chatHistory.at(-1))).finally(() => this.loading = false)
} else {
ai.chat(this.chatHistory).then(reply => {
const decodeArr = reply.split("")
decodeArr.forEach(e => this.chatHistory.at(-1).msg += e)
}).finally(() => this.loading = false)
}
} else { } else {
this.$message.error("请不要发送空消息!") this.$message.error("请不要发送空消息!")
} }
}, },
readStream(reader, _this, currentResLocation, type) { streamOutput(reader, chat) {
return reader.read().then(({done, value}) => { return reader.read().then(({done, value}) => {
if (done) { if (done) {
return; return;
} }
if (!_this.chatList[currentResLocation].reminder) { if (!chat.reminder) {
_this.chatList[currentResLocation].reminder = ""; chat.reminder = ""
} }
let decoded = new TextDecoder().decode(value); let decode = new TextDecoder().decode(value)
decoded = _this.chatList[currentResLocation].reminder + decoded; decode = chat.reminder + decode
let decodedArray = decoded.split("data: "); let decodedArray = decode.split("data: ");
let longstr = ""; let longstr = "";
decodedArray.forEach(decoded => { decodedArray.forEach(decoded => {
try {
decoded = decoded.trim(); decoded = decoded.trim();
if (longstr == "") { try {
JSON.parse(decoded); if (longstr != "") {
} else {
decoded = longstr + decoded; decoded = longstr + decoded;
longstr = ""; longstr = "";
JSON.parse(decoded);
} }
} catch (e) { } catch (e) {
longstr = decoded; longstr = decoded;
decoded = ""; decoded = "";
} }
if (decoded !== "") { if (!!decoded && decoded !== "[DONE]") {
if (decoded.trim() === "[DONE]") {
return;
} else {
const choices = JSON.parse(decoded).choices const choices = JSON.parse(decoded).choices
if (choices && choices.length > 0) { if (choices?.length > 0) {
if (type === "chat") { const response = choices[0].delta.content || "";
const response = choices[0].delta.content ? choices[0].delta.content : ""; chat.msg += response
_this.chatList[currentResLocation].msg = _this.chatList[currentResLocation].msg + response
_this.scrollBottom();
} else {
const response = choices[0].text;
_this.chatList[currentResLocation].msg = _this.chatList[currentResLocation].msg + response
}
}
} }
} }
}) })
return this.readStream(reader, _this, currentResLocation, type); return this.streamOutput(reader, chat)
}); })
}, }
} }
} }
</script> </script>

View File

@@ -188,5 +188,9 @@ export default {
} }
} }
} }
:deep(.vuepress-markdown-body) {
padding: 2px 0 !important;
}
} }
</style> </style>

View File

@@ -1,7 +1,8 @@
<template> <template>
<section class="chatInput"> <section class="chatInput">
<!--todo 第二阶段接入音频--> <!--todo 第二阶段接入音频-->
<el-input type="textarea" placeholder="请输入你想问的内容" class="inputs" maxlength="2048" rows="2" v-model="text" @change="v=>$emit('update:modelValue',v)"/> <el-input type="textarea" placeholder="请输入你想问的内容" class="inputs" maxlength="2048" rows="2" v-model="text"
@change="v=>$emit('update:modelValue',v)" @keydown.enter.native="handleShortKey"/>
</section> </section>
</template> </template>
@@ -17,6 +18,15 @@ export default {
text: "" text: ""
} }
}, },
methods: {
handleShortKey(e) {
if (e.ctrlKey && e.keyCode == 13) {
this.$emit('update:modelValue', this.text)
this.text = ""
this.$emit('enter')
}
}
}
} }
</script> </script>

View File

@@ -1,11 +1,5 @@
import axios from "axios"; const ins = {
post: (url, body, config) => fetch(url, {...config, method: "POST", body}),
const ins = axios.create({ get: (url, config) => fetch(url, {...config, method: "GET"}),
headers: { }
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST',
'Access-Control-Allow-Headers': 'Content-Type, Access-Control-Allow-Headers, Authorization'
},
responseType: 'json'
})
export default ins export default ins

View File

@@ -1,5 +1,5 @@
import axios from "./axios"; import axios from "./axios";
import {AI_AVATAR} from "./env"; import {AI_AVATAR, OPEN_AI_KEY} from "./env";
class BaseModel { class BaseModel {
constructor(props) { constructor(props) {
@@ -14,16 +14,33 @@ export class ChatGPT extends BaseModel {
constructor() { constructor() {
super({ super({
avatar: AI_AVATAR, avatar: AI_AVATAR, name: 'ChatGPT', id: "gpt-3.5-turbo", desc: "ChatGPT-3.5所基于的模型",
name: 'ChatGPT',
id: "gpt-3.5-turbo",
desc: "ChatGPT-3.5所基于的模型"
}); });
this.apiKey = OPEN_AI_KEY
} }
async chat(history, callback) { setApiKey(key) {
return await axios.post(ChatGPT.base + "/v1/chat/completions") this.apiKey = key
} }
async chat(history) {
const messages = history.map(e => ({role: e.role, content: e.msg}))
return await axios.post(ChatGPT.base + "/v1/chat/completions", JSON.stringify({messages, model: this.id}), {
headers: {
Authorization: 'Bearer ' + this.apiKey, "Content-Type": "application/json", Accept: "application/json",
},
}).then(res => res.json()).then(data => data?.choices?.[0]?.message?.content || "key无效或网络波动,请重新尝试");
}
async chatStream(history) {
const messages = history.map(e => ({role: e.role, content: e.msg}))
return await axios.post(ChatGPT.base + "/v1/chat/completions", JSON.stringify({messages, model: this.id, stream: true}), {
headers: {
Authorization: 'Bearer ' + this.apiKey, "Content-Type": "application/json", Accept: "application/json",
},
}).then(res => res?.body?.getReader());
}
} }
export class ChatGLM extends BaseModel { export class ChatGLM extends BaseModel {
@@ -31,10 +48,7 @@ export class ChatGLM extends BaseModel {
constructor() { constructor() {
super({ super({
avatar: AI_AVATAR, avatar: AI_AVATAR, name: 'ChatGLM', id: "chatglm-6b", desc: "ChatGLM-6B所基于的模型"
name: 'ChatGLM',
id: "chatglm-6b",
desc: "ChatGLM-6B所基于的模型"
}); });
} }