4.7-production (#1053)
* 4.7-alpha3 (#62) * doc * Optimize possible null Pointers and parts of Ux * fix: mulity index training error * feat: doc and rename question guide * fix ios speech input (#59) * fix: prompt editor variables nowrap (#61) * change openapi import in http module with curl import (#60) * chore(ui): dataset import modal ui (#58) * chore(ui): dataset import modal ui * use component * fix height * 4.7 (#63) * fix: claude3 image type verification failed (#1038) (#1040) * perf: curl import modal * doc img * perf: adapt cohere rerank * perf: code * perf: input style * doc --------- Co-authored-by: xiaotian <dimsky@163.com> * fix: ts * docker deploy * perf: prompt call * doc * ts * finish ui * perf: outlink detail ux * perf: user schema * fix: plugin update * feat: get current time plugin * fix: ts * perf: fetch anamation * perf: mark ux * doc * perf: select app ux * fix: split text custom string conflict * peref: inform readed * doc * memo flow component * perf: version * faq * feat: flow max runtimes * feat: similarity tip * feat: auto detect file encoding * Supports asymmetric vector model * fix: ts * perf: max w * move code * perf: hide whisper * fix: ts * feat: system msg modal * perf: catch error * perf: inform tip * fix: inform --------- Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com> Co-authored-by: xiaotian <dimsky@163.com>
2
.github/ISSUE_TEMPLATE/bugs.md
vendored
@@ -19,7 +19,7 @@ assignees: ''
|
||||
**你的版本**
|
||||
|
||||
- [ ] 公有云版本
|
||||
- [ ] 私有部署版本
|
||||
- [ ] 私有部署版本, 具体版本号:
|
||||
|
||||
**问题描述**
|
||||
|
||||
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 629 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 753 KiB After Width: | Height: | Size: 267 KiB |
BIN
docSite/assets/imgs/google_search_1.png
Normal file
After Width: | Height: | Size: 298 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 133 KiB |
BIN
docSite/assets/imgs/question_guide.png
Normal file
After Width: | Height: | Size: 230 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
BIN
docSite/assets/imgs/tts_setting.png
Normal file
After Width: | Height: | Size: 215 KiB |
BIN
docSite/assets/imgs/tts_setting2.png
Normal file
After Width: | Height: | Size: 250 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 216 KiB |
BIN
docSite/assets/imgs/variable3.png
Normal file
After Width: | Height: | Size: 220 KiB |
BIN
docSite/assets/imgs/variable4.png
Normal file
After Width: | Height: | Size: 222 KiB |
BIN
docSite/assets/imgs/variable5.png
Normal file
After Width: | Height: | Size: 243 KiB |
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
@@ -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:
|
||||
|
@@ -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 副本集模式。
|
||||
|
@@ -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`查看环境变量是否生效。
|
@@ -48,7 +48,7 @@ git clone git@github.com:<github_username>/FastGPT.git
|
||||
|
||||
第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。
|
||||
|
||||
Mongo 数据库需要修改副本集的`host`,从原来的`mongo:27017`修改为`ip:27017`(ip为对应的公网IP)。
|
||||
Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnection=true` 参数,才能连接上副本集的数据库。
|
||||
|
||||
### 4. 初始配置
|
||||
|
||||
|
@@ -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 当做一个网关。
|
||||
|
||||

|
||||
|
||||
## 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)
|
||||
}
|
||||
...
|
||||
],
|
||||
|
@@ -36,7 +36,7 @@ weight: 860
|
||||
|
||||
### 触发流程
|
||||
|
||||

|
||||

|
||||
|
||||
## 配置教程
|
||||
### 1. 配置身份校验地址
|
||||
|
@@ -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
|
||||
|
||||
## 修改配置文件
|
||||
|
@@ -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 模型。
|
||||
|
@@ -13,6 +13,59 @@ weight: 353
|
||||
- 无外部输入
|
||||
- 不参与实际调度
|
||||
|
||||
如图,可以在用户提问前给予一定引导。并可以设置引导问题。
|
||||
## 作用
|
||||
|
||||
可以配置欢迎语、全局变量、语言播报等,类似于系统设置,不参与工作流的运行。
|
||||
|
||||

|
||||
|
||||
### 欢迎语
|
||||
|
||||
会在对话开始前发送一个欢迎语。该功能只在网页生效,API是无效的,并且该欢迎语不会被加入 AI 的对话记录中。
|
||||
|
||||
可以通过特殊的 Markdown 语法,来实现快速提问。 如图中的:
|
||||
|
||||
```
|
||||
[剧情介绍]
|
||||
[导演是谁]
|
||||
```
|
||||
|
||||
### 全局变量
|
||||
|
||||
| | |
|
||||
| ----- | ----- |
|
||||
|  |  |
|
||||
|
||||
如上图中,我们配置了一个名为`测试`, key为`test`的变量,类型为`文本`的全局变量。并在对话中,通过`{{test}}`来引用该变量。
|
||||
|
||||
|
||||
| | |
|
||||
| ----- | ----- |
|
||||
|  |  |
|
||||
|
||||
从上图中,可以看出,实际的提示词从:`这是一个变量测试: {{test}}` 变成了: `这是一个变量测试: 变量测试`,因为`{{test}}` 被变量替换了。
|
||||
|
||||
变量在工作流中大部分的`文本输入框`都生效,例如:HTTP模块的URL和参数、各种功能块的提示词。
|
||||
|
||||
有一个特殊类型的变量,交`外部传入`。这种变量不需要用户填写,而是直接在调用时实时传入。目前支持从分享链接的Query和API调用的`variables`对象中获取。
|
||||
|
||||

|
||||
|
||||
|
||||
------
|
||||
|
||||
除了自定义的全局变量外,还有一些系统参数可以选择:
|
||||
|
||||
+ **cTime**: 当前时间,字符串格式,例如:2023/3/3 20:22
|
||||
|
||||
### 语音播报
|
||||
|
||||
| | |
|
||||
| ----- | ----- |
|
||||
|  |  |
|
||||
|
||||
### 猜你想问
|
||||
|
||||
开启后,每次对话结束,会发送最近的6条对话记录给AI,AI会根据这些对话记录,给出 3 个可能的问题。
|
||||
|
||||

|
||||
|
@@ -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 规则(正则匹配通过) |
|
||||
|
||||
## 作用
|
||||
|
||||
|
@@ -1,35 +0,0 @@
|
||||
---
|
||||
title: "全局变量"
|
||||
description: "FastGPT 全局变量模块介绍"
|
||||
icon: "variables"
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 361
|
||||
---
|
||||
|
||||
## 特点
|
||||
|
||||
- 仅可添加 1 个
|
||||
- 需要手动配置
|
||||
- 对其他模块有影响
|
||||
- 可作为用户引导
|
||||
|
||||
## 说明
|
||||
|
||||
可以在对话前设置一些问题,让用户输入或选择,并将用户输入/选择的结果注入到其他模块中。目前仅会注入到 string 类型的数据里(对应蓝色圆圈的输入)。
|
||||
|
||||
如下图,定义了两个变量:目标语言和下拉框测试(忽略)
|
||||
|
||||
用户在对话前会被要求先填写目标语言,配合用户引导,我们就构建了一个简单的翻译机器人。**目标语言**的 `key:language` 被写入到【AI 对话】模块的限定词里。
|
||||
|
||||

