Skip to content

Commit

Permalink
feat: 支持 accessToken 请求 web api 调用 (#80)
Browse files Browse the repository at this point in the history
* feat: 支持 markdown 格式和图片

* perf: 重载的时候滚动条保持

* chore: version 2.5.2

* feat: 添加文字换行

* chore: 添加新封面

* chore: 更新 cover

* feat: 支持 web api 的形式

* feat: 支持新模型和调整超时

* feat: 添加反向代理

* chore: 更新 README.md

* feat: 添加超时和反向代理显示

* chore: version 2.6.0

* chore: update README
  • Loading branch information
Chanzhaoyu authored Feb 21, 2023
1 parent ac9536a commit f40048f
Show file tree
Hide file tree
Showing 18 changed files with 236 additions and 35 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
VITE_GLOB_API_URL=/api

VITE_APP_API_BASE_URL=https://localhost:3002/

VITE_GLOB_API_TIMEOUT=100000
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"chatgpt",
"chenzhaoyu",
"commitlint",
"davinci",
"dockerhub",
"esno",
"GPTAPI",
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## v2.6.0

`2023-02-21`
### Feature
- 新增对 `网页 accessToken` 调用 `ChatGPT`,更智能不过不太稳定 [#51](https://github.com/Chanzhaoyu/chatgpt-web/issues/51)
- 前端页面设置按钮显示查看当前后端服务配置

### Enhancement
- 新增 `TIMEOUT_MS` 环境变量设定后端超时时常(单位:毫秒)[#62](https://github.com/Chanzhaoyu/chatgpt-web/issues/62)

## v2.5.2

`2023-02-21`
Expand Down
68 changes: 60 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
# ChatGPT Web

使用 express 和 vue3 搭建的 ChartGPT 演示网页
使用 `express``vue3` 搭建的支持 `ChatGPT` 双模型演示网页

![cover](./docs/cover.png)
![cover2](./docs/cover2.png)

> 提示:目前 `OpenAI` 开放的模型最高只有 `GPT-3`,和现在网页所使用的 `GPT-3.5``GPT-4` 有很大差距,需要等官方开放最新的模型接口。
## 介绍

支持双模型,提供了两种非官方 `ChatGPT API` 方法

| 方式 | 免费? | 可靠性 | 质量 |
| ---- | ---- | ---- | ---- |
| `ChatGPTAPI(GPT-3)` || 可靠 | 较笨 |
| `ChatGPTUnofficialProxyAPI(网页 accessToken)` || 不可靠 | 聪明 |

***Note:*** 网页 `accessToken` 存在大约 8 小时,而且国内地区网络问题更推荐使用 `GPT-3` 的方式

对比:
1. `ChatGPTAPI` 使用 `text-davinci-003` 通过官方`OpenAI`补全`API`模拟`ChatGPT`(最稳健的方法,但它不是免费的,并且没有使用针对聊天进行微调的模型)
2. `ChatGPTUnofficialProxyAPI` 使用非官方代理服务器访问 `ChatGPT` 的后端`API`,绕过`Cloudflare`(使用真实的的`ChatGPT`,非常轻量级,但依赖于第三方服务器,并且有速率限制)

切换方式:
1. 进入 `service/.env` 文件
2. 使用 `OpenAI API Key` 请填写 `OPENAI_API_KEY` 字段 [(获取 apiKey)](https://platform.openai.com/overview)
3. 使用 `Web API` 请填写 `OPENAI_ACCESS_TOKEN` 字段 [(获取 accessToken)](https://chat.openai.com/api/auth/session)
4. 同时存在时以 `OpenAI API Key` 优先

反向代理:

`ChatGPTUnofficialProxyAPI`时可用 [详情](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy)

```shell
# service/.env
API_REVERSE_PROXY=
```

## 待实现路线
[] 双模型

[] 多会话储存和上下文逻辑

[] 对代码等消息类型的格式化美化处理
Expand All @@ -34,12 +64,17 @@ node -v
npm install pnpm -g
```

### OpenAI API Key
注册并获取 [OpenAI API key](https://platform.openai.com/overview) 并填写到本地环境变量
### 填写密钥
获取 `Openai Api Key``accessToken` 并填写本地环境变量 [跳转](#介绍)

```
# service/.env 文件
OPENAI_API_KEY='Your key'
# OpenAI API Key - https://platform.openai.com/overview
OPENAI_API_KEY=
# change this to an `accessToken` extracted from the ChatGPT site's `https://chat.openai.com/api/auth/session` response
OPENAI_ACCESS_TOKEN=
```

## 安装依赖
Expand All @@ -60,7 +95,7 @@ pnpm install
pnpm bootstrap
```

## 运行
## 测试环境运行
### 后端服务

进入文件夹 `/service` 运行以下命令
Expand All @@ -69,7 +104,7 @@ pnpm bootstrap
pnpm start
```

### 网页
### 前端网页
根目录下运行以下命令
```shell
pnpm dev
Expand All @@ -78,6 +113,16 @@ pnpm dev
## 打包

### 使用 Docker

### Docker 参数示例

- `OPENAI_API_KEY` 二选一
- `OPENAI_ACCESS_TOKEN` 二选一,同时存在时,`OPENAI_API_KEY` 优先
- `API_REVERSE_PROXY` 可选,设置 `OPENAI_ACCESS_TOKEN` 时可用 [参考](#介绍)
- `TIMEOUT_MS` 超时,单位毫秒,可选

![docker](./docs/docker.png)

### Docker build & Run

```bash
Expand Down Expand Up @@ -106,7 +151,14 @@ services:
ports:
- 3002:3002
environment:
# 二选一
OPENAI_API_KEY: xxxxxx
# 二选一
OPENAI_ACCESS_TOKEN: xxxxxx
# 反向代理,可选
API_REVERSE_PROXY: xxx
# 超时,单位毫秒,可选
TIMEOUT_MS: 60000
```
Expand All @@ -129,7 +181,7 @@ pnpm prod

PS: 不进行打包,直接在服务器上运行 `pnpm start` 也可

### 前端打包
### 前端网页

1、修改根目录下 `.env` 内 `VITE_APP_API_BASE_URL` 为你的实际后端接口地址

Expand Down
Binary file added docs/docker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "chatgpt-web",
"version": "2.5.2",
"version": "2.6.0",
"private": false,
"description": "ChatGPT Web",
"author": "ChenZhaoYu <[email protected]>",
Expand Down
9 changes: 9 additions & 0 deletions service/.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
# OpenAI API Key - https://platform.openai.com/overview
OPENAI_API_KEY=

# change this to an `accessToken` extracted from the ChatGPT site's `https://chat.openai.com/api/auth/session` response
OPENAI_ACCESS_TOKEN=

# Reverse Proxy
API_REVERSE_PROXY=

# timeout
TIMEOUT_MS=60000
2 changes: 1 addition & 1 deletion service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"common:cleanup": "rimraf node_modules && rimraf pnpm-lock.yaml"
},
"dependencies": {
"chatgpt": "^4.7.1",
"chatgpt": "^4.7.2",
"dotenv": "^16.0.3",
"esno": "^0.16.3",
"express": "^4.18.2",
Expand Down
8 changes: 4 additions & 4 deletions service/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 39 additions & 9 deletions service/src/chatgpt.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
import * as dotenv from 'dotenv'
import 'isomorphic-fetch'
import type { ChatGPTAPI, SendMessageOptions } from 'chatgpt'
import { ChatGPTUnofficialProxyAPI } from 'chatgpt'
import { sendResponse } from './utils'

dotenv.config()

let apiModel: 'ChatGPTAPI' | 'ChatGPTUnofficialProxyAPI' | undefined

export interface ChatContext {
conversationId?: string
parentMessageId?: string
}

dotenv.config()

const apiKey = process.env.OPENAI_API_KEY
const timeoutMs: number = !isNaN(+process.env.TIMEOUT_MS) ? +process.env.TIMEOUT_MS : 30 * 1000

if (apiKey === undefined)
throw new Error('OPENAI_API_KEY is not defined')
if (!process.env.OPENAI_API_KEY && !process.env.OPENAI_ACCESS_TOKEN)
throw new Error('Missing OPENAI_API_KEY or OPENAI_ACCESS_TOKEN environment variable')

let api: ChatGPTAPI
let api: ChatGPTAPI | ChatGPTUnofficialProxyAPI

// To use ESM in CommonJS, you can use a dynamic import
(async () => {
// More Info: https://github.com/transitive-bullshit/chatgpt-api
const { ChatGPTAPI } = await import('chatgpt')
api = new ChatGPTAPI({ apiKey: process.env.OPENAI_API_KEY })

if (process.env.OPENAI_API_KEY) {
api = new ChatGPTAPI({ apiKey: process.env.OPENAI_API_KEY })
apiModel = 'ChatGPTAPI'
}
else {
let options = {}

if (process.env.API_REVERSE_PROXY)
options = { apiReverseProxyUrl: process.env.API_REVERSE_PROXY }

api = new ChatGPTUnofficialProxyAPI({
accessToken: process.env.OPENAI_ACCESS_TOKEN,
...options,
})
apiModel = 'ChatGPTUnofficialProxyAPI'
}
})()

async function chatReply(
Expand All @@ -32,7 +51,7 @@ async function chatReply(
return sendResponse({ type: 'Fail', message: 'Message is empty' })

try {
let options: SendMessageOptions = { timeoutMs: 30 * 1000 }
let options: SendMessageOptions = { timeoutMs }

if (lastContext)
options = { ...lastContext }
Expand All @@ -46,4 +65,15 @@ async function chatReply(
}
}

export { chatReply }
async function chatConfig() {
return sendResponse({
type: 'Success',
data: {
apiModel,
reverseProxy: process.env.API_REVERSE_PROXY,
timeoutMs,
},
})
}

export { chatReply, chatConfig }
12 changes: 11 additions & 1 deletion service/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import express from 'express'
import type { ChatContext } from './chatgpt'
import { chatReply } from './chatgpt'
import { chatConfig, chatReply } from './chatgpt'

const app = express()
const router = express.Router()
Expand All @@ -26,6 +26,16 @@ router.post('/chat', async (req, res) => {
}
})

router.post('/config', async (req, res) => {
try {
const response = await chatConfig()
res.send(response)
}
catch (error) {
res.send(error)
}
})

app.use('', router)
app.use('/api', router)

Expand Down
6 changes: 6 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ export function fetchChatAPI<T = any>(
signal,
})
}

export function fetchChatConfig<T = any>() {
return post<T>({
url: '/config',
})
}
67 changes: 67 additions & 0 deletions src/components/common/Setting/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<script setup lang='ts'>
import { computed, ref, watch } from 'vue'
import { NCard, NModal } from 'naive-ui'
import { fetchChatConfig } from '@/api'
interface Props {
visible: boolean
}
interface Emit {
(e: 'update:visible', visible: boolean): void
}
interface ConfigState {
timeoutMs?: number
reverseProxy?: string
apiModel?: string
}
const props = defineProps<Props>()
const emit = defineEmits<Emit>()
const show = computed({
get() {
return props.visible
},
set(visible: boolean) {
emit('update:visible', visible)
},
})
const config = ref<ConfigState>()
async function fetchConfig() {
try {
const { data } = await fetchChatConfig<ConfigState>()
config.value = data
}
catch (error) {
// ...
}
}
watch(
() => props.visible,
(val) => {
if (val)
fetchConfig()
},
)
</script>

<template>
<NModal v-model:show="show" style="width: 80%; max-width: 460px;">
<NCard>
<div class="space-y-4">
<h1 class="text-xl font-bold">
当前后台设置
</h1>
<p>API方式:{{ config?.apiModel ?? '-' }}</p>
<p>反向代理:{{ config?.reverseProxy ?? '-' }}</p>
<p>超时时间:{{ config?.timeoutMs ?? '-' }}</p>
</div>
</NCard>
</NModal>
</template>
Loading

0 comments on commit f40048f

Please sign in to comment.