diff --git a/.github/ISSUE_TEMPLATE/bugs.md b/.github/ISSUE_TEMPLATE/bugs.md index 8aa370dcf..4c0f0bf1b 100644 --- a/.github/ISSUE_TEMPLATE/bugs.md +++ b/.github/ISSUE_TEMPLATE/bugs.md @@ -19,7 +19,7 @@ assignees: '' **你的版本** - [ ] 公有云版本 -- [ ] 私有部署版本 +- [ ] 私有部署版本, 具体版本号: **问题描述** diff --git a/docSite/assets/imgs/dataset_search_params1.png b/docSite/assets/imgs/dataset_search_params1.png index cea981ffd..159ddd9d9 100644 Binary files a/docSite/assets/imgs/dataset_search_params1.png and b/docSite/assets/imgs/dataset_search_params1.png differ diff --git a/docSite/assets/imgs/demo-dalle1.png b/docSite/assets/imgs/demo-dalle1.png index cbfca9114..094d2459f 100644 Binary files a/docSite/assets/imgs/demo-dalle1.png and b/docSite/assets/imgs/demo-dalle1.png differ diff --git a/docSite/assets/imgs/demo-dalle2.png b/docSite/assets/imgs/demo-dalle2.png index ea048f0fb..790f6df72 100644 Binary files a/docSite/assets/imgs/demo-dalle2.png and b/docSite/assets/imgs/demo-dalle2.png differ diff --git a/docSite/assets/imgs/google_search_1.png b/docSite/assets/imgs/google_search_1.png new file mode 100644 index 000000000..059cab5f0 Binary files /dev/null and b/docSite/assets/imgs/google_search_1.png differ diff --git a/docSite/assets/imgs/guide.png b/docSite/assets/imgs/guide.png index 657f02179..e42b41c06 100644 Binary files a/docSite/assets/imgs/guide.png and b/docSite/assets/imgs/guide.png differ diff --git a/docSite/assets/imgs/question_guide.png b/docSite/assets/imgs/question_guide.png new file mode 100644 index 000000000..30c518c6f Binary files /dev/null and b/docSite/assets/imgs/question_guide.png differ diff --git a/docSite/assets/imgs/sharelinkProcess.png b/docSite/assets/imgs/sharelink_process.png similarity index 100% rename from docSite/assets/imgs/sharelinkProcess.png rename to docSite/assets/imgs/sharelink_process.png diff --git a/docSite/assets/imgs/tts_setting.png b/docSite/assets/imgs/tts_setting.png new file mode 100644 index 000000000..d6e84ee03 Binary files /dev/null and b/docSite/assets/imgs/tts_setting.png differ diff --git a/docSite/assets/imgs/tts_setting2.png b/docSite/assets/imgs/tts_setting2.png new file mode 100644 index 000000000..bb06c2304 Binary files /dev/null and b/docSite/assets/imgs/tts_setting2.png differ diff --git a/docSite/assets/imgs/variable.png b/docSite/assets/imgs/variable.png index dcd3f9d96..885d08d5f 100644 Binary files a/docSite/assets/imgs/variable.png and b/docSite/assets/imgs/variable.png differ diff --git a/docSite/assets/imgs/variable2.png b/docSite/assets/imgs/variable2.png index 03ef463aa..c7fc78d61 100644 Binary files a/docSite/assets/imgs/variable2.png and b/docSite/assets/imgs/variable2.png differ diff --git a/docSite/assets/imgs/variable3.png b/docSite/assets/imgs/variable3.png new file mode 100644 index 000000000..d37eb4586 Binary files /dev/null and b/docSite/assets/imgs/variable3.png differ diff --git a/docSite/assets/imgs/variable4.png b/docSite/assets/imgs/variable4.png new file mode 100644 index 000000000..dfeac60ff Binary files /dev/null and b/docSite/assets/imgs/variable4.png differ diff --git a/docSite/assets/imgs/variable5.png b/docSite/assets/imgs/variable5.png new file mode 100644 index 000000000..2d47bae45 Binary files /dev/null and b/docSite/assets/imgs/variable5.png differ diff --git a/docSite/content/docs/development/configuration.md b/docSite/content/docs/development/configuration.md index 847261d9f..10db56a94 100644 --- a/docSite/content/docs/development/configuration.md +++ b/docSite/content/docs/development/configuration.md @@ -33,8 +33,8 @@ llm模型全部合并 "maxResponse": 4000, // 最大回复 "quoteMaxToken": 13000, // 最大引用内容 "maxTemperature": 1.2, // 最大温度 - "charsPointsPrice": 0, - "censor": false, + "charsPointsPrice": 0, // n积分/1k token(商业版) + "censor": false, // 是否开启敏感校验(商业版) "vision": false, // 是否支持图片输入 "datasetProcess": true, // 是否设置为知识库处理模型(QA),务必保证至少有一个为true,否则知识库会报错 "usedInClassify": true, // 是否用于问题分类(务必保证至少有一个为true) @@ -46,7 +46,7 @@ llm模型全部合并 "customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型 "customExtractPrompt": "", // 自定义内容提取提示词 "defaultSystemChatPrompt": "", // 对话默认携带的系统提示词 - "defaultConfig":{} // LLM默认配置,可以针对不同模型设置特殊值(比如 GLM4 的 top_p + "defaultConfig":{} // 请求API时,挟带一些默认配置(比如 GLM4 的 top_p) }, { "model": "gpt-4-0125-preview", @@ -133,7 +133,7 @@ llm模型全部合并 ## 关于模型 logo -统一放置在项目的`public/imgs/model/xxx`目录中,目前内置了以下几种,如果有需要,可以PR增加。 +统一放置在项目的`public/imgs/model/xxx`目录中,目前内置了以下几种,如果有需要,可以PR增加。默认头像为 Hugging face 的 logo~ - /imgs/model/baichuan.svg - 百川 - /imgs/model/chatglm.svg - 智谱 @@ -147,7 +147,7 @@ llm模型全部合并 ## 特殊模型 -### ReRank 接入 +### ReRank 接入(私有部署) 请使用 4.6.6-alpha 以上版本,配置文件中的 `reRankModels` 为重排模型,虽然是数组,不过目前仅有第1个生效。 @@ -168,3 +168,23 @@ llm模型全部合并 ] } ``` + +### ReRank 接入(Cohere) + +这个重排模型对中文不是很好,不如 bge 的好用。 + +1. 申请 Cohere 官方 Key: https://dashboard.cohere.com/api-keys +2. 修改 FastGPT 配置文件 + +```json +{ + "reRankModels": [ + { + "model": "rerank-multilingual-v2.0", // 这里的model需要对应 cohere 的模型名 + "name": "检索重排", // 随意 + "requestUrl": "https://api.cohere.ai/v1/rerank", + "requestAuth": "Coherer上申请的key" + } + ] +} +``` diff --git a/docSite/content/docs/development/custom-models/reranker.md b/docSite/content/docs/development/custom-models/reranker.md index ab07c3c01..137636453 100644 --- a/docSite/content/docs/development/custom-models/reranker.md +++ b/docSite/content/docs/development/custom-models/reranker.md @@ -44,7 +44,7 @@ weight: 910 ### docker 部署 -+ 镜像名: `luanshaotong/reranker:v0.1` ++ 镜像名: `luanshaotong/reranker:v0.2` + 端口号: 6006 + 大小:约8GB @@ -56,12 +56,12 @@ ACCESS_TOKEN=mytoken **运行命令示例** - 无需GPU环境,使用CPU运行 ```sh -docker run -d --name reranker -p 6006:6006 -e ACCESS_TOKEN=mytoken luanshaotong/reranker:v0.1 +docker run -d --name reranker -p 6006:6006 -e ACCESS_TOKEN=mytoken luanshaotong/reranker:v0.2 ``` - 需要CUDA 11.7环境 ```sh -docker run -d --gpus all --name reranker -p 6006:6006 -e ACCESS_TOKEN=mytoken luanshaotong/reranker:v0.1 +docker run -d --gpus all --name reranker -p 6006:6006 -e ACCESS_TOKEN=mytoken luanshaotong/reranker:v0.2 ``` **docker-compose.yml示例** @@ -69,7 +69,7 @@ docker run -d --gpus all --name reranker -p 6006:6006 -e ACCESS_TOKEN=mytoken lu version: "3" services: reranker: - image: luanshaotong/reranker:v0.1 + image: luanshaotong/reranker:v0.2 container_name: reranker # GPU运行环境,如果宿主机未安装,将deploy配置隐藏即可 deploy: diff --git a/docSite/content/docs/development/docker.md b/docSite/content/docs/development/docker.md index 7ec9aea71..99973f842 100644 --- a/docSite/content/docs/development/docker.md +++ b/docSite/content/docs/development/docker.md @@ -85,7 +85,7 @@ brew install orbstack 非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载下面2个链接的文件: [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/deploy/fastgpt/docker-compose.yml),[config.json](https://github.com/labring/FastGPT/blob/main/projects/app/data/config.json) -**注意: `docker-compose.yml` 配置文件中 Mongo 为 5.x,部分服务器不支持,需手动更改其镜像版本为 4.4.24** +**注意: `docker-compose.yml` 配置文件中 Mongo 为 5.x,部分服务器不支持,需手动更改其镜像版本为 4.4.24**(需要自己在docker hub下载,阿里云镜像没做备份) ```bash mkdir fastgpt @@ -94,53 +94,22 @@ curl -O https://raw.githubusercontent.com/labring/FastGPT/main/files/deploy/fast curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data/config.json ``` -## 三、修改 docker-compose.yml 的环境变量 - -修改`docker-compose.yml`中的`OPENAI_BASE_URL`(API 接口的地址,需要加/v1)和`CHAT_API_KEY`(API 接口的凭证)。 - -使用 OneAPI 的话,OPENAI_BASE_URL=OneAPI访问地址/v1;CHAT_API_KEY=令牌 - - -## 四、启动容器 +## 三、启动容器 在 docker-compose.yml 同级目录下执行 ```bash -# 进入项目目录 -cd 项目目录 # 启动容器 -docker-compose pull docker-compose up -d +# 重启一次oneapi(由于OneAPI的默认Key有点问题,不重启的话会提示找不到渠道,临时手动重启一次解决,等待作者修复) +docker restart oneapi ``` -## 五、初始化 Mongo 副本集(4.6.8以前可忽略) +## 四、打开 OneAPI 添加模型 -FastGPT 4.6.8 后使用了 MongoDB 的事务,需要运行在副本集上。副本集没法自动化初始化,需手动操作。 +可以通过`ip:3001`访问OneAPI,默认账号为`root`密码为`123456`。 -```bash -# 查看 mongo 容器是否正常运行 -docker ps -# 进入容器 -docker exec -it mongo bash - -# 连接数据库(这里要填Mongo的用户名和密码) -mongo -u myusername -p mypassword --authenticationDatabase admin - -# 初始化副本集。如果需要外网访问,mongo:27017 可以改成 ip:27017。但是需要同时修改 FastGPT 连接的参数(MONGODB_URI=mongodb://myname:mypassword@mongo:27017/fastgpt?authSource=admin => MONGODB_URI=mongodb://myname:mypassword@ip:27017/fastgpt?authSource=admin) -rs.initiate({ - _id: "rs0", - members: [ - { _id: 0, host: "mongo:27017" } - ] -}) -# 检查状态。如果提示 rs0 状态,则代表运行成功 -rs.status() -``` - -**关于 host: "mongo:27017" 说明** - -1. mongo:27017 代表指向同一个 docker 网络的 mongo 容器的 27017 服务。因此,如果使用该参数,外网是无法访问到数据库的。 -2. ip:27017 (ip替换成公网IP):代表通过你的公网IP进行访问。如果用该方法,同时需要修改 docker-compose 中 mongo 的连接参数,因为默认是用 `mongo:27017` 进行连接。 +在OneApi中添加合适的AI模型渠道。 ## 五、访问 FastGPT @@ -148,22 +117,24 @@ rs.status() 如果需要域名访问,请自行安装并配置 Nginx。 +首次运行,会自动初始化 root 用户,密码为 `1234`(与环境变量中的`DEFAULT_ROOT_PSW`一致),日志里会提示一次`MongoServerError: Unable to read from a snapshot due to pending collection catalog changes;`可忽略。 ## FAQ -### Mongo 启动失败 +### Mongo 副本集自动初始化失败 -docker-compose 示例优化 Mongo 副本集参数,不需要手动创建再挂载。如果无法启动,可以尝试更换下面的脚本: +最新的 docker-compose 示例优化 Mongo 副本集初始化,实现了全自动。目前在 unbuntu20,22 centos7, wsl2, mac, window 均通过测试。如果你的环境特殊,可以手动初始化副本集: -1. 终端中执行: +1. 终端中执行下面命令,创建mongo密钥: ```bash openssl rand -base64 756 > ./mongodb.key chmod 600 ./mongodb.key +# 修改密钥权限,部分系统是admin,部分是root chown 999:root ./mongodb.key ``` -2. 修改 docker-compose.yml: +2. 修改 docker-compose.yml,挂载密钥 ```yml mongo: @@ -191,4 +162,109 @@ docker-compose down docker-compose up -d ``` -4. 进入容器执行副本集合初始化(看上方) +4. 进入容器执行副本集合初始化 + +```bash +# 查看 mongo 容器是否正常运行 +docker ps +# 进入容器 +docker exec -it mongo bash + +# 连接数据库(这里要填Mongo的用户名和密码) +mongo -u myusername -p mypassword --authenticationDatabase admin + +# 初始化副本集。如果需要外网访问,mongo:27017 。如果需要外网访问,需要增加Mongo连接参数:directConnection=true +rs.initiate({ + _id: "rs0", + members: [ + { _id: 0, host: "mongo:27017" } + ] +}) +# 检查状态。如果提示 rs0 状态,则代表运行成功 +rs.status() +``` + +### 如何修改API地址和密钥 + +默认是写了OneAPi的连接地址和密钥,可以通过修改`docker-compose.yml`中,fastgpt容器的环境变量实现。 + + +`OPENAI_BASE_URL`(API 接口的地址,需要加/v1) +`CHAT_API_KEY`(API 接口的凭证)。 + +修改完后重启: + +```bash +docker-compose down +docker-compose up -d +``` + +### 如何更新版本? + +1. 查看[更新文档](/docs/development/upgrading/intro/),确认要升级的版本,避免跨版本升级。 +2. 修改镜像 tag 到指定版本 +3. 执行下面命令会自动拉取镜像: + + ```bash + docker-compose pull + docker-compose up -d + ``` + +4. 执行初始化脚本(如果有) + +### 如何自定义配置文件? + +修改`config.json`文件,并执行`docker-compose down`再执行`docker-compose up -d`重起容器。具体配置,参考[配置详解](/docs/development/configuration)。 + +### 如何检查自定义配置文件是否挂载 + +1. `docker logs fastgpt` 可以查看日志,在启动容器后,第一次请求网页,会进行配置文件读取,可以看看有没有读取成功以及有无错误日志。 +2. `docker exec -it fastgpt sh` 进入 FastGPT 容器,可以通过`ls data`查看目录下是否成功挂载`config.json`文件。可通过`cat data/config.json`查看配置文件。 + +**可能不生效的原因** + +1. 挂载目录不正确 +2. 配置文件不正确,日志中会提示`invalid json`,配置文件需要是标准的 JSON 文件。 +3. 修改后,没有`docker-compose down`再`docker-compose up -d`,restart是不会重新挂载文件的。 + +### 如何检查环境变量是否正常加载 + +1. `docker exec -it fastgpt sh` 进入 FastGPT 容器。 +2. 直接输入`env`命令查看所有环境变量。 + + +### 为什么无法连接`本地模型`镜像。 + +`docker-compose.yml`中使用了桥接的模式建立了`fastgpt`网络,如想通过0.0.0.0或镜像名访问其它镜像,需将其它镜像也加入到网络中。 + +### 端口冲突怎么解决? + +docker-compose 端口定义为:`映射端口:运行端口`。 + +桥接模式下,容器运行端口不会有冲突,但是会有映射端口冲突,只需将映射端口修改成不同端口即可。 + +如果`容器1`需要连接`容器2`,使用`容器2:运行端口`来进行连接即可。 + +(自行补习 docker 基本知识) + +### relation "modeldata" does not exist + +PG 数据库没有连接上/初始化失败,可以查看日志。FastGPT 会在每次连接上 PG 时进行表初始化,如果报错会有对应日志。 + +1. 检查数据库容器是否正常启动 +2. 非 docker 部署的,需要手动安装 pg vector 插件 +3. 查看 fastgpt 日志,有没有相关报错 + +### Operation `auth_codes.findOne()` buffering timed out after 10000ms + +mongo连接失败,查看mongo的运行状态对应日志。 + +可能原因: + +1. mongo 服务有没有起来(有些 cpu 不支持 AVX,无法用 mongo5,需要换成 mongo4.x,可以docker hub找个最新的4.x,修改镜像版本,重新运行) +2. 连接数据库的环境变量填写错误(账号密码,注意host和port,非容器网络连接,需要用公网ip并加上 directConnection=true) +3. 副本集启动失败。导致容器一直重启。 + +### 首次部署,root用户提示未注册 + +日志会有错误提示。大概率是没有启动 Mongo 副本集模式。 diff --git a/docSite/content/docs/development/faq.md b/docSite/content/docs/development/faq.md index 12b22c2ba..5d73a23a4 100644 --- a/docSite/content/docs/development/faq.md +++ b/docSite/content/docs/development/faq.md @@ -23,22 +23,10 @@ images: [] 可以。需要准备好向量模型和LLM模型。 -### insufficient_user_quota user quota is not enough - -OneAPI 账号的余额不足,默认 root 用户只有 200 刀,可以手动修改。 - -### xxx渠道找不到 - -OneAPI 中没有配置该模型渠道。或者是修改了配置文件中一部分的模型,但没有全部修改。 - ### 页面中可以正常回复,API 报错 页面中是用 stream=true 模式,所以API也需要设置 stream=true 来进行测试。部分模型接口(国产居多)非 Stream 的兼容有点垃圾。 -### Incorrect API key provided: sk-xxxx.You can find your api Key at xxx - -OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并重启容器(先 docker-compose down 然后再 docker-compose up -d 运行一次)。可以`exec`进入容器,`env`查看环境变量是否生效。 - ### 其他模型没法进行问题分类/内容提取 需要给其他模型配置`toolChoice=false`,就会默认走提示词模式。目前内置提示词仅针对了商业模型API进行测试,国内外的商业模型基本都可用。 @@ -54,7 +42,7 @@ OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并 1. 问题补全需要经过一轮AI生成。 2. 会进行3~5轮的查询,如果数据库性能不足,会有明显影响。 -### 模型响应为空 +### 模型响应为空(core.chat.Chat API is error or undefined) 1. 检查 key 问题。 2. 如果是国内模型,可能是命中风控了。 @@ -62,77 +50,32 @@ OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并 ### 知识库索引没有进度 -先看日志报错信息。 +先看日志报错信息。有以下几种情况: 1. 可以对话,但是索引没有进度:没有配置向量模型(vectorModels) 2. 不能对话,也不能索引:API调用失败。可能是没连上OneAPI或OpenAI 3. 有进度,但是非常慢:api key不行,OpenAI的免费号,一分钟只有3次还是60次。一天上限200次。 -## 三、Docker 部署常见问题 +### Connection error -### 如何更新? +网络异常。国内服务器无法请求OpenAI,自行检查与AI模型的连接是否正常。 -1. 查看[更新文档](/docs/development/upgrading/intro/),确认要升级的版本,避免跨版本升级。 -2. 修改镜像 tag 到指定版本 -3. 执行下面命令会自动拉取镜像: +或者是FastGPT请求不到 OneAPI(没放同一个网络) - ```bash - docker-compose pull - docker-compose up -d - ``` +## 三、常见的 OneAPI 错误 -4. 执行初始化脚本(如果有) +### insufficient_user_quota user quota is not enough -### 如何自定义配置文件? +OneAPI 账号的余额不足,默认 root 用户只有 200 刀,可以手动修改。 -修改`config.json`文件,并执行`docker-compose down`再执行`docker-compose up -d`重起容器。具体配置,参考[配置详解](/docs/development/configuration)。 +### xxx渠道找不到 -### 如何检查自定义配置文件是否挂载 +OneAPI 中没有配置该模型渠道。或者是修改了配置文件中一部分的模型,但没有全部修改。 -1. `docker logs fastgpt` 可以查看日志,在启动容器后,第一次请求网页,会进行配置文件读取,可以看看有没有读取成功以及有无错误日志。 -2. `docker exec -it fastgpt sh` 进入 FastGPT 容器,可以通过`ls data`查看目录下是否成功挂载`config.json`文件。可通过`cat data/config.json`查看配置文件。 +如果OneAPI中,没有配置对应的模型,`config.json`中也不要配置,否则容易报错。 -**可能不生效的原因** +### Incorrect API key provided: sk-xxxx.You can find your api Key at xxx -1. 挂载目录不正确 -2. 配置文件不正确,日志中会提示`invalid json`,配置文件需要是标准的 JSON 文件。 -3. 修改后,没有`docker-compose down`再`docker-compose up -d`,restart是不会重新挂载文件的。 +OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并重启容器(先 docker-compose down 然后再 docker-compose up -d 运行一次)。 -### 如何检查环境变量是否正常加载 - -1. `docker exec -it fastgpt sh` 进入 FastGPT 容器。 -2. 直接输入`env`命令查看所有环境变量。 - - -### 为什么无法连接`本地模型`镜像。 - -`docker-compose.yml`中使用了桥接的模式建立了`fastgpt`网络,如想通过0.0.0.0或镜像名访问其它镜像,需将其它镜像也加入到网络中。 - -### 端口冲突怎么解决? - -docker-compose 端口定义为:`映射端口:运行端口`。 - -桥接模式下,容器运行端口不会有冲突,但是会有映射端口冲突,只需将映射端口修改成不同端口即可。 - -如果`容器1`需要连接`容器2`,使用`容器2:运行端口`来进行连接即可。 - -(自行补习 docker 基本知识) - -### relation "modeldata" does not exist - -PG 数据库没有连接上/初始化失败,可以查看日志。FastGPT 会在每次连接上 PG 时进行表初始化,如果报错会有对应日志。 - -1. 检查数据库容器是否正常启动 -2. 非 docker 部署的,需要手动安装 pg vector 插件 -3. 查看 fastgpt 日志,有没有相关报错 - -### Operation `auth_codes.findOne()` buffering timed out after 10000ms - -mongo连接失败,检查 -1. mongo 服务有没有起来(有些 cpu 不支持 AVX,无法用 mongo5,需要换成 mongo4.x,可以dockerhub找个最新的4.x,修改镜像版本,重新运行) -2. 环境变量(账号密码,注意host和port) -3. 副本集启动失败,一直在重启:没挂载mongo key;key没有权限; - -### 首次部署,root用户提示未注册 - -没有启动 Mongo 副本集模式。 +可以`exec`进入容器,`env`查看环境变量是否生效。 \ No newline at end of file diff --git a/docSite/content/docs/development/intro.md b/docSite/content/docs/development/intro.md index 635181c8d..f99d6abb3 100644 --- a/docSite/content/docs/development/intro.md +++ b/docSite/content/docs/development/intro.md @@ -48,7 +48,7 @@ git clone git@github.com:/FastGPT.git 第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。 -Mongo 数据库需要修改副本集的`host`,从原来的`mongo:27017`修改为`ip:27017`(ip为对应的公网IP)。 +Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnection=true` 参数,才能连接上副本集的数据库。 ### 4. 初始配置 diff --git a/docSite/content/docs/development/one-api.md b/docSite/content/docs/development/one-api.md index 30e15d967..f544927be 100644 --- a/docSite/content/docs/development/one-api.md +++ b/docSite/content/docs/development/one-api.md @@ -1,6 +1,6 @@ --- -title: '接入微软、ChatGLM、本地模型等' -description: '部署和接入 OneAPI,实现对各种大模型的支持' +title: '部署和使用OneAPI,实现Azure、ChatGLM、本地模型接入' +description: '部署和使用OneAPI,实现Azure、ChatGLM、本地模型接入' icon: 'Api' draft: false toc: true @@ -13,9 +13,17 @@ weight: 708 ## FastGPT 与 OneAPI 关系 +可以把 OneAPI 当做一个网关。 + ![](/imgs/sealos-fastgpt.webp) -## MySQL 版本 +## 部署 + +### docker 版本 + +已加入最新的`docker-compose.yml`文件中。 + +### Sealos - MySQL 版本 MySQL 版本支持多实例,高并发。 @@ -25,7 +33,7 @@ MySQL 版本支持多实例,高并发。 部署完后会跳转「应用管理」,数据库在另一个应用「数据库」中。需要等待 1~3 分钟数据库运行后才能访问成功。 -## SqlLite 版本 +### Sealos - SqlLite 版本 SqlLite 版本不支持多实例,适合个人小流量使用,但是价格非常便宜。 @@ -130,7 +138,7 @@ CHAT_API_KEY=sk-xxxxxx "customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型 "customExtractPrompt": "", // 自定义内容提取提示词 "defaultSystemChatPrompt": "", // 对话默认携带的系统提示词 - "defaultConfig":{} // 对话默认配置(比如 GLM4 的 top_p + "defaultConfig":{} // 请求API时,挟带一些默认配置(比如 GLM4 的 top_p) } ... ], diff --git a/docSite/content/docs/development/openapi/share.md b/docSite/content/docs/development/openapi/share.md index 1033d4390..8977ba5d1 100644 --- a/docSite/content/docs/development/openapi/share.md +++ b/docSite/content/docs/development/openapi/share.md @@ -36,7 +36,7 @@ weight: 860 ### 触发流程 -![](/imgs/sharelinkProcess.png) +![](/imgs/sharelink_process.png) ## 配置教程 ### 1. 配置身份校验地址 diff --git a/docSite/content/docs/development/upgrading/468.md b/docSite/content/docs/development/upgrading/468.md index ad5c9d9b1..1b579ab0b 100644 --- a/docSite/content/docs/development/upgrading/468.md +++ b/docSite/content/docs/development/upgrading/468.md @@ -9,7 +9,7 @@ weight: 828 ## docker 部署 - 手动更新 Mongo -1. 修改 docker-compose.yml 的mongo部分,补上`command`和`mongodb.key` +1. 修改 docker-compose.yml 的mongo部分,补上`command`和`entrypoint` ```yml mongo: @@ -22,44 +22,51 @@ mongo: - fastgpt command: mongod --keyFile /data/mongodb.key --replSet rs0 environment: - # 这里密码不用变。 - - MONGO_INITDB_ROOT_USERNAME=myname - - MONGO_INITDB_ROOT_PASSWORD=mypassword + # 这里密码注意要和以前的一致 + - MONGO_INITDB_ROOT_USERNAME=username + - MONGO_INITDB_ROOT_PASSWORD=password volumes: - ./mongo/data:/data/db - - ./mongodb.key:/data/mongodb.key + entrypoint: + - bash + - -c + - | + openssl rand -base64 128 > /data/mongodb.key + chmod 400 /data/mongodb.key + chown 999:999 /data/mongodb.key + echo 'const isInited = rs.status().ok === 1 + if(!isInited){ + rs.initiate({ + _id: "rs0", + members: [ + { _id: 0, host: "mongo:27017" } + ] + }) + }' > /data/initReplicaSet.js + # 启动MongoDB服务 + exec docker-entrypoint.sh "$@" & + + # 等待MongoDB服务启动 + until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')" > /dev/null 2>&1; do + echo "Waiting for MongoDB to start..." + sleep 2 + done + + # 执行初始化副本集的脚本 + mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js + + # 等待docker-entrypoint.sh脚本执行的MongoDB服务进程 + wait $! ``` -2. 创建 mongo 密钥 +2. 重启 MongoDB ```bash -cd 项目目录 -# 创建 mongo 密钥 -openssl rand -base64 756 > ./mongodb.key -# 600不行可以用chmod 999 -chmod 600 ./mongodb.key -chown 999:root ./mongodb.key # 重启 Mongo docker-compose down docker-compose up -d ``` -3. 进入容器初始化部分集合 - -```bash -docker exec -it mongo bash -mongo -u myname -p mypassword --authenticationDatabase admin -# 初始化副本集。如果需要外网访问,mongo:27017 可以改成 ip:27017。但是需要同时修改 FastGPT 连接的参数(MONGODB_URI=mongodb://myname:mypassword@mongo:27017/fastgpt?authSource=admin => MONGODB_URI=mongodb://myname:mypassword@ip:27017/fastgpt?authSource=admin) -rs.initiate({ - _id: "rs0", - members: [ - { _id: 0, host: "mongo:27017" } - ] -}) -# 检查状态。如果提示 rs0 状态,则代表运行成功 -rs.status() -``` - ## Sealos 部署 - 无需更新 Mongo ## 修改配置文件 diff --git a/docSite/content/docs/development/upgrading/47.md b/docSite/content/docs/development/upgrading/47.md index 420eac63e..54f5b4cec 100644 --- a/docSite/content/docs/development/upgrading/47.md +++ b/docSite/content/docs/development/upgrading/47.md @@ -24,6 +24,29 @@ curl --location --request POST 'https://{{host}}/api/admin/initv47' \ 脚本功能: 1. 初始化插件的 parentId +## 升级 ReRank 模型 + +4.7对ReRank模型进行了格式变动,兼容 cohere 的格式,可以直接使用 cohere 提供的 API。如果是本地的 ReRank 模型,需要修改镜像为:`luanshaotong/reranker:v0.2` 。 + +cohere的重排模型对中文不是很好,感觉不如 bge 的好用,接入教程如下: + +1. 申请 Cohere 官方 Key: https://dashboard.cohere.com/api-keys +2. 修改 FastGPT 配置文件 + +```json +{ + "reRankModels": [ + { + "model": "rerank-multilingual-v2.0", // 这里的 model 需要对应 cohere 的模型名 + "name": "检索重排", // 随意 + "requestUrl": "https://api.cohere.ai/v1/rerank", + "requestAuth": "Coherer上申请的key" + } + ] +} +``` + + ## V4.7 更新说明 1. 新增 - 工具调用模块,可以让LLM模型根据用户意图,动态的选择其他模型或插件执行。 @@ -37,3 +60,4 @@ curl --location --request POST 'https://{{host}}/api/admin/initv47' \ 9. 新增 - 支持 http url 使用变量。 10. 修复 - 469 的提取的提示词容易造成幻觉。 11. 修复 - PG HNSW索引未实际生效问题,本次更新后,搜索速度大幅度提升(但是可能会出现精度损失,如果出现精度损失需要参考PgVector文档,对索引进行调整)。详细见:https://github.com/pgvector/pgvector?tab=readme-ov-file#troubleshooting +12. 新增 - Rerank 模型兼容 [cohere的格式](https://docs.cohere.com/reference/rerank-1),可以直接使用 cohere 的 rerank 模型。 diff --git a/docSite/content/docs/workflow/modules/guide.md b/docSite/content/docs/workflow/modules/guide.md index de9c81e38..b5d083b37 100644 --- a/docSite/content/docs/workflow/modules/guide.md +++ b/docSite/content/docs/workflow/modules/guide.md @@ -13,6 +13,59 @@ weight: 353 - 无外部输入 - 不参与实际调度 -如图,可以在用户提问前给予一定引导。并可以设置引导问题。 +## 作用 + +可以配置欢迎语、全局变量、语言播报等,类似于系统设置,不参与工作流的运行。 ![](/imgs/guide.png) + +### 欢迎语 + +会在对话开始前发送一个欢迎语。该功能只在网页生效,API是无效的,并且该欢迎语不会被加入 AI 的对话记录中。 + +可以通过特殊的 Markdown 语法,来实现快速提问。 如图中的: + +``` +[剧情介绍] +[导演是谁] +``` + +### 全局变量 + +| | | +| ----- | ----- | +| ![](/imgs/variable.png) | ![](/imgs/variable2.png) | + +如上图中,我们配置了一个名为`测试`, key为`test`的变量,类型为`文本`的全局变量。并在对话中,通过`{{test}}`来引用该变量。 + + +| | | +| ----- | ----- | +| ![](/imgs/variable3.png) | ![](/imgs/variable4.png) | + +从上图中,可以看出,实际的提示词从:`这是一个变量测试: {{test}}` 变成了: `这是一个变量测试: 变量测试`,因为`{{test}}` 被变量替换了。 + +变量在工作流中大部分的`文本输入框`都生效,例如:HTTP模块的URL和参数、各种功能块的提示词。 + +有一个特殊类型的变量,交`外部传入`。这种变量不需要用户填写,而是直接在调用时实时传入。目前支持从分享链接的Query和API调用的`variables`对象中获取。 + +![](/imgs/variable4.png) + + +------ + +除了自定义的全局变量外,还有一些系统参数可以选择: + ++ **cTime**: 当前时间,字符串格式,例如:2023/3/3 20:22 + +### 语音播报 + +| | | +| ----- | ----- | +| ![](/imgs/tts_setting.png) | ![](/imgs/tts_setting2.png) | + +### 猜你想问 + +开启后,每次对话结束,会发送最近的6条对话记录给AI,AI会根据这些对话记录,给出 3 个可能的问题。 + +![](/imgs/question_guide.png) diff --git a/docSite/content/docs/workflow/modules/tfswitch.md b/docSite/content/docs/workflow/modules/tfswitch.md index ef1e9d97e..4b7998eb3 100644 --- a/docSite/content/docs/workflow/modules/tfswitch.md +++ b/docSite/content/docs/workflow/modules/tfswitch.md @@ -21,7 +21,40 @@ weight: 362 也可以增加自定义规则来补充输出 false 的内容,每行代表一个匹配规则,支持正则表达式。 -根据上方示例图的匹配规则,当我们输入`123` `hi` `你好` 和任意手机号码时(正则匹配)同样也会输出 False 。 +**例子1** + +不填写任何自定义 False 规则。 + +| 输入 | 输出 | +| --- | --- | +| 123 | true | +| 这是一段文本 | true | +| false | false | +| 0 | false | +| null | false | + +**例子2** + +自定义 False 规则: + +``` +123 +你好 +aa +/dd/ +``` + +| 输入 | 输出 | 说明 | +| --- | --- | --- | +| 123 | false | 命中自定义 false 规则 | +| 这是一段文本 | true | 未命中 | +| false | false | 命中自定义 内置 规则 | +| 0 | false | 命中自定义 内置 规则 | +| null | false | 命中自定义 内置 规则 | +| aa | false | 命中自定义 false 规则 | +| aaa | true | 未命中 | +| bb | false | 命中自定义 false 规则 | +| bbb | false | 命中自定义 false 规则(正则匹配通过) | ## 作用 diff --git a/docSite/content/docs/workflow/modules/variable.md b/docSite/content/docs/workflow/modules/variable.md deleted file mode 100644 index 90b1541d2..000000000 --- a/docSite/content/docs/workflow/modules/variable.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: "全局变量" -description: "FastGPT 全局变量模块介绍" -icon: "variables" -draft: false -toc: true -weight: 361 ---- - -## 特点 - -- 仅可添加 1 个 -- 需要手动配置 -- 对其他模块有影响 -- 可作为用户引导 - -## 说明 - -可以在对话前设置一些问题,让用户输入或选择,并将用户输入/选择的结果注入到其他模块中。目前仅会注入到 string 类型的数据里(对应蓝色圆圈的输入)。 - -如下图,定义了两个变量:目标语言和下拉框测试(忽略) - -用户在对话前会被要求先填写目标语言,配合用户引导,我们就构建了一个简单的翻译机器人。**目标语言**的 `key:language` 被写入到【AI 对话】模块的限定词里。 - -![](/imgs/variable.png) - -通过完整对话记录我们可以看到,实际的限定词从:“将我的问题直接翻译成{{language}}” 变成了 “将我的问题直接翻译成英语”,因为 {{language}} 被变量替换了。 - -![](/imgs/variable2.png) - -## 系统级变量 - -除了用户自定义设置的变量外,还会有一些系统变量: - -+ **cTime**: 当前时间。例如:2023/3/3 20:22 \ No newline at end of file diff --git a/files/deploy/fastgpt/docker-compose.yml b/files/deploy/fastgpt/docker-compose.yml index 67f3ad12a..969c3c484 100644 --- a/files/deploy/fastgpt/docker-compose.yml +++ b/files/deploy/fastgpt/docker-compose.yml @@ -1,5 +1,7 @@ -# 非 host 版本, 不使用本机代理 -# (不懂 Docker 的,只需要关心 OPENAI_BASE_URL 和 CHAT_API_KEY 即可!) +# 数据库的默认账号和密码仅首次运行时设置有效 +# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~ +# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。 + version: '3.3' services: pg: @@ -39,11 +41,33 @@ services: openssl rand -base64 128 > /data/mongodb.key chmod 400 /data/mongodb.key chown 999:999 /data/mongodb.key - exec docker-entrypoint.sh $$@ + echo 'const isInited = rs.status().ok === 1 + if(!isInited){ + rs.initiate({ + _id: "rs0", + members: [ + { _id: 0, host: "mongo:27017" } + ] + }) + }' > /data/initReplicaSet.js + # 启动MongoDB服务 + exec docker-entrypoint.sh "$@" & + + # 等待MongoDB服务启动 + until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')" > /dev/null 2>&1; do + echo "Waiting for MongoDB to start..." + sleep 2 + done + + # 执行初始化副本集的脚本 + mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js + + # 等待docker-entrypoint.sh脚本执行的MongoDB服务进程 + wait $! fastgpt: container_name: fastgpt - image: ghcr.io/labring/fastgpt:latest # git - # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云 + image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.6.9 # git + # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.6.9 # 阿里云 ports: - 3000:3000 networks: @@ -53,20 +77,65 @@ services: - pg restart: always environment: - # root 密码,用户名为: root + # root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。 - DEFAULT_ROOT_PSW=1234 - # 中转地址,如果是用官方号,不需要管。务必加 /v1 - - OPENAI_BASE_URL=https://api.openai.com/v1 - - CHAT_API_KEY=sk-xxxx - - DB_MAX_LINK=5 # database max link + # AI模型的API地址哦。务必加 /v1。这里默认填写了OneApi的访问地址。 + - OPENAI_BASE_URL=http://oneapi:3000/v1 + # AI模型的API Key。(这里默认填写了OneAPI的快速默认key,测试通后,务必及时修改) + - CHAT_API_KEY=sk-fastgpt + # 数据库最大连接数 + - DB_MAX_LINK=30 + # 登录凭证密钥 - TOKEN_KEY=any + # root的密钥,常用于升级时候的初始化请求 - ROOT_KEY=root_key + # 文件阅读加密 - FILE_TOKEN_KEY=filetoken - # mongo 配置,不需要改. 用户名myusername,密码mypassword。 + # MongoDB 连接参数. 用户名myusername,密码mypassword。 - MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin - # pg配置. 不需要改 + # pg 连接参数 - PG_URL=postgresql://username:password@pg:5432/postgres volumes: - ./config.json:/app/data/config.json + mysql: + image: mysql:8.0.36 + container_name: mysql + restart: always + ports: + - 3306:3306 + networks: + - fastgpt + command: --default-authentication-plugin=mysql_native_password + environment: + # 默认root密码,仅首次运行有效 + MYSQL_ROOT_PASSWORD: oneapimmysql + MYSQL_DATABASE: oneapi + volumes: + - ./mysql:/var/lib/mysql + oneapi: + container_name: oneapi + image: ghcr.io/songquanpeng/one-api:latest + ports: + - 3001:3000 + depends_on: + - mysql + networks: + - fastgpt + restart: always + environment: + # mysql 连接参数 + - SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi + # 登录凭证加密密钥 + - SESSION_SECRET=oneapikey + # 内存缓存 + - MEMORY_CACHE_ENABLED=true + # 启动聚合更新,减少数据交互频率 + - BATCH_UPDATE_ENABLED=true + # 聚合更新时长 + - BATCH_UPDATE_INTERVAL=10 + # 初始化的 root 密钥(建议部署完后更改,否则容易泄露) + - INITIAL_ROOT_TOKEN=fastgpt + volumes: + - ./oneapi:/data networks: fastgpt: diff --git a/packages/global/common/file/tools.ts b/packages/global/common/file/tools.ts index 7944da451..8b8414bc5 100644 --- a/packages/global/common/file/tools.ts +++ b/packages/global/common/file/tools.ts @@ -1,3 +1,5 @@ +import { detect } from 'jschardet'; + export const formatFileSize = (bytes: number): string => { if (bytes === 0) return '0 B'; @@ -7,3 +9,7 @@ export const formatFileSize = (bytes: number): string => { return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; + +export const detectFileEncoding = (buffers: string | Buffer) => { + return detect(buffers)?.encoding || 'utf-8'; +}; diff --git a/packages/global/common/string/textSplitter.ts b/packages/global/common/string/textSplitter.ts index 5fb21ae57..1f8591b3a 100644 --- a/packages/global/common/string/textSplitter.ts +++ b/packages/global/common/string/textSplitter.ts @@ -30,7 +30,10 @@ export const splitText2Chunks = (props: { // The larger maxLen is, the next sentence is less likely to trigger splitting const stepReges: { reg: RegExp; maxLen: number }[] = [ - ...customReg.map((text) => ({ reg: new RegExp(`(${text})`, 'g'), maxLen: chunkLen * 1.4 })), + ...customReg.map((text) => ({ + reg: new RegExp(`(${text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'g'), + maxLen: chunkLen * 1.4 + })), { reg: /^(#\s[^\n]+)\n/gm, maxLen: chunkLen * 1.2 }, { reg: /^(##\s[^\n]+)\n/gm, maxLen: chunkLen * 1.2 }, { reg: /^(###\s[^\n]+)\n/gm, maxLen: chunkLen * 1.2 }, diff --git a/packages/global/common/system/config/constants.ts b/packages/global/common/system/config/constants.ts index 0ae8aaaf5..01296c22e 100644 --- a/packages/global/common/system/config/constants.ts +++ b/packages/global/common/system/config/constants.ts @@ -1,6 +1,7 @@ export enum SystemConfigsTypeEnum { fastgpt = 'fastgpt', - fastgptPro = 'fastgptPro' + fastgptPro = 'fastgptPro', + systemMsgModal = 'systemMsgModal' } export const SystemConfigsTypeMap = { @@ -9,5 +10,8 @@ export const SystemConfigsTypeMap = { }, [SystemConfigsTypeEnum.fastgptPro]: { label: 'fastgptPro' + }, + [SystemConfigsTypeEnum.systemMsgModal]: { + label: 'systemMsgModal' } }; diff --git a/packages/global/core/ai/api.d.ts b/packages/global/core/ai/api.d.ts deleted file mode 100644 index e010d0db0..000000000 --- a/packages/global/core/ai/api.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type PostReRankProps = { - query: string; - inputs: { id: string; text: string }[]; -}; -export type PostReRankResponse = { id: string; score?: number }[]; diff --git a/packages/global/core/ai/constants.ts b/packages/global/core/ai/constants.ts index a697841dc..f01859ab1 100644 --- a/packages/global/core/ai/constants.ts +++ b/packages/global/core/ai/constants.ts @@ -25,3 +25,8 @@ export const llmModelTypeFilterMap = { [LLMModelTypeEnum.toolCall]: 'usedInToolCall', [LLMModelTypeEnum.queryExtension]: 'usedInQueryExtension' }; + +export enum EmbeddingTypeEnm { + query = 'query', + db = 'db' +} diff --git a/packages/global/core/ai/model.d.ts b/packages/global/core/ai/model.d.ts index 16210cef4..a1f26bb9c 100644 --- a/packages/global/core/ai/model.d.ts +++ b/packages/global/core/ai/model.d.ts @@ -30,23 +30,25 @@ export type LLMModelItemType = { }; export type VectorModelItemType = { - model: string; - name: string; + model: string; // model name + name: string; // show name avatar?: string; - defaultToken: number; - charsPointsPrice: number; - maxToken: number; - weight: number; - hidden?: boolean; - defaultConfig?: Record; + defaultToken: number; // split text default token + charsPointsPrice: number; // 1k tokens=n points + maxToken: number; // model max token + weight: number; // training weight + hidden?: boolean; // Disallow creation + defaultConfig?: Record; // post request config + dbConfig?: Record; // Custom parameters for storage + queryConfig?: Record; // Custom parameters for query }; export type ReRankModelItemType = { model: string; name: string; charsPointsPrice: number; - requestUrl?: string; - requestAuth?: string; + requestUrl: string; + requestAuth: string; }; export type AudioSpeechModelType = { diff --git a/packages/global/core/module/node/constant.ts b/packages/global/core/module/node/constant.ts index e2e4507f0..7239e4bab 100644 --- a/packages/global/core/module/node/constant.ts +++ b/packages/global/core/module/node/constant.ts @@ -1,4 +1,5 @@ export enum FlowNodeInputTypeEnum { + triggerAndFinish = 'triggerAndFinish', systemInput = 'systemInput', // history, userChatInput, variableInput input = 'input', // one line input diff --git a/packages/global/core/module/template/input.ts b/packages/global/core/module/template/input.ts index 16e5bb8d3..dc698b277 100644 --- a/packages/global/core/module/template/input.ts +++ b/packages/global/core/module/template/input.ts @@ -6,8 +6,8 @@ import { chatNodeSystemPromptTip } from './tip'; export const Input_Template_Switch: FlowNodeInputItemType = { key: ModuleInputKeyEnum.switch, - type: FlowNodeInputTypeEnum.target, - label: 'core.module.input.label.switch', + type: FlowNodeInputTypeEnum.triggerAndFinish, + label: '', description: 'core.module.input.description.Trigger', valueType: ModuleIOValueTypeEnum.any, showTargetInApp: true, diff --git a/packages/global/core/module/template/output.ts b/packages/global/core/module/template/output.ts index 22ad1763a..d25923b7d 100644 --- a/packages/global/core/module/template/output.ts +++ b/packages/global/core/module/template/output.ts @@ -13,10 +13,10 @@ export const Output_Template_UserChatInput: FlowNodeOutputItemType = { export const Output_Template_Finish: FlowNodeOutputItemType = { key: ModuleOutputKeyEnum.finish, - label: 'core.module.output.label.running done', - description: 'core.module.output.description.running done', + label: '', + description: '', valueType: ModuleIOValueTypeEnum.boolean, - type: FlowNodeOutputTypeEnum.source, + type: FlowNodeOutputTypeEnum.hidden, targets: [] }; diff --git a/packages/global/core/module/template/system/datasetConcat.ts b/packages/global/core/module/template/system/datasetConcat.ts index ec929b522..63c84d9fe 100644 --- a/packages/global/core/module/template/system/datasetConcat.ts +++ b/packages/global/core/module/template/system/datasetConcat.ts @@ -48,7 +48,6 @@ export const DatasetConcatModule: FlowNodeTemplateType = { type: FlowNodeOutputTypeEnum.source, valueType: ModuleIOValueTypeEnum.datasetQuote, targets: [] - }, - Output_Template_Finish + } ] }; diff --git a/packages/global/core/module/template/system/datasetSearch.ts b/packages/global/core/module/template/system/datasetSearch.ts index b831de62d..b4eb4a1ea 100644 --- a/packages/global/core/module/template/system/datasetSearch.ts +++ b/packages/global/core/module/template/system/datasetSearch.ts @@ -14,13 +14,16 @@ import { Input_Template_Switch, Input_Template_UserChatInput } from '../input'; import { Output_Template_Finish, Output_Template_UserChatInput } from '../output'; import { DatasetSearchModeEnum } from '../../../dataset/constants'; +export const Dataset_SEARCH_DESC = + '调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容'; + export const DatasetSearchModule: FlowNodeTemplateType = { id: FlowNodeTypeEnum.datasetSearchNode, templateType: FlowNodeTemplateTypeEnum.functionCall, flowType: FlowNodeTypeEnum.datasetSearchNode, avatar: '/imgs/module/db.png', name: '知识库搜索', - intro: '调用知识库搜索能力,查找“有可能”与问题相关的内容', + intro: Dataset_SEARCH_DESC, showStatus: true, isTool: true, inputs: [ @@ -125,7 +128,6 @@ export const DatasetSearchModule: FlowNodeTemplateType = { type: FlowNodeOutputTypeEnum.source, valueType: ModuleIOValueTypeEnum.datasetQuote, targets: [] - }, - Output_Template_Finish + } ] }; diff --git a/packages/global/core/module/template/system/http468.ts b/packages/global/core/module/template/system/http468.ts index 3d556583f..a4357bc58 100644 --- a/packages/global/core/module/template/system/http468.ts +++ b/packages/global/core/module/template/system/http468.ts @@ -99,7 +99,6 @@ export const HttpModule468: FlowNodeTemplateType = { } ], outputs: [ - Output_Template_Finish, { key: ModuleOutputKeyEnum.httpRawResponse, label: '原始响应', diff --git a/packages/global/core/module/type.d.ts b/packages/global/core/module/type.d.ts index 3d8122aab..04107dcc0 100644 --- a/packages/global/core/module/type.d.ts +++ b/packages/global/core/module/type.d.ts @@ -114,6 +114,7 @@ export type ChatDispatchProps = { inputFiles?: UserChatItemValueItemType['file'][]; stream: boolean; detail: boolean; // response detail + maxRunTimes: number; }; export type ModuleDispatchProps = ChatDispatchProps & { diff --git a/packages/global/package.json b/packages/global/package.json index 79d286b0a..5b2257c7e 100644 --- a/packages/global/package.json +++ b/packages/global/package.json @@ -11,7 +11,8 @@ "nanoid": "^4.0.1", "js-yaml": "^4.1.0", "timezones-list": "^3.0.2", - "next": "13.5.2" + "next": "13.5.2", + "jschardet": "3.1.1" }, "devDependencies": { "@types/js-yaml": "^4.0.9", diff --git a/packages/global/support/user/inform/constants.ts b/packages/global/support/user/inform/constants.ts index 6b6a1d2db..073e8805e 100644 --- a/packages/global/support/user/inform/constants.ts +++ b/packages/global/support/user/inform/constants.ts @@ -1,13 +1,17 @@ -export enum InformTypeEnum { - system = 'system', - admin = 'admin' +export enum InformLevelEnum { + 'common' = 'common', + 'important' = 'important', + 'emergency' = 'emergency' } -export const InformTypeMap = { - [InformTypeEnum.system]: { - label: '系统通知' +export const InformLevelMap = { + [InformLevelEnum.common]: { + label: '普通' }, - [InformTypeEnum.admin]: { - label: '管理员' + [InformLevelEnum.important]: { + label: '重要' + }, + [InformLevelEnum.emergency]: { + label: '紧急' } }; diff --git a/packages/global/support/user/inform/type.d.ts b/packages/global/support/user/inform/type.d.ts index 7a258c0db..d05847ffa 100644 --- a/packages/global/support/user/inform/type.d.ts +++ b/packages/global/support/user/inform/type.d.ts @@ -1,17 +1,19 @@ -import { InformTypeEnum } from './constants'; +import { InformLevelEnum } from './constants'; export type SendInformProps = { - tmbId?: string; - type: `${InformTypeEnum}`; title: string; content: string; + level: `${InformLevelEnum}`; +}; +export type SendInform2UserProps = SendInformProps & { + tmbId: string; }; export type UserInformSchema = { _id: string; userId: string; time: Date; - type: `${InformTypeEnum}`; + level: `${InformLevelEnum}`; title: string; content: string; read: boolean; diff --git a/packages/global/support/user/type.d.ts b/packages/global/support/user/type.d.ts index 3a3d56ea0..d689f1a0b 100644 --- a/packages/global/support/user/type.d.ts +++ b/packages/global/support/user/type.d.ts @@ -1,12 +1,14 @@ -import { InformTypeEnum, UserStatusEnum } from './constant'; +import { UserStatusEnum } from './constant'; import { TeamItemType } from './team/type'; export type UserModelSchema = { _id: string; username: string; + email?: string; + phonePrefix?: number; + phone?: string; password: string; avatar: string; - balance: number; promotionRate: number; inviterId?: string; openaiKey: string; @@ -24,7 +26,6 @@ export type UserType = { _id: string; username: string; avatar: string; - balance: number; timezone: string; promotionRate: UserModelSchema['promotionRate']; openaiAccount: UserModelSchema['openaiAccount']; diff --git a/packages/service/common/api/serverRequest.ts b/packages/service/common/api/serverRequest.ts index fbe6338e1..51fc31480 100644 --- a/packages/service/common/api/serverRequest.ts +++ b/packages/service/common/api/serverRequest.ts @@ -35,7 +35,7 @@ function checkRes(data: ResponseDataType) { } else if (data?.code && (data.code < 200 || data.code >= 400)) { return Promise.reject(data); } - return data.data; + return data; } /** diff --git a/packages/service/common/file/utils.ts b/packages/service/common/file/utils.ts index d75da5958..380194747 100644 --- a/packages/service/common/file/utils.ts +++ b/packages/service/common/file/utils.ts @@ -10,15 +10,15 @@ export const removeFilesByPaths = (paths: string[]) => { }); }; -const imageTypeMap: Record = { - '/': 'image/jpeg', - i: 'image/png', - R: 'image/gif', - U: 'image/webp', - Q: 'image/bmp' -}; +export const guessBase64ImageType = (str: string) => { + const imageTypeMap: Record = { + '/': 'image/jpeg', + i: 'image/png', + R: 'image/gif', + U: 'image/webp', + Q: 'image/bmp' + }; -export const guessImageTypeFromBase64 = (str: string) => { const defaultType = 'image/jpeg'; if (typeof str !== 'string' || str.length === 0) { return defaultType; diff --git a/packages/service/common/vectorStore/controller.ts b/packages/service/common/vectorStore/controller.ts index e5d6a0e2a..6c682da0d 100644 --- a/packages/service/common/vectorStore/controller.ts +++ b/packages/service/common/vectorStore/controller.ts @@ -24,7 +24,8 @@ export const insertDatasetDataVector = async ({ }) => { const { vectors, tokens } = await getVectorsByText({ model, - input: query + input: query, + type: 'db' }); const { insertId } = await getVectorObj().insert({ ...props, diff --git a/packages/service/core/ai/embedding/index.ts b/packages/service/core/ai/embedding/index.ts index 50251dc91..40171873e 100644 --- a/packages/service/core/ai/embedding/index.ts +++ b/packages/service/core/ai/embedding/index.ts @@ -1,14 +1,16 @@ import { VectorModelItemType } from '@fastgpt/global/core/ai/model.d'; import { getAIApi } from '../config'; import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken'; +import { EmbeddingTypeEnm } from '@fastgpt/global/core/ai/constants'; type GetVectorProps = { model: VectorModelItemType; input: string; + type?: `${EmbeddingTypeEnm}`; }; // text to vector -export async function getVectorsByText({ model, input }: GetVectorProps) { +export async function getVectorsByText({ model, input, type }: GetVectorProps) { if (!input) { return Promise.reject({ code: 500, @@ -23,6 +25,8 @@ export async function getVectorsByText({ model, input }: GetVectorProps) { const result = await ai.embeddings .create({ ...model.defaultConfig, + ...(type === EmbeddingTypeEnm.db && model.dbConfig), + ...(type === EmbeddingTypeEnm.query && model.queryConfig), model: model.model, input: [input] }) diff --git a/packages/service/core/ai/functions/queryExtension.ts b/packages/service/core/ai/functions/queryExtension.ts index da36515c4..5850104ed 100644 --- a/packages/service/core/ai/functions/queryExtension.ts +++ b/packages/service/core/ai/functions/queryExtension.ts @@ -160,7 +160,7 @@ A: ${chatBg} return { rawQuery: query, - extensionQueries: queries, + extensionQueries: Array.isArray(queries) ? queries : [], model, tokens: countGptMessagesTokens(messages) }; diff --git a/packages/service/core/ai/rerank/index.ts b/packages/service/core/ai/rerank/index.ts index b7f56a6ae..b881a2c33 100644 --- a/packages/service/core/ai/rerank/index.ts +++ b/packages/service/core/ai/rerank/index.ts @@ -1,7 +1,21 @@ -import { PostReRankProps, PostReRankResponse } from '@fastgpt/global/core/ai/api.d'; import { POST } from '../../../common/api/serverRequest'; -export function reRankRecall({ query, inputs }: PostReRankProps) { +type PostReRankResponse = { + id: string; + results: { + index: number; + relevance_score: number; + }[]; +}; +type ReRankCallResult = { id: string; score?: number }[]; + +export function reRankRecall({ + query, + documents +}: { + query: string; + documents: { id: string; text: string }[]; +}): Promise { const model = global.reRankModels[0]; if (!model || !model?.requestUrl) { @@ -12,19 +26,24 @@ export function reRankRecall({ query, inputs }: PostReRankProps) { return POST( model.requestUrl, { + model: model.model, query, - inputs + documents: documents.map((doc) => doc.text) }, { headers: { Authorization: `Bearer ${model.requestAuth}` }, - timeout: 120000 + timeout: 30000 } ) .then((data) => { console.log('rerank time:', Date.now() - start); - return data; + + return data?.results?.map((item) => ({ + id: documents[item.index].id, + score: item.relevance_score + })); }) .catch((err) => { console.log('rerank error:', err); diff --git a/packages/service/core/dataset/search/controller.ts b/packages/service/core/dataset/search/controller.ts index 4d42bbe0c..643a124cb 100644 --- a/packages/service/core/dataset/search/controller.ts +++ b/packages/service/core/dataset/search/controller.ts @@ -77,7 +77,8 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { const embeddingRecall = async ({ query, limit }: { query: string; limit: number }) => { const { vectors, tokens } = await getVectorsByText({ model: getVectorModel(model), - input: query + input: query, + type: 'query' }); const { results } = await recallFromVectorStore({ @@ -225,7 +226,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { try { const results = await reRankRecall({ query, - inputs: data.map((item) => ({ + documents: data.map((item) => ({ id: item.id, text: `${item.q}\n${item.a}` })) diff --git a/packages/service/core/workflow/dispatch/agent/runTool/constants.ts b/packages/service/core/workflow/dispatch/agent/runTool/constants.ts index 1a91b5f84..eca3f8466 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/constants.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/constants.ts @@ -1,12 +1,9 @@ export const Prompt_Tool_Call = ` 你是一个智能机器人,除了可以回答用户问题外,你还掌握工具的使用能力。有时候,你可以依赖工具的运行结果,来更准确的回答用户。 -下面是你可以使用的工具,使用 JSON Schema 的格式声明,其中 toolId 是工具的 description 是工具的描述,parameters 是工具的参数,包括参数的类型和描述,required 是必填参数的列表。 -""" -{{toolsPrompt}} -""" +工具使用了 JSON Schema 的格式声明,其中 toolId 是工具的 description 是工具的描述,parameters 是工具的参数,包括参数的类型和描述,required 是必填参数的列表。 -接下来,请你根据工具描述,决定回答问题或是使用工具。在完成任务过程中,USER代表用户的输入,TOOL_RESPONSE代表工具运行结果。ASSISTANT 代表你的输出。 +请你根据工具描述,决定回答问题或是使用工具。在完成任务过程中,USER代表用户的输入,TOOL_RESPONSE代表工具运行结果。ASSISTANT 代表你的输出。 你的每次输出都必须以0,1开头,代表是否需要调用工具: 0: 不使用工具,直接回答内容。 1: 使用工具,返回工具调用的参数。 @@ -29,7 +26,13 @@ TOOL_RESPONSE: """ ANSWER: 0: 今天杭州是晴天,适合去西湖、灵隐寺、千岛湖等地玩。 -现在,我们开始吧! +现在,我们开始吧!下面是你本次可以使用的工具: + +""" +{{toolsPrompt}} +""" + +下面是正式的对话内容: USER: {{question}} ANSWER: diff --git a/packages/service/core/workflow/dispatch/index.ts b/packages/service/core/workflow/dispatch/index.ts index 11eecdc1d..6d119c1e9 100644 --- a/packages/service/core/workflow/dispatch/index.ts +++ b/packages/service/core/workflow/dispatch/index.ts @@ -125,6 +125,7 @@ export async function dispatchWorkFlow({ } if (nodeDispatchUsages) { chatNodeUsages = chatNodeUsages.concat(nodeDispatchUsages); + props.maxRunTimes -= nodeDispatchUsages.length; } if (toolResponses !== undefined) { if (Array.isArray(toolResponses) && toolResponses.length === 0) return; @@ -217,7 +218,7 @@ export async function dispatchWorkFlow({ ); } async function moduleRun(module: RunningModuleItemType): Promise { - if (res.closed) return Promise.resolve(); + if (res.closed || props.maxRunTimes <= 0) return Promise.resolve(); if (stream && detail && module.showStatus) { responseStatus({ diff --git a/packages/service/core/workflow/dispatch/plugin/run.ts b/packages/service/core/workflow/dispatch/plugin/run.ts index c4cd78bbe..af974e451 100644 --- a/packages/service/core/workflow/dispatch/plugin/run.ts +++ b/packages/service/core/workflow/dispatch/plugin/run.ts @@ -19,7 +19,6 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise; params: Record; }): Promise> { - const { data: response } = await axios>({ + const { data: response } = await axios({ method, baseURL: `http://${SERVICE_LOCAL_HOST}`, url, @@ -241,7 +241,8 @@ async function fetchData({ }; return { - formatResponse: parseJson(response), + formatResponse: + typeof response === 'object' && !Array.isArray(response) ? parseJson(response) : {}, rawResponse: response }; } diff --git a/packages/service/support/user/controller.ts b/packages/service/support/user/controller.ts index 5765df3a7..a76092770 100644 --- a/packages/service/support/user/controller.ts +++ b/packages/service/support/user/controller.ts @@ -39,7 +39,6 @@ export async function getUserDetail({ _id: user._id, username: user.username, avatar: user.avatar, - balance: user.balance, timezone: user.timezone, promotionRate: user.promotionRate, openaiAccount: user.openaiAccount, diff --git a/packages/service/support/user/inform/type.d.ts b/packages/service/support/user/inform/type.d.ts new file mode 100644 index 000000000..a03318316 --- /dev/null +++ b/packages/service/support/user/inform/type.d.ts @@ -0,0 +1,4 @@ +export type SystemMsgModalValueType = { + id: string; + content: string; +}; diff --git a/packages/service/support/user/schema.ts b/packages/service/support/user/schema.ts index 496726860..39935971a 100644 --- a/packages/service/support/user/schema.ts +++ b/packages/service/support/user/schema.ts @@ -1,7 +1,6 @@ import { connectionMongo, type Model } from '../../common/mongo'; const { Schema, model, models } = connectionMongo; import { hashStr } from '@fastgpt/global/common/string/tools'; -import { PRICE_SCALE } from '@fastgpt/global/support/wallet/constants'; import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant'; @@ -19,6 +18,15 @@ const UserSchema = new Schema({ required: true, unique: true // 唯一 }, + email: { + type: String + }, + phonePrefix: { + type: Number + }, + phone: { + type: String + }, password: { type: String, required: true, @@ -34,10 +42,6 @@ const UserSchema = new Schema({ type: String, default: '/icon/human.svg' }, - balance: { - type: Number, - default: 2 * PRICE_SCALE - }, inviterId: { // 谁邀请注册的 type: Schema.Types.ObjectId, diff --git a/packages/service/support/user/team/controller.ts b/packages/service/support/user/team/controller.ts index 772afc2f0..eee28d3fa 100644 --- a/packages/service/support/user/team/controller.ts +++ b/packages/service/support/user/team/controller.ts @@ -70,8 +70,6 @@ export async function createDefaultTeam({ }); if (!tmb) { - console.log('create default team', userId); - // create const [{ _id: insertedId }] = await MongoTeam.create( [ @@ -99,6 +97,7 @@ export async function createDefaultTeam({ ], { session } ); + console.log('create default team', userId); } else { console.log('default team exist', userId); await MongoTeam.findByIdAndUpdate(tmb.teamId, { diff --git a/packages/web/common/file/read/rawText.ts b/packages/web/common/file/read/rawText.ts index 6a9e4faeb..ab9f7ce5b 100644 --- a/packages/web/common/file/read/rawText.ts +++ b/packages/web/common/file/read/rawText.ts @@ -1,3 +1,5 @@ +import { detectFileEncoding } from '@fastgpt/global/common/file/tools'; + /** * read file raw text */ @@ -6,15 +8,27 @@ export const readFileRawText = (file: File) => { try { const reader = new FileReader(); reader.onload = () => { - resolve({ - rawText: reader.result as string - }); + //@ts-ignore + const encode = detectFileEncoding(reader.result); + + // 再次读取文件,这次使用检测到的编码 + const reader2 = new FileReader(); + reader2.onload = () => { + resolve({ + rawText: reader2.result as string + }); + }; + reader2.onerror = (err) => { + console.log('Error reading file with detected encoding:', err); + reject('Read file error with detected encoding'); + }; + reader2.readAsText(file, encode); }; reader.onerror = (err) => { console.log('error txt read:', err); reject('Read file error'); }; - reader.readAsText(file); + reader.readAsBinaryString(file); } catch (error) { reject(error); } diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 87732f1b3..5d50fee76 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -16,6 +16,7 @@ export const iconPaths = { 'common/confirm/rightTip': () => import('./icons/common/confirm/rightTip.svg'), 'common/courseLight': () => import('./icons/common/courseLight.svg'), 'common/customTitleLight': () => import('./icons/common/customTitleLight.svg'), + 'common/data': () => import('./icons/common/data.svg'), 'common/editor/resizer': () => import('./icons/common/editor/resizer.svg'), 'common/errorFill': () => import('./icons/common/errorFill.svg'), 'common/file/move': () => import('./icons/common/file/move.svg'), diff --git a/packages/web/components/common/Icon/icons/common/data.svg b/packages/web/components/common/Icon/icons/common/data.svg new file mode 100644 index 000000000..a39dc9462 --- /dev/null +++ b/packages/web/components/common/Icon/icons/common/data.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/core/chat/recordFill.svg b/packages/web/components/common/Icon/icons/core/chat/recordFill.svg index 23921cbc8..171dc5539 100644 --- a/packages/web/components/common/Icon/icons/core/chat/recordFill.svg +++ b/packages/web/components/common/Icon/icons/core/chat/recordFill.svg @@ -1,3 +1,4 @@ - - + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/core/chat/stopSpeechFill.svg b/packages/web/components/common/Icon/icons/core/chat/stopSpeechFill.svg index 64b726127..b7c022844 100644 --- a/packages/web/components/common/Icon/icons/core/chat/stopSpeechFill.svg +++ b/packages/web/components/common/Icon/icons/core/chat/stopSpeechFill.svg @@ -1,10 +1,12 @@ - + - + - + \ No newline at end of file diff --git a/packages/web/components/common/Input/HttpInput/Editor.tsx b/packages/web/components/common/Input/HttpInput/Editor.tsx index dfa28e8cd..74f2cc3c8 100644 --- a/packages/web/components/common/Input/HttpInput/Editor.tsx +++ b/packages/web/components/common/Input/HttpInput/Editor.tsx @@ -5,7 +5,7 @@ import { ContentEditable } from '@lexical/react/LexicalContentEditable'; import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'; import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'; import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary'; -import { Box } from '@chakra-ui/react'; +import { Box, Flex } from '@chakra-ui/react'; import styles from './index.module.scss'; import { EditorState, LexicalEditor } from 'lexical'; import { getNanoid } from '@fastgpt/global/common/string/tools'; @@ -78,7 +78,15 @@ export default function Editor({ ); return ( - + } @@ -125,6 +133,6 @@ export default function Editor({ {focus && hasDropDownPlugin && ( )} - + ); } diff --git a/packages/web/components/common/Input/HttpInput/index.module.scss b/packages/web/components/common/Input/HttpInput/index.module.scss index 36103efa4..ea71ee3d6 100644 --- a/packages/web/components/common/Input/HttpInput/index.module.scss +++ b/packages/web/components/common/Input/HttpInput/index.module.scss @@ -3,7 +3,9 @@ display: flex; flex-direction: column; justify-content: center; + flex-grow: 1; overflow: auto; padding-left: 8px; + padding-right: 8px; position: relative; } diff --git a/packages/web/components/common/Textarea/PromptEditor/modules/DropDownMenu/index.tsx b/packages/web/components/common/Textarea/PromptEditor/modules/DropDownMenu/index.tsx index 79d736727..c6c9f273c 100644 --- a/packages/web/components/common/Textarea/PromptEditor/modules/DropDownMenu/index.tsx +++ b/packages/web/components/common/Textarea/PromptEditor/modules/DropDownMenu/index.tsx @@ -42,6 +42,7 @@ export default function DropDownMenu({ p={2} borderRadius={'md'} position={'absolute'} + top={'100%'} w={'auto'} zIndex={99999} maxH={'300px'} diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePickerPlugin/index.tsx b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePickerPlugin/index.tsx index 4147a5019..03158b4fd 100644 --- a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePickerPlugin/index.tsx +++ b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePickerPlugin/index.tsx @@ -99,7 +99,7 @@ export default function VariablePickerPlugin({ }} > - + {item.key} {item.key !== item.label && `(${item.label})`} diff --git a/projects/app/src/web/common/hooks/useConfirm.tsx b/packages/web/hooks/useConfirm.tsx similarity index 98% rename from projects/app/src/web/common/hooks/useConfirm.tsx rename to packages/web/hooks/useConfirm.tsx index 4c82bbfe2..a64c6b79b 100644 --- a/projects/app/src/web/common/hooks/useConfirm.tsx +++ b/packages/web/hooks/useConfirm.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDisclosure, Button, ModalBody, ModalFooter } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; -import MyModal from '@fastgpt/web/components/common/MyModal'; +import MyModal from '../components/common/MyModal'; export const useConfirm = (props?: { title?: string; diff --git a/packages/web/hooks/useRequest.tsx b/packages/web/hooks/useRequest.tsx new file mode 100644 index 000000000..15795d8f4 --- /dev/null +++ b/packages/web/hooks/useRequest.tsx @@ -0,0 +1,41 @@ +import { useToast } from './useToast'; +import { useMutation } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getErrText } from '@fastgpt/global/common/error/utils'; +import { useTranslation } from 'next-i18next'; + +interface Props extends UseMutationOptions { + successToast?: string | null; + errorToast?: string | null; +} + +export const useRequest = ({ successToast, errorToast, onSuccess, onError, ...props }: Props) => { + const { toast } = useToast(); + const { t } = useTranslation(); + const mutation = useMutation({ + ...props, + onSuccess(res, variables: void, context: unknown) { + onSuccess?.(res, variables, context); + successToast && + toast({ + title: successToast, + status: 'success' + }); + }, + onError(err: any, variables: void, context: unknown) { + onError?.(err, variables, context); + + if (errorToast !== undefined) { + const errText = t(getErrText(err, errorToast || '')); + if (errText) { + toast({ + title: errText, + status: 'error' + }); + } + } + } + }); + + return mutation; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7cf6541b0..ac6149b3e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -50,6 +50,9 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 + jschardet: + specifier: 3.1.1 + version: 3.1.1 nanoid: specifier: ^4.0.1 version: 4.0.2 @@ -272,6 +275,9 @@ importers: '@apidevtools/swagger-parser': specifier: ^10.1.0 version: 10.1.0(openapi-types@12.1.3) + '@bany/curl-to-json': + specifier: ^1.2.8 + version: 1.2.8 '@chakra-ui/anatomy': specifier: 2.2.1 version: 2.2.1 @@ -353,9 +359,6 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 - jschardet: - specifier: ^3.0.0 - version: 3.1.0 jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 @@ -387,8 +390,8 @@ importers: specifier: 18.2.0 version: 18.2.0(react@18.2.0) react-hook-form: - specifier: ^7.43.1 - version: 7.51.1(react@18.2.0) + specifier: 7.43.1 + version: 7.43.1(react@18.2.0) react-i18next: specifier: 13.5.0 version: 13.5.0(i18next@23.10.0)(react-dom@18.2.0)(react@18.2.0) @@ -1778,6 +1781,12 @@ packages: '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 + /@bany/curl-to-json@1.2.8: + resolution: {integrity: sha512-hPt9KUM2sGZ5Ojx3O9utjzUgjRZI3CZPAlLf+cRY9EUzVs7tWt1OpA0bhEUTX2PEEkOeyZ6sC0tAQMOHh9ld+Q==} + dependencies: + minimist: 1.2.8 + dev: false + /@braintree/sanitize-url@6.0.4: resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==} dev: false @@ -8347,8 +8356,8 @@ packages: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} dev: false - /jschardet@3.1.0: - resolution: {integrity: sha512-MND0yjRsoQ/3iFXce7lqV/iBmqH9oWGUTlty36obRBZjhFDWCLKjXgfxY75wYfwlW7EFqw52tyziy/q4WsQmrA==} + /jschardet@3.1.1: + resolution: {integrity: sha512-Jbygqaa20I+0ImPjmMbrsY3QrMkfwfI5G/VNlb6c9nDIyyOw8msfWHzTy04/sawa4rjn0t9WYy3nahWlSjB5zw==} engines: {node: '>=0.1.90'} dev: false @@ -10215,8 +10224,8 @@ packages: use-sidecar: 1.1.2(@types/react@18.2.0)(react@18.2.0) dev: false - /react-hook-form@7.51.1(react@18.2.0): - resolution: {integrity: sha512-ifnBjl+kW0ksINHd+8C/Gp6a4eZOdWyvRv0UBaByShwU8JbVx5hTcTWEcd5VdybvmPTATkVVXk9npXArHmo56w==} + /react-hook-form@7.43.1(react@18.2.0): + resolution: {integrity: sha512-+s3+s8LLytRMriwwuSqeLStVjRXFGxgjjx2jED7Z+wz1J/88vpxieRQGvJVvzrzVxshZ0BRuocFERb779m2kNg==} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 diff --git a/projects/app/.env.template b/projects/app/.env.template index de2a5dbb4..4baf9a491 100644 --- a/projects/app/.env.template +++ b/projects/app/.env.template @@ -16,7 +16,7 @@ OPENAI_BASE_URL=https://api.openai.com/v1 # 通用key。可以是 openai 的也可以是 oneapi 的。 # 此处逻辑:优先走 ONEAPI_URL,如果填写了 ONEAPI_URL,key 也需要是 ONEAPI 的 key CHAT_API_KEY=sk-xxxx -# mongo 数据库连接参数 +# mongo 数据库连接参数,本地开发时,mongo可能需要增加 directConnection=true 参数,才能连接上。 MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin # PG 数据库连接参数 PG_URL=postgresql://username:password@host:port/postgres diff --git a/projects/app/data/pluginTemplates/customFeedback.json b/projects/app/data/pluginTemplates/customFeedback.json index cac505685..9ef9e35f2 100644 --- a/projects/app/data/pluginTemplates/customFeedback.json +++ b/projects/app/data/pluginTemplates/customFeedback.json @@ -6,6 +6,7 @@ "intro": "该模块被触发时,会给当前的对话记录增加一条反馈。可用于自动记录对话效果等。", "showStatus": false, "isTool": false, + "weight": 0, "modules": [ { "moduleId": "w90mfp", @@ -250,7 +251,7 @@ "label": "core.module.output.label.running done", "description": "core.module.output.description.running done", "valueType": "boolean", - "type": "source", + "type": "hidden", "targets": [] }, { @@ -332,7 +333,7 @@ "label": "core.module.output.label.running done", "description": "core.module.output.description.running done", "valueType": "boolean", - "type": "source", + "type": "hidden", "targets": [] } ] diff --git a/projects/app/data/pluginTemplates/getCurrentTime.json b/projects/app/data/pluginTemplates/getCurrentTime.json new file mode 100644 index 000000000..235b1f9c6 --- /dev/null +++ b/projects/app/data/pluginTemplates/getCurrentTime.json @@ -0,0 +1,195 @@ +{ + "author": "FastGPT Team", + "templateType": "tools", + "name": "获取当前时间", + "avatar": "/imgs/module/getCurrentTime.svg", + "intro": "获取用户当前时区的时间。", + "showStatus": false, + "isTool": true, + "weight": 10, + "modules": [ + { + "moduleId": "m8dupj", + "name": "定义插件输入", + "intro": "自定义配置外部输入,使用插件时,仅暴露自定义配置的输入", + "avatar": "/imgs/module/input.png", + "flowType": "pluginInput", + "showStatus": false, + "position": { + "x": 187.94161749205568, + "y": 179.78772129776746 + }, + "inputs": [ + { + "key": "pluginStart", + "type": "hidden", + "valueType": "boolean", + "label": "插件开始运行", + "description": "插件开始运行时,会输出一个 True 的标识。有时候,插件不会有额外的的输入,为了顺利的进入下一个阶段,你可以将该值连接到下一个节点的触发器中。", + "showTargetInApp": true, + "showTargetInPlugin": true, + "connected": true + } + ], + "outputs": [ + { + "key": "pluginStart", + "label": "插件开始运行", + "type": "source", + "valueType": "boolean", + "targets": [ + { + "moduleId": "cv13yt", + "key": "switch" + } + ] + } + ] + }, + { + "moduleId": "bjsa7r", + "name": "定义插件输出", + "intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出", + "avatar": "/imgs/module/output.png", + "flowType": "pluginOutput", + "showStatus": false, + "position": { + "x": 1176.9471084832217, + "y": 138.94098316727695 + }, + "inputs": [ + { + "key": "time", + "valueType": "string", + "label": "time", + "type": "target", + "required": true, + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": false, + "dataType": true, + "inputType": false + }, + "connected": true + } + ], + "outputs": [ + { + "key": "time", + "valueType": "string", + "label": "time", + "type": "source", + "edit": true, + "targets": [] + } + ] + }, + { + "moduleId": "cv13yt", + "name": "文本加工", + "intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。", + "avatar": "/imgs/module/textEditor.svg", + "flowType": "pluginModule", + "showStatus": false, + "position": { + "x": 600.7190079155914, + "y": 1.4754510232677944 + }, + "inputs": [ + { + "key": "pluginId", + "type": "hidden", + "label": "", + "value": "community-textEditor", + "valueType": "string", + "connected": false, + "showTargetInApp": false, + "showTargetInPlugin": false + }, + { + "key": "switch", + "type": "triggerAndFinish", + "label": "", + "description": "core.module.input.description.Trigger", + "valueType": "any", + "showTargetInApp": true, + "showTargetInPlugin": true, + "connected": true + }, + { + "key": "textarea", + "valueType": "string", + "label": "文本内容", + "type": "textarea", + "required": true, + "description": "可以通过 {{key}} 的方式引用传入的变量。变量仅支持字符串或数字。", + "edit": false, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true, + "inputType": true + }, + "connected": false, + "placeholder": "可以通过 {{key}} 的方式引用传入的变量。变量仅支持字符串或数字。", + "value": "{{cTime}}" + }, + { + "key": "DYNAMIC_INPUT_KEY", + "valueType": "any", + "label": "需要加工的输入", + "type": "addInputParam", + "required": false, + "description": "可动态的添加字符串类型变量,在文本编辑中通过 {{key}} 使用变量。非字符串类型,会自动转成字符串类型。", + "edit": false, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true, + "inputType": false + }, + "defaultEditField": { + "label": "", + "key": "", + "description": "", + "inputType": "target", + "valueType": "string", + "required": true + }, + "connected": false + } + ], + "outputs": [ + { + "key": "text", + "valueType": "string", + "label": "core.module.output.label.text", + "type": "source", + "edit": false, + "targets": [ + { + "moduleId": "bjsa7r", + "key": "time" + } + ] + }, + { + "key": "finish", + "label": "", + "description": "", + "valueType": "boolean", + "type": "hidden", + "targets": [] + } + ] + } + ] +} diff --git a/projects/app/data/pluginTemplates/textEditor.json b/projects/app/data/pluginTemplates/textEditor.json index 3a8f0e2a8..f562f3ec2 100644 --- a/projects/app/data/pluginTemplates/textEditor.json +++ b/projects/app/data/pluginTemplates/textEditor.json @@ -6,6 +6,7 @@ "intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。", "showStatus": false, "isTool": false, + "weight": 100, "modules": [ { "moduleId": "w90mfp", @@ -281,7 +282,7 @@ "label": "core.module.output.label.running done", "description": "core.module.output.description.running done", "valueType": "boolean", - "type": "source", + "type": "hidden", "targets": [] }, { diff --git a/projects/app/data/pluginTemplates/tfSwitch.json b/projects/app/data/pluginTemplates/tfSwitch.json index 7f805f282..797cb67cc 100644 --- a/projects/app/data/pluginTemplates/tfSwitch.json +++ b/projects/app/data/pluginTemplates/tfSwitch.json @@ -6,6 +6,7 @@ "intro": "根据传入的内容进行 True False 输出。默认情况下,当传入的内容为 false, undefined, null, 0, none 时,会输出 false。你也可以增加一些自定义的字符串来补充输出 false 的内容。非字符、非数字、非布尔类型,直接输出 True。", "showStatus": false, "isTool": false, + "weight": 10, "modules": [ { "moduleId": "w90mfp", @@ -298,7 +299,7 @@ "label": "core.module.output.label.running done", "description": "core.module.output.description.running done", "valueType": "boolean", - "type": "source", + "type": "hidden", "targets": [] }, { diff --git a/projects/app/package.json b/projects/app/package.json index 1be8f9f5c..8bae72268 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@apidevtools/swagger-parser": "^10.1.0", + "@bany/curl-to-json": "^1.2.8", "@chakra-ui/anatomy": "2.2.1", "@chakra-ui/icons": "2.1.1", "@chakra-ui/next-js": "2.1.5", @@ -36,7 +37,6 @@ "i18next": "23.10.0", "immer": "^9.0.19", "js-yaml": "^4.1.0", - "jschardet": "^3.0.0", "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "mermaid": "^10.2.3", @@ -48,7 +48,7 @@ "react": "18.2.0", "react-day-picker": "^8.7.1", "react-dom": "18.2.0", - "react-hook-form": "^7.43.1", + "react-hook-form": "7.43.1", "react-i18next": "13.5.0", "react-markdown": "^8.0.7", "react-syntax-highlighter": "^15.5.0", diff --git a/projects/app/public/imgs/module/getCurrentTime.svg b/projects/app/public/imgs/module/getCurrentTime.svg new file mode 100644 index 000000000..bce53c3fe --- /dev/null +++ b/projects/app/public/imgs/module/getCurrentTime.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index c66a70b50..c01c85d55 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -124,6 +124,7 @@ "Price used": "Usage", "Read document": "Read document", "Read intro": "Read intro", + "Readed": "", "Remove": "Remove", "Rename": "Rename", "Rename Failed": "Rename Failed", @@ -145,6 +146,7 @@ "Status": "Status", "Submit failed": "Submit failed", "Submit success": "Update Success", + "System version": "System version", "Team": "Team", "Team Tags Set": "Team Tags", "Test": "Test", @@ -226,6 +228,7 @@ "error tip": "Speech Failed" }, "system": { + "Commercial version function": "Commercial version special function", "Help Chatbot": "Chatbot Helper", "Use Helper": "UsingHelp" }, @@ -252,6 +255,7 @@ "Ai point price": "AI points", "Max context": "Max context", "Model": "Model", + "Not deploy rerank model": "Not deploy rerank model", "Prompt": "Prompt", "Support tool": "Function call", "model": { @@ -271,8 +275,7 @@ "Max histories": "Dialog round", "Max tokens": "Max tokens", "Name and avatar": "Avatar & Name", - "Next Step Guide": "Next step guide", - "Question Guide": "", + "Question Guide": "Question Guide", "Question Guide Tip": "At the end of the conversation, three leading questions will be asked.", "Quote prompt": "Quote prompt", "Quote templates": "Quote templates", @@ -414,7 +417,7 @@ "Mark Description Title": "Mark Description", "New Chat": "New Chat", "Pin": "Pin", - "Question Guide Tips": "I guess what you're asking is", + "Question Guide": "I guess what you're asking is", "Quote": "Quote", "Quote Amount": "Dataset Quote:{{amount}}", "Read Mark Description": "Read mark description", @@ -696,6 +699,7 @@ "Max Tokens Tips": "The maximum number of Tokens in a single search, about 1 word in Chinese =1.7Tokens, about 1 word in English =1 tokens", "Min Similarity": "Min Similarity", "Min Similarity Tips": "The similarity of different index models is different, please use the search test to select the appropriate value", + "No support similarity": "Relevance filtering is supported when using only rearrangement or semantic search", "Nonsupport": "Nonsupport", "Not similarity": "", "Params Setting": "Params Setting", @@ -814,6 +818,7 @@ }, "Quote prompt setting": "Quote prompt setting", "Qupte prompt setting": "", + "Select app": "Select app", "Setting quote prompt": "Setting quote prompt", "Unlink tip": "[{{name}}] An unfilled or unconnected parameter exists", "Variable": "Variables", @@ -841,14 +846,14 @@ "Histories": "histories", "Key already exists": "Key already exists", "Key cannot be empty": "Name cannot be empty", - "OpenAPI import": "OpenAPI import", - "OpenAPI import placeholder": "Please enter the OpenAPI format content, and the request information for the first interface will be extracted.", "Props name": "Name", "Props tip": "You can set parameters related to Http requests\nGlobal changes or external parameter inputs can be invoked by {{key}}", "Props value": "Value", "ResponseChatItemId": "Ai response id", "Url and params have been split": "Path parameters are automatically added to Params", "Variables": "Global variables", + "curl import": "curl Import", + "curl import placeholder": "Please enter the curl format content, and the request information for the first interface will be extracted.", "params": "" }, "input": { @@ -1005,6 +1010,7 @@ "Custom headers": "Custom Headers", "Delete http plugin": "Are you sure to delete this group of HTTP plug-ins? All plug-ins in the directory are deleted.", "Get Plugin Module Detail Failed": "Load plugin failed", + "Http plugin intro placeholder": "Only for demonstration, no practical effect", "Intro placeholder": "If the plug-in is called as a tool, the introduction is used as the prompt word." }, "shareChat": { @@ -1223,7 +1229,7 @@ "Get Plugin Module Detail Failed": "Get plugin detail failed", "HTTP Plugin": "HTTP plugin", "Import Plugin": "Import HTTP plugin", - "Import from URL": "Import from URL", + "Import from URL": "Import from URL. https://xxxx", "Intro": "Plugin Intro", "Invalid Schema": "Invalid Schema", "Invalid URL": "Invalid URL", @@ -1249,6 +1255,9 @@ "account": { "Individuation": "Individuation" }, + "inform": { + "Read": "Read" + }, "openapi": { "Api baseurl": "Baseurl", "Api manager": "API key management", @@ -1259,6 +1268,7 @@ "Usage": "Usage" }, "outlink": { + "Delete link tip": "Are you sure to delete this no-sign link? After deletion, the link will be invalid immediately and the session log will still be retained. Please confirm!", "Max usage points": "Max usage", "Max usage points tip": "The maximum number of credits allowed for this link will not be used. -1 indicates no limit.", "Usage points": "Usage points", @@ -1291,6 +1301,9 @@ "auth": { "Sending Code": "Sending" }, + "inform": { + "System message": "System message" + }, "login": { "And": "&", "Email": "Email", diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index aafe31860..c448c482a 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -124,6 +124,7 @@ "Price used": "金额消耗", "Read document": "查看文档", "Read intro": "查看说明", + "Readed": "已读", "Remove": "移除", "Rename": "重命名", "Rename Failed": "重命名失败", @@ -145,6 +146,7 @@ "Status": "状态", "Submit failed": "提交失败", "Submit success": "提交成功", + "System version": "系统版本", "Team": "团队", "Team Tags Set": "标签", "Test": "测试", @@ -226,6 +228,7 @@ "error tip": "语音转文字失败" }, "system": { + "Commercial version function": "商业版特有功能", "Help Chatbot": "机器人助手", "Use Helper": "使用帮助" }, @@ -252,6 +255,7 @@ "Ai point price": "AI积分消耗", "Max context": "最大上下文", "Model": "AI 模型", + "Not deploy rerank model": "未部署重排模型", "Prompt": "提示词", "Support tool": "函数调用", "model": { @@ -271,8 +275,7 @@ "Max histories": "聊天记录数量", "Max tokens": "回复上限", "Name and avatar": "头像 & 名称", - "Next Step Guide": "下一步指引", - "Question Guide": "问题引导", + "Question Guide": "猜你想问", "Question Guide Tip": "对话结束后,会为生成 3 个引导性问题。", "Quote prompt": "引用模板提示词", "Quote templates": "引用内容模板", @@ -414,7 +417,7 @@ "Mark Description Title": "标注功能介绍", "New Chat": "新对话", "Pin": "置顶", - "Question Guide Tips": "猜你想问", + "Question Guide": "猜你想问", "Quote": "引用", "Quote Amount": "知识库引用({{amount}}条)", "Read Mark Description": "查看标注功能介绍", @@ -698,6 +701,7 @@ "Max Tokens Tips": "单次搜索最大的 Tokens 数量,中文约1字=1.7Tokens,英文约1字=1Tokens", "Min Similarity": "最低相关度", "Min Similarity Tips": "不同索引模型的相关度有区别,请通过搜索测试来选择合适的数值,使用 ReRank 时,相关度可能会很低。", + "No support similarity": "仅使用结果重排或语义检索时,支持相关度过滤", "Nonsupport": "不支持", "Not similarity": "", "Params Setting": "搜索参数设置", @@ -816,6 +820,7 @@ }, "Quote prompt setting": "引用提示词配置", "Qupte prompt setting": "", + "Select app": "选择应用", "Setting quote prompt": "配置引用提示词", "Unlink tip": "【{{name}}】存在未填或未连接参数", "Variable": "全局变量", @@ -843,14 +848,14 @@ "Histories": "历史纪录,最多取10条", "Key already exists": "Key 已经存在", "Key cannot be empty": "参数名不能为空", - "OpenAPI import": "OpenAPI 导入", - "OpenAPI import placeholder": "请输入 OpenAPI 格式内容,将会提取第一个接口的请求信息。", "Props name": "参数名", "Props tip": "可以设置 Http 请求的相关参数\n可通过 {{key}} 来调用全局变了或外部参数输入,当前可使用变量:\n{{variable}}", "Props value": "参数值", "ResponseChatItemId": "AI回复的ID", "Url and params have been split": "路径参数已被自动加入 Params 中", "Variables": "全局变量", + "curl import": "curl 导入", + "curl import placeholder": "请输入 curl 格式内容,将会提取第一个接口的请求信息。", "params": "Params" }, "input": { @@ -1007,6 +1012,7 @@ "Custom headers": "自定义请求头", "Delete http plugin": "确认删除该组HTTP插件?会删除该目录下所有插件。", "Get Plugin Module Detail Failed": "加载插件异常", + "Http plugin intro placeholder": "仅做展示,无实际效果", "Intro placeholder": "如果该插件作为工具被调用,则会使用该介绍作为提示词。" }, "shareChat": { @@ -1225,7 +1231,7 @@ "Get Plugin Module Detail Failed": "获取插件信息异常", "HTTP Plugin": "HTTP 插件", "Import Plugin": "导入 HTTP 插件", - "Import from URL": "URL 导入", + "Import from URL": "从URL导入。https://xxxx", "Intro": "插件介绍", "Invalid Schema": "Schema 无效", "Invalid URL": "URL 无效", @@ -1251,6 +1257,9 @@ "account": { "Individuation": "个性化" }, + "inform": { + "Read": "已读" + }, "openapi": { "Api baseurl": "API根地址", "Api manager": "API 秘钥管理", @@ -1261,6 +1270,7 @@ "Usage": "已用额度(¥)" }, "outlink": { + "Delete link tip": "确认删除该免登录链接?删除后,该链接将会立即失效,对话日志仍会保留,请确认!", "Max usage points": "积分上限", "Max usage points tip": "该链接最多允许使用多少积分,超出后将无法使用。-1 代表无限制。", "Usage points": "积分消耗", @@ -1293,6 +1303,9 @@ "auth": { "Sending Code": "正在发送" }, + "inform": { + "System message": "系统消息" + }, "login": { "And": "和", "Email": "邮箱", @@ -1419,7 +1432,7 @@ "experience": "体验版", "experience desc": "", "free": "免费版", - "free desc": "每月均可免费使用基础功能,15天无使用记录时,将会清除知识库", + "free desc": "每月均可免费使用基础功能,30天无使用记录时,将会清除知识库", "team": "团队版" }, "type": { diff --git a/projects/app/src/components/ChatBox/FeedbackModal.tsx b/projects/app/src/components/ChatBox/FeedbackModal.tsx index 3915e14bf..1dfdcf56a 100644 --- a/projects/app/src/components/ChatBox/FeedbackModal.tsx +++ b/projects/app/src/components/ChatBox/FeedbackModal.tsx @@ -1,7 +1,7 @@ import React, { useRef } from 'react'; import { ModalBody, Textarea, ModalFooter, Button } from '@chakra-ui/react'; import MyModal from '@fastgpt/web/components/common/MyModal'; -import { useRequest } from '@/web/common/hooks/useRequest'; +import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; import { updateChatUserFeedback } from '@/web/core/chat/api'; diff --git a/projects/app/src/components/ChatBox/MessageInput.tsx b/projects/app/src/components/ChatBox/MessageInput.tsx index 3b637fc2c..fc1be41df 100644 --- a/projects/app/src/components/ChatBox/MessageInput.tsx +++ b/projects/app/src/components/ChatBox/MessageInput.tsx @@ -10,7 +10,7 @@ import { compressImgFileAndUpload } from '@/web/common/file/controller'; import { customAlphabet } from 'nanoid'; import { ChatFileTypeEnum } from '@fastgpt/global/core/chat/constants'; import { addDays } from 'date-fns'; -import { useRequest } from '@/web/common/hooks/useRequest'; +import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat'; import { ChatBoxInputFormType, ChatBoxInputType, UserInputFileItemType } from './type'; @@ -61,7 +61,7 @@ const MessageInput = ({ renderAudioGraph, stream } = useSpeech({ shareId, outLinkUid, teamId, teamToken }); - const { isPc } = useSystemStore(); + const { isPc, whisperModel } = useSystemStore(); const canvasRef = useRef(null); const { t } = useTranslation(); @@ -369,7 +369,7 @@ const MessageInput = ({ bottom={['10px', '12px']} > {/* voice-input */} - {!shareId && !havInput && !isChatting && ( + {!shareId && !havInput && !isChatting && !!whisperModel && ( <> @@ -410,7 +410,7 @@ const MessageInput = ({ )} {/* send and stop icon */} {isSpeaking ? ( - + {speakingTimeString} ) : ( diff --git a/projects/app/src/components/ChatBox/SelectMarkCollection.tsx b/projects/app/src/components/ChatBox/SelectMarkCollection.tsx index de4aaa5f9..7dc187a2a 100644 --- a/projects/app/src/components/ChatBox/SelectMarkCollection.tsx +++ b/projects/app/src/components/ChatBox/SelectMarkCollection.tsx @@ -32,8 +32,6 @@ const SelectMarkCollection = ({ }) => { const { t } = useTranslation(); const theme = useTheme(); - const [selectedDatasetId, setSelectedDatasetId] = useState(); - const [selectedDatasetCollectionIds, setSelectedDatasetCollectionIds] = useState([]); const { paths, setParentId, datasets, isFetching } = useDatasetSelect(); return ( @@ -45,17 +43,18 @@ const SelectMarkCollection = ({ paths={paths} onClose={onClose} setParentId={setParentId} + isLoading={isFetching} tips={t('core.chat.Select dataset Desc')} > {datasets.map((item) => (() => { - const selected = selectedDatasetId === item._id; return ( { if (item.type === DatasetTypeEnum.folder) { setParentId(item._id); } else { - setSelectedDatasetId(item._id); + setAdminMarkData({ ...adminMarkData, datasetId: item._id }); } }} > @@ -104,29 +98,21 @@ const SelectMarkCollection = ({ )} - - - )} {/* select collection */} - {adminMarkData.datasetId && !adminMarkData.collectionId && ( + {adminMarkData.datasetId && ( { - setSelectedDatasetCollectionIds(collectionIds); + setAdminMarkData({ + ...adminMarkData, + collectionId: collectionIds[0] + }); }} CustomFooter={ @@ -142,17 +128,6 @@ const SelectMarkCollection = ({ > {t('common.Last Step')} - } /> @@ -161,7 +136,12 @@ const SelectMarkCollection = ({ {/* input data */} {adminMarkData.datasetId && adminMarkData.collectionId && ( { + setAdminMarkData({ + ...adminMarkData, + collectionId: undefined + }); + }} collectionId={adminMarkData.collectionId} dataId={adminMarkData.dataId} defaultValue={{ diff --git a/projects/app/src/components/ChatBox/components/ChatItem.tsx b/projects/app/src/components/ChatBox/components/ChatItem.tsx index 024f2714a..e13d127e4 100644 --- a/projects/app/src/components/ChatBox/components/ChatItem.tsx +++ b/projects/app/src/components/ChatBox/components/ChatItem.tsx @@ -90,13 +90,14 @@ const ChatItem = ({ ); } + /* AI */ return ( {chat.value.map((value, i) => { const key = `${chat.dataId}-ai-${i}`; if (value.text) { - let source = value.text?.content || ''; + let source = (value.text?.content || '').trim(); if (!source && chat.value.length > 1) return <>; @@ -137,6 +138,7 @@ ${JSON.stringify(questionGuides)}`; return tool.response; } })(); + return ( @@ -169,7 +171,7 @@ ${JSON.stringify(questionGuides)}`; maxH={'500px'} overflowY={'auto'} > - {toolParams && ( + {toolParams && toolParams !== '{}' && ( { @@ -676,7 +676,7 @@ const ChatBox = ( }); }; }, - [appId, chatId, feedbackType] + [appId, chatId, feedbackType, teamId, teamToken] ); const onADdUserDislike = useCallback( (chat: ChatSiteItemType) => { @@ -713,7 +713,7 @@ const ChatBox = ( return () => setFeedbackId(chat.dataId); } }, - [appId, chatId, feedbackType, outLinkUid, shareId] + [appId, chatId, feedbackType, outLinkUid, shareId, teamId, teamToken] ); const onReadUserDislike = useCallback( (chat: ChatSiteItemType) => { @@ -938,7 +938,7 @@ const ChatBox = ( icon="core/app/markLight" text={t('core.chat.Admin Mark Content')} /> - + {item.adminFeedback.q} {item.adminFeedback.a} diff --git a/projects/app/src/components/Layout/index.tsx b/projects/app/src/components/Layout/index.tsx index 1262318b9..4a21b9f71 100644 --- a/projects/app/src/components/Layout/index.tsx +++ b/projects/app/src/components/Layout/index.tsx @@ -14,6 +14,8 @@ import Navbar from './navbar'; import NavbarPhone from './navbarPhone'; const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal')); const NotSufficientModal = dynamic(() => import('@/components/support/wallet/NotSufficientModal')); +const SystemMsgModal = dynamic(() => import('@/components/support/user/inform/SystemMsgModal')); +const ImportantInform = dynamic(() => import('@/components/support/user/inform/ImportantInform')); const pcUnShowLayoutRoute: Record = { '/': true, @@ -70,10 +72,12 @@ const Layout = ({ children }: { children: JSX.Element }) => { }; }, [setScreenWidth]); - const { data: unread = 0 } = useQuery(['getUnreadCount'], getUnreadCount, { + const { data, refetch: refetchUnRead } = useQuery(['getUnreadCount'], getUnreadCount, { enabled: !!userInfo && !!feConfigs.isPlus, refetchInterval: 10000 }); + const unread = data?.unReadCount || 0; + const importantInforms = data?.importantInforms || []; const isHideNavbar = !!pcUnShowLayoutRoute[router.pathname]; @@ -117,6 +121,10 @@ const Layout = ({ children }: { children: JSX.Element }) => { {!!userInfo && } {isNotSufficientModal && !isHideNavbar && } + {!!userInfo && } + {!!userInfo && importantInforms.length > 0 && ( + + )} diff --git a/projects/app/src/components/Markdown/chat/QuestionGuide.tsx b/projects/app/src/components/Markdown/chat/QuestionGuide.tsx index e187bb3d8..a955048d2 100644 --- a/projects/app/src/components/Markdown/chat/QuestionGuide.tsx +++ b/projects/app/src/components/Markdown/chat/QuestionGuide.tsx @@ -24,7 +24,7 @@ const QuestionGuide = ({ text }: { text: string }) => { return questionGuides.length > 0 ? ( - + {questionGuides.map((text) => ( any; }[]; } @@ -22,15 +33,25 @@ const MyMenu = ({ Button, menuList }: Props) => { - const menuItemStyles = { + const typeMapStyle: Record = { + primary: { + _hover: { + backgroundColor: 'primary.50', + color: 'primary.600' + } + }, + danger: { + _hover: { + color: 'red.600', + background: 'red.1' + } + } + }; + const menuItemStyles: MenuItemProps = { borderRadius: 'sm', py: 3, display: 'flex', - alignItems: 'center', - _hover: { - backgroundColor: 'myGray.05', - color: 'primary.600' - } + alignItems: 'center' }; const ref = useRef(null); const closeTimer = useRef(); @@ -92,6 +113,7 @@ const MyMenu = ({ { e.stopPropagation(); setIsOpen(false); diff --git a/projects/app/src/components/SideTabs/index.tsx b/projects/app/src/components/SideTabs/index.tsx index e65b87eea..5209c8aeb 100644 --- a/projects/app/src/components/SideTabs/index.tsx +++ b/projects/app/src/components/SideTabs/index.tsx @@ -42,16 +42,17 @@ const SideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => borderRadius={'md'} px={3} mb={2} + fontWeight={'medium'} alignItems={'center'} {...(activeId === item.id ? { bg: ' primary.100 !important', - fontWeight: 'bold', color: 'primary.600 ', cursor: 'default' } : { - cursor: 'pointer' + cursor: 'pointer', + color: 'myGray.600' })} _hover={{ bg: 'myGray.05' @@ -61,7 +62,7 @@ const SideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => onChange(item.id); }} > - + {item.label} ))} diff --git a/projects/app/src/components/common/MyRadio/index.tsx b/projects/app/src/components/common/MyRadio/index.tsx index 69b8ba44a..18d8ce447 100644 --- a/projects/app/src/components/common/MyRadio/index.tsx +++ b/projects/app/src/components/common/MyRadio/index.tsx @@ -2,14 +2,22 @@ import React from 'react'; import { Box, Flex, useTheme, Grid, type GridProps, theme, Image } from '@chakra-ui/react'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useTranslation } from 'next-i18next'; +import { useToast } from '@fastgpt/web/hooks/useToast'; // @ts-ignore interface Props extends GridProps { - list: { icon?: string; title: string | React.ReactNode; desc?: string; value: any }[]; + list: { + icon?: string; + title: string | React.ReactNode; + desc?: string; + value: any; + forbidTip?: string; // If this value is exists, it will be prompted to disable when clicked + }[]; iconSize?: string; align?: 'top' | 'center'; value: any; hiddenCircle?: boolean; + onChange: (e: any) => void; } @@ -25,6 +33,8 @@ const MyRadio = ({ }: Props) => { const { t } = useTranslation(); const theme = useTheme(); + const { toast } = useToast(); + return ( {list.map((item) => ( @@ -73,7 +83,16 @@ const MyRadio = ({ borderColor: 'myGray.200' }) }} - onClick={() => onChange(item.value)} + onClick={() => { + if (item.forbidTip) { + toast({ + status: 'warning', + title: item.forbidTip + }); + } else { + onChange(item.value); + } + }} > {!!item.icon && ( <> diff --git a/projects/app/src/components/core/dataset/RawSourceBox.tsx b/projects/app/src/components/core/dataset/RawSourceBox.tsx index e4e0ba381..be9345bce 100644 --- a/projects/app/src/components/core/dataset/RawSourceBox.tsx +++ b/projects/app/src/components/core/dataset/RawSourceBox.tsx @@ -30,7 +30,8 @@ const RawSourceBox = ({ sourceId, sourceName = '', canView = true, ...props }: P shouldWrapChildren={false} > void; tips?: string | null; + isLoading?: boolean; children: React.ReactNode; }) => { const { t } = useTranslation(); @@ -57,7 +60,9 @@ const DatasetSelectContainer = ({ maxW={['90vw', '900px']} isCentered > - {children} + + {children} + ); }; diff --git a/projects/app/src/components/core/module/DatasetParamsModal.tsx b/projects/app/src/components/core/module/DatasetParamsModal.tsx index 75edb45fc..db3933c18 100644 --- a/projects/app/src/components/core/module/DatasetParamsModal.tsx +++ b/projects/app/src/components/core/module/DatasetParamsModal.tsx @@ -81,6 +81,8 @@ const DatasetParamsModal = ({ const datasetSearchUsingCfrForm = watch('datasetSearchUsingExtensionQuery'); const queryExtensionModel = watch('datasetSearchExtensionModel'); const cfbBgDesc = watch('datasetSearchExtensionBg'); + const usingReRankWatch = watch('usingReRank'); + const searchModeWatch = watch('searchMode'); const chatModelSelectList = (() => llmModelList @@ -97,16 +99,10 @@ const DatasetParamsModal = ({ const showSimilarity = useMemo(() => { if (similarity === undefined) return false; - if ( - getValues('searchMode') === DatasetSearchModeEnum.fullTextRecall && - !getValues('usingReRank') - ) - return false; - if (getValues('searchMode') === DatasetSearchModeEnum.mixedRecall && !getValues('usingReRank')) - return false; - - return true; - }, [getValues, similarity]); + if (usingReRankWatch) return true; + if (searchModeWatch === DatasetSearchModeEnum.embedding) return true; + return false; + }, [searchModeWatch, similarity, usingReRankWatch]); const showReRank = useMemo(() => { return usingReRank !== undefined && reRankModelList.length > 0; @@ -155,64 +151,57 @@ const DatasetParamsModal = ({ setRefresh(!refresh); }} /> - {showReRank && ( - <> - - { - if ( - teamPlanStatus?.standardConstants && - !teamPlanStatus?.standardConstants?.permissionReRank - ) { - return toast({ - status: 'warning', - title: t('support.team.limit.No permission rerank') - }); + <> + + !state); - }} - > - - - {t('core.dataset.search.ReRank')} - - {t('core.dataset.search.ReRank desc')} - + : {})} + onClick={(e) => { + if (!showReRank) { + return toast({ + status: 'warning', + title: t('core.ai.Not deploy rerank model') + }); + } + if ( + teamPlanStatus?.standardConstants && + !teamPlanStatus?.standardConstants?.permissionReRank + ) { + return toast({ + status: 'warning', + title: t('support.team.limit.No permission rerank') + }); + } + setValue('usingReRank', !getValues('usingReRank')); + setRefresh((state) => !state); + }} + > + + + {t('core.dataset.search.ReRank')} + + {t('core.dataset.search.ReRank desc')} - - - - - - - )} + + + + + + + )} {currentTabType === SearchSettingTabEnum.limit && ( @@ -243,15 +232,15 @@ const DatasetParamsModal = ({ )} - {showSimilarity && ( - - - {t('core.dataset.search.Min Similarity')} - - - - - + + + {t('core.dataset.search.Min Similarity')} + + + + + + {showSimilarity ? ( - + ) : ( + {t('core.dataset.search.No support similarity')} + )} - )} + )} {currentTabType === SearchSettingTabEnum.queryExtension && ( diff --git a/projects/app/src/components/core/module/Flow/ModuleTemplateList.tsx b/projects/app/src/components/core/module/Flow/ModuleTemplateList.tsx index 540acf2cd..f0ff49040 100644 --- a/projects/app/src/components/core/module/Flow/ModuleTemplateList.tsx +++ b/projects/app/src/components/core/module/Flow/ModuleTemplateList.tsx @@ -20,7 +20,7 @@ import { getErrText } from '@fastgpt/global/common/error/utils'; import { moduleTemplatesList } from '@fastgpt/global/core/module/template/constants'; import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs'; import { useWorkflowStore } from '@/web/core/workflow/store/workflow'; -import { useRequest } from '@/web/common/hooks/useRequest'; +import { useRequest } from '@fastgpt/web/hooks/useRequest'; import ParentPaths from '@/components/common/ParentPaths'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useRouter } from 'next/router'; @@ -70,7 +70,7 @@ const ModuleTemplateList = ({ isOpen, onClose }: ModuleTemplateListProps) => { searchKey ? item.pluginType !== PluginTypeEnum.folder : true ) }; - return map[templateType]; + return JSON.stringify(map[templateType]); }, [basicNodeTemplates, searchKey, systemNodeTemplates, teamPluginNodeTemplates, templateType]); const { mutate: onChangeTab } = useRequest({ @@ -96,120 +96,125 @@ const ModuleTemplateList = ({ isOpen, onClose }: ModuleTemplateListProps) => { }) ); - return ( - <> - - - - - - {/* close icon */} - } - w={'26px'} - h={'26px'} - borderColor={'myGray.300'} - variant={'grayBase'} - aria-label={''} - onClick={onClose} - /> - - {templateType === TemplateTypeEnum.teamPlugin && ( - - - - - - - - router.push('/plugin/list')} - > - 去创建 - - - - )} - {templateType === TemplateTypeEnum.teamPlugin && !searchKey && currentParent && ( - - { - setCurrentParent(undefined); - }} - fontSize="md" + const Render = useMemo(() => { + const parseTemplates = JSON.parse(templates) as FlowNodeTemplateType[]; + return ( + <> + + + + + + {/* close icon */} + } + w={'26px'} + h={'26px'} + borderColor={'myGray.300'} + variant={'grayBase'} + aria-label={''} + onClick={onClose} /> - )} - - - - - ); + {templateType === TemplateTypeEnum.teamPlugin && ( + + + + + + + + router.push('/plugin/list')} + > + 去创建 + + + + )} + {templateType === TemplateTypeEnum.teamPlugin && !searchKey && currentParent && ( + + { + setCurrentParent(undefined); + }} + fontSize="md" + /> + + )} + + + + + ); + }, [currentParent, isOpen, onChangeTab, onClose, router, searchKey, t, templateType, templates]); + + return Render; }; export default React.memo(ModuleTemplateList); diff --git a/projects/app/src/components/core/module/Flow/SelectAppModal.tsx b/projects/app/src/components/core/module/Flow/SelectAppModal.tsx index 8f1bf6937..1802f918b 100644 --- a/projects/app/src/components/core/module/Flow/SelectAppModal.tsx +++ b/projects/app/src/components/core/module/Flow/SelectAppModal.tsx @@ -40,11 +40,10 @@ const SelectAppModal = ({ title={`选择应用${max > 1 ? `(${selectedApps.length}/${max})` : ''}`} iconSrc="/imgs/module/ai.svg" onClose={onClose} - minW={'700px'} position={'relative'} + w={'600px'} > { const { t } = useTranslation(); return ( - - {t('core.app.Next Step Guide')} + + {t('core.app.Question Guide')} diff --git a/projects/app/src/components/core/module/Flow/components/modules/VariableEdit.tsx b/projects/app/src/components/core/module/Flow/components/modules/VariableEdit.tsx index a5616c6c5..fb3ea556e 100644 --- a/projects/app/src/components/core/module/Flow/components/modules/VariableEdit.tsx +++ b/projects/app/src/components/core/module/Flow/components/modules/VariableEdit.tsx @@ -124,12 +124,12 @@ const VariableEdit = ({ - - - - - + + + + + diff --git a/projects/app/src/components/core/module/Flow/components/nodes/NodeCQNode.tsx b/projects/app/src/components/core/module/Flow/components/nodes/NodeCQNode.tsx index e5da08fd0..24256e339 100644 --- a/projects/app/src/components/core/module/Flow/components/nodes/NodeCQNode.tsx +++ b/projects/app/src/components/core/module/Flow/components/nodes/NodeCQNode.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { NodeProps } from 'reactflow'; import { Box, Button, Flex, Textarea } from '@chakra-ui/react'; import NodeCard from '../render/NodeCard'; @@ -16,93 +16,69 @@ import { useTranslation } from 'next-i18next'; import SourceHandle from '../render/SourceHandle'; import MyTooltip from '@/components/MyTooltip'; import { onChangeNode } from '../../FlowProvider'; +import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type'; const NodeCQNode = ({ data, selected }: NodeProps) => { const { t } = useTranslation(); const { moduleId, inputs } = data; - return ( - - - - { - const agents = value as ClassifyQuestionAgentItemType[]; - return ( - - {agents.map((item, i) => ( - - - - { - onChangeNode({ - moduleId, - type: 'updateInput', - key: agentKey, - value: { - ...props, - key: agentKey, - value: agents.filter((input) => input.key !== item.key) - } - }); - onChangeNode({ - moduleId, - type: 'delOutput', - key: item.key - }); - }} - /> - - 分类{i + 1} - - -
- {t('core.module.variable.variable name')}{t('core.module.variable.key')}{t('common.Require Input')}
+ {t('core.module.variable.variable name')}{t('core.module.variable.key')}{t('common.Require Input')}