Compare commits

10 Commits

Author SHA1 Message Date
ChenZhaoYu
98e75c0c5c chore: release v1.0.5 2023-02-12 10:40:22 +08:00
ChenZhaoYu
06fd5a861c chore: CHANGELOG.md 2023-02-12 10:38:17 +08:00
ChenZhaoYu
fc0d2073aa chore: 默认 MD 文件 2023-02-12 10:34:51 +08:00
ChenZhaoYu
2c816afbd2 pref: 取消 disabled 以使 input 焦点连续输入 2023-02-12 10:29:21 +08:00
ChenZhaoYu
1bf2399a45 feat: chat 回复样式错误 2023-02-12 10:28:35 +08:00
Redon
ddec57f562 Merge pull request #6 from jjm2473/patch-1
fix: 防止中文输入法输入英文时触发提交
2023-02-12 10:24:04 +08:00
练亮斌
63af4a8e97 fix: 防止中文输入法输入英文时触发提交
部分输入法支持中英文混输,输入过程中按空格选择中文,按回车把当前的英文直接上屏。

至少在Chrome浏览器中,会把输入法选英文词的回车作为keyup事件,导致未输入完成的句子被提交。改成监听keypress事件就没问题了。
其他浏览器未测试。
2023-02-11 23:51:09 +08:00
ChenZhaoYu
a5a67f688e chore: release v1.0.4 2023-02-11 15:38:57 +08:00
ChenZhaoYu
692d37ec35 chore: version 1.0.4 2023-02-11 15:38:44 +08:00
ChenZhaoYu
4b16560958 feat: 支持上下文联想 2023-02-11 15:20:58 +08:00
11 changed files with 155 additions and 74 deletions

View File

@@ -1,3 +1,21 @@
## v1.0.5
`2023-02-12`
### Enhancement
- 输入框焦点,连续提交
### BugFix
- 修复信息框样式问题
- 修复中文输入法提交问题
## v1.0.4
`2023-02-11`
### Feature
- 支持上下文联想
## v1.0.3
`2023-02-11`

39
README.en.md Normal file
View File

@@ -0,0 +1,39 @@
# ChatGPT Web Bot
[中文](./README.md) | English
ChartGPT demo page built with express and vue3
![cover](./docs/cover.png)
## 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

View File

@@ -1,36 +1,37 @@
# ChatGPT Web Bot
English | [中文](./README.zh-CN.md)
中文 | [English](./README.en.md)
ChartGPT demo page built with express and vue3
使用 express vue3 搭建的 ChartGPT 演示网页
![cover](./docs/cover.png)
## Usage
> Make sure `node >= 18`
If pnpm is not installed
## 使用
> 确保 `node >= 18`
如果你没有安装过 `pnpm`
```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.
获取 [OpenAI API key](https://platform.openai.com/overview) 到本地环境变量
```
# .env
OPENAI_API_KEY="Your Key"
```
Run service
运行服务
```shell
pnpm run service
```
Run web
运行网页
```shell
pnpm run dev
```

View File

@@ -1,40 +0,0 @@
# ChatGPT Web Bot
[English](./README.md) | 中文
使用 express 和 vue3 搭建的 ChartGPT 演示网页
![cover](./docs/cover.png)
## 使用
> 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)

View File

@@ -1,6 +1,6 @@
{
"name": "chatgpt-web",
"version": "1.0.3",
"version": "1.0.5",
"private": false,
"description": "ChatGPT Web Bot",
"author": "ChenZhaoYu <chenzhaoyu1994@gami.com>",

6
pnpm-lock.yaml generated
View File

@@ -1412,8 +1412,8 @@ packages:
fsevents: 2.3.2
dev: true
/ci-info/3.7.1:
resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
/ci-info/3.8.0:
resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
engines: {node: '>=8'}
dev: true
@@ -2030,7 +2030,7 @@ packages:
dependencies:
'@babel/helper-validator-identifier': 7.19.1
'@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
eslint: 8.34.0
esquery: 1.4.0

View File

@@ -1,19 +1,54 @@
import dotenv from 'dotenv'
import { ChatGPTAPI } from 'chatgpt'
interface ChatContext {
conversationId?: string
parentMessageId?: string
}
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
*/
const api = new ChatGPTAPI({ apiKey: process.env.OPENAI_API_KEY || apiKey })
const api = new ChatGPTAPI({ apiKey })
async function chatReply(message: string) {
if (!message)
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 }

View File

@@ -1,5 +1,5 @@
import express from 'express'
import { chatReply } from './chatgpt'
import { chatReply, clearChatContext } from './chatgpt'
const app = express()
@@ -19,3 +19,8 @@ app.post('/chat', async (req, res) => {
const response = await chatReply(message)
res.send(response)
})
app.post('/clear', async (req, res) => {
const response = await clearChatContext()
res.send(response)
})

View File

@@ -19,7 +19,7 @@ defineProps<Props>()
>
<Avatar :image="reversal" />
</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]">
{{ dateTime }}
</span>

View File

@@ -2,7 +2,7 @@
import { nextTick, onMounted, ref } from 'vue'
import { NButton, NInput, NPopover, useMessage } from 'naive-ui'
import { Message } from './components'
import { fetchChatAPI } from './request'
import { clearChatContext, fetchChatAPI } from './request'
import { Icon } from '@/components'
interface ListProps {
@@ -15,7 +15,7 @@ const scrollRef = ref<HTMLDivElement>()
const ms = useMessage()
const value = ref('')
const prompt = ref('')
const loading = ref(false)
@@ -27,9 +27,16 @@ function initChat() {
addMessage('Hi, I am ChatGPT, a chatbot based on GPT-3.', false)
}
function handleClear() {
list.value = []
setTimeout(initChat, 100)
async function handleClear() {
try {
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) {
@@ -38,17 +45,19 @@ function handleEnter(event: KeyboardEvent) {
}
async function handleSubmit() {
if (!value.value) {
const message = prompt.value.trim()
if (!message || !message.length) {
ms.warning('Please enter a message')
return
}
addMessage(value.value, true)
addMessage(message, true)
prompt.value = ''
try {
loading.value = true
const { text } = await fetchChatAPI(value.value)
value.value = ''
const { text } = await fetchChatAPI(message)
addMessage(text, false)
}
catch (error: any) {
@@ -81,7 +90,7 @@ function addMessage(message: string, reversal = false) {
<Icon icon="ri:delete-bin-6-line" />
</button>
</template>
<span>Clear</span>
<span>Clear Context</span>
</NPopover>
</div>
</header>
@@ -97,7 +106,7 @@ function addMessage(message: string, reversal = false) {
</main>
<footer class="p-4">
<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">
<template #icon>
<Icon icon="ri:send-plane-fill" />

View File

@@ -1,13 +1,13 @@
import axios from 'axios'
async function fetchChatAPI(message: string) {
const url = `${import.meta.env.VITE_GLOB_API_URL}/chat`
const BASE_URL = import.meta.env.VITE_GLOB_API_URL
async function fetchChatAPI(message: string) {
if (!message || message.trim() === '')
return
try {
const { status, data } = await axios.post(url, { message })
const { status, data } = await axios.post(`${BASE_URL}/chat`, { message })
if (status === 200) {
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 }