pass
This commit is contained in:
267
components/Conversation.vue
Normal file
267
components/Conversation.vue
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
<script setup>
|
||||||
|
import {EventStreamContentType, fetchEventSource} from '@microsoft/fetch-event-source'
|
||||||
|
|
||||||
|
const { $i18n, $auth } = useNuxtApp()
|
||||||
|
const runtimeConfig = useRuntimeConfig()
|
||||||
|
const currentModel = useCurrentModel()
|
||||||
|
const openaiApiKey = useApiKey()
|
||||||
|
const fetchingResponse = ref(false)
|
||||||
|
const messageQueue = []
|
||||||
|
let isProcessingQueue = false
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
conversation: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const processMessageQueue = () => {
|
||||||
|
if (isProcessingQueue || messageQueue.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!props.conversation.messages[props.conversation.messages.length - 1].is_bot) {
|
||||||
|
props.conversation.messages.push({id: null, is_bot: true, message: ''})
|
||||||
|
}
|
||||||
|
isProcessingQueue = true
|
||||||
|
const nextMessage = messageQueue.shift()
|
||||||
|
if (runtimeConfig.public.typewriter) {
|
||||||
|
let wordIndex = 0;
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
props.conversation.messages[props.conversation.messages.length - 1].message += nextMessage[wordIndex]
|
||||||
|
wordIndex++
|
||||||
|
if (wordIndex === nextMessage.length) {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
isProcessingQueue = false
|
||||||
|
processMessageQueue()
|
||||||
|
}
|
||||||
|
}, runtimeConfig.public.typewriterDelay)
|
||||||
|
} else {
|
||||||
|
props.conversation.messages[props.conversation.messages.length - 1].message += nextMessage
|
||||||
|
isProcessingQueue = false
|
||||||
|
processMessageQueue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctrl
|
||||||
|
const abortFetch = () => {
|
||||||
|
if (ctrl) {
|
||||||
|
ctrl.abort()
|
||||||
|
}
|
||||||
|
fetchingResponse.value = false
|
||||||
|
}
|
||||||
|
const fetchReply = async (message) => {
|
||||||
|
ctrl = new AbortController()
|
||||||
|
|
||||||
|
let webSearchParams = {}
|
||||||
|
if (enableWebSearch.value) {
|
||||||
|
webSearchParams['web_search'] = {
|
||||||
|
ua: navigator.userAgent,
|
||||||
|
default_prompt: $i18n.t('webSearchDefaultPrompt')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Object.assign({}, currentModel.value, {
|
||||||
|
openaiApiKey: enableCustomApiKey.value ? openaiApiKey.value : null,
|
||||||
|
message: message,
|
||||||
|
conversationId: props.conversation.id
|
||||||
|
}, webSearchParams)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fetchEventSource('/api/conversation/', {
|
||||||
|
signal: ctrl.signal,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
onopen(response) {
|
||||||
|
if (response.ok && response.headers.get('content-type') === EventStreamContentType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to send message. HTTP ${response.status} - ${response.statusText}`);
|
||||||
|
},
|
||||||
|
onclose() {
|
||||||
|
if (ctrl.signal.aborted === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to send message. Server closed the connection unexpectedly.`);
|
||||||
|
},
|
||||||
|
onerror(err) {
|
||||||
|
throw err;
|
||||||
|
},
|
||||||
|
async onmessage(message) {
|
||||||
|
const event = message.event
|
||||||
|
const data = JSON.parse(message.data)
|
||||||
|
|
||||||
|
if (event === 'error') {
|
||||||
|
abortFetch()
|
||||||
|
showSnackbar(data.error)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event === 'userMessageId') {
|
||||||
|
props.conversation.messages[props.conversation.messages.length - 1].id = data.userMessageId
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event === 'done') {
|
||||||
|
if (props.conversation.id === null) {
|
||||||
|
props.conversation.id = data.conversationId
|
||||||
|
genTitle(props.conversation.id)
|
||||||
|
}
|
||||||
|
props.conversation.messages[props.conversation.messages.length - 1].id = data.messageId
|
||||||
|
abortFetch()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
messageQueue.push(data.content)
|
||||||
|
processMessageQueue()
|
||||||
|
|
||||||
|
scrollChatWindow()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
abortFetch()
|
||||||
|
showSnackbar(err.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const grab = ref(null)
|
||||||
|
const scrollChatWindow = () => {
|
||||||
|
if (grab.value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
grab.value.scrollIntoView({behavior: 'smooth'})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const send = (message) => {
|
||||||
|
fetchingResponse.value = true
|
||||||
|
props.conversation.messages.push({message: message})
|
||||||
|
fetchReply(message)
|
||||||
|
scrollChatWindow()
|
||||||
|
}
|
||||||
|
const stop = () => {
|
||||||
|
abortFetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
const snackbar = ref(false)
|
||||||
|
const snackbarText = ref('')
|
||||||
|
const showSnackbar = (text) => {
|
||||||
|
snackbarText.value = text
|
||||||
|
snackbar.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const editor = ref(null)
|
||||||
|
const usePrompt = (prompt) => {
|
||||||
|
editor.value.usePrompt(prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteMessage = (index) => {
|
||||||
|
props.conversation.messages.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const showWebSearchToggle = ref(false)
|
||||||
|
const enableWebSearch = ref(false)
|
||||||
|
const enableCustomApiKey = ref(false)
|
||||||
|
|
||||||
|
const settings = useSettings()
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (settings.value) {
|
||||||
|
const settingsValue = toRaw(settings.value)
|
||||||
|
showWebSearchToggle.value = settingsValue.open_web_search && settingsValue.open_web_search === 'True'
|
||||||
|
enableCustomApiKey.value = settingsValue.open_api_key_setting && settingsValue.open_api_key_setting === 'True'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="conversation.messages"
|
||||||
|
ref="chatWindow"
|
||||||
|
>
|
||||||
|
<v-container>
|
||||||
|
<v-row>
|
||||||
|
<v-col
|
||||||
|
v-for="(message, index) in conversation.messages" :key="index"
|
||||||
|
cols="12"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="d-flex align-center"
|
||||||
|
:class="message.is_bot ? 'justify-start' : 'justify-end'"
|
||||||
|
>
|
||||||
|
<MessageActions
|
||||||
|
v-if="!message.is_bot"
|
||||||
|
:message="message"
|
||||||
|
:message-index="index"
|
||||||
|
:use-prompt="usePrompt"
|
||||||
|
:delete-message="deleteMessage"
|
||||||
|
/>
|
||||||
|
<MsgContent :message="message" />
|
||||||
|
<MessageActions
|
||||||
|
v-if="message.is_bot"
|
||||||
|
:message="message"
|
||||||
|
:message-index="index"
|
||||||
|
:use-prompt="usePrompt"
|
||||||
|
:delete-message="deleteMessage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
|
||||||
|
<div ref="grab" class="w-100" style="height: 200px;"></div>
|
||||||
|
</div>
|
||||||
|
<Welcome v-else />
|
||||||
|
<v-footer app>
|
||||||
|
<div class="px-md-16 w-100 d-flex flex-column">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-btn
|
||||||
|
v-show="fetchingResponse"
|
||||||
|
icon="close"
|
||||||
|
title="stop"
|
||||||
|
class="mr-3"
|
||||||
|
@click="stop"
|
||||||
|
></v-btn>
|
||||||
|
<MsgEditor ref="editor" :send-message="send" :disabled="fetchingResponse" :loading="fetchingResponse" />
|
||||||
|
</div>
|
||||||
|
<v-toolbar
|
||||||
|
density="comfortable"
|
||||||
|
color="transparent"
|
||||||
|
>
|
||||||
|
<Prompt v-show="!fetchingResponse" :use-prompt="usePrompt" />
|
||||||
|
<v-switch
|
||||||
|
v-if="showWebSearchToggle"
|
||||||
|
v-model="enableWebSearch"
|
||||||
|
hide-details
|
||||||
|
color="primary"
|
||||||
|
:label="$t('webSearch')"
|
||||||
|
></v-switch>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
</v-toolbar>
|
||||||
|
</div>
|
||||||
|
</v-footer>
|
||||||
|
<v-snackbar
|
||||||
|
v-model="snackbar"
|
||||||
|
multi-line
|
||||||
|
location="top"
|
||||||
|
>
|
||||||
|
{{ snackbarText }}
|
||||||
|
|
||||||
|
<template v-slot:actions>
|
||||||
|
<v-btn
|
||||||
|
color="red"
|
||||||
|
variant="text"
|
||||||
|
@click="snackbar = false"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
|
|
||||||
|
</template>
|
||||||
@@ -5,8 +5,8 @@ export const useCurrentModel = () => useState('currentModel', () => getCurrentMo
|
|||||||
|
|
||||||
export const useApiKey = () => useState('apiKey', () => getStoredApiKey())
|
export const useApiKey = () => useState('apiKey', () => getStoredApiKey())
|
||||||
|
|
||||||
export const useConversion = () => useState('conversion', () => getDefaultConversionData())
|
export const useConversation = () => useState('conversation', () => getDefaultConversationData())
|
||||||
|
|
||||||
export const useConversions = () => useState('conversions', () => [])
|
export const useConversations = () => useState('conversations', () => [])
|
||||||
|
|
||||||
export const useSettings = () => useState('settings', () => {})
|
export const useSettings = () => useState('settings', () => {})
|
||||||
@@ -22,8 +22,8 @@ const setLang = (lang) => {
|
|||||||
setLocale(lang)
|
setLocale(lang)
|
||||||
}
|
}
|
||||||
|
|
||||||
const conversations = useConversions()
|
const conversations = useConversations()
|
||||||
const currentConversation = useConversion()
|
const currentConversation = useConversation()
|
||||||
|
|
||||||
const editingConversation = ref(null)
|
const editingConversation = ref(null)
|
||||||
const deletingConversationIndex = ref(null)
|
const deletingConversationIndex = ref(null)
|
||||||
@@ -54,7 +54,7 @@ const deleteConversation = async (index) => {
|
|||||||
deletingConversationIndex.value = null
|
deletingConversationIndex.value = null
|
||||||
if (!error.value) {
|
if (!error.value) {
|
||||||
if (conversations.value[index].id === currentConversation.value.id) {
|
if (conversations.value[index].id === currentConversation.value.id) {
|
||||||
createNewConversion()
|
createNewConversation()
|
||||||
}
|
}
|
||||||
conversations.value.splice(index, 1)
|
conversations.value.splice(index, 1)
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ const loadingConversations = ref(false)
|
|||||||
|
|
||||||
const loadConversations = async () => {
|
const loadConversations = async () => {
|
||||||
loadingConversations.value = true
|
loadingConversations.value = true
|
||||||
conversations.value = await getConversions()
|
conversations.value = await getConversations()
|
||||||
loadingConversations.value = false
|
loadingConversations.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ onMounted(async () => {
|
|||||||
block
|
block
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
prepend-icon="add"
|
prepend-icon="add"
|
||||||
@click="createNewConversion()"
|
@click="createNewConversation()"
|
||||||
class="text-none"
|
class="text-none"
|
||||||
>
|
>
|
||||||
{{ $t('newConversation') }}
|
{{ $t('newConversation') }}
|
||||||
@@ -172,7 +172,7 @@ onMounted(async () => {
|
|||||||
<v-list-item
|
<v-list-item
|
||||||
rounded="xl"
|
rounded="xl"
|
||||||
active-color="primary"
|
active-color="primary"
|
||||||
@click="openConversationMessages(conversation)"
|
:to="`/${conversation.id}`"
|
||||||
v-bind="props"
|
v-bind="props"
|
||||||
>
|
>
|
||||||
<v-list-item-title>{{ conversation.topic }}</v-list-item-title>
|
<v-list-item-title>{{ conversation.topic }}</v-list-item-title>
|
||||||
@@ -310,7 +310,7 @@ onMounted(async () => {
|
|||||||
<v-btn
|
<v-btn
|
||||||
:title="$t('newConversation')"
|
:title="$t('newConversation')"
|
||||||
icon="add"
|
icon="add"
|
||||||
@click="createNewConversion()"
|
@click="createNewConversation()"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
|
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|||||||
275
pages/index.vue
275
pages/index.vue
@@ -1,270 +1,29 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Prompt from "~/components/Prompt.vue";
|
import {getDefaultConversationData} from "~/utils/helper";
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ["auth"]
|
middleware: ["auth"],
|
||||||
|
path: '/:id?',
|
||||||
|
keepalive: true
|
||||||
})
|
})
|
||||||
import {EventStreamContentType, fetchEventSource} from '@microsoft/fetch-event-source'
|
const route = useRoute()
|
||||||
|
const conversation = ref({})
|
||||||
const { $i18n, $auth } = useNuxtApp()
|
const loadMessage = async () => {
|
||||||
const runtimeConfig = useRuntimeConfig()
|
conversation.value = Object.assign(conversation.value, conversation)
|
||||||
const currentModel = useCurrentModel()
|
conversation.value.loadingMessages = true
|
||||||
const openaiApiKey = useApiKey()
|
const { data, error } = await useAuthFetch('/api/chat/messages/?conversationId=' + route.params.id)
|
||||||
const fetchingResponse = ref(false)
|
if (!error.value) {
|
||||||
const messageQueue = []
|
conversation.value.messages = data.value
|
||||||
let isProcessingQueue = false
|
|
||||||
|
|
||||||
const processMessageQueue = () => {
|
|
||||||
if (isProcessingQueue || messageQueue.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (!currentConversation.value.messages[currentConversation.value.messages.length - 1].is_bot) {
|
conversation.value.loadingMessages = true
|
||||||
currentConversation.value.messages.push({id: null, is_bot: true, message: ''})
|
|
||||||
}
|
}
|
||||||
isProcessingQueue = true
|
onMounted(async () => {
|
||||||
const nextMessage = messageQueue.shift()
|
if (route.params.id) {
|
||||||
if (runtimeConfig.public.typewriter) {
|
await loadMessage()
|
||||||
let wordIndex = 0;
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
currentConversation.value.messages[currentConversation.value.messages.length - 1].message += nextMessage[wordIndex]
|
|
||||||
wordIndex++
|
|
||||||
if (wordIndex === nextMessage.length) {
|
|
||||||
clearInterval(intervalId)
|
|
||||||
isProcessingQueue = false
|
|
||||||
processMessageQueue()
|
|
||||||
}
|
|
||||||
}, runtimeConfig.public.typewriterDelay)
|
|
||||||
} else {
|
|
||||||
currentConversation.value.messages[currentConversation.value.messages.length - 1].message += nextMessage
|
|
||||||
isProcessingQueue = false
|
|
||||||
processMessageQueue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctrl
|
|
||||||
const abortFetch = () => {
|
|
||||||
if (ctrl) {
|
|
||||||
ctrl.abort()
|
|
||||||
}
|
|
||||||
fetchingResponse.value = false
|
|
||||||
}
|
|
||||||
const fetchReply = async (message) => {
|
|
||||||
ctrl = new AbortController()
|
|
||||||
|
|
||||||
let webSearchParams = {}
|
|
||||||
if (enableWebSearch.value) {
|
|
||||||
webSearchParams['web_search'] = {
|
|
||||||
ua: navigator.userAgent,
|
|
||||||
default_prompt: $i18n.t('webSearchDefaultPrompt')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = Object.assign({}, currentModel.value, {
|
|
||||||
openaiApiKey: enableCustomApiKey.value ? openaiApiKey.value : null,
|
|
||||||
message: message,
|
|
||||||
conversationId: currentConversation.value.id
|
|
||||||
}, webSearchParams)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fetchEventSource('/api/conversation/', {
|
|
||||||
signal: ctrl.signal,
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
onopen(response) {
|
|
||||||
if (response.ok && response.headers.get('content-type') === EventStreamContentType) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error(`Failed to send message. HTTP ${response.status} - ${response.statusText}`);
|
|
||||||
},
|
|
||||||
onclose() {
|
|
||||||
if (ctrl.signal.aborted === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error(`Failed to send message. Server closed the connection unexpectedly.`);
|
|
||||||
},
|
|
||||||
onerror(err) {
|
|
||||||
throw err;
|
|
||||||
},
|
|
||||||
async onmessage(message) {
|
|
||||||
const event = message.event
|
|
||||||
const data = JSON.parse(message.data)
|
|
||||||
|
|
||||||
if (event === 'error') {
|
|
||||||
abortFetch()
|
|
||||||
showSnackbar(data.error)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event === 'userMessageId') {
|
|
||||||
currentConversation.value.messages[currentConversation.value.messages.length - 1].id = data.userMessageId
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event === 'done') {
|
|
||||||
if (currentConversation.value.id === null) {
|
|
||||||
currentConversation.value.id = data.conversationId
|
|
||||||
genTitle(currentConversation.value.id)
|
|
||||||
}
|
|
||||||
currentConversation.value.messages[currentConversation.value.messages.length - 1].id = data.messageId
|
|
||||||
abortFetch()
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
messageQueue.push(data.content)
|
|
||||||
processMessageQueue()
|
|
||||||
|
|
||||||
scrollChatWindow()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
abortFetch()
|
|
||||||
showSnackbar(err.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentConversation = useConversion()
|
|
||||||
|
|
||||||
const grab = ref(null)
|
|
||||||
const scrollChatWindow = () => {
|
|
||||||
if (grab.value === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
grab.value.scrollIntoView({behavior: 'smooth'})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const send = (message) => {
|
|
||||||
fetchingResponse.value = true
|
|
||||||
currentConversation.value.messages.push({message: message})
|
|
||||||
fetchReply(message)
|
|
||||||
scrollChatWindow()
|
|
||||||
}
|
|
||||||
const stop = () => {
|
|
||||||
abortFetch()
|
|
||||||
}
|
|
||||||
|
|
||||||
const snackbar = ref(false)
|
|
||||||
const snackbarText = ref('')
|
|
||||||
const showSnackbar = (text) => {
|
|
||||||
snackbarText.value = text
|
|
||||||
snackbar.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = ref(null)
|
|
||||||
const usePrompt = (prompt) => {
|
|
||||||
editor.value.usePrompt(prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteMessage = (index) => {
|
|
||||||
currentConversation.value.messages.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const showWebSearchToggle = ref(false)
|
|
||||||
const enableWebSearch = ref(false)
|
|
||||||
const enableCustomApiKey = ref(false)
|
|
||||||
|
|
||||||
const settings = useSettings()
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (settings.value) {
|
|
||||||
const settingsValue = toRaw(settings.value)
|
|
||||||
showWebSearchToggle.value = settingsValue.open_web_search && settingsValue.open_web_search === 'True'
|
|
||||||
enableCustomApiKey.value = settingsValue.open_api_key_setting && settingsValue.open_api_key_setting === 'True'
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Conversation :conversation="conversation" />
|
||||||
v-if="currentConversation.messages.length > 0"
|
|
||||||
ref="chatWindow"
|
|
||||||
>
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-col
|
|
||||||
v-for="(message, index) in currentConversation.messages" :key="index"
|
|
||||||
cols="12"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="d-flex align-center"
|
|
||||||
:class="message.is_bot ? 'justify-start' : 'justify-end'"
|
|
||||||
>
|
|
||||||
<MessageActions
|
|
||||||
v-if="!message.is_bot"
|
|
||||||
:message="message"
|
|
||||||
:message-index="index"
|
|
||||||
:use-prompt="usePrompt"
|
|
||||||
:delete-message="deleteMessage"
|
|
||||||
/>
|
|
||||||
<MsgContent :message="message" />
|
|
||||||
<MessageActions
|
|
||||||
v-if="message.is_bot"
|
|
||||||
:message="message"
|
|
||||||
:message-index="index"
|
|
||||||
:use-prompt="usePrompt"
|
|
||||||
:delete-message="deleteMessage"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<div ref="grab" class="w-100" style="height: 200px;"></div>
|
|
||||||
</div>
|
|
||||||
<Welcome v-else />
|
|
||||||
<v-footer app>
|
|
||||||
<div class="px-md-16 w-100 d-flex flex-column">
|
|
||||||
<div class="d-flex align-center">
|
|
||||||
<v-btn
|
|
||||||
v-show="fetchingResponse"
|
|
||||||
icon="close"
|
|
||||||
title="stop"
|
|
||||||
class="mr-3"
|
|
||||||
@click="stop"
|
|
||||||
></v-btn>
|
|
||||||
<MsgEditor ref="editor" :send-message="send" :disabled="fetchingResponse" :loading="fetchingResponse" />
|
|
||||||
</div>
|
|
||||||
<v-toolbar
|
|
||||||
density="comfortable"
|
|
||||||
color="transparent"
|
|
||||||
>
|
|
||||||
<Prompt v-show="!fetchingResponse" :use-prompt="usePrompt" />
|
|
||||||
<v-switch
|
|
||||||
v-if="showWebSearchToggle"
|
|
||||||
v-model="enableWebSearch"
|
|
||||||
hide-details
|
|
||||||
color="primary"
|
|
||||||
:label="$t('webSearch')"
|
|
||||||
></v-switch>
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
</v-toolbar>
|
|
||||||
|
|
||||||
<!-- <div class="py-2 text-disabled text-caption font-weight-light text-center">-->
|
|
||||||
<!-- © {{ new Date().getFullYear() }} {{ runtimeConfig.public.appName }}-->
|
|
||||||
<!-- </div>-->
|
|
||||||
</div>
|
|
||||||
</v-footer>
|
|
||||||
<v-snackbar
|
|
||||||
v-model="snackbar"
|
|
||||||
multi-line
|
|
||||||
location="top"
|
|
||||||
>
|
|
||||||
{{ snackbarText }}
|
|
||||||
|
|
||||||
<template v-slot:actions>
|
|
||||||
<v-btn
|
|
||||||
color="red"
|
|
||||||
variant="text"
|
|
||||||
@click="snackbar = false"
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
</v-snackbar>
|
|
||||||
</template>
|
</template>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
export const getDefaultConversionData = () => {
|
export const getDefaultConversationData = () => {
|
||||||
return {
|
return {
|
||||||
id: null,
|
id: null,
|
||||||
topic: null,
|
topic: null,
|
||||||
@@ -8,7 +8,7 @@ export const getDefaultConversionData = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getConversions = async () => {
|
export const getConversations = async () => {
|
||||||
const { data, error } = await useAuthFetch('/api/chat/conversations/')
|
const { data, error } = await useAuthFetch('/api/chat/conversations/')
|
||||||
if (!error.value) {
|
if (!error.value) {
|
||||||
return data.value
|
return data.value
|
||||||
@@ -16,13 +16,13 @@ export const getConversions = async () => {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createNewConversion = () => {
|
export const createNewConversation = () => {
|
||||||
const conversation = useConversion()
|
const conversation = useConversation()
|
||||||
conversation.value = getDefaultConversionData()
|
conversation.value = getDefaultConversationData()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const openConversationMessages = async (currentConversation) => {
|
export const openConversationMessages = async (currentConversation) => {
|
||||||
const conversation = useConversion()
|
const conversation = useConversation()
|
||||||
conversation.value = Object.assign(conversation.value, currentConversation)
|
conversation.value = Object.assign(conversation.value, currentConversation)
|
||||||
conversation.value.loadingMessages = true
|
conversation.value.loadingMessages = true
|
||||||
const { data, error } = await useAuthFetch('/api/chat/messages/?conversationId=' + currentConversation.id)
|
const { data, error } = await useAuthFetch('/api/chat/messages/?conversationId=' + currentConversation.id)
|
||||||
@@ -44,7 +44,7 @@ export const genTitle = async (conversationId) => {
|
|||||||
id: conversationId,
|
id: conversationId,
|
||||||
topic: data.value.title,
|
topic: data.value.title,
|
||||||
}
|
}
|
||||||
const conversations = useConversions()
|
const conversations = useConversations()
|
||||||
// prepend to conversations
|
// prepend to conversations
|
||||||
conversations.value = [conversation, ...conversations.value]
|
conversations.value = [conversation, ...conversations.value]
|
||||||
return data.value.title
|
return data.value.title
|
||||||
|
|||||||
Reference in New Issue
Block a user