Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98e75c0c5c | ||
|
|
06fd5a861c | ||
|
|
fc0d2073aa | ||
|
|
2c816afbd2 | ||
|
|
1bf2399a45 | ||
|
|
ddec57f562 | ||
|
|
63af4a8e97 | ||
|
|
a5a67f688e | ||
|
|
692d37ec35 | ||
|
|
4b16560958 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,3 +1,21 @@
|
|||||||
|
## v1.0.5
|
||||||
|
|
||||||
|
`2023-02-12`
|
||||||
|
|
||||||
|
### Enhancement
|
||||||
|
- 输入框焦点,连续提交
|
||||||
|
|
||||||
|
### BugFix
|
||||||
|
- 修复信息框样式问题
|
||||||
|
- 修复中文输入法提交问题
|
||||||
|
|
||||||
|
## v1.0.4
|
||||||
|
|
||||||
|
`2023-02-11`
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
- 支持上下文联想
|
||||||
|
|
||||||
## v1.0.3
|
## v1.0.3
|
||||||
|
|
||||||
`2023-02-11`
|
`2023-02-11`
|
||||||
|
|||||||
39
README.en.md
Normal file
39
README.en.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# ChatGPT Web Bot
|
||||||
|
|
||||||
|
[中文](./README.md) | English
|
||||||
|
|
||||||
|
ChartGPT demo page built with express and vue3
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Usage
|
||||||
|
> Make sure `node >= 18`
|
||||||
|
|
||||||
|
If pnpm is not installed
|
||||||
|
```shell
|
||||||
|
npm install pnpm -g
|
||||||
|
```
|
||||||
|
|
||||||
|
install node deps
|
||||||
|
```shell
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Sign up for an [OpenAI API key](https://platform.openai.com/overview) and store it in your environment.
|
||||||
|
|
||||||
|
```
|
||||||
|
# .env
|
||||||
|
OPENAI_API_KEY="Your Key"
|
||||||
|
```
|
||||||
|
|
||||||
|
Run service
|
||||||
|
```shell
|
||||||
|
pnpm run service
|
||||||
|
```
|
||||||
|
|
||||||
|
Run web
|
||||||
|
```shell
|
||||||
|
pnpm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
19
README.md
19
README.md
@@ -1,36 +1,37 @@
|
|||||||
# ChatGPT Web Bot
|
# ChatGPT Web Bot
|
||||||
|
|
||||||
English | [中文](./README.zh-CN.md)
|
中文 | [English](./README.en.md)
|
||||||
|
|
||||||
ChartGPT demo page built with express and vue3
|
使用 express 和 vue3 搭建的 ChartGPT 演示网页
|
||||||
|
|
||||||

|

