commit
c933a851f4
11
package.json
11
package.json
|
@ -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"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 }
|
|
||||||
|
|
|
@ -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)
|
|
||||||
})
|
|
||||||
|
|
|
@ -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 },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export interface ChatOptions {
|
||||||
|
conversationId?: string
|
||||||
|
parentMessageId?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatProps {
|
||||||
|
dateTime: string
|
||||||
|
message: string
|
||||||
|
reversal?: boolean
|
||||||
|
error?: boolean
|
||||||
|
options?: ChatOptions
|
||||||
|
}
|
Loading…
Reference in New Issue