基本功能已完成
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ export default {
|
|||||||
return {
|
return {
|
||||||
showSettings: false,
|
showSettings: false,
|
||||||
setting: {
|
setting: {
|
||||||
model: new ChatGPT()
|
model: new ChatGPT(),
|
||||||
|
stream: true
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -188,5 +188,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.vuepress-markdown-body) {
|
||||||
|
padding: 2px 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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所基于的模型"
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user