merge
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
310
README.md
@ -2,301 +2,43 @@
|
|||||||
|
|
||||||
Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接口,目前集成了 gpt35 和 embedding. 可构建自己的知识库。
|
Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接口,目前集成了 gpt35 和 embedding. 可构建自己的知识库。
|
||||||
|
|
||||||
## 知识库原理
|
## 🛸 在线体验
|
||||||
|
|
||||||
|
🎉 [fastgpt.run](https://fastgpt.run/) (国内版)
|
||||||
|
🎉 [ai.fastgpt.run](https://ai.fastgpt.run/) (海外版)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 知识库原理图
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 开发
|
## 👨💻 开发
|
||||||
|
|
||||||
**第一次开发,请先看下方部署,需要部署数据库.开发的环境变量和部署的环境变量几乎相同,仅 IP 地址不一样.所以部署好后,直接把部署文件的环境变量复制过来,改下 Ip 就 OK 了。**
|
项目技术栈: NextJs + TS + ChakraUI + Mongo + Postgres(Vector 插件)
|
||||||
|
这是一个平台项目,非单机项目,除了模型调用外还涉及非常多用户的内容。
|
||||||
|
[本地开发 Quick Start](docs/dev/README.md)
|
||||||
|
|
||||||
**配置环境变量**
|
## 🚀 私有化部署
|
||||||
复制.env.template 文件,生成一个.env.local 环境变量文件夹,修改.env.local 内容,参考下方:
|
|
||||||
|
|
||||||
```bash
|
[docker-compose 部署教程](docs/deploy/docker.md)
|
||||||
# proxy(可选)
|
|
||||||
AXIOS_PROXY_HOST=127.0.0.1
|
|
||||||
AXIOS_PROXY_PORT=7890
|
|
||||||
# openai 中转连接(可选)
|
|
||||||
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
||||||
OPENAI_BASE_URL_AUTH=可选的安全凭证
|
|
||||||
queueTask=1
|
|
||||||
parentUrl=https://hostname/api/openapi/startEvents
|
|
||||||
MY_MAIL=xxx@qq.com
|
|
||||||
MAILE_CODE=xxx
|
|
||||||
aliAccessKeyId=xxx
|
|
||||||
aliAccessKeySecret=xxx
|
|
||||||
aliSignName=xxx
|
|
||||||
aliTemplateCode=SMS_xxx
|
|
||||||
TOKEN_KEY=xxx
|
|
||||||
OPENAIKEY=sk-xxx
|
|
||||||
# 和mongo镜像的username,password对应
|
|
||||||
MONGODB_URI=mongodb://username:password@服务器IP:27017/test?authSource=admin
|
|
||||||
MONGODB_NAME=xxx
|
|
||||||
PG_HOST=服务器IP
|
|
||||||
PG_PORT=8100
|
|
||||||
PG_USER=fastgpt # POSTGRES_USER
|
|
||||||
PG_PASSWORD=1234 # POSTGRES_PASSWORD
|
|
||||||
PG_DB_NAME=fastgpt # POSTGRES_DB
|
|
||||||
```
|
|
||||||
|
|
||||||
**运行**
|
## :point_right: RoadMap
|
||||||
|
|
||||||
```
|
- [FastGpt RoadMap](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte)
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## 部署
|
## 🏘️ 交流群
|
||||||
|
|
||||||
### 代理环境(国外服务器可忽略)
|
wx: fastgpt123
|
||||||
|

|
||||||
|
|
||||||
选择一个即可。
|
## 👀 其他
|
||||||
|
|
||||||
1. [clash 方案](./docs/proxy/clash.md) - 仅需一台服务器(需要有 clash)
|
- [FastGpt 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||||
2. [nginx 方案](./docs/proxy/nginx.md) - 需要一台国外服务器
|
- [FastGpt + Laf 最佳实践,将知识库装入公众号,点击去 Laf 公众号体验效果](https://hnvacz-laf-upload-ai.oss.laf.run/3ffd528ee2f9ae1dcd3508fe9994dd9.png)
|
||||||
3. [cloudflare 方案](./docs/proxy/cloudflare.md) - 需要有域名(每日免费 10w 次代理请求)
|
- [FastGpt V3.4 更新集合](https://www.bilibili.com/video/BV1Lo4y147Qh/?vd_source=92041a1a395f852f9d89158eaa3f61b4)
|
||||||
|
- [FastGpt 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||||
|
|
||||||
### docker 部署
|
## 🌟 Star History
|
||||||
|
|
||||||
#### 1. 准备
|
[](https://star-history.com/#c121914yu/FastGPT&Date)
|
||||||
|
|
||||||
> 1. 服务器开通 80 端口。用代理的话,对应的代理端口也需要打开。
|
|
||||||
> 2. QQ 邮箱 Code:进入 QQ 邮箱 -> 账号 -> 申请 SMTP 账号
|
|
||||||
> 3. 有域名的准备好 SSL 证书
|
|
||||||
|
|
||||||
#### 2. 安装 docker 和 docker-compose
|
|
||||||
|
|
||||||
这个不同系统略有区别,百度安装下。验证安装成功后进行下一步。下面给出一个例子:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 安装docker
|
|
||||||
curl -L https://get.daocloud.io/docker | sh
|
|
||||||
sudo systemctl start docker
|
|
||||||
# 安装 docker-compose
|
|
||||||
curl -L https://github.com/docker/compose/releases/download/1.23.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
|
|
||||||
sudo chmod +x /usr/local/bin/docker-compose
|
|
||||||
# 验证安装
|
|
||||||
docker -v
|
|
||||||
docker-compose -v
|
|
||||||
# 如果docker-compose运行不了,可以把 deploy/docker-compose 文件复制到服务器,然后在 docker-compose 文件夹里执行 sh init.sh。会把docker-compose文件复制到对应目录。
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. 创建 3 个初始化文件
|
|
||||||
|
|
||||||
手动创建或者直接把 deploy 里内容复制过去,然后把 deploy 文件夹改名为: fastgpt
|
|
||||||
|
|
||||||
**/root/fastgpt/pg/init.sql PG 数据库初始化**
|
|
||||||
|
|
||||||
```sql
|
|
||||||
set -e
|
|
||||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
||||||
|
|
||||||
CREATE EXTENSION vector;
|
|
||||||
-- init table
|
|
||||||
CREATE TABLE modelData (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
vector VECTOR(1536),
|
|
||||||
status VARCHAR(50) NOT NULL,
|
|
||||||
user_id VARCHAR(50) NOT NULL,
|
|
||||||
model_id VARCHAR(50) NOT NULL,
|
|
||||||
q TEXT NOT NULL,
|
|
||||||
a TEXT NOT NULL
|
|
||||||
);
|
|
||||||
-- create index
|
|
||||||
CREATE INDEX modelData_status_index ON modelData USING HASH (status);
|
|
||||||
CREATE INDEX modelData_userId_index ON modelData USING HASH (user_id);
|
|
||||||
CREATE INDEX modelData_modelId_index ON modelData USING HASH (model_id);
|
|
||||||
EOSQL
|
|
||||||
```
|
|
||||||
|
|
||||||
**/root/fastgpt/nginx/nginx.conf Nginx 配置**
|
|
||||||
|
|
||||||
```conf
|
|
||||||
user nginx;
|
|
||||||
worker_processes auto;
|
|
||||||
worker_rlimit_nofile 51200;
|
|
||||||
|
|
||||||
events {
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
http {
|
|
||||||
resolver 8.8.8.8;
|
|
||||||
proxy_ssl_server_name on;
|
|
||||||
|
|
||||||
access_log off;
|
|
||||||
server_names_hash_bucket_size 512;
|
|
||||||
client_header_buffer_size 64k;
|
|
||||||
large_client_header_buffers 4 64k;
|
|
||||||
client_max_body_size 50M;
|
|
||||||
|
|
||||||
proxy_connect_timeout 240s;
|
|
||||||
proxy_read_timeout 240s;
|
|
||||||
proxy_buffer_size 128k;
|
|
||||||
proxy_buffers 4 256k;
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
gzip_min_length 1k;
|
|
||||||
gzip_buffers 4 8k;
|
|
||||||
gzip_http_version 1.1;
|
|
||||||
gzip_comp_level 6;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_types text/plain application/x-javascript text/css application/javascript application/json application/xml;
|
|
||||||
gzip_disable "MSIE [1-6]\.";
|
|
||||||
|
|
||||||
open_file_cache max=1000 inactive=1d;
|
|
||||||
open_file_cache_valid 30s;
|
|
||||||
open_file_cache_min_uses 8;
|
|
||||||
open_file_cache_errors off;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443 ssl;
|
|
||||||
# 改成自己的域名和证书
|
|
||||||
server_name docgpt.ahapocket.cn;
|
|
||||||
ssl_certificate /ssl/docgpt.pem;
|
|
||||||
ssl_certificate_key /ssl/docgpt.key;
|
|
||||||
ssl_session_timeout 5m;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://localhost:3000;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name docgpt.ahapocket.cn;
|
|
||||||
rewrite ^(.*) https://$server_name$1 permanent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**/root/fastgpt/docker-compose.yml 核心部署文件**
|
|
||||||
|
|
||||||
```yml
|
|
||||||
version: '3.3'
|
|
||||||
services:
|
|
||||||
fast-gpt:
|
|
||||||
image: c121914yu/fast-gpt:latest
|
|
||||||
network_mode: host
|
|
||||||
restart: always
|
|
||||||
container_name: fast-gpt
|
|
||||||
environment:
|
|
||||||
# proxy(可选)
|
|
||||||
- AXIOS_PROXY_HOST=127.0.0.1
|
|
||||||
- AXIOS_PROXY_PORT=7890
|
|
||||||
# openai 中转连接(可选)
|
|
||||||
- OPENAI_BASE_URL=https://api.openai.com/v1
|
|
||||||
- OPENAI_BASE_URL_AUTH=可选的安全凭证
|
|
||||||
# 是否开启队列任务。 1-开启,0-关闭(请求 parentUrl 去执行任务,单机时直接填1)
|
|
||||||
- queueTask=1
|
|
||||||
- parentUrl=https://hostname/api/openapi/startEvents
|
|
||||||
# 发送邮箱验证码配置。用的是QQ邮箱。参考 nodeMail 获取MAILE_CODE,自行百度。
|
|
||||||
- MY_MAIL=xxxx@qq.com
|
|
||||||
- MAILE_CODE=xxxx
|
|
||||||
# 阿里短信服务(邮箱和短信至少二选一)
|
|
||||||
- aliAccessKeyId=xxxx
|
|
||||||
- aliAccessKeySecret=xxxx
|
|
||||||
- aliSignName=xxxxx
|
|
||||||
- aliTemplateCode=SMS_xxxx
|
|
||||||
# token加密凭证(随便填,作为登录凭证)
|
|
||||||
- TOKEN_KEY=xxxx
|
|
||||||
# 和下方mongo镜像的username,password对应
|
|
||||||
- MONGODB_URI=mongodb://username:passsword@0.0.0.0:27017/?authSource=admin
|
|
||||||
- MONGODB_NAME=xxx
|
|
||||||
- PG_HOST=0.0.0.0
|
|
||||||
- PG_PORT=8100
|
|
||||||
# 和下方PG镜像对应.
|
|
||||||
- PG_USER=fastgpt # POSTGRES_USER
|
|
||||||
- PG_PASSWORD=1234 # POSTGRES_PASSWORD
|
|
||||||
- PG_DB_NAME=fastgpt # POSTGRES_DB
|
|
||||||
# openai api key
|
|
||||||
- OPENAIKEY=sk-xxxxx
|
|
||||||
nginx:
|
|
||||||
image: nginx:alpine3.17
|
|
||||||
container_name: nginx
|
|
||||||
restart: always
|
|
||||||
network_mode: host
|
|
||||||
volumes:
|
|
||||||
# 刚创建的文件
|
|
||||||
- /root/fastgpt/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
|
||||||
- /root/fastgpt/nginx/logs:/var/log/nginx
|
|
||||||
# https证书,没有的话不填,对应的nginx.conf也要修改
|
|
||||||
- /root/fastgpt/nginx/ssl/docgpt.key:/ssl/docgpt.key
|
|
||||||
- /root/fastgpt/nginx/ssl/docgpt.pem:/ssl/docgpt.pem
|
|
||||||
pg:
|
|
||||||
image: ankane/pgvector
|
|
||||||
container_name: pg
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 8100:5432
|
|
||||||
environment:
|
|
||||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
|
||||||
- POSTGRES_USER=fastgpt
|
|
||||||
- POSTGRES_PASSWORD=1234
|
|
||||||
- POSTGRES_DB=fastgpt
|
|
||||||
volumes:
|
|
||||||
# 刚创建的文件
|
|
||||||
- /root/fastgpt/pg/init.sql:/docker-entrypoint-initdb.d/init.sh
|
|
||||||
- /root/fastgpt/pg/data:/var/lib/postgresql/data
|
|
||||||
- /etc/localtime:/etc/localtime:ro
|
|
||||||
mongodb:
|
|
||||||
image: mongo:6.0.4
|
|
||||||
container_name: mongo
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 27017:27017
|
|
||||||
environment:
|
|
||||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
|
||||||
- MONGO_INITDB_ROOT_USERNAME=username
|
|
||||||
- MONGO_INITDB_ROOT_PASSWORD=password
|
|
||||||
volumes:
|
|
||||||
- /root/fastgpt/mongo/data:/data/db
|
|
||||||
- /root/fastgpt/mongo/logs:/var/log/mongodb
|
|
||||||
- /etc/localtime:/etc/localtime:ro
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. 运行 docker-compose
|
|
||||||
|
|
||||||
下面是一个辅助脚本,也可以直接 docker-compose up -d
|
|
||||||
|
|
||||||
**run.sh 运行文件**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
docker-compose pull
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
echo "Docker Compose 重新拉取镜像完成!"
|
|
||||||
|
|
||||||
# 删除本地旧镜像
|
|
||||||
images=$(docker images --format "{{.ID}} {{.Repository}}" | grep fast-gpt)
|
|
||||||
|
|
||||||
# 将镜像 ID 和名称放入数组中
|
|
||||||
IFS=$'\n' read -rd '' -a image_array <<<"$images"
|
|
||||||
|
|
||||||
# 遍历数组并删除所有旧的镜像
|
|
||||||
for ((i=1; i<${#image_array[@]}; i++))
|
|
||||||
do
|
|
||||||
image=${image_array[$i]}
|
|
||||||
image_id=${image%% *}
|
|
||||||
docker rmi $image_id
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
## 其他优化点
|
|
||||||
|
|
||||||
### Git Action 自动打包镜像
|
|
||||||
|
|
||||||
.github 里拥有一个 git 提交到 main 分支时自动打包 amd64 和 arm64 镜像的 actions。你仅需要提前在 git 配置好 session。
|
|
||||||
|
|
||||||
1. 创建账号 session: 头像 -> settings -> 最底部 Developer settings -> Personal access tokens -> tokens(classic) -> 创建新 session,把一些看起来需要的权限勾上。
|
|
||||||
2. 添加 session 到仓库: 仓库 -> settings -> Secrets and variables -> Actions -> 创建 secret
|
|
||||||
3. 填写 secret: Name-GH_PAT, Secret-第一步的 tokens
|
|
||||||
|
|
||||||
## 其他问题
|
|
||||||
|
|
||||||
### Mac 可能的问题
|
|
||||||
|
|
||||||
> 因为教程有部分镜像不兼容 arm64,所以写个文档指导新手如何快速在 mac 上面搭建 fast-gpt[如何在 mac 上面部署 fastgpt](./docs/mac.md)
|
|
||||||
|
|||||||
256
docs/deploy/docker.md
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
# Docker 部署 FastGpt
|
||||||
|
|
||||||
|
## 代理环境(国外服务器可忽略)
|
||||||
|
|
||||||
|
选择一个即可。
|
||||||
|
|
||||||
|
1. [sealos nginx 方案](./proxy/sealos.md) - 推荐。约等于不用钱,不需要额外准备任何东西。
|
||||||
|
2. [clash 方案](./proxy/clash.md) - 仅需一台服务器(需要有 clash)
|
||||||
|
3. [nginx 方案](./proxy/nginx.md) - 需要一台国外服务器
|
||||||
|
4. [cloudflare 方案](./proxy/cloudflare.md) - 需要有域名(每日免费 10w 次代理请求)
|
||||||
|
|
||||||
|
### 1. 准备一些内容
|
||||||
|
|
||||||
|
> 1. 服务器开通 80 端口。用代理的话,对应的代理端口也需要打开。
|
||||||
|
> 2. QQ 邮箱 Code:进入 QQ 邮箱 -> 账号 -> 申请 SMTP 账号
|
||||||
|
> 3. 有域名的准备好 SSL 证书
|
||||||
|
|
||||||
|
### 2. 安装 docker 和 docker-compose
|
||||||
|
|
||||||
|
这个不同系统略有区别,百度安装下。验证安装成功后进行下一步。下面给出一个例子:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装docker
|
||||||
|
curl -L https://get.daocloud.io/docker | sh
|
||||||
|
sudo systemctl start docker
|
||||||
|
# 安装 docker-compose
|
||||||
|
curl -L https://github.com/docker/compose/releases/download/1.23.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
|
||||||
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
# 验证安装
|
||||||
|
docker -v
|
||||||
|
docker-compose -v
|
||||||
|
# 如果docker-compose运行不了,可以把 deploy/fastgpt/docker-compose 文件复制到服务器,然后在 docker-compose 文件夹里执行 sh init.sh。会把docker-compose文件复制到对应目录。
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 创建 3 个初始化文件
|
||||||
|
|
||||||
|
手动创建或者直接把 fastgpt 文件夹复制过去。
|
||||||
|
|
||||||
|
**/root/fastgpt/pg/init.sql PG 数据库初始化**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
set -e
|
||||||
|
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||||
|
|
||||||
|
CREATE EXTENSION vector;
|
||||||
|
-- init table
|
||||||
|
CREATE TABLE modelData (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
vector VECTOR(1536),
|
||||||
|
status VARCHAR(50) NOT NULL,
|
||||||
|
user_id VARCHAR(50) NOT NULL,
|
||||||
|
model_id VARCHAR(50) NOT NULL,
|
||||||
|
q TEXT NOT NULL,
|
||||||
|
a TEXT NOT NULL
|
||||||
|
);
|
||||||
|
-- create index
|
||||||
|
CREATE INDEX modelData_status_index ON modelData USING HASH (status);
|
||||||
|
CREATE INDEX modelData_userId_index ON modelData USING HASH (user_id);
|
||||||
|
CREATE INDEX modelData_modelId_index ON modelData USING HASH (model_id);
|
||||||
|
EOSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
**/root/fastgpt/nginx/nginx.conf Nginx 配置**
|
||||||
|
|
||||||
|
```conf
|
||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
worker_rlimit_nofile 51200;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
resolver 8.8.8.8;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
server_names_hash_bucket_size 512;
|
||||||
|
client_header_buffer_size 64k;
|
||||||
|
large_client_header_buffers 4 64k;
|
||||||
|
client_max_body_size 50M;
|
||||||
|
|
||||||
|
proxy_connect_timeout 240s;
|
||||||
|
proxy_read_timeout 240s;
|
||||||
|
proxy_buffer_size 128k;
|
||||||
|
proxy_buffers 4 256k;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_min_length 1k;
|
||||||
|
gzip_buffers 4 8k;
|
||||||
|
gzip_http_version 1.1;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_types text/plain application/x-javascript text/css application/javascript application/json application/xml;
|
||||||
|
gzip_disable "MSIE [1-6]\.";
|
||||||
|
|
||||||
|
open_file_cache max=1000 inactive=1d;
|
||||||
|
open_file_cache_valid 30s;
|
||||||
|
open_file_cache_min_uses 8;
|
||||||
|
open_file_cache_errors off;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
# 改成自己的域名和证书
|
||||||
|
server_name docgpt.ahapocket.cn;
|
||||||
|
ssl_certificate /ssl/docgpt.pem;
|
||||||
|
ssl_certificate_key /ssl/docgpt.key;
|
||||||
|
ssl_session_timeout 5m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name docgpt.ahapocket.cn;
|
||||||
|
rewrite ^(.*) https://$server_name$1 permanent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**/root/fastgpt/docker-compose.yml 核心部署文件**
|
||||||
|
|
||||||
|
环境变量内容和开发时的环境变量基本相同,除了数据库的地址。
|
||||||
|
|
||||||
|
```yml
|
||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
fast-gpt:
|
||||||
|
image: c121914yu/fast-gpt:latest
|
||||||
|
network_mode: host
|
||||||
|
restart: always
|
||||||
|
container_name: fastgpt
|
||||||
|
environment:
|
||||||
|
# proxy(可选)
|
||||||
|
- AXIOS_PROXY_HOST=127.0.0.1
|
||||||
|
- AXIOS_PROXY_PORT=7890
|
||||||
|
# openai 中转连接(可选)
|
||||||
|
- OPENAI_BASE_URL=https://api.openai.com/v1
|
||||||
|
- OPENAI_BASE_URL_AUTH=可选的安全凭证
|
||||||
|
# 是否开启队列任务。 1-开启,0-关闭(请求 parentUrl 去执行任务,单机时直接填1)
|
||||||
|
- queueTask=1
|
||||||
|
- parentUrl=https://hostname/api/openapi/startEvents
|
||||||
|
# 发送邮箱验证码配置。用的是QQ邮箱。参考 nodeMail 获取MAILE_CODE,自行百度。
|
||||||
|
- MY_MAIL=xxxx@qq.com
|
||||||
|
- MAILE_CODE=xxxx
|
||||||
|
# 阿里短信服务(邮箱和短信至少二选一)
|
||||||
|
- aliAccessKeyId=xxxx
|
||||||
|
- aliAccessKeySecret=xxxx
|
||||||
|
- aliSignName=xxxxx
|
||||||
|
- aliTemplateCode=SMS_xxxx
|
||||||
|
# token加密凭证(随便填,作为登录凭证)
|
||||||
|
- TOKEN_KEY=xxxx
|
||||||
|
- queueTask=1
|
||||||
|
- parentUrl=https://hostname/api/openapi/startEvents
|
||||||
|
# 和下方mongo镜像的username,password对应
|
||||||
|
- MONGODB_URI=mongodb://username:passsword@0.0.0.0:27017/?authSource=admin
|
||||||
|
- MONGODB_NAME=xxx
|
||||||
|
- PG_HOST=0.0.0.0
|
||||||
|
- PG_PORT=8100
|
||||||
|
# 和下方PG镜像对应.
|
||||||
|
- PG_USER=fastgpt # POSTGRES_USER
|
||||||
|
- PG_PASSWORD=1234 # POSTGRES_PASSWORD
|
||||||
|
- PG_DB_NAME=fastgpt # POSTGRES_DB
|
||||||
|
- OPENAIKEY=sk-xxxxx
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine3.17
|
||||||
|
container_name: nginx
|
||||||
|
restart: always
|
||||||
|
network_mode: host
|
||||||
|
volumes:
|
||||||
|
# 刚创建的文件
|
||||||
|
- /root/fastgpt/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- /root/fastgpt/nginx/logs:/var/log/nginx
|
||||||
|
# https证书,没有的话不填,对应的nginx.conf也要修改
|
||||||
|
- /root/fastgpt/nginx/ssl/docgpt.key:/ssl/docgpt.key
|
||||||
|
- /root/fastgpt/nginx/ssl/docgpt.pem:/ssl/docgpt.pem
|
||||||
|
pg:
|
||||||
|
image: ankane/pgvector:v0.4.1
|
||||||
|
container_name: pg
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8100:5432
|
||||||
|
environment:
|
||||||
|
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||||
|
- POSTGRES_USER=fastgpt
|
||||||
|
- POSTGRES_PASSWORD=1234
|
||||||
|
- POSTGRES_DB=fastgpt
|
||||||
|
volumes:
|
||||||
|
# 刚创建的文件
|
||||||
|
- /root/fastgpt/pg/init.sql:/docker-entrypoint-initdb.d/init.sh
|
||||||
|
- /root/fastgpt/pg/data:/var/lib/postgresql/data
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
mongodb:
|
||||||
|
image: mongo:6.0.4
|
||||||
|
container_name: mongo
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
environment:
|
||||||
|
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||||
|
- MONGO_INITDB_ROOT_USERNAME=username
|
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=password
|
||||||
|
volumes:
|
||||||
|
- /root/fastgpt/mongo/data:/data/db
|
||||||
|
- /root/fastgpt/mongo/logs:/var/log/mongodb
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 运行 docker-compose
|
||||||
|
|
||||||
|
下面是一个辅助脚本,也可以直接 docker-compose up -d
|
||||||
|
|
||||||
|
**run.sh 运行文件**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
docker-compose pull
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
echo "Docker Compose 重新拉取镜像完成!"
|
||||||
|
|
||||||
|
# 删除本地旧镜像
|
||||||
|
images=$(docker images --format "{{.ID}} {{.Repository}}" | grep fast-gpt)
|
||||||
|
|
||||||
|
# 将镜像 ID 和名称放入数组中
|
||||||
|
IFS=$'\n' read -rd '' -a image_array <<<"$images"
|
||||||
|
|
||||||
|
# 遍历数组并删除所有旧的镜像
|
||||||
|
for ((i=1; i<${#image_array[@]}; i++))
|
||||||
|
do
|
||||||
|
image=${image_array[$i]}
|
||||||
|
image_id=${image%% *}
|
||||||
|
docker rmi $image_id
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## 其他优化点
|
||||||
|
|
||||||
|
# Git Action 自动打包镜像
|
||||||
|
|
||||||
|
.github 里拥有一个 git 提交到 main 分支时自动打包 amd64 和 arm64 镜像的 actions。你仅需要提前在 git 配置好 session。
|
||||||
|
|
||||||
|
1. 创建账号 session: 头像 -> settings -> 最底部 Developer settings -> Personal access tokens -> tokens(classic) -> 创建新 session,把一些看起来需要的权限勾上。
|
||||||
|
2. 添加 session 到仓库: 仓库 -> settings -> Secrets and variables -> Actions -> 创建 secret
|
||||||
|
3. 填写 secret: Name-GH_PAT, Secret-第一步的 tokens
|
||||||
|
|
||||||
|
## 其他问题
|
||||||
|
|
||||||
|
### Mac 可能的问题
|
||||||
|
|
||||||
|
> 因为教程有部分镜像不兼容 arm64,所以写个文档指导新手如何快速在 mac 上面搭建 fast-gpt[在 mac 上面部署 fastgpt 可能存在的问题](./mac.md)
|
||||||
@ -49,7 +49,7 @@ services:
|
|||||||
- /root/fastgpt/nginx/ssl/docgpt.key:/ssl/docgpt.key
|
- /root/fastgpt/nginx/ssl/docgpt.key:/ssl/docgpt.key
|
||||||
- /root/fastgpt/nginx/ssl/docgpt.pem:/ssl/docgpt.pem
|
- /root/fastgpt/nginx/ssl/docgpt.pem:/ssl/docgpt.pem
|
||||||
pg:
|
pg:
|
||||||
image: ankane/pgvector
|
image: ankane/pgvector:v0.4.1
|
||||||
container_name: pg
|
container_name: pg
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
@ -1,22 +1,22 @@
|
|||||||
## 怎么在mac上面部署fastgpt
|
## Mac 上部署可能遇到的问题
|
||||||
|
|
||||||
### 前置条件
|
### 前置条件
|
||||||
|
|
||||||
1、可以 curl api.openai.com
|
1、可以 curl api.openai.com
|
||||||
|
|
||||||
2、有openai key
|
2、有 openai key
|
||||||
|
|
||||||
3、有邮箱MAILE_CODE
|
3、有邮箱 MAILE_CODE
|
||||||
|
|
||||||
4、有docker
|
4、有 docker
|
||||||
|
|
||||||
```
|
```
|
||||||
docker -v
|
docker -v
|
||||||
```
|
```
|
||||||
|
|
||||||
5、有pnpm ,可以使用`brew install pnpm`安装
|
5、有 pnpm ,可以使用`brew install pnpm`安装
|
||||||
|
|
||||||
6、需要创建一个放置pg和mongo数据的文件夹,这里创建在`~/fastgpt`目录中,里面有`pg` 和`mongo `两个文件夹
|
6、需要创建一个放置 pg 和 mongo 数据的文件夹,这里创建在`~/fastgpt`目录中,里面有`pg` 和`mongo `两个文件夹
|
||||||
|
|
||||||
```
|
```
|
||||||
➜ fast-gpt pwd
|
➜ fast-gpt pwd
|
||||||
@ -25,11 +25,9 @@ docker -v
|
|||||||
mongo pg
|
mongo pg
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### docker 部署方式
|
||||||
|
|
||||||
|
这种方式主要是为了方便调试,可以使用`pnpm dev ` 运行 fast-gpt 项目
|
||||||
### docker部署方式
|
|
||||||
|
|
||||||
这种方式主要是为了方便调试,可以使用`pnpm dev ` 运行fast-gpt项目
|
|
||||||
|
|
||||||
**1、.env.local 文件**
|
**1、.env.local 文件**
|
||||||
|
|
||||||
@ -60,19 +58,19 @@ PG_PASSWORD=xxx
|
|||||||
PG_DB_NAME=xxx
|
PG_DB_NAME=xxx
|
||||||
```
|
```
|
||||||
|
|
||||||
**2、部署mongo**
|
**2、部署 mongo**
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run --name mongo -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=username -e MONGO_INITDB_ROOT_PASSWORD=password -v ~/fast-gpt/mongo/data:/data/db -d mongo:4.0.1
|
docker run --name mongo -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=username -e MONGO_INITDB_ROOT_PASSWORD=password -v ~/fast-gpt/mongo/data:/data/db -d mongo:4.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
**3、部署pgsql**
|
**3、部署 pgsql**
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -it --name pg -e "POSTGRES_PASSWORD=xxx" -e POSTGRES_USER=xxx -p 8100:5432 -v ~/fast-gpt/pg/data:/var/lib/postgresql/data -d octoberlan/pgvector:v0.4.1
|
docker run -it --name pg -e "POSTGRES_PASSWORD=xxx" -e POSTGRES_USER=xxx -p 8100:5432 -v ~/fast-gpt/pg/data:/var/lib/postgresql/data -d octoberlan/pgvector:v0.4.1
|
||||||
```
|
```
|
||||||
|
|
||||||
进pgsql容器运行
|
进 pgsql 容器运行
|
||||||
|
|
||||||
```
|
```
|
||||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||||
@ -95,6 +93,4 @@ CREATE INDEX modelData_userId_index ON modelData (userId);
|
|||||||
EOSQL
|
EOSQL
|
||||||
```
|
```
|
||||||
|
|
||||||
|
4、**最后在 FASTGPT 项目里面运行 pnpm dev 运行项目,然后进入 localhost:3000 看项目是否跑起来了**
|
||||||
|
|
||||||
4、**最后在FASTGPT项目里面运行pnpm dev 运行项目,然后进入localhost:3000 看项目是否跑起来了**
|
|
||||||
BIN
docs/deploy/proxy/imgs/sealos1.png
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
BIN
docs/deploy/proxy/imgs/sealos2.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
docs/deploy/proxy/imgs/sealos3.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
docs/deploy/proxy/imgs/sealos4.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
docs/deploy/proxy/imgs/sealos5.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
@ -1,4 +1,5 @@
|
|||||||
# nginx 反向代理 openai 接口
|
# nginx 反向代理 openai 接口
|
||||||
|
|
||||||
如果你有国外的服务器,可以通过配置 nginx 反向代理,转发 openai 相关的请求,从而让国内的服务器可以通过访问该 nginx 去访问 openai 接口。
|
如果你有国外的服务器,可以通过配置 nginx 反向代理,转发 openai 相关的请求,从而让国内的服务器可以通过访问该 nginx 去访问 openai 接口。
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
@ -43,7 +44,7 @@ http {
|
|||||||
|
|
||||||
location ~ /openai/(.*) {
|
location ~ /openai/(.*) {
|
||||||
# auth check
|
# auth check
|
||||||
if ($http_authkey != "xxxxxx") {
|
if ($auth != "xxxxxx") {
|
||||||
return 403;
|
return 403;
|
||||||
}
|
}
|
||||||
|
|
||||||
100
docs/deploy/proxy/sealos.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# sealos 部署 openai 中转
|
||||||
|
|
||||||
|
## 登录 sealos cloud
|
||||||
|
|
||||||
|
[sealos cloud](https://cloud.sealos.io/)
|
||||||
|
|
||||||
|
## 创建应用
|
||||||
|
|
||||||
|
打开 App Launchpad -> 新建应用
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
### 开启外网访问
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 添加 configmap 文件
|
||||||
|
|
||||||
|
1. 复制下面这段代码,注意 `server_name` 后面的内容替换成上图的地址。
|
||||||
|
|
||||||
|
```
|
||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
worker_rlimit_nofile 51200;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
resolver 8.8.8.8;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
server_names_hash_bucket_size 512;
|
||||||
|
client_header_buffer_size 64k;
|
||||||
|
large_client_header_buffers 4 64k;
|
||||||
|
client_max_body_size 50M;
|
||||||
|
|
||||||
|
proxy_connect_timeout 240s;
|
||||||
|
proxy_read_timeout 240s;
|
||||||
|
proxy_buffer_size 128k;
|
||||||
|
proxy_buffers 4 256k;
|
||||||
|
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name tgohwtdlrmer.cloud.sealos.io; # 这个地方替换成 sealos 提供的内容
|
||||||
|
|
||||||
|
location ~ /openai/(.*) {
|
||||||
|
# auth check
|
||||||
|
if ($http_auth != "auth") { # 安全凭证
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_pass https://api.openai.com/$1$is_args$args;
|
||||||
|
proxy_set_header Host api.openai.com;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
# 如果响应是流式的
|
||||||
|
proxy_set_header Connection '';
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
chunked_transfer_encoding off;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_cache off;
|
||||||
|
# 如果响应是一般的
|
||||||
|
proxy_buffer_size 128k;
|
||||||
|
proxy_buffers 4 256k;
|
||||||
|
proxy_busy_buffers_size 256k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 点开高级配置
|
||||||
|
3. 点击新增 configmap
|
||||||
|
4. 文件名写: `/etc/nginx/nginx.conf`
|
||||||
|
5. 文件值为刚刚复制的那段代码
|
||||||
|
6. 点击确认
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 部署应用
|
||||||
|
|
||||||
|
填写完毕后,点击右上角的 `部署应用`,即可完成。
|
||||||
|
|
||||||
|
## 修改 FastGpt 环境变量
|
||||||
|
|
||||||
|
1. 进入刚刚部署应用的详情,复制外网地址
|
||||||
|

|
||||||
|
|
||||||
|
2. 修改环境变量:
|
||||||
|
|
||||||
|
```
|
||||||
|
OPENAI_BASE_URL=https://tgohwtdlrmer.cloud.sealos.io/openai/v1
|
||||||
|
OPENAI_BASE_URL_AUTH=auth
|
||||||
|
```
|
||||||
|
|
||||||
|
**Done!**
|
||||||
47
docs/dev/README.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# FastGpt 本地开发
|
||||||
|
|
||||||
|
第一次开发,请先[部署教程](docs/deploy/docker.md),需要部署数据库.
|
||||||
|
|
||||||
|
## 环境变量配置
|
||||||
|
|
||||||
|
复制.env.template 文件,生成一个.env.local 环境变量文件夹,修改.env.local 里内容。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# proxy(可选)
|
||||||
|
AXIOS_PROXY_HOST=127.0.0.1
|
||||||
|
AXIOS_PROXY_PORT=7890
|
||||||
|
# openai 中转连接(可选)
|
||||||
|
OPENAI_BASE_URL=https://api.openai.com/v1
|
||||||
|
OPENAI_BASE_URL_AUTH=可选的安全凭证
|
||||||
|
# 是否开启队列任务。 1-开启,0-关闭(请求 parentUrl 去执行任务,单机时直接填1)
|
||||||
|
queueTask=1
|
||||||
|
parentUrl=https://hostname/api/openapi/startEvents
|
||||||
|
# 发送邮箱验证码配置。用的是 QQ 邮箱。参考 nodeMail 获取MAILE_CODE,自行百度。
|
||||||
|
MY_MAIL=xxxx@qq.com
|
||||||
|
MAILE_CODE=xxxx
|
||||||
|
# 阿里短信服务(邮箱和短信至少二选一)
|
||||||
|
aliAccessKeyId=xxxx
|
||||||
|
aliAccessKeySecret=xxxx
|
||||||
|
aliSignName=xxxxx
|
||||||
|
aliTemplateCode=SMS_xxxx
|
||||||
|
# token加密凭证(随便填,作为登录凭证)
|
||||||
|
TOKEN_KEY=xxxx
|
||||||
|
queueTask=1
|
||||||
|
parentUrl=https://hostname/api/openapi/startEvents
|
||||||
|
# 和mongo镜像的username,password对应
|
||||||
|
MONGODB_URI=mongodb://username:passsword@0.0.0.0:27017/?authSource=admin
|
||||||
|
MONGODB_NAME=xxx
|
||||||
|
PG_HOST=0.0.0.0
|
||||||
|
PG_PORT=8100
|
||||||
|
# 和PG镜像对应.
|
||||||
|
PG_USER=fastgpt # POSTGRES_USER
|
||||||
|
PG_PASSWORD=1234 # POSTGRES_PASSWORD
|
||||||
|
PG_DB_NAME=fastgpt # POSTGRES_DB
|
||||||
|
OPENAIKEY=sk-xxxxx
|
||||||
|
```
|
||||||
|
|
||||||
|
## 运行
|
||||||
|
|
||||||
|
```
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
BIN
docs/imgs/demo.png
Normal file
|
After Width: | Height: | Size: 456 KiB |
BIN
docs/imgs/wx300.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
@ -24,7 +24,7 @@ wx 号: fastgpt123
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| claude - 对话 | 免费 |
|
| claude - 对话 | 免费 |
|
||||||
| chatgpt - 对话 | 0.03 |
|
| chatgpt - 对话 | 0.03 |
|
||||||
| 知识库 - 对话 | 0.03 |
|
| gpt4 - 对话 | 0.5 |
|
||||||
| 知识库 - 索引 | 免费 |
|
| 知识库 - 索引 | 免费 |
|
||||||
| 文件拆分 | 0.03 |
|
| 文件拆分 | 0.03 |
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
||||||
import { clearToken } from '@/utils/user';
|
import { clearCookie } from '@/utils/user';
|
||||||
import { TOKEN_ERROR_CODE } from '@/service/errorCode';
|
import { TOKEN_ERROR_CODE } from '@/service/errorCode';
|
||||||
|
|
||||||
interface ConfigType {
|
interface ConfigType {
|
||||||
@ -58,7 +58,7 @@ function responseError(err: any) {
|
|||||||
// 有报错响应
|
// 有报错响应
|
||||||
const res = err.response;
|
const res = err.response;
|
||||||
if (res.data.code in TOKEN_ERROR_CODE) {
|
if (res.data.code in TOKEN_ERROR_CODE) {
|
||||||
clearToken();
|
clearCookie();
|
||||||
return Promise.reject({ message: 'token过期,重新登录' });
|
return Promise.reject({ message: 'token过期,重新登录' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,6 +64,8 @@ export const postLogin = ({ username, password }: { username: string; password:
|
|||||||
password: createHashPassword(password)
|
password: createHashPassword(password)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const loginOut = () => GET('/user/loginout');
|
||||||
|
|
||||||
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data);
|
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data);
|
||||||
|
|
||||||
export const getUserBills = (data: RequestPaging) =>
|
export const getUserBills = (data: RequestPaging) =>
|
||||||
|
|||||||
@ -35,7 +35,7 @@ const Layout = ({ children, isPcDevice }: { children: JSX.Element; isPcDevice: b
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box h={'100%'} overflowY={'auto'} bg={'gray.100'}>
|
<Box h={'100%'} bg={'gray.100'}>
|
||||||
{isPc ? (
|
{isPc ? (
|
||||||
pcUnShowLayoutRoute[router.pathname] ? (
|
pcUnShowLayoutRoute[router.pathname] ? (
|
||||||
<Auth>{children}</Auth>
|
<Auth>{children}</Auth>
|
||||||
|
|||||||
@ -102,9 +102,7 @@ const Navbar = () => {
|
|||||||
justifyContent={'center'}
|
justifyContent={'center'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (item.link === router.asPath) return;
|
if (item.link === router.asPath) return;
|
||||||
router.push(item.link, undefined, {
|
router.push(item.link);
|
||||||
shallow: true
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
w={'60px'}
|
w={'60px'}
|
||||||
|
|||||||
@ -28,31 +28,31 @@ export const ChatModelMap = {
|
|||||||
chatModel: OpenAiChatEnum.GPT35,
|
chatModel: OpenAiChatEnum.GPT35,
|
||||||
name: 'ChatGpt',
|
name: 'ChatGpt',
|
||||||
contextMaxToken: 4096,
|
contextMaxToken: 4096,
|
||||||
systemMaxToken: 2500,
|
systemMaxToken: 2400,
|
||||||
maxTemperature: 1.5,
|
maxTemperature: 1.2,
|
||||||
price: 3
|
price: 3
|
||||||
},
|
},
|
||||||
[OpenAiChatEnum.GPT4]: {
|
[OpenAiChatEnum.GPT4]: {
|
||||||
chatModel: OpenAiChatEnum.GPT4,
|
chatModel: OpenAiChatEnum.GPT4,
|
||||||
name: 'Gpt4',
|
name: 'Gpt4',
|
||||||
contextMaxToken: 8000,
|
contextMaxToken: 8000,
|
||||||
systemMaxToken: 3500,
|
systemMaxToken: 3000,
|
||||||
maxTemperature: 1.5,
|
maxTemperature: 1.2,
|
||||||
price: 30
|
price: 50
|
||||||
},
|
},
|
||||||
[OpenAiChatEnum.GPT432k]: {
|
[OpenAiChatEnum.GPT432k]: {
|
||||||
chatModel: OpenAiChatEnum.GPT432k,
|
chatModel: OpenAiChatEnum.GPT432k,
|
||||||
name: 'Gpt4-32k',
|
name: 'Gpt4-32k',
|
||||||
contextMaxToken: 32000,
|
contextMaxToken: 32000,
|
||||||
systemMaxToken: 6000,
|
systemMaxToken: 3000,
|
||||||
maxTemperature: 1.5,
|
maxTemperature: 1.2,
|
||||||
price: 30
|
price: 90
|
||||||
},
|
},
|
||||||
[ClaudeEnum.Claude]: {
|
[ClaudeEnum.Claude]: {
|
||||||
chatModel: ClaudeEnum.Claude,
|
chatModel: ClaudeEnum.Claude,
|
||||||
name: 'Claude(免费体验)',
|
name: 'Claude(免费体验)',
|
||||||
contextMaxToken: 9000,
|
contextMaxToken: 9000,
|
||||||
systemMaxToken: 2500,
|
systemMaxToken: 2400,
|
||||||
maxTemperature: 1,
|
maxTemperature: 1,
|
||||||
price: 0
|
price: 0
|
||||||
}
|
}
|
||||||
@ -60,6 +60,7 @@ export const ChatModelMap = {
|
|||||||
|
|
||||||
export const chatModelList: ChatModelItemType[] = [
|
export const chatModelList: ChatModelItemType[] = [
|
||||||
ChatModelMap[OpenAiChatEnum.GPT35],
|
ChatModelMap[OpenAiChatEnum.GPT35],
|
||||||
|
ChatModelMap[OpenAiChatEnum.GPT4],
|
||||||
ChatModelMap[ClaudeEnum.Claude]
|
ChatModelMap[ClaudeEnum.Claude]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -68,11 +68,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
return res.send(searchPrompts[0]?.value);
|
return res.send(searchPrompts[0]?.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
prompts.splice(prompts.length - 1, 0, ...searchPrompts);
|
prompts.splice(prompts.length - 3, 0, ...searchPrompts);
|
||||||
} else {
|
} else {
|
||||||
// 没有用知识库搜索,仅用系统提示词
|
// 没有用知识库搜索,仅用系统提示词
|
||||||
model.chat.systemPrompt &&
|
model.chat.systemPrompt &&
|
||||||
prompts.unshift({
|
prompts.splice(prompts.length - 3, 0, {
|
||||||
obj: ChatRoleEnum.System,
|
obj: ChatRoleEnum.System,
|
||||||
value: model.chat.systemPrompt
|
value: model.chat.systemPrompt
|
||||||
});
|
});
|
||||||
|
|||||||
@ -87,12 +87,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
prompts.splice(prompts.length - 1, 0, ...searchPrompts);
|
prompts.splice(prompts.length - 1, 0, ...searchPrompts);
|
||||||
} else {
|
} else {
|
||||||
// 没有用知识库搜索,仅用系统提示词
|
// 没有用知识库搜索,仅用系统提示词
|
||||||
if (model.chat.systemPrompt) {
|
model.chat.systemPrompt &&
|
||||||
prompts.unshift({
|
prompts.splice(prompts.length - 1, 0, {
|
||||||
obj: ChatRoleEnum.System,
|
obj: ChatRoleEnum.System,
|
||||||
value: model.chat.systemPrompt
|
value: model.chat.systemPrompt
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算温度
|
// 计算温度
|
||||||
|
|||||||
16
src/pages/api/user/loginout.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { clearCookie } from '@/service/utils/tools';
|
||||||
|
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
|
try {
|
||||||
|
clearCookie(res);
|
||||||
|
jsonRes(res);
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -198,7 +198,6 @@ const Chat = ({
|
|||||||
if (newChatId) {
|
if (newChatId) {
|
||||||
setForbidLoadChatData(true);
|
setForbidLoadChatData(true);
|
||||||
router.replace(`/chat?modelId=${modelId}&chatId=${newChatId}`);
|
router.replace(`/chat?modelId=${modelId}&chatId=${newChatId}`);
|
||||||
loadHistory({ pageNum: 1, init: true });
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast({
|
toast({
|
||||||
@ -222,6 +221,9 @@ const Chat = ({
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// refresh history
|
||||||
|
loadHistory({ pageNum: 1, init: true });
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
chatId,
|
chatId,
|
||||||
@ -488,10 +490,6 @@ const Chat = ({
|
|||||||
modelId && setLastChatModelId(modelId);
|
modelId && setLastChatModelId(modelId);
|
||||||
setLastChatId(chatId);
|
setLastChatId(chatId);
|
||||||
|
|
||||||
/* get mode and chat into ↓ */
|
|
||||||
|
|
||||||
// phone: history page
|
|
||||||
if (!isPc && Object.keys(router.query).length === 0) return null;
|
|
||||||
if (forbidLoadChatData) {
|
if (forbidLoadChatData) {
|
||||||
setForbidLoadChatData(false);
|
setForbidLoadChatData(false);
|
||||||
return null;
|
return null;
|
||||||
@ -566,7 +564,7 @@ const Chat = ({
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
await onclickDelHistory(chatData.chatId);
|
await onclickDelHistory(chatData.chatId);
|
||||||
router.replace(`/chat`);
|
router.replace(`/chat?modelId=${modelId}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { useToast } from '@/hooks/useToast';
|
|||||||
import { useGlobalStore } from '@/store/global';
|
import { useGlobalStore } from '@/store/global';
|
||||||
import { useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
import { UserType } from '@/types/user';
|
import { UserType } from '@/types/user';
|
||||||
import { clearToken } from '@/utils/user';
|
import { clearCookie } from '@/utils/user';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
@ -75,7 +75,7 @@ const NumberSetting = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onclickLogOut = useCallback(() => {
|
const onclickLogOut = useCallback(() => {
|
||||||
clearToken();
|
clearCookie();
|
||||||
setUserInfo(null);
|
setUserInfo(null);
|
||||||
router.replace('/login');
|
router.replace('/login');
|
||||||
}, [router, setUserInfo]);
|
}, [router, setUserInfo]);
|
||||||
|
|||||||
@ -81,7 +81,7 @@ const OpenApi = () => {
|
|||||||
mr={4}
|
mr={4}
|
||||||
variant={'outline'}
|
variant={'outline'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyData(`${location.origin}?inviterId=${userInfo?._id}`, '已复制邀请链接');
|
copyData(`${location.origin}/?inviterId=${userInfo?._id}`, '已复制邀请链接');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
复制邀请链接
|
复制邀请链接
|
||||||
|
|||||||
@ -105,11 +105,12 @@ export const searchKb = async ({
|
|||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
obj: ChatRoleEnum.System,
|
obj: ChatRoleEnum.System,
|
||||||
value: `我们来玩问答游戏,规则为:
|
value: `玩一个问答游戏,规则为:
|
||||||
1.你只能回答关于"${model.name}"的问题
|
1.你完全忘记你已有的知识
|
||||||
2.你只能从知识库中选择内容进行回答
|
2.你只回答关于"${model.name}"的问题
|
||||||
3.如果问题不在知识库中,你会回答"我不知道。"
|
3.你只从知识库中选择内容进行回答
|
||||||
务必遵守规则`
|
4.如果问题不在知识库中,你会回答:"我不知道。"
|
||||||
|
请务必遵守规则`
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
];
|
];
|
||||||
@ -125,7 +126,8 @@ export const searchKb = async ({
|
|||||||
length: Math.floor(maxTokens * rate)
|
length: Math.floor(maxTokens * rate)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.join('\n');
|
.join('\n')
|
||||||
|
.trim();
|
||||||
|
|
||||||
/* 高相似度+不回复 */
|
/* 高相似度+不回复 */
|
||||||
if (!filterSystemPrompt && model.chat.searchMode === ModelVectorSearchModeEnum.hightSimilarity) {
|
if (!filterSystemPrompt && model.chat.searchMode === ModelVectorSearchModeEnum.hightSimilarity) {
|
||||||
@ -160,7 +162,7 @@ export const searchKb = async ({
|
|||||||
searchPrompts: [
|
searchPrompts: [
|
||||||
{
|
{
|
||||||
obj: ChatRoleEnum.System,
|
obj: ChatRoleEnum.System,
|
||||||
value: `知识库:'${filterSystemPrompt}'`
|
value: `知识库:${filterSystemPrompt}`
|
||||||
},
|
},
|
||||||
...fixedPrompts
|
...fixedPrompts
|
||||||
]
|
]
|
||||||
|
|||||||
@ -55,11 +55,11 @@ export const getApiKey = async ({
|
|||||||
},
|
},
|
||||||
[OpenAiChatEnum.GPT4]: {
|
[OpenAiChatEnum.GPT4]: {
|
||||||
userOpenAiKey: user.openaiKey || '',
|
userOpenAiKey: user.openaiKey || '',
|
||||||
systemAuthKey: process.env.OPENAIKEY as string
|
systemAuthKey: process.env.GPT4KEY as string
|
||||||
},
|
},
|
||||||
[OpenAiChatEnum.GPT432k]: {
|
[OpenAiChatEnum.GPT432k]: {
|
||||||
userOpenAiKey: user.openaiKey || '',
|
userOpenAiKey: user.openaiKey || '',
|
||||||
systemAuthKey: process.env.OPENAIKEY as string
|
systemAuthKey: process.env.GPT4KEY as string
|
||||||
},
|
},
|
||||||
[ClaudeEnum.Claude]: {
|
[ClaudeEnum.Claude]: {
|
||||||
userOpenAiKey: '',
|
userOpenAiKey: '',
|
||||||
|
|||||||
@ -25,9 +25,9 @@ export const lafClaudChat = async ({
|
|||||||
.filter((item) => item.obj === 'System')
|
.filter((item) => item.obj === 'System')
|
||||||
.map((item) => item.value)
|
.map((item) => item.value)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
const systemPromptText = systemPrompt ? `\n知识库内容:'${systemPrompt}'\n` : '';
|
const systemPromptText = systemPrompt ? `你本次知识:${systemPrompt}\n` : '';
|
||||||
|
|
||||||
const prompt = `${systemPromptText}\n我的问题是:'${messages[messages.length - 1].value}'`;
|
const prompt = `${systemPromptText}我的问题是:'${messages[messages.length - 1].value}'`;
|
||||||
|
|
||||||
const lafResponse = await axios.post(
|
const lafResponse = await axios.post(
|
||||||
'https://hnvacz.laf.run/claude-gpt',
|
'https://hnvacz.laf.run/claude-gpt',
|
||||||
|
|||||||
@ -109,35 +109,22 @@ export const ChatContextFilter = ({
|
|||||||
|
|
||||||
// 根据 tokens 截断内容
|
// 根据 tokens 截断内容
|
||||||
const chats: ChatItemSimpleType[] = [];
|
const chats: ChatItemSimpleType[] = [];
|
||||||
let systemPrompt: ChatItemSimpleType | null = null;
|
|
||||||
|
|
||||||
// System 词保留
|
|
||||||
if (formatPrompts[0].obj === ChatRoleEnum.System) {
|
|
||||||
const prompt = formatPrompts.shift();
|
|
||||||
if (prompt) {
|
|
||||||
systemPrompt = prompt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let messages: ChatItemSimpleType[] = [];
|
|
||||||
|
|
||||||
// 从后往前截取对话内容
|
// 从后往前截取对话内容
|
||||||
for (let i = formatPrompts.length - 1; i >= 0; i--) {
|
for (let i = formatPrompts.length - 1; i >= 0; i--) {
|
||||||
chats.unshift(formatPrompts[i]);
|
chats.unshift(formatPrompts[i]);
|
||||||
|
|
||||||
messages = systemPrompt ? [systemPrompt, ...chats] : chats;
|
|
||||||
|
|
||||||
const tokens = modelToolMap[model].countTokens({
|
const tokens = modelToolMap[model].countTokens({
|
||||||
messages
|
messages: chats
|
||||||
});
|
});
|
||||||
|
|
||||||
/* 整体 tokens 超出范围 */
|
/* 整体 tokens 超出范围, system必须保留 */
|
||||||
if (tokens >= maxTokens) {
|
if (tokens >= maxTokens && formatPrompts[i].obj !== ChatRoleEnum.System) {
|
||||||
return systemPrompt ? [systemPrompt, ...chats.slice(1)] : chats.slice(1);
|
return chats.slice(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return messages;
|
return chats;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* stream response */
|
/* stream response */
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
import { PRICE_SCALE } from '@/constants/common';
|
import { PRICE_SCALE } from '@/constants/common';
|
||||||
const tokenKey = 'fast-gpt-token';
|
import { loginOut } from '@/api/user';
|
||||||
|
|
||||||
export const clearToken = () => {
|
export const clearCookie = () => {
|
||||||
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
try {
|
||||||
|
loginOut();
|
||||||
|
} catch (error) {
|
||||||
|
error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||