Compare commits
39 Commits
v4.8.22
...
v4.8.23-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d4776b3aa | ||
|
|
2d351c3654 | ||
|
|
662a4a4671 | ||
|
|
3fadabd28b | ||
|
|
dbf25cef88 | ||
|
|
b2e2fa6b76 | ||
|
|
576c60bd55 | ||
|
|
33617ab5dc | ||
|
|
b4dda6a41b | ||
|
|
e860c56b77 | ||
|
|
efac5312b4 | ||
|
|
4bc7f21182 | ||
|
|
113e8f711f | ||
|
|
abc6dffb41 | ||
|
|
f7b2a57ca3 | ||
|
|
cf0aaa1091 | ||
|
|
ac4255ea0c | ||
|
|
df4d6f86ce | ||
|
|
e697fda82f | ||
|
|
1aa319e7aa | ||
|
|
fc9e614f88 | ||
|
|
1121ea33bd | ||
|
|
9bbee60cde | ||
|
|
9f57ad0017 | ||
|
|
c3d3b30d7e | ||
|
|
fb0eb49196 | ||
|
|
27ebd2e8cf | ||
|
|
81a06718d8 | ||
|
|
3c382d1240 | ||
|
|
747bb303ec | ||
|
|
cf9c8e9f6a | ||
|
|
5d5bee9e41 | ||
|
|
4f0dd96699 | ||
|
|
fb6dbaf2d6 | ||
|
|
ffc1520f4c | ||
|
|
255764400f | ||
|
|
3bfe802c48 | ||
|
|
2bf17dbb87 | ||
|
|
8d766372fe |
2
.github/workflows/docs-deploy-kubeconfig.yml
vendored
2
.github/workflows/docs-deploy-kubeconfig.yml
vendored
@@ -6,8 +6,6 @@ on:
|
|||||||
- 'docSite/**'
|
- 'docSite/**'
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-fastgpt-docs-images:
|
build-fastgpt-docs-images:
|
||||||
|
|||||||
2
.github/workflows/docs-deploy-vercel.yml
vendored
2
.github/workflows/docs-deploy-vercel.yml
vendored
@@ -7,8 +7,6 @@ on:
|
|||||||
- 'docSite/**'
|
- 'docSite/**'
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
|
|
||||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
2
.github/workflows/docs-preview.yml
vendored
2
.github/workflows/docs-preview.yml
vendored
@@ -4,8 +4,6 @@ on:
|
|||||||
pull_request_target:
|
pull_request_target:
|
||||||
paths:
|
paths:
|
||||||
- 'docSite/**'
|
- 'docSite/**'
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
driver-opts: network=host
|
driver-opts: network=host
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/.buildx-cache
|
path: /tmp/.buildx-cache
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
@@ -108,7 +108,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
driver-opts: network=host
|
driver-opts: network=host
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/.buildx-cache
|
path: /tmp/.buildx-cache
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
@@ -191,7 +191,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
driver-opts: network=host
|
driver-opts: network=host
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/.buildx-cache
|
path: /tmp/.buildx-cache
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
@@ -68,14 +68,3 @@ jobs:
|
|||||||
SEALOS_TYPE: 'pr_comment'
|
SEALOS_TYPE: 'pr_comment'
|
||||||
SEALOS_FILENAME: 'report.md'
|
SEALOS_FILENAME: 'report.md'
|
||||||
SEALOS_REPLACE_TAG: 'DEFAULT_REPLACE_DEPLOY'
|
SEALOS_REPLACE_TAG: 'DEFAULT_REPLACE_DEPLOY'
|
||||||
|
|
||||||
helm-check:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Helm Check
|
|
||||||
run: |
|
|
||||||
helm dependency update files/helm/fastgpt
|
|
||||||
helm lint files/helm/fastgpt
|
|
||||||
helm package files/helm/fastgpt
|
|
||||||
4
.github/workflows/helm-release.yaml
vendored
4
.github/workflows/helm-release.yaml
vendored
@@ -24,6 +24,6 @@ jobs:
|
|||||||
export APP_VERSION=${{ steps.vars.outputs.tag }}
|
export APP_VERSION=${{ steps.vars.outputs.tag }}
|
||||||
export HELM_VERSION=${{ steps.vars.outputs.tag }}
|
export HELM_VERSION=${{ steps.vars.outputs.tag }}
|
||||||
export HELM_REPO=ghcr.io/${{ github.repository_owner }}
|
export HELM_REPO=ghcr.io/${{ github.repository_owner }}
|
||||||
helm dependency update files/helm/fastgpt
|
helm dependency update deploy/helm/fastgpt
|
||||||
helm package files/helm/fastgpt --version ${HELM_VERSION}-helm --app-version ${APP_VERSION} -d bin
|
helm package deploy/helm/fastgpt --version ${HELM_VERSION}-helm --app-version ${APP_VERSION} -d bin
|
||||||
helm push bin/fastgpt-${HELM_VERSION}-helm.tgz oci://${HELM_REPO}
|
helm push bin/fastgpt-${HELM_VERSION}-helm.tgz oci://${HELM_REPO}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
driver-opts: network=host
|
driver-opts: network=host
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/.buildx-cache
|
path: /tmp/.buildx-cache
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
2
.vscode/nextapi.code-snippets
vendored
2
.vscode/nextapi.code-snippets
vendored
@@ -58,7 +58,7 @@
|
|||||||
"body": [
|
"body": [
|
||||||
"import '@/pages/api/__mocks__/base';",
|
"import '@/pages/api/__mocks__/base';",
|
||||||
"import { root } from '@/pages/api/__mocks__/db/init';",
|
"import { root } from '@/pages/api/__mocks__/db/init';",
|
||||||
"import { getTestRequest } from '@/test/utils';",
|
"import { getTestRequest } from '@fastgpt/service/test/utils'; ;",
|
||||||
"import { AppErrEnum } from '@fastgpt/global/common/error/code/app';",
|
"import { AppErrEnum } from '@fastgpt/global/common/error/code/app';",
|
||||||
"import handler from './demo';",
|
"import handler from './demo';",
|
||||||
"",
|
"",
|
||||||
|
|||||||
26
SECURITY.md
Normal file
26
SECURITY.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# 安全策略
|
||||||
|
|
||||||
|
## 漏洞报告
|
||||||
|
|
||||||
|
如果您发现了 FastGPT 的安全漏洞,请按照以下步骤进行报告:
|
||||||
|
|
||||||
|
1. **报告方式**
|
||||||
|
发送邮件至:yujinlong@sealos.io
|
||||||
|
请备注版本以及您的 GitHub 账号
|
||||||
|
|
||||||
|
3. **响应时间**
|
||||||
|
- 我们会在 48 小时内确认收到您的报告
|
||||||
|
- 一般在 3 个工作日内给出初步评估结果
|
||||||
|
|
||||||
|
4. **漏洞处理流程**
|
||||||
|
- 确认漏洞:我们会验证漏洞的存在性和影响范围
|
||||||
|
- 修复开发:针对已确认的漏洞进行修复
|
||||||
|
- 版本发布:在下一个版本更新中发布安全补丁
|
||||||
|
- 公开披露:在修复完成后,我们会在更新日志中公布相关信息
|
||||||
|
|
||||||
|
5. **注意事项**
|
||||||
|
- 在漏洞未修复前,请勿公开披露漏洞详情
|
||||||
|
- 我们欢迎负责任的漏洞披露
|
||||||
|
- 对于重大贡献者,我们会在项目致谢名单中提及
|
||||||
|
|
||||||
|
感谢您为 FastGPT 的安全性做出贡献!
|
||||||
@@ -114,15 +114,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.21-fix # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.21-fix # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.21-fix # git
|
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.21-fix # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -133,7 +133,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
@@ -72,15 +72,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.21-fix # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.21-fix # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.21-fix # git
|
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.21-fix # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -91,7 +91,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
@@ -53,15 +53,15 @@ services:
|
|||||||
wait $$!
|
wait $$!
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.21-fix # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.21-fix # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.21-fix # git
|
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.21-fix # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -71,7 +71,7 @@ services:
|
|||||||
- sandbox
|
- sandbox
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
# 前端访问地址: http://localhost:3000
|
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||||
- DEFAULT_ROOT_PSW=1234
|
- DEFAULT_ROOT_PSW=1234
|
||||||
@@ -31,9 +31,9 @@ weight: 920
|
|||||||
|
|
||||||
3 个模型代码分别为:
|
3 个模型代码分别为:
|
||||||
|
|
||||||
1. [https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-base](https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-base)
|
1. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-base](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-base)
|
||||||
2. [https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-large](https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-large)
|
2. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-large](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-large)
|
||||||
3. [https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-v2-m3](https://github.com/labring/FastGPT/tree/main/python/bge-rerank/bge-reranker-v2-m3)
|
3. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-v2-m3](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-v2-m3)
|
||||||
|
|
||||||
### 3. 安装依赖
|
### 3. 安装依赖
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ PDF 是一个相对复杂的文件格式,在 FastGPT 内置的 pdf 解析器
|
|||||||
|
|
||||||
### 1. 按照 Marker
|
### 1. 按照 Marker
|
||||||
|
|
||||||
参考文档 [Marker 安装教程](https://github.com/labring/FastGPT/tree/main/python/pdf-marker),安装 Marker 模型。封装的 API 已经适配了 FastGPT 自定义解析服务。
|
参考文档 [Marker 安装教程](https://github.com/labring/FastGPT/tree/main/plugins/model/pdf-marker),安装 Marker 模型。封装的 API 已经适配了 FastGPT 自定义解析服务。
|
||||||
|
|
||||||
这里介绍快速 Docker 安装的方法:
|
这里介绍快速 Docker 安装的方法:
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ brew install orbstack
|
|||||||
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`,在这个文件夹中依据下载的配置文件运行docker,若作为本地开发使用推荐`docker-compose-pgvector`版本,并且自行拉取并运行`sandbox`和`fastgpt`,并在docker配置文件中注释掉`sandbox`和`fastgpt`的部分
|
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`,在这个文件夹中依据下载的配置文件运行docker,若作为本地开发使用推荐`docker-compose-pgvector`版本,并且自行拉取并运行`sandbox`和`fastgpt`,并在docker配置文件中注释掉`sandbox`和`fastgpt`的部分
|
||||||
|
|
||||||
- [config.json](https://raw.githubusercontent.com/labring/FastGPT/refs/heads/main/projects/app/data/config.json)
|
- [config.json](https://raw.githubusercontent.com/labring/FastGPT/refs/heads/main/projects/app/data/config.json)
|
||||||
- [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker) (注意,不同向量库版本的文件不一样)
|
- [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/deploy/docker) (注意,不同向量库版本的文件不一样)
|
||||||
|
|
||||||
{{% alert icon="🤖" context="success" %}}
|
{{% alert icon="🤖" context="success" %}}
|
||||||
|
|
||||||
@@ -134,11 +134,11 @@ cd fastgpt
|
|||||||
curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data/config.json
|
curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data/config.json
|
||||||
|
|
||||||
# pgvector 版本(测试推荐,简单快捷)
|
# pgvector 版本(测试推荐,简单快捷)
|
||||||
curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/files/docker/docker-compose-pgvector.yml
|
curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-pgvector.yml
|
||||||
# milvus 版本
|
# milvus 版本
|
||||||
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/files/docker/docker-compose-milvus.yml
|
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-milvus.yml
|
||||||
# zilliz 版本
|
# zilliz 版本
|
||||||
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/files/docker/docker-compose-zilliz.yml
|
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-zilliz.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 修改环境变量
|
### 2. 修改环境变量
|
||||||
@@ -201,6 +201,8 @@ docker restart oneapi
|
|||||||
|
|
||||||
在OneApi中添加合适的AI模型渠道。[点击查看相关教程](/docs/development/modelconfig/one-api/)
|
在OneApi中添加合适的AI模型渠道。[点击查看相关教程](/docs/development/modelconfig/one-api/)
|
||||||
|
|
||||||
|
只需要添加模型即可,模板已经配置好了oneapi的连接地址和令牌,无需变更。
|
||||||
|
|
||||||
### 5. 访问 FastGPT
|
### 5. 访问 FastGPT
|
||||||
|
|
||||||
目前可以通过 `ip:3000` 直接访问(注意防火墙)。登录用户名为 `root`,密码为`docker-compose.yml`环境变量里设置的 `DEFAULT_ROOT_PSW`。
|
目前可以通过 `ip:3000` 直接访问(注意防火墙)。登录用户名为 `root`,密码为`docker-compose.yml`环境变量里设置的 `DEFAULT_ROOT_PSW`。
|
||||||
@@ -211,7 +213,7 @@ docker restart oneapi
|
|||||||
|
|
||||||
### 6. 配置模型
|
### 6. 配置模型
|
||||||
|
|
||||||
务必先配置至少一组模型,否则系统无法正常使用。
|
登录FastGPT后,进入模型配置页面,务必先配置至少一个语言模型和一个向量模型,否则系统无法正常使用。
|
||||||
|
|
||||||
[点击查看模型配置教程](/docs/development/modelConfig/intro/)
|
[点击查看模型配置教程](/docs/development/modelConfig/intro/)
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ weight: 744
|
|||||||
|
|
||||||
{{% alert icon=" " context="info" %}}
|
{{% alert icon=" " context="info" %}}
|
||||||
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
|
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
|
||||||
- [Sealos AIProxy](https://hzh.sealos.run/?openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
|
- [Sealos AIProxy](https://cloud.sealos.run/?uid=fnWRt09fZP&openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。
|
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。
|
||||||
@@ -467,4 +467,4 @@ OneAPI 的语言识别接口,无法正确的识别其他模型(会始终识
|
|||||||
"charsPointsPrice": 0
|
"charsPointsPrice": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ SANDBOX_URL=内网地址
|
|||||||
|
|
||||||
## Docker 部署
|
## Docker 部署
|
||||||
|
|
||||||
可以拉取最新 [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker/docker-compose.yml) 文件参考
|
可以拉取最新 [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/deploy/docker/docker-compose.yml) 文件参考
|
||||||
|
|
||||||
1. 新增一个容器 `sandbox`
|
1. 新增一个容器 `sandbox`
|
||||||
2. fastgpt 和 fastgpt-pro(商业版) 容器新增环境变量: `SANDBOX_URL`
|
2. fastgpt 和 fastgpt-pro(商业版) 容器新增环境变量: `SANDBOX_URL`
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: 'V4.8.22(进行中)'
|
title: 'V4.8.22(包含升级脚本)'
|
||||||
description: 'FastGPT V4.8.22 更新说明'
|
description: 'FastGPT V4.8.22 更新说明'
|
||||||
icon: 'upgrade'
|
icon: 'upgrade'
|
||||||
draft: false
|
draft: false
|
||||||
@@ -13,8 +13,8 @@ weight: 802
|
|||||||
|
|
||||||
### 2. 更新镜像:
|
### 2. 更新镜像:
|
||||||
|
|
||||||
- 更新 fastgpt 镜像 tag: v4.8.22-alpha
|
- 更新 fastgpt 镜像 tag: v4.8.22
|
||||||
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.22-alpha
|
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.22
|
||||||
- Sandbox 镜像无需更新
|
- Sandbox 镜像无需更新
|
||||||
|
|
||||||
### 3. 运行升级脚本
|
### 3. 运行升级脚本
|
||||||
|
|||||||
54
docSite/content/zh-cn/docs/development/upgrading/4823.md
Normal file
54
docSite/content/zh-cn/docs/development/upgrading/4823.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
title: 'V4.8.23'
|
||||||
|
description: 'FastGPT V4.8.23 更新说明'
|
||||||
|
icon: 'upgrade'
|
||||||
|
draft: false
|
||||||
|
toc: true
|
||||||
|
weight: 802
|
||||||
|
---
|
||||||
|
|
||||||
|
## 更新指南
|
||||||
|
|
||||||
|
### 1. 做好数据库备份
|
||||||
|
|
||||||
|
### 2. 更新镜像:
|
||||||
|
|
||||||
|
- 更新 fastgpt 镜像 tag: v4.8.23-fix
|
||||||
|
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.23-fix
|
||||||
|
- Sandbox 镜像无需更新
|
||||||
|
|
||||||
|
### 3. 运行升级脚本
|
||||||
|
|
||||||
|
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location --request POST 'https://{{host}}/api/admin/initv4823' \
|
||||||
|
--header 'rootkey: {{rootkey}}' \
|
||||||
|
--header 'Content-Type: application/json'
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本会清理一些知识库脏数据,主要是多余的全文索引。
|
||||||
|
|
||||||
|
## 🚀 新增内容
|
||||||
|
|
||||||
|
1. 增加默认“知识库文本理解模型”配置
|
||||||
|
2. AI proxy V1版,可替换 OneAPI使用,同时提供完整模型调用日志,便于排查问题。
|
||||||
|
3. 增加工单入口支持。
|
||||||
|
|
||||||
|
## ⚙️ 优化
|
||||||
|
|
||||||
|
1. 模型配置表单,增加必填项校验。
|
||||||
|
2. 集合列表数据统计方式,提高大数据量统计性能。
|
||||||
|
3. 优化数学公式,转义 Latex 格式成 Markdown 格式。
|
||||||
|
4. 解析文档图片,图片太大时,自动忽略。
|
||||||
|
5. 时间选择器,当天开始时间自动设0,结束设置设 23:59:59,避免 UI 与实际逻辑偏差。
|
||||||
|
6. 升级 mongoose 库版本依赖。
|
||||||
|
|
||||||
|
## 🐛 修复
|
||||||
|
|
||||||
|
1. 标签过滤时,子文件夹未成功过滤。
|
||||||
|
2. 暂时移除 md 阅读优化,避免链接分割错误。
|
||||||
|
3. 离开团队时,未刷新成员列表。
|
||||||
|
4. PPTX 编码错误,导致解析失败。
|
||||||
|
5. 删除知识库单条数据时,全文索引未跟随删除。
|
||||||
|
6. 修复 Mongo Dataset text 索引在查询数据时未生效。
|
||||||
@@ -15,7 +15,7 @@ weight: 821
|
|||||||
|
|
||||||
## 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).
|
1. 新增 - 支持 Milvus 数据库, 可参考最新的 [docker-compose-milvus.yml](https://github.com/labring/FastGPT/blob/main/deploy/docker/docker-compose-milvus.yml).
|
||||||
2. 新增 - 给 chat 接口 empty answer 增加 log,便于排查模型问题。
|
2. 新增 - 给 chat 接口 empty answer 增加 log,便于排查模型问题。
|
||||||
3. 新增 - ifelse判断器,字符串支持正则。
|
3. 新增 - ifelse判断器,字符串支持正则。
|
||||||
4. 新增 - 代码运行支持 console.log 输出调试。
|
4. 新增 - 代码运行支持 console.log 输出调试。
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ toc: true
|
|||||||
weight: 102
|
weight: 102
|
||||||
---
|
---
|
||||||
|
|
||||||
更多使用技巧,[查看视屏教程](https://www.bilibili.com/video/BV1sH4y1T7s9)
|
更多使用技巧,[查看视频教程](https://www.bilibili.com/video/BV1sH4y1T7s9)
|
||||||
|
|
||||||
## 知识库
|
## 知识库
|
||||||
|
|
||||||
开始前,请准备一份测试电子文档,WORD,PDF,TXT,excel,markdown 都可以,比如公司休假制度,不涉密的销售说辞,产品知识等等。
|
开始前,请准备一份测试电子文档,WORD、PDF、TXT、excel、markdown 都可以,比如公司休假制度、不涉密的销售说辞、产品知识等等。
|
||||||
|
|
||||||
这里使用 FastGPT 中文 README 文件为例。
|
这里使用 FastGPT 中文 README 文件为例。
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ weight: 102
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
点击上传后我们需要等待数据处理完成,等到我们上传的文件状态为可用。
|
点击上传后我们需要等待数据处理完成,直到我们上传的文件状态为可用。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
3
packages/README.md
Normal file
3
packages/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# 目录说明
|
||||||
|
|
||||||
|
该目录为 FastGPT 的依赖包,多端复用。
|
||||||
@@ -4,6 +4,7 @@ import { ErrType } from '../errorCode';
|
|||||||
/* dataset: 501000 */
|
/* dataset: 501000 */
|
||||||
export enum DatasetErrEnum {
|
export enum DatasetErrEnum {
|
||||||
unExist = 'unExistDataset',
|
unExist = 'unExistDataset',
|
||||||
|
unExistCollection = 'unExistCollection',
|
||||||
unAuthDataset = 'unAuthDataset',
|
unAuthDataset = 'unAuthDataset',
|
||||||
unCreateCollection = 'unCreateCollection',
|
unCreateCollection = 'unCreateCollection',
|
||||||
unAuthDatasetCollection = 'unAuthDatasetCollection',
|
unAuthDatasetCollection = 'unAuthDatasetCollection',
|
||||||
@@ -28,6 +29,10 @@ const datasetErr = [
|
|||||||
statusText: DatasetErrEnum.unExist,
|
statusText: DatasetErrEnum.unExist,
|
||||||
message: 'core.dataset.error.unExistDataset'
|
message: 'core.dataset.error.unExistDataset'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
statusText: DatasetErrEnum.unExistCollection,
|
||||||
|
message: i18nT('common:error_collection_not_exist')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
statusText: DatasetErrEnum.unAuthDataset,
|
statusText: DatasetErrEnum.unAuthDataset,
|
||||||
message: 'core.dataset.error.unAuthDataset'
|
message: 'core.dataset.error.unAuthDataset'
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import { i18nT } from '../../../web/i18n/utils';
|
|||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
export const formatTime2YMDHMW = (time?: Date) => dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
|
export const formatTime2YMDHMW = (time?: Date | number) =>
|
||||||
export const formatTime2YMDHMS = (time?: Date) =>
|
dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
|
||||||
|
export const formatTime2YMDHMS = (time?: Date | number) =>
|
||||||
time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '';
|
time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '';
|
||||||
export const formatTime2YMDHM = (time?: Date) =>
|
export const formatTime2YMDHM = (time?: Date | number) =>
|
||||||
time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '';
|
time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '';
|
||||||
export const formatTime2YMD = (time?: Date) => (time ? dayjs(time).format('YYYY-MM-DD') : '');
|
export const formatTime2YMD = (time?: Date | number) =>
|
||||||
|
time ? dayjs(time).format('YYYY-MM-DD') : '';
|
||||||
export const formatTime2HM = (time: Date = new Date()) => dayjs(time).format('HH:mm');
|
export const formatTime2HM = (time: Date = new Date()) => dayjs(time).format('HH:mm');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export type FastGPTConfigFileType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FastGPTFeConfigsType = {
|
export type FastGPTFeConfigsType = {
|
||||||
|
show_workorder?: boolean;
|
||||||
show_emptyChat?: boolean;
|
show_emptyChat?: boolean;
|
||||||
register_method?: ['email' | 'phone' | 'sync'];
|
register_method?: ['email' | 'phone' | 'sync'];
|
||||||
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
|
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
|
||||||
@@ -53,6 +54,7 @@ export type FastGPTFeConfigsType = {
|
|||||||
show_promotion?: boolean;
|
show_promotion?: boolean;
|
||||||
show_team_chat?: boolean;
|
show_team_chat?: boolean;
|
||||||
show_compliance_copywriting?: boolean;
|
show_compliance_copywriting?: boolean;
|
||||||
|
show_aiproxy?: boolean;
|
||||||
concatMd?: string;
|
concatMd?: string;
|
||||||
|
|
||||||
docUrl?: string;
|
docUrl?: string;
|
||||||
|
|||||||
2
packages/global/core/ai/model.d.ts
vendored
2
packages/global/core/ai/model.d.ts
vendored
@@ -17,6 +17,8 @@ type BaseModelItemType = {
|
|||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
isCustom?: boolean;
|
isCustom?: boolean;
|
||||||
isDefault?: boolean;
|
isDefault?: boolean;
|
||||||
|
isDefaultDatasetTextModel?: boolean;
|
||||||
|
isDefaultDatasetImageModel?: boolean;
|
||||||
|
|
||||||
// If has requestUrl, it will request the model directly
|
// If has requestUrl, it will request the model directly
|
||||||
requestUrl?: string;
|
requestUrl?: string;
|
||||||
|
|||||||
1
packages/global/core/dataset/type.d.ts
vendored
1
packages/global/core/dataset/type.d.ts
vendored
@@ -192,6 +192,7 @@ export type DatasetCollectionItemType = CollectionWithDatasetType & {
|
|||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
file?: DatasetFileSchema;
|
file?: DatasetFileSchema;
|
||||||
permission: DatasetPermission;
|
permission: DatasetPermission;
|
||||||
|
indexAmount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================= data ===================== */
|
/* ================= data ===================== */
|
||||||
|
|||||||
@@ -420,137 +420,3 @@ export function rewriteNodeOutputByHistories(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse <think></think> tags to think and answer - unstream response
|
|
||||||
export const parseReasoningContent = (text: string): [string, string] => {
|
|
||||||
const regex = /<think>([\s\S]*?)<\/think>/;
|
|
||||||
const match = text.match(regex);
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
return ['', text];
|
|
||||||
}
|
|
||||||
|
|
||||||
const thinkContent = match[1].trim();
|
|
||||||
|
|
||||||
// Add answer (remaining text after think tag)
|
|
||||||
const answerContent = text.slice(match.index! + match[0].length);
|
|
||||||
|
|
||||||
return [thinkContent, answerContent];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse <think></think> tags to think and answer - stream response
|
|
||||||
export const parseReasoningStreamContent = () => {
|
|
||||||
let isInThinkTag: boolean | undefined;
|
|
||||||
|
|
||||||
const startTag = '<think>';
|
|
||||||
let startTagBuffer = '';
|
|
||||||
|
|
||||||
const endTag = '</think>';
|
|
||||||
let endTagBuffer = '';
|
|
||||||
|
|
||||||
/*
|
|
||||||
parseReasoning - 只控制是否主动解析 <think></think>,如果接口已经解析了,仍然会返回 think 内容。
|
|
||||||
*/
|
|
||||||
const parsePart = (
|
|
||||||
part: {
|
|
||||||
choices: {
|
|
||||||
delta: {
|
|
||||||
content?: string;
|
|
||||||
reasoning_content?: string;
|
|
||||||
};
|
|
||||||
}[];
|
|
||||||
},
|
|
||||||
parseReasoning = false
|
|
||||||
): [string, string] => {
|
|
||||||
const content = part.choices?.[0]?.delta?.content || '';
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
|
|
||||||
if (reasoningContent || !parseReasoning) {
|
|
||||||
isInThinkTag = false;
|
|
||||||
return [reasoningContent, content];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!content) {
|
|
||||||
return ['', ''];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content
|
|
||||||
if (isInThinkTag === false) {
|
|
||||||
return ['', content];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检测是否为 think 标签开头的数据
|
|
||||||
if (isInThinkTag === undefined) {
|
|
||||||
// Parse content think and answer
|
|
||||||
startTagBuffer += content;
|
|
||||||
// 太少内容时候,暂时不解析
|
|
||||||
if (startTagBuffer.length < startTag.length) {
|
|
||||||
return ['', ''];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startTagBuffer.startsWith(startTag)) {
|
|
||||||
isInThinkTag = true;
|
|
||||||
return [startTagBuffer.slice(startTag.length), ''];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content
|
|
||||||
isInThinkTag = false;
|
|
||||||
return ['', startTagBuffer];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确认是 think 标签内容,开始返回 think 内容,并实时检测 </think>
|
|
||||||
/*
|
|
||||||
检测 </think> 方案。
|
|
||||||
存储所有疑似 </think> 的内容,直到检测到完整的 </think> 标签或超出 </think> 长度。
|
|
||||||
content 返回值包含以下几种情况:
|
|
||||||
abc - 完全未命中尾标签
|
|
||||||
abc<th - 命中一部分尾标签
|
|
||||||
abc</think> - 完全命中尾标签
|
|
||||||
abc</think>abc - 完全命中尾标签
|
|
||||||
</think>abc - 完全命中尾标签
|
|
||||||
k>abc - 命中一部分尾标签
|
|
||||||
*/
|
|
||||||
// endTagBuffer 专门用来记录疑似尾标签的内容
|
|
||||||
if (endTagBuffer) {
|
|
||||||
endTagBuffer += content;
|
|
||||||
if (endTagBuffer.includes(endTag)) {
|
|
||||||
isInThinkTag = false;
|
|
||||||
const answer = endTagBuffer.slice(endTag.length);
|
|
||||||
return ['', answer];
|
|
||||||
} else if (endTagBuffer.length >= endTag.length) {
|
|
||||||
// 缓存内容超出尾标签长度,且仍未命中 </think>,则认为本次猜测 </think> 失败,仍处于 think 阶段。
|
|
||||||
const tmp = endTagBuffer;
|
|
||||||
endTagBuffer = '';
|
|
||||||
return [tmp, ''];
|
|
||||||
}
|
|
||||||
return ['', ''];
|
|
||||||
} else if (content.includes(endTag)) {
|
|
||||||
// 返回内容,完整命中</think>,直接结束
|
|
||||||
isInThinkTag = false;
|
|
||||||
const [think, answer] = content.split(endTag);
|
|
||||||
return [think, answer];
|
|
||||||
} else {
|
|
||||||
// 无 buffer,且未命中 </think>,开始疑似 </think> 检测。
|
|
||||||
for (let i = 1; i < endTag.length; i++) {
|
|
||||||
const partialEndTag = endTag.slice(0, i);
|
|
||||||
// 命中一部分尾标签
|
|
||||||
if (content.endsWith(partialEndTag)) {
|
|
||||||
const think = content.slice(0, -partialEndTag.length);
|
|
||||||
endTagBuffer += partialEndTag;
|
|
||||||
return [think, ''];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 完全未命中尾标签,还是 think 阶段。
|
|
||||||
return [content, ''];
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStartTagBuffer = () => startTagBuffer;
|
|
||||||
|
|
||||||
return {
|
|
||||||
parsePart,
|
|
||||||
getStartTagBuffer
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export type AuthTeamRoleProps = {
|
|||||||
export type CreateTeamProps = {
|
export type CreateTeamProps = {
|
||||||
name: string;
|
name: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
defaultTeam?: boolean;
|
|
||||||
memberName?: string;
|
memberName?: string;
|
||||||
memberAvatar?: string;
|
memberAvatar?: string;
|
||||||
notificationAccount?: string;
|
notificationAccount?: string;
|
||||||
|
|||||||
2
packages/global/support/user/team/type.d.ts
vendored
2
packages/global/support/user/team/type.d.ts
vendored
@@ -47,7 +47,6 @@ export type TeamMemberSchema = {
|
|||||||
role: `${TeamMemberRoleEnum}`;
|
role: `${TeamMemberRoleEnum}`;
|
||||||
status: `${TeamMemberStatusEnum}`;
|
status: `${TeamMemberStatusEnum}`;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
defaultTeam: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeamMemberWithTeamAndUserSchema = TeamMemberSchema & {
|
export type TeamMemberWithTeamAndUserSchema = TeamMemberSchema & {
|
||||||
@@ -65,7 +64,6 @@ export type TeamTmbItemType = {
|
|||||||
balance?: number;
|
balance?: number;
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
teamDomain: string;
|
teamDomain: string;
|
||||||
defaultTeam: boolean;
|
|
||||||
role: `${TeamMemberRoleEnum}`;
|
role: `${TeamMemberRoleEnum}`;
|
||||||
status: `${TeamMemberStatusEnum}`;
|
status: `${TeamMemberStatusEnum}`;
|
||||||
notificationAccount?: string;
|
notificationAccount?: string;
|
||||||
|
|||||||
1
packages/global/support/user/type.d.ts
vendored
1
packages/global/support/user/type.d.ts
vendored
@@ -27,7 +27,6 @@ export type UserType = {
|
|||||||
timezone: string;
|
timezone: string;
|
||||||
promotionRate: UserModelSchema['promotionRate'];
|
promotionRate: UserModelSchema['promotionRate'];
|
||||||
team: TeamTmbItemType;
|
team: TeamTmbItemType;
|
||||||
standardInfo?: standardInfoType;
|
|
||||||
notificationAccount?: string;
|
notificationAccount?: string;
|
||||||
permission: TeamPermission;
|
permission: TeamPermission;
|
||||||
contact?: string;
|
contact?: string;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"nodeId": "lmpb9v2lo2lk",
|
"nodeId": "lmpb9v2lo2lk",
|
||||||
"name": "插件开始",
|
"name": "插件开始",
|
||||||
"intro": "自定义配置外部输入,使用插件时,仅暴露自定义配置的输入",
|
"intro": "自定义配置外部输入,使用插件时,仅暴露自定义配置的输入",
|
||||||
"avatar": "/imgs/workflow/input.png",
|
"avatar": "core/workflow/template/workflowStart",
|
||||||
"flowNodeType": "pluginInput",
|
"flowNodeType": "pluginInput",
|
||||||
"showStatus": false,
|
"showStatus": false,
|
||||||
"position": {
|
"position": {
|
||||||
@@ -26,14 +26,16 @@
|
|||||||
"version": "481",
|
"version": "481",
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
"renderTypeList": ["reference"],
|
"renderTypeList": ["input", "reference"],
|
||||||
"selectedTypeIndex": 0,
|
"selectedTypeIndex": 0,
|
||||||
"valueType": "string",
|
"valueType": "string",
|
||||||
"key": "url",
|
"key": "url",
|
||||||
"label": "url",
|
"label": "url",
|
||||||
"description": "需要读取的网页链接",
|
"description": "需要读取的网页链接",
|
||||||
"required": true,
|
"required": true,
|
||||||
"toolDescription": "需要读取的网页链接"
|
"toolDescription": "需要读取的网页链接",
|
||||||
|
"list": [],
|
||||||
|
"defaultValue": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -50,12 +52,12 @@
|
|||||||
"nodeId": "i7uow4wj2wdp",
|
"nodeId": "i7uow4wj2wdp",
|
||||||
"name": "插件输出",
|
"name": "插件输出",
|
||||||
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
|
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
|
||||||
"avatar": "/imgs/workflow/output.png",
|
"avatar": "core/workflow/template/pluginOutput",
|
||||||
"flowNodeType": "pluginOutput",
|
"flowNodeType": "pluginOutput",
|
||||||
"showStatus": false,
|
"showStatus": false,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 1607.7142331269129,
|
"x": 1853.935047606551,
|
||||||
"y": -150.8808596935447
|
"y": -154.13661665265613
|
||||||
},
|
},
|
||||||
"version": "481",
|
"version": "481",
|
||||||
"inputs": [
|
"inputs": [
|
||||||
@@ -81,12 +83,12 @@
|
|||||||
"nodeId": "ebLCxU43hHuZ",
|
"nodeId": "ebLCxU43hHuZ",
|
||||||
"name": "HTTP 请求",
|
"name": "HTTP 请求",
|
||||||
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
|
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
|
||||||
"avatar": "/imgs/workflow/http.png",
|
"avatar": "core/workflow/template/httpRequest",
|
||||||
"flowNodeType": "httpRequest468",
|
"flowNodeType": "httpRequest468",
|
||||||
"showStatus": true,
|
"showStatus": true,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 1050.9890727421412,
|
"x": 1054.2940501177068,
|
||||||
"y": -415.2085119990912
|
"y": -503.13661665265613
|
||||||
},
|
},
|
||||||
"version": "481",
|
"version": "481",
|
||||||
"inputs": [
|
"inputs": [
|
||||||
@@ -96,7 +98,7 @@
|
|||||||
"valueType": "dynamic",
|
"valueType": "dynamic",
|
||||||
"label": "",
|
"label": "",
|
||||||
"required": false,
|
"required": false,
|
||||||
"description": "core.module.input.description.HTTP Dynamic Input",
|
"description": "common:core.module.input.description.HTTP Dynamic Input",
|
||||||
"customInputConfig": {
|
"customInputConfig": {
|
||||||
"selectValueTypeList": [
|
"selectValueTypeList": [
|
||||||
"string",
|
"string",
|
||||||
@@ -107,16 +109,19 @@
|
|||||||
"arrayNumber",
|
"arrayNumber",
|
||||||
"arrayBoolean",
|
"arrayBoolean",
|
||||||
"arrayObject",
|
"arrayObject",
|
||||||
|
"arrayAny",
|
||||||
"any",
|
"any",
|
||||||
"chatHistory",
|
"chatHistory",
|
||||||
"datasetQuote",
|
"datasetQuote",
|
||||||
"dynamic",
|
"dynamic",
|
||||||
"selectApp",
|
"selectDataset",
|
||||||
"selectDataset"
|
"selectApp"
|
||||||
],
|
],
|
||||||
"showDescription": false,
|
"showDescription": false,
|
||||||
"showDefaultValue": true
|
"showDefaultValue": true
|
||||||
}
|
},
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "system_httpMethod",
|
"key": "system_httpMethod",
|
||||||
@@ -124,17 +129,33 @@
|
|||||||
"valueType": "string",
|
"valueType": "string",
|
||||||
"label": "",
|
"label": "",
|
||||||
"value": "POST",
|
"value": "POST",
|
||||||
"required": true
|
"required": true,
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system_httpTimeout",
|
||||||
|
"renderTypeList": ["custom"],
|
||||||
|
"valueType": "number",
|
||||||
|
"label": "",
|
||||||
|
"value": 30,
|
||||||
|
"min": 5,
|
||||||
|
"max": 600,
|
||||||
|
"required": true,
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "system_httpReqUrl",
|
"key": "system_httpReqUrl",
|
||||||
"renderTypeList": ["hidden"],
|
"renderTypeList": ["hidden"],
|
||||||
"valueType": "string",
|
"valueType": "string",
|
||||||
"label": "",
|
"label": "",
|
||||||
"description": "core.module.input.description.Http Request Url",
|
"description": "common:core.module.input.description.Http Request Url",
|
||||||
"placeholder": "https://api.ai.com/getInventory",
|
"placeholder": "https://api.ai.com/getInventory",
|
||||||
"required": false,
|
"required": false,
|
||||||
"value": "fetchUrl"
|
"value": "fetchUrl",
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "system_httpHeader",
|
"key": "system_httpHeader",
|
||||||
@@ -142,9 +163,11 @@
|
|||||||
"valueType": "any",
|
"valueType": "any",
|
||||||
"value": [],
|
"value": [],
|
||||||
"label": "",
|
"label": "",
|
||||||
"description": "core.module.input.description.Http Request Header",
|
"description": "common:core.module.input.description.Http Request Header",
|
||||||
"placeholder": "core.module.input.description.Http Request Header",
|
"placeholder": "common:core.module.input.description.Http Request Header",
|
||||||
"required": false
|
"required": false,
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "system_httpParams",
|
"key": "system_httpParams",
|
||||||
@@ -152,7 +175,9 @@
|
|||||||
"valueType": "any",
|
"valueType": "any",
|
||||||
"value": [],
|
"value": [],
|
||||||
"label": "",
|
"label": "",
|
||||||
"required": false
|
"required": false,
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "system_httpJsonBody",
|
"key": "system_httpJsonBody",
|
||||||
@@ -160,7 +185,29 @@
|
|||||||
"valueType": "any",
|
"valueType": "any",
|
||||||
"value": "{\n \"url\": \"{{url}}\"\n}",
|
"value": "{\n \"url\": \"{{url}}\"\n}",
|
||||||
"label": "",
|
"label": "",
|
||||||
"required": false
|
"required": false,
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system_httpFormBody",
|
||||||
|
"renderTypeList": ["hidden"],
|
||||||
|
"valueType": "any",
|
||||||
|
"value": [],
|
||||||
|
"label": "",
|
||||||
|
"required": false,
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system_httpContentType",
|
||||||
|
"renderTypeList": ["hidden"],
|
||||||
|
"valueType": "string",
|
||||||
|
"value": "json",
|
||||||
|
"label": "",
|
||||||
|
"required": false,
|
||||||
|
"debugLabel": "",
|
||||||
|
"toolDescription": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"renderTypeList": ["reference"],
|
"renderTypeList": ["reference"],
|
||||||
@@ -178,12 +225,13 @@
|
|||||||
"arrayNumber",
|
"arrayNumber",
|
||||||
"arrayBoolean",
|
"arrayBoolean",
|
||||||
"arrayObject",
|
"arrayObject",
|
||||||
|
"arrayAny",
|
||||||
"any",
|
"any",
|
||||||
"chatHistory",
|
"chatHistory",
|
||||||
"datasetQuote",
|
"datasetQuote",
|
||||||
"dynamic",
|
"dynamic",
|
||||||
"selectApp",
|
"selectDataset",
|
||||||
"selectDataset"
|
"selectApp"
|
||||||
],
|
],
|
||||||
"showDescription": false,
|
"showDescription": false,
|
||||||
"showDefaultValue": true
|
"showDefaultValue": true
|
||||||
@@ -193,6 +241,23 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
|
{
|
||||||
|
"id": "error",
|
||||||
|
"key": "error",
|
||||||
|
"label": "workflow:request_error",
|
||||||
|
"description": "HTTP请求错误信息,成功时返回空",
|
||||||
|
"valueType": "object",
|
||||||
|
"type": "static"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "httpRawResponse",
|
||||||
|
"key": "httpRawResponse",
|
||||||
|
"required": true,
|
||||||
|
"label": "workflow:raw_response",
|
||||||
|
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||||
|
"valueType": "any",
|
||||||
|
"type": "static"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "system_addOutputParam",
|
"id": "system_addOutputParam",
|
||||||
"key": "system_addOutputParam",
|
"key": "system_addOutputParam",
|
||||||
@@ -220,23 +285,6 @@
|
|||||||
"showDefaultValue": true
|
"showDefaultValue": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "error",
|
|
||||||
"key": "error",
|
|
||||||
"label": "请求错误",
|
|
||||||
"description": "HTTP请求错误信息,成功时返回空",
|
|
||||||
"valueType": "object",
|
|
||||||
"type": "static"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "httpRawResponse",
|
|
||||||
"key": "httpRawResponse",
|
|
||||||
"label": "原始响应",
|
|
||||||
"required": true,
|
|
||||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
|
||||||
"valueType": "any",
|
|
||||||
"type": "static"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "rH4tMV02robs",
|
"id": "rH4tMV02robs",
|
||||||
"valueType": "string",
|
"valueType": "string",
|
||||||
@@ -260,6 +308,34 @@
|
|||||||
"sourceHandle": "ebLCxU43hHuZ-source-right",
|
"sourceHandle": "ebLCxU43hHuZ-source-right",
|
||||||
"targetHandle": "i7uow4wj2wdp-target-left"
|
"targetHandle": "i7uow4wj2wdp-target-left"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"chatConfig": {
|
||||||
|
"welcomeText": "",
|
||||||
|
"variables": [],
|
||||||
|
"questionGuide": {
|
||||||
|
"open": false,
|
||||||
|
"model": "gpt-4o-mini",
|
||||||
|
"customPrompt": "You are an AI assistant tasked with predicting the user's next question based on the conversation history. Your goal is to generate 3 potential questions that will guide the user to continue the conversation. When generating these questions, adhere to the following rules:\n\n1. Use the same language as the user's last question in the conversation history.\n2. Keep each question under 20 characters in length.\n\nAnalyze the conversation history provided to you and use it as context to generate relevant and engaging follow-up questions. Your predictions should be logical extensions of the current topic or related areas that the user might be interested in exploring further.\n\nRemember to maintain consistency in tone and style with the existing conversation while providing diverse options for the user to choose from. Your goal is to keep the conversation flowing naturally and help the user delve deeper into the subject matter or explore related topics."
|
||||||
|
},
|
||||||
|
"ttsConfig": {
|
||||||
|
"type": "web"
|
||||||
|
},
|
||||||
|
"whisperConfig": {
|
||||||
|
"open": false,
|
||||||
|
"autoSend": false,
|
||||||
|
"autoTTSResponse": false
|
||||||
|
},
|
||||||
|
"chatInputGuide": {
|
||||||
|
"open": false,
|
||||||
|
"textList": [],
|
||||||
|
"customUrl": ""
|
||||||
|
},
|
||||||
|
"instruction": "",
|
||||||
|
"autoExecute": {
|
||||||
|
"open": false,
|
||||||
|
"defaultPrompt": ""
|
||||||
|
},
|
||||||
|
"_id": "677b59849d672185a5671b45"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ export function getGFSCollection(bucket: `${BucketNameEnum}`) {
|
|||||||
MongoDatasetFileSchema;
|
MongoDatasetFileSchema;
|
||||||
MongoChatFileSchema;
|
MongoChatFileSchema;
|
||||||
|
|
||||||
return connectionMongo.connection.db.collection(`${bucket}.files`);
|
return connectionMongo.connection.db!.collection(`${bucket}.files`);
|
||||||
}
|
}
|
||||||
export function getGridBucket(bucket: `${BucketNameEnum}`) {
|
export function getGridBucket(bucket: `${BucketNameEnum}`) {
|
||||||
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db, {
|
return new connectionMongo.mongo.GridFSBucket(connectionMongo.connection.db!, {
|
||||||
bucketName: bucket,
|
bucketName: bucket,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
readPreference: ReadPreference.SECONDARY_PREFERRED // Read from secondary node
|
readPreference: ReadPreference.SECONDARY_PREFERRED // Read from secondary node
|
||||||
|
|||||||
@@ -3,13 +3,16 @@ import { PassThrough } from 'stream';
|
|||||||
|
|
||||||
export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
||||||
return new Promise<Buffer>((resolve, reject) => {
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
let tmpBuffer: Buffer = Buffer.from([]);
|
const chunks: Buffer[] = [];
|
||||||
|
let totalLength = 0;
|
||||||
|
|
||||||
stream.on('data', (chunk) => {
|
stream.on('data', (chunk) => {
|
||||||
tmpBuffer = Buffer.concat([tmpBuffer, chunk]);
|
chunks.push(chunk);
|
||||||
|
totalLength += chunk.length;
|
||||||
});
|
});
|
||||||
stream.on('end', () => {
|
stream.on('end', () => {
|
||||||
resolve(tmpBuffer);
|
const resultBuffer = Buffer.concat(chunks, totalLength); // 一次性拼接
|
||||||
|
resolve(resultBuffer);
|
||||||
});
|
});
|
||||||
stream.on('error', (err) => {
|
stream.on('error', (err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export async function delImgByRelatedId({
|
|||||||
}: {
|
}: {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
relateIds: string[];
|
relateIds: string[];
|
||||||
session: ClientSession;
|
session?: ClientSession;
|
||||||
}) {
|
}) {
|
||||||
if (relateIds.length === 0) return;
|
if (relateIds.length === 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -111,15 +111,21 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
// markdown data format
|
// markdown data format
|
||||||
if (imageList) {
|
if (imageList) {
|
||||||
await batchRun(imageList, async (item) => {
|
await batchRun(imageList, async (item) => {
|
||||||
const src = await uploadMongoImg({
|
const src = await (async () => {
|
||||||
base64Img: `data:${item.mime};base64,${item.base64}`,
|
try {
|
||||||
teamId,
|
return await uploadMongoImg({
|
||||||
// expiredTime: addHours(new Date(), 1),
|
base64Img: `data:${item.mime};base64,${item.base64}`,
|
||||||
metadata: {
|
teamId,
|
||||||
...metadata,
|
// expiredTime: addHours(new Date(), 1),
|
||||||
mime: item.mime
|
metadata: {
|
||||||
|
...metadata,
|
||||||
|
mime: item.mime
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
});
|
})();
|
||||||
rawText = rawText.replace(item.uuid, src);
|
rawText = rawText.replace(item.uuid, src);
|
||||||
if (formatText) {
|
if (formatText) {
|
||||||
formatText = formatText.replace(item.uuid, src);
|
formatText = formatText.replace(item.uuid, src);
|
||||||
|
|||||||
@@ -38,10 +38,12 @@ const addCommonMiddleware = (schema: mongoose.Schema) => {
|
|||||||
schema.post(op, function (this: any, result: any, next) {
|
schema.post(op, function (this: any, result: any, next) {
|
||||||
if (this._startTime) {
|
if (this._startTime) {
|
||||||
const duration = Date.now() - this._startTime;
|
const duration = Date.now() - this._startTime;
|
||||||
|
|
||||||
const warnLogData = {
|
const warnLogData = {
|
||||||
query: this._query,
|
collectionName: this.collection?.name,
|
||||||
op,
|
op: this.op,
|
||||||
|
...(this._query && { query: this._query }),
|
||||||
|
...(this._update && { update: this._update }),
|
||||||
|
...(this._delete && { delete: this._delete }),
|
||||||
duration
|
duration
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,30 @@ export async function connectMongo(): Promise<Mongoose> {
|
|||||||
|
|
||||||
console.log('mongo start connect');
|
console.log('mongo start connect');
|
||||||
try {
|
try {
|
||||||
connectionMongo.set('strictQuery', true);
|
// Remove existing listeners to prevent duplicates
|
||||||
|
connectionMongo.connection.removeAllListeners('error');
|
||||||
|
connectionMongo.connection.removeAllListeners('disconnected');
|
||||||
|
connectionMongo.set('strictQuery', false);
|
||||||
|
|
||||||
connectionMongo.connection.on('error', async (error) => {
|
connectionMongo.connection.on('error', async (error) => {
|
||||||
console.log('mongo error', error);
|
console.log('mongo error', error);
|
||||||
await connectionMongo.disconnect();
|
try {
|
||||||
await delay(1000);
|
if (connectionMongo.connection.readyState !== 0) {
|
||||||
connectMongo();
|
await connectionMongo.disconnect();
|
||||||
|
await delay(1000);
|
||||||
|
await connectMongo();
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
});
|
});
|
||||||
connectionMongo.connection.on('disconnected', () => {
|
connectionMongo.connection.on('disconnected', async () => {
|
||||||
console.log('mongo disconnected');
|
console.log('mongo disconnected');
|
||||||
|
try {
|
||||||
|
if (connectionMongo.connection.readyState !== 0) {
|
||||||
|
await connectionMongo.disconnect();
|
||||||
|
await delay(1000);
|
||||||
|
await connectMongo();
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
await connectionMongo.connect(process.env.MONGODB_URI as string, {
|
await connectionMongo.connect(process.env.MONGODB_URI as string, {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { UrlFetchParams, UrlFetchResponse } from '@fastgpt/global/common/file/ap
|
|||||||
import * as cheerio from 'cheerio';
|
import * as cheerio from 'cheerio';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { htmlToMarkdown } from './utils';
|
import { htmlToMarkdown } from './utils';
|
||||||
|
import { isInternalAddress } from '../system/utils';
|
||||||
|
|
||||||
export const cheerioToHtml = ({
|
export const cheerioToHtml = ({
|
||||||
fetchUrl,
|
fetchUrl,
|
||||||
@@ -75,6 +76,16 @@ export const urlsFetch = async ({
|
|||||||
|
|
||||||
const response = await Promise.all(
|
const response = await Promise.all(
|
||||||
urlList.map(async (url) => {
|
urlList.map(async (url) => {
|
||||||
|
const isInternal = isInternalAddress(url);
|
||||||
|
if (isInternal) {
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
title: '',
|
||||||
|
content: 'Cannot fetch internal url',
|
||||||
|
selector: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fetchRes = await axios.get(url, {
|
const fetchRes = await axios.get(url, {
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : '';
|
export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : '';
|
||||||
|
export const isFastGPTMainService = !!process.env.PRO_URL;
|
||||||
|
// @ts-ignore
|
||||||
|
export const isFastGPTProService = () => !!global.systemConfig;
|
||||||
|
|||||||
63
packages/service/common/system/utils.ts
Normal file
63
packages/service/common/system/utils.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { SERVICE_LOCAL_HOST } from './tools';
|
||||||
|
|
||||||
|
export const isInternalAddress = (url: string): boolean => {
|
||||||
|
try {
|
||||||
|
const parsedUrl = new URL(url);
|
||||||
|
const hostname = parsedUrl.hostname;
|
||||||
|
const fullUrl = parsedUrl.toString();
|
||||||
|
|
||||||
|
// Check for localhost and common internal domains
|
||||||
|
if (hostname === SERVICE_LOCAL_HOST) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata endpoints whitelist
|
||||||
|
const metadataEndpoints = [
|
||||||
|
// AWS
|
||||||
|
'http://169.254.169.254/latest/meta-data/',
|
||||||
|
// Azure
|
||||||
|
'http://169.254.169.254/metadata/instance?api-version=2021-02-01',
|
||||||
|
// GCP
|
||||||
|
'http://metadata.google.internal/computeMetadata/v1/',
|
||||||
|
// Alibaba Cloud
|
||||||
|
'http://100.100.100.200/latest/meta-data/',
|
||||||
|
// Tencent Cloud
|
||||||
|
'http://metadata.tencentyun.com/latest/meta-data/',
|
||||||
|
// Huawei Cloud
|
||||||
|
'http://169.254.169.254/latest/meta-data/'
|
||||||
|
];
|
||||||
|
if (metadataEndpoints.some((endpoint) => fullUrl.startsWith(endpoint))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For non-metadata URLs, check if it's a domain name
|
||||||
|
const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||||
|
if (!ipv4Pattern.test(hostname)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... existing IP validation code ...
|
||||||
|
const parts = hostname.split('.').map(Number);
|
||||||
|
|
||||||
|
if (parts.length !== 4 || parts.some((part) => part < 0 || part > 255)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow public IP ranges
|
||||||
|
return (
|
||||||
|
parts[0] !== 0 &&
|
||||||
|
parts[0] !== 10 &&
|
||||||
|
parts[0] !== 127 &&
|
||||||
|
!(parts[0] === 169 && parts[1] === 254) &&
|
||||||
|
!(parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) &&
|
||||||
|
!(parts[0] === 192 && parts[1] === 168) &&
|
||||||
|
!(parts[0] >= 224 && parts[0] <= 239) &&
|
||||||
|
!(parts[0] >= 240 && parts[0] <= 255) &&
|
||||||
|
!(parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127) &&
|
||||||
|
!(parts[0] === 9 && parts[1] === 0) &&
|
||||||
|
!(parts[0] === 11 && parts[1] === 0)
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return false; // If URL parsing fails, reject it as potentially unsafe
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -21,6 +21,7 @@ export const recallFromVectorStore = Vector.embRecall;
|
|||||||
export const getVectorDataByTime = Vector.getVectorDataByTime;
|
export const getVectorDataByTime = Vector.getVectorDataByTime;
|
||||||
export const getVectorCountByTeamId = Vector.getVectorCountByTeamId;
|
export const getVectorCountByTeamId = Vector.getVectorCountByTeamId;
|
||||||
export const getVectorCountByDatasetId = Vector.getVectorCountByDatasetId;
|
export const getVectorCountByDatasetId = Vector.getVectorCountByDatasetId;
|
||||||
|
export const getVectorCountByCollectionId = Vector.getVectorCountByCollectionId;
|
||||||
|
|
||||||
export const insertDatasetDataVector = async ({
|
export const insertDatasetDataVector = async ({
|
||||||
model,
|
model,
|
||||||
|
|||||||
@@ -321,6 +321,23 @@ export class MilvusCtrl {
|
|||||||
|
|
||||||
return total;
|
return total;
|
||||||
};
|
};
|
||||||
|
getVectorCountByCollectionId = async (
|
||||||
|
teamId: string,
|
||||||
|
datasetId: string,
|
||||||
|
collectionId: string
|
||||||
|
) => {
|
||||||
|
const client = await this.getClient();
|
||||||
|
|
||||||
|
const result = await client.query({
|
||||||
|
collection_name: DatasetVectorTableName,
|
||||||
|
output_fields: ['count(*)'],
|
||||||
|
filter: `(teamId == "${String(teamId)}") and (datasetId == "${String(datasetId)}") and (collectionId == "${String(collectionId)}")`
|
||||||
|
});
|
||||||
|
|
||||||
|
const total = result.data?.[0]?.['count(*)'] as number;
|
||||||
|
|
||||||
|
return total;
|
||||||
|
};
|
||||||
|
|
||||||
getVectorDataByTime = async (start: Date, end: Date) => {
|
getVectorDataByTime = async (start: Date, end: Date) => {
|
||||||
const client = await this.getClient();
|
const client = await this.getClient();
|
||||||
|
|||||||
@@ -240,6 +240,23 @@ export class PgVectorCtrl {
|
|||||||
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
|
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return total;
|
||||||
|
};
|
||||||
|
getVectorCountByCollectionId = async (
|
||||||
|
teamId: string,
|
||||||
|
datasetId: string,
|
||||||
|
collectionId: string
|
||||||
|
) => {
|
||||||
|
const total = await PgClient.count(DatasetVectorTableName, {
|
||||||
|
where: [
|
||||||
|
['team_id', String(teamId)],
|
||||||
|
'and',
|
||||||
|
['dataset_id', String(datasetId)],
|
||||||
|
'and',
|
||||||
|
['collection_id', String(collectionId)]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,17 @@ import { i18nT } from '../../../web/i18n/utils';
|
|||||||
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
|
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
|
||||||
import { getLLMModel } from './model';
|
import { getLLMModel } from './model';
|
||||||
|
|
||||||
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
const aiProxyBaseUrl = process.env.AIPROXY_API_ENDPOINT
|
||||||
|
? `${process.env.AIPROXY_API_ENDPOINT}/v1`
|
||||||
|
: undefined;
|
||||||
|
const openaiBaseUrl = aiProxyBaseUrl || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
||||||
|
const openaiBaseKey = process.env.AIPROXY_API_TOKEN || process.env.CHAT_API_KEY || '';
|
||||||
|
|
||||||
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
|
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
|
||||||
const { userKey, timeout } = props || {};
|
const { userKey, timeout } = props || {};
|
||||||
|
|
||||||
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
||||||
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
|
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || openaiBaseKey;
|
||||||
|
|
||||||
return new OpenAI({
|
return new OpenAI({
|
||||||
baseURL: baseUrl,
|
baseURL: baseUrl,
|
||||||
apiKey,
|
apiKey,
|
||||||
@@ -32,7 +35,7 @@ export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
|
|||||||
const { userKey } = props || {};
|
const { userKey } = props || {};
|
||||||
|
|
||||||
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
|
||||||
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
|
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || openaiBaseKey;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -72,6 +75,7 @@ export const createChatCompletion = async ({
|
|||||||
userKey,
|
userKey,
|
||||||
timeout: formatTimeout
|
timeout: formatTimeout
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await ai.chat.completions.create(body, {
|
const response = await ai.chat.completions.create(body, {
|
||||||
...options,
|
...options,
|
||||||
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),
|
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
{
|
{
|
||||||
"provider": "AliCloud",
|
"provider": "AliCloud",
|
||||||
"list": []
|
"list": [
|
||||||
}
|
{
|
||||||
|
"model": "SenseVoiceSmall",
|
||||||
|
"name": "SenseVoiceSmall",
|
||||||
|
"type": "stt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,30 @@
|
|||||||
{
|
{
|
||||||
"provider": "Claude",
|
"provider": "Claude",
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"model": "claude-3-7-sonnet-20250219",
|
||||||
|
"name": "claude-3-7-sonnet-20250219",
|
||||||
|
"maxContext": 200000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 100000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true,
|
||||||
|
"vision": true,
|
||||||
|
"toolChoice": true,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "claude-3-5-haiku-20241022",
|
"model": "claude-3-5-haiku-20241022",
|
||||||
"name": "claude-3-5-haiku-20241022",
|
"name": "claude-3-5-haiku-20241022",
|
||||||
@@ -10,7 +34,7 @@
|
|||||||
"maxTemperature": 1,
|
"maxTemperature": 1,
|
||||||
"showTopP": true,
|
"showTopP": true,
|
||||||
"showStopSign": true,
|
"showStopSign": true,
|
||||||
"vision": false,
|
"vision": true,
|
||||||
"toolChoice": true,
|
"toolChoice": true,
|
||||||
"functionCall": false,
|
"functionCall": false,
|
||||||
"defaultSystemChatPrompt": "",
|
"defaultSystemChatPrompt": "",
|
||||||
@@ -98,4 +122,4 @@
|
|||||||
"type": "llm"
|
"type": "llm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,29 @@
|
|||||||
{
|
{
|
||||||
"provider": "Grok",
|
"provider": "Grok",
|
||||||
"list": []
|
"list": [
|
||||||
}
|
{
|
||||||
|
"model": "grok-3",
|
||||||
|
"name": "grok-3",
|
||||||
|
"maxContext": 128000,
|
||||||
|
"maxResponse": 8000,
|
||||||
|
"quoteMaxToken": 128000,
|
||||||
|
"maxTemperature": 1,
|
||||||
|
"showTopP": true,
|
||||||
|
"showStopSign": true,
|
||||||
|
"vision": false,
|
||||||
|
"toolChoice": false,
|
||||||
|
"functionCall": false,
|
||||||
|
"defaultSystemChatPrompt": "",
|
||||||
|
"datasetProcess": true,
|
||||||
|
"usedInClassify": true,
|
||||||
|
"customCQPrompt": "",
|
||||||
|
"usedInExtractFields": true,
|
||||||
|
"usedInQueryExtension": true,
|
||||||
|
"customExtractPrompt": "",
|
||||||
|
"usedInToolCall": true,
|
||||||
|
"defaultConfig": {},
|
||||||
|
"fieldMap": {},
|
||||||
|
"type": "llm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ export const loadSystemModels = async (init = false) => {
|
|||||||
if (model.isDefault) {
|
if (model.isDefault) {
|
||||||
global.systemDefaultModel.llm = model;
|
global.systemDefaultModel.llm = model;
|
||||||
}
|
}
|
||||||
|
if (model.isDefaultDatasetTextModel) {
|
||||||
|
global.systemDefaultModel.datasetTextLLM = model;
|
||||||
|
}
|
||||||
|
if (model.isDefaultDatasetImageModel) {
|
||||||
|
global.systemDefaultModel.datasetImageLLM = model;
|
||||||
|
}
|
||||||
} else if (model.type === ModelTypeEnum.embedding) {
|
} else if (model.type === ModelTypeEnum.embedding) {
|
||||||
global.embeddingModelMap.set(model.model, model);
|
global.embeddingModelMap.set(model.model, model);
|
||||||
global.embeddingModelMap.set(model.name, model);
|
global.embeddingModelMap.set(model.name, model);
|
||||||
@@ -134,6 +140,16 @@ export const loadSystemModels = async (init = false) => {
|
|||||||
if (!global.systemDefaultModel.llm) {
|
if (!global.systemDefaultModel.llm) {
|
||||||
global.systemDefaultModel.llm = Array.from(global.llmModelMap.values())[0];
|
global.systemDefaultModel.llm = Array.from(global.llmModelMap.values())[0];
|
||||||
}
|
}
|
||||||
|
if (!global.systemDefaultModel.datasetTextLLM) {
|
||||||
|
global.systemDefaultModel.datasetTextLLM = Array.from(global.llmModelMap.values()).find(
|
||||||
|
(item) => item.datasetProcess
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!global.systemDefaultModel.datasetImageLLM) {
|
||||||
|
global.systemDefaultModel.datasetImageLLM = Array.from(global.llmModelMap.values()).find(
|
||||||
|
(item) => item.vision
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!global.systemDefaultModel.embedding) {
|
if (!global.systemDefaultModel.embedding) {
|
||||||
global.systemDefaultModel.embedding = Array.from(global.embeddingModelMap.values())[0];
|
global.systemDefaultModel.embedding = Array.from(global.embeddingModelMap.values())[0];
|
||||||
}
|
}
|
||||||
|
|||||||
3
packages/service/core/ai/type.d.ts
vendored
3
packages/service/core/ai/type.d.ts
vendored
@@ -22,6 +22,9 @@ export type SystemModelItemType =
|
|||||||
|
|
||||||
export type SystemDefaultModelType = {
|
export type SystemDefaultModelType = {
|
||||||
[ModelTypeEnum.llm]?: LLMModelItemType;
|
[ModelTypeEnum.llm]?: LLMModelItemType;
|
||||||
|
datasetTextLLM?: LLMModelItemType;
|
||||||
|
datasetImageLLM?: LLMModelItemType;
|
||||||
|
|
||||||
[ModelTypeEnum.embedding]?: EmbeddingModelItemType;
|
[ModelTypeEnum.embedding]?: EmbeddingModelItemType;
|
||||||
[ModelTypeEnum.tts]?: TTSModelType;
|
[ModelTypeEnum.tts]?: TTSModelType;
|
||||||
[ModelTypeEnum.stt]?: STTModelType;
|
[ModelTypeEnum.stt]?: STTModelType;
|
||||||
|
|||||||
@@ -95,11 +95,145 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
|
|||||||
return requestBody as unknown as InferCompletionsBody<T>;
|
return requestBody as unknown as InferCompletionsBody<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const llmStreamResponseToText = async (response: StreamChatType) => {
|
export const llmStreamResponseToAnswerText = async (response: StreamChatType) => {
|
||||||
let answer = '';
|
let answer = '';
|
||||||
for await (const part of response) {
|
for await (const part of response) {
|
||||||
const content = part.choices?.[0]?.delta?.content || '';
|
const content = part.choices?.[0]?.delta?.content || '';
|
||||||
answer += content;
|
answer += content;
|
||||||
}
|
}
|
||||||
return answer;
|
return parseReasoningContent(answer)[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse <think></think> tags to think and answer - unstream response
|
||||||
|
export const parseReasoningContent = (text: string): [string, string] => {
|
||||||
|
const regex = /<think>([\s\S]*?)<\/think>/;
|
||||||
|
const match = text.match(regex);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return ['', text];
|
||||||
|
}
|
||||||
|
|
||||||
|
const thinkContent = match[1].trim();
|
||||||
|
|
||||||
|
// Add answer (remaining text after think tag)
|
||||||
|
const answerContent = text.slice(match.index! + match[0].length);
|
||||||
|
|
||||||
|
return [thinkContent, answerContent];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse <think></think> tags to think and answer - stream response
|
||||||
|
export const parseReasoningStreamContent = () => {
|
||||||
|
let isInThinkTag: boolean | undefined;
|
||||||
|
|
||||||
|
const startTag = '<think>';
|
||||||
|
let startTagBuffer = '';
|
||||||
|
|
||||||
|
const endTag = '</think>';
|
||||||
|
let endTagBuffer = '';
|
||||||
|
|
||||||
|
/*
|
||||||
|
parseReasoning - 只控制是否主动解析 <think></think>,如果接口已经解析了,仍然会返回 think 内容。
|
||||||
|
*/
|
||||||
|
const parsePart = (
|
||||||
|
part: {
|
||||||
|
choices: {
|
||||||
|
delta: {
|
||||||
|
content?: string;
|
||||||
|
reasoning_content?: string;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
},
|
||||||
|
parseReasoning = false
|
||||||
|
): [string, string] => {
|
||||||
|
const content = part.choices?.[0]?.delta?.content || '';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
|
||||||
|
if (reasoningContent || !parseReasoning) {
|
||||||
|
isInThinkTag = false;
|
||||||
|
return [reasoningContent, content];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
return ['', ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content
|
||||||
|
if (isInThinkTag === false) {
|
||||||
|
return ['', content];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测是否为 think 标签开头的数据
|
||||||
|
if (isInThinkTag === undefined) {
|
||||||
|
// Parse content think and answer
|
||||||
|
startTagBuffer += content;
|
||||||
|
// 太少内容时候,暂时不解析
|
||||||
|
if (startTagBuffer.length < startTag.length) {
|
||||||
|
return ['', ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startTagBuffer.startsWith(startTag)) {
|
||||||
|
isInThinkTag = true;
|
||||||
|
return [startTagBuffer.slice(startTag.length), ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content
|
||||||
|
isInThinkTag = false;
|
||||||
|
return ['', startTagBuffer];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认是 think 标签内容,开始返回 think 内容,并实时检测 </think>
|
||||||
|
/*
|
||||||
|
检测 </think> 方案。
|
||||||
|
存储所有疑似 </think> 的内容,直到检测到完整的 </think> 标签或超出 </think> 长度。
|
||||||
|
content 返回值包含以下几种情况:
|
||||||
|
abc - 完全未命中尾标签
|
||||||
|
abc<th - 命中一部分尾标签
|
||||||
|
abc</think> - 完全命中尾标签
|
||||||
|
abc</think>abc - 完全命中尾标签
|
||||||
|
</think>abc - 完全命中尾标签
|
||||||
|
k>abc - 命中一部分尾标签
|
||||||
|
*/
|
||||||
|
// endTagBuffer 专门用来记录疑似尾标签的内容
|
||||||
|
if (endTagBuffer) {
|
||||||
|
endTagBuffer += content;
|
||||||
|
if (endTagBuffer.includes(endTag)) {
|
||||||
|
isInThinkTag = false;
|
||||||
|
const answer = endTagBuffer.slice(endTag.length);
|
||||||
|
return ['', answer];
|
||||||
|
} else if (endTagBuffer.length >= endTag.length) {
|
||||||
|
// 缓存内容超出尾标签长度,且仍未命中 </think>,则认为本次猜测 </think> 失败,仍处于 think 阶段。
|
||||||
|
const tmp = endTagBuffer;
|
||||||
|
endTagBuffer = '';
|
||||||
|
return [tmp, ''];
|
||||||
|
}
|
||||||
|
return ['', ''];
|
||||||
|
} else if (content.includes(endTag)) {
|
||||||
|
// 返回内容,完整命中</think>,直接结束
|
||||||
|
isInThinkTag = false;
|
||||||
|
const [think, answer] = content.split(endTag);
|
||||||
|
return [think, answer];
|
||||||
|
} else {
|
||||||
|
// 无 buffer,且未命中 </think>,开始疑似 </think> 检测。
|
||||||
|
for (let i = 1; i < endTag.length; i++) {
|
||||||
|
const partialEndTag = endTag.slice(0, i);
|
||||||
|
// 命中一部分尾标签
|
||||||
|
if (content.endsWith(partialEndTag)) {
|
||||||
|
const think = content.slice(0, -partialEndTag.length);
|
||||||
|
endTagBuffer += partialEndTag;
|
||||||
|
return [think, ''];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完全未命中尾标签,还是 think 阶段。
|
||||||
|
return [content, ''];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStartTagBuffer = () => startTagBuffer;
|
||||||
|
|
||||||
|
return {
|
||||||
|
parsePart,
|
||||||
|
getStartTagBuffer
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
1
packages/service/core/app/plugin/type.d.ts
vendored
1
packages/service/core/app/plugin/type.d.ts
vendored
@@ -25,6 +25,7 @@ export type SystemPluginConfigSchemaType = {
|
|||||||
templateType: string;
|
templateType: string;
|
||||||
associatedPluginId: string;
|
associatedPluginId: string;
|
||||||
userGuide: string;
|
userGuide: string;
|
||||||
|
author?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ const AppTemplateSchema = new Schema({
|
|||||||
avatar: {
|
avatar: {
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
|
author: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
tags: {
|
tags: {
|
||||||
type: [String],
|
type: [String],
|
||||||
default: undefined
|
default: undefined
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { MongoImage } from '../../../common/file/image/schema';
|
|||||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||||
import { addDays } from 'date-fns';
|
import { addDays } from 'date-fns';
|
||||||
import { MongoDatasetDataText } from '../data/dataTextSchema';
|
import { MongoDatasetDataText } from '../data/dataTextSchema';
|
||||||
|
import { delay, retryFn } from '@fastgpt/global/common/system/utils';
|
||||||
|
|
||||||
export const createCollectionAndInsertData = async ({
|
export const createCollectionAndInsertData = async ({
|
||||||
dataset,
|
dataset,
|
||||||
@@ -216,7 +217,7 @@ export async function createOneCollection({
|
|||||||
nextSyncTime
|
nextSyncTime
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
{ session }
|
{ session, ordered: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
return collection;
|
return collection;
|
||||||
@@ -227,8 +228,14 @@ export const delCollectionRelatedSource = async ({
|
|||||||
collections,
|
collections,
|
||||||
session
|
session
|
||||||
}: {
|
}: {
|
||||||
collections: DatasetCollectionSchemaType[];
|
collections: {
|
||||||
session: ClientSession;
|
teamId: string;
|
||||||
|
fileId?: string;
|
||||||
|
metadata?: {
|
||||||
|
relatedImgId?: string;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
session?: ClientSession;
|
||||||
}) => {
|
}) => {
|
||||||
if (collections.length === 0) return;
|
if (collections.length === 0) return;
|
||||||
|
|
||||||
@@ -259,11 +266,13 @@ export const delCollectionRelatedSource = async ({
|
|||||||
export async function delCollection({
|
export async function delCollection({
|
||||||
collections,
|
collections,
|
||||||
session,
|
session,
|
||||||
delRelatedSource
|
delImg = true,
|
||||||
|
delFile = true
|
||||||
}: {
|
}: {
|
||||||
collections: DatasetCollectionSchemaType[];
|
collections: DatasetCollectionSchemaType[];
|
||||||
session: ClientSession;
|
session: ClientSession;
|
||||||
delRelatedSource: boolean;
|
delImg: boolean;
|
||||||
|
delFile: boolean;
|
||||||
}) {
|
}) {
|
||||||
if (collections.length === 0) return;
|
if (collections.length === 0) return;
|
||||||
|
|
||||||
@@ -274,83 +283,55 @@ export async function delCollection({
|
|||||||
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
|
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
|
||||||
const collectionIds = collections.map((item) => String(item._id));
|
const collectionIds = collections.map((item) => String(item._id));
|
||||||
|
|
||||||
// Delete training data
|
await retryFn(async () => {
|
||||||
await MongoDatasetTraining.deleteMany({
|
await Promise.all([
|
||||||
teamId,
|
// Delete training data
|
||||||
datasetIds: { $in: datasetIds },
|
MongoDatasetTraining.deleteMany({
|
||||||
collectionId: { $in: collectionIds }
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
collectionId: { $in: collectionIds }
|
||||||
|
}),
|
||||||
|
// Delete dataset_data_texts
|
||||||
|
MongoDatasetDataText.deleteMany({
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
collectionId: { $in: collectionIds }
|
||||||
|
}),
|
||||||
|
// Delete dataset_datas
|
||||||
|
MongoDatasetData.deleteMany({
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
collectionId: { $in: collectionIds }
|
||||||
|
}),
|
||||||
|
...(delImg
|
||||||
|
? [
|
||||||
|
delImgByRelatedId({
|
||||||
|
teamId,
|
||||||
|
relateIds: collections
|
||||||
|
.map((item) => item?.metadata?.relatedImgId || '')
|
||||||
|
.filter(Boolean)
|
||||||
|
})
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...(delFile
|
||||||
|
? [
|
||||||
|
delFileByFileIdList({
|
||||||
|
bucketName: BucketNameEnum.dataset,
|
||||||
|
fileIdList: collections.map((item) => item?.fileId || '').filter(Boolean)
|
||||||
|
})
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
// Delete vector data
|
||||||
|
deleteDatasetDataVector({ teamId, datasetIds, collectionIds })
|
||||||
|
]);
|
||||||
|
|
||||||
|
// delete collections
|
||||||
|
await MongoDatasetCollection.deleteMany(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
_id: { $in: collectionIds }
|
||||||
|
},
|
||||||
|
{ session }
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/* file and imgs */
|
|
||||||
if (delRelatedSource) {
|
|
||||||
await delCollectionRelatedSource({ collections, session });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete dataset_datas
|
|
||||||
await MongoDatasetData.deleteMany(
|
|
||||||
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
|
||||||
{ session }
|
|
||||||
);
|
|
||||||
// Delete dataset_data_texts
|
|
||||||
await MongoDatasetDataText.deleteMany(
|
|
||||||
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
|
||||||
{ session }
|
|
||||||
);
|
|
||||||
|
|
||||||
// delete collections
|
|
||||||
await MongoDatasetCollection.deleteMany(
|
|
||||||
{
|
|
||||||
teamId,
|
|
||||||
_id: { $in: collectionIds }
|
|
||||||
},
|
|
||||||
{ session }
|
|
||||||
);
|
|
||||||
|
|
||||||
// no session delete: delete files, vector data
|
|
||||||
await deleteDatasetDataVector({ teamId, datasetIds, collectionIds });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* delete delOnlyCollection
|
|
||||||
*/
|
|
||||||
export async function delOnlyCollection({
|
|
||||||
collections,
|
|
||||||
session
|
|
||||||
}: {
|
|
||||||
collections: DatasetCollectionSchemaType[];
|
|
||||||
session: ClientSession;
|
|
||||||
}) {
|
|
||||||
if (collections.length === 0) return;
|
|
||||||
|
|
||||||
const teamId = collections[0].teamId;
|
|
||||||
|
|
||||||
if (!teamId) return Promise.reject('teamId is not exist');
|
|
||||||
|
|
||||||
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
|
|
||||||
const collectionIds = collections.map((item) => String(item._id));
|
|
||||||
|
|
||||||
// delete training data
|
|
||||||
await MongoDatasetTraining.deleteMany({
|
|
||||||
teamId,
|
|
||||||
datasetIds: { $in: datasetIds },
|
|
||||||
collectionId: { $in: collectionIds }
|
|
||||||
});
|
|
||||||
|
|
||||||
// delete dataset.datas
|
|
||||||
await MongoDatasetData.deleteMany(
|
|
||||||
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
|
||||||
{ session }
|
|
||||||
);
|
|
||||||
|
|
||||||
// delete collections
|
|
||||||
await MongoDatasetCollection.deleteMany(
|
|
||||||
{
|
|
||||||
teamId,
|
|
||||||
_id: { $in: collectionIds }
|
|
||||||
},
|
|
||||||
{ session }
|
|
||||||
);
|
|
||||||
|
|
||||||
// no session delete: delete files, vector data
|
|
||||||
await deleteDatasetDataVector({ teamId, datasetIds, collectionIds });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export const createOrGetCollectionTags = async ({
|
|||||||
datasetId,
|
datasetId,
|
||||||
tag: tagContent
|
tag: tagContent
|
||||||
})),
|
})),
|
||||||
{ session }
|
{ session, ordered: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
return [...existingTags.map((tag) => tag._id), ...newTags.map((tag) => tag._id)];
|
return [...existingTags.map((tag) => tag._id), ...newTags.map((tag) => tag._id)];
|
||||||
@@ -174,6 +174,14 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await mongoSessionRun(async (session) => {
|
await mongoSessionRun(async (session) => {
|
||||||
|
// Delete old collection
|
||||||
|
await delCollection({
|
||||||
|
collections: [collection],
|
||||||
|
delImg: false,
|
||||||
|
delFile: false,
|
||||||
|
session
|
||||||
|
});
|
||||||
|
|
||||||
// Create new collection
|
// Create new collection
|
||||||
await createCollectionAndInsertData({
|
await createCollectionAndInsertData({
|
||||||
session,
|
session,
|
||||||
@@ -208,13 +216,6 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
|
|||||||
updateTime: new Date()
|
updateTime: new Date()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete old collection
|
|
||||||
await delCollection({
|
|
||||||
collections: [collection],
|
|
||||||
delRelatedSource: false,
|
|
||||||
session
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return DatasetCollectionSyncResultEnum.success;
|
return DatasetCollectionSyncResultEnum.success;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { MongoDatasetTraining } from './training/schema';
|
|||||||
import { MongoDatasetData } from './data/schema';
|
import { MongoDatasetData } from './data/schema';
|
||||||
import { deleteDatasetDataVector } from '../../common/vectorStore/controller';
|
import { deleteDatasetDataVector } from '../../common/vectorStore/controller';
|
||||||
import { MongoDatasetDataText } from './data/dataTextSchema';
|
import { MongoDatasetDataText } from './data/dataTextSchema';
|
||||||
|
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||||
|
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||||
|
|
||||||
/* ============= dataset ========== */
|
/* ============= dataset ========== */
|
||||||
/* find all datasetId by top datasetId */
|
/* find all datasetId by top datasetId */
|
||||||
@@ -54,7 +56,7 @@ export async function getCollectionWithDataset(collectionId: string) {
|
|||||||
.populate<{ dataset: DatasetSchemaType }>('dataset')
|
.populate<{ dataset: DatasetSchemaType }>('dataset')
|
||||||
.lean();
|
.lean();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return Promise.reject('Collection is not exist');
|
return Promise.reject(DatasetErrEnum.unExistCollection);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -77,40 +79,39 @@ export async function delDatasetRelevantData({
|
|||||||
|
|
||||||
const datasetIds = datasets.map((item) => item._id);
|
const datasetIds = datasets.map((item) => item._id);
|
||||||
|
|
||||||
// delete training data
|
|
||||||
await MongoDatasetTraining.deleteMany({
|
|
||||||
teamId,
|
|
||||||
datasetId: { $in: datasetIds }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get _id, teamId, fileId, metadata.relatedImgId for all collections
|
// Get _id, teamId, fileId, metadata.relatedImgId for all collections
|
||||||
const collections = await MongoDatasetCollection.find(
|
const collections = await MongoDatasetCollection.find(
|
||||||
{
|
{
|
||||||
teamId,
|
teamId,
|
||||||
datasetId: { $in: datasetIds }
|
datasetId: { $in: datasetIds }
|
||||||
},
|
},
|
||||||
'_id teamId datasetId fileId metadata',
|
'_id teamId datasetId fileId metadata'
|
||||||
{ session }
|
|
||||||
).lean();
|
).lean();
|
||||||
|
|
||||||
// Delete Image and file
|
await retryFn(async () => {
|
||||||
await delCollectionRelatedSource({ collections, session });
|
await Promise.all([
|
||||||
|
// delete training data
|
||||||
|
MongoDatasetTraining.deleteMany({
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds }
|
||||||
|
}),
|
||||||
|
//Delete dataset_data_texts
|
||||||
|
MongoDatasetDataText.deleteMany({
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds }
|
||||||
|
}),
|
||||||
|
//delete dataset_datas
|
||||||
|
MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } }),
|
||||||
|
// Delete Image and file
|
||||||
|
delCollectionRelatedSource({ collections }),
|
||||||
|
// Delete vector data
|
||||||
|
deleteDatasetDataVector({ teamId, datasetIds })
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
// delete collections
|
// delete collections
|
||||||
await MongoDatasetCollection.deleteMany({
|
await MongoDatasetCollection.deleteMany({
|
||||||
teamId,
|
teamId,
|
||||||
datasetId: { $in: datasetIds }
|
datasetId: { $in: datasetIds }
|
||||||
}).session(session);
|
}).session(session);
|
||||||
|
|
||||||
// No session delete:
|
|
||||||
// Delete dataset_data_texts
|
|
||||||
await MongoDatasetDataText.deleteMany({
|
|
||||||
teamId,
|
|
||||||
datasetId: { $in: datasetIds }
|
|
||||||
});
|
|
||||||
// delete dataset_datas
|
|
||||||
await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } });
|
|
||||||
|
|
||||||
// Delete vector data
|
|
||||||
await deleteDatasetDataVector({ teamId, datasetIds });
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||||
const { Schema } = connectionMongo;
|
const { Schema } = connectionMongo;
|
||||||
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
import { DatasetDataTextSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
||||||
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||||
import { DatasetCollectionName } from '../schema';
|
import { DatasetCollectionName } from '../schema';
|
||||||
import { DatasetColCollectionName } from '../collection/schema';
|
import { DatasetColCollectionName } from '../collection/schema';
|
||||||
@@ -40,12 +40,13 @@ try {
|
|||||||
default_language: 'none'
|
default_language: 'none'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
DatasetDataTextSchema.index({ teamId: 1, datasetId: 1, collectionId: 1 });
|
||||||
DatasetDataTextSchema.index({ dataId: 1 }, { unique: true });
|
DatasetDataTextSchema.index({ dataId: 1 }, { unique: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MongoDatasetDataText = getMongoModel<DatasetDataSchemaType>(
|
export const MongoDatasetDataText = getMongoModel<DatasetDataTextSchemaType>(
|
||||||
DatasetDataTextCollectionName,
|
DatasetDataTextCollectionName,
|
||||||
DatasetDataTextSchema
|
DatasetDataTextSchema
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ export async function searchDatasetData(
|
|||||||
forbidCollectionIdList: collections.map((item) => String(item._id))
|
forbidCollectionIdList: collections.map((item) => String(item._id))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Collection metadata filter
|
Collection metadata filter
|
||||||
标签过滤:
|
标签过滤:
|
||||||
@@ -207,6 +208,63 @@ export async function searchDatasetData(
|
|||||||
2. and 标签和 null 不能共存,否则返回空数组
|
2. and 标签和 null 不能共存,否则返回空数组
|
||||||
*/
|
*/
|
||||||
const filterCollectionByMetadata = async (): Promise<string[] | undefined> => {
|
const filterCollectionByMetadata = async (): Promise<string[] | undefined> => {
|
||||||
|
const getAllCollectionIds = async ({
|
||||||
|
parentCollectionIds
|
||||||
|
}: {
|
||||||
|
parentCollectionIds?: string[];
|
||||||
|
}): Promise<string[] | undefined> => {
|
||||||
|
if (!parentCollectionIds) return;
|
||||||
|
if (parentCollectionIds.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const collections = await MongoDatasetCollection.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
_id: { $in: parentCollectionIds }
|
||||||
|
},
|
||||||
|
'_id type',
|
||||||
|
{
|
||||||
|
...readFromSecondary
|
||||||
|
}
|
||||||
|
).lean();
|
||||||
|
|
||||||
|
const resultIds = new Set<string>();
|
||||||
|
collections.forEach((item) => {
|
||||||
|
if (item.type !== 'folder') {
|
||||||
|
resultIds.add(String(item._id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const folderIds = collections
|
||||||
|
.filter((item) => item.type === 'folder')
|
||||||
|
.map((item) => String(item._id));
|
||||||
|
|
||||||
|
// Get all child collection ids
|
||||||
|
if (folderIds.length) {
|
||||||
|
const childCollections = await MongoDatasetCollection.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
datasetId: { $in: datasetIds },
|
||||||
|
parentId: { $in: folderIds }
|
||||||
|
},
|
||||||
|
'_id type',
|
||||||
|
{
|
||||||
|
...readFromSecondary
|
||||||
|
}
|
||||||
|
).lean();
|
||||||
|
|
||||||
|
const childIds = await getAllCollectionIds({
|
||||||
|
parentCollectionIds: childCollections.map((item) => String(item._id))
|
||||||
|
});
|
||||||
|
|
||||||
|
childIds?.forEach((id) => resultIds.add(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(resultIds);
|
||||||
|
};
|
||||||
|
|
||||||
if (!collectionFilterMatch || !global.feConfigs.isPlus) return;
|
if (!collectionFilterMatch || !global.feConfigs.isPlus) return;
|
||||||
|
|
||||||
let tagCollectionIdList: string[] | undefined = undefined;
|
let tagCollectionIdList: string[] | undefined = undefined;
|
||||||
@@ -326,13 +384,19 @@ export async function searchDatasetData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Concat tag and time
|
// Concat tag and time
|
||||||
if (tagCollectionIdList && createTimeCollectionIdList) {
|
const collectionIds = (() => {
|
||||||
return tagCollectionIdList.filter((id) => createTimeCollectionIdList!.includes(id));
|
if (tagCollectionIdList && createTimeCollectionIdList) {
|
||||||
} else if (tagCollectionIdList) {
|
return tagCollectionIdList.filter((id) =>
|
||||||
return tagCollectionIdList;
|
(createTimeCollectionIdList as string[]).includes(id)
|
||||||
} else if (createTimeCollectionIdList) {
|
);
|
||||||
return createTimeCollectionIdList;
|
}
|
||||||
}
|
|
||||||
|
return tagCollectionIdList || createTimeCollectionIdList;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return await getAllCollectionIds({
|
||||||
|
parentCollectionIds: collectionIds
|
||||||
|
});
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
const embeddingRecall = async ({
|
const embeddingRecall = async ({
|
||||||
|
|||||||
@@ -3,11 +3,8 @@ import { filterGPTMessageByMaxContext, loadRequestMessages } from '../../../chat
|
|||||||
import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import {
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
parseReasoningContent,
|
import { parseReasoningContent, parseReasoningStreamContent } from '../../../ai/utils';
|
||||||
parseReasoningStreamContent,
|
|
||||||
textAdaptGptResponse
|
|
||||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { createChatCompletion } from '../../../ai/config';
|
||||||
import type { ChatCompletionMessageParam, StreamChatType } from '@fastgpt/global/core/ai/type.d';
|
import type { ChatCompletionMessageParam, StreamChatType } from '@fastgpt/global/core/ai/type.d';
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
|
|||||||
|
|
||||||
const response = await dispatchWorkFlow({
|
const response = await dispatchWorkFlow({
|
||||||
...props,
|
...props,
|
||||||
|
variables: newVariables,
|
||||||
runtimeEdges: cloneDeep(runtimeEdges)
|
runtimeEdges: cloneDeep(runtimeEdges)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -120,27 +120,144 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
|||||||
2. Replace newline strings
|
2. Replace newline strings
|
||||||
*/
|
*/
|
||||||
const replaceJsonBodyString = (text: string) => {
|
const replaceJsonBodyString = (text: string) => {
|
||||||
const valToStr = (val: any) => {
|
// Check if the variable is in quotes
|
||||||
|
const isVariableInQuotes = (text: string, variable: string) => {
|
||||||
|
const index = text.indexOf(variable);
|
||||||
|
if (index === -1) return false;
|
||||||
|
|
||||||
|
// 计算变量前面的引号数量
|
||||||
|
const textBeforeVar = text.substring(0, index);
|
||||||
|
const matches = textBeforeVar.match(/"/g) || [];
|
||||||
|
|
||||||
|
// 如果引号数量为奇数,则变量在引号内
|
||||||
|
return matches.length % 2 === 1;
|
||||||
|
};
|
||||||
|
const valToStr = (val: any, isQuoted = false) => {
|
||||||
if (val === undefined) return 'null';
|
if (val === undefined) return 'null';
|
||||||
if (val === null) return 'null';
|
if (val === null) return 'null';
|
||||||
|
|
||||||
if (typeof val === 'object') return JSON.stringify(val);
|
if (typeof val === 'object') return JSON.stringify(val);
|
||||||
|
|
||||||
if (typeof val === 'string') {
|
if (typeof val === 'string') {
|
||||||
|
if (isQuoted) {
|
||||||
|
return val.replace(/(?<!\\)"/g, '\\"');
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(val);
|
JSON.parse(val);
|
||||||
if (typeof parsed === 'object') {
|
|
||||||
return JSON.stringify(parsed);
|
|
||||||
}
|
|
||||||
return val;
|
return val;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const str = JSON.stringify(val);
|
const str = JSON.stringify(val);
|
||||||
|
|
||||||
return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
|
return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return String(val);
|
return String(val);
|
||||||
};
|
};
|
||||||
|
// Test cases for variable replacement in JSON body
|
||||||
|
// const bodyTest = () => {
|
||||||
|
// const testData = [
|
||||||
|
// // 基本字符串替换
|
||||||
|
// {
|
||||||
|
// body: `{"name":"{{name}}","age":"18"}`,
|
||||||
|
// variables: [{ key: '{{name}}', value: '测试' }],
|
||||||
|
// result: `{"name":"测试","age":"18"}`
|
||||||
|
// },
|
||||||
|
// // 特殊字符处理
|
||||||
|
// {
|
||||||
|
// body: `{"text":"{{text}}"}`,
|
||||||
|
// variables: [{ key: '{{text}}', value: '包含"引号"和\\反斜杠' }],
|
||||||
|
// result: `{"text":"包含\\"引号\\"和\\反斜杠"}`
|
||||||
|
// },
|
||||||
|
// // 数字类型处理
|
||||||
|
// {
|
||||||
|
// body: `{"count":{{count}},"price":{{price}}}`,
|
||||||
|
// variables: [
|
||||||
|
// { key: '{{count}}', value: '42' },
|
||||||
|
// { key: '{{price}}', value: '99.99' }
|
||||||
|
// ],
|
||||||
|
// result: `{"count":42,"price":99.99}`
|
||||||
|
// },
|
||||||
|
// // 布尔值处理
|
||||||
|
// {
|
||||||
|
// body: `{"isActive":{{isActive}},"hasData":{{hasData}}}`,
|
||||||
|
// variables: [
|
||||||
|
// { key: '{{isActive}}', value: 'true' },
|
||||||
|
// { key: '{{hasData}}', value: 'false' }
|
||||||
|
// ],
|
||||||
|
// result: `{"isActive":true,"hasData":false}`
|
||||||
|
// },
|
||||||
|
// // 对象类型处理
|
||||||
|
// {
|
||||||
|
// body: `{"user":{{user}},"user2":"{{user2}}"}`,
|
||||||
|
// variables: [
|
||||||
|
// { key: '{{user}}', value: `{"id":1,"name":"张三"}` },
|
||||||
|
// { key: '{{user2}}', value: `{"id":1,"name":"张三"}` }
|
||||||
|
// ],
|
||||||
|
// result: `{"user":{"id":1,"name":"张三"},"user2":"{\\"id\\":1,\\"name\\":\\"张三\\"}"}`
|
||||||
|
// },
|
||||||
|
// // 数组类型处理
|
||||||
|
// {
|
||||||
|
// body: `{"items":{{items}}}`,
|
||||||
|
// variables: [{ key: '{{items}}', value: '[1, 2, 3]' }],
|
||||||
|
// result: `{"items":[1,2,3]}`
|
||||||
|
// },
|
||||||
|
// // null 和 undefined 处理
|
||||||
|
// {
|
||||||
|
// body: `{"nullValue":{{nullValue}},"undefinedValue":{{undefinedValue}}}`,
|
||||||
|
// variables: [
|
||||||
|
// { key: '{{nullValue}}', value: 'null' },
|
||||||
|
// { key: '{{undefinedValue}}', value: 'undefined' }
|
||||||
|
// ],
|
||||||
|
// result: `{"nullValue":null,"undefinedValue":null}`
|
||||||
|
// },
|
||||||
|
// // 嵌套JSON结构
|
||||||
|
// {
|
||||||
|
// body: `{"data":{"nested":{"value":"{{nestedValue}}"}}}`,
|
||||||
|
// variables: [{ key: '{{nestedValue}}', value: '嵌套值' }],
|
||||||
|
// result: `{"data":{"nested":{"value":"嵌套值"}}}`
|
||||||
|
// },
|
||||||
|
// // 多变量替换
|
||||||
|
// {
|
||||||
|
// body: `{"first":"{{first}}","second":"{{second}}","third":{{third}}}`,
|
||||||
|
// variables: [
|
||||||
|
// { key: '{{first}}', value: '第一' },
|
||||||
|
// { key: '{{second}}', value: '第二' },
|
||||||
|
// { key: '{{third}}', value: '3' }
|
||||||
|
// ],
|
||||||
|
// result: `{"first":"第一","second":"第二","third":3}`
|
||||||
|
// },
|
||||||
|
// // JSON字符串作为变量值
|
||||||
|
// {
|
||||||
|
// body: `{"config":{{config}}}`,
|
||||||
|
// variables: [{ key: '{{config}}', value: '{"setting":"enabled","mode":"advanced"}' }],
|
||||||
|
// result: `{"config":{"setting":"enabled","mode":"advanced"}}`
|
||||||
|
// }
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// for (let i = 0; i < testData.length; i++) {
|
||||||
|
// const item = testData[i];
|
||||||
|
// let bodyStr = item.body;
|
||||||
|
// for (const variable of item.variables) {
|
||||||
|
// const isQuote = isVariableInQuotes(bodyStr, variable.key);
|
||||||
|
// bodyStr = bodyStr.replace(variable.key, valToStr(variable.value, isQuote));
|
||||||
|
// }
|
||||||
|
// bodyStr = bodyStr.replace(/(".*?")\s*:\s*undefined\b/g, '$1:null');
|
||||||
|
|
||||||
|
// console.log(bodyStr === item.result, i);
|
||||||
|
// if (bodyStr !== item.result) {
|
||||||
|
// console.log(bodyStr);
|
||||||
|
// console.log(item.result);
|
||||||
|
// } else {
|
||||||
|
// try {
|
||||||
|
// JSON.parse(item.result);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.log('反序列化异常', i, item.result);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// bodyTest();
|
||||||
|
|
||||||
// 1. Replace {{key.key}} variables
|
// 1. Replace {{key.key}} variables
|
||||||
const regex1 = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g;
|
const regex1 = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g;
|
||||||
@@ -148,6 +265,10 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
|||||||
matches1.forEach((match) => {
|
matches1.forEach((match) => {
|
||||||
const nodeId = match[1];
|
const nodeId = match[1];
|
||||||
const id = match[2];
|
const id = match[2];
|
||||||
|
const fullMatch = match[0];
|
||||||
|
|
||||||
|
// 检查变量是否在引号内
|
||||||
|
const isInQuotes = isVariableInQuotes(text, fullMatch);
|
||||||
|
|
||||||
const variableVal = (() => {
|
const variableVal = (() => {
|
||||||
if (nodeId === VARIABLE_NODE_ID) {
|
if (nodeId === VARIABLE_NODE_ID) {
|
||||||
@@ -165,9 +286,9 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
|||||||
return getReferenceVariableValue({ value: input.value, nodes: runtimeNodes, variables });
|
return getReferenceVariableValue({ value: input.value, nodes: runtimeNodes, variables });
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const formatVal = valToStr(variableVal);
|
const formatVal = valToStr(variableVal, isInQuotes);
|
||||||
|
|
||||||
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, 'g');
|
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, '');
|
||||||
text = text.replace(regex, () => formatVal);
|
text = text.replace(regex, () => formatVal);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -176,10 +297,16 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
|||||||
const matches2 = text.match(regex2) || [];
|
const matches2 = text.match(regex2) || [];
|
||||||
const uniqueKeys2 = [...new Set(matches2.map((match) => match.slice(2, -2)))];
|
const uniqueKeys2 = [...new Set(matches2.map((match) => match.slice(2, -2)))];
|
||||||
for (const key of uniqueKeys2) {
|
for (const key of uniqueKeys2) {
|
||||||
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), () => valToStr(allVariables[key]));
|
const fullMatch = `{{${key}}}`;
|
||||||
|
// 检查变量是否在引号内
|
||||||
|
const isInQuotes = isVariableInQuotes(text, fullMatch);
|
||||||
|
|
||||||
|
text = text.replace(new RegExp(`{{(${key})}}`, ''), () =>
|
||||||
|
valToStr(allVariables[key], isInQuotes)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return text.replace(/(".*?")\s*:\s*undefined\b/g, '$1: null');
|
return text.replace(/(".*?")\s*:\s*undefined\b/g, '$1:null');
|
||||||
};
|
};
|
||||||
|
|
||||||
httpReqUrl = replaceStringVariables(httpReqUrl);
|
httpReqUrl = replaceStringVariables(httpReqUrl);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mammoth": "^1.6.0",
|
"mammoth": "^1.6.0",
|
||||||
"mongoose": "^7.0.2",
|
"mongoose": "^8.10.1",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"next": "14.2.5",
|
"next": "14.2.5",
|
||||||
"nextjs-cors": "^2.2.0",
|
"nextjs-cors": "^2.2.0",
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export const getClbsAndGroupsWithInfo = async ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const delResourcePermissionById = (id: string) => {
|
export const delResourcePermissionById = (id: string) => {
|
||||||
return MongoResourcePermission.findByIdAndRemove(id);
|
return MongoResourcePermission.findByIdAndDelete(id);
|
||||||
};
|
};
|
||||||
export const delResourcePermission = ({
|
export const delResourcePermission = ({
|
||||||
session,
|
session,
|
||||||
|
|||||||
@@ -196,7 +196,8 @@ export async function syncCollaborators({
|
|||||||
permission: item.permission
|
permission: item.permission
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
session
|
session,
|
||||||
|
ordered: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export async function authOutLinkCrud({
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* outLink exist and it app exist */
|
/* outLink exist and it app exist */
|
||||||
export async function authOutLinkValid<T extends OutlinkAppType = undefined>({
|
export async function authOutLinkValid<T extends OutlinkAppType = any>({
|
||||||
shareId
|
shareId
|
||||||
}: {
|
}: {
|
||||||
shareId?: string;
|
shareId?: string;
|
||||||
@@ -62,7 +62,7 @@ export async function authOutLinkValid<T extends OutlinkAppType = undefined>({
|
|||||||
if (!shareId) {
|
if (!shareId) {
|
||||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||||
}
|
}
|
||||||
const outLinkConfig = (await MongoOutLink.findOne({ shareId }).lean()) as OutLinkSchema<T>;
|
const outLinkConfig = await MongoOutLink.findOne({ shareId }).lean<OutLinkSchema<T>>();
|
||||||
|
|
||||||
if (!outLinkConfig) {
|
if (!outLinkConfig) {
|
||||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||||
@@ -70,6 +70,6 @@ export async function authOutLinkValid<T extends OutlinkAppType = undefined>({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
appId: outLinkConfig.appId,
|
appId: outLinkConfig.appId,
|
||||||
outLinkConfig
|
outLinkConfig: outLinkConfig
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
|
|||||||
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
||||||
const [{ standardConstants }, appCount] = await Promise.all([
|
const [{ standardConstants }, appCount] = await Promise.all([
|
||||||
getTeamStandPlan({ teamId }),
|
getTeamStandPlan({ teamId }),
|
||||||
MongoApp.count({
|
MongoApp.countDocuments({
|
||||||
teamId,
|
teamId,
|
||||||
type: { $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin] }
|
type: { $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin] }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/use
|
|||||||
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
|
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
|
||||||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
import { getAIApi, openaiBaseUrl } from '../../../core/ai/config';
|
import { getAIApi } from '../../../core/ai/config';
|
||||||
import { createRootOrg } from '../../permission/org/controllers';
|
import { createRootOrg } from '../../permission/org/controllers';
|
||||||
import { refreshSourceAvatar } from '../../../common/file/image/controller';
|
import { refreshSourceAvatar } from '../../../common/file/image/controller';
|
||||||
|
|
||||||
@@ -43,7 +43,6 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
|
|||||||
teamDomain: tmb.team?.teamDomain,
|
teamDomain: tmb.team?.teamDomain,
|
||||||
role: tmb.role,
|
role: tmb.role,
|
||||||
status: tmb.status,
|
status: tmb.status,
|
||||||
defaultTeam: tmb.defaultTeam,
|
|
||||||
permission: new TeamPermission({
|
permission: new TeamPermission({
|
||||||
per: Per ?? TeamDefaultPermissionVal,
|
per: Per ?? TeamDefaultPermissionVal,
|
||||||
isOwner: tmb.role === TeamMemberRoleEnum.owner
|
isOwner: tmb.role === TeamMemberRoleEnum.owner
|
||||||
@@ -71,8 +70,7 @@ export async function getUserDefaultTeam({ userId }: { userId: string }) {
|
|||||||
return Promise.reject('tmbId or userId is required');
|
return Promise.reject('tmbId or userId is required');
|
||||||
}
|
}
|
||||||
return getTeamMember({
|
return getTeamMember({
|
||||||
userId: new Types.ObjectId(userId),
|
userId: new Types.ObjectId(userId)
|
||||||
defaultTeam: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +150,7 @@ export async function updateTeam({
|
|||||||
// auth openai key
|
// auth openai key
|
||||||
if (openaiAccount?.key) {
|
if (openaiAccount?.key) {
|
||||||
console.log('auth user openai key', openaiAccount?.key);
|
console.log('auth user openai key', openaiAccount?.key);
|
||||||
const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl;
|
const baseUrl = openaiAccount?.baseUrl || 'https://api.openai.com/v1';
|
||||||
openaiAccount.baseUrl = baseUrl;
|
openaiAccount.baseUrl = baseUrl;
|
||||||
|
|
||||||
const ai = getAIApi({
|
const ai = getAIApi({
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ const TeamMemberSchema = new Schema({
|
|||||||
updateTime: {
|
updateTime: {
|
||||||
type: Date
|
type: Date
|
||||||
},
|
},
|
||||||
defaultTeam: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
|
|
||||||
// Abandoned
|
// Abandoned
|
||||||
role: {
|
role: {
|
||||||
type: String
|
type: String
|
||||||
|
},
|
||||||
|
// Abandoned
|
||||||
|
defaultTeam: {
|
||||||
|
type: Boolean
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export const initTeamFreePlan = async ({
|
|||||||
surplusPoints: freePoints
|
surplusPoints: freePoints
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
{ session }
|
{ session, ordered: true }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import { i18nT } from '../../../../web/i18n/utils';
|
|||||||
import { pushConcatBillTask, pushReduceTeamAiPointsTask } from './utils';
|
import { pushConcatBillTask, pushReduceTeamAiPointsTask } from './utils';
|
||||||
|
|
||||||
import { POST } from '../../../common/api/plusRequest';
|
import { POST } from '../../../common/api/plusRequest';
|
||||||
import { FastGPTProUrl } from '../../../common/system/constants';
|
import { isFastGPTMainService } from '../../../common/system/constants';
|
||||||
|
|
||||||
export async function createUsage(data: CreateUsageProps) {
|
export async function createUsage(data: CreateUsageProps) {
|
||||||
try {
|
try {
|
||||||
// In FastGPT server
|
// In FastGPT server
|
||||||
if (FastGPTProUrl) {
|
if (isFastGPTMainService) {
|
||||||
await POST('/support/wallet/usage/createUsage', data);
|
await POST('/support/wallet/usage/createUsage', data);
|
||||||
} else if (global.reduceAiPointsQueue) {
|
} else if (global.reduceAiPointsQueue) {
|
||||||
// In FastGPT pro server
|
// In FastGPT pro server
|
||||||
@@ -31,7 +31,7 @@ export async function createUsage(data: CreateUsageProps) {
|
|||||||
export async function concatUsage(data: ConcatUsageProps) {
|
export async function concatUsage(data: ConcatUsageProps) {
|
||||||
try {
|
try {
|
||||||
// In FastGPT server
|
// In FastGPT server
|
||||||
if (FastGPTProUrl) {
|
if (isFastGPTMainService) {
|
||||||
await POST('/support/wallet/usage/concatUsage', data);
|
await POST('/support/wallet/usage/concatUsage', data);
|
||||||
} else if (global.reduceAiPointsQueue) {
|
} else if (global.reduceAiPointsQueue) {
|
||||||
const {
|
const {
|
||||||
@@ -160,7 +160,7 @@ export const createTrainingUsage = async ({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
{ session }
|
{ session, ordered: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
return { billId: String(_id) };
|
return { billId: String(_id) };
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ const parsePowerPoint = async ({
|
|||||||
|
|
||||||
// Returning an array of all the xml contents read using fs.readFileSync
|
// Returning an array of all the xml contents read using fs.readFileSync
|
||||||
const xmlContentArray = await Promise.all(
|
const xmlContentArray = await Promise.all(
|
||||||
files.map((file) => {
|
files.map(async (file) => {
|
||||||
try {
|
try {
|
||||||
return fs.promises.readFile(`${decompressPath}/${file.path}`, encoding);
|
return await fs.promises.readFile(`${decompressPath}/${file.path}`, encoding);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return fs.promises.readFile(`${decompressPath}/${file.path}`, 'utf-8');
|
return await fs.promises.readFile(`${decompressPath}/${file.path}`, 'utf-8');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -100,6 +100,13 @@ const DateRangePicker = ({
|
|||||||
if (date?.to === undefined) {
|
if (date?.to === undefined) {
|
||||||
date.to = date.from;
|
date.to = date.from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (date?.from) {
|
||||||
|
date.from = new Date(date.from.setHours(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
if (date?.to) {
|
||||||
|
date.to = new Date(date.to.setHours(23, 59, 59, 999));
|
||||||
|
}
|
||||||
setRange(date);
|
setRange(date);
|
||||||
onChange?.(date);
|
onChange?.(date);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const iconPaths = {
|
|||||||
chatSend: () => import('./icons/chatSend.svg'),
|
chatSend: () => import('./icons/chatSend.svg'),
|
||||||
check: () => import('./icons/check.svg'),
|
check: () => import('./icons/check.svg'),
|
||||||
checkCircle: () => import('./icons/checkCircle.svg'),
|
checkCircle: () => import('./icons/checkCircle.svg'),
|
||||||
|
close: () => import('./icons/close.svg'),
|
||||||
closeSolid: () => import('./icons/closeSolid.svg'),
|
closeSolid: () => import('./icons/closeSolid.svg'),
|
||||||
code: () => import('./icons/code.svg'),
|
code: () => import('./icons/code.svg'),
|
||||||
collectionLight: () => import('./icons/collectionLight.svg'),
|
collectionLight: () => import('./icons/collectionLight.svg'),
|
||||||
@@ -32,8 +33,10 @@ export const iconPaths = {
|
|||||||
'common/customTitleLight': () => import('./icons/common/customTitleLight.svg'),
|
'common/customTitleLight': () => import('./icons/common/customTitleLight.svg'),
|
||||||
'common/data': () => import('./icons/common/data.svg'),
|
'common/data': () => import('./icons/common/data.svg'),
|
||||||
'common/dingtalkFill': () => import('./icons/common/dingtalkFill.svg'),
|
'common/dingtalkFill': () => import('./icons/common/dingtalkFill.svg'),
|
||||||
|
'common/disable': () => import('./icons/common/disable.svg'),
|
||||||
'common/downArrowFill': () => import('./icons/common/downArrowFill.svg'),
|
'common/downArrowFill': () => import('./icons/common/downArrowFill.svg'),
|
||||||
'common/editor/resizer': () => import('./icons/common/editor/resizer.svg'),
|
'common/editor/resizer': () => import('./icons/common/editor/resizer.svg'),
|
||||||
|
'common/enable': () => import('./icons/common/enable.svg'),
|
||||||
'common/errorFill': () => import('./icons/common/errorFill.svg'),
|
'common/errorFill': () => import('./icons/common/errorFill.svg'),
|
||||||
'common/file/move': () => import('./icons/common/file/move.svg'),
|
'common/file/move': () => import('./icons/common/file/move.svg'),
|
||||||
'common/folderFill': () => import('./icons/common/folderFill.svg'),
|
'common/folderFill': () => import('./icons/common/folderFill.svg'),
|
||||||
@@ -161,6 +164,7 @@ export const iconPaths = {
|
|||||||
'core/chat/chatLight': () => import('./icons/core/chat/chatLight.svg'),
|
'core/chat/chatLight': () => import('./icons/core/chat/chatLight.svg'),
|
||||||
'core/chat/chatModelTag': () => import('./icons/core/chat/chatModelTag.svg'),
|
'core/chat/chatModelTag': () => import('./icons/core/chat/chatModelTag.svg'),
|
||||||
'core/chat/chevronDown': () => import('./icons/core/chat/chevronDown.svg'),
|
'core/chat/chevronDown': () => import('./icons/core/chat/chevronDown.svg'),
|
||||||
|
'core/chat/chevronLeft': () => import('./icons/core/chat/chevronLeft.svg'),
|
||||||
'core/chat/chevronRight': () => import('./icons/core/chat/chevronRight.svg'),
|
'core/chat/chevronRight': () => import('./icons/core/chat/chevronRight.svg'),
|
||||||
'core/chat/chevronSelector': () => import('./icons/core/chat/chevronSelector.svg'),
|
'core/chat/chevronSelector': () => import('./icons/core/chat/chevronSelector.svg'),
|
||||||
'core/chat/chevronUp': () => import('./icons/core/chat/chevronUp.svg'),
|
'core/chat/chevronUp': () => import('./icons/core/chat/chevronUp.svg'),
|
||||||
@@ -329,6 +333,7 @@ export const iconPaths = {
|
|||||||
edit: () => import('./icons/edit.svg'),
|
edit: () => import('./icons/edit.svg'),
|
||||||
empty: () => import('./icons/empty.svg'),
|
empty: () => import('./icons/empty.svg'),
|
||||||
export: () => import('./icons/export.svg'),
|
export: () => import('./icons/export.svg'),
|
||||||
|
feedback: () => import('./icons/feedback.svg'),
|
||||||
'file/csv': () => import('./icons/file/csv.svg'),
|
'file/csv': () => import('./icons/file/csv.svg'),
|
||||||
'file/fill/csv': () => import('./icons/file/fill/csv.svg'),
|
'file/fill/csv': () => import('./icons/file/fill/csv.svg'),
|
||||||
'file/fill/doc': () => import('./icons/file/fill/doc.svg'),
|
'file/fill/doc': () => import('./icons/file/fill/doc.svg'),
|
||||||
|
|||||||
3
packages/web/components/common/Icon/icons/close.svg
Normal file
3
packages/web/components/common/Icon/icons/close.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.65613 2.84383C9.85139 3.0391 9.85139 3.35568 9.65613 3.55094L7.20708 5.99999L9.65617 8.44908C9.85143 8.64434 9.85143 8.96093 9.65617 9.15619C9.46091 9.35145 9.14433 9.35145 8.94906 9.15619L6.49997 6.7071L4.05088 9.15619C3.85562 9.35145 3.53904 9.35145 3.34377 9.15619C3.14851 8.96093 3.14851 8.64434 3.34377 8.44908L5.79286 5.99999L3.34382 3.55094C3.14855 3.35568 3.14855 3.0391 3.34382 2.84383C3.53908 2.64857 3.85566 2.64857 4.05092 2.84383L6.49997 5.29288L8.94902 2.84383C9.14428 2.64857 9.46087 2.64857 9.65613 2.84383Z" fill="#92A5C9"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 675 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1740494996853" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2899" width="64" height="64"><path d="M512 953.6a441.6 441.6 0 1 1 0-883.2 441.6 441.6 0 0 1 0 883.2z m0-64a377.6 377.6 0 1 0 0-755.2 377.6 377.6 0 0 0 0 755.2z" p-id="2900"></path><path d="M182.1696 227.4304l45.2608-45.2608 614.4 614.4-45.2608 45.2608z" p-id="2901"></path></svg>
|
||||||
|
After Width: | Height: | Size: 397 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1740495050372" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4745" width="64" height="64"><path d="M510.2 959.7c-246.9-1-447-202.6-446-449.5s202.6-447 449.5-446 447 202.6 446 449.5-202.6 447-449.5 446z m3.3-833.7c-212.8-0.8-386.7 171.7-387.5 384.5S297.7 897.2 510.5 898 897.2 726.3 898 513.5 726.3 126.8 513.5 126z" p-id="4746"></path><path d="M465.8 712.3L291.1 537.6l43.7-43.7 131 131 262-262 43.7 43.7z" p-id="4747"></path></svg>
|
||||||
|
After Width: | Height: | Size: 486 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4714 3.52864C10.7317 3.78899 10.7317 4.2111 10.4714 4.47145L6.94277 8.00004L10.4714 11.5286C10.7317 11.789 10.7317 12.2111 10.4714 12.4714C10.211 12.7318 9.7889 12.7318 9.52855 12.4714L5.52855 8.47144C5.26821 8.21109 5.26821 7.78898 5.52855 7.52864L9.52855 3.52864C9.7889 3.26829 10.211 3.26829 10.4714 3.52864Z" fill="#92A5C9"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 464 B |
29
packages/web/components/common/Icon/icons/feedback.svg
Normal file
29
packages/web/components/common/Icon/icons/feedback.svg
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<svg viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.49993 10.1261C8.49993 9.48174 9.02226 8.9594 9.6666 8.9594H14.3333C14.9776 8.9594 15.4999 9.48174 15.4999 10.1261C15.4999 10.7704 14.9776 11.2927 14.3333 11.2927H9.6666C9.02226 11.2927 8.49993 10.7704 8.49993 10.1261Z" fill="url(#paint0_linear_17422_2138)"/>
|
||||||
|
<path d="M8.49993 14.7927C8.49993 14.1484 9.02226 13.6261 9.6666 13.6261H18.4041C19.0485 13.6261 19.5708 14.1484 19.5708 14.7927C19.5708 15.4371 19.0485 15.9594 18.4041 15.9594H9.6666C9.02226 15.9594 8.49993 15.4371 8.49993 14.7927Z" fill="url(#paint1_linear_17422_2138)"/>
|
||||||
|
<path d="M17.6859 9.1641C17.3948 9.37757 17.2057 9.72212 17.2057 10.1108V11.2504C17.2057 11.2738 17.2247 11.2927 17.248 11.2927H18.3745C18.7286 11.2927 19.0462 11.1358 19.262 10.8878L25.8249 4.32494C26.2805 3.86933 26.2805 3.13063 25.8249 2.67502C25.3693 2.21941 24.6306 2.21941 24.175 2.67502L17.6859 9.1641Z" fill="url(#paint2_linear_17422_2138)"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5122 3.45903C17.5122 2.81469 16.9899 2.29236 16.3456 2.29236H7.34644C5.41344 2.29236 3.84644 3.85936 3.84644 5.79236V22.1257C3.84644 24.0587 5.41344 25.6257 7.34644 25.6257H21.6535C23.5865 25.6257 25.1535 24.0587 25.1535 22.1257V12.1788C25.1535 11.5344 24.6311 11.0121 23.9868 11.0121C23.3425 11.0121 22.8201 11.5345 22.8201 12.1788V22.1257C22.8201 22.77 22.2978 23.2924 21.6535 23.2924H7.34644C6.7021 23.2924 6.17977 22.77 6.17977 22.1257V5.79236C6.17977 5.14803 6.7021 4.62569 7.34644 4.62569H16.3456C16.9899 4.62569 17.5122 4.10336 17.5122 3.45903Z" fill="url(#paint3_linear_17422_2138)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#499DFF"/>
|
||||||
|
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||||
|
<stop offset="1" stop-color="#6E80FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#499DFF"/>
|
||||||
|
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||||
|
<stop offset="1" stop-color="#6E80FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint2_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#499DFF"/>
|
||||||
|
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||||
|
<stop offset="1" stop-color="#6E80FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint3_linear_17422_2138" x1="15.0065" y1="3.04993" x2="15.0065" y2="24.8681" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#499DFF"/>
|
||||||
|
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||||
|
<stop offset="1" stop-color="#6E80FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import type { IconProps } from '@chakra-ui/react';
|
import type { IconProps } from '@chakra-ui/react';
|
||||||
import { Box, Icon } from '@chakra-ui/react';
|
import { Box, Icon } from '@chakra-ui/react';
|
||||||
import { iconPaths } from './constants';
|
import { iconPaths } from './constants';
|
||||||
@@ -8,7 +8,7 @@ import { useRefresh } from '../../../hooks/useRefresh';
|
|||||||
const iconCache: Record<string, any> = {};
|
const iconCache: Record<string, any> = {};
|
||||||
|
|
||||||
const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType } & IconProps) => {
|
const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType } & IconProps) => {
|
||||||
const { refresh } = useRefresh();
|
const [, setUpdate] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (iconCache[name]) {
|
if (iconCache[name]) {
|
||||||
@@ -20,7 +20,7 @@ const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconNameType
|
|||||||
const component = { as: icon.default };
|
const component = { as: icon.default };
|
||||||
// Store in cache
|
// Store in cache
|
||||||
iconCache[name] = component;
|
iconCache[name] = component;
|
||||||
refresh();
|
setUpdate((prev) => prev + 1); // force update
|
||||||
})
|
})
|
||||||
.catch((error) => console.log(error));
|
.catch((error) => console.log(error));
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user