feat: 上限文联想改为前端进行

This commit is contained in:
ChenZhaoYu 2023-02-14 10:04:32 +08:00
parent 5d1f8519c2
commit 86fcbbff0b
6 changed files with 69 additions and 110 deletions

View File

@ -47,9 +47,8 @@
"vue-tsc": "^1.0.24"
},
"lint-staged": {
"*.{js,ts,jsx,tsx,vue}": [
"pnpm lint:fix",
"pnpm type-check"
"*.{ts,tsx,vue}": [
"pnpm lint:fix"
]
}
}

View File

@ -15,67 +15,31 @@ const apiKey = process.env.OPENAI_API_KEY
if (apiKey === undefined)
throw new Error('OPENAI_API_KEY is not defined')
const chatContext = new Set<ChatContext>()
/**
* More Info: https://github.com/transitive-bullshit/chatgpt-api
*/
const api = new ChatGPTAPI({ apiKey })
const api = new ChatGPTAPI({ apiKey, debug: false })
async function chatReply(message: string) {
async function chatReply(
message: string,
lastContext?: { conversationId?: string; parentMessageId?: string },
) {
if (!message)
return sendResponse({ type: 'Fail', message: 'Message is empty' })
try {
// Get the last context from the chat context
let options: SendMessageOptions = {}
const lastContext = Array.from(chatContext).pop()
if (lastContext)
options = { ...lastContext }
const response = await api.sendMessage(message, { ...options })
const { conversationId, id } = response
// Add the new context to the chat context
if (conversationId && id)
chatContext.add({ conversationId, parentMessageId: id })
return sendResponse({ type: 'Success', data: response })
}
catch (error: any) {
global.console.log(error)
return sendResponse({ type: 'Fail', message: error.message })
}
}
async function chatReplayOne(message: string, options?: ChatContext) {
if (!message)
return sendResponse({ type: 'Fail', message: 'Message is empty' })
try {
let messageOptions: SendMessageOptions = {}
if (options) {
const { conversationId, parentMessageId } = options
messageOptions = { conversationId, parentMessageId }
const response = await api.sendMessage(message, { ...messageOptions })
return sendResponse({ type: 'Success', data: response })
}
}
catch (error: any) {
return sendResponse({ type: 'Fail', message: error.message })
}
}
async function clearChatContext() {
// Clear the chat context
chatContext.clear()
return sendResponse({ type: 'Success', message: 'Chat context cleared' })
}
export { chatReply, chatReplayOne, clearChatContext }
export { chatReply }

View File

@ -1,35 +1,22 @@
import express from 'express'
import type { ChatContext } from './chatgpt'
import { chatReplayOne, chatReply, clearChatContext } from './chatgpt'
import { chatReply } from './chatgpt'
const app = express()
app.use(express.json())
app.all('*', (req, res, next) => {
app.all('*', (_, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Content-Type')
res.header('Access-Control-Allow-Methods', '*')
next()
})
app.listen(3002, () => globalThis.console.log('Server is running on port 3002'))
app.post('/chat', async (req, res) => {
try {
const { prompt } = req.body as { prompt: string }
const response = await chatReply(prompt)
res.send(response)
}
catch (error) {
res.send(error)
}
})
app.post('./chatOne', async (req, res) => {
try {
const { prompt, options = {} } = req.body as { prompt: string; options?: ChatContext }
const response = await chatReplayOne(prompt, options)
const response = await chatReply(prompt, options)
res.send(response)
}
catch (error) {
@ -37,7 +24,4 @@ app.post('./chatOne', async (req, res) => {
}
})
app.post('/clear', async (req, res) => {
const response = await clearChatContext()
res.send(response)
})
app.listen(3002, () => globalThis.console.log('Server is running on port 3002'))

View File

@ -1,9 +1,12 @@
import { post } from '@/utils/request'
export function fetchChatAPI<T = any>(prompt: string) {
export function fetchChatAPI<T = any>(
prompt: string,
options?: { conversationId?: string; parentMessageId?: string },
) {
return post<T>({
url: '/chat',
data: { prompt },
data: { prompt, options },
})
}

View File

@ -1,51 +1,27 @@
<script setup lang='ts'>
import { nextTick, onMounted, ref } from 'vue'
import { computed, nextTick, onMounted, ref } from 'vue'
import { NButton, NInput, useMessage } from 'naive-ui'
import type { ChatOptions, ChatProps } from './types'
import { Message } from './components'
import { Layout } from './layout'
import { clearConversations, fetchChatAPI } from '@/api'
import { fetchChatAPI } from '@/api'
import { HoverButton, SvgIcon } from '@/components/common'
interface ListProps {
dateTime: string
message: string
reversal?: boolean
error?: boolean
}
const scrollRef = ref<HTMLDivElement>()
const ms = useMessage()
const prompt = ref('')
const loading = ref(false)
const list = ref<ListProps[]>([])
onMounted(initChat)
const list = ref<ChatProps[]>([])
const chatList = computed(() => list.value.filter(item => (!item.reversal && !item.error)))
function initChat() {
addMessage('Hi, I am ChatGPT, a chatbot based on GPT-3.', false)
addMessage('Hi, I am ChatGPT, a chatbot based on GPT-3.')
}
async function handleClear() {
try {
await clearConversations()
}
catch (error) {
ms.error('Clear failed, please try again later.')
}
finally {
list.value = []
setTimeout(initChat, 100)
}
}
function handleEnter(event: KeyboardEvent) {
if (event.key === 'Enter')
handleSubmit()
}
onMounted(initChat)
async function handleSubmit() {
if (loading.value)
@ -58,26 +34,51 @@ async function handleSubmit() {
return
}
addMessage(message, true)
addMessage(message, { reversal: true })
prompt.value = ''
let options: ChatOptions = {}
const lastContext = chatList.value[chatList.value.length - 1]?.options
if (lastContext)
options = { ...lastContext }
try {
loading.value = true
const { data } = await fetchChatAPI(message)
addMessage(data?.text ?? '', false)
const { data } = await fetchChatAPI(message, options)
addMessage(data?.text ?? '', { options: { conversationId: data.conversationId, parentMessageId: data.id } })
}
catch (error: any) {
addMessage(`Error: ${error.message ?? 'Request failed, please try again later.'}`, false, true)
addMessage(`Error: ${error.message ?? 'Request failed, please try again later.'}`, { error: true })
}
finally {
loading.value = false
}
}
function addMessage(message: string, reversal = false, error = false) {
list.value.push({ dateTime: new Date().toLocaleString(), message, reversal, error })
function handleEnter(event: KeyboardEvent) {
if (event.key === 'Enter')
handleSubmit()
}
function addMessage(
message: string,
args?: { reversal?: boolean; error?: boolean; options?: ChatOptions },
) {
list.value.push({
dateTime: new Date().toLocaleString(),
message,
reversal: args?.reversal ?? false,
error: args?.error ?? false,
options: args?.options ?? undefined,
})
nextTick(() => scrollRef.value && (scrollRef.value.scrollTop = scrollRef.value.scrollHeight))
}
function handleClear() {
list.value = []
setTimeout(initChat, 100)
}
</script>
<template>
@ -87,12 +88,8 @@ function addMessage(message: string, reversal = false, error = false) {
<div ref="scrollRef" class="h-full p-4 overflow-hidden overflow-y-auto">
<div>
<Message
v-for="(item, index) of list"
:key="index"
:date-time="item.dateTime"
:message="item.message"
:reversal="item.reversal"
:error="item.error"
v-for="(item, index) of list" :key="index" :date-time="item.dateTime" :message="item.message"
:reversal="item.reversal" :error="item.error"
/>
</div>
</div>

View File

@ -0,0 +1,12 @@
export interface ChatOptions {
conversationId?: string
parentMessageId?: string
}
export interface ChatProps {
dateTime: string
message: string
reversal?: boolean
error?: boolean
options?: ChatOptions
}