Merge pull request #1584 from Yidadaa/bugfix-0518

fix: #1571 #1578 handle more error code
This commit is contained in:
Yifei Zhang 2023-05-18 01:28:02 +08:00 committed by GitHub
commit 9ef6713aa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1289 additions and 80 deletions

2
.gitignore vendored
View File

@ -36,8 +36,6 @@ yarn-error.log*
next-env.d.ts
dev
public/prompts.json
.vscode
.idea

View File

@ -25,10 +25,6 @@ export async function requestOpenai(req: NextRequest) {
console.log("[Org ID]", process.env.OPENAI_ORG_ID);
}
if (!authValue || !authValue.startsWith("Bearer sk-")) {
console.error("[OpenAI Request] invalid api key provided", authValue);
}
return fetch(`${baseUrl}/${openaiPath}`, {
headers: {
"Content-Type": "application/json",

View File

@ -71,9 +71,13 @@ export class ChatGPTApi implements LLMApi {
if (shouldStream) {
let responseText = "";
let finished = false;
const finish = () => {
options.onFinish(responseText);
if (!finished) {
options.onFinish(responseText);
finished = true;
}
};
controller.signal.onabort = finish;
@ -83,19 +87,21 @@ export class ChatGPTApi implements LLMApi {
async onopen(res) {
clearTimeout(requestTimeoutId);
if (
res.ok &&
res.headers.get("content-type") !== EventStreamContentType
!res.ok ||
res.headers.get("content-type") !== EventStreamContentType ||
res.status !== 200
) {
responseText += await res.clone().json();
return finish();
}
if (res.status === 401) {
let extraInfo = { error: undefined };
try {
extraInfo = await res.clone().json();
} catch {}
responseText += "\n\n" + Locale.Error.Unauthorized;
if (res.status === 401) {
if (responseText.length > 0) {
responseText += "\n\n";
}
responseText += Locale.Error.Unauthorized;
}
if (extraInfo.error) {
responseText += "\n\n" + prettyObject(extraInfo);

View File

@ -53,7 +53,7 @@ import chatStyle from "./chat.module.scss";
import { ListItem, Modal, showModal } from "./ui-lib";
import { useLocation, useNavigate } from "react-router-dom";
import { LAST_INPUT_KEY, Path } from "../constant";
import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant";
import { Avatar } from "./emoji";
import { MaskAvatar, MaskConfig } from "./mask";
import { useMaskStore } from "../store/mask";
@ -487,6 +487,16 @@ export function Chat() {
// stop response
const onUserStop = (messageId: number) => {
chatStore.updateCurrentSession((session) => {
const stopTiming = Date.now() - REQUEST_TIMEOUT_MS;
session.messages.forEach((m) => {
// check if should stop all stale messages
if (m.streaming && new Date(m.date).getTime() < stopTiming) {
m.isError = false;
m.streaming = false;
}
});
});
ChatControllerPool.stop(sessionIndex, messageId);
};

View File

@ -7,7 +7,7 @@ import Locale from "../locales";
import { showToast } from "../components/ui-lib";
import { ModelType } from "./config";
import { createEmptyMask, Mask } from "./mask";
import { StoreKey } from "../constant";
import { REQUEST_TIMEOUT_MS, StoreKey } from "../constant";
import { api, RequestMessage } from "../client/api";
import { ChatControllerPool } from "../client/controller";
import { prettyObject } from "../utils/format";

View File

@ -1,7 +1,8 @@
# 常见问题
## 如何快速获得帮助?
1. 询问ChatGPT / Bing / 百度 / Google等。
1. 询问 ChatGPT / Bing / 百度 / Google 等。
2. 询问网友。请提供问题的背景信息和碰到问题的详细描述。高质量的提问容易获得有用的答案。
# 部署相关问题
@ -9,30 +10,37 @@
各种部署方式详细教程参考https://rptzik3toh.feishu.cn/docx/XtrdduHwXoSCGIxeFLlcEPsdn8b
## 为什么 Docker 部署版本一直提示更新
Docker 版本相当于稳定版latest Docker 总是与 latest release version 一致,目前我们的发版频率是一到两天发一次,所以 Docker 版本会总是落后最新的提交一到两天,这在预期内。
## 如何部署在Vercel上
1. 注册Github账号fork该项目
2. 注册Vercel需手机验证可以用中国号码连接你的Github账户
3. Vercel上新建项目选择你在Github fork的项目按需填写环境变量开始部署。部署之后你可以在有梯子的条件下通过vercel提供的域名访问你的项目。
4. 如果需要在国内无墙访问在你的域名管理网站添加一条域名的CNAME记录指向cname.vercel-dns.com。之后在Vercel上设置你的域名访问。
## 如何部署在 Vercel 上
1. 注册 Github 账号fork 该项目
2. 注册 Vercel需手机验证可以用中国号码连接你的 Github 账户
3. Vercel 上新建项目,选择你在 Github fork 的项目,按需填写环境变量,开始部署。部署之后,你可以在有梯子的条件下,通过 vercel 提供的域名访问你的项目。
4. 如果需要在国内无墙访问:在你的域名管理网站,添加一条域名的 CNAME 记录,指向 cname.vercel-dns.com。之后在 Vercel 上设置你的域名访问。
## 如何修改 Vercel 环境变量
- 进入 vercel 的控制台页面;
- 选中你的 chatgpt next web 项目;
- 点击页面头部的 Settings 选项;
- 找到侧边栏的 Environment Variables 选项;
- 修改对应的值即可。
## 环境变量CODE是什么必须设置吗
## 环境变量 CODE 是什么?必须设置吗?
这是你自定义的访问密码,你可以选择:
1. 不设置,删除该环境变量即可。谨慎:此时任何人可以访问你的项目。
2. 部署项目时设置环境变量CODE支持多个密码逗号分隔。设置访问密码后用户需要在设置界面输入访问密码才可以使用。参见[相关说明](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/README_CN.md#%E9%85%8D%E7%BD%AE%E9%A1%B5%E9%9D%A2%E8%AE%BF%E9%97%AE%E5%AF%86%E7%A0%81)
2. 部署项目时,设置环境变量 CODE支持多个密码逗号分隔。设置访问密码后用户需要在设置界面输入访问密码才可以使用。参见[相关说明](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/README_CN.md#%E9%85%8D%E7%BD%AE%E9%A1%B5%E9%9D%A2%E8%AE%BF%E9%97%AE%E5%AF%86%E7%A0%81)
## 为什么我部署的版本没有流式响应
> 相关讨论:[#386](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/386)
如果你使用 ngnix 反向代理,需要在配置文件中增加下列代码:
```
# 不缓存,支持流式输出
proxy_cache off; # 关闭缓存
@ -46,7 +54,9 @@ keepalive_timeout 300; # 设定keep-alive超时时间为65秒
如果你是在 netlify 部署,此问题依然等待解决,请耐心等待。
## 我部署好了,但是无法访问
请检查排除以下问题:
- 服务启动了吗?
- 端口正确映射了吗?
- 防火墙开放端口了吗?
@ -54,54 +64,72 @@ keepalive_timeout 300; # 设定keep-alive超时时间为65秒
- 域名正确解析了吗?
## 什么是代理,如何使用?
由于OpenAI的IP限制中国和其他一些国家/地区无法直接连接OpenAI API需要通过代理。你可以使用代理服务器正向代理或者已经设置好的OpenAI API反向代理。
- 正向代理例子科学上网梯子。docker部署的情况下设置环境变量HTTP_PROXY为你的代理地址例如10.10.10.10:8002
- 反向代理例子可以用别人搭建的代理地址或者通过Cloudflare免费设置。设置项目环境变量BASE_URL为你的代理地址。
由于 OpenAI 的 IP 限制,中国和其他一些国家/地区无法直接连接 OpenAI API需要通过代理。你可以使用代理服务器正向代理或者已经设置好的 OpenAI API 反向代理。
- 正向代理例子科学上网梯子。docker 部署的情况下,设置环境变量 HTTP_PROXY 为你的代理地址例如10.10.10.10:8002
- 反向代理例子:可以用别人搭建的代理地址,或者通过 Cloudflare 免费设置。设置项目环境变量 BASE_URL 为你的代理地址。
## 国内服务器可以部署吗?
可以但需要解决的问题:
- 需要代理才能连接github和openAI等网站
- 国内服务器要设置域名解析的话需要备案;
- 国内政策限制代理访问外网/ChatGPT相关应用可能被封。
可以但需要解决的问题:
- 需要代理才能连接 github 和 openAI 等网站;
- 国内服务器要设置域名解析的话需要备案;
- 国内政策限制代理访问外网/ChatGPT 相关应用,可能被封。
## 为什么 docker 部署后出现网络错误?
详见讨论https://github.com/Yidadaa/ChatGPT-Next-Web/issues/1569
# 使用相关问题
## 为什么会一直提示“出错了,稍后重试吧”
原因可能有很多,请依次排查:
- 请先检查你的代码版本是否为最新版本,更新到最新版本后重试;
- 请检查 api key 是否设置正确,环境变量名称必须为全大写加下划线;
- 请检查 api key 是否可用;
- 如果经历了上述步骤依旧无法确定问题,请在 issue 区提交一个新 issue并附上 vercel 的 runtime log 或者 docker 运行时的 log。
## 为什么 ChatGPT 的回复会乱码
设置界面 - 模型设置项中,有一项为 `temperature`,如果此值大于 1那么就有可能造成回复乱码将其调回 1 以内即可。
## 使用时提示“现在是未授权状态,请在设置页输入访问密码”?
项目通过环境变量CODE设置了访问密码。第一次使用时需要到设置中输入访问码才可以使用。
项目通过环境变量 CODE 设置了访问密码。第一次使用时,需要到设置中,输入访问码才可以使用。
## 使用时提示"You exceeded your current quota, ..."
API KEY有问题。余额不足。
API KEY 有问题。余额不足。
# 网络服务相关问题
## Cloudflare是什么
CloudflareCF是一个提供CDN域名管理静态页面托管边缘计算函数部署等的网络服务供应商。常见的用途购买和/或托管你的域名解析、动态域名等给你的服务器套上CDN可以隐藏ip免被墙部署网站CF Pages。CF免费提供大多数服务。
## Vercel是什么
Vercel 是一个全球化的云平台,旨在帮助开发人员更快地构建和部署现代 Web 应用程序。本项目以及许多Web应用可以一键免费部署在Vercel上。无需懂代码无需懂linux无需服务器无需付费无需设置OpenAI API代理。缺点是需要绑定域名才可以在国内无墙访问。
## Cloudflare 是什么?
CloudflareCF是一个提供 CDN域名管理静态页面托管边缘计算函数部署等的网络服务供应商。常见的用途购买和/或托管你的域名(解析、动态域名等),给你的服务器套上 CDN可以隐藏 ip 免被墙部署网站CF Pages。CF 免费提供大多数服务。
## Vercel 是什么?
Vercel 是一个全球化的云平台,旨在帮助开发人员更快地构建和部署现代 Web 应用程序。本项目以及许多 Web 应用可以一键免费部署在 Vercel 上。无需懂代码,无需懂 linux无需服务器无需付费无需设置 OpenAI API 代理。缺点是需要绑定域名才可以在国内无墙访问。
## 如何获得一个域名?
1. 自己去域名供应商处注册国外有Namesilo支持支付宝, Cloudflare等等国内有万网等等
1. 自己去域名供应商处注册,国外有 Namesilo支持支付宝, Cloudflare 等等,国内有万网等等;
2. 免费的域名供应商eu.org(二级域名)等;
3. 问朋友要一个免费的二级域名。
## 如何获得一台服务器
- 国外服务器供应商举例亚马逊云谷歌云VultrBandwagonHostdare等等
国外服务器事项服务器线路影响国内访问速度推荐CN2 GIA和CN2线路的服务器。若服务器在国内访问困难丢包严重等可以尝试套CDNCloudflare等供应商
国外服务器事项:服务器线路影响国内访问速度,推荐 CN2 GIA CN2 线路的服务器。若服务器在国内访问困难(丢包严重等),可以尝试套 CDNCloudflare 等供应商)。
- 国内服务器供应商:阿里云,腾讯等;
国内服务器事项解析域名需要备案国内服务器带宽较贵访问国外网站Github, openAI等需要代理。
国内服务器事项解析域名需要备案国内服务器带宽较贵访问国外网站Github, openAI 等)需要代理。
## 什么情况下服务器要备案?
在中国大陆经营的网站按监管要求需要备案。实际操作中,服务器位于国内且有域名解析的情况下,服务器供应商会执行监管的备案要求,否则会关停服务。通常的规则如下:
|服务器位置|域名供应商|是否需要备案|
|---|---|---|
@ -112,36 +140,47 @@ Vercel 是一个全球化的云平台,旨在帮助开发人员更快地构建
换服务器供应商后需要转备案。
# OpenAI相关问题
## 如何注册OpenAI账号
去chat.openai.com注册。你需要
- 一个良好的梯子OpenAI支持地区原生IP地址
- 一个支持的邮箱例如Gmail或者公司/学校邮箱非Outlook或qq邮箱
- 接收短信认证的方式例如SMS-activate网站
# OpenAI 相关问题
## 如何注册 OpenAI 账号?
去 chat.openai.com 注册。你需要:
- 一个良好的梯子OpenAI 支持地区原生 IP 地址)
- 一个支持的邮箱(例如 Gmail 或者公司/学校邮箱,非 Outlook 或 qq 邮箱)
- 接收短信认证的方式(例如 SMS-activate 网站)
## 怎么开通 OpenAI API? 怎么查询 API 余额?
## 怎么开通OpenAI API? 怎么查询API余额
官网地址需梯子https://platform.openai.com/account/usage
有网友搭建了无需梯子的余额查询代理请询问网友获取。请鉴别来源是否可靠以免API Key泄露。
有网友搭建了无需梯子的余额查询代理,请询问网友获取。请鉴别来源是否可靠,以免 API Key 泄露。
## 我新注册的OpenAI账号怎么没有API余额
4月6日更新新注册账号通常会在24小时后显示API余额。当前新注册账号赠送5美元余额。
## 我新注册的 OpenAI 账号怎么没有 API 余额?
## 如何给OpenAI API充值
OpenAI只接受指定地区的信用卡中国信用卡无法使用。一些途径举例
1. Depay虚拟信用卡
4 月 6 日更新)新注册账号通常会在 24 小时后显示 API 余额。当前新注册账号赠送 5 美元余额。
## 如何给 OpenAI API 充值?
OpenAI 只接受指定地区的信用卡(中国信用卡无法使用)。一些途径举例:
1. Depay 虚拟信用卡
2. 申请国外信用卡
3. 网上找人代充
## 如何使用GPT-4的API访问
- GPT-4的API访问需要单独申请。到以下地址填写你的信息进入申请队列waitlist准备好你的OpenAI组织IDhttps://openai.com/waitlist/gpt-4-api
之后等待邮件消息。
## 如何使用 GPT-4 的 API 访问?
- GPT-4 的 API 访问需要单独申请。到以下地址填写你的信息进入申请队列 waitlist准备好你的 OpenAI 组织 IDhttps://openai.com/waitlist/gpt-4-api
之后等待邮件消息。
- 开通 ChatGPT Plus 不代表有 GPT-4 权限,两者毫无关系。
## 如何使用 Azure OpenAI 接口
请参考:[#371](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/371)
## 为什么我的 Token 消耗得这么快?
> 相关讨论:[#518](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518)
- 如果你有 GPT 4 的权限,并且日常在使用 GPT 4 api那么由于 GPT 4 价格是 GPT 3.5 的 15 倍左右,你的账单金额会急速膨胀;
- 如果你在使用 GPT 3.5,并且使用频率并不高,仍然发现自己的账单金额在飞快增加,那么请马上按照以下步骤排查:
- 去 openai 官网查看你的 api key 消费记录,如果你的 token 每小时都有消费,并且每次都消耗了上万 token那你的 key 一定是泄露了,请立即删除重新生成。**不要在乱七八糟的网站上查余额。**
@ -150,16 +189,19 @@ OpenAI只接受指定地区的信用卡中国信用卡无法使用。一
- 如果 openai 消费记录异常,但是 docker 日志没有问题,那么说明是 api key 泄露;
- 如果 docker 日志发现大量 got access code 爆破记录,那么就是密码被爆破了。
## API是怎么计费的
OpenAI网站计费说明https://openai.com/pricing#language-models
OpenAI根据token数收费1000个token通常可代表750个英文单词或500个汉字。输入Prompt和输出Completion分别统计费用。
|模型|用户输入Prompt计费|模型输出Completion计费|每次交互最大token数|
|----|----|----|----|
|gpt-3.5|$0.002 / 1千tokens|$0.002 / 1千tokens|4096|
|gpt-4|$0.03 / 1千tokens|$0.06 / 1千tokens|8192|
|gpt-4-32K|$0.06 / 1千tokens|$0.12 / 1千tokens|32768|
## API 是怎么计费的?
OpenAI 网站计费说明https://openai.com/pricing#language-models
OpenAI 根据 token 数收费1000 个 token 通常可代表 750 个英文单词,或 500 个汉字。输入Prompt和输出Completion分别统计费用。
|模型|用户输入Prompt计费|模型输出Completion计费|每次交互最大 token 数|
|----|----|----|----|
|gpt-3.5|$0.002 / 1 千 tokens|$0.002 / 1 千 tokens|4096|
|gpt-4|$0.03 / 1 千 tokens|$0.06 / 1 千 tokens|8192|
|gpt-4-32K|$0.06 / 1 千 tokens|$0.12 / 1 千 tokens|32768|
## gpt-3.5-turbo 和 gpt3.5-turbo-0301(或者 gpt3.5-turbo-mmdd)模型有什么区别?
## gpt-3.5-turbo和gpt3.5-turbo-0301(或者gpt3.5-turbo-mmdd)模型有什么区别?
官方文档说明https://platform.openai.com/docs/models/gpt-3-5
- gpt-3.5-turbo是最新的模型会不断得到更新。
- gpt-3.5-turbo-0301是3月1日定格的模型快照不会变化预期3个月后被新快照替代。
- gpt-3.5-turbo 是最新的模型,会不断得到更新。
- gpt-3.5-turbo-0301 是 3 月 1 日定格的模型快照,不会变化,预期 3 个月后被新快照替代。

View File

@ -4,11 +4,11 @@
"private": false,
"license": "Anti 996",
"scripts": {
"dev": "yarn fetch && next dev",
"build": "yarn fetch && next build",
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"fetch": "node ./scripts/fetch-prompts.mjs",
"prompts": "node ./scripts/fetch-prompts.mjs",
"prepare": "husky install",
"proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev"
},

1144
public/prompts.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ import fetch from "node-fetch";
import fs from "fs/promises";
const RAW_FILE_URL = "https://raw.githubusercontent.com/";
const MIRRORF_FILE_URL = "https://raw.fgit.ml/";
const MIRRORF_FILE_URL = "http://raw.fgit.ml/";
const RAW_CN_URL = "PlexPt/awesome-chatgpt-prompts-zh/main/prompts-zh.json";
const CN_URL = MIRRORF_FILE_URL + RAW_CN_URL;
@ -10,10 +10,12 @@ const RAW_EN_URL = "f/awesome-chatgpt-prompts/main/prompts.csv";
const EN_URL = MIRRORF_FILE_URL + RAW_EN_URL;
const FILE = "./public/prompts.json";
const ignoreWords = ["涩涩", "魅魔"];
const timeoutPromise = (timeout) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Request timeout'));
reject(new Error("Request timeout"));
}, timeout);
});
};
@ -21,10 +23,16 @@ const timeoutPromise = (timeout) => {
async function fetchCN() {
console.log("[Fetch] fetching cn prompts...");
try {
// const raw = await (await fetch(CN_URL)).json();
const response = await Promise.race([fetch(CN_URL), timeoutPromise(5000)]);
const raw = await response.json();
return raw.map((v) => [v.act, v.prompt]);
return raw
.map((v) => [v.act, v.prompt])
.filter(
(v) =>
v[0] &&
v[1] &&
ignoreWords.every((w) => !v[0].includes(w) && !v[1].includes(w)),
);
} catch (error) {
console.error("[Fetch] failed to fetch cn prompts", error);
return [];
@ -40,7 +48,12 @@ async function fetchEN() {
return raw
.split("\n")
.slice(1)
.map((v) => v.split('","').map((v) => v.replace(/^"|"$/g, '').replaceAll('""','"')));
.map((v) =>
v
.split('","')
.map((v) => v.replace(/^"|"$/g, "").replaceAll('""', '"'))
.filter((v) => v[0] && v[1]),
);
} catch (error) {
console.error("[Fetch] failed to fetch en prompts", error);
return [];