diff --git a/.github/workflows/preview-image.yml b/.github/workflows/preview-image.yml index 90eca3741..820cca773 100644 --- a/.github/workflows/preview-image.yml +++ b/.github/workflows/preview-image.yml @@ -4,8 +4,6 @@ on: paths: - 'projects/app/**' - 'packages/**' - branches: - - 'main' workflow_dispatch: jobs: diff --git a/.npmrc b/.npmrc index b82e07751..6199b3669 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ public-hoist-pattern[]=*tiktoken* +public-hoist-pattern[]=*@zilliz/milvus2-sdk-node* diff --git a/README.md b/README.md index ba1b9546f..778e69aad 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,10 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b `1` 应用编排能力 - [x] 提供简易模式,无需操作编排 - [x] 工作流编排 - - [x] 源文件引用追踪 - [x] 工具调用 - [x] 插件 - 工作流封装能力 - - [ ] Code sandbox + - [x] Code sandbox + - [ ] 循环调用 `2` 知识库能力 - [x] 多库复用,混用 @@ -65,7 +65,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b - [x] 支持 txt,md,html,pdf,docx,pptx,csv,xlsx (有需要更多可 PR file loader) - [x] 支持 url 读取、CSV 批量导入 - [x] 混合检索 & 重排 - - [ ] 支持文件阅读器 + - [ ] 标签过滤 `3` 应用调试能力 - [x] 知识库单点搜索测试 diff --git a/docSite/assets/imgs/zilliz_key.png b/docSite/assets/imgs/zilliz_key.png new file mode 100644 index 000000000..b127ebc1f Binary files /dev/null and b/docSite/assets/imgs/zilliz_key.png differ diff --git a/docSite/content/zh-cn/docs/development/docker.md b/docSite/content/zh-cn/docs/development/docker.md index dfcae74ec..b9e0e6276 100644 --- a/docSite/content/zh-cn/docs/development/docker.md +++ b/docSite/content/zh-cn/docs/development/docker.md @@ -7,34 +7,60 @@ toc: true weight: 707 --- +## 部署架构图 + +![](/imgs/sealos-fastgpt.webp) + + +{{% alert icon="🤖" context="success" %}} + +- MongoDB:用于存储除了向量外的各类数据 +- PostgreSQL/Milvus:存储向量数据 +- OneAPI: 聚合各类 AI API,支持多模型调用 (任何模型问题,先自行通过 OneAPI 测试校验) + +{{% /alert %}} + ## 推荐配置 +### PgVector版本 + +体验测试首选 + {{< table "table-hover table-striped-columns" >}} | 环境 | 最低配置(单节点) | 推荐配置 | | ---- | ---- | ---- | | 测试 | 2c2g | 2c4g | | 100w 组向量 | 4c8g 50GB | 4c16g 50GB | -| 500w 组向量 | 8c32g | 16c64g 200GB | +| 500w 组向量 | 8c32g 200GB | 16c64g 200GB | {{< /table >}} -## 部署架构图 +### Milvus版本 -![](/imgs/sealos-fastgpt.webp) +对于千万级以上向量性能更优秀。 +[点击查看 Milvus 官方推荐配置](https://milvus.io/docs/prerequisite-docker.md) -### 1. 准备好代理环境(国外服务器可忽略) +{{< table "table-hover table-striped-columns" >}} +| 环境 | 最低配置(单节点) | 推荐配置 | +| ---- | ---- | ---- | +| 测试 | 2c8g | 4c16g | +| 100w 组向量 | 未测试 | | +| 500w 组向量 | | | +{{< /table >}} -确保可以访问 OpenAI,具体方案可以参考:[代理方案](/docs/development/proxy/)。或直接在 Sealos 上 [部署 OneAPI](/docs/development/one-api),既解决代理问题也能实现多 Key 轮询、接入其他大模型。 +### zilliz cloud版本 -### 2. 多模型支持 +亿级以上向量首选。 -FastGPT 使用了 one-api 项目来管理模型池,其可以兼容 OpenAI 、Azure 、国内主流模型和本地模型等。 +由于向量库使用了 Cloud,无需占用本地资源,无需太关注。 -可选择 [Sealos 快速部署 OneAPI](/docs/development/one-api),更多部署方法可参考该项目的 [README](https://github.com/songquanpeng/one-api),也可以直接通过以下按钮一键部署: +## 前置工作 -Deploy on Sealos +### 1. 确保网络环境 -## 一、安装 Docker 和 docker-compose +如果使用`OpenAI`等国外模型接口,请确保可以正常访问,否则会报错:`Connection error` 等。 方案可以参考:[代理方案](/docs/development/proxy/) + +### 2. 准备 Docker 环境 {{< tabs tabTotal="3" >}} {{< tab tabName="Linux" >}} @@ -79,22 +105,75 @@ brew install orbstack {{< /tab >}} {{< /tabs >}} -## 二、创建目录并下载 docker-compose.yml -依次执行下面命令,创建 FastGPT 文件并拉取`docker-compose.yml`和`config.json`,执行完后目录下会有 2 个文件。 +## 开始部署 -非 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) +### 1. 下载 docker-compose.yml -**注意: `docker-compose.yml` 配置文件中 Mongo 为 5.x,部分服务器不支持,需手动更改其镜像版本为 4.4.24**(需要自己在docker hub下载,阿里云镜像没做备份) + +非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml` + +- [config.json](https://github.com/labring/FastGPT/blob/main/projects/app/data/config.json) +- [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker) (注意,不同向量库版本的文件不一样) + +{{% alert icon="🤖" context="success" %}} + +所有 `docker-compose.yml` 配置文件中 `MongoDB` 为 5.x,需要用到AUX指令集,部分 CPU 不支持,需手动更改其镜像版本为 4.4.24**(需要自己在docker hub下载,阿里云镜像没做备份) + +{{% /alert %}} + +**Linux 快速脚本** ```bash mkdir fastgpt cd fastgpt -curl -O https://raw.githubusercontent.com/labring/FastGPT/main/files/deploy/fastgpt/docker-compose.yml curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data/config.json + +# pgvector 版本(测试推荐,简单快捷) +curl -o docker-compose.yml https://github.com/labring/FastGPT/blob/main/files/docker/docker-compose-pgvector.yml +# milvus 版本 +# curl -o docker-compose.yml https://github.com/labring/FastGPT/blob/main/files/docker/docker-compose-milvus.yml +# zilliz 版本 +# curl -o docker-compose.yml https://github.com/labring/FastGPT/blob/main/files/docker/docker-compose-zilliz.yml ``` -## 三、启动容器 +### 2. 修改 docker-compose.yml 环境变量 + +{{< tabs tabTotal="3" >}} +{{< tab tabName="PgVector版本" >}} +{{< markdownify >}} + +``` +无需操作 +``` + +{{< /markdownify >}} +{{< /tab >}} +{{< tab tabName="Milvus版本" >}} +{{< markdownify >}} + +``` +无需操作 +``` + +{{< /markdownify >}} +{{< /tab >}} +{{< tab tabName="Zilliz版本" >}} +{{< markdownify >}} + +![zilliz_key](/imgs/zilliz_key.png) + +{{% alert icon="🤖" context="success" %}} + +修改`MILVUS_ADDRESS`和`MILVUS_TOKEN`链接参数,分别对应 `zilliz` 的 `Public Endpoint` 和 `Api key`,记得把自己ip加入白名单。 + +{{% /alert %}} + +{{< /markdownify >}} +{{< /tab >}} +{{< /tabs >}} + +### 3. 启动容器 在 docker-compose.yml 同级目录下执行。请确保`docker-compose`版本最好在2.17以上,否则可能无法执行自动化命令。 @@ -107,13 +186,13 @@ sleep 10 docker restart oneapi ``` -## 四、打开 OneAPI 添加模型 +### 4. 打开 OneAPI 添加模型 可以通过`ip:3001`访问OneAPI,默认账号为`root`密码为`123456`。 在OneApi中添加合适的AI模型渠道。[点击查看相关教程](/docs/development/one-api/) -## 五、访问 FastGPT +### 5. 访问 FastGPT 目前可以通过 `ip:3000` 直接访问(注意防火墙)。登录用户名为 `root`,密码为`docker-compose.yml`环境变量里设置的 `DEFAULT_ROOT_PSW`。 @@ -125,7 +204,9 @@ docker restart oneapi ### Mongo 副本集自动初始化失败 -最新的 docker-compose 示例优化 Mongo 副本集初始化,实现了全自动。目前在 unbuntu20,22 centos7, wsl2, mac, window 均通过测试。如果你的环境特殊,可以手动初始化副本集: +最新的 docker-compose 示例优化 Mongo 副本集初始化,实现了全自动。目前在 unbuntu20,22 centos7, wsl2, mac, window 均通过测试。仍无法正常启动,大部分是因为 cpu 不支持 AUX 指令集,可以切换 Mongo4.x 版本。 + +如果是由于,无法自动初始化副本集合,可以手动初始化副本集: 1. 终端中执行下面命令,创建mongo密钥: @@ -266,13 +347,14 @@ PG 数据库没有连接上/初始化失败,可以查看日志。FastGPT 会 ### Operation `auth_codes.findOne()` buffering timed out after 10000ms -mongo连接失败,查看mongo的运行状态对应日志。 +mongo连接失败,查看mongo的运行状态**对应日志**。 可能原因: 1. mongo 服务有没有起来(有些 cpu 不支持 AVX,无法用 mongo5,需要换成 mongo4.x,可以docker hub找个最新的4.x,修改镜像版本,重新运行) 2. 连接数据库的环境变量填写错误(账号密码,注意host和port,非容器网络连接,需要用公网ip并加上 directConnection=true) 3. 副本集启动失败。导致容器一直重启。 +4. `Illegal instruction.... Waiting for MongoDB to start`: cpu 不支持 AVX,无法用 mongo5,需要换成 mongo4.x ### 首次部署,root用户提示未注册 diff --git a/docSite/content/zh-cn/docs/development/upgrading/482.md b/docSite/content/zh-cn/docs/development/upgrading/482.md index d2180d27d..8ad111499 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/482.md +++ b/docSite/content/zh-cn/docs/development/upgrading/482.md @@ -20,10 +20,10 @@ SANDBOX_URL=内网地址 ## Docker 部署 -可以拉取最新 [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/deploy/fastgpt/docker-compose.yml) 文件参考 +可以拉取最新 [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker/docker-compose.yml) 文件参考 1. 新增一个容器 `sandbox` -2. fastgpt容器新增环境变量: `SANDBOX_URL` +2. fastgpt 和 fastgpt-pro(商业版) 容器新增环境变量: `SANDBOX_URL` 3. sandbox 简易不要开启外网访问,未做凭证校验。 ## V4.8.2 更新说明 diff --git a/docSite/content/zh-cn/docs/development/upgrading/483.md b/docSite/content/zh-cn/docs/development/upgrading/483.md new file mode 100644 index 000000000..f448f2383 --- /dev/null +++ b/docSite/content/zh-cn/docs/development/upgrading/483.md @@ -0,0 +1,22 @@ +--- +title: 'V4.8.3(进行中)' +description: 'FastGPT V4.8.3 更新说明' +icon: 'upgrade' +draft: false +toc: true +weight: 821 +--- + +## 升级指南 + +- fastgpt 镜像 tag 修改成 v4.8.3 +- fastgpt-sandbox 镜像 tag 修改成 v4.8.3 +- 商业版镜像 tag 修改成 v4.8.3 + +## V4.8.3 更新说明 + +1. 新增 - 支持 Milvus 数据库, 可参考最新的 [docker-compose-milvus.yml](https://github.com/labring/FastGPT/blob/main/files/docker/docker-compose-milvus.yml). +2. 新增 - 给 chat 接口 empty answer 增加 log,便于排查模型问题。 +3. 新增 - ifelse判断器,字符串支持正则。 +4. 新增 - 代码支持 +5. 修复 - 变量更新在 Debug 模式下出错。 \ No newline at end of file diff --git a/files/docker/docker-compose-milvus.yml b/files/docker/docker-compose-milvus.yml new file mode 100644 index 000000000..2a4526aa8 --- /dev/null +++ b/files/docker/docker-compose-milvus.yml @@ -0,0 +1,202 @@ +# 数据库的默认账号和密码仅首次运行时设置有效 +# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~ +# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。 +# 如何无法访问 dockerhub 和 git,可以用阿里云(阿里云没有arm包) + +version: '3.3' +services: + minio: + container_name: minio + image: minio/minio:RELEASE.2023-03-20T20-16-18Z + environment: + MINIO_ACCESS_KEY: minioadmin + MINIO_SECRET_KEY: minioadmin + ports: + - '9001:9001' + - '9000:9000' + networks: + - fastgpt + volumes: + - ./minio:/minio_data + command: minio server /minio_data --console-address ":9001" + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live'] + interval: 30s + timeout: 20s + retries: 3 + # milvus + milvusEtcd: + container_name: milvusEtcd + image: quay.io/coreos/etcd:v3.5.5 + environment: + - ETCD_AUTO_COMPACTION_MODE=revision + - ETCD_AUTO_COMPACTION_RETENTION=1000 + - ETCD_QUOTA_BACKEND_BYTES=4294967296 + - ETCD_SNAPSHOT_COUNT=50000 + networks: + - fastgpt + volumes: + - ./milvus/etcd:/etcd + command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd + healthcheck: + test: ['CMD', 'etcdctl', 'endpoint', 'health'] + interval: 30s + timeout: 20s + retries: 3 + milvusStandalone: + container_name: milvusStandalone + image: milvusdb/milvus:v2.4.3 + command: ['milvus', 'run', 'standalone'] + security_opt: + - seccomp:unconfined + environment: + ETCD_ENDPOINTS: milvusEtcd:2379 + MINIO_ADDRESS: minio:9000 + networks: + - fastgpt + volumes: + - ./milvus/data:/var/lib/milvus + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:9091/healthz'] + interval: 30s + start_period: 90s + timeout: 20s + retries: 3 + depends_on: + - 'milvusEtcd' + - 'minio' + + mongo: + image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云 + container_name: mongo + restart: always + ports: + - 27017:27017 + networks: + - fastgpt + command: mongod --keyFile /data/mongodb.key --replSet rs0 + environment: + - MONGO_INITDB_ROOT_USERNAME=myusername + - MONGO_INITDB_ROOT_PASSWORD=mypassword + volumes: + - ./mongo/data:/data/db + 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 $$! + + # fastgpt + sandbox: + container_name: sandbox + image: ghcr.io/labring/fastgpt-sandbox:v4.8.2 # git + # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.2 # 阿里云 + networks: + - fastgpt + restart: always + fastgpt: + container_name: fastgpt + image: ghcr.io/labring/fastgpt:v4.8.3-alpha # git + # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3-alpha # 阿里云 + ports: + - 3000:3000 + networks: + - fastgpt + depends_on: + - mongo + - milvusStandalone + - sandbox + restart: always + environment: + # root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。 + - DEFAULT_ROOT_PSW=1234 + # 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 + # MongoDB 连接参数. 用户名myusername,密码mypassword。 + - MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin + # zilliz 连接参数 + - MILVUS_ADDRESS=http://milvusStandalone:19530 + - MILVUS_TOKEN=none + # sandbox 地址 + - SANDBOX_URL=http://sandbox:3000 + volumes: + - ./config.json:/app/data/config.json + + # oneapi + mysql: + image: mysql:8.0.36 + container_name: mysql + restart: always + ports: + - 3306:3306 + networks: + - fastgpt + command: --default-authentication-plugin=mysql_native_password + environment: + # 默认root密码,仅首次运行有效 + MYSQL_ROOT_PASSWORD: oneapimmysql + MYSQL_DATABASE: oneapi + volumes: + - ./mysql:/var/lib/mysql + oneapi: + container_name: oneapi + image: ghcr.io/songquanpeng/one-api:latest + ports: + - 3001:3000 + depends_on: + - mysql + networks: + - fastgpt + restart: always + environment: + # mysql 连接参数 + - SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi + # 登录凭证加密密钥 + - SESSION_SECRET=oneapikey + # 内存缓存 + - MEMORY_CACHE_ENABLED=true + # 启动聚合更新,减少数据交互频率 + - BATCH_UPDATE_ENABLED=true + # 聚合更新时长 + - BATCH_UPDATE_INTERVAL=10 + # 初始化的 root 密钥(建议部署完后更改,否则容易泄露) + - INITIAL_ROOT_TOKEN=fastgpt + volumes: + - ./oneapi:/data +networks: + fastgpt: diff --git a/files/deploy/fastgpt/docker-compose.yml b/files/docker/docker-compose-pgvector.yml similarity index 99% rename from files/deploy/fastgpt/docker-compose.yml rename to files/docker/docker-compose-pgvector.yml index 1e80de5ee..d7e965946 100644 --- a/files/deploy/fastgpt/docker-compose.yml +++ b/files/docker/docker-compose-pgvector.yml @@ -5,6 +5,7 @@ version: '3.3' services: + # db pg: image: pgvector/pgvector:0.7.0-pg15 # docker hub # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.7.0 # 阿里云 @@ -67,6 +68,8 @@ services: # 等待docker-entrypoint.sh脚本执行的MongoDB服务进程 wait $$! + + # fastgpt sandbox: container_name: sandbox image: ghcr.io/labring/fastgpt-sandbox:v4.8.2 # git @@ -110,7 +113,8 @@ services: - SANDBOX_URL=http://sandbox:3000 volumes: - ./config.json:/app/data/config.json - - ./fastgpt/tmp:/app/tmp + + # oneapi mysql: image: mysql:8.0.36 container_name: mysql diff --git a/files/docker/docker-compose-zilliz.yml b/files/docker/docker-compose-zilliz.yml new file mode 100644 index 000000000..c9272cfe5 --- /dev/null +++ b/files/docker/docker-compose-zilliz.yml @@ -0,0 +1,140 @@ +# 数据库的默认账号和密码仅首次运行时设置有效 +# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~ +# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。 +# 如何无法访问 dockerhub 和 git,可以用阿里云(阿里云没有arm包) + +version: '3.3' +services: + mongo: + image: mongo:5.0.18 # dockerhub + # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云 + # image: mongo:4.4.29 # cpu不支持AVX时候使用 + container_name: mongo + restart: always + ports: + - 27017:27017 + networks: + - fastgpt + command: mongod --keyFile /data/mongodb.key --replSet rs0 + environment: + - MONGO_INITDB_ROOT_USERNAME=myusername + - MONGO_INITDB_ROOT_PASSWORD=mypassword + volumes: + - ./mongo/data:/data/db + 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 $$! + sandbox: + container_name: sandbox + image: ghcr.io/labring/fastgpt-sandbox:v4.8.2 # git + # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.2 # 阿里云 + networks: + - fastgpt + restart: always + fastgpt: + container_name: fastgpt + image: ghcr.io/labring/fastgpt:v4.8.2 # git + # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.2 # 阿里云 + ports: + - 3000:3000 + networks: + - fastgpt + depends_on: + - mongo + - sandbox + restart: always + environment: + # root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。 + - DEFAULT_ROOT_PSW=1234 + # 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 + # MongoDB 连接参数. 用户名myusername,密码mypassword。 + - MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin + # zilliz 连接参数 + - MILVUS_ADDRESS=zilliz_cloud_address + - MILVUS_TOKEN=zilliz_cloud_token + # sandbox 地址 + - SANDBOX_URL=http://sandbox:3000 + volumes: + - ./config.json:/app/data/config.json + + # oneapi + mysql: + image: mysql:8.0.36 + container_name: mysql + restart: always + ports: + - 3306:3306 + networks: + - fastgpt + command: --default-authentication-plugin=mysql_native_password + environment: + # 默认root密码,仅首次运行有效 + MYSQL_ROOT_PASSWORD: oneapimmysql + MYSQL_DATABASE: oneapi + volumes: + - ./mysql:/var/lib/mysql + oneapi: + container_name: oneapi + image: ghcr.io/songquanpeng/one-api:latest + ports: + - 3001:3000 + depends_on: + - mysql + networks: + - fastgpt + restart: always + environment: + # mysql 连接参数 + - SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi + # 登录凭证加密密钥 + - SESSION_SECRET=oneapikey + # 内存缓存 + - MEMORY_CACHE_ENABLED=true + # 启动聚合更新,减少数据交互频率 + - BATCH_UPDATE_ENABLED=true + # 聚合更新时长 + - BATCH_UPDATE_INTERVAL=10 + # 初始化的 root 密钥(建议部署完后更改,否则容易泄露) + - INITIAL_ROOT_TOKEN=fastgpt + volumes: + - ./oneapi:/data +networks: + fastgpt: diff --git a/files/deploy/fastgpt/docker-compose/docker-compose b/files/docker/docker-compose/docker-compose similarity index 100% rename from files/deploy/fastgpt/docker-compose/docker-compose rename to files/docker/docker-compose/docker-compose diff --git a/files/deploy/fastgpt/docker-compose/init.sh b/files/docker/docker-compose/init.sh similarity index 100% rename from files/deploy/fastgpt/docker-compose/init.sh rename to files/docker/docker-compose/init.sh diff --git a/files/deploy/fastgpt/run.sh b/files/docker/run.sh similarity index 100% rename from files/deploy/fastgpt/run.sh rename to files/docker/run.sh diff --git a/package.json b/package.json index 4bce5cf08..80c0b7338 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,15 @@ "react-i18next": "13.5.0", "zhlint": "^0.7.1" }, + "resolutions": { + "react": "18.3.1", + "react-dom": "18.3.1", + "@types/react": "18.3.0", + "@types/react-dom": "18.3.0" + }, "lint-staged": { "./**/**/*.{ts,tsx,scss}": "npm run format-code", - "./**/**/*.md": "npm run format-doc" + "./docSite/**/**/*.md": "npm run format-doc" }, "engines": { "node": ">=18.0.0", diff --git a/packages/global/common/error/code/plugin.ts b/packages/global/common/error/code/plugin.ts index 430f0d2a7..f2027b6aa 100644 --- a/packages/global/common/error/code/plugin.ts +++ b/packages/global/common/error/code/plugin.ts @@ -1,6 +1,6 @@ import { ErrType } from '../errorCode'; -/* dataset: 507000 */ +/* dataset: 508000 */ export enum PluginErrEnum { unExist = 'pluginUnExist', unAuth = 'pluginUnAuth' @@ -19,7 +19,7 @@ export default errList.reduce((acc, cur, index) => { return { ...acc, [cur.statusText]: { - code: 507000 + index, + code: 508000 + index, statusText: cur.statusText, message: cur.message, data: null diff --git a/packages/global/common/error/code/system.ts b/packages/global/common/error/code/system.ts new file mode 100644 index 000000000..700670382 --- /dev/null +++ b/packages/global/common/error/code/system.ts @@ -0,0 +1,23 @@ +import { ErrType } from '../errorCode'; + +/* dataset: 509000 */ +export enum SystemErrEnum { + communityVersionNumLimit = 'communityVersionNumLimit' +} +const systemErr = [ + { + statusText: SystemErrEnum.communityVersionNumLimit, + message: '超出开源版数量限制,请升级商业版: https://fastgpt.in' + } +]; +export default systemErr.reduce((acc, cur, index) => { + return { + ...acc, + [cur.statusText]: { + code: 509000 + index, + statusText: cur.statusText, + message: cur.message, + data: null + } + }; +}, {} as ErrType<`${SystemErrEnum}`>); diff --git a/packages/global/common/error/errorCode.ts b/packages/global/common/error/errorCode.ts index 4fbc28ae5..bb6785119 100644 --- a/packages/global/common/error/errorCode.ts +++ b/packages/global/common/error/errorCode.ts @@ -7,6 +7,7 @@ import outLinkErr from './code/outLink'; import teamErr from './code/team'; import userErr from './code/user'; import commonErr from './code/common'; +import SystemErrEnum from './code/system'; export const ERROR_CODE: { [key: number]: string } = { 400: '请求失败', @@ -98,5 +99,6 @@ export const ERROR_RESPONSE: Record< ...teamErr, ...userErr, ...pluginErr, - ...commonErr + ...commonErr, + ...SystemErrEnum }; diff --git a/packages/global/common/error/utils.ts b/packages/global/common/error/utils.ts index e306e54c6..73f0ec646 100644 --- a/packages/global/common/error/utils.ts +++ b/packages/global/common/error/utils.ts @@ -1,7 +1,10 @@ import { replaceSensitiveText } from '../string/tools'; export const getErrText = (err: any, def = '') => { - const msg: string = typeof err === 'string' ? err : err?.message ?? def; + const msg: string = + typeof err === 'string' + ? err + : err?.response?.data?.message || err?.response?.message || err?.message || def; msg && console.log('error =>', msg); return replaceSensitiveText(msg); }; diff --git a/packages/global/common/vectorStore/constants.ts b/packages/global/common/vectorStore/constants.ts deleted file mode 100644 index 92db0e358..000000000 --- a/packages/global/common/vectorStore/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const PgDatasetTableName = 'modeldata'; diff --git a/packages/global/core/workflow/runtime/type.d.ts b/packages/global/core/workflow/runtime/type.d.ts index f2dc6861a..cd7b41f2d 100644 --- a/packages/global/core/workflow/runtime/type.d.ts +++ b/packages/global/core/workflow/runtime/type.d.ts @@ -84,6 +84,9 @@ export type DispatchNodeResponseType = { toolCallTokens?: number; toolDetail?: ChatHistoryItemResType[]; toolStop?: boolean; + + // code + codeLog?: string; }; export type DispatchNodeResultType = { diff --git a/packages/global/core/workflow/template/system/ifElse/constant.ts b/packages/global/core/workflow/template/system/ifElse/constant.ts index 49c000ef0..29e07679a 100644 --- a/packages/global/core/workflow/template/system/ifElse/constant.ts +++ b/packages/global/core/workflow/template/system/ifElse/constant.ts @@ -8,6 +8,8 @@ export enum VariableConditionEnum { startWith = 'startWith', endWith = 'endWith', + reg = 'reg', + greaterThan = 'greaterThan', greaterThanOrEqualTo = 'greaterThanOrEqualTo', lessThan = 'lessThan', @@ -31,6 +33,7 @@ export const stringConditionList = [ { label: '不为空', value: VariableConditionEnum.isNotEmpty }, { label: '等于', value: VariableConditionEnum.equalTo }, { label: '不等于', value: VariableConditionEnum.notEqual }, + { label: '正则', value: VariableConditionEnum.reg }, { label: '包含', value: VariableConditionEnum.include }, { label: '不包含', value: VariableConditionEnum.notInclude }, { label: '开始为', value: VariableConditionEnum.startWith }, diff --git a/packages/global/core/workflow/template/system/sandbox/index.ts b/packages/global/core/workflow/template/system/sandbox/index.ts index 2624d1c87..7f0bd127f 100644 --- a/packages/global/core/workflow/template/system/sandbox/index.ts +++ b/packages/global/core/workflow/template/system/sandbox/index.ts @@ -67,6 +67,20 @@ export const CodeNode: FlowNodeTemplateType = { description: '代码运行错误信息,成功时返回空', valueType: WorkflowIOValueTypeEnum.object, type: FlowNodeOutputTypeEnum.static + }, + { + id: 'qLUQfhG0ILRX', + type: FlowNodeOutputTypeEnum.dynamic, + key: 'result', + valueType: WorkflowIOValueTypeEnum.string, + label: 'result' + }, + { + id: 'gR0mkQpJ4Og8', + type: FlowNodeOutputTypeEnum.dynamic, + key: 'data2', + valueType: WorkflowIOValueTypeEnum.string, + label: 'data2' } ] }; diff --git a/packages/global/core/workflow/type/io.d.ts b/packages/global/core/workflow/type/io.d.ts index 2c1023d12..6df9267db 100644 --- a/packages/global/core/workflow/type/io.d.ts +++ b/packages/global/core/workflow/type/io.d.ts @@ -43,7 +43,7 @@ export type FlowNodeInputItemType = { export type FlowNodeOutputItemType = { id: string; // output unique id(Does not follow the key change) - type: `${FlowNodeOutputTypeEnum}`; + type: FlowNodeOutputTypeEnum; key: `${NodeOutputKeyEnum}` | string; valueType?: WorkflowIOValueTypeEnum; value?: any; diff --git a/packages/service/common/mongo/sessionRun.ts b/packages/service/common/mongo/sessionRun.ts index 6bfa29992..7c8d73de1 100644 --- a/packages/service/common/mongo/sessionRun.ts +++ b/packages/service/common/mongo/sessionRun.ts @@ -2,18 +2,18 @@ import { connectionMongo, ClientSession } from './index'; export const mongoSessionRun = async (fn: (session: ClientSession) => Promise) => { const session = await connectionMongo.startSession(); - session.startTransaction(); try { + session.startTransaction(); const result = await fn(session); await session.commitTransaction(); - await session.endSession(); return result as T; } catch (error) { await session.abortTransaction(); - await session.endSession(); return Promise.reject(error); + } finally { + await session.endSession(); } }; diff --git a/packages/service/common/system/log.ts b/packages/service/common/system/log.ts index 7c7cbb72b..9157bfa27 100644 --- a/packages/service/common/system/log.ts +++ b/packages/service/common/system/log.ts @@ -1,13 +1,35 @@ import dayjs from 'dayjs'; +import chalk from 'chalk'; + +enum LogLevelEnum { + debug = 'debug', + info = 'info', + warn = 'warn', + error = 'error' +} +const logMap = { + [LogLevelEnum.debug]: { + levelLog: chalk.green('[Debug]') + }, + [LogLevelEnum.info]: { + levelLog: chalk.blue('[Info]') + }, + [LogLevelEnum.warn]: { + levelLog: chalk.yellow('[Warn]') + }, + [LogLevelEnum.error]: { + levelLog: chalk.red('[Error]') + } +}; /* add logger */ export const addLog = { - log(level: 'info' | 'warn' | 'error', msg: string, obj: Record = {}) { + log(level: LogLevelEnum, msg: string, obj: Record = {}) { const stringifyObj = JSON.stringify(obj); const isEmpty = Object.keys(obj).length === 0; console.log( - `[${level.toLocaleUpperCase()}] ${dayjs().format('YYYY-MM-DD HH:mm:ss')} ${msg} ${ + `${logMap[level].levelLog} ${dayjs().format('YYYY-MM-DD HH:mm:ss')} ${msg} ${ level !== 'error' && !isEmpty ? stringifyObj : '' }` ); @@ -44,14 +66,17 @@ export const addLog = { }); } catch (error) {} }, + debug(msg: string, obj?: Record) { + this.log(LogLevelEnum.debug, msg, obj); + }, info(msg: string, obj?: Record) { - this.log('info', msg, obj); + this.log(LogLevelEnum.info, msg, obj); }, warn(msg: string, obj?: Record) { - this.log('warn', msg, obj); + this.log(LogLevelEnum.warn, msg, obj); }, error(msg: string, error?: any) { - this.log('error', msg, { + this.log(LogLevelEnum.error, msg, { message: error?.message || error, stack: error?.stack, ...(error?.config && { diff --git a/packages/service/common/vectorStore/constants.ts b/packages/service/common/vectorStore/constants.ts new file mode 100644 index 000000000..5b9206eb5 --- /dev/null +++ b/packages/service/common/vectorStore/constants.ts @@ -0,0 +1,6 @@ +export const DatasetVectorDbName = 'fastgpt'; +export const DatasetVectorTableName = 'modeldata'; + +export const PG_ADDRESS = process.env.PG_URL; +export const MILVUS_ADDRESS = process.env.MILVUS_ADDRESS; +export const MILVUS_TOKEN = process.env.MILVUS_TOKEN; diff --git a/packages/service/common/vectorStore/controller.d.ts b/packages/service/common/vectorStore/controller.d.ts index ee823786f..03272ea40 100644 --- a/packages/service/common/vectorStore/controller.d.ts +++ b/packages/service/common/vectorStore/controller.d.ts @@ -1,3 +1,5 @@ +import type { EmbeddingRecallItemType } from './type'; + export type DeleteDatasetVectorProps = ( | { id: string } | { datasetIds: string[]; collectionIds?: string[] } @@ -5,12 +7,19 @@ export type DeleteDatasetVectorProps = ( ) & { teamId: string; }; +export type DelDatasetVectorCtrlProps = DeleteDatasetVectorProps & { + retry?: number; +}; export type InsertVectorProps = { teamId: string; datasetId: string; collectionId: string; }; +export type InsertVectorControllerProps = InsertVectorProps & { + vector: number[]; + retry?: number; +}; export type EmbeddingRecallProps = { teamId: string; @@ -18,3 +27,11 @@ export type EmbeddingRecallProps = { // similarity?: number; // efSearch?: number; }; +export type EmbeddingRecallCtrlProps = EmbeddingRecallProps & { + vector: number[]; + limit: number; + retry?: number; +}; +export type EmbeddingRecallResponse = { + results: EmbeddingRecallItemType[]; +}; diff --git a/packages/service/common/vectorStore/controller.ts b/packages/service/common/vectorStore/controller.ts index 6c682da0d..b2bd216e3 100644 --- a/packages/service/common/vectorStore/controller.ts +++ b/packages/service/common/vectorStore/controller.ts @@ -1,18 +1,25 @@ /* vector crud */ -import { PgVector } from './pg/class'; +import { PgVectorCtrl } from './pg/class'; import { getVectorsByText } from '../../core/ai/embedding'; import { InsertVectorProps } from './controller.d'; import { VectorModelItemType } from '@fastgpt/global/core/ai/model.d'; +import { MILVUS_ADDRESS, PG_ADDRESS } from './constants'; +import { MilvusCtrl } from './milvus/class'; const getVectorObj = () => { - return new PgVector(); + if (PG_ADDRESS) return new PgVectorCtrl(); + if (MILVUS_ADDRESS) return new MilvusCtrl(); + + return new PgVectorCtrl(); }; -export const initVectorStore = getVectorObj().init; -export const deleteDatasetDataVector = getVectorObj().delete; -export const recallFromVectorStore = getVectorObj().recall; -export const getVectorDataByTime = getVectorObj().getVectorDataByTime; -export const getVectorCountByTeamId = getVectorObj().getVectorCountByTeamId; +const Vector = getVectorObj(); + +export const initVectorStore = Vector.init; +export const deleteDatasetDataVector = Vector.delete; +export const recallFromVectorStore = Vector.embRecall; +export const getVectorDataByTime = Vector.getVectorDataByTime; +export const getVectorCountByTeamId = Vector.getVectorCountByTeamId; export const insertDatasetDataVector = async ({ model, @@ -27,9 +34,9 @@ export const insertDatasetDataVector = async ({ input: query, type: 'db' }); - const { insertId } = await getVectorObj().insert({ + const { insertId } = await Vector.insert({ ...props, - vectors + vector: vectors[0] }); return { diff --git a/packages/service/common/vectorStore/milvus/class.ts b/packages/service/common/vectorStore/milvus/class.ts new file mode 100644 index 000000000..84edaab02 --- /dev/null +++ b/packages/service/common/vectorStore/milvus/class.ts @@ -0,0 +1,287 @@ +import { DataType, LoadState, MilvusClient } from '@zilliz/milvus2-sdk-node'; +import { + DatasetVectorDbName, + DatasetVectorTableName, + MILVUS_ADDRESS, + MILVUS_TOKEN +} from '../constants'; +import type { + DelDatasetVectorCtrlProps, + EmbeddingRecallCtrlProps, + EmbeddingRecallResponse, + InsertVectorControllerProps +} from '../controller.d'; +import { delay } from '@fastgpt/global/common/system/utils'; +import { addLog } from '../../../common/system/log'; + +export class MilvusCtrl { + constructor() {} + getClient = async () => { + if (!MILVUS_ADDRESS) { + return Promise.reject('MILVUS_ADDRESS is not set'); + } + if (global.milvusClient) return global.milvusClient; + + global.milvusClient = new MilvusClient({ + address: MILVUS_ADDRESS, + token: MILVUS_TOKEN + }); + + addLog.info(`Milvus connected`); + + return global.milvusClient; + }; + init = async () => { + const client = await this.getClient(); + + // init db(zilliz cloud will error) + try { + const { db_names } = await client.listDatabases(); + + if (!db_names.includes(DatasetVectorDbName)) { + await client.createDatabase({ + db_name: DatasetVectorDbName + }); + } + + await client.useDatabase({ + db_name: DatasetVectorDbName + }); + } catch (error) {} + + // init collection and index + const { value: hasCollection } = await client.hasCollection({ + collection_name: DatasetVectorTableName + }); + if (!hasCollection) { + const result = await client.createCollection({ + collection_name: DatasetVectorTableName, + description: 'Store dataset vector', + enableDynamicField: true, + fields: [ + { + name: 'id', + data_type: DataType.Int64, + is_primary_key: true, + autoID: true + }, + { + name: 'vector', + data_type: DataType.FloatVector, + dim: 1536 + }, + { name: 'teamId', data_type: DataType.VarChar, max_length: 64 }, + { name: 'datasetId', data_type: DataType.VarChar, max_length: 64 }, + { name: 'collectionId', data_type: DataType.VarChar, max_length: 64 }, + { + name: 'createTime', + data_type: DataType.Int64 + } + ], + index_params: [ + { + field_name: 'vector', + index_name: 'vector_HNSW', + index_type: 'HNSW', + metric_type: 'IP', + params: { efConstruction: 32, M: 64 } + }, + { + field_name: 'teamId', + index_type: 'Trie' + }, + { + field_name: 'datasetId', + index_type: 'Trie' + }, + { + field_name: 'collectionId', + index_type: 'Trie' + }, + { + field_name: 'createTime', + index_type: 'STL_SORT' + } + ] + }); + + addLog.info(`Create milvus collection: `, result); + } + + const { state: colLoadState } = await client.getLoadState({ + collection_name: DatasetVectorTableName + }); + + if ( + colLoadState === LoadState.LoadStateNotExist || + colLoadState === LoadState.LoadStateNotLoad + ) { + await client.loadCollectionSync({ + collection_name: DatasetVectorTableName + }); + addLog.info(`Milvus collection load success`); + } + }; + + insert = async (props: InsertVectorControllerProps): Promise<{ insertId: string }> => { + const client = await this.getClient(); + const { teamId, datasetId, collectionId, vector, retry = 3 } = props; + + try { + const result = await client.insert({ + collection_name: DatasetVectorTableName, + data: [ + { + vector, + teamId: String(teamId), + datasetId: String(datasetId), + collectionId: String(collectionId), + createTime: Date.now() + } + ] + }); + + const insertId = (() => { + if ('int_id' in result.IDs) { + return `${result.IDs.int_id.data?.[0]}`; + } + return `${result.IDs.str_id.data?.[0]}`; + })(); + + return { + insertId: insertId + }; + } catch (error) { + if (retry <= 0) { + return Promise.reject(error); + } + await delay(500); + return this.insert({ + ...props, + retry: retry - 1 + }); + } + }; + delete = async (props: DelDatasetVectorCtrlProps): Promise => { + const { teamId, retry = 2 } = props; + const client = await this.getClient(); + + const teamIdWhere = `(teamId=="${String(teamId)}")`; + const where = await (() => { + if ('id' in props && props.id) return `(id==${props.id})`; + + if ('datasetIds' in props && props.datasetIds) { + const datasetIdWhere = `(datasetId in [${props.datasetIds + .map((id) => `"${String(id)}"`) + .join(',')}])`; + + if ('collectionIds' in props && props.collectionIds) { + return `${datasetIdWhere} and (collectionId in [${props.collectionIds + .map((id) => `"${String(id)}"`) + .join(',')}])`; + } + + return `${datasetIdWhere}`; + } + + if ('idList' in props && Array.isArray(props.idList)) { + if (props.idList.length === 0) return; + return `(id in [${props.idList.map((id) => String(id)).join(',')}])`; + } + return Promise.reject('deleteDatasetData: no where'); + })(); + + if (!where) return; + + const concatWhere = `${teamIdWhere} and ${where}`; + + try { + await client.delete({ + collection_name: DatasetVectorTableName, + filter: concatWhere + }); + } catch (error) { + if (retry <= 0) { + return Promise.reject(error); + } + await delay(500); + return this.delete({ + ...props, + retry: retry - 1 + }); + } + }; + embRecall = async (props: EmbeddingRecallCtrlProps): Promise => { + const client = await this.getClient(); + const { teamId, datasetIds, vector, limit, retry = 2 } = props; + + try { + const { results } = await client.search({ + collection_name: DatasetVectorTableName, + data: vector, + limit, + filter: `(teamId == "${teamId}") and (datasetId in [${datasetIds.map((id) => `"${String(id)}"`).join(',')}])`, + output_fields: ['collectionId'] + }); + + const rows = results as { + score: number; + id: string; + collectionId: string; + }[]; + + return { + results: rows.map((item) => ({ + id: String(item.id), + collectionId: item.collectionId, + score: item.score + })) + }; + } catch (error) { + if (retry <= 0) { + return Promise.reject(error); + } + return this.embRecall({ + ...props, + retry: retry - 1 + }); + } + }; + + getVectorCountByTeamId = async (teamId: string) => { + const client = await this.getClient(); + + const result = await client.query({ + collection_name: DatasetVectorTableName, + output_fields: ['count(*)'], + filter: `teamId == "${String(teamId)}"` + }); + + const total = result.data?.[0]?.['count(*)'] as number; + + return total; + }; + getVectorDataByTime = async (start: Date, end: Date) => { + const client = await this.getClient(); + const startTimestamp = new Date(start).getTime(); + const endTimestamp = new Date(end).getTime(); + + const result = await client.query({ + collection_name: DatasetVectorTableName, + output_fields: ['id', 'teamId', 'datasetId'], + filter: `(createTime >= ${startTimestamp}) and (createTime <= ${endTimestamp})` + }); + + const rows = result.data as { + id: string; + teamId: string; + datasetId: string; + }[]; + + return rows.map((item) => ({ + id: String(item.id), + teamId: item.teamId, + datasetId: item.datasetId + })); + }; +} diff --git a/packages/service/common/vectorStore/pg/class.ts b/packages/service/common/vectorStore/pg/class.ts index eeab2b0ee..ec58aa1f3 100644 --- a/packages/service/common/vectorStore/pg/class.ts +++ b/packages/service/common/vectorStore/pg/class.ts @@ -1,18 +1,180 @@ +/* pg vector crud */ +import { DatasetVectorTableName } from '../constants'; +import { delay } from '@fastgpt/global/common/system/utils'; +import { PgClient, connectPg } from './index'; +import { PgSearchRawType } from '@fastgpt/global/core/dataset/api'; import { - initPg, - insertDatasetDataVector, - deleteDatasetDataVector, - embeddingRecall, - getVectorDataByTime, - getVectorCountByTeamId -} from './controller'; + DelDatasetVectorCtrlProps, + EmbeddingRecallCtrlProps, + EmbeddingRecallResponse, + InsertVectorControllerProps +} from '../controller.d'; +import dayjs from 'dayjs'; -export class PgVector { +export class PgVectorCtrl { constructor() {} - init = initPg; - insert = insertDatasetDataVector; - delete = deleteDatasetDataVector; - recall = embeddingRecall; - getVectorCountByTeamId = getVectorCountByTeamId; - getVectorDataByTime = getVectorDataByTime; + init = async () => { + try { + await connectPg(); + await PgClient.query(` + CREATE EXTENSION IF NOT EXISTS vector; + CREATE TABLE IF NOT EXISTS ${DatasetVectorTableName} ( + id BIGSERIAL PRIMARY KEY, + vector VECTOR(1536) NOT NULL, + team_id VARCHAR(50) NOT NULL, + dataset_id VARCHAR(50) NOT NULL, + collection_id VARCHAR(50) NOT NULL, + createtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + `); + + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS vector_index ON ${DatasetVectorTableName} USING hnsw (vector vector_ip_ops) WITH (m = 32, ef_construction = 128);` + ); + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS team_dataset_collection_index ON ${DatasetVectorTableName} USING btree(team_id, dataset_id, collection_id);` + ); + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS create_time_index ON ${DatasetVectorTableName} USING btree(createtime);` + ); + + console.log('init pg successful'); + } catch (error) { + console.log('init pg error', error); + } + }; + insert = async (props: InsertVectorControllerProps): Promise<{ insertId: string }> => { + const { teamId, datasetId, collectionId, vector, retry = 3 } = props; + + try { + const { rows } = await PgClient.insert(DatasetVectorTableName, { + values: [ + [ + { key: 'vector', value: `[${vector}]` }, + { key: 'team_id', value: String(teamId) }, + { key: 'dataset_id', value: String(datasetId) }, + { key: 'collection_id', value: String(collectionId) } + ] + ] + }); + return { + insertId: rows[0].id + }; + } catch (error) { + if (retry <= 0) { + return Promise.reject(error); + } + await delay(500); + return this.insert({ + ...props, + retry: retry - 1 + }); + } + }; + delete = async (props: DelDatasetVectorCtrlProps): Promise => { + const { teamId, retry = 2 } = props; + + const teamIdWhere = `team_id='${String(teamId)}' AND`; + + const where = await (() => { + if ('id' in props && props.id) return `${teamIdWhere} id=${props.id}`; + + if ('datasetIds' in props && props.datasetIds) { + const datasetIdWhere = `dataset_id IN (${props.datasetIds + .map((id) => `'${String(id)}'`) + .join(',')})`; + + if ('collectionIds' in props && props.collectionIds) { + return `${teamIdWhere} ${datasetIdWhere} AND collection_id IN (${props.collectionIds + .map((id) => `'${String(id)}'`) + .join(',')})`; + } + + return `${teamIdWhere} ${datasetIdWhere}`; + } + + if ('idList' in props && Array.isArray(props.idList)) { + if (props.idList.length === 0) return; + return `${teamIdWhere} id IN (${props.idList.map((id) => String(id)).join(',')})`; + } + return Promise.reject('deleteDatasetData: no where'); + })(); + + if (!where) return; + + try { + await PgClient.delete(DatasetVectorTableName, { + where: [where] + }); + } catch (error) { + if (retry <= 0) { + return Promise.reject(error); + } + await delay(500); + return this.delete({ + ...props, + retry: retry - 1 + }); + } + }; + embRecall = async (props: EmbeddingRecallCtrlProps): Promise => { + const { teamId, datasetIds, vector, limit, retry = 2 } = props; + + try { + const results: any = await PgClient.query( + ` + BEGIN; + SET LOCAL hnsw.ef_search = ${global.systemEnv?.pgHNSWEfSearch || 100}; + select id, collection_id, vector <#> '[${vector}]' AS score + from ${DatasetVectorTableName} + where team_id='${teamId}' + AND dataset_id IN (${datasetIds.map((id) => `'${String(id)}'`).join(',')}) + order by score limit ${limit}; + COMMIT;` + ); + + const rows = results?.[2]?.rows as PgSearchRawType[]; + + return { + results: rows.map((item) => ({ + id: String(item.id), + collectionId: item.collection_id, + score: item.score * -1 + })) + }; + } catch (error) { + if (retry <= 0) { + return Promise.reject(error); + } + return this.embRecall({ + ...props, + retry: retry - 1 + }); + } + }; + getVectorCountByTeamId = async (teamId: string) => { + const total = await PgClient.count(DatasetVectorTableName, { + where: [['team_id', String(teamId)]] + }); + + return total; + }; + getVectorDataByTime = async (start: Date, end: Date) => { + const { rows } = await PgClient.query<{ + id: string; + team_id: string; + dataset_id: string; + }>(`SELECT id, team_id, dataset_id + FROM ${DatasetVectorTableName} + WHERE createtime BETWEEN '${dayjs(start).format('YYYY-MM-DD HH:mm:ss')}' AND '${dayjs( + end + ).format('YYYY-MM-DD HH:mm:ss')}'; + `); + + return rows.map((item) => ({ + id: String(item.id), + teamId: item.team_id, + datasetId: item.dataset_id + })); + }; } diff --git a/packages/service/common/vectorStore/pg/controller.ts b/packages/service/common/vectorStore/pg/controller.ts deleted file mode 100644 index 0ce6203a9..000000000 --- a/packages/service/common/vectorStore/pg/controller.ts +++ /dev/null @@ -1,195 +0,0 @@ -/* pg vector crud */ -import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants'; -import { delay } from '@fastgpt/global/common/system/utils'; -import { PgClient, connectPg } from './index'; -import { PgSearchRawType } from '@fastgpt/global/core/dataset/api'; -import { EmbeddingRecallItemType } from '../type'; -import { DeleteDatasetVectorProps, EmbeddingRecallProps, InsertVectorProps } from '../controller.d'; -import dayjs from 'dayjs'; - -export async function initPg() { - try { - await connectPg(); - await PgClient.query(` - CREATE EXTENSION IF NOT EXISTS vector; - CREATE TABLE IF NOT EXISTS ${PgDatasetTableName} ( - id BIGSERIAL PRIMARY KEY, - vector VECTOR(1536) NOT NULL, - team_id VARCHAR(50) NOT NULL, - dataset_id VARCHAR(50) NOT NULL, - collection_id VARCHAR(50) NOT NULL, - createtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - `); - - await PgClient.query( - `CREATE INDEX CONCURRENTLY IF NOT EXISTS vector_index ON ${PgDatasetTableName} USING hnsw (vector vector_ip_ops) WITH (m = 32, ef_construction = 128);` - ); - await PgClient.query( - `CREATE INDEX CONCURRENTLY IF NOT EXISTS team_dataset_collection_index ON ${PgDatasetTableName} USING btree(team_id, dataset_id, collection_id);` - ); - await PgClient.query( - `CREATE INDEX CONCURRENTLY IF NOT EXISTS create_time_index ON ${PgDatasetTableName} USING btree(createtime);` - ); - - console.log('init pg successful'); - } catch (error) { - console.log('init pg error', error); - } -} - -export const insertDatasetDataVector = async ( - props: InsertVectorProps & { - vectors: number[][]; - retry?: number; - } -): Promise<{ insertId: string }> => { - const { teamId, datasetId, collectionId, vectors, retry = 3 } = props; - - try { - const { rows } = await PgClient.insert(PgDatasetTableName, { - values: [ - [ - { key: 'vector', value: `[${vectors[0]}]` }, - { key: 'team_id', value: String(teamId) }, - { key: 'dataset_id', value: String(datasetId) }, - { key: 'collection_id', value: String(collectionId) } - ] - ] - }); - return { - insertId: rows[0].id - }; - } catch (error) { - if (retry <= 0) { - return Promise.reject(error); - } - await delay(500); - return insertDatasetDataVector({ - ...props, - retry: retry - 1 - }); - } -}; - -export const deleteDatasetDataVector = async ( - props: DeleteDatasetVectorProps & { - retry?: number; - } -): Promise => { - const { teamId, retry = 2 } = props; - - const teamIdWhere = `team_id='${String(teamId)}' AND`; - - const where = await (() => { - if ('id' in props && props.id) return `${teamIdWhere} id=${props.id}`; - - if ('datasetIds' in props && props.datasetIds) { - const datasetIdWhere = `dataset_id IN (${props.datasetIds - .map((id) => `'${String(id)}'`) - .join(',')})`; - - if ('collectionIds' in props && props.collectionIds) { - return `${teamIdWhere} ${datasetIdWhere} AND collection_id IN (${props.collectionIds - .map((id) => `'${String(id)}'`) - .join(',')})`; - } - - return `${teamIdWhere} ${datasetIdWhere}`; - } - - if ('idList' in props && Array.isArray(props.idList)) { - if (props.idList.length === 0) return; - return `${teamIdWhere} id IN (${props.idList.map((id) => `'${String(id)}'`).join(',')})`; - } - return Promise.reject('deleteDatasetData: no where'); - })(); - - if (!where) return; - - try { - await PgClient.delete(PgDatasetTableName, { - where: [where] - }); - } catch (error) { - if (retry <= 0) { - return Promise.reject(error); - } - await delay(500); - return deleteDatasetDataVector({ - ...props, - retry: retry - 1 - }); - } -}; - -export const embeddingRecall = async ( - props: EmbeddingRecallProps & { - vectors: number[][]; - limit: number; - retry?: number; - } -): Promise<{ - results: EmbeddingRecallItemType[]; -}> => { - const { teamId, datasetIds, vectors, limit, retry = 2 } = props; - - try { - const results: any = await PgClient.query( - ` - BEGIN; - SET LOCAL hnsw.ef_search = ${global.systemEnv?.pgHNSWEfSearch || 100}; - select id, collection_id, vector <#> '[${vectors[0]}]' AS score - from ${PgDatasetTableName} - where team_id='${teamId}' - AND dataset_id IN (${datasetIds.map((id) => `'${String(id)}'`).join(',')}) - order by score limit ${limit}; - COMMIT;` - ); - - const rows = results?.[2]?.rows as PgSearchRawType[]; - - return { - results: rows.map((item) => ({ - id: item.id, - collectionId: item.collection_id, - score: item.score * -1 - })) - }; - } catch (error) { - console.log(error); - if (retry <= 0) { - return Promise.reject(error); - } - return embeddingRecall({ - ...props, - retry: retry - 1 - }); - } -}; - -export const getVectorCountByTeamId = async (teamId: string) => { - const total = await PgClient.count(PgDatasetTableName, { - where: [['team_id', String(teamId)]] - }); - - return total; -}; -export const getVectorDataByTime = async (start: Date, end: Date) => { - const { rows } = await PgClient.query<{ - id: string; - team_id: string; - dataset_id: string; - }>(`SELECT id, team_id, dataset_id - FROM ${PgDatasetTableName} - WHERE createtime BETWEEN '${dayjs(start).format('YYYY-MM-DD HH:mm:ss')}' AND '${dayjs(end).format( - 'YYYY-MM-DD HH:mm:ss' - )}'; - `); - - return rows.map((item) => ({ - id: String(item.id), - teamId: item.team_id, - datasetId: item.dataset_id - })); -}; diff --git a/packages/service/common/vectorStore/pg/index.ts b/packages/service/common/vectorStore/pg/index.ts index cbb8c975d..56b8f5ad4 100644 --- a/packages/service/common/vectorStore/pg/index.ts +++ b/packages/service/common/vectorStore/pg/index.ts @@ -2,6 +2,7 @@ import { delay } from '@fastgpt/global/common/system/utils'; import { addLog } from '../../system/log'; import { Pool } from 'pg'; import type { QueryResultRow } from 'pg'; +import { PG_ADDRESS } from '../constants'; export const connectPg = async (): Promise => { if (global.pgClient) { @@ -9,7 +10,7 @@ export const connectPg = async (): Promise => { } global.pgClient = new Pool({ - connectionString: process.env.PG_URL, + connectionString: PG_ADDRESS, max: Number(process.env.DB_MAX_LINK || 20), min: 10, keepAlive: true, diff --git a/packages/service/common/vectorStore/type.d.ts b/packages/service/common/vectorStore/type.d.ts index 0f7624662..2ccc1f4a6 100644 --- a/packages/service/common/vectorStore/type.d.ts +++ b/packages/service/common/vectorStore/type.d.ts @@ -1,7 +1,9 @@ import type { Pool } from 'pg'; +import { MilvusClient } from '@zilliz/milvus2-sdk-node'; declare global { var pgClient: Pool | null; + var milvusClient: MilvusClient | null; } export type EmbeddingRecallItemType = { diff --git a/packages/service/core/dataset/search/controller.ts b/packages/service/core/dataset/search/controller.ts index 60ade42f4..6fafd33f5 100644 --- a/packages/service/core/dataset/search/controller.ts +++ b/packages/service/core/dataset/search/controller.ts @@ -85,7 +85,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { const { results } = await recallFromVectorStore({ teamId, datasetIds, - vectors, + vector: vectors[0], limit }); @@ -94,7 +94,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { { teamId, datasetId: { $in: datasetIds }, - collectionId: { $in: results.map((item) => item.collectionId) }, + collectionId: { $in: Array.from(new Set(results.map((item) => item.collectionId))) }, 'indexes.dataId': { $in: results.map((item) => item.id?.trim()) } }, 'datasetId collectionId q a chunkIndex indexes' @@ -118,26 +118,24 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { concatResults.sort((a, b) => b.score - a.score); - const formatResult = concatResults - .map((data, index) => { - if (!data.collectionId) { - console.log('Collection is not found', data); - } + const formatResult = concatResults.map((data, index) => { + if (!data.collectionId) { + console.log('Collection is not found', data); + } - const result: SearchDataResponseItemType = { - id: String(data._id), - q: data.q, - a: data.a, - chunkIndex: data.chunkIndex, - datasetId: String(data.datasetId), - collectionId: String(data.collectionId?._id), - ...getCollectionSourceData(data.collectionId), - score: [{ type: SearchScoreTypeEnum.embedding, value: data.score, index }] - }; + const result: SearchDataResponseItemType = { + id: String(data._id), + q: data.q, + a: data.a, + chunkIndex: data.chunkIndex, + datasetId: String(data.datasetId), + collectionId: String(data.collectionId?._id), + ...getCollectionSourceData(data.collectionId), + score: [{ type: SearchScoreTypeEnum.embedding, value: data.score, index }] + }; - return result; - }) - .filter((item) => item !== null) as SearchDataResponseItemType[]; + return result; + }); return { embeddingRecallResults: formatResult, diff --git a/packages/service/core/workflow/dispatch/chat/oneapi.ts b/packages/service/core/workflow/dispatch/chat/oneapi.ts index 10898e40c..968061525 100644 --- a/packages/service/core/workflow/dispatch/chat/oneapi.ts +++ b/packages/service/core/workflow/dispatch/chat/oneapi.ts @@ -45,6 +45,7 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti import { getHistories } from '../utils'; import { filterSearchResultsByMaxChars } from '../../utils'; import { getHistoryPreview } from '@fastgpt/global/core/chat/utils'; +import { addLog } from '../../../../common/system/log'; export type ChatProps = ModuleDispatchProps< AIChatNodeProps & { @@ -167,21 +168,19 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise { if (res && stream) { @@ -189,7 +188,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise; }) { const write = responseWriteController({ res, @@ -378,6 +380,7 @@ async function streamResponse({ } if (!answer) { + addLog.info(`LLM model response empty`, requestBody); return Promise.reject('core.chat.Chat API is error or undefined'); } diff --git a/packages/service/core/workflow/dispatch/code/run.ts b/packages/service/core/workflow/dispatch/code/run.ts index d8fd77b7b..c427398ba 100644 --- a/packages/service/core/workflow/dispatch/code/run.ts +++ b/packages/service/core/workflow/dispatch/code/run.ts @@ -25,7 +25,10 @@ export const dispatchRunCode = async (props: RunCodeType): Promise; + data: { + codeReturn: Record; + log: string; + }; }>(sandBoxRequestUrl, { code, variables: customVariables @@ -33,10 +36,11 @@ export const dispatchRunCode = async (props: RunCodeType): Promise isEmpty(variableValue), - [VariableConditionEnum.isNotEmpty]: () => !isEmpty(variableValue), +function checkCondition(condition: VariableConditionEnum, inputValue: any, value: string) { + const operations: Record boolean> = { + [VariableConditionEnum.isEmpty]: () => isEmpty(inputValue), + [VariableConditionEnum.isNotEmpty]: () => !isEmpty(inputValue), - [VariableConditionEnum.equalTo]: () => String(variableValue) === value, - [VariableConditionEnum.notEqual]: () => String(variableValue) !== value, + [VariableConditionEnum.equalTo]: () => String(inputValue) === value, + [VariableConditionEnum.notEqual]: () => String(inputValue) !== value, // number - [VariableConditionEnum.greaterThan]: () => Number(variableValue) > Number(value), - [VariableConditionEnum.lessThan]: () => Number(variableValue) < Number(value), - [VariableConditionEnum.greaterThanOrEqualTo]: () => Number(variableValue) >= Number(value), - [VariableConditionEnum.lessThanOrEqualTo]: () => Number(variableValue) <= Number(value), + [VariableConditionEnum.greaterThan]: () => Number(inputValue) > Number(value), + [VariableConditionEnum.lessThan]: () => Number(inputValue) < Number(value), + [VariableConditionEnum.greaterThanOrEqualTo]: () => Number(inputValue) >= Number(value), + [VariableConditionEnum.lessThanOrEqualTo]: () => Number(inputValue) <= Number(value), // array or string - [VariableConditionEnum.include]: () => isInclude(variableValue, value), - [VariableConditionEnum.notInclude]: () => !isInclude(variableValue, value), + [VariableConditionEnum.include]: () => isInclude(inputValue, value), + [VariableConditionEnum.notInclude]: () => !isInclude(inputValue, value), // string - [VariableConditionEnum.startWith]: () => variableValue?.startsWith(value), - [VariableConditionEnum.endWith]: () => variableValue?.endsWith(value), + [VariableConditionEnum.startWith]: () => inputValue?.startsWith(value), + [VariableConditionEnum.endWith]: () => inputValue?.endsWith(value), + [VariableConditionEnum.reg]: () => { + if (typeof inputValue !== 'string' || !value) return false; + if (value.startsWith('/')) { + value = value.slice(1); + } + if (value.endsWith('/')) { + value = value.slice(0, -1); + } + + const reg = new RegExp(value, 'g'); + const result = reg.test(inputValue); + + return result; + }, // array - [VariableConditionEnum.lengthEqualTo]: () => variableValue?.length === Number(value), - [VariableConditionEnum.lengthNotEqualTo]: () => variableValue?.length !== Number(value), - [VariableConditionEnum.lengthGreaterThan]: () => variableValue?.length > Number(value), - [VariableConditionEnum.lengthGreaterThanOrEqualTo]: () => - variableValue?.length >= Number(value), - [VariableConditionEnum.lengthLessThan]: () => variableValue?.length < Number(value), - [VariableConditionEnum.lengthLessThanOrEqualTo]: () => variableValue?.length <= Number(value) + [VariableConditionEnum.lengthEqualTo]: () => inputValue?.length === Number(value), + [VariableConditionEnum.lengthNotEqualTo]: () => inputValue?.length !== Number(value), + [VariableConditionEnum.lengthGreaterThan]: () => inputValue?.length > Number(value), + [VariableConditionEnum.lengthGreaterThanOrEqualTo]: () => inputValue?.length >= Number(value), + [VariableConditionEnum.lengthLessThan]: () => inputValue?.length < Number(value), + [VariableConditionEnum.lengthLessThanOrEqualTo]: () => inputValue?.length <= Number(value) }; - return (operations[condition] || (() => false))(); + return operations[condition]?.() ?? false; } function getResult( @@ -92,13 +106,13 @@ function getResult( const listResult = list.map((item) => { const { variable, condition: variableCondition, value } = item; - const variableValue = getReferenceVariableValue({ + const inputValue = getReferenceVariableValue({ value: variable, variables, nodes: runtimeNodes }); - return checkCondition(variableCondition as VariableConditionEnum, variableValue, value || ''); + return checkCondition(variableCondition as VariableConditionEnum, inputValue, value || ''); }); return condition === 'AND' ? listResult.every(Boolean) : listResult.some(Boolean); diff --git a/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts b/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts index 8d2163c08..a5bf18833 100644 --- a/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts +++ b/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts @@ -16,7 +16,7 @@ type Props = ModuleDispatchProps<{ type Response = DispatchNodeResultType<{}>; export const dispatchUpdateVariable = async (props: Props): Promise => { - const { res, detail, params, variables, runtimeNodes } = props; + const { res, detail, stream, params, variables, runtimeNodes } = props; const { updateList } = params; updateList.forEach((item) => { @@ -54,7 +54,7 @@ export const dispatchUpdateVariable = async (props: Props): Promise => } }); - if (detail) { + if (detail && stream) { responseWrite({ res, event: SseResponseEventEnum.updateVariables, diff --git a/packages/service/core/workflow/dispatch/utils.ts b/packages/service/core/workflow/dispatch/utils.ts index ca0e86f0e..c81f46172 100644 --- a/packages/service/core/workflow/dispatch/utils.ts +++ b/packages/service/core/workflow/dispatch/utils.ts @@ -1,3 +1,5 @@ +import { getErrText } from '@fastgpt/global/common/error/utils'; +import { replaceSensitiveText } from '@fastgpt/global/common/string/tools'; import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; import { WorkflowIOValueTypeEnum, @@ -89,11 +91,10 @@ export const removeSystemVariable = (variables: Record) => { export const formatHttpError = (error: any) => { return { - message: error?.message, + message: getErrText(error), + data: error?.response?.data, name: error?.name, method: error?.config?.method, - baseURL: error?.config?.baseURL, - url: error?.config?.url, code: error?.code, status: error?.status }; diff --git a/packages/service/package.json b/packages/service/package.json index 3d7a33bdc..40316dd50 100644 --- a/packages/service/package.json +++ b/packages/service/package.json @@ -5,7 +5,9 @@ "@fastgpt/global": "workspace:*", "@node-rs/jieba": "1.10.0", "@xmldom/xmldom": "^0.8.10", + "@zilliz/milvus2-sdk-node": "2.4.2", "axios": "^1.5.1", + "chalk": "^5.3.0", "cheerio": "1.0.0-rc.12", "cookie": "^0.5.0", "date-fns": "2.30.0", @@ -22,7 +24,7 @@ "mongoose": "^7.0.2", "multer": "1.4.5-lts.1", "next": "14.2.3", - "nextjs-cors": "^2.1.2", + "nextjs-cors": "^2.2.0", "node-cron": "^3.0.3", "node-xlsx": "^0.23.0", "papaparse": "5.4.1", diff --git a/packages/service/support/permission/teamLimit.ts b/packages/service/support/permission/teamLimit.ts index fd948021d..c64884e99 100644 --- a/packages/service/support/permission/teamLimit.ts +++ b/packages/service/support/permission/teamLimit.ts @@ -5,6 +5,7 @@ import { MongoPlugin } from '../../core/plugin/schema'; import { MongoDataset } from '../../core/dataset/schema'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; +import { SystemErrEnum } from '@fastgpt/global/common/error/code/system'; export const checkDatasetLimit = async ({ teamId, @@ -13,14 +14,14 @@ export const checkDatasetLimit = async ({ teamId: string; insertLen?: number; }) => { - const [{ standardConstants, totalPoints, usedPoints, datasetMaxSize }, usedSize] = - await Promise.all([getTeamPlanStatus({ teamId }), getVectorCountByTeamId(teamId)]); + const { standardConstants, totalPoints, usedPoints, datasetMaxSize, usedDatasetSize } = + await getTeamPlanStatus({ teamId }); if (!standardConstants) return; - if (usedSize + insertLen >= datasetMaxSize) { + if (usedDatasetSize + insertLen >= datasetMaxSize) { return Promise.reject( - `您的知识库容量为: ${datasetMaxSize}组,已使用: ${usedSize}组,导入当前文件需要: ${insertLen}组,请增加知识库容量后导入。` + `您的知识库容量为: ${datasetMaxSize}组,已使用: ${usedDatasetSize}组,导入当前文件需要: ${insertLen}组,请增加知识库容量后导入。` ); } @@ -59,6 +60,9 @@ export const checkTeamDatasetLimit = async (teamId: string) => { if (standardConstants && datasetCount >= standardConstants.maxDatasetAmount) { return Promise.reject(TeamErrEnum.datasetAmountNotEnough); } + if (!global.feConfigs.isPlus && datasetCount >= 30) { + return Promise.reject(SystemErrEnum.communityVersionNumLimit); + } }; export const checkTeamAppLimit = async (teamId: string) => { const [{ standardConstants }, appCount] = await Promise.all([ diff --git a/packages/web/components/common/MySelect/MultipleRowSelect.tsx b/packages/web/components/common/MySelect/MultipleRowSelect.tsx index 4b19991f3..94f8c206c 100644 --- a/packages/web/components/common/MySelect/MultipleRowSelect.tsx +++ b/packages/web/components/common/MySelect/MultipleRowSelect.tsx @@ -56,11 +56,18 @@ const MultipleRowSelect = ({ }} onClick={() => { const newValue = [...cloneValue]; - newValue[index] = item.value; - setCloneValue(newValue); - if (!hasChildren) { + + if (item.value === selectedValue) { + newValue[index] = undefined; + setCloneValue(newValue); onSelect(newValue); - onClose(); + } else { + newValue[index] = item.value; + setCloneValue(newValue); + if (!hasChildren) { + onSelect(newValue); + onClose(); + } } }} {...(item.value === selectedValue diff --git a/packages/web/styles/theme.ts b/packages/web/styles/theme.ts index fd53bd1ef..8748f64d2 100644 --- a/packages/web/styles/theme.ts +++ b/packages/web/styles/theme.ts @@ -20,6 +20,8 @@ const { definePartsStyle: numInputPart, defineMultiStyleConfig: numInputMultiSty const { definePartsStyle: checkBoxPart, defineMultiStyleConfig: checkBoxMultiStyle } = createMultiStyleConfigHelpers(checkboxAnatomy.keys); +const shadowLight = '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)'; + // 按键 const Button = defineStyleConfig({ baseStyle: { @@ -289,7 +291,7 @@ const Input: ComponentStyleConfig = { borderColor: 'borderColor.low', _focus: { borderColor: 'primary.500', - boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)', + boxShadow: shadowLight, bg: 'white' }, _disabled: { @@ -328,7 +330,7 @@ const NumberInput = numInputMultiStyle({ borderColor: 'myGray.200', _focus: { borderColor: 'primary.500 !important', - boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15) !important', + boxShadow: `${shadowLight} !important`, bg: 'transparent' }, _disabled: { @@ -362,7 +364,7 @@ const Textarea: ComponentStyleConfig = { }, _focus: { borderColor: 'primary.500', - boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)', + boxShadow: shadowLight, bg: 'white' } } @@ -396,7 +398,7 @@ const Select = selectMultiStyle({ field: { borderColor: 'myGray.200', _focusWithin: { - boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)', + boxShadow: shadowLight, borderColor: 'primary.500' } } @@ -408,6 +410,21 @@ const Checkbox = checkBoxMultiStyle({ baseStyle: checkBoxPart({ label: { fontFamily: 'mono' // change the font family of the label + }, + control: { + bg: 'none', + _checked: { + bg: 'primary.50', + borderColor: 'primary.600', + color: 'primary.600', + boxShadow: `${shadowLight} !important`, + _hover: { + bg: 'primary.50' + } + }, + _hover: { + borderColor: 'primary.400' + } } }) }); @@ -437,6 +454,11 @@ export const theme = extendTheme({ }, a: { color: 'primary.600' + }, + '*': { + _focusVisible: { + boxShadow: 'none' + } } } }, @@ -491,6 +513,25 @@ export const theme = extendTheme({ 800: '#2450B5', 900: '#1D4091' }, + blue: { + 1: 'rgba(51, 112, 255, 0.1)', + '015': 'rgba(51, 112, 255, 0.15)', + 3: 'rgba(51, 112, 255, 0.3)', + 5: 'rgba(51, 112, 255, 0.5)', + 7: 'rgba(51, 112, 255, 0.7)', + 9: 'rgba(51, 112, 255, 0.9)', + + 50: '#F0F4FF', + 100: '#E1EAFF', + 200: '#C5D7FF', + 300: '#94B5FF', + 400: '#5E8FFF', + 500: '#487FFF', + 600: '#3370FF', + 700: '#2B5FD9', + 800: '#2450B5', + 900: '#1D4091' + }, red: { 1: 'rgba(217,45,32,0.1)', 3: 'rgba(217,45,32,0.3)', @@ -579,7 +620,8 @@ export const theme = extendTheme({ 5: '0px 0px 1px 0px rgba(19, 51, 107, 0.15), 0px 20px 24px -8px rgba(19, 51, 107, 0.15)', 6: '0px 0px 1px 0px rgba(19, 51, 107, 0.20), 0px 24px 48px -12px rgba(19, 51, 107, 0.20)', 7: '0px 0px 1px 0px rgba(19, 51, 107, 0.20), 0px 32px 64px -12px rgba(19, 51, 107, 0.20)', - focus: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)' + focus: shadowLight, + outline: 'none' }, breakpoints: { sm: '900px', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4de1abe47..4c8b394a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,12 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + react: 18.3.1 + react-dom: 18.3.1 + '@types/react': 18.3.0 + '@types/react-dom': 18.3.0 + importers: .: @@ -102,9 +108,15 @@ importers: '@xmldom/xmldom': specifier: ^0.8.10 version: 0.8.10 + '@zilliz/milvus2-sdk-node': + specifier: 2.4.2 + version: 2.4.2 axios: specifier: ^1.5.1 version: 1.5.1 + chalk: + specifier: ^5.3.0 + version: 5.3.0 cheerio: specifier: 1.0.0-rc.12 version: 1.0.0-rc.12 @@ -154,8 +166,8 @@ importers: specifier: 14.2.3 version: 14.2.3(@babel/core@7.24.4)(react-dom@18.3.1)(react@18.3.1)(sass@1.58.3) nextjs-cors: - specifier: ^2.1.2 - version: 2.1.2(next@14.2.3) + specifier: ^2.2.0 + version: 2.2.0(next@14.2.3) node-cron: specifier: ^3.0.3 version: 3.0.3 @@ -3162,6 +3174,11 @@ packages: dev: true optional: true + /@colors/colors@1.6.0: + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + dev: false + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -3169,6 +3186,14 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true + /@dabh/diagnostics@2.0.3: + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + dev: false + /@emnapi/core@1.1.1: resolution: {integrity: sha512-eu4KjHfXg3I+UUR7vSuwZXpRo4c8h4Rtb5Lu2F7Z4JqJFl/eidquONEBiRs6viXKpWBC3BaJBy68xGJ2j56idw==} requiresBuild: true @@ -3669,6 +3694,25 @@ packages: engines: {node: '>=16.15'} dev: false + /@grpc/grpc-js@1.10.8: + resolution: {integrity: sha512-vYVqYzHicDqyKB+NQhAc54I1QWCBLCrYG6unqOIcBTHx+7x8C9lcoLj3KVJXs2VB4lUbpWY+Kk9NipcbXYWmvg==} + engines: {node: '>=12.10.0'} + dependencies: + '@grpc/proto-loader': 0.7.13 + '@js-sdsl/ordered-map': 4.4.2 + dev: false + + /@grpc/proto-loader@0.7.13: + resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} + engines: {node: '>=6'} + hasBin: true + dependencies: + lodash.camelcase: 4.3.0 + long: 5.2.3 + protobufjs: 7.3.0 + yargs: 17.7.2 + dev: false + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -3968,6 +4012,10 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@js-sdsl/ordered-map@4.4.2: + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + dev: false + /@jsdevtools/ono@7.1.3: resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} dev: false @@ -4797,6 +4845,10 @@ packages: transitivePeerDependencies: - encoding + /@petamoriken/float16@3.8.7: + resolution: {integrity: sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==} + dev: false + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -4807,6 +4859,49 @@ packages: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false + /@protobufjs/aspromise@1.1.2: + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + dev: false + + /@protobufjs/base64@1.1.2: + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + dev: false + + /@protobufjs/codegen@2.0.4: + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + dev: false + + /@protobufjs/eventemitter@1.1.0: + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + dev: false + + /@protobufjs/fetch@1.1.0: + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + dev: false + + /@protobufjs/float@1.0.2: + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + dev: false + + /@protobufjs/inquire@1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + dev: false + + /@protobufjs/path@1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + dev: false + + /@protobufjs/pool@1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + dev: false + + /@protobufjs/utf8@1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + dev: false + /@reactflow/background@11.2.4(immer@9.0.19)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-SYQbCRCU0GuxT/40Tm7ZK+l5wByGnNJSLtZhbL9C/Hl7JhsJXV3UGXr0vrlhVZUBEtkWA7XhZM/5S9XEA5XSFA==} peerDependencies: @@ -5686,6 +5781,10 @@ packages: '@types/superagent': 8.1.7 dev: true + /@types/triple-beam@1.3.5: + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + dev: false + /@types/tunnel@0.0.4: resolution: {integrity: sha512-bQgDBL5XiqrrPUaZd9bZ2esOXcU4GTmgg0n6LHDqoMJezO3VFRZsW8qN6Gp64/LAmjtzNU3iAHBfV3Z2ht5DSg==} dependencies: @@ -6034,6 +6133,19 @@ packages: '@zag-js/dom-query': 0.16.0 dev: false + /@zilliz/milvus2-sdk-node@2.4.2: + resolution: {integrity: sha512-fkPu7XXzfUvHoCnSPVOjqQpWuSnnn9x2NMmmCcIOyRzMeXIsrz4Mf/+M7LUzmT8J9F0Khx65B0rJgCu27YzWQw==} + dependencies: + '@grpc/grpc-js': 1.10.8 + '@grpc/proto-loader': 0.7.13 + '@petamoriken/float16': 3.8.7 + dayjs: 1.11.7 + generic-pool: 3.9.0 + lru-cache: 9.1.2 + protobufjs: 7.3.0 + winston: 3.13.0 + dev: false + /abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} requiresBuild: true @@ -6433,6 +6545,10 @@ packages: engines: {node: '>=8'} dev: true + /async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + dev: false + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -6951,6 +7067,11 @@ packages: engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: true + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: false + /char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} @@ -7162,7 +7283,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} @@ -7199,6 +7319,13 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + /color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true @@ -7210,10 +7337,24 @@ packages: resolution: {integrity: sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==} dev: false + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + dev: false + /colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true + /colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + dev: false + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -8258,6 +8399,10 @@ packages: engines: {node: '>= 4'} dev: false + /enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + dev: false + /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -9308,6 +9453,10 @@ packages: pend: 1.2.0 dev: false + /fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + dev: false + /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -9409,6 +9558,10 @@ packages: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true + /fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + dev: false + /focus-lock@1.3.4: resolution: {integrity: sha512-Gv0N3mvej3pD+HWkNryrF8sExzEHqhQ6OSFxD4DPxm9n5HGCaHme98ZMBZroNEAJcsdtHxk+skvThGKyUeoEGA==} engines: {node: '>=10'} @@ -9611,6 +9764,11 @@ packages: dev: false optional: true + /generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + dev: false + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -9618,7 +9776,6 @@ packages: /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - dev: true /get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} @@ -10235,6 +10392,10 @@ packages: /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + /is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} @@ -10422,7 +10583,6 @@ packages: /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - dev: true /is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} @@ -11197,6 +11357,10 @@ packages: engines: {node: '>=6'} dev: false + /kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + dev: false + /language-subtag-registry@0.3.22: resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} dev: true @@ -11345,6 +11509,10 @@ packages: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} dev: false + /lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + dev: false + /lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true @@ -11417,6 +11585,22 @@ packages: wrap-ansi: 6.2.0 dev: true + /logform@2.6.0: + resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} + engines: {node: '>= 12.0.0'} + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.4.3 + triple-beam: 1.4.1 + dev: false + + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + dev: false + /longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} dev: false @@ -11464,6 +11648,11 @@ packages: yallist: 4.0.0 dev: false + /lru-cache@9.1.2: + resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==} + engines: {node: 14 || >=16.14} + dev: false + /luxon@3.4.4: resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} engines: {node: '>=12'} @@ -12444,10 +12633,10 @@ packages: - '@babel/core' - babel-plugin-macros - /nextjs-cors@2.1.2(next@14.2.3): - resolution: {integrity: sha512-2yOVivaaf2ILe4f/qY32hnj3oC77VCOsUQJQfhVMGsXE/YMEWUY2zy78sH9FKUCM7eG42/l3pDofIzMD781XGA==} + /nextjs-cors@2.2.0(next@14.2.3): + resolution: {integrity: sha512-FZu/A+L59J4POJNqwXYyCPDvsLDeu5HjSBvytzS6lsrJeDz5cmnH45zV+VoNic0hjaeER9xGaiIjZIWzEHnxQg==} peerDependencies: - next: ^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0 + next: ^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 dependencies: cors: 2.8.5 next: 14.2.3(@babel/core@7.24.4)(react-dom@18.3.1)(react@18.3.1)(sass@1.58.3) @@ -12728,6 +12917,12 @@ packages: dependencies: wrappy: 1.0.2 + /one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + dependencies: + fn.name: 1.1.0 + dev: false + /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -13331,6 +13526,25 @@ packages: resolution: {integrity: sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==} dev: false + /protobufjs@7.3.0: + resolution: {integrity: sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g==} + engines: {node: '>=12.0.0'} + requiresBuild: true + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 20.8.5 + long: 5.2.3 + dev: false + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -13943,7 +14157,6 @@ packages: /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - dev: true /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -14331,6 +14544,12 @@ packages: simple-concat: 1.0.1 dev: false + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true @@ -14473,6 +14692,10 @@ packages: deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' dev: true + /stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + dev: false + /stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -14878,6 +15101,10 @@ packages: minimatch: 3.1.2 dev: true + /text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + dev: false + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true @@ -15001,6 +15228,11 @@ packages: deprecated: Use String.prototype.trim() instead dev: true + /triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + dev: false + /trough@1.0.5: resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} dev: true @@ -15951,6 +16183,32 @@ packages: execa: 4.1.0 dev: true + /winston-transport@4.7.0: + resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} + engines: {node: '>= 12.0.0'} + dependencies: + logform: 2.6.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + dev: false + + /winston@3.13.0: + resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} + engines: {node: '>= 12.0.0'} + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.5 + is-stream: 2.0.1 + logform: 2.6.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.7.0 + dev: false + /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -15999,7 +16257,6 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - dev: true /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -16021,7 +16278,6 @@ packages: /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - dev: true /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} @@ -16034,7 +16290,6 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true /yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} diff --git a/projects/app/.env.template b/projects/app/.env.template index d2bc08177..13aa25d71 100644 --- a/projects/app/.env.template +++ b/projects/app/.env.template @@ -18,8 +18,14 @@ OPENAI_BASE_URL=https://api.openai.com/v1 CHAT_API_KEY=sk-xxxx # mongo 数据库连接参数,本地开发时,mongo可能需要增加 directConnection=true 参数,才能连接上。 MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin -# PG 数据库连接参数 + +# 向量库优先级: pg > milvus +# PG 向量库连接参数 PG_URL=postgresql://username:password@host:port/postgres +# mivlus 向量库连接参数 +MILVUS_ADDRESS=https://in03-78bd7f60e6e2a7c.api.gcp-us-west1.zillizcloud.com +MILVUS_TOKEN=133964348b00b4b4e4b51bef680a61350950385c8c64a3ec16b1ab92d3c67dcc4e0370fb9dd15791bcd6dadaf765e98a98735d0d + # code sandbox url SANDBOX_URL=http://localhost:3001 # 商业版地址 diff --git a/projects/app/Dockerfile b/projects/app/Dockerfile index 4e09ea7b4..22f80536b 100644 --- a/projects/app/Dockerfile +++ b/projects/app/Dockerfile @@ -62,9 +62,11 @@ COPY --from=builder --chown=nextjs:nodejs /app/projects/app/.next/server/chunks # copy worker COPY --from=builder --chown=nextjs:nodejs /app/projects/app/.next/server/worker /app/projects/app/.next/server/worker -# copy tiktoken but not copy ./node_modules/tiktoken/encoders +# copy standload packages COPY --from=mainDeps /app/node_modules/tiktoken ./node_modules/tiktoken RUN rm -rf ./node_modules/tiktoken/encoders +COPY --from=mainDeps /app/node_modules/@zilliz/milvus2-sdk-node ./node_modules/@zilliz/milvus2-sdk-node + # copy package.json to version file COPY --from=builder /app/projects/app/package.json ./package.json diff --git a/projects/app/i18n/en/workflow.json b/projects/app/i18n/en/workflow.json index 8e61365e2..700d91742 100644 --- a/projects/app/i18n/en/workflow.json +++ b/projects/app/i18n/en/workflow.json @@ -5,7 +5,12 @@ "Reset template": "Reset template", "Reset template confirm": "Are you sure to restore the code template? Be careful to save the current code." }, + "ifelse": { + "Input value": "Input", + "Select value": "Select" + }, "response": { + "Code log": "Log", "Custom inputs": "Custom inputs", "Custom outputs": "Custom outputs", "Error": "Error" diff --git a/projects/app/i18n/zh/common.json b/projects/app/i18n/zh/common.json index c307f9296..efffb8717 100644 --- a/projects/app/i18n/zh/common.json +++ b/projects/app/i18n/zh/common.json @@ -184,7 +184,7 @@ "not support": "您的浏览器不支持语音输入" }, "system": { - "Commercial version function": "商业版特有功能", + "Commercial version function": "请升级商业版后使用该功能: https://fastgpt.in", "Help Chatbot": "机器人助手", "Use Helper": "使用帮助" }, diff --git a/projects/app/i18n/zh/workflow.json b/projects/app/i18n/zh/workflow.json index f3636f5bc..259990316 100644 --- a/projects/app/i18n/zh/workflow.json +++ b/projects/app/i18n/zh/workflow.json @@ -5,7 +5,12 @@ "Reset template": "还原模板", "Reset template confirm": "确认还原代码模板?请注意保存当前代码。" }, + "ifelse": { + "Input value": "输入值", + "Select value": "选择值" + }, "response": { + "Code log": "Log日志", "Custom inputs": "自定义输入", "Custom outputs": "自定义输出", "Error": "错误信息" diff --git a/projects/app/next.config.js b/projects/app/next.config.js index 135f3fb54..26a34c873 100644 --- a/projects/app/next.config.js +++ b/projects/app/next.config.js @@ -2,10 +2,12 @@ const { i18n } = require('./next-i18next.config'); const path = require('path'); +const isDev = process.env.NODE_ENV === 'development'; + const nextConfig = { i18n, output: 'standalone', - reactStrictMode: process.env.NODE_ENV === 'development' ? false : true, + reactStrictMode: isDev ? false : true, compress: true, webpack(config, { isServer, nextRuntime }) { Object.assign(config.resolve.alias, { @@ -41,11 +43,9 @@ const nextConfig = { } if (isServer) { - config.externals.push('worker_threads'); + // config.externals.push('@zilliz/milvus2-sdk-node'); if (nextRuntime === 'nodejs') { - // config.output.globalObject = 'self'; - const oldEntry = config.entry; config = { ...config, @@ -89,7 +89,12 @@ const nextConfig = { transpilePackages: ['@fastgpt/*', 'ahooks'], experimental: { // 优化 Server Components 的构建和运行,避免不必要的客户端打包。 - serverComponentsExternalPackages: ['mongoose', 'pg', '@node-rs/jieba'], + serverComponentsExternalPackages: [ + 'mongoose', + 'pg', + '@node-rs/jieba', + '@zilliz/milvus2-sdk-node' + ], outputFileTracingRoot: path.join(__dirname, '../../') } }; diff --git a/projects/app/package.json b/projects/app/package.json index ab5180639..03d75be2c 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.8.1", + "version": "4.8.3", "private": false, "scripts": { "dev": "next dev", diff --git a/projects/app/src/components/ChatBox/Input/ChatInput.tsx b/projects/app/src/components/ChatBox/Input/ChatInput.tsx index 202e7072d..35166c147 100644 --- a/projects/app/src/components/ChatBox/Input/ChatInput.tsx +++ b/projects/app/src/components/ChatBox/Input/ChatInput.tsx @@ -51,16 +51,8 @@ const ChatInput = ({ name: 'files' }); - const { - shareId, - outLinkUid, - teamId, - teamToken, - isChatting, - whisperConfig, - autoTTSResponse, - chatInputGuide - } = useContextSelector(ChatBoxContext, (v) => v); + const { isChatting, whisperConfig, autoTTSResponse, chatInputGuide, outLinkAuthData } = + useContextSelector(ChatBoxContext, (v) => v); const { isPc, whisperModel } = useSystemStore(); const canvasRef = useRef(null); const { t } = useTranslation(); @@ -87,10 +79,7 @@ const ChatInput = ({ maxSize: 1024 * 1024 * 16, // 7 day expired. expiredTime: addDays(new Date(), 7), - shareId, - outLinkUid, - teamId, - teamToken + ...outLinkAuthData }); updateFile(fileIndex, { ...file, @@ -175,7 +164,7 @@ const ChatInput = ({ speakingTimeString, renderAudioGraph, stream - } = useSpeech({ appId, shareId, outLinkUid, teamId, teamToken }); + } = useSpeech({ appId, ...outLinkAuthData }); useEffect(() => { if (!stream) { return; diff --git a/projects/app/src/components/ChatBox/Input/InputGuideBox.tsx b/projects/app/src/components/ChatBox/Input/InputGuideBox.tsx index b1108211f..f5f0bf6a5 100644 --- a/projects/app/src/components/ChatBox/Input/InputGuideBox.tsx +++ b/projects/app/src/components/ChatBox/Input/InputGuideBox.tsx @@ -24,6 +24,7 @@ export default function InputGuideBox({ const { t } = useTranslation(); const { chatT } = useI18n(); const chatInputGuide = useContextSelector(ChatBoxContext, (v) => v.chatInputGuide); + const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData); const { data = [] } = useRequest2( async () => { @@ -31,7 +32,8 @@ export default function InputGuideBox({ return await queryChatInputGuideList( { appId, - searchKey: text.slice(0, 50) + searchKey: text.slice(0, 50), + ...outLinkAuthData }, chatInputGuide.customUrl ? chatInputGuide.customUrl : undefined ); diff --git a/projects/app/src/components/ChatBox/Provider.tsx b/projects/app/src/components/ChatBox/Provider.tsx index 970c0443f..12683a9f5 100644 --- a/projects/app/src/components/ChatBox/Provider.tsx +++ b/projects/app/src/components/ChatBox/Provider.tsx @@ -45,6 +45,7 @@ type useChatStoreType = OutLinkChatAuthProps & { setChatHistories: React.Dispatch>; isChatting: boolean; chatInputGuide: ChatInputGuideConfigType; + outLinkAuthData: OutLinkChatAuthProps; }; export const ChatBoxContext = createContext({ welcomeText: '', @@ -98,7 +99,8 @@ export const ChatBoxContext = createContext({ chatInputGuide: { open: false, customUrl: '' - } + }, + outLinkAuthData: {} }); export type ChatProviderProps = OutLinkChatAuthProps & { @@ -128,6 +130,16 @@ const Provider = ({ chatInputGuide = defaultChatInputGuideConfig } = useMemo(() => chatConfig, [chatConfig]); + const outLinkAuthData = useMemo( + () => ({ + shareId, + outLinkUid, + teamId, + teamToken + }), + [shareId, outLinkUid, teamId, teamToken] + ); + // segment audio const [audioPlayingChatId, setAudioPlayingChatId] = useState(); const { @@ -141,10 +153,7 @@ const Provider = ({ splitText2Audio } = useAudioPlay({ ttsConfig, - shareId, - outLinkUid, - teamId, - teamToken + ...outLinkAuthData }); const autoTTSResponse = @@ -181,7 +190,8 @@ const Provider = ({ chatHistories, setChatHistories, isChatting, - chatInputGuide + chatInputGuide, + outLinkAuthData }; return {children}; diff --git a/projects/app/src/components/ChatBox/components/WholeResponseModal.tsx b/projects/app/src/components/ChatBox/components/WholeResponseModal.tsx index 5e272723d..dbb7dcd06 100644 --- a/projects/app/src/components/ChatBox/components/WholeResponseModal.tsx +++ b/projects/app/src/components/ChatBox/components/WholeResponseModal.tsx @@ -321,6 +321,7 @@ export const ResponseBox = React.memo(function ResponseBox({ {/* code */} + ); diff --git a/projects/app/src/components/Layout/index.tsx b/projects/app/src/components/Layout/index.tsx index 3959fd596..98be4e281 100644 --- a/projects/app/src/components/Layout/index.tsx +++ b/projects/app/src/components/Layout/index.tsx @@ -13,19 +13,10 @@ import Auth from './auth'; const Navbar = dynamic(() => import('./navbar')); const NavbarPhone = dynamic(() => import('./navbarPhone')); -const UpdateInviteModal = dynamic( - () => import('@/components/support/user/team/UpdateInviteModal'), - { ssr: false } -); -const NotSufficientModal = dynamic(() => import('@/components/support/wallet/NotSufficientModal'), { - ssr: false -}); -const SystemMsgModal = dynamic(() => import('@/components/support/user/inform/SystemMsgModal'), { - ssr: false -}); -const ImportantInform = dynamic(() => import('@/components/support/user/inform/ImportantInform'), { - ssr: false -}); +const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal')); +const NotSufficientModal = dynamic(() => import('@/components/support/wallet/NotSufficientModal')); +const SystemMsgModal = dynamic(() => import('@/components/support/user/inform/SystemMsgModal')); +const ImportantInform = dynamic(() => import('@/components/support/user/inform/ImportantInform')); const pcUnShowLayoutRoute: Record = { '/': true, @@ -126,7 +117,7 @@ const Layout = ({ children }: { children: JSX.Element }) => { {feConfigs?.isPlus && ( <> {!!userInfo && } - {isNotSufficientModal && !isHideNavbar && } + {isNotSufficientModal && } {!!userInfo && } {!!userInfo && importantInforms.length > 0 && ( diff --git a/projects/app/src/components/common/NextHead/index.tsx b/projects/app/src/components/common/NextHead/index.tsx index a3f529bc0..823607154 100644 --- a/projects/app/src/components/common/NextHead/index.tsx +++ b/projects/app/src/components/common/NextHead/index.tsx @@ -5,6 +5,10 @@ const NextHead = ({ title, icon, desc }: { title?: string; icon?: string; desc?: return ( {title} + {desc && } {icon && } diff --git a/projects/app/src/components/core/workflow/Flow/hooks/useDebug.tsx b/projects/app/src/components/core/workflow/Flow/hooks/useDebug.tsx index 594a6c0d1..86297caba 100644 --- a/projects/app/src/components/core/workflow/Flow/hooks/useDebug.tsx +++ b/projects/app/src/components/core/workflow/Flow/hooks/useDebug.tsx @@ -145,10 +145,27 @@ export const useDebug = () => { node.nodeId === runtimeNode.nodeId ? { ...runtimeNode, - inputs: runtimeNode.inputs.map((input) => ({ - ...input, - value: data[input.key] ?? input.value - })) + inputs: runtimeNode.inputs.map((input) => { + let parseValue = (() => { + try { + if ( + input.valueType === WorkflowIOValueTypeEnum.string || + input.valueType === WorkflowIOValueTypeEnum.number || + input.valueType === WorkflowIOValueTypeEnum.boolean + ) + return data[input.key]; + + return JSON.parse(data[input.key]); + } catch (e) { + return data[input.key]; + } + })(); + + return { + ...input, + value: parseValue ?? input.value + }; + }) } : node ), @@ -168,7 +185,7 @@ export const useDebug = () => { {renderInputs.map((input) => { const required = input.required || false; - console.log(input.valueType); + const RenderInput = (() => { if (input.valueType === WorkflowIOValueTypeEnum.string) { return ( @@ -206,19 +223,23 @@ export const useDebug = () => { ); } - if (typeof input.value === 'string') { - return ( - { - setValue(input.key, e); - }} - /> - ); + + let value = getValues(input.key) || ''; + if (typeof value !== 'string') { + value = JSON.stringify(value, null, 2); } + + return ( + { + setValue(input.key, e); + }} + /> + ); })(); return !!RenderInput ? ( diff --git a/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/ListItem.tsx b/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/ListItem.tsx index 591071e25..ad602f86f 100644 --- a/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/ListItem.tsx +++ b/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/ListItem.tsx @@ -31,6 +31,7 @@ import { Position, useReactFlow } from 'reactflow'; import { getRefData } from '@/web/core/workflow/utils'; import DragIcon from '@fastgpt/web/components/common/DndDrag/DragIcon'; import { AppContext } from '@/web/core/app/context/appContext'; +import { useI18n } from '@/web/context/I18n'; const ListItem = ({ provided, @@ -415,6 +416,7 @@ const ConditionValueInput = ({ condition?: VariableConditionEnum; onChange: (e: string) => void; }) => { + const { workflowT } = useI18n(); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); // get value type @@ -439,7 +441,7 @@ const ConditionValueInput = ({ ]} onchange={onChange} value={value} - placeholder={'选择值'} + placeholder={workflowT('ifelse.Select value')} isDisabled={ condition === VariableConditionEnum.isEmpty || condition === VariableConditionEnum.isNotEmpty @@ -450,7 +452,11 @@ const ConditionValueInput = ({ return ( ); } - }, [condition, onChange, value, valueType]); + }, [condition, onChange, value, valueType, workflowT]); return Render; }; diff --git a/projects/app/src/components/core/workflow/Flow/nodes/render/RenderInput/templates/Reference.tsx b/projects/app/src/components/core/workflow/Flow/nodes/render/RenderInput/templates/Reference.tsx index 6d0c87095..e947e866d 100644 --- a/projects/app/src/components/core/workflow/Flow/nodes/render/RenderInput/templates/Reference.tsx +++ b/projects/app/src/components/core/workflow/Flow/nodes/render/RenderInput/templates/Reference.tsx @@ -42,7 +42,7 @@ const Reference = ({ item, nodeId }: RenderInputProps) => { const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const onSelect = useCallback( - (e: any) => { + (e: ReferenceValueProps) => { const workflowStartNode = nodeList.find( (node) => node.flowNodeType === FlowNodeTypeEnum.workflowStart ); diff --git a/projects/app/src/components/core/workflow/Flow/nodes/render/RenderOutput/index.tsx b/projects/app/src/components/core/workflow/Flow/nodes/render/RenderOutput/index.tsx index ee766c62a..0cb05758a 100644 --- a/projects/app/src/components/core/workflow/Flow/nodes/render/RenderOutput/index.tsx +++ b/projects/app/src/components/core/workflow/Flow/nodes/render/RenderOutput/index.tsx @@ -16,7 +16,7 @@ import { WorkflowContext } from '@/components/core/workflow/context'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; const RenderList: { - types: `${FlowNodeOutputTypeEnum}`[]; + types: FlowNodeOutputTypeEnum[]; Component: React.ComponentType; }[] = []; diff --git a/projects/app/src/pages/api/admin/initv481.ts b/projects/app/src/pages/api/admin/initv481.ts index 47fdbd394..ca370235b 100644 --- a/projects/app/src/pages/api/admin/initv481.ts +++ b/projects/app/src/pages/api/admin/initv481.ts @@ -1,12 +1,8 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { PgClient } from '@fastgpt/service/common/vectorStore/pg'; import { NextAPI } from '@/service/middleware/entry'; -import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants'; import { connectionMongo } from '@fastgpt/service/common/mongo'; -import { addLog } from '@fastgpt/service/common/system/log'; /* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ async function handler(req: NextApiRequest, res: NextApiResponse) { diff --git a/projects/app/src/pages/api/core/app/questionGuides/import.ts b/projects/app/src/pages/api/core/app/questionGuides/import.ts deleted file mode 100644 index da6abb16d..000000000 --- a/projects/app/src/pages/api/core/app/questionGuides/import.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; -import { NextApiRequest, NextApiResponse } from 'next'; -import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema'; -import axios from 'axios'; -import { NextAPI } from '@/service/middleware/entry'; - -async function handler(req: NextApiRequest, res: NextApiResponse) { - const { textList = [], appId, customUrl } = req.body; - - if (!customUrl) { - const { teamId } = await authUserNotVisitor({ req, authToken: true }); - - const currentQGuide = await MongoChatInputGuide.find({ appId, teamId }); - const currentTexts = currentQGuide.map((item) => item.text); - const textsToDelete = currentTexts.filter((text) => !textList.includes(text)); - - await MongoChatInputGuide.deleteMany({ text: { $in: textsToDelete }, appId, teamId }); - - const newTexts = textList.filter((text: string) => !currentTexts.includes(text)); - - const newDocuments = newTexts.map((text: string) => ({ - text: text, - appId: appId, - teamId: teamId - })); - - await MongoChatInputGuide.insertMany(newDocuments); - } else { - try { - const response = await axios.post(customUrl, { - textList, - appId - }); - res.status(200).json(response.data); - } catch (error) { - res.status(500).json({ error }); - } - } -} - -export default NextAPI(handler); diff --git a/projects/app/src/pages/api/core/chat/inputGuide/query.ts b/projects/app/src/pages/api/core/chat/inputGuide/query.ts index a6ff75437..4afca44e1 100644 --- a/projects/app/src/pages/api/core/chat/inputGuide/query.ts +++ b/projects/app/src/pages/api/core/chat/inputGuide/query.ts @@ -2,21 +2,29 @@ import type { NextApiResponse } from 'next'; import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema'; import { NextAPI } from '@/service/middleware/entry'; import { ApiRequestProps } from '@fastgpt/service/type/next'; -import { authApp } from '@fastgpt/service/support/permission/auth/app'; +import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat'; +import { authChatCert } from '@/service/support/permission/auth/chat'; +import { MongoApp } from '@fastgpt/service/core/app/schema'; +import { AppErrEnum } from '@fastgpt/global/common/error/code/app'; -export type QueryChatInputGuideProps = { +export type QueryChatInputGuideBody = OutLinkChatAuthProps & { appId: string; searchKey: string; }; export type QueryChatInputGuideResponse = string[]; async function handler( - req: ApiRequestProps<{}, QueryChatInputGuideProps>, + req: ApiRequestProps, res: NextApiResponse ): Promise { - const { appId, searchKey } = req.query; + const { appId, searchKey } = req.body; - await authApp({ req, appId, authToken: true, authApiKey: true, per: 'r' }); + // tmp auth + const { teamId } = await authChatCert({ req, authToken: true }); + const app = await MongoApp.findOne({ _id: appId, teamId }); + if (!app) { + return Promise.reject(AppErrEnum.unAuthApp); + } const params = { appId, diff --git a/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx b/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx index de6831b4a..e069e763d 100644 --- a/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx +++ b/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx @@ -31,26 +31,17 @@ import { useI18n } from '@/web/context/I18n'; import { useContextSelector } from 'use-context-selector'; import { AppContext } from '@/web/core/app/context/appContext'; -const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'), { - ssr: false -}); -const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'), { - ssr: false -}); -const ToolSelectModal = dynamic(() => import('./ToolSelectModal'), { ssr: false }); -const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect'), { ssr: false }); -const QGSwitch = dynamic(() => import('@/components/core/app/QGSwitch'), { ssr: false }); -const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig'), { ssr: false }); -const InputGuideConfig = dynamic(() => import('@/components/core/app/InputGuideConfig'), { - ssr: false -}); +const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal')); +const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal')); +const ToolSelectModal = dynamic(() => import('./ToolSelectModal')); +const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect')); +const QGSwitch = dynamic(() => import('@/components/core/app/QGSwitch')); +const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig')); +const InputGuideConfig = dynamic(() => import('@/components/core/app/InputGuideConfig')); const ScheduledTriggerConfig = dynamic( - () => import('@/components/core/app/ScheduledTriggerConfig'), - { ssr: false } + () => import('@/components/core/app/ScheduledTriggerConfig') ); -const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig'), { - ssr: false -}); +const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig')); const BoxStyles: BoxProps = { px: 5, diff --git a/projects/app/src/pages/chat/components/ChatHeader.tsx b/projects/app/src/pages/chat/components/ChatHeader.tsx index 42eb0b0dd..3022fa2b9 100644 --- a/projects/app/src/pages/chat/components/ChatHeader.tsx +++ b/projects/app/src/pages/chat/components/ChatHeader.tsx @@ -15,16 +15,16 @@ const ChatHeader = ({ appName, appAvatar, chatModels, - appId, showHistory, + onRoute2AppDetail, onOpenSlider }: { history: ChatItemType[]; appName: string; appAvatar: string; chatModels?: string[]; - appId?: string; showHistory?: boolean; + onRoute2AppDetail?: () => void; onOpenSlider: () => void; }) => { const router = useRouter(); @@ -80,13 +80,7 @@ const ChatHeader = ({ - { - appId && router.push(`/app/detail?appId=${appId}`); - }} - > + {appName} diff --git a/projects/app/src/pages/chat/index.tsx b/projects/app/src/pages/chat/index.tsx index 565cf0238..e06fa8d21 100644 --- a/projects/app/src/pages/chat/index.tsx +++ b/projects/app/src/pages/chat/index.tsx @@ -335,10 +335,10 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => { router.push(`/app/detail?appId=${appId}`)} showHistory /> diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx index 8de038c46..a1ab3865c 100644 --- a/projects/app/src/pages/chat/share.tsx +++ b/projects/app/src/pages/chat/share.tsx @@ -378,7 +378,6 @@ const OutLink = ({ history={chatData.history} showHistory={showHistory === '1'} onOpenSlider={onOpenSlider} - appId={chatData.appId} /> {/* chat box */} diff --git a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx index e0987b657..bfefd2c8c 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useRef, useState } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import { Box, Flex, @@ -17,7 +17,7 @@ import { import MyIcon from '@fastgpt/web/components/common/Icon'; import { useTranslation } from 'next-i18next'; import LeftRadio from '@fastgpt/web/components/common/Radio/LeftRadio'; -import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; +import { TrainingModeEnum, TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; import { ImportProcessWayEnum } from '@/web/core/dataset/constants'; import MyTooltip from '@/components/MyTooltip'; import { useSystemStore } from '@/web/common/system/useSystemStore'; @@ -27,6 +27,7 @@ import Preview from '../components/Preview'; import Tag from '@fastgpt/web/components/common/Tag/index'; import { useContextSelector } from 'use-context-selector'; import { DatasetImportContext } from '../Context'; +import { useToast } from '@fastgpt/web/hooks/useToast'; function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean }) { const { t } = useTranslation(); @@ -42,8 +43,10 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean maxChunkSize, priceTip } = useContextSelector(DatasetImportContext, (v) => v); - const { getValues, setValue, register } = processParamsForm; - const [refresh, setRefresh] = useState(false); + const { getValues, setValue, register, watch } = processParamsForm; + const { toast } = useToast(); + const mode = watch('mode'); + const way = watch('way'); const { isOpen: isOpenCustomPrompt, @@ -53,12 +56,21 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean const trainingModeList = useMemo(() => { const list = Object.entries(TrainingTypeMap); + return list; + }, []); - return list.filter(([key, value]) => { - if (feConfigs?.isPlus) return true; - return value.openSource; - }); - }, [feConfigs?.isPlus]); + const onSelectTrainWay = useCallback( + (e: TrainingModeEnum) => { + if (!feConfigs?.isPlus && !TrainingTypeMap[e]?.openSource) { + return toast({ + status: 'warning', + title: t('common.system.Commercial version function') + }); + } + setValue('mode', e); + }, + [feConfigs?.isPlus, setValue, t, toast] + ); return ( @@ -80,11 +92,8 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean }))} px={3} py={2} - value={getValues('mode')} - onChange={(e) => { - setValue('mode', e); - setRefresh(!refresh); - }} + value={mode} + onChange={onSelectTrainWay} gridTemplateColumns={'repeat(3,1fr)'} defaultBg="white" activeBg="white" @@ -105,7 +114,7 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean title: t('core.dataset.import.Custom process'), desc: t('core.dataset.import.Custom process desc'), value: ImportProcessWayEnum.custom, - children: getValues('way') === ImportProcessWayEnum.custom && ( + children: way === ImportProcessWayEnum.custom && ( {showChunkInput && chunkSizeField && ( @@ -250,11 +259,10 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean py={3} defaultBg="white" activeBg="white" - value={getValues('way')} + value={way} w={'100%'} onChange={(e) => { setValue('way', e); - setRefresh(!refresh); }} > @@ -286,7 +294,6 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean defaultValue={getValues('qaPrompt')} onChange={(e) => { setValue('qaPrompt', e); - setRefresh(!refresh); }} onClose={onCloseCustomPrompt} /> diff --git a/projects/app/src/pages/dataset/list/component/CreateModal.tsx b/projects/app/src/pages/dataset/list/component/CreateModal.tsx index bdf742a3d..4cb46ae79 100644 --- a/projects/app/src/pages/dataset/list/component/CreateModal.tsx +++ b/projects/app/src/pages/dataset/list/component/CreateModal.tsx @@ -18,21 +18,19 @@ import MyRadio from '@/components/common/MyRadio'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; import { QuestionOutlineIcon } from '@chakra-ui/icons'; -import MySelect from '@fastgpt/web/components/common/MySelect'; import AIModelSelector from '@/components/Select/AIModelSelector'; import { useI18n } from '@/web/context/I18n'; const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: string }) => { const { t } = useTranslation(); const { datasetT } = useI18n(); - const [refresh, setRefresh] = useState(false); const { toast } = useToast(); const router = useRouter(); const { isPc, feConfigs, vectorModelList, datasetModelList } = useSystemStore(); const filterNotHiddenVectorModelList = vectorModelList.filter((item) => !item.hidden); - const { register, setValue, getValues, handleSubmit } = useForm({ + const { register, setValue, handleSubmit, watch } = useForm({ defaultValues: { parentId, type: DatasetTypeEnum.dataset, @@ -43,6 +41,10 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st agentModel: datasetModelList[0].model } }); + const avatar = watch('avatar'); + const datasetType = watch('type'); + const vectorModel = watch('vectorModel'); + const agentModel = watch('agentModel'); const { File, onOpen: onOpenSelectFile } = useSelectFile({ fileType: '.jpg,.png', @@ -61,7 +63,6 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st maxH: 300 }); setValue('avatar', src); - setRefresh((state) => !state); } catch (err: any) { toast({ title: getErrText(err, t('common.avatar.Select Failed')), @@ -85,6 +86,22 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st } }); + const onSelectDatasetType = useCallback( + (e: DatasetTypeEnum) => { + if ( + !feConfigs?.isPlus && + (e === DatasetTypeEnum.websiteDataset || e === DatasetTypeEnum.externalFile) + ) { + return toast({ + status: 'warning', + title: t('common.system.Commercial version function') + }); + } + setValue('type', e); + }, + [feConfigs?.isPlus, setValue, t, toast] + ); + return ( void; parentId?: st icon: 'core/dataset/commonDataset', desc: datasetT('Common Dataset Desc') }, - ...(feConfigs.isPlus - ? [ - { - title: datasetT('Website Dataset'), - value: DatasetTypeEnum.websiteDataset, - icon: 'core/dataset/websiteDataset', - desc: datasetT('Website Dataset Desc') - }, - { - title: datasetT('External File'), - value: DatasetTypeEnum.externalFile, - icon: 'core/dataset/externalDataset', - desc: datasetT('External file Dataset Desc') - } - ] - : []) + { + title: datasetT('Website Dataset'), + value: DatasetTypeEnum.websiteDataset, + icon: 'core/dataset/websiteDataset', + desc: datasetT('Website Dataset Desc') + }, + { + title: datasetT('External File'), + value: DatasetTypeEnum.externalFile, + icon: 'core/dataset/externalDataset', + desc: datasetT('External file Dataset Desc') + } ]} - value={getValues('type')} - onChange={(e) => { - setValue('type', e as DatasetTypeEnum); - setRefresh(!refresh); - }} + value={datasetType} + onChange={onSelectDatasetType} /> @@ -141,7 +151,7 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st void; parentId?: st ({ label: item.name, value: item.model }))} onchange={(e) => { setValue('vectorModel', e); - setRefresh((state) => !state); }} /> @@ -192,14 +201,13 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st ({ label: item.name, value: item.model }))} onchange={(e) => { setValue('agentModel', e); - setRefresh((state) => !state); }} /> diff --git a/projects/app/src/pages/login/fastlogin.tsx b/projects/app/src/pages/login/fastlogin.tsx index 10dbf5bbc..2a207c7be 100644 --- a/projects/app/src/pages/login/fastlogin.tsx +++ b/projects/app/src/pages/login/fastlogin.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useEffect } from 'react'; import { useRouter } from 'next/router'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; import type { ResLogin } from '@/global/support/api/userRes.d'; import { useChatStore } from '@/web/core/chat/storeChat'; import { useUserStore } from '@/web/support/user/useUserStore'; @@ -9,7 +8,6 @@ import { postFastLogin } from '@/web/support/user/api'; import { useToast } from '@fastgpt/web/hooks/useToast'; import Loading from '@fastgpt/web/components/common/MyLoading'; import { serviceSideProps } from '@/web/common/utils/i18n'; -import { useQuery } from '@tanstack/react-query'; import { getErrText } from '@fastgpt/global/common/error/utils'; const FastLogin = ({ diff --git a/projects/app/src/web/core/app/templates.ts b/projects/app/src/web/core/app/templates.ts index b26c2bad8..0229e6f83 100644 --- a/projects/app/src/web/core/app/templates.ts +++ b/projects/app/src/web/core/app/templates.ts @@ -3,6 +3,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; import { FlowNodeInputTypeEnum, + FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; @@ -109,7 +110,7 @@ export const appTemplates: (AppItemType & { key: 'userChatInput', label: 'core.module.input.label.user question', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }, @@ -220,7 +221,7 @@ export const appTemplates: (AppItemType & { label: 'core.module.output.label.New context', description: 'core.module.output.description.New context', valueType: WorkflowIOValueTypeEnum.chatHistory, - type: 'static' + type: FlowNodeOutputTypeEnum.static }, { id: 'answerText', @@ -228,7 +229,7 @@ export const appTemplates: (AppItemType & { label: 'core.module.output.label.Ai response content', description: 'core.module.output.description.Ai response content', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] } @@ -356,7 +357,7 @@ export const appTemplates: (AppItemType & { key: 'userChatInput', label: 'core.module.input.label.user question', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }, @@ -467,7 +468,7 @@ export const appTemplates: (AppItemType & { label: 'core.module.output.label.New context', description: 'core.module.output.description.New context', valueType: WorkflowIOValueTypeEnum.chatHistory, - type: 'static' + type: FlowNodeOutputTypeEnum.static }, { id: 'answerText', @@ -475,7 +476,7 @@ export const appTemplates: (AppItemType & { label: 'core.module.output.label.Ai response content', description: 'core.module.output.description.Ai response content', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] } @@ -586,7 +587,7 @@ export const appTemplates: (AppItemType & { key: 'userChatInput', label: 'core.module.input.label.user question', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }, @@ -698,7 +699,7 @@ export const appTemplates: (AppItemType & { label: 'core.module.output.label.New context', description: 'core.module.output.description.New context', valueType: WorkflowIOValueTypeEnum.chatHistory, - type: 'static' + type: FlowNodeOutputTypeEnum.static }, { id: 'answerText', @@ -706,7 +707,7 @@ export const appTemplates: (AppItemType & { label: 'core.module.output.label.Ai response content', description: 'core.module.output.description.Ai response content', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }, @@ -795,7 +796,7 @@ export const appTemplates: (AppItemType & { id: 'quoteQA', key: 'quoteQA', label: 'core.module.Dataset quote.label', - type: 'static', + type: FlowNodeOutputTypeEnum.static, valueType: WorkflowIOValueTypeEnum.datasetQuote, description: '特殊数组格式,搜索结果为空时,返回空数组。' } @@ -914,7 +915,7 @@ export const appTemplates: (AppItemType & { key: 'userChatInput', label: 'core.module.input.label.user question', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }, @@ -1026,7 +1027,7 @@ export const appTemplates: (AppItemType & { label: 'core.module.output.label.New context', description: 'core.module.output.description.New context', valueType: WorkflowIOValueTypeEnum.chatHistory, - type: 'static' + type: FlowNodeOutputTypeEnum.static }, { id: 'answerText', @@ -1034,7 +1035,7 @@ export const appTemplates: (AppItemType & { label: 'core.module.output.label.Ai response content', description: 'core.module.output.description.Ai response content', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }, @@ -1116,7 +1117,7 @@ export const appTemplates: (AppItemType & { key: 'cqResult', label: '分类结果', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }, @@ -1232,7 +1233,7 @@ export const appTemplates: (AppItemType & { key: 'quoteQA', label: 'core.module.Dataset quote.label', description: '特殊数组格式,搜索结果为空时,返回空数组。', - type: 'static', + type: FlowNodeOutputTypeEnum.static, valueType: WorkflowIOValueTypeEnum.datasetQuote } ] diff --git a/projects/app/src/web/core/app/utils.ts b/projects/app/src/web/core/app/utils.ts index 959cbd2c2..c3b21d93f 100644 --- a/projects/app/src/web/core/app/utils.ts +++ b/projects/app/src/web/core/app/utils.ts @@ -7,6 +7,7 @@ import { import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/index.d'; import { FlowNodeInputTypeEnum, + FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; @@ -65,7 +66,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType { key: 'userChatInput', label: 'core.module.input.label.user question', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }; @@ -181,7 +182,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType { label: 'core.module.output.label.New context', description: 'core.module.output.description.New context', valueType: WorkflowIOValueTypeEnum.chatHistory, - type: 'static' + type: FlowNodeOutputTypeEnum.static }, { id: 'answerText', @@ -189,7 +190,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType { label: 'core.module.output.label.Ai response content', description: 'core.module.output.description.Ai response content', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] } @@ -315,7 +316,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType { label: 'core.module.output.label.New context', description: 'core.module.output.description.New context', valueType: WorkflowIOValueTypeEnum.chatHistory, - type: 'static' + type: FlowNodeOutputTypeEnum.static }, { id: 'answerText', @@ -323,7 +324,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType { label: 'core.module.output.label.Ai response content', description: 'core.module.output.description.Ai response content', valueType: WorkflowIOValueTypeEnum.string, - type: 'static' + type: FlowNodeOutputTypeEnum.static } ] }, @@ -416,7 +417,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType { id: 'quoteQA', key: 'quoteQA', label: 'core.module.Dataset quote.label', - type: 'static', + type: FlowNodeOutputTypeEnum.static, valueType: WorkflowIOValueTypeEnum.datasetQuote } ] @@ -537,7 +538,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType { id: 'quoteQA', key: 'quoteQA', label: 'core.module.Dataset quote.label', - type: 'static', + type: FlowNodeOutputTypeEnum.static, valueType: WorkflowIOValueTypeEnum.datasetQuote } ] diff --git a/projects/app/src/web/core/chat/inputGuide/api.ts b/projects/app/src/web/core/chat/inputGuide/api.ts index a1a53ae53..c65521a2e 100644 --- a/projects/app/src/web/core/chat/inputGuide/api.ts +++ b/projects/app/src/web/core/chat/inputGuide/api.ts @@ -14,7 +14,7 @@ import type { import type { updateInputGuideBody } from '@/pages/api/core/chat/inputGuide/update'; import type { deleteChatInputGuideQuery } from '@/pages/api/core/chat/inputGuide/delete'; import type { - QueryChatInputGuideProps, + QueryChatInputGuideBody, QueryChatInputGuideResponse } from '@/pages/api/core/chat/inputGuide/query'; @@ -26,10 +26,13 @@ export const getCountChatInputGuideTotal = (data: countChatInputGuideTotalQuery) export const getChatInputGuideList = (data: ChatInputGuideProps) => GET(`/core/chat/inputGuide/list`, data); -export const queryChatInputGuideList = (data: QueryChatInputGuideProps, url?: string) => { - return GET(url ?? `/core/chat/inputGuide/query`, data, { - withCredentials: !url - }); +export const queryChatInputGuideList = (data: QueryChatInputGuideBody, url?: string) => { + if (url) { + return GET(url, data, { + withCredentials: !url + }); + } + return POST(`/core/chat/inputGuide/query`, data); }; export const postChatInputGuides = (data: createInputGuideBody) => diff --git a/projects/app/src/web/core/workflow/adapt.ts b/projects/app/src/web/core/workflow/adapt.ts index 27fcf3ddc..2985e1f48 100644 --- a/projects/app/src/web/core/workflow/adapt.ts +++ b/projects/app/src/web/core/workflow/adapt.ts @@ -167,7 +167,7 @@ type V1WorkflowType = { }; defaultEditField?: { inputType?: InputTypeEnum; // input type - outputType?: `${FlowNodeOutputTypeEnum}`; + outputType?: FlowNodeOutputTypeEnum; required?: boolean; key?: string; label?: string; @@ -219,7 +219,7 @@ type V1WorkflowType = { }; defaultEditField?: { inputType?: `${FlowNodeInputTypeEnum}`; // input type - outputType?: `${FlowNodeOutputTypeEnum}`; + outputType?: FlowNodeOutputTypeEnum; required?: boolean; key?: string; label?: string; diff --git a/projects/app/src/web/core/workflow/api.ts b/projects/app/src/web/core/workflow/api.ts index 88e1d330e..9b07d8ca8 100644 --- a/projects/app/src/web/core/workflow/api.ts +++ b/projects/app/src/web/core/workflow/api.ts @@ -2,7 +2,13 @@ import { GET, POST, PUT, DELETE } from '@/web/common/api/request'; import { PostWorkflowDebugProps, PostWorkflowDebugResponse } from '@/global/core/workflow/api'; export const postWorkflowDebug = (data: PostWorkflowDebugProps) => - POST('/core/workflow/debug', { - ...data, - mode: 'debug' - }); + POST( + '/core/workflow/debug', + { + ...data, + mode: 'debug' + }, + { + timeout: 300000 + } + ); diff --git a/projects/sandbox/package.json b/projects/sandbox/package.json index 2d7d4a342..4bc5bf16b 100644 --- a/projects/sandbox/package.json +++ b/projects/sandbox/package.json @@ -1,6 +1,6 @@ { "name": "sandbox", - "version": "0.0.1", + "version": "4.8.3", "description": "", "author": "", "private": true, diff --git a/projects/sandbox/src/http-exception.filter.ts b/projects/sandbox/src/http-exception.filter.ts index 8e2204536..dc5c168f5 100644 --- a/projects/sandbox/src/http-exception.filter.ts +++ b/projects/sandbox/src/http-exception.filter.ts @@ -12,7 +12,7 @@ export class HttpExceptionFilter implements ExceptionFilter { response.status(500).send({ success: false, time: new Date(), - msg: getErrText(error) + message: getErrText(error) }); } } diff --git a/projects/sandbox/src/sandbox/dto/create-sandbox.dto.ts b/projects/sandbox/src/sandbox/dto/create-sandbox.dto.ts index 5965007c4..4d04c4cec 100644 --- a/projects/sandbox/src/sandbox/dto/create-sandbox.dto.ts +++ b/projects/sandbox/src/sandbox/dto/create-sandbox.dto.ts @@ -2,3 +2,8 @@ export class RunCodeDto { code: string; variables: object; } + +export class RunCodeResponse { + codeReturn: Record; + log: string; +} diff --git a/projects/sandbox/src/sandbox/sandbox.controller.ts b/projects/sandbox/src/sandbox/sandbox.controller.ts index 45032e4a0..bd865b943 100644 --- a/projects/sandbox/src/sandbox/sandbox.controller.ts +++ b/projects/sandbox/src/sandbox/sandbox.controller.ts @@ -1,6 +1,6 @@ import { Controller, Post, Body, HttpCode } from '@nestjs/common'; import { SandboxService } from './sandbox.service'; -import { RunCodeDto } from './dto/create-sandbox.dto'; +import { RunCodeDto, RunCodeResponse } from './dto/create-sandbox.dto'; import { WorkerNameEnum, runWorker } from 'src/worker/utils'; @Controller('sandbox') @@ -10,6 +10,6 @@ export class SandboxController { @Post('/js') @HttpCode(200) runJs(@Body() codeProps: RunCodeDto) { - return runWorker(WorkerNameEnum.runJs, codeProps); + return runWorker(WorkerNameEnum.runJs, codeProps); } } diff --git a/projects/sandbox/src/worker/runJs.ts b/projects/sandbox/src/worker/runJs.ts index 4d1c2b095..8ea6e2b87 100644 --- a/projects/sandbox/src/worker/runJs.ts +++ b/projects/sandbox/src/worker/runJs.ts @@ -1,4 +1,4 @@ -import { RunCodeDto } from 'src/sandbox/dto/create-sandbox.dto'; +import { RunCodeDto, RunCodeResponse } from 'src/sandbox/dto/create-sandbox.dto'; import { parentPort } from 'worker_threads'; import { workerResponse } from './utils'; @@ -6,19 +6,33 @@ import { workerResponse } from './utils'; const ivm = require('isolated-vm'); parentPort?.on('message', ({ code, variables = {} }: RunCodeDto) => { - const resolve = (data: any) => workerResponse({ parentPort, type: 'success', data }); + const resolve = (data: RunCodeResponse) => workerResponse({ parentPort, type: 'success', data }); const reject = (error: any) => workerResponse({ parentPort, type: 'error', data: error }); const isolate = new ivm.Isolate({ memoryLimit: 32 }); const context = isolate.createContextSync(); const jail = context.global; - // custom log function + // custom function + const logData = []; + const CustomLogStr = 'CUSTOM_LOG'; + code = code.replace(/console\.log/g, `${CustomLogStr}`); + jail.setSync(CustomLogStr, function (...args) { + logData.push( + args + .map((item) => (typeof item === 'object' ? JSON.stringify(item, null, 2) : item)) + .join(', ') + ); + }); + jail.setSync('responseData', function (args: any): any { if (typeof args === 'object') { - resolve(args); + resolve({ + codeReturn: args, + log: logData.join('\n') + }); } else { - reject('Not an invalid response'); + reject('Not an invalid response, must return an object'); } });