hold conversations
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
|
||||
export const useModels = () => useState('models', () => getStoredModels())
|
||||
|
||||
export const useCurrentModel = () => useState('currentModel', () => getCurrentModel())
|
||||
|
||||
export const useApiKey = () => useState('apiKey', () => getStoredApiKey())
|
||||
|
||||
export const useConversion = () => useState('conversion', () => getDefaultConversionData())
|
||||
|
||||
export const useConversions = () => useState('conversions', () => [])
|
||||
@@ -1,4 +1,7 @@
|
||||
<script setup>
|
||||
import {useConversions} from "../composables/states";
|
||||
import {getConversions} from "../utils/helper";
|
||||
|
||||
const { $i18n } = useNuxtApp()
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const colorMode = useColorMode()
|
||||
@@ -19,6 +22,13 @@ const { locale, locales, setLocale } = useI18n()
|
||||
const setLang = (lang) => {
|
||||
setLocale(lang)
|
||||
}
|
||||
|
||||
const conversations = useConversions()
|
||||
|
||||
onNuxtReady(async () => {
|
||||
conversations.value = await getConversions()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -28,47 +38,66 @@ const setLang = (lang) => {
|
||||
<v-navigation-drawer
|
||||
v-model="drawer"
|
||||
>
|
||||
<v-list>
|
||||
<ModelDialog/>
|
||||
</v-list>
|
||||
|
||||
<template v-slot:append>
|
||||
<v-divider></v-divider>
|
||||
<div class="px-2 py-2">
|
||||
<v-btn
|
||||
block
|
||||
variant="outlined"
|
||||
prepend-icon="add"
|
||||
size="large"
|
||||
@click="createNewConversion()"
|
||||
>
|
||||
New conversation
|
||||
</v-btn>
|
||||
<v-list>
|
||||
<ApiKeyDialog/>
|
||||
|
||||
<v-menu
|
||||
>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-list-item
|
||||
v-bind="props"
|
||||
rounded="xl"
|
||||
:prepend-icon="$colorMode.value === 'light' ? 'light_mode' : 'dark_mode'"
|
||||
:title="$t('themeMode')"
|
||||
></v-list-item>
|
||||
</template>
|
||||
<v-list
|
||||
bg-color="white"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(theme, idx) in themes"
|
||||
:key="idx"
|
||||
@click="setTheme(theme.value)"
|
||||
>
|
||||
<v-list-item-title>{{ theme.title }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<SettingsLanguages/>
|
||||
|
||||
<v-list-item
|
||||
rounded="xl"
|
||||
prepend-icon="help_outline"
|
||||
:title="$t('feedback')"
|
||||
@click="feedback"
|
||||
v-for="conversation in conversations"
|
||||
:key="conversation.id"
|
||||
:title="conversation.topic"
|
||||
active-color="primary"
|
||||
@click="openConversationMessages(conversation)"
|
||||
></v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
|
||||
<template v-slot:append>
|
||||
<div class="px-1">
|
||||
<v-divider></v-divider>
|
||||
<v-list>
|
||||
<ApiKeyDialog/>
|
||||
|
||||
<v-menu
|
||||
>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-list-item
|
||||
v-bind="props"
|
||||
rounded="xl"
|
||||
:prepend-icon="$colorMode.value === 'light' ? 'light_mode' : 'dark_mode'"
|
||||
:title="$t('themeMode')"
|
||||
></v-list-item>
|
||||
</template>
|
||||
<v-list
|
||||
bg-color="white"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(theme, idx) in themes"
|
||||
:key="idx"
|
||||
@click="setTheme(theme.value)"
|
||||
>
|
||||
<v-list-item-title>{{ theme.title }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<SettingsLanguages/>
|
||||
|
||||
<v-list-item
|
||||
rounded="xl"
|
||||
prepend-icon="help_outline"
|
||||
:title="$t('feedback')"
|
||||
@click="feedback"
|
||||
></v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
</template>
|
||||
</v-navigation-drawer>
|
||||
|
||||
@@ -106,3 +135,16 @@ const setLang = (lang) => {
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.v-navigation-drawer__content::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
.v-navigation-drawer__content:hover::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.v-navigation-drawer__content:hover::-webkit-scrollbar-thumb {
|
||||
background-color: #999;
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
@@ -52,7 +52,7 @@ const fetchReply = async (message, parentMessageId) => {
|
||||
throw err;
|
||||
},
|
||||
onmessage(message) {
|
||||
console.log(message)
|
||||
// console.log(message)
|
||||
const event = message.event
|
||||
const data = JSON.parse(message.data)
|
||||
|
||||
@@ -63,19 +63,20 @@ const fetchReply = async (message, parentMessageId) => {
|
||||
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;
|
||||
}
|
||||
|
||||
if (currentConversation.value.messages[currentConversation.value.messages.length - 1].from === 'ai') {
|
||||
if (currentConversation.value.messages[currentConversation.value.messages.length - 1].is_bot) {
|
||||
currentConversation.value.messages[currentConversation.value.messages.length - 1].message += data.content
|
||||
} else {
|
||||
currentConversation.value.messages.push({id: null, from: 'ai', message: data.content})
|
||||
currentConversation.value.messages.push({id: null, is_bot: true, message: data.content})
|
||||
}
|
||||
|
||||
// scrollChatWindow()
|
||||
scrollChatWindow()
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -85,11 +86,7 @@ const fetchReply = async (message, parentMessageId) => {
|
||||
}
|
||||
}
|
||||
|
||||
const defaultConversation = ref({
|
||||
id: null,
|
||||
messages: []
|
||||
})
|
||||
const currentConversation = ref({})
|
||||
const currentConversation = useConversion()
|
||||
|
||||
const grab = ref(null)
|
||||
const scrollChatWindow = () => {
|
||||
@@ -99,20 +96,17 @@ const scrollChatWindow = () => {
|
||||
grab.value.scrollIntoView({behavior: 'smooth'})
|
||||
}
|
||||
|
||||
const createNewConversation = () => {
|
||||
currentConversation.value = Object.assign(defaultConversation.value, {
|
||||
})
|
||||
}
|
||||
|
||||
const send = (message) => {
|
||||
fetchingResponse.value = true
|
||||
let parentMessageId = null
|
||||
if (currentConversation.value.messages.length > 0) {
|
||||
const lastMessage = currentConversation.value.messages[currentConversation.value.messages.length - 1]
|
||||
if (lastMessage.from === 'ai' && lastMessage.id !== null) {
|
||||
if (lastMessage.is_bot && lastMessage.id !== null) {
|
||||
parentMessageId = lastMessage.id
|
||||
}
|
||||
}
|
||||
currentConversation.value.messages.push({from: 'me', parentMessageId: parentMessageId, message: message})
|
||||
currentConversation.value.messages.push({parentMessageId: parentMessageId, message: message})
|
||||
fetchReply(message, parentMessageId)
|
||||
scrollChatWindow()
|
||||
}
|
||||
@@ -127,7 +121,6 @@ const showSnackbar = (text) => {
|
||||
snackbar.value = true
|
||||
}
|
||||
|
||||
createNewConversation()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -140,10 +133,10 @@ createNewConversation()
|
||||
elevation="0"
|
||||
v-for="(conversation, index) in currentConversation.messages"
|
||||
:key="index"
|
||||
:variant="conversation.from === 'ai' ? 'tonal' : 'text'"
|
||||
:variant="conversation.is_bot ? 'tonal' : 'text'"
|
||||
>
|
||||
<v-container>
|
||||
<v-card-text class="text-caption text-disabled">{{ $t(`roles.${conversation.from}`) }}</v-card-text>
|
||||
<v-card-text class="text-caption text-disabled">{{ $t(`roles.${conversation.is_bot?'ai':'me'}`) }}</v-card-text>
|
||||
<v-card-text>
|
||||
<MsgContent :content="conversation.message" />
|
||||
</v-card-text>
|
||||
|
||||
@@ -84,10 +84,11 @@ export default defineNuxtPlugin(() => {
|
||||
'refresh': refreshToken.value
|
||||
}
|
||||
})
|
||||
console.log('refresh', data, error)
|
||||
if (!error.value) {
|
||||
token.value = data.value.access
|
||||
return data.value.access
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
async callback () {
|
||||
@@ -105,9 +106,9 @@ export default defineNuxtPlugin(() => {
|
||||
return null
|
||||
}
|
||||
if (!token.value) {
|
||||
await this.refresh()
|
||||
return await this.refresh()
|
||||
}
|
||||
return token.value || null
|
||||
return token.value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
53
utils/helper.js
Normal file
53
utils/helper.js
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
export const getDefaultConversionData = () => {
|
||||
return {
|
||||
id: null,
|
||||
topic: null,
|
||||
messages: [],
|
||||
loadingMessages: false,
|
||||
}
|
||||
}
|
||||
|
||||
export const getConversions = async () => {
|
||||
const { data, error } = await useAuthFetch('/api/chat/conversations')
|
||||
if (!error.value) {
|
||||
return data.value
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
export const createNewConversion = () => {
|
||||
const conversation = useConversion()
|
||||
conversation.value = getDefaultConversionData()
|
||||
}
|
||||
|
||||
export const openConversationMessages = async (currentConversation) => {
|
||||
const conversation = useConversion()
|
||||
conversation.value = Object.assign(conversation.value, currentConversation)
|
||||
conversation.value.loadingMessages = true
|
||||
const { data, error } = await useAuthFetch('/api/chat/messages/?conversationId=' + currentConversation.id)
|
||||
if (!error.value) {
|
||||
conversation.value.messages = data.value
|
||||
}
|
||||
conversation.value.loadingMessages = true
|
||||
}
|
||||
|
||||
export const genTitle = async (conversationId) => {
|
||||
const { data, error } = await useAuthFetch('/api/gen_title', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
conversationId: conversationId
|
||||
}
|
||||
})
|
||||
if (!error.value) {
|
||||
const conversation = {
|
||||
id: conversationId,
|
||||
topic: data.value.title,
|
||||
}
|
||||
const conversations = useConversions()
|
||||
// prepend to conversations
|
||||
conversations.value = [conversation, ...conversations.value]
|
||||
return data.value.title
|
||||
}
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user