Multiple improvements for conversation
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {EventStreamContentType, fetchEventSource} from '@microsoft/fetch-event-source'
|
import {EventStreamContentType, fetchEventSource} from '@microsoft/fetch-event-source'
|
||||||
|
import {addConversation} from "../utils/helper";
|
||||||
|
|
||||||
const { $i18n, $auth } = useNuxtApp()
|
const { $i18n, $auth } = useNuxtApp()
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
@@ -137,9 +138,17 @@ const scrollChatWindow = () => {
|
|||||||
grab.value.scrollIntoView({behavior: 'smooth'})
|
grab.value.scrollIntoView({behavior: 'smooth'})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkOrAddConversation = () => {
|
||||||
|
if (props.conversation.messages.length === 0) {
|
||||||
|
props.conversation.messages.push({id: null, is_bot: true, message: ''})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const send = (message) => {
|
const send = (message) => {
|
||||||
fetchingResponse.value = true
|
fetchingResponse.value = true
|
||||||
|
if (props.conversation.messages.length === 0) {
|
||||||
|
addConversation(props.conversation)
|
||||||
|
}
|
||||||
props.conversation.messages.push({message: message})
|
props.conversation.messages.push({message: message})
|
||||||
fetchReply(message)
|
fetchReply(message)
|
||||||
scrollChatWindow()
|
scrollChatWindow()
|
||||||
@@ -182,42 +191,55 @@ watchEffect(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="conversation.messages"
|
v-if="conversation.loadingMessages"
|
||||||
ref="chatWindow"
|
class="text-center"
|
||||||
>
|
>
|
||||||
<v-container>
|
<v-progress-circular
|
||||||
<v-row>
|
indeterminate
|
||||||
<v-col
|
color="primary"
|
||||||
v-for="(message, index) in conversation.messages" :key="index"
|
></v-progress-circular>
|
||||||
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>
|
</div>
|
||||||
<Welcome v-else />
|
<div v-else>
|
||||||
|
<div
|
||||||
|
v-if="conversation.messages.length > 0"
|
||||||
|
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-if="conversation.id === null && conversation.messages.length === 0" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<v-footer app>
|
<v-footer app>
|
||||||
<div class="px-md-16 w-100 d-flex flex-column">
|
<div class="px-md-16 w-100 d-flex flex-column">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"themeMode": "Theme Mode",
|
"themeMode": "Theme Mode",
|
||||||
"feedback": "Feedback",
|
"feedback": "Feedback",
|
||||||
"newConversation": "New conversation",
|
"newConversation": "New conversation",
|
||||||
|
"defaultConversationTitle": "Unnamed",
|
||||||
"clearConversations": "Clear conversations",
|
"clearConversations": "Clear conversations",
|
||||||
"modelParameters": "Model Parameters",
|
"modelParameters": "Model Parameters",
|
||||||
"model": "Model",
|
"model": "Model",
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
"signOut": "Sign out",
|
"signOut": "Sign out",
|
||||||
"webSearch": "Web Search",
|
"webSearch": "Web Search",
|
||||||
"webSearchDefaultPrompt": "Web search results:\n\n[web_results]\nCurrent date: [current_date]\n\nInstructions: Using the provided web search results, write a comprehensive reply to the given query. Make sure to cite results using [[number](URL)] notation after the reference. If the provided search results refer to multiple subjects with the same name, write separate answers for each subject.\nQuery: [query]",
|
"webSearchDefaultPrompt": "Web search results:\n\n[web_results]\nCurrent date: [current_date]\n\nInstructions: Using the provided web search results, write a comprehensive reply to the given query. Make sure to cite results using [[number](URL)] notation after the reference. If the provided search results refer to multiple subjects with the same name, write separate answers for each subject.\nQuery: [query]",
|
||||||
|
"genTitlePrompt": "Generate a short title for the following content, no more than 10 words. \n\nContent: ",
|
||||||
"welcomeScreen": {
|
"welcomeScreen": {
|
||||||
"introduction1": "is an unofficial client for ChatGPT, but uses the official OpenAI API.",
|
"introduction1": "is an unofficial client for ChatGPT, but uses the official OpenAI API.",
|
||||||
"introduction2": "You will need an OpenAI API Key before you can use this client.",
|
"introduction2": "You will need an OpenAI API Key before you can use this client.",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"themeMode": "Тема",
|
"themeMode": "Тема",
|
||||||
"feedback": "Обратная связь",
|
"feedback": "Обратная связь",
|
||||||
"newConversation": "Новый чат",
|
"newConversation": "Новый чат",
|
||||||
|
"defaultConversationTitle": "Безымянный",
|
||||||
"clearConversations": "Очистить чаты",
|
"clearConversations": "Очистить чаты",
|
||||||
"modelParameters": "Параметры модели",
|
"modelParameters": "Параметры модели",
|
||||||
"model": "Модель",
|
"model": "Модель",
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
"signOut": "Выход",
|
"signOut": "Выход",
|
||||||
"webSearch": "Поиск в интернете",
|
"webSearch": "Поиск в интернете",
|
||||||
"webSearchDefaultPrompt": "Результаты веб-поиска:\n\n[web_results]\nТекущая дата: [current_date]\n\nИнструкции: Используя предоставленные результаты веб-поиска, напишите развернутый ответ на заданный запрос. Обязательно цитируйте результаты, используя обозначение [[number](URL)] после ссылки. Если предоставленные результаты поиска относятся к нескольким темам с одинаковым названием, напишите отдельные ответы для каждой темы.\nЗапрос: [query]",
|
"webSearchDefaultPrompt": "Результаты веб-поиска:\n\n[web_results]\nТекущая дата: [current_date]\n\nИнструкции: Используя предоставленные результаты веб-поиска, напишите развернутый ответ на заданный запрос. Обязательно цитируйте результаты, используя обозначение [[number](URL)] после ссылки. Если предоставленные результаты поиска относятся к нескольким темам с одинаковым названием, напишите отдельные ответы для каждой темы.\nЗапрос: [query]",
|
||||||
|
"genTitlePrompt": "Придумайте короткий заголовок для следующего содержания, не более 10 слов. \n\nСодержание: ",
|
||||||
"welcomeScreen": {
|
"welcomeScreen": {
|
||||||
"introduction1": "является неофициальным клиентом для ChatGPT, но использует официальный API OpenAI.",
|
"introduction1": "является неофициальным клиентом для ChatGPT, но использует официальный API OpenAI.",
|
||||||
"introduction2": "Вам понадобится ключ API OpenAI, прежде чем вы сможете использовать этот клиент.",
|
"introduction2": "Вам понадобится ключ API OpenAI, прежде чем вы сможете использовать этот клиент.",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"themeMode": "主题模式",
|
"themeMode": "主题模式",
|
||||||
"feedback": "反馈",
|
"feedback": "反馈",
|
||||||
"newConversation": "新的对话",
|
"newConversation": "新的对话",
|
||||||
|
"defaultConversationTitle": "未命名",
|
||||||
"clearConversations": "清除对话",
|
"clearConversations": "清除对话",
|
||||||
"modelParameters": "模型参数",
|
"modelParameters": "模型参数",
|
||||||
"model": "模型",
|
"model": "模型",
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
"signOut": "退出登录",
|
"signOut": "退出登录",
|
||||||
"webSearch": "网页搜索",
|
"webSearch": "网页搜索",
|
||||||
"webSearchDefaultPrompt": "网络搜索结果:\n\n[web_results]\n当前日期:[current_date]\n\n说明:使用提供的网络搜索结果,对给定的查询写出全面的回复。确保在引用参考文献后使用 [[number](URL)] 符号进行引用结果. 如果提供的搜索结果涉及到多个具有相同名称的主题,请针对每个主题编写单独的答案。\n查询:[query]",
|
"webSearchDefaultPrompt": "网络搜索结果:\n\n[web_results]\n当前日期:[current_date]\n\n说明:使用提供的网络搜索结果,对给定的查询写出全面的回复。确保在引用参考文献后使用 [[number](URL)] 符号进行引用结果. 如果提供的搜索结果涉及到多个具有相同名称的主题,请针对每个主题编写单独的答案。\n查询:[query]",
|
||||||
|
"genTitlePrompt": "为以下内容生成一个不超过10个字的简短标题。 \n\n内容: ",
|
||||||
"welcomeScreen": {
|
"welcomeScreen": {
|
||||||
"introduction1": "是一个非官方的ChatGPT客户端,但使用OpenAI的官方API",
|
"introduction1": "是一个非官方的ChatGPT客户端,但使用OpenAI的官方API",
|
||||||
"introduction2": "在使用本客户端之前,您需要一个OpenAI API密钥。",
|
"introduction2": "在使用本客户端之前,您需要一个OpenAI API密钥。",
|
||||||
|
|||||||
@@ -54,6 +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) {
|
||||||
|
console.log('delete current conversation')
|
||||||
createNewConversation()
|
createNewConversation()
|
||||||
}
|
}
|
||||||
conversations.value.splice(index, 1)
|
conversations.value.splice(index, 1)
|
||||||
@@ -130,8 +131,8 @@ onMounted(async () => {
|
|||||||
block
|
block
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
prepend-icon="add"
|
prepend-icon="add"
|
||||||
@click="createNewConversation()"
|
|
||||||
class="text-none"
|
class="text-none"
|
||||||
|
@click="createNewConversation"
|
||||||
>
|
>
|
||||||
{{ $t('newConversation') }}
|
{{ $t('newConversation') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@@ -172,19 +173,19 @@ onMounted(async () => {
|
|||||||
<v-list-item
|
<v-list-item
|
||||||
rounded="xl"
|
rounded="xl"
|
||||||
active-color="primary"
|
active-color="primary"
|
||||||
:to="`/${conversation.id}`"
|
:to="conversation.id ? `/${conversation.id}` : undefined"
|
||||||
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>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<div
|
<div
|
||||||
v-show="isHovering"
|
v-show="isHovering && conversation.id"
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
icon="edit"
|
icon="edit"
|
||||||
size="small"
|
size="small"
|
||||||
variant="text"
|
variant="text"
|
||||||
@click.stop="editConversation(cIdx)"
|
@click.prevent="editConversation(cIdx)"
|
||||||
>
|
>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
@@ -192,7 +193,7 @@ onMounted(async () => {
|
|||||||
size="small"
|
size="small"
|
||||||
variant="text"
|
variant="text"
|
||||||
:loading="deletingConversationIndex === cIdx"
|
:loading="deletingConversationIndex === cIdx"
|
||||||
@click.stop="deleteConversation(cIdx)"
|
@click.prevent="deleteConversation(cIdx)"
|
||||||
>
|
>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,26 +1,32 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {getDefaultConversationData} from "~/utils/helper";
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ["auth"],
|
middleware: ["auth"],
|
||||||
path: '/:id?',
|
path: '/:id?',
|
||||||
keepalive: true
|
keepalive: true
|
||||||
})
|
})
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const conversation = ref({})
|
const currentConversation = useConversation()
|
||||||
|
const conversation = ref(getDefaultConversationData())
|
||||||
|
watchEffect(() => {
|
||||||
|
if (!route.params.id) {
|
||||||
|
conversation.value = getDefaultConversationData()
|
||||||
|
}
|
||||||
|
})
|
||||||
const loadMessage = async () => {
|
const loadMessage = async () => {
|
||||||
conversation.value = Object.assign(conversation.value, conversation)
|
|
||||||
conversation.value.loadingMessages = true
|
conversation.value.loadingMessages = true
|
||||||
const { data, error } = await useAuthFetch('/api/chat/messages/?conversationId=' + route.params.id)
|
const { data, error } = await useAuthFetch('/api/chat/messages/?conversationId=' + route.params.id)
|
||||||
if (!error.value) {
|
if (!error.value) {
|
||||||
conversation.value.messages = data.value
|
conversation.value.messages = data.value
|
||||||
}
|
}
|
||||||
conversation.value.loadingMessages = true
|
conversation.value.loadingMessages = false
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (route.params.id) {
|
if (route.params.id) {
|
||||||
|
conversation.value.id = parseInt(route.params.id)
|
||||||
await loadMessage()
|
await loadMessage()
|
||||||
}
|
}
|
||||||
|
currentConversation.value = Object.assign({}, conversation.value)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
|
||||||
export const getDefaultConversationData = () => {
|
export const getDefaultConversationData = () => {
|
||||||
|
const { $i18n } = useNuxtApp()
|
||||||
return {
|
return {
|
||||||
id: null,
|
id: null,
|
||||||
topic: null,
|
topic: $i18n.t('defaultConversationTitle'),
|
||||||
messages: [],
|
messages: [],
|
||||||
loadingMessages: false,
|
loadingMessages: false,
|
||||||
}
|
}
|
||||||
@@ -19,34 +20,32 @@ export const getConversations = async () => {
|
|||||||
export const createNewConversation = () => {
|
export const createNewConversation = () => {
|
||||||
const conversation = useConversation()
|
const conversation = useConversation()
|
||||||
conversation.value = getDefaultConversationData()
|
conversation.value = getDefaultConversationData()
|
||||||
|
navigateTo('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const openConversationMessages = async (currentConversation) => {
|
|
||||||
const conversation = useConversation()
|
export const addConversation = (conversation) => {
|
||||||
conversation.value = Object.assign(conversation.value, currentConversation)
|
const conversations = useConversations()
|
||||||
conversation.value.loadingMessages = true
|
conversations.value = [conversation, ...conversations.value]
|
||||||
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) => {
|
export const genTitle = async (conversationId) => {
|
||||||
|
const { $i18n } = useNuxtApp()
|
||||||
const { data, error } = await useAuthFetch('/api/gen_title/', {
|
const { data, error } = await useAuthFetch('/api/gen_title/', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
conversationId: conversationId
|
conversationId: conversationId,
|
||||||
|
prompt: $i18n.t('genTitlePrompt')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!error.value) {
|
if (!error.value) {
|
||||||
const conversation = {
|
|
||||||
id: conversationId,
|
|
||||||
topic: data.value.title,
|
|
||||||
}
|
|
||||||
const conversations = useConversations()
|
const conversations = useConversations()
|
||||||
// prepend to conversations
|
let index = conversations.value.findIndex(item => item.id === conversationId)
|
||||||
conversations.value = [conversation, ...conversations.value]
|
if (index === -1) {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
conversations.value[index].topic = data.value.title
|
||||||
return data.value.title
|
return data.value.title
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|||||||
Reference in New Issue
Block a user