diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md index cd0f4da69..16405d10e 100644 --- a/README.md +++ b/README.md @@ -2,301 +2,43 @@ Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接口,目前集成了 gpt35 和 embedding. 可构建自己的知识库。 -## 知识库原理 +## 🛸 在线体验 + +🎉 [fastgpt.run](https://fastgpt.run/) (国内版) +🎉 [ai.fastgpt.run](https://ai.fastgpt.run/) (海外版) + +![Demo](docs/imgs/demo.png?raw=true 'demo') + +#### 知识库原理图 ![KBProcess](docs/imgs/KBProcess.jpg?raw=true 'KBProcess') -## 开发 +## 👨‍💻 开发 -**第一次开发,请先看下方部署,需要部署数据库.开发的环境变量和部署的环境变量几乎相同,仅 IP 地址不一样.所以部署好后,直接把部署文件的环境变量复制过来,改下 Ip 就 OK 了。** +项目技术栈: NextJs + TS + ChakraUI + Mongo + Postgres(Vector 插件) +这是一个平台项目,非单机项目,除了模型调用外还涉及非常多用户的内容。 +[本地开发 Quick Start](docs/dev/README.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=可选的安全凭证 -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 -``` +[docker-compose 部署教程](docs/deploy/docker.md) -**运行** +## :point_right: RoadMap -``` -pnpm dev -``` +- [FastGpt RoadMap](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte) -## 部署 +## 🏘️ 交流群 -### 代理环境(国外服务器可忽略) +wx: fastgpt123 +![Demo](docs/imgs/wx300.jpg?raw=true 'wx') -选择一个即可。 +## 👀 其他 -1. [clash 方案](./docs/proxy/clash.md) - 仅需一台服务器(需要有 clash) -2. [nginx 方案](./docs/proxy/nginx.md) - 需要一台国外服务器 -3. [cloudflare 方案](./docs/proxy/cloudflare.md) - 需要有域名(每日免费 10w 次代理请求) +- [FastGpt 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh) +- [FastGpt + Laf 最佳实践,将知识库装入公众号,点击去 Laf 公众号体验效果](https://hnvacz-laf-upload-ai.oss.laf.run/3ffd528ee2f9ae1dcd3508fe9994dd9.png) +- [FastGpt V3.4 更新集合](https://www.bilibili.com/video/BV1Lo4y147Qh/?vd_source=92041a1a395f852f9d89158eaa3f61b4) +- [FastGpt 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/) -### docker 部署 +## 🌟 Star History -#### 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/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) +[![Star History Chart](https://api.star-history.com/svg?repos=c121914yu/FastGPT&type=Date)](https://star-history.com/#c121914yu/FastGPT&Date) diff --git a/docs/deploy/docker.md b/docs/deploy/docker.md new file mode 100644 index 000000000..b230b5b72 --- /dev/null +++ b/docs/deploy/docker.md @@ -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) diff --git a/deploy/clash/Country.mmdb b/docs/deploy/fastgpt/clash/Country.mmdb similarity index 100% rename from deploy/clash/Country.mmdb rename to docs/deploy/fastgpt/clash/Country.mmdb diff --git a/deploy/clash/clash-linux-amd64-v3 b/docs/deploy/fastgpt/clash/clash-linux-amd64-v3 similarity index 100% rename from deploy/clash/clash-linux-amd64-v3 rename to docs/deploy/fastgpt/clash/clash-linux-amd64-v3 diff --git a/deploy/clash/config.yaml b/docs/deploy/fastgpt/clash/config.yaml similarity index 100% rename from deploy/clash/config.yaml rename to docs/deploy/fastgpt/clash/config.yaml diff --git a/deploy/clash/proxy.sh b/docs/deploy/fastgpt/clash/proxy.sh similarity index 100% rename from deploy/clash/proxy.sh rename to docs/deploy/fastgpt/clash/proxy.sh diff --git a/deploy/clash/stop.sh b/docs/deploy/fastgpt/clash/stop.sh similarity index 100% rename from deploy/clash/stop.sh rename to docs/deploy/fastgpt/clash/stop.sh diff --git a/deploy/docker-compose.yml b/docs/deploy/fastgpt/docker-compose.yml similarity index 98% rename from deploy/docker-compose.yml rename to docs/deploy/fastgpt/docker-compose.yml index 525619884..0dbdb9b8b 100644 --- a/deploy/docker-compose.yml +++ b/docs/deploy/fastgpt/docker-compose.yml @@ -49,7 +49,7 @@ services: - /root/fastgpt/nginx/ssl/docgpt.key:/ssl/docgpt.key - /root/fastgpt/nginx/ssl/docgpt.pem:/ssl/docgpt.pem pg: - image: ankane/pgvector + image: ankane/pgvector:v0.4.1 container_name: pg restart: always ports: diff --git a/deploy/docker-compose/docker-compose b/docs/deploy/fastgpt/docker-compose/docker-compose similarity index 100% rename from deploy/docker-compose/docker-compose rename to docs/deploy/fastgpt/docker-compose/docker-compose diff --git a/deploy/docker-compose/init.sh b/docs/deploy/fastgpt/docker-compose/init.sh similarity index 100% rename from deploy/docker-compose/init.sh rename to docs/deploy/fastgpt/docker-compose/init.sh diff --git a/deploy/nginx/nginx.conf b/docs/deploy/fastgpt/nginx/nginx.conf similarity index 100% rename from deploy/nginx/nginx.conf rename to docs/deploy/fastgpt/nginx/nginx.conf diff --git a/deploy/pg/init.sql b/docs/deploy/fastgpt/pg/init.sql similarity index 100% rename from deploy/pg/init.sql rename to docs/deploy/fastgpt/pg/init.sql diff --git a/deploy/run.sh b/docs/deploy/fastgpt/run.sh similarity index 100% rename from deploy/run.sh rename to docs/deploy/fastgpt/run.sh diff --git a/docs/mac.md b/docs/deploy/mac.md similarity index 74% rename from docs/mac.md rename to docs/deploy/mac.md index b0d162a1d..7262c5ad5 100644 --- a/docs/mac.md +++ b/docs/deploy/mac.md @@ -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 ``` -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 @@ -25,11 +25,9 @@ docker -v mongo pg ``` +### docker 部署方式 - -### docker部署方式 - -这种方式主要是为了方便调试,可以使用`pnpm dev ` 运行fast-gpt项目 +这种方式主要是为了方便调试,可以使用`pnpm dev ` 运行 fast-gpt 项目 **1、.env.local 文件** @@ -60,19 +58,19 @@ PG_PASSWORD=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 ``` -**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 ``` -进pgsql容器运行 +进 pgsql 容器运行 ``` 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 ``` - - -4、**最后在FASTGPT项目里面运行pnpm dev 运行项目,然后进入localhost:3000 看项目是否跑起来了** \ No newline at end of file +4、**最后在 FASTGPT 项目里面运行 pnpm dev 运行项目,然后进入 localhost:3000 看项目是否跑起来了** diff --git a/docs/proxy/clash.md b/docs/deploy/proxy/clash.md similarity index 100% rename from docs/proxy/clash.md rename to docs/deploy/proxy/clash.md diff --git a/docs/proxy/cloudflare.md b/docs/deploy/proxy/cloudflare.md similarity index 100% rename from docs/proxy/cloudflare.md rename to docs/deploy/proxy/cloudflare.md diff --git a/docs/deploy/proxy/imgs/sealos1.png b/docs/deploy/proxy/imgs/sealos1.png new file mode 100644 index 000000000..6498bc35f Binary files /dev/null and b/docs/deploy/proxy/imgs/sealos1.png differ diff --git a/docs/deploy/proxy/imgs/sealos2.png b/docs/deploy/proxy/imgs/sealos2.png new file mode 100644 index 000000000..acc252bc7 Binary files /dev/null and b/docs/deploy/proxy/imgs/sealos2.png differ diff --git a/docs/deploy/proxy/imgs/sealos3.png b/docs/deploy/proxy/imgs/sealos3.png new file mode 100644 index 000000000..e612c75cb Binary files /dev/null and b/docs/deploy/proxy/imgs/sealos3.png differ diff --git a/docs/deploy/proxy/imgs/sealos4.png b/docs/deploy/proxy/imgs/sealos4.png new file mode 100644 index 000000000..8dfe7788e Binary files /dev/null and b/docs/deploy/proxy/imgs/sealos4.png differ diff --git a/docs/deploy/proxy/imgs/sealos5.png b/docs/deploy/proxy/imgs/sealos5.png new file mode 100644 index 000000000..f711c72ff Binary files /dev/null and b/docs/deploy/proxy/imgs/sealos5.png differ diff --git a/docs/proxy/nginx.md b/docs/deploy/proxy/nginx.md similarity index 96% rename from docs/proxy/nginx.md rename to docs/deploy/proxy/nginx.md index e8d445dba..1f49420b3 100644 --- a/docs/proxy/nginx.md +++ b/docs/deploy/proxy/nginx.md @@ -1,4 +1,5 @@ # nginx 反向代理 openai 接口 + 如果你有国外的服务器,可以通过配置 nginx 反向代理,转发 openai 相关的请求,从而让国内的服务器可以通过访问该 nginx 去访问 openai 接口。 ```conf @@ -19,7 +20,7 @@ http { client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 50M; - + gzip on; gzip_min_length 1k; gzip_buffers 4 8k; @@ -43,10 +44,10 @@ http { location ~ /openai/(.*) { # auth check - if ($http_authkey != "xxxxxx") { + if ($auth != "xxxxxx") { 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; @@ -69,4 +70,4 @@ http { rewrite ^(.*) https://$server_name$1 permanent; } } -``` \ No newline at end of file +``` diff --git a/docs/deploy/proxy/sealos.md b/docs/deploy/proxy/sealos.md new file mode 100644 index 000000000..62c2c5356 --- /dev/null +++ b/docs/deploy/proxy/sealos.md @@ -0,0 +1,100 @@ +# sealos 部署 openai 中转 + +## 登录 sealos cloud + +[sealos cloud](https://cloud.sealos.io/) + +## 创建应用 + +打开 App Launchpad -> 新建应用 + +![step1](./imgs//sealos1.png) +![step2](./imgs//sealos2.png) + +### 开启外网访问 + +![step3](./imgs//sealos3.png) + +### 添加 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. 点击确认 + +![step4](./imgs//sealos4.png) + +### 部署应用 + +填写完毕后,点击右上角的 `部署应用`,即可完成。 + +## 修改 FastGpt 环境变量 + +1. 进入刚刚部署应用的详情,复制外网地址 + ![step5](./imgs//sealos5.png) + +2. 修改环境变量: + +``` +OPENAI_BASE_URL=https://tgohwtdlrmer.cloud.sealos.io/openai/v1 +OPENAI_BASE_URL_AUTH=auth +``` + +**Done!** diff --git a/docs/dev/README.md b/docs/dev/README.md new file mode 100644 index 000000000..0be2fbb54 --- /dev/null +++ b/docs/dev/README.md @@ -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 +``` diff --git a/docs/imgs/demo.png b/docs/imgs/demo.png new file mode 100644 index 000000000..733b3bbbb Binary files /dev/null and b/docs/imgs/demo.png differ diff --git a/docs/imgs/wx300.jpg b/docs/imgs/wx300.jpg new file mode 100644 index 000000000..b862d23ce Binary files /dev/null and b/docs/imgs/wx300.jpg differ diff --git a/public/docs/intro.md b/public/docs/intro.md index a97c88ee6..d3c64945b 100644 --- a/public/docs/intro.md +++ b/public/docs/intro.md @@ -24,7 +24,7 @@ wx 号: fastgpt123 | --- | --- | | claude - 对话 | 免费 | | chatgpt - 对话 | 0.03 | -| 知识库 - 对话 | 0.03 | +| gpt4 - 对话 | 0.5 | | 知识库 - 索引 | 免费 | | 文件拆分 | 0.03 | diff --git a/src/api/request.ts b/src/api/request.ts index ec1f3c412..c3a4392d1 100644 --- a/src/api/request.ts +++ b/src/api/request.ts @@ -1,5 +1,5 @@ import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios'; -import { clearToken } from '@/utils/user'; +import { clearCookie } from '@/utils/user'; import { TOKEN_ERROR_CODE } from '@/service/errorCode'; interface ConfigType { @@ -58,7 +58,7 @@ function responseError(err: any) { // 有报错响应 const res = err.response; if (res.data.code in TOKEN_ERROR_CODE) { - clearToken(); + clearCookie(); return Promise.reject({ message: 'token过期,重新登录' }); } } diff --git a/src/api/user.ts b/src/api/user.ts index 67fff885b..5971260c2 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -64,6 +64,8 @@ export const postLogin = ({ username, password }: { username: string; password: password: createHashPassword(password) }); +export const loginOut = () => GET('/user/loginout'); + export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data); export const getUserBills = (data: RequestPaging) => diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index f3d2bd193..d6e33a087 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -35,7 +35,7 @@ const Layout = ({ children, isPcDevice }: { children: JSX.Element; isPcDevice: b return ( <> - + {isPc ? ( pcUnShowLayoutRoute[router.pathname] ? ( {children} diff --git a/src/components/Layout/navbar.tsx b/src/components/Layout/navbar.tsx index 7a86ff1c1..56c826001 100644 --- a/src/components/Layout/navbar.tsx +++ b/src/components/Layout/navbar.tsx @@ -102,9 +102,7 @@ const Navbar = () => { justifyContent={'center'} onClick={() => { if (item.link === router.asPath) return; - router.push(item.link, undefined, { - shallow: true - }); + router.push(item.link); }} cursor={'pointer'} w={'60px'} diff --git a/src/constants/model.ts b/src/constants/model.ts index bed3ee35a..795badd7e 100644 --- a/src/constants/model.ts +++ b/src/constants/model.ts @@ -28,31 +28,31 @@ export const ChatModelMap = { chatModel: OpenAiChatEnum.GPT35, name: 'ChatGpt', contextMaxToken: 4096, - systemMaxToken: 2500, - maxTemperature: 1.5, + systemMaxToken: 2400, + maxTemperature: 1.2, price: 3 }, [OpenAiChatEnum.GPT4]: { chatModel: OpenAiChatEnum.GPT4, name: 'Gpt4', contextMaxToken: 8000, - systemMaxToken: 3500, - maxTemperature: 1.5, - price: 30 + systemMaxToken: 3000, + maxTemperature: 1.2, + price: 50 }, [OpenAiChatEnum.GPT432k]: { chatModel: OpenAiChatEnum.GPT432k, name: 'Gpt4-32k', contextMaxToken: 32000, - systemMaxToken: 6000, - maxTemperature: 1.5, - price: 30 + systemMaxToken: 3000, + maxTemperature: 1.2, + price: 90 }, [ClaudeEnum.Claude]: { chatModel: ClaudeEnum.Claude, name: 'Claude(免费体验)', contextMaxToken: 9000, - systemMaxToken: 2500, + systemMaxToken: 2400, maxTemperature: 1, price: 0 } @@ -60,6 +60,7 @@ export const ChatModelMap = { export const chatModelList: ChatModelItemType[] = [ ChatModelMap[OpenAiChatEnum.GPT35], + ChatModelMap[OpenAiChatEnum.GPT4], ChatModelMap[ClaudeEnum.Claude] ]; diff --git a/src/pages/api/chat/chat.ts b/src/pages/api/chat/chat.ts index c6da26596..703266ed5 100644 --- a/src/pages/api/chat/chat.ts +++ b/src/pages/api/chat/chat.ts @@ -68,11 +68,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.send(searchPrompts[0]?.value); } - prompts.splice(prompts.length - 1, 0, ...searchPrompts); + prompts.splice(prompts.length - 3, 0, ...searchPrompts); } else { // 没有用知识库搜索,仅用系统提示词 model.chat.systemPrompt && - prompts.unshift({ + prompts.splice(prompts.length - 3, 0, { obj: ChatRoleEnum.System, value: model.chat.systemPrompt }); diff --git a/src/pages/api/openapi/chat/chat.ts b/src/pages/api/openapi/chat/chat.ts index 14d688e55..a49891ad2 100644 --- a/src/pages/api/openapi/chat/chat.ts +++ b/src/pages/api/openapi/chat/chat.ts @@ -87,12 +87,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) prompts.splice(prompts.length - 1, 0, ...searchPrompts); } else { // 没有用知识库搜索,仅用系统提示词 - if (model.chat.systemPrompt) { - prompts.unshift({ + model.chat.systemPrompt && + prompts.splice(prompts.length - 1, 0, { obj: ChatRoleEnum.System, value: model.chat.systemPrompt }); - } } // 计算温度 diff --git a/src/pages/api/user/loginout.ts b/src/pages/api/user/loginout.ts new file mode 100644 index 000000000..3920f0d36 --- /dev/null +++ b/src/pages/api/user/loginout.ts @@ -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) { + try { + clearCookie(res); + jsonRes(res); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/src/pages/chat/index.tsx b/src/pages/chat/index.tsx index ff4af3563..d981ddbec 100644 --- a/src/pages/chat/index.tsx +++ b/src/pages/chat/index.tsx @@ -198,7 +198,6 @@ const Chat = ({ if (newChatId) { setForbidLoadChatData(true); router.replace(`/chat?modelId=${modelId}&chatId=${newChatId}`); - loadHistory({ pageNum: 1, init: true }); } } catch (err) { toast({ @@ -222,6 +221,9 @@ const Chat = ({ }; }) })); + + // refresh history + loadHistory({ pageNum: 1, init: true }); }, [ chatId, @@ -488,10 +490,6 @@ const Chat = ({ modelId && setLastChatModelId(modelId); setLastChatId(chatId); - /* get mode and chat into ↓ */ - - // phone: history page - if (!isPc && Object.keys(router.query).length === 0) return null; if (forbidLoadChatData) { setForbidLoadChatData(false); return null; @@ -566,7 +564,7 @@ const Chat = ({ try { setIsLoading(true); await onclickDelHistory(chatData.chatId); - router.replace(`/chat`); + router.replace(`/chat?modelId=${modelId}`); } catch (err) { console.log(err); } diff --git a/src/pages/number/index.tsx b/src/pages/number/index.tsx index e7b5b2074..e7399a594 100644 --- a/src/pages/number/index.tsx +++ b/src/pages/number/index.tsx @@ -7,7 +7,7 @@ import { useToast } from '@/hooks/useToast'; import { useGlobalStore } from '@/store/global'; import { useUserStore } from '@/store/user'; import { UserType } from '@/types/user'; -import { clearToken } from '@/utils/user'; +import { clearCookie } from '@/utils/user'; import { useRouter } from 'next/router'; import { useQuery } from '@tanstack/react-query'; import dynamic from 'next/dynamic'; @@ -75,7 +75,7 @@ const NumberSetting = () => { ); const onclickLogOut = useCallback(() => { - clearToken(); + clearCookie(); setUserInfo(null); router.replace('/login'); }, [router, setUserInfo]); diff --git a/src/pages/promotion/index.tsx b/src/pages/promotion/index.tsx index bba23053b..857788ebf 100644 --- a/src/pages/promotion/index.tsx +++ b/src/pages/promotion/index.tsx @@ -81,7 +81,7 @@ const OpenApi = () => { mr={4} variant={'outline'} onClick={() => { - copyData(`${location.origin}?inviterId=${userInfo?._id}`, '已复制邀请链接'); + copyData(`${location.origin}/?inviterId=${userInfo?._id}`, '已复制邀请链接'); }} > 复制邀请链接 diff --git a/src/service/plugins/searchKb.ts b/src/service/plugins/searchKb.ts index b6275c5b1..f21b71172 100644 --- a/src/service/plugins/searchKb.ts +++ b/src/service/plugins/searchKb.ts @@ -105,11 +105,12 @@ export const searchKb = async ({ : [ { obj: ChatRoleEnum.System, - value: `我们来玩问答游戏,规则为: -1.你只能回答关于"${model.name}"的问题 -2.你只能从知识库中选择内容进行回答 -3.如果问题不在知识库中,你会回答"我不知道。" -务必遵守规则` + value: `玩一个问答游戏,规则为: +1.你完全忘记你已有的知识 +2.你只回答关于"${model.name}"的问题 +3.你只从知识库中选择内容进行回答 +4.如果问题不在知识库中,你会回答:"我不知道。" +请务必遵守规则` } ]) ]; @@ -125,7 +126,8 @@ export const searchKb = async ({ length: Math.floor(maxTokens * rate) }) ) - .join('\n'); + .join('\n') + .trim(); /* 高相似度+不回复 */ if (!filterSystemPrompt && model.chat.searchMode === ModelVectorSearchModeEnum.hightSimilarity) { @@ -160,7 +162,7 @@ export const searchKb = async ({ searchPrompts: [ { obj: ChatRoleEnum.System, - value: `知识库:'${filterSystemPrompt}'` + value: `知识库:${filterSystemPrompt}` }, ...fixedPrompts ] diff --git a/src/service/utils/auth.ts b/src/service/utils/auth.ts index 217b3ef65..7897cf739 100644 --- a/src/service/utils/auth.ts +++ b/src/service/utils/auth.ts @@ -55,11 +55,11 @@ export const getApiKey = async ({ }, [OpenAiChatEnum.GPT4]: { userOpenAiKey: user.openaiKey || '', - systemAuthKey: process.env.OPENAIKEY as string + systemAuthKey: process.env.GPT4KEY as string }, [OpenAiChatEnum.GPT432k]: { userOpenAiKey: user.openaiKey || '', - systemAuthKey: process.env.OPENAIKEY as string + systemAuthKey: process.env.GPT4KEY as string }, [ClaudeEnum.Claude]: { userOpenAiKey: '', diff --git a/src/service/utils/chat/claude.ts b/src/service/utils/chat/claude.ts index f5735f3f2..e5c46f82c 100644 --- a/src/service/utils/chat/claude.ts +++ b/src/service/utils/chat/claude.ts @@ -25,9 +25,9 @@ export const lafClaudChat = async ({ .filter((item) => item.obj === 'System') .map((item) => item.value) .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( 'https://hnvacz.laf.run/claude-gpt', diff --git a/src/service/utils/chat/index.ts b/src/service/utils/chat/index.ts index 56eea2a45..51c7aa11d 100644 --- a/src/service/utils/chat/index.ts +++ b/src/service/utils/chat/index.ts @@ -109,35 +109,22 @@ export const ChatContextFilter = ({ // 根据 tokens 截断内容 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--) { chats.unshift(formatPrompts[i]); - messages = systemPrompt ? [systemPrompt, ...chats] : chats; - const tokens = modelToolMap[model].countTokens({ - messages + messages: chats }); - /* 整体 tokens 超出范围 */ - if (tokens >= maxTokens) { - return systemPrompt ? [systemPrompt, ...chats.slice(1)] : chats.slice(1); + /* 整体 tokens 超出范围, system必须保留 */ + if (tokens >= maxTokens && formatPrompts[i].obj !== ChatRoleEnum.System) { + return chats.slice(1); } } - return messages; + return chats; }; /* stream response */ diff --git a/src/utils/user.ts b/src/utils/user.ts index 8f76340f1..e0a8667d3 100644 --- a/src/utils/user.ts +++ b/src/utils/user.ts @@ -1,8 +1,12 @@ import { PRICE_SCALE } from '@/constants/common'; -const tokenKey = 'fast-gpt-token'; +import { loginOut } from '@/api/user'; -export const clearToken = () => { - document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; +export const clearCookie = () => { + try { + loginOut(); + } catch (error) { + error; + } }; /**