Improve the conversation process

This commit is contained in:
Rafi
2023-04-04 19:16:07 +08:00
parent 16c9b0e230
commit 3e3283029d
14 changed files with 152 additions and 99 deletions

View File

@@ -1,5 +1,7 @@
FROM node:18-alpine3.16 as builder
ENV NITRO_PORT=80
WORKDIR /app
COPY package.json yarn.lock ./
@@ -8,15 +10,8 @@ RUN yarn install
COPY . .
RUN yarn generate
FROM nginx:alpine
WORKDIR /app
COPY --from=builder /app/.output/public .
COPY nginx.conf /etc/nginx/templates/default.conf.template
RUN yarn build
EXPOSE 80
ENTRYPOINT ["node", ".output/server/index.mjs"]

View File

@@ -1,6 +1,5 @@
<script setup>
import {EventStreamContentType, fetchEventSource} from '@microsoft/fetch-event-source'
import {addConversation} from "../utils/helper";
const { $i18n } = useNuxtApp()
const runtimeConfig = useRuntimeConfig()
@@ -26,6 +25,8 @@ const processMessageQueue = () => {
}
isProcessingQueue = true
const nextMessage = messageQueue.shift()
console.log(runtimeConfig.public.typewriter)
console.log(process.evn.NUXT_PUBLIC_TYPEWRITER)
if (runtimeConfig.public.typewriter) {
let wordIndex = 0;
const intervalId = setInterval(() => {
@@ -108,12 +109,12 @@ const fetchReply = async (message) => {
}
if (event === 'done') {
if (props.conversation.id === null) {
abortFetch()
props.conversation.messages[props.conversation.messages.length - 1].id = data.messageId
if (!props.conversation.topic || props.conversation.topic === '') {
props.conversation.id = data.conversationId
genTitle(props.conversation.id)
}
props.conversation.messages[props.conversation.messages.length - 1].id = data.messageId
abortFetch()
return;
}
@@ -190,53 +191,54 @@ watchEffect(() => {
</script>
<template>
<div
v-if="conversation.loadingMessages"
class="text-center"
>
<v-progress-circular
indeterminate
color="primary"
></v-progress-circular>
</div>
<div v-else>
<div v-if="conversation">
<div
v-if="conversation.messages.length > 0"
ref="chatWindow"
v-if="conversation.loadingMessages"
class="text-center"
>
<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>
<v-progress-circular
indeterminate
color="primary"
></v-progress-circular>
</div>
<div v-else>
<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>
</div>
<Welcome v-if="conversation.id === null && conversation.messages.length === 0" />
</div>

View File

@@ -4,10 +4,9 @@
<v-col cols="12">
<div class="text-center">
<h2 class="text-h2">{{ $t('welcomeTo') }} <span class="text-primary">{{ runtimeConfig.public.appName }}</span></h2>
<p class="text-caption mt-5">
<p class="text-caption my-5">
{{ runtimeConfig.public.appName }} {{ $t('welcomeScreen.introduction1') }}
<br>
{{ $t('welcomeScreen.introduction2') }}
</p>
</div>
</v-col>

18
composables/fetch.js Normal file
View File

@@ -0,0 +1,18 @@
export const useMyFetch = (url, options = {}) => {
let defaultOptions = {
headers: {
Accept: 'application/json',
}
}
if (process.server) {
defaultOptions.baseURL = process.env.SERVER_DOMAIN
}
return useFetch(url, Object.assign(defaultOptions, options))
}
export const useAuthFetch = async (url, options = {}) => {
const res = await useMyFetch(url, options)
if (res.error.value && res.error.value.status === 401) {
await logout()
}
return res
}

View File

@@ -9,6 +9,6 @@ export const useConversation = () => useState('conversation', () => getDefaultCo
export const useConversations = () => useState('conversations', () => [])
export const useSettings = () => useState('settings', () => {})
export const useSettings = () => useState('settings', () => getSystemSettings())
export const useUser = () => useState('user', () => null)

View File

@@ -1,8 +0,0 @@
export const useAuthFetch = async (url, options = {}) => {
const res = await useFetch(url, options)
if (res.error.value && res.error.value.status === 401) {
await logout()
}
return res
}

View File

@@ -4,13 +4,13 @@ services:
platform: linux/x86_64
build: .
environment:
SERVER_DOMAIN: http://web-server
SERVER_DOMAIN: ${SERVER_DOMAIN:-http://web-server}
ports:
- '${CLIENT_PORT:-8080}:80'
- '${CLIENT_PORT:-80}:80'
networks:
- chatgpt_network
restart: always
networks:
chatgpt_network:
external: True
driver: bridge

16
docker-compose.test.yml Normal file
View File

@@ -0,0 +1,16 @@
version: '3'
services:
client:
platform: linux/x86_64
build: .
environment:
SERVER_DOMAIN: ${SERVER_DOMAIN:-http://web-server}
ports:
- '${CLIENT_PORT:-80}:80'
networks:
- chatgpt_network
restart: always
networks:
chatgpt_network:
driver: bridge

View File

@@ -110,9 +110,15 @@ watchEffect(() => {
const user = useUser()
onMounted(async () => {
const navTitle = computed(() => {
if (currentConversation.value && currentConversation.value.topic !== null) {
return currentConversation.value.topic === '' ? $i18n.t('defaultConversationTitle') : currentConversation.value.topic
}
return runtimeConfig.public.appName
})
onNuxtReady(async () => {
loadConversations()
loadSettings()
})
</script>
@@ -213,7 +219,7 @@ onMounted(async () => {
:to="conversation.id ? `/${conversation.id}` : undefined"
v-bind="props"
>
<v-list-item-title>{{ conversation.topic !== "" ? conversation.topic : $t('defaultConversationTitle') }}</v-list-item-title>
<v-list-item-title>{{ (conversation.topic && conversation.topic !== '') ? conversation.topic : $t('defaultConversationTitle') }}</v-list-item-title>
<template v-slot:append>
<div
v-show="isHovering && conversation.id"
@@ -334,7 +340,7 @@ onMounted(async () => {
>
<v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
<v-toolbar-title>{{ currentConversation.id ? currentConversation.topic : runtimeConfig.public.appName }}</v-toolbar-title>
<v-toolbar-title>{{ navTitle }}</v-toolbar-title>
<v-spacer></v-spacer>

View File

@@ -1,8 +1,5 @@
export default defineNuxtRouteMiddleware(async (to, from) => {
// skip middleware on server
if (process.server) return
const user = useUser()
const signInPath = '/account/signin'
if (!user.value && to.path !== signInPath) {

View File

@@ -28,7 +28,7 @@ export default defineNuxtConfig({
modules: [
'@kevinmarrec/nuxt-pwa',
'@nuxtjs/color-mode',
'@nuxtjs/i18n',
'@nuxtjs/i18n'
],
pwa: {
manifest: {
@@ -69,13 +69,13 @@ export default defineNuxtConfig({
fallbackLocale: 'en',
},
},
nitro: {
devProxy: {
"/api": {
target: process.env.NUXT_DEV_SERVER ?? 'http://localhost:8000/api',
changeOrigin: true,
}
}
},
// nitro: {
// devProxy: {
// "/api": {
// target: process.env.NUXT_DEV_SERVER ?? 'http://localhost:8000/api',
// changeOrigin: true,
// }
//
// }
// },
})

View File

@@ -22,14 +22,18 @@ const loadMessage = async () => {
}
}
const updateCurrentConversation = () => {
currentConversation.value = Object.assign({}, conversation.value)
}
onMounted(async () => {
if (route.params.id) {
conversation.value.loadingMessages = true
await loadConversation()
await loadMessage()
conversation.value.loadingMessages = false
updateCurrentConversation()
} else {
conversation.value = getDefaultConversationData()
watch(currentConversation, (val) => {
conversation.value = Object.assign({}, val)
})
@@ -37,11 +41,12 @@ onMounted(async () => {
})
onActivated(async () => {
currentConversation.value = Object.assign({}, conversation.value)
updateCurrentConversation()
})
</script>
<template>
<Conversation :conversation="conversation" />
<Welcome v-if="!route.params.id && conversation.messages.length === 0" />
<Conversation :conversation="conversation" />
</template>

View File

@@ -0,0 +1,10 @@
import { createProxyMiddleware, Filter, Options, RequestHandler } from 'http-proxy-middleware'
export default defineEventHandler((event) => {
// @ts-ignore
if (event.node.req.url.startsWith('/api/')) {
return proxyRequest(
event,
(process.env.SERVER_DOMAIN || 'http://localhost:8000') + event.node.req.url
)
}
})

View File

@@ -3,7 +3,7 @@ export const getDefaultConversationData = () => {
const { $i18n } = useNuxtApp()
return {
id: null,
topic: $i18n.t('defaultConversationTitle'),
topic: null,
messages: [],
loadingMessages: false,
}
@@ -18,7 +18,15 @@ export const getConversations = async () => {
}
export const createNewConversation = () => {
navigateTo('/')
const route = useRoute()
const { $i18n } = useNuxtApp()
const currentConversation = useConversation()
currentConversation.value = Object.assign(getDefaultConversationData(), {
topic: $i18n.t('newConversation')
})
if (route.path !== '/') {
return navigateTo('/')
}
}
@@ -38,12 +46,17 @@ export const genTitle = async (conversationId) => {
}
})
if (!error.value) {
const route = useRoute()
const conversations = useConversations()
const currentConversation = useConversation()
let index = conversations.value.findIndex(item => item.id === conversationId)
if (index === -1) {
index = 0
}
conversations.value[index].topic = data.value.title
if (route.path === '/') {
currentConversation.value.topic = data.value.title
}
return data.value.title
}
return null
@@ -58,18 +71,18 @@ const transformData = (list) => {
return result;
}
export const loadSettings = async () => {
const settings = useSettings()
export const getSystemSettings = async () => {
const { data, error } = await useAuthFetch('/api/chat/settings/', {
method: 'GET'
method: 'GET',
})
if (!error.value) {
settings.value = transformData(data.value)
return transformData(data.value)
}
return {}
}
export const fetchUser = async () => {
return useFetch('/api/account/user/')
return useMyFetch('/api/account/user/')
}
export const setUser = (userData) => {