|
||||||
## Usage
|
|
||||||
> Make sure `node >= 18`
|
|
||||||
|
|
||||||
If pnpm is not installed
|
## 使用
|
||||||
|
> 确保 `node >= 18`
|
||||||
|
|
||||||
|
如果你没有安装过 `pnpm`
|
||||||
```shell
|
```shell
|
||||||
npm install pnpm -g
|
npm install pnpm -g
|
||||||
```
|
```
|
||||||
|
|
||||||
install node deps
|
安装依赖
|
||||||
```shell
|
```shell
|
||||||
pnpm install
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
Sign up for an [OpenAI API key](https://platform.openai.com/overview) and store it in your environment.
|
获取 [OpenAI API key](https://platform.openai.com/overview) 到本地环境变量
|
||||||
|
|
||||||
```
|
```
|
||||||
# .env
|
# .env
|
||||||
OPENAI_API_KEY="Your Key"
|
OPENAI_API_KEY="Your Key"
|
||||||
```
|
```
|
||||||
|
|
||||||
Run service
|
运行服务
|
||||||
```shell
|
```shell
|
||||||
pnpm run service
|
pnpm run service
|
||||||
```
|
```
|
||||||
|
|
||||||
Run web
|
运行网页
|
||||||
```shell
|
```shell
|
||||||
pnpm run dev
|
pnpm run dev
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
# ChatGPT Web Bot
|
|
||||||
|
|
||||||
[English](./README.md) | 中文
|
|
||||||
|
|
||||||
使用 express 和 vue3 搭建的 ChartGPT 演示网页
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 使用
|
|
||||||
> Make sure `node >= 18`
|
|
||||||
|
|
||||||
如果你没有安装过 `pnpm`
|
|
||||||
```shell
|
|
||||||
npm install pnpm -g
|
|
||||||
```
|
|
||||||
|
|
||||||
安装依赖
|
|
||||||
```shell
|
|
||||||
pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
获取 [OpenAI API key](https://platform.openai.com/overview) 到本地环境变量
|
|
||||||
|
|
||||||
```
|
|
||||||
# .env
|
|
||||||
OPENAI_API_KEY="Your Key"
|
|
||||||
```
|
|
||||||
|
|
||||||
运行服务
|
|
||||||
```shell
|
|
||||||
pnpm run service
|
|
||||||
```
|
|
||||||
|
|
||||||
运行网页
|
|
||||||
```shell
|
|
||||||
pnpm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
MIT © [ChenZhaoYu](./license)
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "chatgpt-web",
|
"name": "chatgpt-web",
|
||||||
"version": "1.0.3",
|
"version": "1.0.5",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "ChatGPT Web Bot",
|
"description": "ChatGPT Web Bot",
|
||||||
"author": "ChenZhaoYu <chenzhaoyu1994@gami.com>",
|
"author": "ChenZhaoYu <chenzhaoyu1994@gami.com>",
|
||||||
|
|||||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -1412,8 +1412,8 @@ packages:
|
|||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/ci-info/3.7.1:
|
/ci-info/3.8.0:
|
||||||
resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
|
resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -2030,7 +2030,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@babel/helper-validator-identifier': 7.19.1
|
'@babel/helper-validator-identifier': 7.19.1
|
||||||
'@eslint-community/eslint-utils': 4.1.2_eslint@8.34.0
|
'@eslint-community/eslint-utils': 4.1.2_eslint@8.34.0
|
||||||
ci-info: 3.7.1
|
ci-info: 3.8.0
|
||||||
clean-regexp: 1.0.0
|
clean-regexp: 1.0.0
|
||||||
eslint: 8.34.0
|
eslint: 8.34.0
|
||||||
esquery: 1.4.0
|
esquery: 1.4.0
|
||||||
|
|||||||
@@ -1,19 +1,54 @@
|
|||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
import { ChatGPTAPI } from 'chatgpt'
|
import { ChatGPTAPI } from 'chatgpt'
|
||||||
|
|
||||||
|
interface ChatContext {
|
||||||
|
conversationId?: string
|
||||||
|
parentMessageId?: string
|
||||||
|
}
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const apiKey = ''
|
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
|
* More Info: https://github.com/transitive-bullshit/chatgpt-api
|
||||||
*/
|
*/
|
||||||
const api = new ChatGPTAPI({ apiKey: process.env.OPENAI_API_KEY || apiKey })
|
const api = new ChatGPTAPI({ apiKey })
|
||||||
|
|
||||||
async function chatReply(message: string) {
|
async function chatReply(message: string) {
|
||||||
if (!message)
|
if (!message)
|
||||||
return
|
return
|
||||||
return await api.sendMessage(message)
|
|
||||||
|
// Get the last context from the chat context
|
||||||
|
// If there is a last context, add it to the options
|
||||||
|
let options = {}
|
||||||
|
const lastContext = Array.from(chatContext).pop()
|
||||||
|
if (lastContext) {
|
||||||
|
const { conversationId, parentMessageId } = lastContext
|
||||||
|
options = { conversationId, parentMessageId }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the message to the API
|
||||||
|
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 response
|
||||||
}
|
}
|
||||||
|
|
||||||
export { chatReply }
|
async function clearChatContext() {
|
||||||
|
// Clear the chat context
|
||||||
|
chatContext.clear()
|
||||||
|
return Promise.resolve({ message: 'Chat context cleared' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export { chatReply, clearChatContext }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { chatReply } from './chatgpt'
|
import { chatReply, clearChatContext } from './chatgpt'
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
@@ -19,3 +19,8 @@ app.post('/chat', async (req, res) => {
|
|||||||
const response = await chatReply(message)
|
const response = await chatReply(message)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.post('/clear', async (req, res) => {
|
||||||
|
const response = await clearChatContext()
|
||||||
|
res.send(response)
|
||||||
|
})
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ defineProps<Props>()
|
|||||||
>
|
>
|
||||||
<Avatar :image="reversal" />
|
<Avatar :image="reversal" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col flex-1 text-sm" :class="[{ 'items-end': reversal }]">
|
<div class="flex flex-col flex-1 text-sm" :class="[reversal ? 'items-end' : 'items-start']">
|
||||||
<span class="text-xs text-[#b4bbc4]">
|
<span class="text-xs text-[#b4bbc4]">
|
||||||
{{ dateTime }}
|
{{ dateTime }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { nextTick, onMounted, ref } from 'vue'
|
import { nextTick, onMounted, ref } from 'vue'
|
||||||
import { NButton, NInput, NPopover, useMessage } from 'naive-ui'
|
import { NButton, NInput, NPopover, useMessage } from 'naive-ui'
|
||||||
import { Message } from './components'
|
import { Message } from './components'
|
||||||
import { fetchChatAPI } from './request'
|
import { clearChatContext, fetchChatAPI } from './request'
|
||||||
import { Icon } from '@/components'
|
import { Icon } from '@/components'
|
||||||
|
|
||||||
interface ListProps {
|
interface ListProps {
|
||||||
@@ -15,7 +15,7 @@ const scrollRef = ref<HTMLDivElement>()
|
|||||||
|
|
||||||
const ms = useMessage()
|
const ms = useMessage()
|
||||||
|
|
||||||
const value = ref('')
|
const prompt = ref('')
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
@@ -27,9 +27,16 @@ 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.', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClear() {
|
async function handleClear() {
|
||||||
list.value = []
|
try {
|
||||||
setTimeout(initChat, 100)
|
const { message } = await clearChatContext()
|
||||||
|
ms.success(message)
|
||||||
|
list.value = []
|
||||||
|
setTimeout(initChat, 100)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
ms.error('Clear failed, please try again later.')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEnter(event: KeyboardEvent) {
|
function handleEnter(event: KeyboardEvent) {
|
||||||
@@ -38,17 +45,19 @@ function handleEnter(event: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (!value.value) {
|
const message = prompt.value.trim()
|
||||||
|
|
||||||
|
if (!message || !message.length) {
|
||||||
ms.warning('Please enter a message')
|
ms.warning('Please enter a message')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
addMessage(value.value, true)
|
addMessage(message, true)
|
||||||
|
prompt.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const { text } = await fetchChatAPI(value.value)
|
const { text } = await fetchChatAPI(message)
|
||||||
value.value = ''
|
|
||||||
addMessage(text, false)
|
addMessage(text, false)
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
catch (error: any) {
|
||||||
@@ -81,7 +90,7 @@ function addMessage(message: string, reversal = false) {
|
|||||||
<Icon icon="ri:delete-bin-6-line" />
|
<Icon icon="ri:delete-bin-6-line" />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<span>Clear</span>
|
<span>Clear Context</span>
|
||||||
</NPopover>
|
</NPopover>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -97,7 +106,7 @@ function addMessage(message: string, reversal = false) {
|
|||||||
</main>
|
</main>
|
||||||
<footer class="p-4">
|
<footer class="p-4">
|
||||||
<div class="flex items-center justify-between space-x-2">
|
<div class="flex items-center justify-between space-x-2">
|
||||||
<NInput v-model:value="value" :disabled="loading" placeholder="Type a message..." @keyup="handleEnter" />
|
<NInput v-model:value="prompt" placeholder="Type a message..." @keypress="handleEnter" />
|
||||||
<NButton type="primary" :loading="loading" @click="handleSubmit">
|
<NButton type="primary" :loading="loading" @click="handleSubmit">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<Icon icon="ri:send-plane-fill" />
|
<Icon icon="ri:send-plane-fill" />
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
async function fetchChatAPI(message: string) {
|
const BASE_URL = import.meta.env.VITE_GLOB_API_URL
|
||||||
const url = `${import.meta.env.VITE_GLOB_API_URL}/chat`
|
|
||||||
|
|
||||||
|
async function fetchChatAPI(message: string) {
|
||||||
if (!message || message.trim() === '')
|
if (!message || message.trim() === '')
|
||||||
return
|
return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, data } = await axios.post(url, { message })
|
const { status, data } = await axios.post(`${BASE_URL}/chat`, { message })
|
||||||
|
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
if (data.text)
|
if (data.text)
|
||||||
@@ -24,4 +24,18 @@ async function fetchChatAPI(message: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { fetchChatAPI }
|
async function clearChatContext() {
|
||||||
|
try {
|
||||||
|
const { status, data } = await axios.post(`${BASE_URL}/clear`)
|
||||||
|
|
||||||
|
if (status === 200)
|
||||||
|
return Promise.resolve(data)
|
||||||
|
|
||||||
|
return Promise.reject(new Error('Request failed'))
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { fetchChatAPI, clearChatContext }
|
||||||
|
|||||||
Reference in New Issue
Block a user