|
|
|
@ -12,7 +12,7 @@ import {
|
|
|
|
|
} from '@ionic/vue'
|
|
|
|
|
import { showConfirm, showError, showHelpToast } from '@/utils/dialog'
|
|
|
|
|
import { ChatCompleteChunkInfo } from '@/types/base'
|
|
|
|
|
import { setPageTitle } from '@/utils/other'
|
|
|
|
|
import { DEFAULT_BOT_AVATAR, secondTimestamp, setPageTitle } from '@/utils/other'
|
|
|
|
|
import { pinConversation } from '@/utils/actions'
|
|
|
|
|
import { moduleConversationRoute } from '@/types/enum'
|
|
|
|
|
import { IonRouterOutletInstance } from '@/types/instance'
|
|
|
|
@ -42,6 +42,11 @@ const state = reactive({
|
|
|
|
|
conversationTitle: '加载中...',
|
|
|
|
|
conversationId: 0,
|
|
|
|
|
pinned: false,
|
|
|
|
|
personaLoaded: false,
|
|
|
|
|
|
|
|
|
|
botId: '',
|
|
|
|
|
botName: '',
|
|
|
|
|
botAvatar: '',
|
|
|
|
|
|
|
|
|
|
conversationChunkIdList: [] as number[],
|
|
|
|
|
messageChunkList: [] as ConversationChunkInfo[],
|
|
|
|
@ -73,6 +78,8 @@ if (route.params.id) {
|
|
|
|
|
state.conversationId = parseInt(Array.isArray(route.params.id) ? route.params.id[0] : route.params.id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isNewConversaion = computed(() => state.conversationId === 0)
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// 帮助信息
|
|
|
|
|
// ============================================================================
|
|
|
|
@ -100,12 +107,14 @@ const onSendMessage = async () => {
|
|
|
|
|
contentEl?.scrollToBottom()
|
|
|
|
|
ignoreAutoScroll = false
|
|
|
|
|
|
|
|
|
|
state.personaLoaded = true
|
|
|
|
|
state.conversationLoading = true
|
|
|
|
|
try {
|
|
|
|
|
let question = state.formMessage
|
|
|
|
|
const startRes = await api.chat.startChatComplete(pageStore.title, question, {
|
|
|
|
|
conversationId: state.conversationId,
|
|
|
|
|
editMessageId: state.editingId || undefined,
|
|
|
|
|
botId: state.botId,
|
|
|
|
|
inCollection: settingsStore.collectionMode,
|
|
|
|
|
extractLimit: settingsStore.docExtractLimit
|
|
|
|
|
})
|
|
|
|
@ -181,7 +190,7 @@ const onSendMessage = async () => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (startRes.conversation_id !== state.conversationId) {
|
|
|
|
|
if (state.conversationId === 0) {
|
|
|
|
|
if (isNewConversaion.value) {
|
|
|
|
|
// 新建对话
|
|
|
|
|
conversationStore.list.push({
|
|
|
|
|
id: startRes.conversation_id,
|
|
|
|
@ -323,10 +332,14 @@ const loadPrevConversationChunk = (): Promise<void> => {
|
|
|
|
|
|
|
|
|
|
const loadConversation = async () => {
|
|
|
|
|
try {
|
|
|
|
|
if (state.conversationId === 0) {
|
|
|
|
|
if (isNewConversaion.value) {
|
|
|
|
|
// 新建对话
|
|
|
|
|
state.conversationTitle = '未命名对话'
|
|
|
|
|
state.conversationLoading = true
|
|
|
|
|
state.botId = route.query.botId as string ?? 'default'
|
|
|
|
|
await loadBotPersona()
|
|
|
|
|
} else {
|
|
|
|
|
state.personaLoaded = true
|
|
|
|
|
state.messageChunkList = []
|
|
|
|
|
state.errorMessage = null
|
|
|
|
|
state.formMessage = ''
|
|
|
|
@ -346,6 +359,9 @@ const loadConversation = async () => {
|
|
|
|
|
|
|
|
|
|
state.conversationTitle = conversationInfo.title ?? '未命名对话'
|
|
|
|
|
state.pinned = conversationInfo.pinned
|
|
|
|
|
state.botId = conversationInfo.extra.bot_id ?? 'default'
|
|
|
|
|
|
|
|
|
|
await loadBotPersona()
|
|
|
|
|
|
|
|
|
|
state.conversationChunkIdList = await api.chat.getConversationChunkList({
|
|
|
|
|
id: state.conversationId
|
|
|
|
@ -387,6 +403,37 @@ const onInfiniteScrollLoad = async (event: InfiniteScrollCustomEvent) => {
|
|
|
|
|
await event.target.complete()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// 加载机器人信息
|
|
|
|
|
// ============================================================================
|
|
|
|
|
const loadBotPersona = async () => {
|
|
|
|
|
if (state.personaLoaded) return
|
|
|
|
|
|
|
|
|
|
state.personaLoaded = true
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
let personaInfo = await api.chat.getBotPersonaInfo({
|
|
|
|
|
bot_id: state.botId
|
|
|
|
|
})
|
|
|
|
|
if (isNewConversaion.value) {
|
|
|
|
|
// 新对话,加载机器人预设值
|
|
|
|
|
state.messageChunkList.push({
|
|
|
|
|
id: 0,
|
|
|
|
|
message_data: personaInfo.message_log?.map((message, index) => ({
|
|
|
|
|
...message,
|
|
|
|
|
id: '',
|
|
|
|
|
time: secondTimestamp()
|
|
|
|
|
})) ?? []
|
|
|
|
|
})
|
|
|
|
|
state.formMessage = personaInfo.default_question ?? ''
|
|
|
|
|
}
|
|
|
|
|
state.botName = personaInfo.bot_name
|
|
|
|
|
state.botAvatar = personaInfo.bot_avatar ?? DEFAULT_BOT_AVATAR
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
console.error(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// 修改消息、创建分支
|
|
|
|
|
// ============================================================================
|
|
|
|
@ -753,19 +800,21 @@ watch(() => conversationInfo.value, (currentConversation) => {
|
|
|
|
|
<ion-infinite-scroll-content loadingText="加载中..." loadingSpinner="lines"></ion-infinite-scroll-content>
|
|
|
|
|
</ion-infinite-scroll>
|
|
|
|
|
<!-- 消息列表 -->
|
|
|
|
|
<div class="message-chunk" v-for="chunk in ascMessageChunkList" :key="chunk.id">
|
|
|
|
|
<template v-for="message, index in chunk.message_data" :key="index">
|
|
|
|
|
<chat-message v-if="message.role == 'user'" :sender="userStore.displayName" :avatar="userStore.displayAvatar"
|
|
|
|
|
:content="message.content" :time="message.time"
|
|
|
|
|
:edit="chunk.id == state.lastUserMsgId[0] && message.id == state.lastUserMsgId[1]"
|
|
|
|
|
@click-edit="onEditMessage(chunk.id, message.id)"></chat-message>
|
|
|
|
|
<chat-message v-else-if="message.role == 'assistant'" sender="写作助手" avatar="/images/assistant-avatar.png"
|
|
|
|
|
:content="message.content" :time="message.time" assistant :cursor="message.streaming"
|
|
|
|
|
:change="chunk.id == state.lastAssistantMsgId[0] && message.id == state.lastAssistantMsgId[1]"
|
|
|
|
|
@click-create-branch="onCreateBranch(chunk.id, message.id)"
|
|
|
|
|
@click-change="onChangeResponse(chunk.id, message.id)"></chat-message>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
<transition-group name="fade">
|
|
|
|
|
<div v-for="chunk in ascMessageChunkList" :key="chunk.id" class="message-chunk">
|
|
|
|
|
<template v-for="message, index in chunk.message_data" :key="index">
|
|
|
|
|
<chat-message v-if="message.role == 'user'" :sender="userStore.displayName" :avatar="userStore.displayAvatar"
|
|
|
|
|
:content="message.content" :time="message.time" :msg-id="message.id"
|
|
|
|
|
:edit="chunk.id == state.lastUserMsgId[0] && message.id == state.lastUserMsgId[1]"
|
|
|
|
|
@click-edit="onEditMessage(chunk.id, message.id)"></chat-message>
|
|
|
|
|
<chat-message v-else-if="message.role == 'assistant'" :sender="state.botName" :avatar="state.botAvatar"
|
|
|
|
|
:content="message.content" :time="message.time" assistant :cursor="message.streaming" :msg-id="message.id"
|
|
|
|
|
:change="chunk.id == state.lastAssistantMsgId[0] && message.id == state.lastAssistantMsgId[1]"
|
|
|
|
|
@click-create-branch="onCreateBranch(chunk.id, message.id)"
|
|
|
|
|
@click-change="onChangeResponse(chunk.id, message.id)"></chat-message>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</transition-group>
|
|
|
|
|
</ion-content>
|
|
|
|
|
|
|
|
|
|
<ion-footer :translucent="true">
|
|
|
|
@ -799,13 +848,13 @@ watch(() => conversationInfo.value, (currentConversation) => {
|
|
|
|
|
</ion-footer>
|
|
|
|
|
|
|
|
|
|
<chat-complete-settings-modal v-model:visible="state.settingsModalOpened" @close="onSettingsModalClose"
|
|
|
|
|
@manual-update-index="updatePageIndex"
|
|
|
|
|
:presenting-element="mainRouterOutlet?.$el"></chat-complete-settings-modal>
|
|
|
|
|
@manual-update-index="updatePageIndex" :presenting-element="mainRouterOutlet?.$el"></chat-complete-settings-modal>
|
|
|
|
|
</ion-page>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
@import '@/theme/content-container.scss';
|
|
|
|
|
@import '@/theme/transition-fade.scss';
|
|
|
|
|
|
|
|
|
|
#container strong {
|
|
|
|
|
font-size: 20px;
|
|
|
|
|