Improve the conversation process
This commit is contained in:
15
Dockerfile
15
Dockerfile
@@ -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"]
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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
18
composables/fetch.js
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
16
docker-compose.test.yml
Normal 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
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// },
|
||||
})
|
||||
|
||||
@@ -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>
|
||||
10
server/middleware/apiProxy.ts
Normal file
10
server/middleware/apiProxy.ts
Normal 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
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user