mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-12-26 10:05:57 +08:00
refactor AI chat message struct, allow users to set whether the AI responds in stream, compatible with the GPT-o1 model
This commit is contained in:
@@ -225,7 +225,7 @@ const newChat = (item) => {
|
||||
}
|
||||
showPicker.value = false
|
||||
const options = item.selectedOptions
|
||||
router.push(`/mobile/chat/session?title=新对话&role_id=${options[0].value}&model_id=${options[1].value}&chat_id=0}`)
|
||||
router.push(`/mobile/chat/session?title=新对话&role_id=${options[0].value}&model_id=${options[1].value}`)
|
||||
}
|
||||
|
||||
const changeChat = (chat) => {
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {nextTick, onMounted, onUnmounted, ref} from "vue";
|
||||
import {nextTick, onMounted, onUnmounted, ref, watch} from "vue";
|
||||
import {showImagePreview, showNotify, showToast} from "vant";
|
||||
import {onBeforeRouteLeave, useRouter} from "vue-router";
|
||||
import {processContent, randString, renderInputText, UUID} from "@/utils/libs";
|
||||
@@ -135,7 +135,8 @@ import ChatReply from "@/components/mobile/ChatReply.vue";
|
||||
import {getSessionId, getUserToken} from "@/store/session";
|
||||
import {checkSession} from "@/store/cache";
|
||||
import Clipboard from "clipboard";
|
||||
import {showLoginDialog} from "@/utils/dialog";
|
||||
import { showMessageError} from "@/utils/dialog";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const winHeight = ref(0)
|
||||
const navBarRef = ref(null)
|
||||
@@ -167,49 +168,51 @@ if (chatId.value) {
|
||||
title.value = res.data.title
|
||||
modelId.value = res.data.model_id
|
||||
roleId.value = res.data.role_id
|
||||
loadModels()
|
||||
}).catch(() => {
|
||||
loadModels()
|
||||
})
|
||||
} else {
|
||||
title.value = "新建对话"
|
||||
chatId.value = UUID()
|
||||
}
|
||||
|
||||
// 加载模型
|
||||
httpGet('/api/model/list').then(res => {
|
||||
models.value = res.data
|
||||
if (!modelId.value) {
|
||||
modelId.value = models.value[0].id
|
||||
}
|
||||
for (let i = 0; i < models.value.length; i++) {
|
||||
models.value[i].text = models.value[i].name
|
||||
models.value[i].mValue = models.value[i].value
|
||||
models.value[i].value = models.value[i].id
|
||||
}
|
||||
modelValue.value = getModelName(modelId.value)
|
||||
// 加载角色列表
|
||||
httpGet(`/api/app/list/user`).then((res) => {
|
||||
roles.value = res.data;
|
||||
if (!roleId.value) {
|
||||
roleId.value = roles.value[0]['id']
|
||||
const loadModels = () => {
|
||||
// 加载模型
|
||||
httpGet('/api/model/list').then(res => {
|
||||
models.value = res.data
|
||||
if (!modelId.value) {
|
||||
modelId.value = models.value[0].id
|
||||
}
|
||||
// build data for role picker
|
||||
for (let i = 0; i < roles.value.length; i++) {
|
||||
roles.value[i].text = roles.value[i].name
|
||||
roles.value[i].value = roles.value[i].id
|
||||
roles.value[i].helloMsg = roles.value[i].hello_msg
|
||||
for (let i = 0; i < models.value.length; i++) {
|
||||
models.value[i].text = models.value[i].name
|
||||
models.value[i].mValue = models.value[i].value
|
||||
models.value[i].value = models.value[i].id
|
||||
}
|
||||
|
||||
role.value = getRoleById(roleId.value)
|
||||
columns.value = [roles.value, models.value]
|
||||
// 新建对话
|
||||
if (!chatId.value) {
|
||||
connect(chatId.value, roleId.value, modelId.value)
|
||||
}
|
||||
}).catch((e) => {
|
||||
showNotify({type: "danger", message: '获取聊天角色失败: ' + e.messages})
|
||||
modelValue.value = getModelName(modelId.value)
|
||||
// 加载角色列表
|
||||
httpGet(`/api/app/list/user`,{id: roleId.value}).then((res) => {
|
||||
roles.value = res.data;
|
||||
if (!roleId.value) {
|
||||
roleId.value = roles.value[0]['id']
|
||||
}
|
||||
// build data for role picker
|
||||
for (let i = 0; i < roles.value.length; i++) {
|
||||
roles.value[i].text = roles.value[i].name
|
||||
roles.value[i].value = roles.value[i].id
|
||||
roles.value[i].helloMsg = roles.value[i].hello_msg
|
||||
}
|
||||
role.value = getRoleById(roleId.value)
|
||||
columns.value = [roles.value, models.value]
|
||||
selectedValues.value = [roleId.value, modelId.value]
|
||||
connect()
|
||||
}).catch((e) => {
|
||||
showNotify({type: "danger", message: '获取聊天角色失败: ' + e.messages})
|
||||
})
|
||||
}).catch(e => {
|
||||
showNotify({type: "danger", message: "加载模型失败: " + e.message})
|
||||
})
|
||||
}).catch(e => {
|
||||
showNotify({type: "danger", message: "加载模型失败: " + e.message})
|
||||
})
|
||||
}
|
||||
|
||||
const url = ref(location.protocol + '//' + location.host + '/mobile/chat/export?chat_id=' + chatId.value)
|
||||
|
||||
@@ -239,11 +242,12 @@ const newChat = (item) => {
|
||||
roleId.value = options[0].value
|
||||
modelId.value = options[1].value
|
||||
modelValue.value = getModelName(modelId.value)
|
||||
chatId.value = ""
|
||||
chatId.value = UUID()
|
||||
chatData.value = []
|
||||
role.value = getRoleById(roleId.value)
|
||||
title.value = "新建对话"
|
||||
connect(chatId.value, roleId.value, modelId.value)
|
||||
loadHistory.value = true
|
||||
connect()
|
||||
}
|
||||
|
||||
const chatData = ref([])
|
||||
@@ -280,51 +284,60 @@ md.use(mathjaxPlugin)
|
||||
|
||||
|
||||
const onLoad = () => {
|
||||
if (chatId.value) {
|
||||
checkSession().then(() => {
|
||||
httpGet('/api/chat/history?chat_id=' + chatId.value).then(res => {
|
||||
// 加载状态结束
|
||||
finished.value = true;
|
||||
const data = res.data
|
||||
if (data && data.length > 0) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i].type === "prompt") {
|
||||
chatData.value.push(data[i]);
|
||||
continue;
|
||||
}
|
||||
// checkSession().then(() => {
|
||||
// connect()
|
||||
// }).catch(() => {
|
||||
// })
|
||||
}
|
||||
|
||||
data[i].orgContent = data[i].content;
|
||||
data[i].content = md.render(processContent(data[i].content))
|
||||
chatData.value.push(data[i]);
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
hl.configure({ignoreUnescapedHTML: true})
|
||||
const blocks = document.querySelector("#message-list-box").querySelectorAll('pre code');
|
||||
blocks.forEach((block) => {
|
||||
hl.highlightElement(block)
|
||||
})
|
||||
|
||||
scrollListBox()
|
||||
})
|
||||
}
|
||||
connect(chatId.value, roleId.value, modelId.value);
|
||||
}).catch(() => {
|
||||
error.value = true
|
||||
const loadChatHistory = () => {
|
||||
httpGet('/api/chat/history?chat_id=' + chatId.value).then(res => {
|
||||
const role = getRoleById(roleId.value)
|
||||
// 加载状态结束
|
||||
finished.value = true;
|
||||
const data = res.data
|
||||
if (data.length === 0) {
|
||||
chatData.value.push({
|
||||
type: "reply",
|
||||
id: randString(32),
|
||||
icon: role.icon,
|
||||
content: role.hello_msg,
|
||||
orgContent: role.hello_msg,
|
||||
})
|
||||
}).catch(() => {
|
||||
return
|
||||
}
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i].type === "prompt") {
|
||||
chatData.value.push(data[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
data[i].orgContent = data[i].content;
|
||||
data[i].content = md.render(processContent(data[i].content))
|
||||
chatData.value.push(data[i]);
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
hl.configure({ignoreUnescapedHTML: true})
|
||||
const blocks = document.querySelector("#message-list-box").querySelectorAll('pre code');
|
||||
blocks.forEach((block) => {
|
||||
hl.highlightElement(block)
|
||||
})
|
||||
|
||||
scrollListBox()
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
}).catch(() => {
|
||||
error.value = true
|
||||
})
|
||||
}
|
||||
|
||||
// 离开页面时主动关闭 websocket 连接,节省网络资源
|
||||
onBeforeRouteLeave(() => {
|
||||
if (socket.value !== null) {
|
||||
activelyClose.value = true;
|
||||
clearTimeout(heartbeatHandle.value)
|
||||
socket.value.close();
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// 创建 socket 连接
|
||||
@@ -334,16 +347,15 @@ const showReGenerate = ref(false); // 重新生成
|
||||
const previousText = ref(''); // 上一次提问
|
||||
const lineBuffer = ref(''); // 输出缓冲行
|
||||
const socket = ref(null);
|
||||
const activelyClose = ref(false); // 主动关闭
|
||||
const canSend = ref(true);
|
||||
const heartbeatHandle = ref(null)
|
||||
const connect = function (chat_id, role_id, model_id) {
|
||||
let isNewChat = false;
|
||||
if (!chat_id) {
|
||||
isNewChat = true;
|
||||
chat_id = UUID();
|
||||
}
|
||||
|
||||
const canSend = ref(true)
|
||||
const isNewMsg = ref(true)
|
||||
const loadHistory = ref(true)
|
||||
const store = useSharedStore()
|
||||
const stream = ref(store.chatStream)
|
||||
watch(() => store.chatStream, (newValue) => {
|
||||
stream.value = newValue
|
||||
});
|
||||
const connect = function () {
|
||||
// 初始化 WebSocket 对象
|
||||
const _sessionId = getSessionId();
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
@@ -354,38 +366,15 @@ const connect = function (chat_id, role_id, model_id) {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
// 心跳函数
|
||||
const sendHeartbeat = () => {
|
||||
if (socket.value !== null) {
|
||||
new Promise((resolve) => {
|
||||
socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
|
||||
resolve("success")
|
||||
}).then(() => {
|
||||
heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${role_id}&chat_id=${chat_id}&model_id=${model_id}&token=${getUserToken()}`);
|
||||
const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${roleId.value}&chat_id=${chatId.value}&model_id=${modelId.value}&token=${getUserToken()}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
loading.value = false
|
||||
previousText.value = '';
|
||||
canSend.value = true;
|
||||
activelyClose.value = false;
|
||||
|
||||
if (isNewChat) { // 加载打招呼信息
|
||||
chatData.value.push({
|
||||
type: "reply",
|
||||
id: randString(32),
|
||||
icon: role.value.icon,
|
||||
content: role.value.hello_msg,
|
||||
orgContent: role.value.hello_msg,
|
||||
})
|
||||
if (loadHistory.value) { // 加载历史消息
|
||||
loadChatHistory()
|
||||
}
|
||||
|
||||
// 发送心跳消息
|
||||
sendHeartbeat()
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
@@ -394,20 +383,27 @@ const connect = function (chat_id, role_id, model_id) {
|
||||
reader.readAsText(event.data, "UTF-8");
|
||||
reader.onload = () => {
|
||||
const data = JSON.parse(String(reader.result));
|
||||
if (data.type === 'start') {
|
||||
if (data.type === 'error') {
|
||||
showMessageError(data.message)
|
||||
return
|
||||
}
|
||||
|
||||
if (isNewMsg.value && data.type !== 'end') {
|
||||
chatData.value.push({
|
||||
type: "reply",
|
||||
id: randString(32),
|
||||
icon: role.value.icon,
|
||||
content: ""
|
||||
content: data.content
|
||||
});
|
||||
if (isNewChat) {
|
||||
if (!title.value) {
|
||||
title.value = previousText.value
|
||||
}
|
||||
lineBuffer.value = data.content;
|
||||
isNewMsg.value = false
|
||||
} else if (data.type === 'end') { // 消息接收完毕
|
||||
enableInput()
|
||||
lineBuffer.value = ''; // 清空缓冲
|
||||
|
||||
isNewMsg.value = true
|
||||
} else {
|
||||
lineBuffer.value += data.content;
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
@@ -443,17 +439,11 @@ const connect = function (chat_id, role_id, model_id) {
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (activelyClose.value || socket.value === null) { // 忽略主动关闭
|
||||
return;
|
||||
}
|
||||
// 停止发送消息
|
||||
canSend.value = true;
|
||||
canSend.value = true
|
||||
loadHistory.value = false
|
||||
// 重连
|
||||
checkSession().then(() => {
|
||||
connect(chat_id, role_id, model_id)
|
||||
}).catch(() => {
|
||||
showLoginDialog(router)
|
||||
});
|
||||
connect()
|
||||
});
|
||||
|
||||
socket.value = _socket;
|
||||
@@ -501,7 +491,7 @@ const sendMessage = () => {
|
||||
})
|
||||
|
||||
disableInput(false)
|
||||
socket.value.send(JSON.stringify({type: "chat", content: prompt.value}));
|
||||
socket.value.send(JSON.stringify({stream: stream.value, content: prompt.value}));
|
||||
previousText.value = prompt.value;
|
||||
prompt.value = '';
|
||||
return true;
|
||||
@@ -524,7 +514,7 @@ const reGenerate = () => {
|
||||
icon: loginUser.value.avatar,
|
||||
content: renderInputText(text)
|
||||
});
|
||||
socket.value.send(JSON.stringify({type: "chat", content: previousText.value}));
|
||||
socket.value.send(JSON.stringify({stream: stream.value, content: previousText.value}));
|
||||
}
|
||||
|
||||
const showShare = ref(false)
|
||||
|
||||
Reference in New Issue
Block a user