|
||||
|
||||
通过完整对话记录我们可以看到,实际的限定词从:“将我的问题直接翻译成{{language}}” 变成了 “将我的问题直接翻译成英语”,因为 {{language}} 被变量替换了。
|
||||
|
||||

|
||||
|
||||
## 系统级变量
|
||||
|
||||
除了用户自定义设置的变量外,还会有一些系统变量:
|
||||
|
||||
+ **cTime**: 当前时间。例如:2023/3/3 20:22
|
@@ -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:
|
||||
|
@@ -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';
|
||||
};
|
||||
|
@@ -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 },
|
||||
|
@@ -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'
|
||||
}
|
||||
};
|
||||
|
5
packages/global/core/ai/api.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
export type PostReRankProps = {
|
||||
query: string;
|
||||
inputs: { id: string; text: string }[];
|
||||
};
|
||||
export type PostReRankResponse = { id: string; score?: number }[];
|
@@ -25,3 +25,8 @@ export const llmModelTypeFilterMap = {
|
||||
[LLMModelTypeEnum.toolCall]: 'usedInToolCall',
|
||||
[LLMModelTypeEnum.queryExtension]: 'usedInQueryExtension'
|
||||
};
|
||||
|
||||
export enum EmbeddingTypeEnm {
|
||||
query = 'query',
|
||||
db = 'db'
|
||||
}
|
||||
|
22
packages/global/core/ai/model.d.ts
vendored
@@ -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<string, any>;
|
||||
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<string, any>; // post request config
|
||||
dbConfig?: Record<string, any>; // Custom parameters for storage
|
||||
queryConfig?: Record<string, any>; // Custom parameters for query
|
||||
};
|
||||
|
||||
export type ReRankModelItemType = {
|
||||
model: string;
|
||||
name: string;
|
||||
charsPointsPrice: number;
|
||||
requestUrl?: string;
|
||||
requestAuth?: string;
|
||||
requestUrl: string;
|
||||
requestAuth: string;
|
||||
};
|
||||
|
||||
export type AudioSpeechModelType = {
|
||||
|
@@ -1,4 +1,5 @@
|
||||
export enum FlowNodeInputTypeEnum {
|
||||
triggerAndFinish = 'triggerAndFinish',
|
||||
systemInput = 'systemInput', // history, userChatInput, variableInput
|
||||
|
||||
input = 'input', // one line input
|
||||
|
@@ -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,
|
||||
|
@@ -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: []
|
||||
};
|
||||
|
||||
|
@@ -48,7 +48,6 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
valueType: ModuleIOValueTypeEnum.datasetQuote,
|
||||
targets: []
|
||||
},
|
||||
Output_Template_Finish
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@@ -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
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@@ -99,7 +99,6 @@ export const HttpModule468: FlowNodeTemplateType = {
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
Output_Template_Finish,
|
||||
{
|
||||
key: ModuleOutputKeyEnum.httpRawResponse,
|
||||
label: '原始响应',
|
||||
|
1
packages/global/core/module/type.d.ts
vendored
@@ -114,6 +114,7 @@ export type ChatDispatchProps = {
|
||||
inputFiles?: UserChatItemValueItemType['file'][];
|
||||
stream: boolean;
|
||||
detail: boolean; // response detail
|
||||
maxRunTimes: number;
|
||||
};
|
||||
|
||||
export type ModuleDispatchProps<T> = ChatDispatchProps & {
|
||||
|
@@ -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",
|
||||
|
@@ -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: '紧急'
|
||||
}
|
||||
};
|
||||
|
10
packages/global/support/user/inform/type.d.ts
vendored
@@ -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;
|
||||
|
7
packages/global/support/user/type.d.ts
vendored
@@ -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'];
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -10,15 +10,15 @@ export const removeFilesByPaths = (paths: string[]) => {
|
||||
});
|
||||
};
|
||||
|
||||
const imageTypeMap: Record<string, string> = {
|
||||
'/': 'image/jpeg',
|
||||
i: 'image/png',
|
||||
R: 'image/gif',
|
||||
U: 'image/webp',
|
||||
Q: 'image/bmp'
|
||||
};
|
||||
export const guessBase64ImageType = (str: string) => {
|
||||
const imageTypeMap: Record<string, string> = {
|
||||
'/': '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;
|
||||
|
@@ -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,
|
||||
|
@@ -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]
|
||||
})
|
||||
|
@@ -160,7 +160,7 @@ A: ${chatBg}
|
||||
|
||||
return {
|
||||
rawQuery: query,
|
||||
extensionQueries: queries,
|
||||
extensionQueries: Array.isArray(queries) ? queries : [],
|
||||
model,
|
||||
tokens: countGptMessagesTokens(messages)
|
||||
};
|
||||
|
@@ -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<ReRankCallResult> {
|
||||
const model = global.reRankModels[0];
|
||||
|
||||
if (!model || !model?.requestUrl) {
|
||||
@@ -12,19 +26,24 @@ export function reRankRecall({ query, inputs }: PostReRankProps) {
|
||||
return POST<PostReRankResponse>(
|
||||
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);
|
||||
|
@@ -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}`
|
||||
}))
|
||||
|
@@ -1,12 +1,9 @@
|
||||
export const Prompt_Tool_Call = `<Instruction>
|
||||
你是一个智能机器人,除了可以回答用户问题外,你还掌握工具的使用能力。有时候,你可以依赖工具的运行结果,来更准确的回答用户。
|
||||
下面是你可以使用的工具,使用 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: 今天杭州是晴天,适合去西湖、灵隐寺、千岛湖等地玩。
|
||||
</Instruction>
|
||||
|
||||
现在,我们开始吧!
|
||||
现在,我们开始吧!下面是你本次可以使用的工具:
|
||||
|
||||
"""
|
||||
{{toolsPrompt}}
|
||||
"""
|
||||
|
||||
下面是正式的对话内容:
|
||||
|
||||
USER: {{question}}
|
||||
ANSWER:
|
||||
|
@@ -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<any> {
|
||||
if (res.closed) return Promise.resolve();
|
||||
if (res.closed || props.maxRunTimes <= 0) return Promise.resolve();
|
||||
|
||||
if (stream && detail && module.showStatus) {
|
||||
responseStatus({
|
||||
|
@@ -19,7 +19,6 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
mode,
|
||||
teamId,
|
||||
tmbId,
|
||||
module,
|
||||
params: { pluginId, ...data }
|
||||
} = props;
|
||||
|
||||
|
@@ -157,7 +157,7 @@ async function fetchData({
|
||||
body: Record<string, any>;
|
||||
params: Record<string, any>;
|
||||
}): Promise<Record<string, any>> {
|
||||
const { data: response } = await axios<Record<string, any>>({
|
||||
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
|
||||
};
|
||||
}
|
||||
|
@@ -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,
|
||||
|
4
packages/service/support/user/inform/type.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export type SystemMsgModalValueType = {
|
||||
id: string;
|
||||
content: string;
|
||||
};
|
@@ -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,
|
||||
|
@@ -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, {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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'),
|
||||
|
@@ -0,0 +1,9 @@
|
||||
<svg t="1711347951866" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6234"
|
||||
width="128" height="128">
|
||||
<path
|
||||
d="M979.2 158.848a32 32 0 0 0-38.4-23.936l-166.848 38.656a32 32 0 0 0-15.808 53.408L794.56 264.64l-194.56 173.568-101.024-95.68a32 32 0 0 0-45.152 1.152l-216.736 227.264A64 64 0 1 0 288 633.6c0-6.944-1.376-13.472-3.424-19.712l193.536-202.944 99.232 93.952a32 32 0 0 0 43.296 0.64L839.04 310.72l41.504 42.976a32 32 0 0 0 53.92-13.92l44.448-165.408a31.84 31.84 0 0 0 0.288-15.52z"
|
||||
p-id="6235"></path>
|
||||
<path
|
||||
d="M928 450.464a32 32 0 0 0-32 32V736a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h530.656a32 32 0 0 0 0-64H160a96 96 0 0 0-96 96v576a96 96 0 0 0 96 96h704a96 96 0 0 0 96-96v-253.536a32 32 0 0 0-32-32zM912 896h-800a32 32 0 0 0 0 64h800a32 32 0 0 0 0-64z"
|
||||
p-id="6236"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 900 B |
@@ -1,3 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.64302 0.976311C8.26814 0.351189 9.11599 0 10 0C10.8841 0 11.7319 0.351189 12.3571 0.976311C12.9822 1.60143 13.3334 2.44928 13.3334 3.33333V10C13.3334 10.8841 12.9822 11.7319 12.3571 12.357C11.7319 12.9821 10.8841 13.3333 10 13.3333C9.11599 13.3333 8.26814 12.9821 7.64302 12.357C7.0179 11.7319 6.66671 10.8841 6.66671 10V3.33333C6.66671 2.44928 7.0179 1.60143 7.64302 0.976311ZM10 1.66667C9.55801 1.66667 9.13409 1.84226 8.82153 2.15482C8.50897 2.46738 8.33337 2.89131 8.33337 3.33333V10C8.33337 10.442 8.50897 10.866 8.82153 11.1785C9.13409 11.4911 9.55801 11.6667 10 11.6667C10.4421 11.6667 10.866 11.4911 11.1786 11.1785C11.4911 10.866 11.6667 10.442 11.6667 10V3.33333C11.6667 2.89131 11.4911 2.46738 11.1786 2.15482C10.866 1.84226 10.4421 1.66667 10 1.66667ZM4.16671 7.5C4.62694 7.5 5.00004 7.8731 5.00004 8.33333V10C5.00004 11.3261 5.52682 12.5979 6.46451 13.5355C7.40219 14.4732 8.67396 15 10 15C11.3261 15 12.5979 14.4732 13.5356 13.5355C14.4733 12.5979 15 11.3261 15 10V8.33333C15 7.8731 15.3731 7.5 15.8334 7.5C16.2936 7.5 16.6667 7.8731 16.6667 8.33333V10C16.6667 11.7681 15.9643 13.4638 14.7141 14.714C13.6619 15.7662 12.2942 16.4304 10.8334 16.6144V18.3333H13.3334C13.7936 18.3333 14.1667 18.7064 14.1667 19.1667C14.1667 19.6269 13.7936 20 13.3334 20H6.66671C6.20647 20 5.83337 19.6269 5.83337 19.1667C5.83337 18.7064 6.20647 18.3333 6.66671 18.3333H9.16671V16.6144C7.70587 16.4304 6.33818 15.7662 5.286 14.714C4.03575 13.4638 3.33337 11.7681 3.33337 10V8.33333C3.33337 7.8731 3.70647 7.5 4.16671 7.5Z" fill="#485058"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M7.64302 0.976311C8.26814 0.351189 9.11599 0 10 0C10.8841 0 11.7319 0.351189 12.3571 0.976311C12.9822 1.60143 13.3334 2.44928 13.3334 3.33333V10C13.3334 10.8841 12.9822 11.7319 12.3571 12.357C11.7319 12.9821 10.8841 13.3333 10 13.3333C9.11599 13.3333 8.26814 12.9821 7.64302 12.357C7.0179 11.7319 6.66671 10.8841 6.66671 10V3.33333C6.66671 2.44928 7.0179 1.60143 7.64302 0.976311ZM10 1.66667C9.55801 1.66667 9.13409 1.84226 8.82153 2.15482C8.50897 2.46738 8.33337 2.89131 8.33337 3.33333V10C8.33337 10.442 8.50897 10.866 8.82153 11.1785C9.13409 11.4911 9.55801 11.6667 10 11.6667C10.4421 11.6667 10.866 11.4911 11.1786 11.1785C11.4911 10.866 11.6667 10.442 11.6667 10V3.33333C11.6667 2.89131 11.4911 2.46738 11.1786 2.15482C10.866 1.84226 10.4421 1.66667 10 1.66667ZM4.16671 7.5C4.62694 7.5 5.00004 7.8731 5.00004 8.33333V10C5.00004 11.3261 5.52682 12.5979 6.46451 13.5355C7.40219 14.4732 8.67396 15 10 15C11.3261 15 12.5979 14.4732 13.5356 13.5355C14.4733 12.5979 15 11.3261 15 10V8.33333C15 7.8731 15.3731 7.5 15.8334 7.5C16.2936 7.5 16.6667 7.8731 16.6667 8.33333V10C16.6667 11.7681 15.9643 13.4638 14.7141 14.714C13.6619 15.7662 12.2942 16.4304 10.8334 16.6144V18.3333H13.3334C13.7936 18.3333 14.1667 18.7064 14.1667 19.1667C14.1667 19.6269 13.7936 20 13.3334 20H6.66671C6.20647 20 5.83337 19.6269 5.83337 19.1667C5.83337 18.7064 6.20647 18.3333 6.66671 18.3333H9.16671V16.6144C7.70587 16.4304 6.33818 15.7662 5.286 14.714C4.03575 13.4638 3.33337 11.7681 3.33337 10V8.33333C3.33337 7.8731 3.70647 7.5 4.16671 7.5Z" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -1,10 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">
|
||||
<g clip-path="url(#clip0_74_2)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.49999C5.85791 2.49999 2.50004 5.85786 2.50004 10C2.50004 14.1421 5.85791 17.5 10 17.5C14.1422 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1422 2.49999 10 2.49999ZM0.833374 10C0.833374 4.93739 4.93743 0.833328 10 0.833328C15.0627 0.833328 19.1667 4.93739 19.1667 10C19.1667 15.0626 15.0627 19.1667 10 19.1667C4.93743 19.1667 0.833374 15.0626 0.833374 10ZM6.66671 7.5C6.66671 7.03976 7.0398 6.66666 7.50004 6.66666H12.5C12.9603 6.66666 13.3334 7.03976 13.3334 7.5V12.5C13.3334 12.9602 12.9603 13.3333 12.5 13.3333H7.50004C7.0398 13.3333 6.66671 12.9602 6.66671 12.5V7.5ZM8.33337 8.33333V11.6667H11.6667V8.33333H8.33337Z" fill="#3370FF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M10 2.49999C5.85791 2.49999 2.50004 5.85786 2.50004 10C2.50004 14.1421 5.85791 17.5 10 17.5C14.1422 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1422 2.49999 10 2.49999ZM0.833374 10C0.833374 4.93739 4.93743 0.833328 10 0.833328C15.0627 0.833328 19.1667 4.93739 19.1667 10C19.1667 15.0626 15.0627 19.1667 10 19.1667C4.93743 19.1667 0.833374 15.0626 0.833374 10ZM6.66671 7.5C6.66671 7.03976 7.0398 6.66666 7.50004 6.66666H12.5C12.9603 6.66666 13.3334 7.03976 13.3334 7.5V12.5C13.3334 12.9602 12.9603 13.3333 12.5 13.3333H7.50004C7.0398 13.3333 6.66671 12.9602 6.66671 12.5V7.5ZM8.33337 8.33333V11.6667H11.6667V8.33333H8.33337Z"
|
||||
fill="#3370FF" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_74_2">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
<rect width="20" height="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 953 B After Width: | Height: | Size: 944 B |
@@ -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 (
|
||||
<Box position={'relative'} width={'full'} h={`${h}px`} cursor={'text'} overflowY={'visible'}>
|
||||
<Flex
|
||||
position={'relative'}
|
||||
width={'full'}
|
||||
minH={`${h}px`}
|
||||
h={'full'}
|
||||
flexDirection={'column'}
|
||||
cursor={'text'}
|
||||
overflowY={'visible'}
|
||||
>
|
||||
<LexicalComposer initialConfig={initialConfig} key={key}>
|
||||
<PlainTextPlugin
|
||||
contentEditable={<ContentEditable className={styles.contentEditable} />}
|
||||
@@ -125,6 +133,6 @@ export default function Editor({
|
||||
{focus && hasDropDownPlugin && (
|
||||
<DropDownMenu variables={dropdownVariables} setDropdownValue={setDropdownValue} />
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -42,6 +42,7 @@ export default function DropDownMenu({
|
||||
p={2}
|
||||
borderRadius={'md'}
|
||||
position={'absolute'}
|
||||
top={'100%'}
|
||||
w={'auto'}
|
||||
zIndex={99999}
|
||||
maxH={'300px'}
|
||||
|
@@ -99,7 +99,7 @@ export default function VariablePickerPlugin({
|
||||
}}
|
||||
>
|
||||
<MyIcon name={(item.icon as any) || 'core/modules/variable'} w={'14px'} />
|
||||
<Box ml={2} fontSize={'sm'}>
|
||||
<Box ml={2} fontSize={'sm'} whiteSpace={'nowrap'}>
|
||||
{item.key}
|
||||
{item.key !== item.label && `(${item.label})`}
|
||||
</Box>
|
||||
|
@@ -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;
|
41
packages/web/hooks/useRequest.tsx
Normal file
@@ -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<any, any, any, any> {
|
||||
successToast?: string | null;
|
||||
errorToast?: string | null;
|
||||
}
|
||||
|
||||
export const useRequest = ({ successToast, errorToast, onSuccess, onError, ...props }: Props) => {
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
const mutation = useMutation<unknown, unknown, any, unknown>({
|
||||
...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;
|
||||
};
|
29
pnpm-lock.yaml
generated
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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": []
|
||||
}
|
||||
]
|
||||
|
195
projects/app/data/pluginTemplates/getCurrentTime.json
Normal file
@@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@@ -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": []
|
||||
},
|
||||
{
|
||||
|
@@ -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": []
|
||||
},
|
||||
{
|
||||
|
@@ -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",
|
||||
|
7
projects/app/public/imgs/module/getCurrentTime.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg t="1711264069112" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7150"
|
||||
width="128" height="128">
|
||||
<path
|
||||
d="M512 0c282.7776 0 512 229.2224 512 512s-229.2224 512-512 512S0 794.7776 0 512 229.2224 0 512 0z m0 70.656a441.344 441.344 0 1 0 0 882.688 441.344 441.344 0 0 0 0-882.688z"
|
||||
fill="#006EFF" p-id="7151"></path>
|
||||
<path d="M529.664 508.672V215.1936h-70.656v364.1344h332.1344v-70.656z" fill="#FFBC00" p-id="7152"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 503 B |
@@ -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",
|
||||
|
@@ -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": {
|
||||
|
@@ -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';
|
||||
|
||||
|
@@ -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<HTMLCanvasElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -369,7 +369,7 @@ const MessageInput = ({
|
||||
bottom={['10px', '12px']}
|
||||
>
|
||||
{/* voice-input */}
|
||||
{!shareId && !havInput && !isChatting && (
|
||||
{!shareId && !havInput && !isChatting && !!whisperModel && (
|
||||
<>
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
@@ -402,7 +402,7 @@ const MessageInput = ({
|
||||
name={isSpeaking ? 'core/chat/stopSpeechFill' : 'core/chat/recordFill'}
|
||||
width={['20px', '22px']}
|
||||
height={['20px', '22px']}
|
||||
color={'primary.500'}
|
||||
color={isSpeaking ? 'primary.500' : 'myGray.600'}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
@@ -410,7 +410,7 @@ const MessageInput = ({
|
||||
)}
|
||||
{/* send and stop icon */}
|
||||
{isSpeaking ? (
|
||||
<Box color={'#5A646E'} w={'36px'} textAlign={'right'}>
|
||||
<Box color={'#5A646E'} w={'36px'} textAlign={'right'} whiteSpace={'nowrap'}>
|
||||
{speakingTimeString}
|
||||
</Box>
|
||||
) : (
|
||||
|
@@ -32,8 +32,6 @@ const SelectMarkCollection = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const [selectedDatasetId, setSelectedDatasetId] = useState<string>();
|
||||
const [selectedDatasetCollectionIds, setSelectedDatasetCollectionIds] = useState<string[]>([]);
|
||||
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')}
|
||||
>
|
||||
<ModalBody flex={'1 0 0'} overflowY={'auto'}>
|
||||
<Grid
|
||||
display={'grid'}
|
||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
|
||||
gridGap={3}
|
||||
userSelect={'none'}
|
||||
>
|
||||
{datasets.map((item) =>
|
||||
(() => {
|
||||
const selected = selectedDatasetId === item._id;
|
||||
return (
|
||||
<Card
|
||||
key={item._id}
|
||||
@@ -67,16 +66,11 @@ const SelectMarkCollection = ({
|
||||
_hover={{
|
||||
boxShadow: 'md'
|
||||
}}
|
||||
{...(selected
|
||||
? {
|
||||
bg: 'primary.200'
|
||||
}
|
||||
: {})}
|
||||
onClick={() => {
|
||||
if (item.type === DatasetTypeEnum.folder) {
|
||||
setParentId(item._id);
|
||||
} else {
|
||||
setSelectedDatasetId(item._id);
|
||||
setAdminMarkData({ ...adminMarkData, datasetId: item._id });
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -104,29 +98,21 @@ const SelectMarkCollection = ({
|
||||
</Flex>
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
isLoading={isFetching}
|
||||
isDisabled={!selectedDatasetId}
|
||||
onClick={() => {
|
||||
setAdminMarkData({ ...adminMarkData, datasetId: selectedDatasetId });
|
||||
}}
|
||||
>
|
||||
{t('common.Next Step')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</DatasetSelectModal>
|
||||
)}
|
||||
|
||||
{/* select collection */}
|
||||
{adminMarkData.datasetId && !adminMarkData.collectionId && (
|
||||
{adminMarkData.datasetId && (
|
||||
<SelectCollections
|
||||
datasetId={adminMarkData.datasetId}
|
||||
type={'collection'}
|
||||
title={t('dataset.collections.Select One Collection To Store')}
|
||||
onClose={onClose}
|
||||
onChange={({ collectionIds }) => {
|
||||
setSelectedDatasetCollectionIds(collectionIds);
|
||||
setAdminMarkData({
|
||||
...adminMarkData,
|
||||
collectionId: collectionIds[0]
|
||||
});
|
||||
}}
|
||||
CustomFooter={
|
||||
<ModalFooter>
|
||||
@@ -142,17 +128,6 @@ const SelectMarkCollection = ({
|
||||
>
|
||||
{t('common.Last Step')}
|
||||
</Button>
|
||||
<Button
|
||||
isDisabled={selectedDatasetCollectionIds.length === 0}
|
||||
onClick={() => {
|
||||
setAdminMarkData({
|
||||
...adminMarkData,
|
||||
collectionId: selectedDatasetCollectionIds[0]
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('common.Next Step')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
}
|
||||
/>
|
||||
@@ -161,7 +136,12 @@ const SelectMarkCollection = ({
|
||||
{/* input data */}
|
||||
{adminMarkData.datasetId && adminMarkData.collectionId && (
|
||||
<InputDataModal
|
||||
onClose={onClose}
|
||||
onClose={() => {
|
||||
setAdminMarkData({
|
||||
...adminMarkData,
|
||||
collectionId: undefined
|
||||
});
|
||||
}}
|
||||
collectionId={adminMarkData.collectionId}
|
||||
dataId={adminMarkData.dataId}
|
||||
defaultValue={{
|
||||
|
@@ -90,13 +90,14 @@ const ChatItem = ({
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* AI */
|
||||
return (
|
||||
<Flex flexDirection={'column'} gap={2}>
|
||||
{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 (
|
||||
<Box key={tool.id}>
|
||||
<Accordion allowToggle>
|
||||
@@ -169,7 +171,7 @@ ${JSON.stringify(questionGuides)}`;
|
||||
maxH={'500px'}
|
||||
overflowY={'auto'}
|
||||
>
|
||||
{toolParams && (
|
||||
{toolParams && toolParams !== '{}' && (
|
||||
<Markdown
|
||||
source={`~~~json#Input
|
||||
${toolParams}`}
|
||||
|
@@ -652,7 +652,7 @@ const ChatBox = (
|
||||
} catch (error) {}
|
||||
};
|
||||
},
|
||||
[appId, chatId, feedbackType, outLinkUid, shareId]
|
||||
[appId, chatId, feedbackType, outLinkUid, shareId, teamId, teamToken]
|
||||
);
|
||||
const onCloseUserLike = useCallback(
|
||||
(chat: ChatSiteItemType) => {
|
||||
@@ -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')}
|
||||
/>
|
||||
<Box whiteSpace={'pre'}>
|
||||
<Box whiteSpace={'pre-wrap'}>
|
||||
<Box color={'black'}>{item.adminFeedback.q}</Box>
|
||||
<Box color={'myGray.600'}>{item.adminFeedback.a}</Box>
|
||||
</Box>
|
||||
|
@@ -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<string, boolean> = {
|
||||
'/': 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 && <UpdateInviteModal />}
|
||||
{isNotSufficientModal && !isHideNavbar && <NotSufficientModal />}
|
||||
{!!userInfo && <SystemMsgModal />}
|
||||
{!!userInfo && importantInforms.length > 0 && (
|
||||
<ImportantInform informs={importantInforms} refetch={refetchUnRead} />
|
||||
)}
|
||||
</Box>
|
||||
<Loading loading={loading} zIndex={999999} />
|
||||
</>
|
||||
|
@@ -24,7 +24,7 @@ const QuestionGuide = ({ text }: { text: string }) => {
|
||||
|
||||
return questionGuides.length > 0 ? (
|
||||
<Box mt={2}>
|
||||
<ChatBoxDivider icon="core/chat/QGFill" text={t('core.chat.Question Guide Tips')} />
|
||||
<ChatBoxDivider icon="core/chat/QGFill" text={t('core.chat.Question Guide')} />
|
||||
<Flex alignItems={'center'} flexWrap={'wrap'} gap={2}>
|
||||
{questionGuides.map((text) => (
|
||||
<Flex
|
||||
|
@@ -1,7 +1,17 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Menu, MenuList, MenuItem, Box, useOutsideClick, MenuButton } from '@chakra-ui/react';
|
||||
import {
|
||||
Menu,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Box,
|
||||
useOutsideClick,
|
||||
MenuButton,
|
||||
MenuItemProps
|
||||
} from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
type MenuItemType = 'primary' | 'danger';
|
||||
|
||||
interface Props {
|
||||
width?: number | string;
|
||||
offset?: [number, number];
|
||||
@@ -11,6 +21,7 @@ interface Props {
|
||||
isActive?: boolean;
|
||||
label: string | React.ReactNode;
|
||||
icon?: string;
|
||||
type?: MenuItemType;
|
||||
onClick: () => any;
|
||||
}[];
|
||||
}
|
||||
@@ -22,15 +33,25 @@ const MyMenu = ({
|
||||
Button,
|
||||
menuList
|
||||
}: Props) => {
|
||||
const menuItemStyles = {
|
||||
const typeMapStyle: Record<MenuItemType, MenuItemProps> = {
|
||||
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<HTMLDivElement>(null);
|
||||
const closeTimer = useRef<any>();
|
||||
@@ -92,6 +113,7 @@ const MyMenu = ({
|
||||
<MenuItem
|
||||
key={i}
|
||||
{...menuItemStyles}
|
||||
{...typeMapStyle[item.type || 'primary']}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsOpen(false);
|
||||
|
@@ -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);
|
||||
}}
|
||||
>
|
||||
<MyIcon mr={2} name={item.icon as IconNameType} w={'16px'} />
|
||||
<MyIcon mr={2} name={item.icon as IconNameType} w={'20px'} />
|
||||
{item.label}
|
||||
</Flex>
|
||||
))}
|
||||
|
@@ -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 (
|
||||
<Grid gridGap={[3, 5]} fontSize={['sm', 'md']} {...props}>
|
||||
{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 && (
|
||||
<>
|
||||
|
@@ -30,7 +30,8 @@ const RawSourceBox = ({ sourceId, sourceName = '', canView = true, ...props }: P
|
||||
shouldWrapChildren={false}
|
||||
>
|
||||
<Box
|
||||
color={'myGray.600'}
|
||||
color={'myGray.900'}
|
||||
fontWeight={'medium'}
|
||||
display={'inline-flex'}
|
||||
whiteSpace={'nowrap'}
|
||||
{...(canPreview
|
||||
|
@@ -5,6 +5,7 @@ import React, { Dispatch, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import ParentPaths from '@/components/common/ParentPaths';
|
||||
import MyBox from '@/components/common/MyBox';
|
||||
|
||||
type PathItemType = {
|
||||
parentId: string;
|
||||
@@ -17,6 +18,7 @@ const DatasetSelectContainer = ({
|
||||
paths,
|
||||
onClose,
|
||||
tips,
|
||||
isLoading,
|
||||
children
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
@@ -24,6 +26,7 @@ const DatasetSelectContainer = ({
|
||||
paths: PathItemType[];
|
||||
onClose: () => void;
|
||||
tips?: string | null;
|
||||
isLoading?: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -57,7 +60,9 @@ const DatasetSelectContainer = ({
|
||||
maxW={['90vw', '900px']}
|
||||
isCentered
|
||||
>
|
||||
{children}
|
||||
<MyBox isLoading={isLoading} h={'100%'}>
|
||||
{children}
|
||||
</MyBox>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
@@ -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 && (
|
||||
<>
|
||||
<Divider my={4} />
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
userSelect={'none'}
|
||||
py={3}
|
||||
pl={'14px'}
|
||||
pr={'16px'}
|
||||
border={theme.borders.sm}
|
||||
borderWidth={'1.5px'}
|
||||
borderRadius={'md'}
|
||||
position={'relative'}
|
||||
{...(getValues('usingReRank')
|
||||
? {
|
||||
borderColor: 'primary.400'
|
||||
}
|
||||
: {})}
|
||||
onClick={(e) => {
|
||||
if (
|
||||
teamPlanStatus?.standardConstants &&
|
||||
!teamPlanStatus?.standardConstants?.permissionReRank
|
||||
) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('support.team.limit.No permission rerank')
|
||||
});
|
||||
<>
|
||||
<Divider my={4} />
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
userSelect={'none'}
|
||||
py={3}
|
||||
pl={'14px'}
|
||||
pr={'16px'}
|
||||
border={theme.borders.sm}
|
||||
borderWidth={'1.5px'}
|
||||
borderRadius={'md'}
|
||||
position={'relative'}
|
||||
{...(getValues('usingReRank')
|
||||
? {
|
||||
borderColor: 'primary.400'
|
||||
}
|
||||
setValue('usingReRank', !getValues('usingReRank'));
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
>
|
||||
<MyIcon name="core/dataset/rerank" w={'18px'} mr={'14px'} />
|
||||
<Box pr={2} color={'myGray.800'} flex={'1 0 0'}>
|
||||
<Box>{t('core.dataset.search.ReRank')}</Box>
|
||||
<Box fontSize={['xs', 'sm']} color={'myGray.500'}>
|
||||
{t('core.dataset.search.ReRank desc')}
|
||||
</Box>
|
||||
: {})}
|
||||
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);
|
||||
}}
|
||||
>
|
||||
<MyIcon name="core/dataset/rerank" w={'18px'} mr={'14px'} />
|
||||
<Box pr={2} color={'myGray.800'} flex={'1 0 0'}>
|
||||
<Box>{t('core.dataset.search.ReRank')}</Box>
|
||||
<Box fontSize={['xs', 'sm']} color={'myGray.500'}>
|
||||
{t('core.dataset.search.ReRank desc')}
|
||||
</Box>
|
||||
<Box position={'relative'} w={'18px'} h={'18px'}>
|
||||
<Checkbox
|
||||
colorScheme="primary"
|
||||
isChecked={getValues('usingReRank')}
|
||||
size="lg"
|
||||
/>
|
||||
<Box
|
||||
position={'absolute'}
|
||||
top={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
left={0}
|
||||
zIndex={1}
|
||||
></Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<Box position={'relative'} w={'18px'} h={'18px'}>
|
||||
<Checkbox colorScheme="primary" isChecked={getValues('usingReRank')} size="lg" />
|
||||
<Box position={'absolute'} top={0} right={0} bottom={0} left={0} zIndex={1}></Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
</>
|
||||
</>
|
||||
)}
|
||||
{currentTabType === SearchSettingTabEnum.limit && (
|
||||
@@ -243,15 +232,15 @@ const DatasetParamsModal = ({
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
{showSimilarity && (
|
||||
<Box display={['block', 'flex']} mt={10}>
|
||||
<Box flex={'0 0 120px'} mb={[8, 0]}>
|
||||
{t('core.dataset.search.Min Similarity')}
|
||||
<MyTooltip label={t('core.dataset.search.Min Similarity Tips')} forceShow>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Box flex={1} mx={4}>
|
||||
<Box display={['block', 'flex']} mt={10}>
|
||||
<Box flex={'0 0 120px'} mb={[8, 0]}>
|
||||
{t('core.dataset.search.Min Similarity')}
|
||||
<MyTooltip label={t('core.dataset.search.Min Similarity Tips')} forceShow>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Box flex={1} mx={4}>
|
||||
{showSimilarity ? (
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '0', value: 0 },
|
||||
@@ -266,9 +255,11 @@ const DatasetParamsModal = ({
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box color={'myGray.500'}>{t('core.dataset.search.No support similarity')}</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
{currentTabType === SearchSettingTabEnum.queryExtension && (
|
||||
|
@@ -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 (
|
||||
<>
|
||||
<Box
|
||||
zIndex={2}
|
||||
display={isOpen ? 'block' : 'none'}
|
||||
position={'absolute'}
|
||||
top={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
w={`${sliderWidth}px`}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Flex
|
||||
zIndex={3}
|
||||
flexDirection={'column'}
|
||||
position={'absolute'}
|
||||
top={'10px'}
|
||||
left={0}
|
||||
pt={'20px'}
|
||||
pb={4}
|
||||
h={isOpen ? 'calc(100% - 20px)' : '0'}
|
||||
w={isOpen ? ['100%', `${sliderWidth}px`] : '0'}
|
||||
bg={'white'}
|
||||
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
||||
borderRadius={'0 20px 20px 0'}
|
||||
transition={'.2s ease'}
|
||||
userSelect={'none'}
|
||||
overflow={isOpen ? 'none' : 'hidden'}
|
||||
>
|
||||
<Box mb={2} pl={'20px'} pr={'10px'} whiteSpace={'nowrap'} overflow={'hidden'}>
|
||||
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
|
||||
<RowTabs
|
||||
list={[
|
||||
{
|
||||
icon: 'core/modules/basicNode',
|
||||
label: t('core.module.template.Basic Node'),
|
||||
value: TemplateTypeEnum.basic
|
||||
},
|
||||
{
|
||||
icon: 'core/modules/systemPlugin',
|
||||
label: t('core.module.template.System Plugin'),
|
||||
value: TemplateTypeEnum.systemPlugin
|
||||
},
|
||||
{
|
||||
icon: 'core/modules/teamPlugin',
|
||||
label: t('core.module.template.Team Plugin'),
|
||||
value: TemplateTypeEnum.teamPlugin
|
||||
}
|
||||
]}
|
||||
py={'5px'}
|
||||
value={templateType}
|
||||
onChange={onChangeTab}
|
||||
/>
|
||||
{/* close icon */}
|
||||
<IconButton
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'common/backFill'} w={'14px'} color={'myGray.700'} />}
|
||||
w={'26px'}
|
||||
h={'26px'}
|
||||
borderColor={'myGray.300'}
|
||||
variant={'grayBase'}
|
||||
aria-label={''}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</Flex>
|
||||
{templateType === TemplateTypeEnum.teamPlugin && (
|
||||
<Flex mt={2} alignItems={'center'} h={10}>
|
||||
<InputGroup mr={4} h={'full'}>
|
||||
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
|
||||
<MyIcon name={'common/searchLight'} w={'16px'} color={'myGray.500'} ml={3} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
h={'full'}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('plugin.Search plugin')}
|
||||
onChange={debounce((e) => setSearchKey(e.target.value), 200)}
|
||||
/>
|
||||
</InputGroup>
|
||||
<Box flex={1} />
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={() => router.push('/plugin/list')}
|
||||
>
|
||||
<Box>去创建</Box>
|
||||
<MyIcon name={'common/rightArrowLight'} w={'14px'} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
{templateType === TemplateTypeEnum.teamPlugin && !searchKey && currentParent && (
|
||||
<Flex alignItems={'center'} mt={2}>
|
||||
<ParentPaths
|
||||
paths={[currentParent]}
|
||||
FirstPathDom={null}
|
||||
onClick={() => {
|
||||
setCurrentParent(undefined);
|
||||
}}
|
||||
fontSize="md"
|
||||
const Render = useMemo(() => {
|
||||
const parseTemplates = JSON.parse(templates) as FlowNodeTemplateType[];
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
zIndex={2}
|
||||
display={isOpen ? 'block' : 'none'}
|
||||
position={'absolute'}
|
||||
top={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
w={`${sliderWidth}px`}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Flex
|
||||
zIndex={3}
|
||||
flexDirection={'column'}
|
||||
position={'absolute'}
|
||||
top={'10px'}
|
||||
left={0}
|
||||
pt={'20px'}
|
||||
pb={4}
|
||||
h={isOpen ? 'calc(100% - 20px)' : '0'}
|
||||
w={isOpen ? ['100%', `${sliderWidth}px`] : '0'}
|
||||
bg={'white'}
|
||||
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
||||
borderRadius={'0 20px 20px 0'}
|
||||
transition={'.2s ease'}
|
||||
userSelect={'none'}
|
||||
overflow={isOpen ? 'none' : 'hidden'}
|
||||
>
|
||||
<Box mb={2} pl={'20px'} pr={'10px'} whiteSpace={'nowrap'} overflow={'hidden'}>
|
||||
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
|
||||
<RowTabs
|
||||
list={[
|
||||
{
|
||||
icon: 'core/modules/basicNode',
|
||||
label: t('core.module.template.Basic Node'),
|
||||
value: TemplateTypeEnum.basic
|
||||
},
|
||||
{
|
||||
icon: 'core/modules/systemPlugin',
|
||||
label: t('core.module.template.System Plugin'),
|
||||
value: TemplateTypeEnum.systemPlugin
|
||||
},
|
||||
{
|
||||
icon: 'core/modules/teamPlugin',
|
||||
label: t('core.module.template.Team Plugin'),
|
||||
value: TemplateTypeEnum.teamPlugin
|
||||
}
|
||||
]}
|
||||
py={'5px'}
|
||||
value={templateType}
|
||||
onChange={onChangeTab}
|
||||
/>
|
||||
{/* close icon */}
|
||||
<IconButton
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'common/backFill'} w={'14px'} color={'myGray.700'} />}
|
||||
w={'26px'}
|
||||
h={'26px'}
|
||||
borderColor={'myGray.300'}
|
||||
variant={'grayBase'}
|
||||
aria-label={''}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
<RenderList
|
||||
templates={templates}
|
||||
onClose={onClose}
|
||||
currentParent={currentParent}
|
||||
setCurrentParent={setCurrentParent}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
{templateType === TemplateTypeEnum.teamPlugin && (
|
||||
<Flex mt={2} alignItems={'center'} h={10}>
|
||||
<InputGroup mr={4} h={'full'}>
|
||||
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
|
||||
<MyIcon name={'common/searchLight'} w={'16px'} color={'myGray.500'} ml={3} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
h={'full'}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('plugin.Search plugin')}
|
||||
onChange={debounce((e) => setSearchKey(e.target.value), 200)}
|
||||
/>
|
||||
</InputGroup>
|
||||
<Box flex={1} />
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={() => router.push('/plugin/list')}
|
||||
>
|
||||
<Box>去创建</Box>
|
||||
<MyIcon name={'common/rightArrowLight'} w={'14px'} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
{templateType === TemplateTypeEnum.teamPlugin && !searchKey && currentParent && (
|
||||
<Flex alignItems={'center'} mt={2}>
|
||||
<ParentPaths
|
||||
paths={[currentParent]}
|
||||
FirstPathDom={null}
|
||||
onClick={() => {
|
||||
setCurrentParent(undefined);
|
||||
}}
|
||||
fontSize="md"
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
<RenderList
|
||||
templates={parseTemplates}
|
||||
onClose={onClose}
|
||||
currentParent={currentParent}
|
||||
setCurrentParent={setCurrentParent}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
}, [currentParent, isOpen, onChangeTab, onClose, router, searchKey, t, templateType, templates]);
|
||||
|
||||
return Render;
|
||||
};
|
||||
|
||||
export default React.memo(ModuleTemplateList);
|
||||
|
@@ -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'}
|
||||
>
|
||||
<ModalBody
|
||||
minH={'300px'}
|
||||
display={'grid'}
|
||||
gridTemplateColumns={['1fr', 'repeat(3, minmax(0, 1fr))']}
|
||||
gridGap={4}
|
||||
|
@@ -5,12 +5,13 @@ import { Box, Flex, Switch, type SwitchProps } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
// question generator switch
|
||||
const QGSwitch = (props: SwitchProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/questionGuide'} mr={2} w={'20px'} />
|
||||
<Box>{t('core.app.Next Step Guide')}</Box>
|
||||
<MyIcon name={'core/chat/QGFill'} mr={2} w={'20px'} />
|
||||
<Box>{t('core.app.Question Guide')}</Box>
|
||||
<MyTooltip label={t('core.app.Question Guide Tip')} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
|
@@ -124,12 +124,12 @@ const VariableEdit = ({
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th w={'18px !important'} p={0} bg={'myGray.50'} />
|
||||
<Th bg={'myGray.50'}>{t('core.module.variable.variable name')}</Th>
|
||||
<Th bg={'myGray.50'}>{t('core.module.variable.key')}</Th>
|
||||
<Th bg={'myGray.50'}>{t('common.Require Input')}</Th>
|
||||
<Th bg={'myGray.50'}></Th>
|
||||
<Tr bg={'myGray.50'}>
|
||||
<Th w={'18px !important'} p={0} />
|
||||
<Th>{t('core.module.variable.variable name')}</Th>
|
||||
<Th>{t('core.module.variable.key')}</Th>
|
||||
<Th>{t('common.Require Input')}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
|
@@ -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<FlowModuleItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const { moduleId, inputs } = data;
|
||||
|
||||
return (
|
||||
<NodeCard minW={'400px'} selected={selected} {...data}>
|
||||
<Divider text={t('common.Input')} />
|
||||
<Container>
|
||||
<RenderInput
|
||||
moduleId={moduleId}
|
||||
flowInputList={inputs}
|
||||
CustomComponent={{
|
||||
[ModuleInputKeyEnum.agents]: ({ key: agentKey, value = [], ...props }) => {
|
||||
const agents = value as ClassifyQuestionAgentItemType[];
|
||||
return (
|
||||
<Box>
|
||||
{agents.map((item, i) => (
|
||||
<Box key={item.key} mb={4}>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyTooltip label={t('common.Delete')}>
|
||||
<MyIcon
|
||||
mt={1}
|
||||
mr={2}
|
||||
name={'minus'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.600'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={() => {
|
||||
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
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box flex={1}>分类{i + 1}</Box>
|
||||
</Flex>
|
||||
<Box position={'relative'}>
|
||||
<Textarea
|
||||
rows={2}
|
||||
mt={1}
|
||||
defaultValue={item.value}
|
||||
onChange={(e) => {
|
||||
const newVal = agents.map((val) =>
|
||||
val.key === item.key
|
||||
? {
|
||||
...val,
|
||||
value: e.target.value
|
||||
}
|
||||
: val
|
||||
);
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: agentKey,
|
||||
value: {
|
||||
...props,
|
||||
key: agentKey,
|
||||
value: newVal
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<SourceHandle
|
||||
handleKey={item.key}
|
||||
valueType={ModuleIOValueTypeEnum.boolean}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
<Button
|
||||
onClick={() => {
|
||||
const key = nanoid();
|
||||
|
||||
const CustomComponent = useMemo(
|
||||
() => ({
|
||||
[ModuleInputKeyEnum.agents]: ({
|
||||
key: agentKey,
|
||||
value = [],
|
||||
...props
|
||||
}: FlowNodeInputItemType) => {
|
||||
const agents = value as ClassifyQuestionAgentItemType[];
|
||||
return (
|
||||
<Box>
|
||||
{agents.map((item, i) => (
|
||||
<Box key={item.key} mb={4}>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyTooltip label={t('common.Delete')}>
|
||||
<MyIcon
|
||||
mt={1}
|
||||
mr={2}
|
||||
name={'minus'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.600'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={() => {
|
||||
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
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box flex={1}>分类{i + 1}</Box>
|
||||
</Flex>
|
||||
<Box position={'relative'}>
|
||||
<Textarea
|
||||
rows={2}
|
||||
mt={1}
|
||||
defaultValue={item.value}
|
||||
onChange={(e) => {
|
||||
const newVal = agents.map((val) =>
|
||||
val.key === item.key
|
||||
? {
|
||||
...val,
|
||||
value: e.target.value
|
||||
}
|
||||
: val
|
||||
);
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
@@ -110,29 +86,56 @@ const NodeCQNode = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
|
||||
value: {
|
||||
...props,
|
||||
key: agentKey,
|
||||
value: agents.concat({ value: '', key })
|
||||
}
|
||||
});
|
||||
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'addOutput',
|
||||
value: {
|
||||
key,
|
||||
label: '',
|
||||
type: FlowNodeOutputTypeEnum.hidden,
|
||||
targets: []
|
||||
value: newVal
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('core.module.Add question type')}
|
||||
</Button>
|
||||
/>
|
||||
<SourceHandle handleKey={item.key} valueType={ModuleIOValueTypeEnum.boolean} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
<Button
|
||||
onClick={() => {
|
||||
const key = nanoid();
|
||||
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: agentKey,
|
||||
value: {
|
||||
...props,
|
||||
key: agentKey,
|
||||
value: agents.concat({ value: '', key })
|
||||
}
|
||||
});
|
||||
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'addOutput',
|
||||
value: {
|
||||
key,
|
||||
label: '',
|
||||
type: FlowNodeOutputTypeEnum.hidden,
|
||||
targets: []
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('core.module.Add question type')}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}),
|
||||
[moduleId, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeCard minW={'400px'} selected={selected} {...data}>
|
||||
<Divider text={t('common.Input')} />
|
||||
<Container>
|
||||
<RenderInput moduleId={moduleId} flowInputList={inputs} CustomComponent={CustomComponent} />
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
|
@@ -20,6 +20,7 @@ import SourceHandle from '../render/SourceHandle';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MySlider from '@/components/Slider';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type';
|
||||
|
||||
const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -27,7 +28,10 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowModuleItemType>) =>
|
||||
const { nodes } = useFlowProviderStore();
|
||||
const { moduleId, inputs, outputs } = data;
|
||||
|
||||
const quotes = inputs.filter((item) => item.valueType === ModuleIOValueTypeEnum.datasetQuote);
|
||||
const quotes = useMemo(
|
||||
() => inputs.filter((item) => item.valueType === ModuleIOValueTypeEnum.datasetQuote),
|
||||
[inputs]
|
||||
);
|
||||
|
||||
const tokenLimit = useMemo(() => {
|
||||
let maxTokens = 3000;
|
||||
@@ -46,8 +50,8 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowModuleItemType>) =>
|
||||
return maxTokens;
|
||||
}, [llmModelList, nodes]);
|
||||
|
||||
const RenderQuoteList = useMemo(
|
||||
() => (
|
||||
const RenderQuoteList = useMemo(() => {
|
||||
return (
|
||||
<Box>
|
||||
<Box>
|
||||
{quotes.map((quote, i) => (
|
||||
@@ -88,45 +92,45 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowModuleItemType>) =>
|
||||
{t('core.module.Dataset quote.Add quote')}
|
||||
</Button>
|
||||
</Box>
|
||||
),
|
||||
[moduleId, quotes, t]
|
||||
);
|
||||
);
|
||||
}, [moduleId, quotes, t]);
|
||||
|
||||
const CustomComponent = useMemo(() => {
|
||||
console.log(111);
|
||||
return {
|
||||
[ModuleInputKeyEnum.datasetMaxTokens]: (item: FlowNodeInputItemType) => (
|
||||
<Box px={2}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: tokenLimit, value: tokenLimit }
|
||||
]}
|
||||
width={'100%'}
|
||||
min={100}
|
||||
max={tokenLimit}
|
||||
step={50}
|
||||
value={item.value}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
};
|
||||
}, [moduleId, tokenLimit]);
|
||||
|
||||
return (
|
||||
<NodeCard minW={'400px'} selected={selected} {...data}>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'} position={'relative'}>
|
||||
<RenderInput
|
||||
moduleId={moduleId}
|
||||
flowInputList={inputs}
|
||||
CustomComponent={{
|
||||
[ModuleInputKeyEnum.datasetMaxTokens]: (item) => (
|
||||
<Box px={2}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: tokenLimit, value: tokenLimit }
|
||||
]}
|
||||
width={'100%'}
|
||||
min={100}
|
||||
max={tokenLimit}
|
||||
step={50}
|
||||
value={item.value}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<RenderInput moduleId={moduleId} flowInputList={inputs} CustomComponent={CustomComponent} />
|
||||
{/* render dataset select */}
|
||||
{RenderQuoteList}
|
||||
<Flex position={'absolute'} right={4} top={'60%'}>
|
||||
|