Merge pull request #21 from Chanzhaoyu/dev

v2.1.0
This commit is contained in:
Redon 2023-02-14 10:05:21 +08:00 committed by GitHub
commit c933a851f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 76 additions and 116 deletions

View File

@ -2,12 +2,12 @@
"name": "chatgpt-web", "name": "chatgpt-web",
"version": "2.0.0", "version": "2.0.0",
"private": false, "private": false,
"description": "ChatGPT Web Bot", "description": "ChatGPT Web",
"author": "ChenZhaoYu <chenzhaoyu1994@gami.com>", "author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
"keywords": [ "keywords": [
"chatgpt-web",
"chatgpt", "chatgpt",
"chatbot", "chatbot",
"web",
"vue" "vue"
], ],
"scripts": { "scripts": {
@ -47,9 +47,8 @@
"vue-tsc": "^1.0.24" "vue-tsc": "^1.0.24"
}, },
"lint-staged": { "lint-staged": {
"*.{js,ts,jsx,tsx,vue}": [ "*.{ts,tsx,vue}": [
"pnpm lint:fix", "pnpm lint:fix"
"pnpm type-check"
] ]
} }
} }

View File

@ -2,12 +2,13 @@
"name": "chatgpt-web-service", "name": "chatgpt-web-service",
"version": "1.0.0", "version": "1.0.0",
"private": false, "private": false,
"description": "ChatGPT Web Bot node service", "description": "ChatGPT Web Service",
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
"keywords": [ "keywords": [
"chatgpt-web",
"chatgpt", "chatgpt",
"chatbot", "chatbot",
"web", "express"
"vue"
], ],
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"

View File

@ -15,67 +15,31 @@ const apiKey = process.env.OPENAI_API_KEY
if (apiKey === undefined) if (apiKey === undefined)
throw new Error('OPENAI_API_KEY is not defined') throw new Error('OPENAI_API_KEY is not defined')
const chatContext = new Set<ChatContext>()
/** /**
* More Info: https://github.com/transitive-bullshit/chatgpt-api * 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) if (!message)
return sendResponse({ type: 'Fail', message: 'Message is empty' }) return sendResponse({ type: 'Fail', message: 'Message is empty' })
try { try {
// Get the last context from the chat context
let options: SendMessageOptions = {} let options: SendMessageOptions = {}
const lastContext = Array.from(chatContext).pop()
if (lastContext) if (lastContext)
options = { ...lastContext } options = { ...lastContext }
const response = await api.sendMessage(message, { ...options }) 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 }) 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) { catch (error: any) {
return sendResponse({ type: 'Fail', message: error.message }) return sendResponse({ type: 'Fail', message: error.message })
} }
} }
async function clearChatContext() { export { chatReply }
// Clear the chat context
chatContext.clear()
return sendResponse({ type: 'Success', message: 'Chat context cleared' })
}
export { chatReply, chatReplayOne, clearChatContext }

View File

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

View File

@ -1,9 +1,12 @@
import { post } from '@/utils/request' 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>({ return post<T>({
url: '/chat', url: '/chat',
data: { prompt }, data: { prompt, options },
}) })
} }

View File

@ -1,51 +1,27 @@
<script setup lang='ts'> <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 { NButton, NInput, useMessage } from 'naive-ui'
import type { ChatOptions, ChatProps } from './types'
import { Message } from './components' import { Message } from './components'
import { Layout } from './layout' import { Layout } from './layout'
import { clearConversations, fetchChatAPI } from '@/api' import { fetchChatAPI } from '@/api'
import { HoverButton, SvgIcon } from '@/components/common' import { HoverButton, SvgIcon } from '@/components/common'
interface ListProps {
dateTime: string
message: string
reversal?: boolean
error?: boolean
}
const scrollRef = ref<HTMLDivElement>() const scrollRef = ref<HTMLDivElement>()
const ms = useMessage() const ms = useMessage()
const prompt = ref('') const prompt = ref('')
const loading = ref(false) const loading = ref(false)
const list = ref<ListProps[]>([]) const list = ref<ChatProps[]>([])
const chatList = computed(() => list.value.filter(item => (!item.reversal && !item.error)))
onMounted(initChat)
function initChat() { 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() { onMounted(initChat)
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()
}
async function handleSubmit() { async function handleSubmit() {
if (loading.value) if (loading.value)
@ -58,26 +34,51 @@ async function handleSubmit() {
return return
} }
addMessage(message, true) addMessage(message, { reversal: true })
prompt.value = '' prompt.value = ''
let options: ChatOptions = {}
const lastContext = chatList.value[chatList.value.length - 1]?.options
if (lastContext)
options = { ...lastContext }
try { try {
loading.value = true loading.value = true
const { data } = await fetchChatAPI(message) const { data } = await fetchChatAPI(message, options)
addMessage(data?.text ?? '', false) addMessage(data?.text ?? '', { options: { conversationId: data.conversationId, parentMessageId: data.id } })
} }
catch (error: any) { 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 { finally {
loading.value = false loading.value = false
} }
} }
function addMessage(message: string, reversal = false, error = false) { function handleEnter(event: KeyboardEvent) {
list.value.push({ dateTime: new Date().toLocaleString(), message, reversal, error }) 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)) nextTick(() => scrollRef.value && (scrollRef.value.scrollTop = scrollRef.value.scrollHeight))
} }
function handleClear() {
list.value = []
setTimeout(initChat, 100)
}
</script> </script>
<template> <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 ref="scrollRef" class="h-full p-4 overflow-hidden overflow-y-auto">
<div> <div>
<Message <Message
v-for="(item, index) of list" v-for="(item, index) of list" :key="index" :date-time="item.dateTime" :message="item.message"
:key="index" :reversal="item.reversal" :error="item.error"
:date-time="item.dateTime"
:message="item.message"
:reversal="item.reversal"
:error="item.error"
/> />
</div> </div>
</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
}