Compare commits
39 Commits
v4.8.17-al
...
v4.8.18-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47f7b1a7a3 | ||
|
|
fadb3e3ceb | ||
|
|
1c0b323b1b | ||
|
|
b26345dcaa | ||
|
|
cef8487ca1 | ||
|
|
ed619edd47 | ||
|
|
93cf5bb372 | ||
|
|
f556fbf0d5 | ||
|
|
07cc849877 | ||
|
|
2066094047 | ||
|
|
2bf1fce32a | ||
|
|
5465ca642f | ||
|
|
c0d0c629c5 | ||
|
|
20c6c202d2 | ||
|
|
c8f60b47d0 | ||
|
|
8b2be89350 | ||
|
|
a401d4d612 | ||
|
|
d24d29ac48 | ||
|
|
9abbc16036 | ||
|
|
28710ac05b | ||
|
|
3412d7009d | ||
|
|
c3480b0ffa | ||
|
|
f89212f35f | ||
|
|
03d1c6a651 | ||
|
|
5d1d4ff64f | ||
|
|
fd9600c6f8 | ||
|
|
efecfd44c3 | ||
|
|
1fc77a126a | ||
|
|
bb669ca3ff | ||
|
|
72ed72e595 | ||
|
|
e5735fddd1 | ||
|
|
fa92ef86b9 | ||
|
|
f39ea04178 | ||
|
|
bd4893ced9 | ||
|
|
b75e807f24 | ||
|
|
b2fdefdc0c | ||
|
|
896fec4b82 | ||
|
|
50bf7f9a3b | ||
|
|
da2831b948 |
4
.github/workflows/preview-fastgpt-image.yml
vendored
4
.github/workflows/preview-fastgpt-image.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
password: ${{ secrets.GH_PAT }}
|
password: ${{ secrets.GH_PAT }}
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
||||||
run: |
|
run: |
|
||||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:${{ github.event.pull_request.number }}" >> $GITHUB_ENV
|
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV
|
||||||
- name: Build image for PR
|
- name: Build image for PR
|
||||||
env:
|
env:
|
||||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
docker buildx build \
|
docker buildx build \
|
||||||
-f projects/app/Dockerfile \
|
-f projects/app/Dockerfile \
|
||||||
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
|
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
|
||||||
--label "org.opencontainers.image.description=fastgpt-pr imae" \
|
--label "org.opencontainers.image.description=fastgpt-pr image" \
|
||||||
--label "org.opencontainers.image.licenses=Apache" \
|
--label "org.opencontainers.image.licenses=Apache" \
|
||||||
--push \
|
--push \
|
||||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||||
|
|||||||
@@ -215,4 +215,4 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||||||
1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。
|
1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。
|
||||||
2. 未经商业授权,任何形式的商用服务均需保留相关版权信息。
|
2. 未经商业授权,任何形式的商用服务均需保留相关版权信息。
|
||||||
3. 完整请查看 [FastGPT Open Source License](./LICENSE)
|
3. 完整请查看 [FastGPT Open Source License](./LICENSE)
|
||||||
4. 联系方式:Dennis@sealos.io,[点击查看商业版定价策略](https://doc.tryfastgpt.ai/docs/commercial)
|
4. 联系方式:Dennis@sealos.io,[点击查看商业版定价策略](https://doc.tryfastgpt.ai/docs/shopping_cart/intro/)
|
||||||
|
|||||||
BIN
docSite/assets/imgs/dataset1.png
Normal file
BIN
docSite/assets/imgs/dataset1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
BIN
docSite/assets/imgs/dataset2.png
Normal file
BIN
docSite/assets/imgs/dataset2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
@@ -168,6 +168,7 @@ weight: 707
|
|||||||
"reRankModels": [],
|
"reRankModels": [],
|
||||||
"audioSpeechModels": [
|
"audioSpeechModels": [
|
||||||
{
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
"model": "tts-1",
|
"model": "tts-1",
|
||||||
"name": "OpenAI TTS1",
|
"name": "OpenAI TTS1",
|
||||||
"charsPointsPrice": 0,
|
"charsPointsPrice": 0,
|
||||||
@@ -182,6 +183,7 @@ weight: 707
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"whisperModel": {
|
"whisperModel": {
|
||||||
|
"provider": "OpenAI",
|
||||||
"model": "whisper-1",
|
"model": "whisper-1",
|
||||||
"name": "Whisper1",
|
"name": "Whisper1",
|
||||||
"charsPointsPrice": 0
|
"charsPointsPrice": 0
|
||||||
@@ -201,7 +203,9 @@ weight: 707
|
|||||||
- OpenAI
|
- OpenAI
|
||||||
- Claude
|
- Claude
|
||||||
- Gemini
|
- Gemini
|
||||||
|
- Meta
|
||||||
- MistralAI
|
- MistralAI
|
||||||
|
- AliCloud - 阿里云
|
||||||
- Qwen - 通义千问
|
- Qwen - 通义千问
|
||||||
- Doubao - 豆包
|
- Doubao - 豆包
|
||||||
- ChatGLM - 智谱
|
- ChatGLM - 智谱
|
||||||
@@ -213,7 +217,10 @@ weight: 707
|
|||||||
- Baichuan - 百川
|
- Baichuan - 百川
|
||||||
- Yi - 零一万物
|
- Yi - 零一万物
|
||||||
- Ernie - 文心一言
|
- Ernie - 文心一言
|
||||||
|
- StepFun - 阶跃星辰
|
||||||
- Ollama
|
- Ollama
|
||||||
|
- BAAI - 智源研究院
|
||||||
|
- FishAudio
|
||||||
- Other - 其他
|
- Other - 其他
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ images: []
|
|||||||
|
|
||||||
## 二、通用问题
|
## 二、通用问题
|
||||||
|
|
||||||
|
### 本地部署的限制
|
||||||
|
|
||||||
|
具体内容参考https://fael3z0zfze.feishu.cn/wiki/OFpAw8XzAi36Guk8dfucrCKUnjg。
|
||||||
|
|
||||||
### 能否纯本地运行
|
### 能否纯本地运行
|
||||||
|
|
||||||
可以。需要准备好向量模型和LLM模型。
|
可以。需要准备好向量模型和LLM模型。
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI
|
|||||||
|
|
||||||
## 加入社区
|
## 加入社区
|
||||||
|
|
||||||
遇到困难了吗?有任何问题吗? 加入微信群与开发者和用户保持沟通。
|
遇到困难了吗?有任何问题吗? 加入飞书群与开发者和用户保持沟通。
|
||||||
|
|
||||||
<img width="400px" src="https://oss.laf.run/otnvvf-imgs/fastgpt-feishu1.png" class="medium-zoom-image" />
|
<img width="400px" src="https://oss.laf.run/otnvvf-imgs/fastgpt-feishu1.png" class="medium-zoom-image" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: 'V4.8.17(进行中)'
|
title: 'V4.8.17(包含升级脚本)'
|
||||||
description: 'FastGPT V4.8.17 更新说明'
|
description: 'FastGPT V4.8.17 更新说明'
|
||||||
icon: 'upgrade'
|
icon: 'upgrade'
|
||||||
draft: false
|
draft: false
|
||||||
@@ -7,7 +7,16 @@ toc: true
|
|||||||
weight: 807
|
weight: 807
|
||||||
---
|
---
|
||||||
|
|
||||||
## 运行升级脚本
|
## 更新指南
|
||||||
|
|
||||||
|
### 1. 更新镜像:
|
||||||
|
|
||||||
|
- 更新 fastgpt 镜像 tag: v4.8.17-fix-title
|
||||||
|
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.17
|
||||||
|
- Sandbox 镜像无需更新
|
||||||
|
|
||||||
|
|
||||||
|
### 2. 运行升级脚本
|
||||||
|
|
||||||
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
||||||
|
|
||||||
@@ -19,6 +28,11 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4817' \
|
|||||||
|
|
||||||
会将用户绑定的 OpenAI 账号移动到团队中。
|
会将用户绑定的 OpenAI 账号移动到团队中。
|
||||||
|
|
||||||
|
|
||||||
|
## 调整 completions 接口返回值
|
||||||
|
|
||||||
|
/api/v1/chat/completions 接口返回值调整,对话节点、工具节点等使用到模型的节点,将不再返回 `tokens` 字段,改为返回 `inputTokens` 和 `outputTokens` 字段,分别表示输入和输出的 Token 数量。
|
||||||
|
|
||||||
## 完整更新内容
|
## 完整更新内容
|
||||||
|
|
||||||
1. 新增 - 简易模式工具调用支持数组类型插件。
|
1. 新增 - 简易模式工具调用支持数组类型插件。
|
||||||
@@ -27,10 +41,12 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4817' \
|
|||||||
4. 新增 - 商业版支持后台配置模板市场。
|
4. 新增 - 商业版支持后台配置模板市场。
|
||||||
5. 新增 - 商业版支持后台配置自定义工作流变量,用于与业务系统鉴权打通。
|
5. 新增 - 商业版支持后台配置自定义工作流变量,用于与业务系统鉴权打通。
|
||||||
6. 新增 - 搜索测试接口支持问题优化。
|
6. 新增 - 搜索测试接口支持问题优化。
|
||||||
7. 优化 - Markdown 大小测试,超出 20 万字符不使用 Markdown 组件,避免崩溃。
|
7. 新增 - 工作流中 Input Token 和 Output Token 分开记录展示。并修复部分请求未记录输出 Token 计费问题。
|
||||||
8. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制。
|
8. 优化 - Markdown 大小测试,超出 20 万字符不使用 Markdown 组件,避免崩溃。
|
||||||
9. 优化 - 可用模型展示
|
9. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制。
|
||||||
10. 优化 - Mongo 查询语句,增加 virtual 字段。
|
10. 优化 - 可用模型展示UI。
|
||||||
11. 修复 - 文件返回接口缺少 Content-Length 头,导致通过非同源文件上传时,阿里 vision 模型无法识别图片。
|
11. 优化 - Mongo 查询语句,增加 virtual 字段。
|
||||||
12. 修复 - 去除判断器两端字符串隐藏换行符,避免判断器失效。
|
12. 修复 - 文件返回接口缺少 Content-Length 头,导致通过非同源文件上传时,阿里 vision 模型无法识别图片。
|
||||||
13. 修复 - 变量更新节点,手动输入更新内容时候,非字符串类型数据类型无法自动转化。
|
13. 修复 - 去除判断器两端字符串隐藏换行符,避免判断器失效。
|
||||||
|
14. 修复 - 变量更新节点,手动输入更新内容时候,非字符串类型数据类型无法自动转化。
|
||||||
|
15. 修复 - 豆包模型无法工具调用。
|
||||||
|
|||||||
37
docSite/content/zh-cn/docs/development/upgrading/4818.md
Normal file
37
docSite/content/zh-cn/docs/development/upgrading/4818.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: 'V4.8.18(进行中)'
|
||||||
|
description: 'FastGPT V4.8.18 更新说明'
|
||||||
|
icon: 'upgrade'
|
||||||
|
draft: false
|
||||||
|
toc: true
|
||||||
|
weight: 806
|
||||||
|
---
|
||||||
|
|
||||||
|
## 更新指南
|
||||||
|
|
||||||
|
### 2. 运行升级脚本
|
||||||
|
|
||||||
|
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location --request POST 'https://{{host}}/api/admin/initv4818' \
|
||||||
|
--header 'rootkey: {{rootkey}}' \
|
||||||
|
--header 'Content-Type: application/json'
|
||||||
|
```
|
||||||
|
|
||||||
|
会迁移全文检索表,时间较长,迁移期间全文检索会失效,日志中会打印已经迁移的数据长度。
|
||||||
|
|
||||||
|
|
||||||
|
## 完整更新内容
|
||||||
|
|
||||||
|
1. 新增 - 支持部门架构权限模式。
|
||||||
|
2. 新增 - 支持配置自定跨域安全策略,默认全开。
|
||||||
|
3. 优化 - 分享链接随机生成用户头像。
|
||||||
|
4. 优化 - 图片上传安全校验。并增加头像图片唯一存储,确保不会累计存储。
|
||||||
|
5. 优化 - Mongo 全文索引表分离。
|
||||||
|
6. 优化 - 知识库检索查询语句合并,同时减少查库数量。
|
||||||
|
7. 优化 - 文件编码检测,减少 CSV 文件乱码概率。
|
||||||
|
8. 优化 - 异步读取文件内容,减少进程阻塞。
|
||||||
|
9. 优化 - 文件阅读,HTML 直接下载,不允许在线阅读。
|
||||||
|
10. 修复 - HTML 文件上传,base64 图片无法自动转图片链接。
|
||||||
|
11. 修复 - 插件计费错误。
|
||||||
@@ -13,4 +13,12 @@ weight: 908
|
|||||||
|
|
||||||
但是,当连续问题之间的关联性较小,模型判断的准确度可能会受到限制。在这种情况下,我们可以引入全局变量的概念来记录分类结果。在后续的问题分类阶段,首先检查全局变量是否存有分类结果。如果有,那么直接沿用该结果;若没有,则让模型自行判断。
|
但是,当连续问题之间的关联性较小,模型判断的准确度可能会受到限制。在这种情况下,我们可以引入全局变量的概念来记录分类结果。在后续的问题分类阶段,首先检查全局变量是否存有分类结果。如果有,那么直接沿用该结果;若没有,则让模型自行判断。
|
||||||
|
|
||||||
建议:构建批量运行脚本进行测试,评估问题分类的准确性。
|
建议:构建批量运行脚本进行测试,评估问题分类的准确性。
|
||||||
|
|
||||||
|
## 系统编排配置中的定时执行,如果用户打开分享的连接,停留在那个页面,定时执行触发问题
|
||||||
|
|
||||||
|
发布后,后台生效。
|
||||||
|
|
||||||
|
## AI对话回答要求中的Markdown语法取消
|
||||||
|
|
||||||
|
在针对知识库的回答要求里有, 要给它配置提示词,不然他就是默认的,默认的里面就有该语法。
|
||||||
@@ -14,4 +14,51 @@ weight: 910
|
|||||||
## 知识库配置里的文件处理模型是什么?与索引模型有什么区别?
|
## 知识库配置里的文件处理模型是什么?与索引模型有什么区别?
|
||||||
|
|
||||||
* **文件处理模型**:用于数据处理的【增强处理】和【问答拆分】。在【增强处理】中,生成相关问题和摘要,在【问答拆分】中执行问答对生成。
|
* **文件处理模型**:用于数据处理的【增强处理】和【问答拆分】。在【增强处理】中,生成相关问题和摘要,在【问答拆分】中执行问答对生成。
|
||||||
* **索引模型**:用于向量化,即通过对文本数据进行处理和组织,构建出一个能够快速查询的数据结构。
|
* **索引模型**:用于向量化,即通过对文本数据进行处理和组织,构建出一个能够快速查询的数据结构。
|
||||||
|
|
||||||
|
## 基于知识库的查询,但是问题相关的答案过多。ai回答到一半就不继续回答。
|
||||||
|
|
||||||
|
FastGPT回复长度计算公式:
|
||||||
|
|
||||||
|
最大回复=min(配置的最大回复(内置的限制),最大上下文(输入和输出的总和)-历史记录)
|
||||||
|
|
||||||
|
18K模型->输入与输出的和
|
||||||
|
|
||||||
|
输出增多->输入减小
|
||||||
|
|
||||||
|
所以可以:
|
||||||
|
|
||||||
|
1. 检查配置的最大回复(回复上限)
|
||||||
|
2. 减小输入来增大输出,即减小历史记录,在工作流其实也就是“聊天记录”
|
||||||
|
|
||||||
|
配置的最大回复:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. 私有化部署的时候,后台配模型参数,可以在配置最大上文时候,预留一些空间,比如 128000 的模型,可以只配置 120000, 剩余的空间后续会被安排给输出
|
||||||
|
|
||||||
|
|
||||||
|
## 受到模型上下文的限制,有时候达不到聊天记录的轮次,连续对话字数过多就会报上下文不够的错误。
|
||||||
|
|
||||||
|
FastGPT回复长度计算公式:
|
||||||
|
|
||||||
|
最大回复=min(配置的最大回复(内置的限制),最大上下文(输入和输出的总和)-历史记录)
|
||||||
|
|
||||||
|
18K模型->输入与输出的和
|
||||||
|
|
||||||
|
输出增多->输入减小
|
||||||
|
|
||||||
|
所以可以:
|
||||||
|
|
||||||
|
1. 检查配置的最大回复(回复上限)
|
||||||
|
2. 减小输入来增大输出,即减小历史记录,在工作流其实也就是“聊天记录”
|
||||||
|
|
||||||
|
配置的最大回复:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. 私有化部署的时候,后台配模型参数,可以在配置最大上文时候,预留一些空间,比如 128000 的模型,可以只配置 120000, 剩余的空间后续会被安排给输出
|
||||||
@@ -160,6 +160,18 @@ default_doi_resolver: 'oadoi.org'
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* 搜索结果为空时会返回友好提示:
|
||||||
|
|
||||||
|
```Bash
|
||||||
|
{
|
||||||
|
"result": "[]",
|
||||||
|
"error": {
|
||||||
|
"message": "No search results",
|
||||||
|
"code": 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
* 失败时通过 Promise.reject 可能返回错误信息:
|
* 失败时通过 Promise.reject 可能返回错误信息:
|
||||||
|
|
||||||
```Bash
|
```Bash
|
||||||
|
|||||||
@@ -114,15 +114,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.16 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.16 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.16 # git
|
image: ghcr.io/labring/fastgpt:v4.8.17 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.16 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -72,15 +72,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.16 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.16 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.16 # git
|
image: ghcr.io/labring/fastgpt:v4.8.17 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.16 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -53,15 +53,15 @@ services:
|
|||||||
wait $$!
|
wait $$!
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.16 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.16 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.16 # git
|
image: ghcr.io/labring/fastgpt:v4.8.17 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.16 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
import { ErrType } from '../errorCode';
|
import { ErrType } from '../errorCode';
|
||||||
|
|
||||||
/* dataset: 507000 */
|
/* dataset: 507000 */
|
||||||
const startCode = 507000;
|
const startCode = 507000;
|
||||||
export enum CommonErrEnum {
|
export enum CommonErrEnum {
|
||||||
|
invalidParams = 'invalidParams',
|
||||||
fileNotFound = 'fileNotFound',
|
fileNotFound = 'fileNotFound',
|
||||||
unAuthFile = 'unAuthFile',
|
unAuthFile = 'unAuthFile',
|
||||||
missingParams = 'missingParams',
|
missingParams = 'missingParams',
|
||||||
inheritPermissionError = 'inheritPermissionError'
|
inheritPermissionError = 'inheritPermissionError'
|
||||||
}
|
}
|
||||||
const datasetErr = [
|
const datasetErr = [
|
||||||
|
{
|
||||||
|
statusText: CommonErrEnum.fileNotFound,
|
||||||
|
message: i18nT('common:error.invalid_params')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
statusText: CommonErrEnum.fileNotFound,
|
statusText: CommonErrEnum.fileNotFound,
|
||||||
message: 'error.fileNotFound'
|
message: 'error.fileNotFound'
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { ErrType } from '../errorCode';
|
|
||||||
import { i18nT } from '../../../../web/i18n/utils';
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
|
import type { ErrType } from '../errorCode';
|
||||||
/* team: 500000 */
|
/* team: 500000 */
|
||||||
export enum TeamErrEnum {
|
export enum TeamErrEnum {
|
||||||
|
notUser = 'notUser',
|
||||||
teamOverSize = 'teamOverSize',
|
teamOverSize = 'teamOverSize',
|
||||||
unAuthTeam = 'unAuthTeam',
|
unAuthTeam = 'unAuthTeam',
|
||||||
|
teamMemberOverSize = 'teamMemberOverSize',
|
||||||
aiPointsNotEnough = 'aiPointsNotEnough',
|
aiPointsNotEnough = 'aiPointsNotEnough',
|
||||||
datasetSizeNotEnough = 'datasetSizeNotEnough',
|
datasetSizeNotEnough = 'datasetSizeNotEnough',
|
||||||
datasetAmountNotEnough = 'datasetAmountNotEnough',
|
datasetAmountNotEnough = 'datasetAmountNotEnough',
|
||||||
@@ -14,11 +16,22 @@ export enum TeamErrEnum {
|
|||||||
groupNameEmpty = 'groupNameEmpty',
|
groupNameEmpty = 'groupNameEmpty',
|
||||||
groupNameDuplicate = 'groupNameDuplicate',
|
groupNameDuplicate = 'groupNameDuplicate',
|
||||||
groupNotExist = 'groupNotExist',
|
groupNotExist = 'groupNotExist',
|
||||||
|
orgMemberNotExist = 'orgMemberNotExist',
|
||||||
|
orgMemberDuplicated = 'orgMemberDuplicated',
|
||||||
|
orgNotExist = 'orgNotExist',
|
||||||
|
orgParentNotExist = 'orgParentNotExist',
|
||||||
|
cannotMoveToSubPath = 'cannotMoveToSubPath',
|
||||||
|
cannotModifyRootOrg = 'cannotModifyRootOrg',
|
||||||
|
cannotDeleteNonEmptyOrg = 'cannotDeleteNonEmptyOrg',
|
||||||
cannotDeleteDefaultGroup = 'cannotDeleteDefaultGroup',
|
cannotDeleteDefaultGroup = 'cannotDeleteDefaultGroup',
|
||||||
userNotActive = 'userNotActive'
|
userNotActive = 'userNotActive'
|
||||||
}
|
}
|
||||||
|
|
||||||
const teamErr = [
|
const teamErr = [
|
||||||
|
{
|
||||||
|
statusText: TeamErrEnum.notUser,
|
||||||
|
message: i18nT('common:code_error.team_error.not_user')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
statusText: TeamErrEnum.teamOverSize,
|
statusText: TeamErrEnum.teamOverSize,
|
||||||
message: i18nT('common:code_error.team_error.over_size')
|
message: i18nT('common:code_error.team_error.over_size')
|
||||||
@@ -71,6 +84,34 @@ const teamErr = [
|
|||||||
{
|
{
|
||||||
statusText: TeamErrEnum.userNotActive,
|
statusText: TeamErrEnum.userNotActive,
|
||||||
message: i18nT('common:code_error.team_error.user_not_active')
|
message: i18nT('common:code_error.team_error.user_not_active')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: TeamErrEnum.orgMemberNotExist,
|
||||||
|
message: i18nT('common:code_error.team_error.org_member_not_exist')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: TeamErrEnum.orgMemberDuplicated,
|
||||||
|
message: i18nT('common:code_error.team_error.org_member_duplicated')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: TeamErrEnum.orgNotExist,
|
||||||
|
message: i18nT('common:code_error.team_error.org_not_exist')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: TeamErrEnum.orgParentNotExist,
|
||||||
|
message: i18nT('common:code_error.team_error.org_parent_not_exist')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: TeamErrEnum.cannotMoveToSubPath,
|
||||||
|
message: i18nT('common:code_error.team_error.cannot_move_to_sub_path')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: TeamErrEnum.cannotModifyRootOrg,
|
||||||
|
message: i18nT('common:code_error.team_error.cannot_modify_root_org')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: TeamErrEnum.cannotDeleteNonEmptyOrg,
|
||||||
|
message: i18nT('common:code_error.team_error.cannot_delete_non_empty_org')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -2,25 +2,16 @@ import { ErrType } from '../errorCode';
|
|||||||
import { i18nT } from '../../../../web/i18n/utils';
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
/* team: 503000 */
|
/* team: 503000 */
|
||||||
export enum UserErrEnum {
|
export enum UserErrEnum {
|
||||||
unAuthUser = 'unAuthUser',
|
|
||||||
unAuthRole = 'unAuthRole',
|
unAuthRole = 'unAuthRole',
|
||||||
binVisitor = 'binVisitor',
|
binVisitor = 'binVisitor',
|
||||||
balanceNotEnough = 'balanceNotEnough',
|
balanceNotEnough = 'balanceNotEnough',
|
||||||
unAuthSso = 'unAuthSso'
|
unAuthSso = 'unAuthSso'
|
||||||
}
|
}
|
||||||
const errList = [
|
const errList = [
|
||||||
{
|
|
||||||
statusText: UserErrEnum.unAuthUser,
|
|
||||||
message: i18nT('common:code_error.user_error.un_auth_user')
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
statusText: UserErrEnum.binVisitor,
|
statusText: UserErrEnum.binVisitor,
|
||||||
message: i18nT('common:code_error.user_error.bin_visitor')
|
message: i18nT('common:code_error.user_error.bin_visitor')
|
||||||
}, // 身份校验未通过
|
},
|
||||||
{
|
|
||||||
statusText: UserErrEnum.binVisitor,
|
|
||||||
message: i18nT('common:code_error.user_error.bin_visitor_guest')
|
|
||||||
}, // 游客身份
|
|
||||||
{
|
{
|
||||||
statusText: UserErrEnum.balanceNotEnough,
|
statusText: UserErrEnum.balanceNotEnough,
|
||||||
message: i18nT('common:code_error.user_error.balance_not_enough')
|
message: i18nT('common:code_error.user_error.balance_not_enough')
|
||||||
|
|||||||
5
packages/global/common/file/api.d.ts
vendored
5
packages/global/common/file/api.d.ts
vendored
@@ -1,10 +1,7 @@
|
|||||||
import { MongoImageTypeEnum } from './image/constants';
|
|
||||||
import { OutLinkChatAuthProps } from '../../support/permission/chat.d';
|
import { OutLinkChatAuthProps } from '../../support/permission/chat.d';
|
||||||
|
|
||||||
export type preUploadImgProps = OutLinkChatAuthProps & {
|
export type preUploadImgProps = OutLinkChatAuthProps & {
|
||||||
type: `${MongoImageTypeEnum}`;
|
// expiredTime?: Date;
|
||||||
|
|
||||||
expiredTime?: Date;
|
|
||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
};
|
};
|
||||||
export type UploadImgProps = preUploadImgProps & {
|
export type UploadImgProps = preUploadImgProps & {
|
||||||
|
|||||||
@@ -1,61 +1,5 @@
|
|||||||
export const imageBaseUrl = '/api/system/img/';
|
export const imageBaseUrl = '/api/system/img/';
|
||||||
|
|
||||||
export enum MongoImageTypeEnum {
|
|
||||||
systemAvatar = 'systemAvatar',
|
|
||||||
appAvatar = 'appAvatar',
|
|
||||||
pluginAvatar = 'pluginAvatar',
|
|
||||||
datasetAvatar = 'datasetAvatar',
|
|
||||||
userAvatar = 'userAvatar',
|
|
||||||
teamAvatar = 'teamAvatar',
|
|
||||||
groupAvatar = 'groupAvatar',
|
|
||||||
|
|
||||||
chatImage = 'chatImage',
|
|
||||||
collectionImage = 'collectionImage'
|
|
||||||
}
|
|
||||||
export const mongoImageTypeMap = {
|
|
||||||
[MongoImageTypeEnum.systemAvatar]: {
|
|
||||||
label: 'appAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.appAvatar]: {
|
|
||||||
label: 'appAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.pluginAvatar]: {
|
|
||||||
label: 'pluginAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.datasetAvatar]: {
|
|
||||||
label: 'datasetAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.userAvatar]: {
|
|
||||||
label: 'userAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.teamAvatar]: {
|
|
||||||
label: 'teamAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.groupAvatar]: {
|
|
||||||
label: 'groupAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
|
|
||||||
[MongoImageTypeEnum.chatImage]: {
|
|
||||||
label: 'chatImage',
|
|
||||||
unique: false
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.collectionImage]: {
|
|
||||||
label: 'collectionImage',
|
|
||||||
unique: false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const uniqueImageTypeList = Object.entries(mongoImageTypeMap)
|
|
||||||
.filter(([key, value]) => value.unique)
|
|
||||||
.map(([key]) => key as `${MongoImageTypeEnum}`);
|
|
||||||
|
|
||||||
export const FolderIcon = 'file/fill/folder';
|
export const FolderIcon = 'file/fill/folder';
|
||||||
export const FolderImgUrl = '/imgs/files/folder.svg';
|
export const FolderImgUrl = '/imgs/files/folder.svg';
|
||||||
export const HttpPluginImgUrl = '/imgs/app/httpPluginFill.svg';
|
export const HttpPluginImgUrl = '/imgs/app/httpPluginFill.svg';
|
||||||
|
|||||||
4
packages/global/common/file/image/type.d.ts
vendored
4
packages/global/common/file/image/type.d.ts
vendored
@@ -1,12 +1,8 @@
|
|||||||
import { MongoImageTypeEnum } from './constants';
|
|
||||||
|
|
||||||
export type MongoImageSchemaType = {
|
export type MongoImageSchemaType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
binary: Buffer;
|
binary: Buffer;
|
||||||
createTime: Date;
|
|
||||||
expiredTime?: Date;
|
expiredTime?: Date;
|
||||||
type: `${MongoImageTypeEnum}`;
|
|
||||||
|
|
||||||
metadata?: {
|
metadata?: {
|
||||||
mime?: string; // image mime type.
|
mime?: string; // image mime type.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { detect } from 'jschardet';
|
|||||||
import { documentFileType, imageFileType } from './constants';
|
import { documentFileType, imageFileType } from './constants';
|
||||||
import { ChatFileTypeEnum } from '../../core/chat/constants';
|
import { ChatFileTypeEnum } from '../../core/chat/constants';
|
||||||
import { UserChatItemValueItemType } from '../../core/chat/type';
|
import { UserChatItemValueItemType } from '../../core/chat/type';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
export const formatFileSize = (bytes: number): string => {
|
export const formatFileSize = (bytes: number): string => {
|
||||||
if (bytes === 0) return '0 B';
|
if (bytes === 0) return '0 B';
|
||||||
@@ -16,6 +17,22 @@ export const formatFileSize = (bytes: number): string => {
|
|||||||
export const detectFileEncoding = (buffer: Buffer) => {
|
export const detectFileEncoding = (buffer: Buffer) => {
|
||||||
return detect(buffer.slice(0, 200))?.encoding?.toLocaleLowerCase();
|
return detect(buffer.slice(0, 200))?.encoding?.toLocaleLowerCase();
|
||||||
};
|
};
|
||||||
|
export const detectFileEncodingByPath = async (path: string) => {
|
||||||
|
// Get 64KB file head
|
||||||
|
const MAX_BYTES = 64 * 1024;
|
||||||
|
const buffer = Buffer.alloc(MAX_BYTES);
|
||||||
|
|
||||||
|
const fd = await fs.promises.open(path, 'r');
|
||||||
|
try {
|
||||||
|
// Read file head
|
||||||
|
const { bytesRead } = await fd.read(buffer, 0, MAX_BYTES, 0);
|
||||||
|
const actualBuffer = buffer.slice(0, bytesRead);
|
||||||
|
|
||||||
|
return detect(actualBuffer)?.encoding?.toLocaleLowerCase();
|
||||||
|
} finally {
|
||||||
|
await fd.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Url => user upload file type
|
// Url => user upload file type
|
||||||
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {
|
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {
|
||||||
|
|||||||
@@ -25,17 +25,22 @@ export const simpleText = (text = '') => {
|
|||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
export const valToStr = (val: any) => {
|
||||||
replace {{variable}} to value
|
if (val === undefined) return 'undefined';
|
||||||
*/
|
if (val === null) return 'null';
|
||||||
|
|
||||||
|
if (typeof val === 'object') return JSON.stringify(val);
|
||||||
|
return String(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
// replace {{variable}} to value
|
||||||
export function replaceVariable(text: any, obj: Record<string, string | number>) {
|
export function replaceVariable(text: any, obj: Record<string, string | number>) {
|
||||||
if (typeof text !== 'string') return text;
|
if (typeof text !== 'string') return text;
|
||||||
|
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
const val = obj[key];
|
const val = obj[key];
|
||||||
const formatVal = typeof val === 'object' ? JSON.stringify(val) : String(val);
|
const formatVal = valToStr(val);
|
||||||
|
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), () => formatVal);
|
||||||
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), formatVal);
|
|
||||||
}
|
}
|
||||||
return text || '';
|
return text || '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
export const HUMAN_ICON = `/icon/human.svg`;
|
export const HUMAN_ICON = `/icon/human.svg`;
|
||||||
export const LOGO_ICON = `/icon/logo.svg`;
|
export const LOGO_ICON = `/icon/logo.svg`;
|
||||||
export const HUGGING_FACE_ICON = `/imgs/model/huggingface.svg`;
|
export const HUGGING_FACE_ICON = `/imgs/model/huggingface.svg`;
|
||||||
|
|
||||||
export const DEFAULT_TEAM_AVATAR = `/imgs/avatar/defaultTeamAvatar.svg`;
|
export const DEFAULT_TEAM_AVATAR = `/imgs/avatar/defaultTeamAvatar.svg`;
|
||||||
|
export const DEFAULT_ORG_AVATAR = '/imgs/avatar/defaultOrgAvatar.svg';
|
||||||
|
export const DEFAULT_USER_AVATAR = '/imgs/avatar/BlueAvatar.svg';
|
||||||
|
|
||||||
export const isProduction = process.env.NODE_ENV === 'production';
|
export const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ export type FastGPTFeConfigsType = {
|
|||||||
google?: string;
|
google?: string;
|
||||||
wechat?: string;
|
wechat?: string;
|
||||||
dingtalk?: string;
|
dingtalk?: string;
|
||||||
|
wecom?: {
|
||||||
|
corpid?: string;
|
||||||
|
agentid?: string;
|
||||||
|
secret?: string;
|
||||||
|
};
|
||||||
microsoft?: {
|
microsoft?: {
|
||||||
clientId?: string;
|
clientId?: string;
|
||||||
tenantId?: string;
|
tenantId?: string;
|
||||||
|
|||||||
24
packages/global/core/ai/model.d.ts
vendored
24
packages/global/core/ai/model.d.ts
vendored
@@ -1,6 +1,13 @@
|
|||||||
import type { ModelProviderIdType } from './provider';
|
import type { ModelProviderIdType } from './provider';
|
||||||
|
|
||||||
export type LLMModelItemType = {
|
type PriceType = {
|
||||||
|
charsPointsPrice?: number; // 1k chars=n points; 60s=n points;
|
||||||
|
|
||||||
|
// If inputPrice is set, the input-output charging scheme is adopted
|
||||||
|
inputPrice?: number; // 1k tokens=n points
|
||||||
|
outputPrice?: number; // 1k tokens=n points
|
||||||
|
};
|
||||||
|
export type LLMModelItemType = PriceType & {
|
||||||
provider: ModelProviderIdType;
|
provider: ModelProviderIdType;
|
||||||
model: string;
|
model: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -10,8 +17,6 @@ export type LLMModelItemType = {
|
|||||||
quoteMaxToken: number;
|
quoteMaxToken: number;
|
||||||
maxTemperature: number;
|
maxTemperature: number;
|
||||||
|
|
||||||
charsPointsPrice: number; // 1k chars=n points
|
|
||||||
|
|
||||||
censor?: boolean;
|
censor?: boolean;
|
||||||
vision?: boolean;
|
vision?: boolean;
|
||||||
|
|
||||||
@@ -33,13 +38,12 @@ export type LLMModelItemType = {
|
|||||||
fieldMap?: Record<string, string>;
|
fieldMap?: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VectorModelItemType = {
|
export type VectorModelItemType = PriceType & {
|
||||||
provider: ModelProviderIdType;
|
provider: ModelProviderIdType;
|
||||||
model: string; // model name
|
model: string; // model name
|
||||||
name: string; // show name
|
name: string; // show name
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
defaultToken: number; // split text default token
|
defaultToken: number; // split text default token
|
||||||
charsPointsPrice: number; // 1k tokens=n points
|
|
||||||
maxToken: number; // model max token
|
maxToken: number; // model max token
|
||||||
weight: number; // training weight
|
weight: number; // training weight
|
||||||
hidden?: boolean; // Disallow creation
|
hidden?: boolean; // Disallow creation
|
||||||
@@ -48,25 +52,23 @@ export type VectorModelItemType = {
|
|||||||
queryConfig?: Record<string, any>; // Custom parameters for query
|
queryConfig?: Record<string, any>; // Custom parameters for query
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReRankModelItemType = {
|
export type ReRankModelItemType = PriceType & {
|
||||||
|
provider: ModelProviderIdType;
|
||||||
model: string;
|
model: string;
|
||||||
name: string;
|
name: string;
|
||||||
charsPointsPrice: number;
|
|
||||||
requestUrl: string;
|
requestUrl: string;
|
||||||
requestAuth: string;
|
requestAuth: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AudioSpeechModelType = {
|
export type AudioSpeechModelType = PriceType & {
|
||||||
provider: ModelProviderIdType;
|
provider: ModelProviderIdType;
|
||||||
model: string;
|
model: string;
|
||||||
name: string;
|
name: string;
|
||||||
charsPointsPrice: number;
|
|
||||||
voices: { label: string; value: string; bufferId: string }[];
|
voices: { label: string; value: string; bufferId: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type STTModelType = {
|
export type STTModelType = PriceType & {
|
||||||
provider: ModelProviderIdType;
|
provider: ModelProviderIdType;
|
||||||
model: string;
|
model: string;
|
||||||
name: string;
|
name: string;
|
||||||
charsPointsPrice: number; // 60s = n points
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,20 +4,25 @@ export type ModelProviderIdType =
|
|||||||
| 'OpenAI'
|
| 'OpenAI'
|
||||||
| 'Claude'
|
| 'Claude'
|
||||||
| 'Gemini'
|
| 'Gemini'
|
||||||
|
| 'Meta'
|
||||||
| 'MistralAI'
|
| 'MistralAI'
|
||||||
| 'Groq'
|
| 'Groq'
|
||||||
|
| 'AliCloud'
|
||||||
| 'Qwen'
|
| 'Qwen'
|
||||||
| 'Doubao'
|
| 'Doubao'
|
||||||
| 'ChatGLM'
|
| 'ChatGLM'
|
||||||
| 'DeepSeek'
|
| 'DeepSeek'
|
||||||
|
| 'Ernie'
|
||||||
| 'Moonshot'
|
| 'Moonshot'
|
||||||
| 'MiniMax'
|
| 'MiniMax'
|
||||||
| 'SparkDesk'
|
| 'SparkDesk'
|
||||||
| 'Hunyuan'
|
| 'Hunyuan'
|
||||||
| 'Baichuan'
|
| 'Baichuan'
|
||||||
|
| 'StepFun'
|
||||||
| 'Yi'
|
| 'Yi'
|
||||||
| 'Ernie'
|
|
||||||
| 'Ollama'
|
| 'Ollama'
|
||||||
|
| 'BAAI'
|
||||||
|
| 'FishAudio'
|
||||||
| 'Other';
|
| 'Other';
|
||||||
|
|
||||||
export type ModelProviderType = {
|
export type ModelProviderType = {
|
||||||
@@ -42,6 +47,11 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: 'Gemini',
|
name: 'Gemini',
|
||||||
avatar: 'model/gemini'
|
avatar: 'model/gemini'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'Meta',
|
||||||
|
name: 'Meta',
|
||||||
|
avatar: 'model/meta'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'MistralAI',
|
id: 'MistralAI',
|
||||||
name: 'MistralAI',
|
name: 'MistralAI',
|
||||||
@@ -52,6 +62,11 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: 'Groq',
|
name: 'Groq',
|
||||||
avatar: 'model/groq'
|
avatar: 'model/groq'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'AliCloud',
|
||||||
|
name: i18nT('common:model_alicloud'),
|
||||||
|
avatar: 'model/alicloud'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Qwen',
|
id: 'Qwen',
|
||||||
name: i18nT('common:model_qwen'),
|
name: i18nT('common:model_qwen'),
|
||||||
@@ -67,6 +82,11 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: i18nT('common:model_chatglm'),
|
name: i18nT('common:model_chatglm'),
|
||||||
avatar: 'model/chatglm'
|
avatar: 'model/chatglm'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'Ernie',
|
||||||
|
name: i18nT('common:model_ernie'),
|
||||||
|
avatar: 'model/ernie'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'DeepSeek',
|
id: 'DeepSeek',
|
||||||
name: 'DeepSeek',
|
name: 'DeepSeek',
|
||||||
@@ -97,21 +117,32 @@ export const ModelProviderList: ModelProviderType[] = [
|
|||||||
name: i18nT('common:model_baichuan'),
|
name: i18nT('common:model_baichuan'),
|
||||||
avatar: 'model/baichuan'
|
avatar: 'model/baichuan'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'StepFun',
|
||||||
|
name: i18nT('common:model_stepfun'),
|
||||||
|
avatar: 'model/stepfun'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Yi',
|
id: 'Yi',
|
||||||
name: i18nT('common:model_yi'),
|
name: i18nT('common:model_yi'),
|
||||||
avatar: 'model/yi'
|
avatar: 'model/yi'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'Ernie',
|
|
||||||
name: i18nT('common:model_ernie'),
|
|
||||||
avatar: 'model/ernie'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'Ollama',
|
id: 'Ollama',
|
||||||
name: 'Ollama',
|
name: 'Ollama',
|
||||||
avatar: 'model/ollama'
|
avatar: 'model/ollama'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'BAAI',
|
||||||
|
name: i18nT('common:model_baai'),
|
||||||
|
avatar: 'model/BAAI'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'FishAudio',
|
||||||
|
name: 'FishAudio',
|
||||||
|
avatar: 'model/fishaudio'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Other',
|
id: 'Other',
|
||||||
name: i18nT('common:model_other'),
|
name: i18nT('common:model_other'),
|
||||||
|
|||||||
5
packages/global/core/app/collaborator.d.ts
vendored
5
packages/global/core/app/collaborator.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
import { RequireOnlyOne } from '../../common/type/utils';
|
import type { RequireOnlyOne } from '../../common/type/utils';
|
||||||
import {
|
import {
|
||||||
UpdateClbPermissionProps,
|
type UpdateClbPermissionProps,
|
||||||
UpdatePermissionBody
|
UpdatePermissionBody
|
||||||
} from '../../support/permission/collaborator';
|
} from '../../support/permission/collaborator';
|
||||||
import { PermissionValueType } from '../../support/permission/type';
|
import { PermissionValueType } from '../../support/permission/type';
|
||||||
@@ -14,4 +14,5 @@ export type AppCollaboratorDeleteParams = {
|
|||||||
} & RequireOnlyOne<{
|
} & RequireOnlyOne<{
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
orgId: string;
|
||||||
}>;
|
}>;
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ export type DatasetCollaboratorDeleteParams = {
|
|||||||
} & RequireOnlyOne<{
|
} & RequireOnlyOne<{
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
orgId: string;
|
||||||
}>;
|
}>;
|
||||||
|
|||||||
9
packages/global/core/dataset/type.d.ts
vendored
9
packages/global/core/dataset/type.d.ts
vendored
@@ -112,6 +112,15 @@ export type DatasetDataSchemaType = {
|
|||||||
rebuilding?: boolean;
|
rebuilding?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DatasetDataTextSchemaType = {
|
||||||
|
_id: string;
|
||||||
|
teamId: string;
|
||||||
|
datasetId: string;
|
||||||
|
collectionId: string;
|
||||||
|
dataId: string;
|
||||||
|
fullTextToken: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type DatasetTrainingSchemaType = {
|
export type DatasetTrainingSchemaType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|||||||
@@ -107,7 +107,9 @@ export type DispatchNodeResponseType = {
|
|||||||
mergeSignId?: string;
|
mergeSignId?: string;
|
||||||
|
|
||||||
// bill
|
// bill
|
||||||
tokens?: number;
|
tokens?: number; // deprecated
|
||||||
|
inputTokens?: number;
|
||||||
|
outputTokens?: number;
|
||||||
model?: string;
|
model?: string;
|
||||||
contextTotalLen?: number;
|
contextTotalLen?: number;
|
||||||
totalPoints?: number;
|
totalPoints?: number;
|
||||||
@@ -157,6 +159,8 @@ export type DispatchNodeResponseType = {
|
|||||||
|
|
||||||
// tool
|
// tool
|
||||||
toolCallTokens?: number;
|
toolCallTokens?: number;
|
||||||
|
toolCallInputTokens?: number;
|
||||||
|
toolCallOutputTokens?: number;
|
||||||
toolDetail?: ChatHistoryItemResType[];
|
toolDetail?: ChatHistoryItemResType[];
|
||||||
toolStop?: boolean;
|
toolStop?: boolean;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { isValidReferenceValueFormat } from '../utils';
|
|||||||
import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
||||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||||
import { replaceVariable } from '../../../common/string/tools';
|
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
||||||
|
|
||||||
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
|
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
|
||||||
let limit = 10;
|
let limit = 10;
|
||||||
@@ -343,11 +343,7 @@ export function replaceEditorVariable({
|
|||||||
if (input) return getReferenceVariableValue({ value: input.value, nodes, variables });
|
if (input) return getReferenceVariableValue({ value: input.value, nodes, variables });
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const formatVal = (() => {
|
const formatVal = valToStr(variableVal);
|
||||||
if (variableVal === undefined) return 'undefined';
|
|
||||||
if (variableVal === null) return 'null';
|
|
||||||
return typeof variableVal === 'object' ? JSON.stringify(variableVal) : String(variableVal);
|
|
||||||
})();
|
|
||||||
|
|
||||||
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, 'g');
|
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, 'g');
|
||||||
text = text.replace(regex, () => formatVal);
|
text = text.replace(regex, () => formatVal);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"next": "14.2.5",
|
"next": "14.2.5",
|
||||||
"openai": "4.61.0",
|
"openai": "4.61.0",
|
||||||
"openapi-types": "^12.1.3",
|
"openapi-types": "^12.1.3",
|
||||||
|
"json5": "^2.2.3",
|
||||||
"timezones-list": "^3.0.2"
|
"timezones-list": "^3.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -10,22 +10,18 @@ export type CollaboratorItemType = {
|
|||||||
} & RequireOnlyOne<{
|
} & RequireOnlyOne<{
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
orgId: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type UpdateClbPermissionProps = {
|
export type UpdateClbPermissionProps = {
|
||||||
members?: string[];
|
members?: string[];
|
||||||
groups?: string[];
|
groups?: string[];
|
||||||
|
orgs?: string[];
|
||||||
permission: PermissionValueType;
|
permission: PermissionValueType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteClbPermissionProps = RequireOnlyOne<{
|
export type DeletePermissionQuery = RequireOnlyOne<{
|
||||||
tmbId: string;
|
tmbId?: string;
|
||||||
groupId: string;
|
groupId?: string;
|
||||||
}>;
|
orgId?: string;
|
||||||
|
|
||||||
export type UpdatePermissionBody = {
|
|
||||||
permission: PermissionValueType;
|
|
||||||
} & RequireOnlyOne<{
|
|
||||||
memberId: string;
|
|
||||||
groupId: string;
|
|
||||||
}>;
|
}>;
|
||||||
|
|||||||
4
packages/global/support/permission/type.d.ts
vendored
4
packages/global/support/permission/type.d.ts
vendored
@@ -1,8 +1,9 @@
|
|||||||
import { UserModelSchema } from '../user/type';
|
import { UserModelSchema } from '../user/type';
|
||||||
import { RequireOnlyOne } from '../../common/type/utils';
|
import { RequireOnlyOne } from '../../common/type/utils';
|
||||||
import { TeamMemberSchema } from '../user/team/type';
|
import { TeamMemberSchema } from '../user/team/type';
|
||||||
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
|
|
||||||
import { MemberGroupSchemaType } from './memberGroup/type';
|
import { MemberGroupSchemaType } from './memberGroup/type';
|
||||||
|
import type { TeamMemberWithUserSchema } from '../user/team/type';
|
||||||
|
import { AuthUserTypeEnum, type PermissionKeyEnum, type PerResourceTypeEnum } from './constant';
|
||||||
|
|
||||||
// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
|
// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
|
||||||
// It is spired by the permission system in Linux.
|
// It is spired by the permission system in Linux.
|
||||||
@@ -29,6 +30,7 @@ export type ResourcePermissionType = {
|
|||||||
} & RequireOnlyOne<{
|
} & RequireOnlyOne<{
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
orgId: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
|
export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
|
||||||
|
|||||||
@@ -17,5 +17,6 @@ export enum OAuthEnum {
|
|||||||
wechat = 'wechat',
|
wechat = 'wechat',
|
||||||
microsoft = 'microsoft',
|
microsoft = 'microsoft',
|
||||||
dingtalk = 'dingtalk',
|
dingtalk = 'dingtalk',
|
||||||
|
wecom = 'wecom',
|
||||||
sso = 'sso'
|
sso = 'sso'
|
||||||
}
|
}
|
||||||
|
|||||||
3
packages/global/support/user/login/constants.ts
Normal file
3
packages/global/support/user/login/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function checkIsWecomTerminal() {
|
||||||
|
return /wxwork/i.test(navigator.userAgent);
|
||||||
|
}
|
||||||
32
packages/global/support/user/team/org/api.d.ts
vendored
Normal file
32
packages/global/support/user/team/org/api.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
export type postCreateOrgData = {
|
||||||
|
name: string;
|
||||||
|
parentId: string;
|
||||||
|
description?: string;
|
||||||
|
avatar?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type putUpdateOrgMembersData = {
|
||||||
|
orgId: string;
|
||||||
|
members: {
|
||||||
|
tmbId: string;
|
||||||
|
// role: `${OrgMemberRole}`;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type putUpdateOrgData = {
|
||||||
|
orgId: string;
|
||||||
|
name?: string;
|
||||||
|
avatar?: string;
|
||||||
|
description?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type putMoveOrgType = {
|
||||||
|
orgId: string;
|
||||||
|
targetOrgId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// type putChnageOrgOwnerData = {
|
||||||
|
// orgId: string;
|
||||||
|
// tmbId: string;
|
||||||
|
// toAdmin?: boolean;
|
||||||
|
// };
|
||||||
12
packages/global/support/user/team/org/constant.ts
Normal file
12
packages/global/support/user/team/org/constant.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { OrgSchemaType } from './type';
|
||||||
|
|
||||||
|
export const OrgCollectionName = 'team_orgs';
|
||||||
|
export const OrgMemberCollectionName = 'team_org_members';
|
||||||
|
|
||||||
|
export const getOrgChildrenPath = (org: OrgSchemaType) => `${org.path}/${org.pathId}`;
|
||||||
|
|
||||||
|
// export enum OrgMemberRole {
|
||||||
|
// owner = 'owner',
|
||||||
|
// admin = 'admin',
|
||||||
|
// member = 'member'
|
||||||
|
// }
|
||||||
25
packages/global/support/user/team/org/type.d.ts
vendored
Normal file
25
packages/global/support/user/team/org/type.d.ts
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import type { TeamPermission } from 'support/permission/user/controller';
|
||||||
|
import { ResourcePermissionType } from '../type';
|
||||||
|
|
||||||
|
type OrgSchemaType = {
|
||||||
|
_id: string;
|
||||||
|
teamId: string;
|
||||||
|
pathId: string;
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
avatar?: string;
|
||||||
|
description?: string;
|
||||||
|
updateTime: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OrgMemberSchemaType = {
|
||||||
|
teamId: string;
|
||||||
|
orgId: string;
|
||||||
|
tmbId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OrgType = Omit<OrgSchemaType, 'avatar'> & {
|
||||||
|
avatar: string;
|
||||||
|
members: OrgMemberSchemaType[];
|
||||||
|
permission: TeamPermission;
|
||||||
|
};
|
||||||
3
packages/global/support/user/team/type.d.ts
vendored
3
packages/global/support/user/team/type.d.ts
vendored
@@ -55,10 +55,11 @@ export type TeamMemberWithTeamAndUserSchema = TeamMemberSchema & {
|
|||||||
export type TeamTmbItemType = {
|
export type TeamTmbItemType = {
|
||||||
userId: string;
|
userId: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
|
teamAvatar?: string;
|
||||||
teamName: string;
|
teamName: string;
|
||||||
memberName: string;
|
memberName: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
balance: number;
|
balance?: number;
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
teamDomain: string;
|
teamDomain: string;
|
||||||
defaultTeam: boolean;
|
defaultTeam: boolean;
|
||||||
|
|||||||
16
packages/global/support/user/utils.ts
Normal file
16
packages/global/support/user/utils.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export const getRandomUserAvatar = () => {
|
||||||
|
const defaultAvatars = [
|
||||||
|
'/imgs/avatar/RoyalBlueAvatar.svg',
|
||||||
|
'/imgs/avatar/PurpleAvatar.svg',
|
||||||
|
'/imgs/avatar/AdoraAvatar.svg',
|
||||||
|
'/imgs/avatar/OrangeAvatar.svg',
|
||||||
|
'/imgs/avatar/RedAvatar.svg',
|
||||||
|
'/imgs/avatar/GrayModernAvatar.svg',
|
||||||
|
'/imgs/avatar/TealAvatar.svg',
|
||||||
|
'/imgs/avatar/GreenAvatar.svg',
|
||||||
|
'/imgs/avatar/BrightBlueAvatar.svg',
|
||||||
|
'/imgs/avatar/BlueAvatar.svg'
|
||||||
|
];
|
||||||
|
|
||||||
|
return defaultAvatars[Math.floor(Math.random() * defaultAvatars.length)];
|
||||||
|
};
|
||||||
@@ -23,7 +23,8 @@ export type BillSchemaType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ChatNodeUsageType = {
|
export type ChatNodeUsageType = {
|
||||||
tokens?: number;
|
inputTokens?: number;
|
||||||
|
outputTokens?: number;
|
||||||
totalPoints: number;
|
totalPoints: number;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ import { CreateUsageProps } from './api';
|
|||||||
import { UsageSourceEnum } from './constants';
|
import { UsageSourceEnum } from './constants';
|
||||||
|
|
||||||
export type UsageListItemCountType = {
|
export type UsageListItemCountType = {
|
||||||
tokens?: number;
|
inputTokens?: number;
|
||||||
|
outputTokens?: number;
|
||||||
charsLength?: number;
|
charsLength?: number;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
tokens?: number;
|
||||||
};
|
};
|
||||||
export type UsageListItemType = UsageListItemCountType & {
|
export type UsageListItemType = UsageListItemCountType & {
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
|
|||||||
@@ -35,24 +35,26 @@ export const list = [...staticPluginList, ...packagePluginList];
|
|||||||
|
|
||||||
/* Get plugins */
|
/* Get plugins */
|
||||||
export const getCommunityPlugins = () => {
|
export const getCommunityPlugins = () => {
|
||||||
return list.map<SystemPluginTemplateItemType>((name) => {
|
return Promise.all(
|
||||||
const config = require(`./src/${name}/template.json`);
|
list.map<Promise<SystemPluginTemplateItemType>>(async (name) => {
|
||||||
|
const config = (await import(`./src/${name}/template.json`))?.default;
|
||||||
|
|
||||||
const isFolder = list.find((item) => item.startsWith(`${name}/`));
|
const isFolder = list.find((item) => item.startsWith(`${name}/`));
|
||||||
|
|
||||||
const parentIdList = name.split('/').slice(0, -1);
|
const parentIdList = name.split('/').slice(0, -1);
|
||||||
const parentId =
|
const parentId =
|
||||||
parentIdList.length > 0 ? `${PluginSourceEnum.community}-${parentIdList.join('/')}` : null;
|
parentIdList.length > 0 ? `${PluginSourceEnum.community}-${parentIdList.join('/')}` : null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
id: `${PluginSourceEnum.community}-${name}`,
|
id: `${PluginSourceEnum.community}-${name}`,
|
||||||
isFolder,
|
isFolder,
|
||||||
parentId,
|
parentId,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isOfficial: true
|
isOfficial: true
|
||||||
};
|
};
|
||||||
});
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSystemPluginTemplates = () => {
|
export const getSystemPluginTemplates = () => {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { SystemPluginSpecialResponse } from '../../../type.d';
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title: string;
|
title: string;
|
||||||
xAxis: string;
|
xAxis: string[];
|
||||||
yAxis: string;
|
yAxis: string[];
|
||||||
chartType: string;
|
chartType: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,7 +27,12 @@ type Option = {
|
|||||||
series: SeriesData[]; // 使用定义的类型
|
series: SeriesData[]; // 使用定义的类型
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateChart = async (title: string, xAxis: string, yAxis: string, chartType: string) => {
|
const generateChart = async (
|
||||||
|
title: string,
|
||||||
|
xAxis: string[],
|
||||||
|
yAxis: string[],
|
||||||
|
chartType: string
|
||||||
|
) => {
|
||||||
// @ts-ignore 无法使用dom,如使用jsdom会出现生成图片无法正常展示,有高手可以帮忙解决
|
// @ts-ignore 无法使用dom,如使用jsdom会出现生成图片无法正常展示,有高手可以帮忙解决
|
||||||
const chart = echarts.init(undefined, undefined, {
|
const chart = echarts.init(undefined, undefined, {
|
||||||
renderer: 'svg', // 必须使用 SVG 模式
|
renderer: 'svg', // 必须使用 SVG 模式
|
||||||
@@ -36,21 +41,11 @@ const generateChart = async (title: string, xAxis: string, yAxis: string, chartT
|
|||||||
height: 300
|
height: 300
|
||||||
});
|
});
|
||||||
|
|
||||||
let parsedXAxis: string[] = [];
|
|
||||||
let parsedYAxis: number[] = [];
|
|
||||||
try {
|
|
||||||
parsedXAxis = json5.parse(xAxis);
|
|
||||||
parsedYAxis = json5.parse(yAxis);
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('解析数据时出错:', error);
|
|
||||||
return Promise.reject('Data error');
|
|
||||||
}
|
|
||||||
|
|
||||||
const option: Option = {
|
const option: Option = {
|
||||||
backgroundColor: '#f5f5f5',
|
backgroundColor: '#f5f5f5',
|
||||||
title: { text: title },
|
title: { text: title },
|
||||||
tooltip: {},
|
tooltip: {},
|
||||||
xAxis: { data: parsedXAxis },
|
xAxis: { data: xAxis },
|
||||||
yAxis: {},
|
yAxis: {},
|
||||||
series: [] // 初始化为空数组
|
series: [] // 初始化为空数组
|
||||||
};
|
};
|
||||||
@@ -58,18 +53,18 @@ const generateChart = async (title: string, xAxis: string, yAxis: string, chartT
|
|||||||
// 根据 chartType 生成不同的图表
|
// 根据 chartType 生成不同的图表
|
||||||
switch (chartType) {
|
switch (chartType) {
|
||||||
case '柱状图':
|
case '柱状图':
|
||||||
option.series.push({ name: 'Sample', type: 'bar', data: parsedYAxis });
|
option.series.push({ name: 'Sample', type: 'bar', data: yAxis.map(Number) });
|
||||||
break;
|
break;
|
||||||
case '折线图':
|
case '折线图':
|
||||||
option.series.push({ name: 'Sample', type: 'line', data: parsedYAxis });
|
option.series.push({ name: 'Sample', type: 'line', data: yAxis.map(Number) });
|
||||||
break;
|
break;
|
||||||
case '饼图':
|
case '饼图':
|
||||||
option.series.push({
|
option.series.push({
|
||||||
name: 'Sample',
|
name: 'Sample',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
data: parsedYAxis.map((value, index) => ({
|
data: yAxis.map((value, index) => ({
|
||||||
value,
|
value: Number(value),
|
||||||
name: parsedXAxis[index] // 使用 xAxis 作为饼图的名称
|
name: xAxis[index] // 使用 xAxis 作为饼图的名称
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"author": "silencezhang",
|
"author": "silencezhang",
|
||||||
"version": "4812",
|
"version": "4817",
|
||||||
"name": "基础图表",
|
"name": "基础图表",
|
||||||
"avatar": "core/workflow/template/baseChart",
|
"avatar": "core/workflow/template/baseChart",
|
||||||
"intro": "根据数据生成图表,可根据chartType生成柱状图,折线图,饼图",
|
"intro": "根据数据生成图表,可根据chartType生成柱状图,折线图,饼图",
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
"canEdit": true,
|
"canEdit": true,
|
||||||
"key": "yAxis",
|
"key": "yAxis",
|
||||||
"label": "yAxis",
|
"label": "yAxis",
|
||||||
"description": "y轴数据,例如:['1', '2', '3']",
|
"description": "y轴数据,例如:[1,2,3]",
|
||||||
"defaultValue": "",
|
"defaultValue": "",
|
||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"required": true,
|
"required": true,
|
||||||
"toolDescription": "y轴数据,例如:['1', '2', '3']"
|
"toolDescription": "y轴数据,例如:[1,2,3]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"renderTypeList": ["select", "reference"],
|
"renderTypeList": ["select", "reference"],
|
||||||
@@ -145,8 +145,8 @@
|
|||||||
"flowNodeType": "pluginOutput",
|
"flowNodeType": "pluginOutput",
|
||||||
"showStatus": false,
|
"showStatus": false,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 2122.252754006148,
|
"x": 2128.8138851197145,
|
||||||
"y": -63.5218674613718
|
"y": -63.52186746137181
|
||||||
},
|
},
|
||||||
"version": "481",
|
"version": "481",
|
||||||
"inputs": [
|
"inputs": [
|
||||||
@@ -154,10 +154,12 @@
|
|||||||
"renderTypeList": ["reference"],
|
"renderTypeList": ["reference"],
|
||||||
"valueType": "string",
|
"valueType": "string",
|
||||||
"canEdit": true,
|
"canEdit": true,
|
||||||
"key": "相对路径URL",
|
"key": "图表 url",
|
||||||
"label": "相对路径URL",
|
"label": "图表 url",
|
||||||
"description": "可用使用markdown格式展示图片,如:",
|
"description": "可用使用markdown格式展示图片,如:",
|
||||||
"value": ["ws0DFKJnCPhk", "bzaYjKyQFOw2"]
|
"value": ["ws0DFKJnCPhk", "bzaYjKyQFOw2"],
|
||||||
|
"isToolOutput": true,
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputs": []
|
"outputs": []
|
||||||
@@ -170,8 +172,8 @@
|
|||||||
"flowNodeType": "httpRequest468",
|
"flowNodeType": "httpRequest468",
|
||||||
"showStatus": true,
|
"showStatus": true,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 1216.5166647574395,
|
"x": 1264.2009472531117,
|
||||||
"y": -206.30162946606856
|
"y": -455.0773486762623
|
||||||
},
|
},
|
||||||
"version": "481",
|
"version": "481",
|
||||||
"inputs": [
|
"inputs": [
|
||||||
@@ -275,7 +277,7 @@
|
|||||||
"key": "system_httpJsonBody",
|
"key": "system_httpJsonBody",
|
||||||
"renderTypeList": ["hidden"],
|
"renderTypeList": ["hidden"],
|
||||||
"valueType": "any",
|
"valueType": "any",
|
||||||
"value": "{\r\n \"title\": \"{{title-plugin}}\",\r\n \"xAxis\": \"{{xAxis-plugin}}\",\r\n \"yAxis\": \"{{yAxis-plugin}}\",\r\n \"chartType\": \"{{chartType-plugin}}\"\r\n}",
|
"value": "{\r\n \"title\": \"{{$pluginInput.title$}}\",\r\n \"xAxis\": {{$pluginInput.xAxis$}},\r\n \"yAxis\": {{$pluginInput.yAxis$}},\r\n \"chartType\": \"{{$pluginInput.chartType$}}\"\r\n}",
|
||||||
"label": "",
|
"label": "",
|
||||||
"required": false,
|
"required": false,
|
||||||
"valueDesc": "",
|
"valueDesc": "",
|
||||||
@@ -306,126 +308,6 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"debugLabel": "",
|
"debugLabel": "",
|
||||||
"toolDescription": ""
|
"toolDescription": ""
|
||||||
},
|
|
||||||
{
|
|
||||||
"renderTypeList": ["reference"],
|
|
||||||
"valueType": "string",
|
|
||||||
"canEdit": true,
|
|
||||||
"key": "title-plugin",
|
|
||||||
"label": "title-plugin",
|
|
||||||
"customInputConfig": {
|
|
||||||
"selectValueTypeList": [
|
|
||||||
"string",
|
|
||||||
"number",
|
|
||||||
"boolean",
|
|
||||||
"object",
|
|
||||||
"arrayString",
|
|
||||||
"arrayNumber",
|
|
||||||
"arrayBoolean",
|
|
||||||
"arrayObject",
|
|
||||||
"arrayAny",
|
|
||||||
"any",
|
|
||||||
"chatHistory",
|
|
||||||
"datasetQuote",
|
|
||||||
"dynamic",
|
|
||||||
"selectApp",
|
|
||||||
"selectDataset"
|
|
||||||
],
|
|
||||||
"showDescription": false,
|
|
||||||
"showDefaultValue": true
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"value": ["pluginInput", "title"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"renderTypeList": ["reference"],
|
|
||||||
"valueType": "string",
|
|
||||||
"canEdit": true,
|
|
||||||
"key": "xAxis-plugin",
|
|
||||||
"label": "xAxis-plugin",
|
|
||||||
"customInputConfig": {
|
|
||||||
"selectValueTypeList": [
|
|
||||||
"string",
|
|
||||||
"number",
|
|
||||||
"boolean",
|
|
||||||
"object",
|
|
||||||
"arrayString",
|
|
||||||
"arrayNumber",
|
|
||||||
"arrayBoolean",
|
|
||||||
"arrayObject",
|
|
||||||
"arrayAny",
|
|
||||||
"any",
|
|
||||||
"chatHistory",
|
|
||||||
"datasetQuote",
|
|
||||||
"dynamic",
|
|
||||||
"selectApp",
|
|
||||||
"selectDataset"
|
|
||||||
],
|
|
||||||
"showDescription": false,
|
|
||||||
"showDefaultValue": true
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"value": ["pluginInput", "xAxis"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"renderTypeList": ["reference"],
|
|
||||||
"valueType": "string",
|
|
||||||
"canEdit": true,
|
|
||||||
"key": "yAxis-plugin",
|
|
||||||
"label": "yAxis-plugin",
|
|
||||||
"customInputConfig": {
|
|
||||||
"selectValueTypeList": [
|
|
||||||
"string",
|
|
||||||
"number",
|
|
||||||
"boolean",
|
|
||||||
"object",
|
|
||||||
"arrayString",
|
|
||||||
"arrayNumber",
|
|
||||||
"arrayBoolean",
|
|
||||||
"arrayObject",
|
|
||||||
"arrayAny",
|
|
||||||
"any",
|
|
||||||
"chatHistory",
|
|
||||||
"datasetQuote",
|
|
||||||
"dynamic",
|
|
||||||
"selectApp",
|
|
||||||
"selectDataset"
|
|
||||||
],
|
|
||||||
"showDescription": false,
|
|
||||||
"showDefaultValue": true
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"value": ["pluginInput", "yAxis"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"renderTypeList": ["reference"],
|
|
||||||
"valueType": "string",
|
|
||||||
"canEdit": true,
|
|
||||||
"key": "chartType-plugin",
|
|
||||||
"label": "chartType-plugin",
|
|
||||||
"customInputConfig": {
|
|
||||||
"selectValueTypeList": [
|
|
||||||
"string",
|
|
||||||
"number",
|
|
||||||
"boolean",
|
|
||||||
"object",
|
|
||||||
"arrayString",
|
|
||||||
"arrayNumber",
|
|
||||||
"arrayBoolean",
|
|
||||||
"arrayObject",
|
|
||||||
"arrayAny",
|
|
||||||
"any",
|
|
||||||
"chatHistory",
|
|
||||||
"datasetQuote",
|
|
||||||
"dynamic",
|
|
||||||
"selectApp",
|
|
||||||
"selectDataset"
|
|
||||||
],
|
|
||||||
"showDescription": false,
|
|
||||||
"showDefaultValue": true
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"value": ["pluginInput", "chartType"]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"version": "488",
|
"version": "488",
|
||||||
"name": "飞书 webhook",
|
"name": "飞书 webhook",
|
||||||
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg",
|
"avatar": "core/app/templates/plugin-feishu",
|
||||||
"intro": "向飞书机器人发起 webhook 请求。",
|
"intro": "向飞书机器人发起 webhook 请求。",
|
||||||
"courseUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
|
"courseUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
|
||||||
"showStatus": false,
|
"showStatus": false,
|
||||||
|
|||||||
@@ -48,6 +48,16 @@ const main = async (props: Props, retry = 3): Response => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (results.length === 0) {
|
||||||
|
return {
|
||||||
|
result: JSON.stringify([]),
|
||||||
|
error: {
|
||||||
|
message: 'No search results',
|
||||||
|
code: 500
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: JSON.stringify(results.slice(0, 10))
|
result: JSON.stringify(results.slice(0, 10))
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import fsp from 'fs/promises';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { DatasetFileSchema } from '@fastgpt/global/core/dataset/type';
|
import { DatasetFileSchema } from '@fastgpt/global/core/dataset/type';
|
||||||
import { MongoChatFileSchema, MongoDatasetFileSchema } from './schema';
|
import { MongoChatFileSchema, MongoDatasetFileSchema } from './schema';
|
||||||
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
|
import { detectFileEncoding, detectFileEncodingByPath } from '@fastgpt/global/common/file/tools';
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
import { MongoRawTextBuffer } from '../../buffer/rawText/schema';
|
import { MongoRawTextBuffer } from '../../buffer/rawText/schema';
|
||||||
import { readRawContentByFileBuffer } from '../read/utils';
|
import { readRawContentByFileBuffer } from '../read/utils';
|
||||||
@@ -36,7 +36,6 @@ export async function uploadFile({
|
|||||||
path,
|
path,
|
||||||
filename,
|
filename,
|
||||||
contentType,
|
contentType,
|
||||||
encoding,
|
|
||||||
metadata = {}
|
metadata = {}
|
||||||
}: {
|
}: {
|
||||||
bucketName: `${BucketNameEnum}`;
|
bucketName: `${BucketNameEnum}`;
|
||||||
@@ -45,7 +44,6 @@ export async function uploadFile({
|
|||||||
path: string;
|
path: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
contentType?: string;
|
contentType?: string;
|
||||||
encoding: string;
|
|
||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
}) {
|
}) {
|
||||||
if (!path) return Promise.reject(`filePath is empty`);
|
if (!path) return Promise.reject(`filePath is empty`);
|
||||||
@@ -59,7 +57,7 @@ export async function uploadFile({
|
|||||||
// Add default metadata
|
// Add default metadata
|
||||||
metadata.teamId = teamId;
|
metadata.teamId = teamId;
|
||||||
metadata.uid = uid;
|
metadata.uid = uid;
|
||||||
metadata.encoding = encoding;
|
metadata.encoding = await detectFileEncodingByPath(path);
|
||||||
|
|
||||||
// create a gridfs bucket
|
// create a gridfs bucket
|
||||||
const bucket = getGridBucket(bucketName);
|
const bucket = getGridBucket(bucketName);
|
||||||
|
|||||||
@@ -1,43 +1,92 @@
|
|||||||
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
||||||
import { imageBaseUrl } from '@fastgpt/global/common/file/image/constants';
|
import { imageBaseUrl } from '@fastgpt/global/common/file/image/constants';
|
||||||
import { MongoImage } from './schema';
|
import { MongoImage } from './schema';
|
||||||
import { ClientSession } from '../../../common/mongo';
|
import { ClientSession, Types } from '../../../common/mongo';
|
||||||
import { guessBase64ImageType } from '../utils';
|
import { guessBase64ImageType } from '../utils';
|
||||||
import { readFromSecondary } from '../../mongo/utils';
|
import { readFromSecondary } from '../../mongo/utils';
|
||||||
|
import { addHours } from 'date-fns';
|
||||||
|
|
||||||
export const maxImgSize = 1024 * 1024 * 12;
|
export const maxImgSize = 1024 * 1024 * 12;
|
||||||
const base64MimeRegex = /data:image\/([^\)]+);base64/;
|
const base64MimeRegex = /data:image\/([^\)]+);base64/;
|
||||||
export async function uploadMongoImg({
|
export async function uploadMongoImg({
|
||||||
type,
|
|
||||||
base64Img,
|
base64Img,
|
||||||
teamId,
|
teamId,
|
||||||
expiredTime,
|
|
||||||
metadata,
|
metadata,
|
||||||
shareId
|
shareId,
|
||||||
|
forever = false
|
||||||
}: UploadImgProps & {
|
}: UploadImgProps & {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
|
forever?: Boolean;
|
||||||
}) {
|
}) {
|
||||||
if (base64Img.length > maxImgSize) {
|
if (base64Img.length > maxImgSize) {
|
||||||
return Promise.reject('Image too large');
|
return Promise.reject('Image too large');
|
||||||
}
|
}
|
||||||
|
|
||||||
const [base64Mime, base64Data] = base64Img.split(',');
|
const [base64Mime, base64Data] = base64Img.split(',');
|
||||||
|
// Check if mime type is valid
|
||||||
|
if (!base64MimeRegex.test(base64Mime)) {
|
||||||
|
return Promise.reject('Invalid image mime type');
|
||||||
|
}
|
||||||
|
|
||||||
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
|
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
|
||||||
const binary = Buffer.from(base64Data, 'base64');
|
const binary = Buffer.from(base64Data, 'base64');
|
||||||
const extension = mime.split('/')[1];
|
const extension = mime.split('/')[1];
|
||||||
|
|
||||||
const { _id } = await MongoImage.create({
|
const { _id } = await MongoImage.create({
|
||||||
type,
|
|
||||||
teamId,
|
teamId,
|
||||||
binary,
|
binary,
|
||||||
expiredTime,
|
|
||||||
metadata: Object.assign({ mime }, metadata),
|
metadata: Object.assign({ mime }, metadata),
|
||||||
shareId
|
shareId,
|
||||||
|
expiredTime: forever ? undefined : addHours(new Date(), 1)
|
||||||
});
|
});
|
||||||
|
|
||||||
return `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(_id)}.${extension}`;
|
return `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(_id)}.${extension}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getIdFromPath = (path?: string) => {
|
||||||
|
if (!path) return;
|
||||||
|
|
||||||
|
const paths = path.split('/');
|
||||||
|
const name = paths[paths.length - 1];
|
||||||
|
|
||||||
|
if (!name) return;
|
||||||
|
|
||||||
|
const id = name.split('.')[0];
|
||||||
|
if (!id || !Types.ObjectId.isValid(id)) return;
|
||||||
|
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
// 删除旧的头像,新的头像去除过期时间
|
||||||
|
export const refreshSourceAvatar = async (
|
||||||
|
path?: string,
|
||||||
|
oldPath?: string,
|
||||||
|
session?: ClientSession
|
||||||
|
) => {
|
||||||
|
const newId = getIdFromPath(path);
|
||||||
|
const oldId = getIdFromPath(oldPath);
|
||||||
|
|
||||||
|
if (!newId) return;
|
||||||
|
|
||||||
|
await MongoImage.updateOne({ _id: newId }, { $unset: { expiredTime: 1 } }, { session });
|
||||||
|
|
||||||
|
if (oldId) {
|
||||||
|
await MongoImage.deleteOne({ _id: oldId }, { session });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const removeImageByPath = (path?: string, session?: ClientSession) => {
|
||||||
|
if (!path) return;
|
||||||
|
|
||||||
|
const paths = path.split('/');
|
||||||
|
const name = paths[paths.length - 1];
|
||||||
|
|
||||||
|
if (!name) return;
|
||||||
|
|
||||||
|
const id = name.split('.')[0];
|
||||||
|
if (!id || !Types.ObjectId.isValid(id)) return;
|
||||||
|
|
||||||
|
return MongoImage.deleteOne({ _id: id }, { session });
|
||||||
|
};
|
||||||
|
|
||||||
export async function readMongoImg({ id }: { id: string }) {
|
export async function readMongoImg({ id }: { id: string }) {
|
||||||
const formatId = id.replace(/\.[^/.]+$/, '');
|
const formatId = id.replace(/\.[^/.]+$/, '');
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||||
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
|
import { connectionMongo, getMongoModel } from '../../mongo';
|
||||||
import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type.d';
|
import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type.d';
|
||||||
import { mongoImageTypeMap } from '@fastgpt/global/common/file/image/constants';
|
const { Schema } = connectionMongo;
|
||||||
const { Schema, model, models } = connectionMongo;
|
|
||||||
|
|
||||||
const ImageSchema = new Schema({
|
const ImageSchema = new Schema({
|
||||||
teamId: {
|
teamId: {
|
||||||
@@ -14,27 +13,15 @@ const ImageSchema = new Schema({
|
|||||||
type: Date,
|
type: Date,
|
||||||
default: () => new Date()
|
default: () => new Date()
|
||||||
},
|
},
|
||||||
expiredTime: {
|
expiredTime: Date,
|
||||||
type: Date
|
binary: Buffer,
|
||||||
},
|
metadata: Object
|
||||||
binary: {
|
|
||||||
type: Buffer
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
enum: Object.keys(mongoImageTypeMap),
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// tts expired(60 Minutes)
|
// tts expired(60 Minutes)
|
||||||
ImageSchema.index({ expiredTime: 1 }, { expireAfterSeconds: 60 * 60 });
|
ImageSchema.index({ expiredTime: 1 }, { expireAfterSeconds: 60 * 60 });
|
||||||
ImageSchema.index({ type: 1 });
|
ImageSchema.index({ type: 1 });
|
||||||
ImageSchema.index({ createTime: 1 });
|
|
||||||
// delete related img
|
// delete related img
|
||||||
ImageSchema.index({ teamId: 1, 'metadata.relatedId': 1 });
|
ImageSchema.index({ teamId: 1, 'metadata.relatedId': 1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { uploadMongoImg } from '../image/controller';
|
import { uploadMongoImg } from '../image/controller';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
|
|
||||||
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
|
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
|
||||||
@@ -8,7 +7,6 @@ import type { ReadFileResponse } from '../../../worker/readFile/type';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { addLog } from '../../system/log';
|
import { addLog } from '../../system/log';
|
||||||
import { batchRun } from '@fastgpt/global/common/fn/utils';
|
import { batchRun } from '@fastgpt/global/common/fn/utils';
|
||||||
import { addHours } from 'date-fns';
|
|
||||||
import { matchMdImgTextAndUpload } from '@fastgpt/global/common/string/markdown';
|
import { matchMdImgTextAndUpload } from '@fastgpt/global/common/string/markdown';
|
||||||
|
|
||||||
export type readRawTextByLocalFileParams = {
|
export type readRawTextByLocalFileParams = {
|
||||||
@@ -22,7 +20,7 @@ export const readRawTextByLocalFile = async (params: readRawTextByLocalFileParam
|
|||||||
|
|
||||||
const extension = path?.split('.')?.pop()?.toLowerCase() || '';
|
const extension = path?.split('.')?.pop()?.toLowerCase() || '';
|
||||||
|
|
||||||
const buffer = fs.readFileSync(path);
|
const buffer = await fs.promises.readFile(path);
|
||||||
|
|
||||||
const { rawText } = await readRawContentByFileBuffer({
|
const { rawText } = await readRawContentByFileBuffer({
|
||||||
extension,
|
extension,
|
||||||
@@ -114,10 +112,9 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
if (imageList) {
|
if (imageList) {
|
||||||
await batchRun(imageList, async (item) => {
|
await batchRun(imageList, async (item) => {
|
||||||
const src = await uploadMongoImg({
|
const src = await uploadMongoImg({
|
||||||
type: MongoImageTypeEnum.collectionImage,
|
|
||||||
base64Img: `data:${item.mime};base64,${item.base64}`,
|
base64Img: `data:${item.mime};base64,${item.base64}`,
|
||||||
teamId,
|
teamId,
|
||||||
expiredTime: addHours(new Date(), 1),
|
// expiredTime: addHours(new Date(), 1),
|
||||||
metadata: {
|
metadata: {
|
||||||
...metadata,
|
...metadata,
|
||||||
mime: item.mime
|
mime: item.mime
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ import NextCors from 'nextjs-cors';
|
|||||||
|
|
||||||
export async function withNextCors(req: NextApiRequest, res: NextApiResponse) {
|
export async function withNextCors(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const methods = ['GET', 'eHEAD', 'PUT', 'PATCH', 'POST', 'DELETE'];
|
const methods = ['GET', 'eHEAD', 'PUT', 'PATCH', 'POST', 'DELETE'];
|
||||||
|
|
||||||
|
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',');
|
||||||
const origin = req.headers.origin;
|
const origin = req.headers.origin;
|
||||||
|
|
||||||
await NextCors(req, res, {
|
await NextCors(req, res, {
|
||||||
methods,
|
methods,
|
||||||
origin: origin,
|
origin: allowedOrigins || origin,
|
||||||
optionsSuccessStatus: 200
|
optionsSuccessStatus: 200
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import { jsonRes } from '../response';
|
|||||||
// unit: times/s
|
// unit: times/s
|
||||||
// how to use?
|
// how to use?
|
||||||
// export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip
|
// export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip
|
||||||
export function useReqFrequencyLimit(seconds: number, limit: number) {
|
export function useReqFrequencyLimit(seconds: number, limit: number, force = false) {
|
||||||
return async (req: ApiRequestProps, res: NextApiResponse) => {
|
return async (req: ApiRequestProps, res: NextApiResponse) => {
|
||||||
const ip = requestIp.getClientIp(req);
|
const ip = requestIp.getClientIp(req);
|
||||||
if (!ip || process.env.USE_IP_LIMIT !== 'true') {
|
if (!ip || (process.env.USE_IP_LIMIT !== 'true' && !force)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -22,10 +22,9 @@ export function useReqFrequencyLimit(seconds: number, limit: number) {
|
|||||||
expiredTime: addSeconds(new Date(), seconds)
|
expiredTime: addSeconds(new Date(), seconds)
|
||||||
});
|
});
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
res.status(429);
|
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
code: 429,
|
code: 429,
|
||||||
message: ERROR_ENUM.tooManyRequest
|
error: ERROR_ENUM.tooManyRequest
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export const jsonRes = <T = any>(
|
|||||||
|
|
||||||
addLog.error(`Api response error: ${url}`, ERROR_RESPONSE[errResponseKey]);
|
addLog.error(`Api response error: ${url}`, ERROR_RESPONSE[errResponseKey]);
|
||||||
|
|
||||||
return res.json(ERROR_RESPONSE[errResponseKey]);
|
return res.status(code).json(ERROR_RESPONSE[errResponseKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// another error
|
// another error
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ export const initFastGPTConfig = (config?: FastGPTConfigFileType) => {
|
|||||||
global.subPlans = config.subPlans;
|
global.subPlans = config.subPlans;
|
||||||
|
|
||||||
global.llmModels = config.llmModels;
|
global.llmModels = config.llmModels;
|
||||||
|
global.llmModelPriceType = global.llmModels.some((item) => typeof item.inputPrice === 'number')
|
||||||
|
? 'IO'
|
||||||
|
: 'Tokens';
|
||||||
global.vectorModels = config.vectorModels;
|
global.vectorModels = config.vectorModels;
|
||||||
global.audioSpeechModels = config.audioSpeechModels;
|
global.audioSpeechModels = config.audioSpeechModels;
|
||||||
global.whisperModel = config.whisperModel;
|
global.whisperModel = config.whisperModel;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"model": "text-embedding-ada-002",
|
||||||
|
"name": "text-embedding-ada-002",
|
||||||
|
|
||||||
|
"defaultToken": 512, // 默认分块 token
|
||||||
|
"maxToken": 3000, // 最大分块 token
|
||||||
|
"weight": 0, // 权重
|
||||||
|
|
||||||
|
"charsPointsPrice": 0 // 积分/1k token
|
||||||
|
}
|
||||||
33
packages/service/core/ai/config/llm/gpt-4o-mini.json
Normal file
33
packages/service/core/ai/config/llm/gpt-4o-mini.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"model": "gpt-4o-mini",
|
||||||
|
"name": "GPT-4o-mini", // alias
|
||||||
|
|
||||||
|
"maxContext": 125000, // 最大上下文
|
||||||
|
"maxResponse": 16000, // 最大回复
|
||||||
|
"quoteMaxToken": 60000, // 最大引用
|
||||||
|
"maxTemperature": 1.2, // 最大温度
|
||||||
|
"presencePenaltyRange": [-2, 2], // 惩罚系数范围
|
||||||
|
"frequencyPenaltyRange": [-2, 2], // 频率惩罚系数范围
|
||||||
|
"responseFormatList": ["text", "json_object", "json_schema"], // 响应格式
|
||||||
|
"showStopSign": true, // 是否显示停止符号
|
||||||
|
|
||||||
|
"vision": true, // 是否支持图片识别
|
||||||
|
"toolChoice": true, // 是否支持工具调用
|
||||||
|
"functionCall": false, // 是否支持函数调用(一般都可以 false 了,基本不用了)
|
||||||
|
"defaultSystemChatPrompt": "", // 默认系统提示
|
||||||
|
|
||||||
|
"datasetProcess": true, // 用于知识库文本处理
|
||||||
|
"usedInClassify": true, // 用于问题分类
|
||||||
|
"customCQPrompt": "", // 自定义问题分类提示
|
||||||
|
"usedInExtractFields": true, // 用于提取字段
|
||||||
|
"customExtractPrompt": "", // 自定义提取提示
|
||||||
|
"usedInToolCall": true, // 用于工具调用
|
||||||
|
"usedInQueryExtension": true, // 用于问题优化
|
||||||
|
|
||||||
|
"defaultConfig": {}, // 额外的自定义 body
|
||||||
|
"fieldMap": {}, // body 字段映射
|
||||||
|
|
||||||
|
"censor": false, // 是否开启敏感词过滤
|
||||||
|
"charsPointsPrice": 0 // n 积分/1k token
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"provider": "BAAI",
|
||||||
|
"model": "bge-reranker-v2-m3",
|
||||||
|
"name": "bge-reranker-v2-m3",
|
||||||
|
"charsPointsPrice": 0
|
||||||
|
}
|
||||||
6
packages/service/core/ai/config/stt/whisper-1.json
Normal file
6
packages/service/core/ai/config/stt/whisper-1.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"model": "whisper-1",
|
||||||
|
"name": "whisper-1",
|
||||||
|
"charsPointsPrice": 0
|
||||||
|
}
|
||||||
32
packages/service/core/ai/config/tts/tts-1.json
Normal file
32
packages/service/core/ai/config/tts/tts-1.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"model": "tts-1",
|
||||||
|
"name": "TTS1",
|
||||||
|
"charsPointsPrice": 0,
|
||||||
|
"voices": [
|
||||||
|
{
|
||||||
|
"label": "Alloy",
|
||||||
|
"value": "alloy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Echo",
|
||||||
|
"value": "echo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Fable",
|
||||||
|
"value": "fable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Onyx",
|
||||||
|
"value": "onyx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Nova",
|
||||||
|
"value": "nova"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Shimmer",
|
||||||
|
"value": "shimmer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
|
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
|
||||||
import { createChatCompletion } from '../config';
|
import { createChatCompletion } from '../config';
|
||||||
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
import { countGptMessagesTokens, countPromptTokens } from '../../../common/string/tiktoken/index';
|
||||||
import { loadRequestMessages } from '../../chat/utils';
|
import { loadRequestMessages } from '../../chat/utils';
|
||||||
import { llmCompletionsBodyFormat } from '../utils';
|
import { llmCompletionsBodyFormat } from '../utils';
|
||||||
import {
|
import {
|
||||||
@@ -20,7 +20,8 @@ export async function createQuestionGuide({
|
|||||||
customPrompt?: string;
|
customPrompt?: string;
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
result: string[];
|
result: string[];
|
||||||
tokens: number;
|
inputTokens: number;
|
||||||
|
outputTokens: number;
|
||||||
}> {
|
}> {
|
||||||
const concatMessages: ChatCompletionMessageParam[] = [
|
const concatMessages: ChatCompletionMessageParam[] = [
|
||||||
...messages,
|
...messages,
|
||||||
@@ -29,6 +30,10 @@ export async function createQuestionGuide({
|
|||||||
content: `${customPrompt || PROMPT_QUESTION_GUIDE}\n${PROMPT_QUESTION_GUIDE_FOOTER}`
|
content: `${customPrompt || PROMPT_QUESTION_GUIDE}\n${PROMPT_QUESTION_GUIDE_FOOTER}`
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
const requestMessages = await loadRequestMessages({
|
||||||
|
messages: concatMessages,
|
||||||
|
useVision: false
|
||||||
|
});
|
||||||
|
|
||||||
const { response: data } = await createChatCompletion({
|
const { response: data } = await createChatCompletion({
|
||||||
body: llmCompletionsBodyFormat(
|
body: llmCompletionsBodyFormat(
|
||||||
@@ -36,10 +41,7 @@ export async function createQuestionGuide({
|
|||||||
model,
|
model,
|
||||||
temperature: 0.1,
|
temperature: 0.1,
|
||||||
max_tokens: 200,
|
max_tokens: 200,
|
||||||
messages: await loadRequestMessages({
|
messages: requestMessages,
|
||||||
messages: concatMessages,
|
|
||||||
useVision: false
|
|
||||||
}),
|
|
||||||
stream: false
|
stream: false
|
||||||
},
|
},
|
||||||
model
|
model
|
||||||
@@ -51,13 +53,15 @@ export async function createQuestionGuide({
|
|||||||
const start = answer.indexOf('[');
|
const start = answer.indexOf('[');
|
||||||
const end = answer.lastIndexOf(']');
|
const end = answer.lastIndexOf(']');
|
||||||
|
|
||||||
const tokens = await countGptMessagesTokens(concatMessages);
|
const inputTokens = await countGptMessagesTokens(requestMessages);
|
||||||
|
const outputTokens = await countPromptTokens(answer);
|
||||||
|
|
||||||
if (start === -1 || end === -1) {
|
if (start === -1 || end === -1) {
|
||||||
addLog.warn('Create question guide error', { answer });
|
addLog.warn('Create question guide error', { answer });
|
||||||
return {
|
return {
|
||||||
result: [],
|
result: [],
|
||||||
tokens: 0
|
inputTokens: 0,
|
||||||
|
outputTokens: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,14 +73,16 @@ export async function createQuestionGuide({
|
|||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
result: json5.parse(jsonStr),
|
result: json5.parse(jsonStr),
|
||||||
tokens
|
inputTokens,
|
||||||
|
outputTokens
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: [],
|
result: [],
|
||||||
tokens: 0
|
inputTokens: 0,
|
||||||
|
outputTokens: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||||
import { createChatCompletion } from '../config';
|
import { createChatCompletion } from '../config';
|
||||||
import { ChatItemType } from '@fastgpt/global/core/chat/type';
|
import { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
import { countGptMessagesTokens, countPromptTokens } from '../../../common/string/tiktoken/index';
|
||||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { getLLMModel } from '../model';
|
import { getLLMModel } from '../model';
|
||||||
import { llmCompletionsBodyFormat } from '../utils';
|
import { llmCompletionsBodyFormat } from '../utils';
|
||||||
@@ -121,7 +121,8 @@ export const queryExtension = async ({
|
|||||||
rawQuery: string;
|
rawQuery: string;
|
||||||
extensionQueries: string[];
|
extensionQueries: string[];
|
||||||
model: string;
|
model: string;
|
||||||
tokens: number;
|
inputTokens: number;
|
||||||
|
outputTokens: number;
|
||||||
}> => {
|
}> => {
|
||||||
const systemFewShot = chatBg
|
const systemFewShot = chatBg
|
||||||
? `Q: 对话背景。
|
? `Q: 对话背景。
|
||||||
@@ -166,7 +167,8 @@ A: ${chatBg}
|
|||||||
rawQuery: query,
|
rawQuery: query,
|
||||||
extensionQueries: [],
|
extensionQueries: [],
|
||||||
model,
|
model,
|
||||||
tokens: 0
|
inputTokens: 0,
|
||||||
|
outputTokens: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +183,8 @@ A: ${chatBg}
|
|||||||
rawQuery: query,
|
rawQuery: query,
|
||||||
extensionQueries: Array.isArray(queries) ? queries : [],
|
extensionQueries: Array.isArray(queries) ? queries : [],
|
||||||
model,
|
model,
|
||||||
tokens: await countGptMessagesTokens(messages)
|
inputTokens: await countGptMessagesTokens(messages),
|
||||||
|
outputTokens: await countPromptTokens(answer)
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addLog.error(`Query extension error`, error);
|
addLog.error(`Query extension error`, error);
|
||||||
@@ -189,7 +192,8 @@ A: ${chatBg}
|
|||||||
rawQuery: query,
|
rawQuery: query,
|
||||||
extensionQueries: [],
|
extensionQueries: [],
|
||||||
model,
|
model,
|
||||||
tokens: 0
|
inputTokens: 0,
|
||||||
|
outputTokens: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export const getLLMModel = (model?: string) => {
|
|||||||
global.llmModels[0]
|
global.llmModels[0]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDatasetModel = (model?: string) => {
|
export const getDatasetModel = (model?: string) => {
|
||||||
return (
|
return (
|
||||||
global.llmModels
|
global.llmModels
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||||
import { PluginRuntimeType } from '@fastgpt/global/core/plugin/type';
|
import { PluginRuntimeType } from '@fastgpt/global/core/plugin/type';
|
||||||
|
import { splitCombinePluginId } from './controller';
|
||||||
|
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Plugin points calculation:
|
Plugin points calculation:
|
||||||
1. Return 0 if error
|
1. 系统插件/商业版插件:
|
||||||
2. Add configured points if commercial plugin
|
- 有错误:返回 0
|
||||||
3. Add sum of child nodes points
|
- 无错误:返回 单次积分 + 子流程积分(可配置)
|
||||||
|
2. 个人插件
|
||||||
|
- 返回 子流程积分
|
||||||
*/
|
*/
|
||||||
export const computedPluginUsage = async ({
|
export const computedPluginUsage = async ({
|
||||||
plugin,
|
plugin,
|
||||||
@@ -16,13 +20,16 @@ export const computedPluginUsage = async ({
|
|||||||
childrenUsage: ChatNodeUsageType[];
|
childrenUsage: ChatNodeUsageType[];
|
||||||
error?: boolean;
|
error?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
if (error) {
|
const { source } = await splitCombinePluginId(plugin.id);
|
||||||
return 0;
|
const childrenUsages = childrenUsage.reduce((sum, item) => sum + (item.totalPoints || 0), 0);
|
||||||
|
|
||||||
|
if (source !== PluginSourceEnum.personal) {
|
||||||
|
if (error) return 0;
|
||||||
|
|
||||||
|
const pluginCurrentCost = plugin.currentCost ?? 0;
|
||||||
|
|
||||||
|
return plugin.hasTokenFee ? pluginCurrentCost + childrenUsages : pluginCurrentCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
const childrenIUsages = childrenUsage.reduce((sum, item) => sum + (item.totalPoints || 0), 0);
|
return childrenUsages;
|
||||||
|
|
||||||
const pluginCurrentCose = plugin.currentCost ?? 0;
|
|
||||||
|
|
||||||
return plugin.hasTokenFee ? pluginCurrentCose + childrenIUsages : pluginCurrentCose;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,24 +86,21 @@ const ChatItemSchema = new Schema({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ChatItemSchema.index({ dataId: 1 }, { background: true });
|
ChatItemSchema.index({ dataId: 1 });
|
||||||
/* delete by app;
|
/* delete by app;
|
||||||
delete by chat id;
|
delete by chat id;
|
||||||
get chat list;
|
get chat list;
|
||||||
get chat logs;
|
get chat logs;
|
||||||
close custom feedback;
|
close custom feedback;
|
||||||
*/
|
*/
|
||||||
ChatItemSchema.index({ appId: 1, chatId: 1, dataId: 1 }, { background: true });
|
ChatItemSchema.index({ appId: 1, chatId: 1, dataId: 1 });
|
||||||
// admin charts
|
// admin charts
|
||||||
ChatItemSchema.index({ time: -1, obj: 1 }, { background: true });
|
ChatItemSchema.index({ time: -1, obj: 1 });
|
||||||
// timer, clear history
|
// timer, clear history
|
||||||
ChatItemSchema.index({ teamId: 1, time: -1 }, { background: true });
|
ChatItemSchema.index({ teamId: 1, time: -1 });
|
||||||
|
|
||||||
// Admin charts
|
// Admin charts
|
||||||
ChatItemSchema.index(
|
ChatItemSchema.index({ obj: 1, time: -1 }, { partialFilterExpression: { obj: 'Human' } });
|
||||||
{ obj: 1, time: -1 },
|
|
||||||
{ background: true, partialFilterExpression: { obj: 'Human' } }
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,19 +81,19 @@ const ChatSchema = new Schema({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ChatSchema.index({ chatId: 1 }, { background: true });
|
ChatSchema.index({ chatId: 1 });
|
||||||
// get user history
|
// get user history
|
||||||
ChatSchema.index({ tmbId: 1, appId: 1, top: -1, updateTime: -1 }, { background: true });
|
ChatSchema.index({ tmbId: 1, appId: 1, top: -1, updateTime: -1 });
|
||||||
// delete by appid; clear history; init chat; update chat; auth chat; get chat;
|
// delete by appid; clear history; init chat; update chat; auth chat; get chat;
|
||||||
ChatSchema.index({ appId: 1, chatId: 1 }, { background: true });
|
ChatSchema.index({ appId: 1, chatId: 1 });
|
||||||
|
|
||||||
// get chat logs;
|
// get chat logs;
|
||||||
ChatSchema.index({ teamId: 1, appId: 1, updateTime: -1 }, { background: true });
|
ChatSchema.index({ teamId: 1, appId: 1, updateTime: -1 });
|
||||||
// get share chat history
|
// get share chat history
|
||||||
ChatSchema.index({ shareId: 1, outLinkUid: 1, updateTime: -1 }, { background: true });
|
ChatSchema.index({ shareId: 1, outLinkUid: 1, updateTime: -1 });
|
||||||
|
|
||||||
// timer, clear history
|
// timer, clear history
|
||||||
ChatSchema.index({ teamId: 1, updateTime: -1 }, { background: true });
|
ChatSchema.index({ teamId: 1, updateTime: -1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { pushDataListToTrainingQueue } from '../training/controller';
|
|||||||
import { MongoImage } from '../../../common/file/image/schema';
|
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';
|
||||||
|
|
||||||
export const createCollectionAndInsertData = async ({
|
export const createCollectionAndInsertData = async ({
|
||||||
dataset,
|
dataset,
|
||||||
@@ -240,12 +241,12 @@ export const delCollectionRelatedSource = async ({
|
|||||||
.map((item) => item?.metadata?.relatedImgId || '')
|
.map((item) => item?.metadata?.relatedImgId || '')
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
// delete files
|
// Delete files
|
||||||
await delFileByFileIdList({
|
await delFileByFileIdList({
|
||||||
bucketName: BucketNameEnum.dataset,
|
bucketName: BucketNameEnum.dataset,
|
||||||
fileIdList
|
fileIdList
|
||||||
});
|
});
|
||||||
// delete images
|
// Delete images
|
||||||
await delImgByRelatedId({
|
await delImgByRelatedId({
|
||||||
teamId,
|
teamId,
|
||||||
relateIds: relatedImageIds,
|
relateIds: relatedImageIds,
|
||||||
@@ -273,7 +274,7 @@ 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
|
// Delete training data
|
||||||
await MongoDatasetTraining.deleteMany({
|
await MongoDatasetTraining.deleteMany({
|
||||||
teamId,
|
teamId,
|
||||||
datasetIds: { $in: datasetIds },
|
datasetIds: { $in: datasetIds },
|
||||||
@@ -285,11 +286,16 @@ export async function delCollection({
|
|||||||
await delCollectionRelatedSource({ collections, session });
|
await delCollectionRelatedSource({ collections, session });
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete dataset.datas
|
// Delete dataset_datas
|
||||||
await MongoDatasetData.deleteMany(
|
await MongoDatasetData.deleteMany(
|
||||||
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
|
// Delete dataset_data_texts
|
||||||
|
await MongoDatasetDataText.deleteMany(
|
||||||
|
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
||||||
|
{ session }
|
||||||
|
);
|
||||||
|
|
||||||
// delete collections
|
// delete collections
|
||||||
await MongoDatasetCollection.deleteMany(
|
await MongoDatasetCollection.deleteMany(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ClientSession } from '../../common/mongo';
|
|||||||
import { MongoDatasetTraining } from './training/schema';
|
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';
|
||||||
|
|
||||||
/* ============= dataset ========== */
|
/* ============= dataset ========== */
|
||||||
/* find all datasetId by top datasetId */
|
/* find all datasetId by top datasetId */
|
||||||
@@ -92,7 +93,7 @@ export async function delDatasetRelevantData({
|
|||||||
{ session }
|
{ session }
|
||||||
).lean();
|
).lean();
|
||||||
|
|
||||||
// image and file
|
// Delete Image and file
|
||||||
await delCollectionRelatedSource({ collections, session });
|
await delCollectionRelatedSource({ collections, session });
|
||||||
|
|
||||||
// delete collections
|
// delete collections
|
||||||
@@ -101,9 +102,15 @@ export async function delDatasetRelevantData({
|
|||||||
datasetId: { $in: datasetIds }
|
datasetId: { $in: datasetIds }
|
||||||
}).session(session);
|
}).session(session);
|
||||||
|
|
||||||
// delete dataset.datas(Not need 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 } });
|
await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } });
|
||||||
|
|
||||||
// no session delete: delete files, vector data
|
// Delete vector data
|
||||||
await deleteDatasetDataVector({ teamId, datasetIds });
|
await deleteDatasetDataVector({ teamId, datasetIds });
|
||||||
}
|
}
|
||||||
|
|||||||
45
packages/service/core/dataset/data/dataTextSchema.ts
Normal file
45
packages/service/core/dataset/data/dataTextSchema.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||||
|
const { Schema } = connectionMongo;
|
||||||
|
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
||||||
|
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||||
|
import { DatasetCollectionName } from '../schema';
|
||||||
|
import { DatasetColCollectionName } from '../collection/schema';
|
||||||
|
import { DatasetDataCollectionName } from './schema';
|
||||||
|
|
||||||
|
export const DatasetDataTextCollectionName = 'dataset_data_texts';
|
||||||
|
|
||||||
|
const DatasetDataTextSchema = new Schema({
|
||||||
|
teamId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: TeamCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
datasetId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: DatasetCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
collectionId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: DatasetColCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
dataId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: DatasetDataCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
fullTextToken: String
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
DatasetDataTextSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
|
||||||
|
DatasetDataTextSchema.index({ dataId: 1 }, { unique: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MongoDatasetDataText = getMongoModel<DatasetDataSchemaType>(
|
||||||
|
DatasetDataTextCollectionName,
|
||||||
|
DatasetDataTextSchema
|
||||||
|
);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
|
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||||
const { Schema, model, models } = connectionMongo;
|
const { Schema, model, models } = connectionMongo;
|
||||||
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
||||||
import {
|
import {
|
||||||
@@ -39,10 +39,6 @@ const DatasetDataSchema = new Schema({
|
|||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
fullTextToken: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
indexes: {
|
indexes: {
|
||||||
type: [
|
type: [
|
||||||
{
|
{
|
||||||
@@ -71,17 +67,11 @@ const DatasetDataSchema = new Schema({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
inited: {
|
rebuilding: Boolean,
|
||||||
type: Boolean
|
|
||||||
},
|
|
||||||
rebuilding: Boolean
|
|
||||||
});
|
|
||||||
|
|
||||||
DatasetDataSchema.virtual('collection', {
|
// Abandon
|
||||||
ref: DatasetColCollectionName,
|
fullTextToken: String,
|
||||||
localField: 'collectionId',
|
initFullText: Boolean
|
||||||
foreignField: '_id',
|
|
||||||
justOne: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -93,13 +83,15 @@ try {
|
|||||||
chunkIndex: 1,
|
chunkIndex: 1,
|
||||||
updateTime: -1
|
updateTime: -1
|
||||||
});
|
});
|
||||||
// full text index
|
// FullText tmp full text index
|
||||||
DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
|
// DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
|
||||||
// Recall vectors after data matching
|
// Recall vectors after data matching
|
||||||
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
|
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
|
||||||
DatasetDataSchema.index({ updateTime: 1 });
|
DatasetDataSchema.index({ updateTime: 1 });
|
||||||
// rebuild data
|
// rebuild data
|
||||||
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
|
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
|
||||||
|
|
||||||
|
DatasetDataSchema.index({ initFullText: 1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import { getVectorsByText } from '../../ai/embedding';
|
|||||||
import { getVectorModel } from '../../ai/model';
|
import { getVectorModel } from '../../ai/model';
|
||||||
import { MongoDatasetData } from '../data/schema';
|
import { MongoDatasetData } from '../data/schema';
|
||||||
import {
|
import {
|
||||||
DatasetCollectionSchemaType,
|
|
||||||
DatasetDataSchemaType,
|
DatasetDataSchemaType,
|
||||||
|
DatasetDataTextSchemaType,
|
||||||
SearchDataResponseItemType
|
SearchDataResponseItemType
|
||||||
} from '@fastgpt/global/core/dataset/type';
|
} from '@fastgpt/global/core/dataset/type';
|
||||||
import { MongoDatasetCollection } from '../collection/schema';
|
import { MongoDatasetCollection } from '../collection/schema';
|
||||||
@@ -23,6 +23,7 @@ import { Types } from '../../../common/mongo';
|
|||||||
import json5 from 'json5';
|
import json5 from 'json5';
|
||||||
import { MongoDatasetCollectionTags } from '../tag/schema';
|
import { MongoDatasetCollectionTags } from '../tag/schema';
|
||||||
import { readFromSecondary } from '../../../common/mongo/utils';
|
import { readFromSecondary } from '../../../common/mongo/utils';
|
||||||
|
import { MongoDatasetDataText } from '../data/dataTextSchema';
|
||||||
|
|
||||||
type SearchDatasetDataProps = {
|
type SearchDatasetDataProps = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
@@ -266,57 +267,60 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
|||||||
filterCollectionIdList
|
filterCollectionIdList
|
||||||
});
|
});
|
||||||
|
|
||||||
// get q and a
|
// Get data and collections
|
||||||
const dataList = await MongoDatasetData.find(
|
const collectionIdList = Array.from(new Set(results.map((item) => item.collectionId)));
|
||||||
{
|
const [dataList, collections] = await Promise.all([
|
||||||
teamId,
|
MongoDatasetData.find(
|
||||||
datasetId: { $in: datasetIds },
|
{
|
||||||
collectionId: { $in: Array.from(new Set(results.map((item) => item.collectionId))) },
|
teamId,
|
||||||
'indexes.dataId': { $in: results.map((item) => item.id?.trim()) }
|
datasetId: { $in: datasetIds },
|
||||||
},
|
collectionId: { $in: collectionIdList },
|
||||||
'datasetId collectionId updateTime q a chunkIndex indexes'
|
'indexes.dataId': { $in: results.map((item) => item.id?.trim()) }
|
||||||
)
|
},
|
||||||
.populate<{ collection: DatasetCollectionSchemaType }>(
|
'_id datasetId collectionId updateTime q a chunkIndex indexes',
|
||||||
'collection',
|
{ ...readFromSecondary }
|
||||||
'name fileId rawLink externalFileId externalFileUrl'
|
).lean(),
|
||||||
)
|
MongoDatasetCollection.find(
|
||||||
.lean();
|
{
|
||||||
|
_id: { $in: collectionIdList }
|
||||||
|
},
|
||||||
|
'_id name fileId rawLink externalFileId externalFileUrl',
|
||||||
|
{ ...readFromSecondary }
|
||||||
|
).lean()
|
||||||
|
]);
|
||||||
|
|
||||||
// add score to data(It's already sorted. The first one is the one with the most points)
|
const formatResult = results
|
||||||
const concatResults = dataList.map((data) => {
|
.map((item, index) => {
|
||||||
const dataIdList = data.indexes.map((item) => item.dataId);
|
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
|
||||||
|
if (!collection) {
|
||||||
|
console.log('Collection is not found', item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = dataList.find((data) =>
|
||||||
|
data.indexes.some((index) => index.dataId === item.id)
|
||||||
|
);
|
||||||
|
if (!data) {
|
||||||
|
console.log('Data is not found', item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const maxScoreResult = results.find((item) => {
|
const score = item?.score || 0;
|
||||||
return dataIdList.includes(item.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
const result: SearchDataResponseItemType = {
|
||||||
...data,
|
id: String(data._id),
|
||||||
score: maxScoreResult?.score || 0
|
updateTime: data.updateTime,
|
||||||
};
|
q: data.q,
|
||||||
});
|
a: data.a,
|
||||||
|
chunkIndex: data.chunkIndex,
|
||||||
|
datasetId: String(data.datasetId),
|
||||||
|
collectionId: String(data.collectionId),
|
||||||
|
...getCollectionSourceData(collection),
|
||||||
|
score: [{ type: SearchScoreTypeEnum.embedding, value: score, index }]
|
||||||
|
};
|
||||||
|
|
||||||
concatResults.sort((a, b) => b.score - a.score);
|
return result;
|
||||||
|
})
|
||||||
const formatResult = concatResults.map((data, index) => {
|
.filter(Boolean) as SearchDataResponseItemType[];
|
||||||
if (!data.collectionId) {
|
|
||||||
console.log('Collection is not found', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: SearchDataResponseItemType = {
|
|
||||||
id: String(data._id),
|
|
||||||
updateTime: data.updateTime,
|
|
||||||
q: data.q,
|
|
||||||
a: data.a,
|
|
||||||
chunkIndex: data.chunkIndex,
|
|
||||||
datasetId: String(data.datasetId),
|
|
||||||
collectionId: String(data.collectionId),
|
|
||||||
...getCollectionSourceData(data.collection),
|
|
||||||
score: [{ type: SearchScoreTypeEnum.embedding, value: data.score, index }]
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
embeddingRecallResults: formatResult,
|
embeddingRecallResults: formatResult,
|
||||||
@@ -344,88 +348,224 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let searchResults = (
|
const searchResults = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
datasetIds.map(async (id) => {
|
datasetIds.map(async (id) => {
|
||||||
return MongoDatasetData.aggregate([
|
return MongoDatasetData.aggregate(
|
||||||
{
|
[
|
||||||
$match: {
|
{
|
||||||
teamId: new Types.ObjectId(teamId),
|
$match: {
|
||||||
datasetId: new Types.ObjectId(id),
|
teamId: new Types.ObjectId(teamId),
|
||||||
$text: { $search: jiebaSplit({ text: query }) },
|
datasetId: new Types.ObjectId(id),
|
||||||
...(filterCollectionIdList
|
$text: { $search: jiebaSplit({ text: query }) },
|
||||||
? {
|
...(filterCollectionIdList
|
||||||
collectionId: {
|
? {
|
||||||
$in: filterCollectionIdList.map((id) => new Types.ObjectId(id))
|
collectionId: {
|
||||||
|
$in: filterCollectionIdList.map((id) => new Types.ObjectId(id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
: {}),
|
||||||
: {}),
|
...(forbidCollectionIdList && forbidCollectionIdList.length > 0
|
||||||
...(forbidCollectionIdList && forbidCollectionIdList.length > 0
|
? {
|
||||||
? {
|
collectionId: {
|
||||||
collectionId: {
|
$nin: forbidCollectionIdList.map((id) => new Types.ObjectId(id))
|
||||||
$nin: forbidCollectionIdList.map((id) => new Types.ObjectId(id))
|
}
|
||||||
}
|
}
|
||||||
}
|
: {})
|
||||||
: {})
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$sort: {
|
||||||
|
score: { $meta: 'textScore' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$limit: limit
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$project: {
|
||||||
|
_id: 1,
|
||||||
|
datasetId: 1,
|
||||||
|
collectionId: 1,
|
||||||
|
updateTime: 1,
|
||||||
|
q: 1,
|
||||||
|
a: 1,
|
||||||
|
chunkIndex: 1,
|
||||||
|
score: { $meta: 'textScore' }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
{
|
{
|
||||||
$addFields: {
|
...readFromSecondary
|
||||||
score: { $meta: 'textScore' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$sort: {
|
|
||||||
score: { $meta: 'textScore' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$limit: limit
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$project: {
|
|
||||||
_id: 1,
|
|
||||||
datasetId: 1,
|
|
||||||
collectionId: 1,
|
|
||||||
updateTime: 1,
|
|
||||||
q: 1,
|
|
||||||
a: 1,
|
|
||||||
chunkIndex: 1,
|
|
||||||
score: 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
).flat() as (DatasetDataSchemaType & { score: number })[];
|
).flat() as (DatasetDataSchemaType & { score: number })[];
|
||||||
|
|
||||||
// resort
|
// Get data and collections
|
||||||
searchResults.sort((a, b) => b.score - a.score);
|
|
||||||
searchResults.slice(0, limit);
|
|
||||||
|
|
||||||
const collections = await MongoDatasetCollection.find(
|
const collections = await MongoDatasetCollection.find(
|
||||||
{
|
{
|
||||||
_id: { $in: searchResults.map((item) => item.collectionId) }
|
_id: { $in: searchResults.map((item) => item.collectionId) }
|
||||||
},
|
},
|
||||||
'_id name fileId rawLink'
|
'_id name fileId rawLink externalFileId externalFileUrl',
|
||||||
);
|
{ ...readFromSecondary }
|
||||||
|
).lean();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fullTextRecallResults: searchResults.map((item, index) => {
|
fullTextRecallResults: searchResults
|
||||||
const collection = collections.find((col) => String(col._id) === String(item.collectionId));
|
.map((data, index) => {
|
||||||
return {
|
const collection = collections.find(
|
||||||
id: String(item._id),
|
(col) => String(col._id) === String(data.collectionId)
|
||||||
datasetId: String(item.datasetId),
|
);
|
||||||
collectionId: String(item.collectionId),
|
if (!collection) {
|
||||||
updateTime: item.updateTime,
|
console.log('Collection is not found', data);
|
||||||
...getCollectionSourceData(collection),
|
return;
|
||||||
q: item.q,
|
}
|
||||||
a: item.a,
|
|
||||||
chunkIndex: item.chunkIndex,
|
return {
|
||||||
indexes: item.indexes,
|
id: String(data._id),
|
||||||
score: [{ type: SearchScoreTypeEnum.fullText, value: item.score, index }]
|
datasetId: String(data.datasetId),
|
||||||
};
|
collectionId: String(data.collectionId),
|
||||||
}),
|
updateTime: data.updateTime,
|
||||||
|
q: data.q,
|
||||||
|
a: data.a,
|
||||||
|
chunkIndex: data.chunkIndex,
|
||||||
|
indexes: data.indexes,
|
||||||
|
...getCollectionSourceData(collection),
|
||||||
|
score: [{ type: SearchScoreTypeEnum.fullText, value: data.score ?? 0, index }]
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean) as SearchDataResponseItemType[],
|
||||||
|
tokenLen: 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const fullTextRecall2 = async ({
|
||||||
|
query,
|
||||||
|
limit,
|
||||||
|
filterCollectionIdList,
|
||||||
|
forbidCollectionIdList
|
||||||
|
}: {
|
||||||
|
query: string;
|
||||||
|
limit: number;
|
||||||
|
filterCollectionIdList?: string[];
|
||||||
|
forbidCollectionIdList: string[];
|
||||||
|
}): Promise<{
|
||||||
|
fullTextRecallResults: SearchDataResponseItemType[];
|
||||||
|
tokenLen: number;
|
||||||
|
}> => {
|
||||||
|
if (limit === 0) {
|
||||||
|
return {
|
||||||
|
fullTextRecallResults: [],
|
||||||
|
tokenLen: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchResults = (
|
||||||
|
await Promise.all(
|
||||||
|
datasetIds.map(async (id) => {
|
||||||
|
return MongoDatasetDataText.aggregate(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
teamId: new Types.ObjectId(teamId),
|
||||||
|
datasetId: new Types.ObjectId(id),
|
||||||
|
$text: { $search: jiebaSplit({ text: query }) },
|
||||||
|
...(filterCollectionIdList
|
||||||
|
? {
|
||||||
|
collectionId: {
|
||||||
|
$in: filterCollectionIdList.map((id) => new Types.ObjectId(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(forbidCollectionIdList && forbidCollectionIdList.length > 0
|
||||||
|
? {
|
||||||
|
collectionId: {
|
||||||
|
$nin: forbidCollectionIdList.map((id) => new Types.ObjectId(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$sort: {
|
||||||
|
score: { $meta: 'textScore' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$limit: limit
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$project: {
|
||||||
|
_id: 1,
|
||||||
|
collectionId: 1,
|
||||||
|
dataId: 1,
|
||||||
|
score: { $meta: 'textScore' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
...readFromSecondary
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).flat() as (DatasetDataTextSchemaType & { score: number })[];
|
||||||
|
|
||||||
|
// Get data and collections
|
||||||
|
const [dataList, collections] = await Promise.all([
|
||||||
|
MongoDatasetData.find(
|
||||||
|
{
|
||||||
|
_id: { $in: searchResults.map((item) => item.dataId) }
|
||||||
|
},
|
||||||
|
'_id datasetId collectionId updateTime q a chunkIndex indexes',
|
||||||
|
{ ...readFromSecondary }
|
||||||
|
).lean(),
|
||||||
|
MongoDatasetCollection.find(
|
||||||
|
{
|
||||||
|
_id: { $in: searchResults.map((item) => item.collectionId) }
|
||||||
|
},
|
||||||
|
'_id name fileId rawLink externalFileId externalFileUrl',
|
||||||
|
{ ...readFromSecondary }
|
||||||
|
).lean()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fullTextRecallResults: searchResults
|
||||||
|
.map((item, index) => {
|
||||||
|
const collection = collections.find(
|
||||||
|
(col) => String(col._id) === String(item.collectionId)
|
||||||
|
);
|
||||||
|
if (!collection) {
|
||||||
|
console.log('Collection is not found', item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = dataList.find((data) => String(data._id) === String(item.dataId));
|
||||||
|
if (!data) {
|
||||||
|
console.log('Data is not found', item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: String(data._id),
|
||||||
|
datasetId: String(data.datasetId),
|
||||||
|
collectionId: String(data.collectionId),
|
||||||
|
updateTime: data.updateTime,
|
||||||
|
q: data.q,
|
||||||
|
a: data.a,
|
||||||
|
chunkIndex: data.chunkIndex,
|
||||||
|
indexes: data.indexes,
|
||||||
|
...getCollectionSourceData(collection),
|
||||||
|
score: [
|
||||||
|
{
|
||||||
|
type: SearchScoreTypeEnum.fullText,
|
||||||
|
value: item.score || 0,
|
||||||
|
index
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean) as SearchDataResponseItemType[],
|
||||||
tokenLen: 0
|
tokenLen: 0
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -496,7 +636,8 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
|||||||
forbidCollectionIdList,
|
forbidCollectionIdList,
|
||||||
filterCollectionIdList
|
filterCollectionIdList
|
||||||
}),
|
}),
|
||||||
fullTextRecall({
|
// FullText tmp
|
||||||
|
fullTextRecall2({
|
||||||
query,
|
query,
|
||||||
limit: fullTextLimit,
|
limit: fullTextLimit,
|
||||||
filterCollectionIdList,
|
filterCollectionIdList,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
|
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { countMessagesTokens } from '../../../../common/string/tiktoken/index';
|
import {
|
||||||
|
countGptMessagesTokens,
|
||||||
|
countPromptTokens
|
||||||
|
} from '../../../../common/string/tiktoken/index';
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { createChatCompletion } from '../../../ai/config';
|
||||||
@@ -49,7 +52,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
|||||||
|
|
||||||
const chatHistories = getHistories(history, histories);
|
const chatHistories = getHistories(history, histories);
|
||||||
|
|
||||||
const { arg, tokens } = await completions({
|
const { arg, inputTokens, outputTokens } = await completions({
|
||||||
...props,
|
...props,
|
||||||
histories: chatHistories,
|
histories: chatHistories,
|
||||||
cqModel
|
cqModel
|
||||||
@@ -59,7 +62,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
|||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
const { totalPoints, modelName } = formatModelChars2Points({
|
||||||
model: cqModel.model,
|
model: cqModel.model,
|
||||||
tokens,
|
inputTokens: inputTokens,
|
||||||
|
outputTokens: outputTokens,
|
||||||
modelType: ModelTypeEnum.llm
|
modelType: ModelTypeEnum.llm
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -72,7 +76,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
|||||||
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
query: userChatInput,
|
query: userChatInput,
|
||||||
tokens,
|
inputTokens: inputTokens,
|
||||||
|
outputTokens: outputTokens,
|
||||||
cqList: agents,
|
cqList: agents,
|
||||||
cqResult: result.value,
|
cqResult: result.value,
|
||||||
contextTotalLen: chatHistories.length + 2
|
contextTotalLen: chatHistories.length + 2
|
||||||
@@ -82,7 +87,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
|||||||
moduleName: name,
|
moduleName: name,
|
||||||
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens
|
inputTokens: inputTokens,
|
||||||
|
outputTokens: outputTokens
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -148,7 +154,8 @@ const completions = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tokens: await countMessagesTokens(messages),
|
inputTokens: await countGptMessagesTokens(requestMessages),
|
||||||
|
outputTokens: await countPromptTokens(answer),
|
||||||
arg: { type: id }
|
arg: { type: id }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../chat/
|
|||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import {
|
import {
|
||||||
countMessagesTokens,
|
countMessagesTokens,
|
||||||
countGptMessagesTokens
|
countGptMessagesTokens,
|
||||||
|
countPromptTokens
|
||||||
} from '../../../../common/string/tiktoken/index';
|
} from '../../../../common/string/tiktoken/index';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { createChatCompletion } from '../../../ai/config';
|
||||||
@@ -59,7 +60,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
|||||||
const extractModel = getLLMModel(model);
|
const extractModel = getLLMModel(model);
|
||||||
const chatHistories = getHistories(history, histories);
|
const chatHistories = getHistories(history, histories);
|
||||||
|
|
||||||
const { arg, tokens } = await (async () => {
|
const { arg, inputTokens, outputTokens } = await (async () => {
|
||||||
if (extractModel.toolChoice) {
|
if (extractModel.toolChoice) {
|
||||||
return toolChoice({
|
return toolChoice({
|
||||||
...props,
|
...props,
|
||||||
@@ -114,7 +115,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
|||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
const { totalPoints, modelName } = formatModelChars2Points({
|
||||||
model: extractModel.model,
|
model: extractModel.model,
|
||||||
tokens,
|
inputTokens: inputTokens,
|
||||||
|
outputTokens: outputTokens,
|
||||||
modelType: ModelTypeEnum.llm
|
modelType: ModelTypeEnum.llm
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -126,7 +128,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
|||||||
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
query: content,
|
query: content,
|
||||||
tokens,
|
inputTokens,
|
||||||
|
outputTokens,
|
||||||
extractDescription: description,
|
extractDescription: description,
|
||||||
extractResult: arg,
|
extractResult: arg,
|
||||||
contextTotalLen: chatHistories.length + 2
|
contextTotalLen: chatHistories.length + 2
|
||||||
@@ -136,7 +139,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
|||||||
moduleName: name,
|
moduleName: name,
|
||||||
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens
|
inputTokens,
|
||||||
|
outputTokens
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -249,15 +253,18 @@ const toolChoice = async (props: ActionProps) => {
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const completeMessages: ChatCompletionMessageParam[] = [
|
const AIMessages: ChatCompletionMessageParam[] = [
|
||||||
...filterMessages,
|
|
||||||
{
|
{
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
tool_calls: response.choices?.[0]?.message?.tool_calls
|
tool_calls: response.choices?.[0]?.message?.tool_calls
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const inputTokens = await countGptMessagesTokens(filterMessages, tools);
|
||||||
|
const outputTokens = await countGptMessagesTokens(AIMessages);
|
||||||
return {
|
return {
|
||||||
tokens: await countGptMessagesTokens(completeMessages, tools),
|
inputTokens,
|
||||||
|
outputTokens,
|
||||||
arg
|
arg
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -286,17 +293,21 @@ const functionCall = async (props: ActionProps) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || '');
|
const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || '');
|
||||||
const completeMessages: ChatCompletionMessageParam[] = [
|
|
||||||
...filterMessages,
|
const AIMessages: ChatCompletionMessageParam[] = [
|
||||||
{
|
{
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
function_call: response.choices?.[0]?.message?.function_call
|
function_call: response.choices?.[0]?.message?.function_call
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const inputTokens = await countGptMessagesTokens(filterMessages, undefined, functions);
|
||||||
|
const outputTokens = await countGptMessagesTokens(AIMessages);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
arg,
|
arg,
|
||||||
tokens: await countGptMessagesTokens(completeMessages, undefined, functions)
|
inputTokens,
|
||||||
|
outputTokens
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(response.choices?.[0]?.message);
|
console.log(response.choices?.[0]?.message);
|
||||||
@@ -305,7 +316,8 @@ const functionCall = async (props: ActionProps) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
arg: {},
|
arg: {},
|
||||||
tokens: 0
|
inputTokens: 0,
|
||||||
|
outputTokens: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -370,7 +382,8 @@ Human: ${content}`
|
|||||||
if (!jsonStr) {
|
if (!jsonStr) {
|
||||||
return {
|
return {
|
||||||
rawResponse: answer,
|
rawResponse: answer,
|
||||||
tokens: await countMessagesTokens(messages),
|
inputTokens: await countMessagesTokens(messages),
|
||||||
|
outputTokens: await countPromptTokens(answer),
|
||||||
arg: {}
|
arg: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -378,7 +391,8 @@ Human: ${content}`
|
|||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
rawResponse: answer,
|
rawResponse: answer,
|
||||||
tokens: await countMessagesTokens(messages),
|
inputTokens: await countMessagesTokens(messages),
|
||||||
|
outputTokens: await countPromptTokens(answer),
|
||||||
arg: json5.parse(jsonStr) as Record<string, any>
|
arg: json5.parse(jsonStr) as Record<string, any>
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -386,7 +400,8 @@ Human: ${content}`
|
|||||||
console.log(error);
|
console.log(error);
|
||||||
return {
|
return {
|
||||||
rawResponse: answer,
|
rawResponse: answer,
|
||||||
tokens: await countMessagesTokens(messages),
|
inputTokens: await countMessagesTokens(messages),
|
||||||
|
outputTokens: await countPromptTokens(answer),
|
||||||
arg: {}
|
arg: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,8 @@ export const runToolWithFunctionCall = async (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse: [toolRunResponse],
|
dispatchFlowResponse: [toolRunResponse],
|
||||||
toolNodeTokens: 0,
|
toolNodeInputTokens: 0,
|
||||||
|
toolNodeOutputTokens: 0,
|
||||||
completeMessages: requestMessages,
|
completeMessages: requestMessages,
|
||||||
assistantResponses: toolRunResponse.assistantResponses,
|
assistantResponses: toolRunResponse.assistantResponses,
|
||||||
runTimes: toolRunResponse.runTimes,
|
runTimes: toolRunResponse.runTimes,
|
||||||
@@ -126,7 +127,8 @@ export const runToolWithFunctionCall = async (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
dispatchFlowResponse: [toolRunResponse],
|
dispatchFlowResponse: [toolRunResponse],
|
||||||
toolNodeTokens: 0,
|
toolNodeInputTokens: 0,
|
||||||
|
toolNodeOutputTokens: 0,
|
||||||
assistantResponses: toolRunResponse.assistantResponses,
|
assistantResponses: toolRunResponse.assistantResponses,
|
||||||
runTimes: toolRunResponse.runTimes
|
runTimes: toolRunResponse.runTimes
|
||||||
}
|
}
|
||||||
@@ -340,7 +342,9 @@ export const runToolWithFunctionCall = async (
|
|||||||
assistantToolMsgParams
|
assistantToolMsgParams
|
||||||
] as ChatCompletionMessageParam[];
|
] as ChatCompletionMessageParam[];
|
||||||
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
|
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
|
||||||
const tokens = await countGptMessagesTokens(concatToolMessages, undefined, functions);
|
// const tokens = await countGptMessagesTokens(concatToolMessages, undefined, functions);
|
||||||
|
const inputTokens = await countGptMessagesTokens(requestMessages, undefined, functions);
|
||||||
|
const outputTokens = await countGptMessagesTokens([assistantToolMsgParams]);
|
||||||
/*
|
/*
|
||||||
...
|
...
|
||||||
user
|
user
|
||||||
@@ -375,7 +379,12 @@ export const runToolWithFunctionCall = async (
|
|||||||
const runTimes =
|
const runTimes =
|
||||||
(response?.runTimes || 0) +
|
(response?.runTimes || 0) +
|
||||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
|
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
|
||||||
const toolNodeTokens = response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens;
|
const toolNodeInputTokens = response?.toolNodeInputTokens
|
||||||
|
? response.toolNodeInputTokens + inputTokens
|
||||||
|
: inputTokens;
|
||||||
|
const toolNodeOutputTokens = response?.toolNodeOutputTokens
|
||||||
|
? response.toolNodeOutputTokens + outputTokens
|
||||||
|
: outputTokens;
|
||||||
|
|
||||||
// Check stop signal
|
// Check stop signal
|
||||||
const hasStopSignal = flatToolsResponseData.some(
|
const hasStopSignal = flatToolsResponseData.some(
|
||||||
@@ -408,7 +417,8 @@ export const runToolWithFunctionCall = async (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse,
|
dispatchFlowResponse,
|
||||||
toolNodeTokens,
|
toolNodeInputTokens,
|
||||||
|
toolNodeOutputTokens,
|
||||||
completeMessages,
|
completeMessages,
|
||||||
assistantResponses: toolNodeAssistants,
|
assistantResponses: toolNodeAssistants,
|
||||||
runTimes,
|
runTimes,
|
||||||
@@ -423,7 +433,8 @@ export const runToolWithFunctionCall = async (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
dispatchFlowResponse,
|
dispatchFlowResponse,
|
||||||
toolNodeTokens,
|
toolNodeInputTokens,
|
||||||
|
toolNodeOutputTokens,
|
||||||
assistantResponses: toolNodeAssistants,
|
assistantResponses: toolNodeAssistants,
|
||||||
runTimes
|
runTimes
|
||||||
}
|
}
|
||||||
@@ -435,7 +446,8 @@ export const runToolWithFunctionCall = async (
|
|||||||
content: answer
|
content: answer
|
||||||
};
|
};
|
||||||
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
||||||
const tokens = await countGptMessagesTokens(completeMessages, undefined, functions);
|
const inputTokens = await countGptMessagesTokens(requestMessages, undefined, functions);
|
||||||
|
const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
|
||||||
// console.log(tokens, 'response token');
|
// console.log(tokens, 'response token');
|
||||||
|
|
||||||
// concat tool assistant
|
// concat tool assistant
|
||||||
@@ -443,7 +455,12 @@ export const runToolWithFunctionCall = async (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||||
toolNodeTokens: response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens,
|
toolNodeInputTokens: response?.toolNodeInputTokens
|
||||||
|
? response.toolNodeInputTokens + inputTokens
|
||||||
|
: inputTokens,
|
||||||
|
toolNodeOutputTokens: response?.toolNodeOutputTokens
|
||||||
|
? response.toolNodeOutputTokens + outputTokens
|
||||||
|
: outputTokens,
|
||||||
completeMessages,
|
completeMessages,
|
||||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||||
runTimes: (response?.runTimes || 0) + 1
|
runTimes: (response?.runTimes || 0) + 1
|
||||||
|
|||||||
@@ -165,6 +165,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
toolWorkflowInteractiveResponse,
|
toolWorkflowInteractiveResponse,
|
||||||
dispatchFlowResponse, // tool flow response
|
dispatchFlowResponse, // tool flow response
|
||||||
toolNodeTokens,
|
toolNodeTokens,
|
||||||
|
toolNodeInputTokens,
|
||||||
|
toolNodeOutputTokens,
|
||||||
completeMessages = [], // The actual message sent to AI(just save text)
|
completeMessages = [], // The actual message sent to AI(just save text)
|
||||||
assistantResponses = [], // FastGPT system store assistant.value response
|
assistantResponses = [], // FastGPT system store assistant.value response
|
||||||
runTimes
|
runTimes
|
||||||
@@ -225,7 +227,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
const { totalPoints, modelName } = formatModelChars2Points({
|
||||||
model,
|
model,
|
||||||
tokens: toolNodeTokens,
|
inputTokens: toolNodeInputTokens,
|
||||||
|
outputTokens: toolNodeOutputTokens,
|
||||||
modelType: ModelTypeEnum.llm
|
modelType: ModelTypeEnum.llm
|
||||||
});
|
});
|
||||||
const toolAIUsage = externalProvider.openaiAccount?.key ? 0 : totalPoints;
|
const toolAIUsage = externalProvider.openaiAccount?.key ? 0 : totalPoints;
|
||||||
@@ -255,6 +258,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
// 展示的积分消耗
|
// 展示的积分消耗
|
||||||
totalPoints: totalPointsUsage,
|
totalPoints: totalPointsUsage,
|
||||||
toolCallTokens: toolNodeTokens,
|
toolCallTokens: toolNodeTokens,
|
||||||
|
toolCallInputTokens: toolNodeInputTokens,
|
||||||
|
toolCallOutputTokens: toolNodeOutputTokens,
|
||||||
childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0),
|
childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0),
|
||||||
model: modelName,
|
model: modelName,
|
||||||
query: userChatInput,
|
query: userChatInput,
|
||||||
@@ -270,9 +275,10 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
// 工具调用本身的积分消耗
|
// 工具调用本身的积分消耗
|
||||||
{
|
{
|
||||||
moduleName: name,
|
moduleName: name,
|
||||||
totalPoints: toolAIUsage,
|
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens: toolNodeTokens
|
totalPoints: toolAIUsage,
|
||||||
|
inputTokens: toolNodeInputTokens,
|
||||||
|
outputTokens: toolNodeOutputTokens
|
||||||
},
|
},
|
||||||
// 工具的消耗
|
// 工具的消耗
|
||||||
...flatUsages
|
...flatUsages
|
||||||
|
|||||||
@@ -115,7 +115,8 @@ export const runToolWithPromptCall = async (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse: [toolRunResponse],
|
dispatchFlowResponse: [toolRunResponse],
|
||||||
toolNodeTokens: 0,
|
toolNodeInputTokens: 0,
|
||||||
|
toolNodeOutputTokens: 0,
|
||||||
completeMessages: concatMessages,
|
completeMessages: concatMessages,
|
||||||
assistantResponses: toolRunResponse.assistantResponses,
|
assistantResponses: toolRunResponse.assistantResponses,
|
||||||
runTimes: toolRunResponse.runTimes,
|
runTimes: toolRunResponse.runTimes,
|
||||||
@@ -131,7 +132,8 @@ export const runToolWithPromptCall = async (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
dispatchFlowResponse: [toolRunResponse],
|
dispatchFlowResponse: [toolRunResponse],
|
||||||
toolNodeTokens: 0,
|
toolNodeInputTokens: 0,
|
||||||
|
toolNodeOutputTokens: 0,
|
||||||
assistantResponses: toolRunResponse.assistantResponses,
|
assistantResponses: toolRunResponse.assistantResponses,
|
||||||
runTimes: toolRunResponse.runTimes
|
runTimes: toolRunResponse.runTimes
|
||||||
}
|
}
|
||||||
@@ -286,15 +288,20 @@ export const runToolWithPromptCall = async (
|
|||||||
content: replaceAnswer
|
content: replaceAnswer
|
||||||
};
|
};
|
||||||
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
||||||
const tokens = await countGptMessagesTokens(completeMessages, undefined);
|
const inputTokens = await countGptMessagesTokens(requestMessages);
|
||||||
// console.log(tokens, 'response token');
|
const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
|
||||||
|
|
||||||
// concat tool assistant
|
// concat tool assistant
|
||||||
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||||
toolNodeTokens: response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens,
|
toolNodeInputTokens: response?.toolNodeInputTokens
|
||||||
|
? response.toolNodeInputTokens + inputTokens
|
||||||
|
: inputTokens,
|
||||||
|
toolNodeOutputTokens: response?.toolNodeOutputTokens
|
||||||
|
? response.toolNodeOutputTokens + outputTokens
|
||||||
|
: outputTokens,
|
||||||
completeMessages,
|
completeMessages,
|
||||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||||
runTimes: (response?.runTimes || 0) + 1
|
runTimes: (response?.runTimes || 0) + 1
|
||||||
@@ -366,17 +373,9 @@ export const runToolWithPromptCall = async (
|
|||||||
function_call: toolJson
|
function_call: toolJson
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
...
|
|
||||||
user
|
|
||||||
assistant: tool data
|
|
||||||
*/
|
|
||||||
const concatToolMessages = [
|
|
||||||
...requestMessages,
|
|
||||||
assistantToolMsgParams
|
|
||||||
] as ChatCompletionMessageParam[];
|
|
||||||
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
|
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
|
||||||
const tokens = await countGptMessagesTokens(concatToolMessages, undefined);
|
const inputTokens = await countGptMessagesTokens(requestMessages);
|
||||||
|
const outputTokens = await countGptMessagesTokens([assistantToolMsgParams]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
...
|
...
|
||||||
@@ -437,7 +436,12 @@ ANSWER: `;
|
|||||||
}
|
}
|
||||||
|
|
||||||
const runTimes = (response?.runTimes || 0) + toolsRunResponse.toolResponse.runTimes;
|
const runTimes = (response?.runTimes || 0) + toolsRunResponse.toolResponse.runTimes;
|
||||||
const toolNodeTokens = response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens;
|
const toolNodeInputTokens = response?.toolNodeInputTokens
|
||||||
|
? response.toolNodeInputTokens + inputTokens
|
||||||
|
: inputTokens;
|
||||||
|
const toolNodeOutputTokens = response?.toolNodeOutputTokens
|
||||||
|
? response.toolNodeOutputTokens + outputTokens
|
||||||
|
: outputTokens;
|
||||||
|
|
||||||
// Check stop signal
|
// Check stop signal
|
||||||
const hasStopSignal = toolsRunResponse.toolResponse.flowResponses.some((item) => !!item.toolStop);
|
const hasStopSignal = toolsRunResponse.toolResponse.flowResponses.some((item) => !!item.toolStop);
|
||||||
@@ -460,7 +464,8 @@ ANSWER: `;
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse,
|
dispatchFlowResponse,
|
||||||
toolNodeTokens,
|
toolNodeInputTokens,
|
||||||
|
toolNodeOutputTokens,
|
||||||
completeMessages: filterMessages,
|
completeMessages: filterMessages,
|
||||||
assistantResponses: toolNodeAssistants,
|
assistantResponses: toolNodeAssistants,
|
||||||
runTimes,
|
runTimes,
|
||||||
@@ -475,7 +480,8 @@ ANSWER: `;
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
dispatchFlowResponse,
|
dispatchFlowResponse,
|
||||||
toolNodeTokens,
|
toolNodeInputTokens,
|
||||||
|
toolNodeOutputTokens,
|
||||||
assistantResponses: toolNodeAssistants,
|
assistantResponses: toolNodeAssistants,
|
||||||
runTimes
|
runTimes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools
|
|||||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
|
|
||||||
type ToolRunResponseType = {
|
type ToolRunResponseType = {
|
||||||
toolRunResponse: DispatchFlowResponse;
|
toolRunResponse?: DispatchFlowResponse;
|
||||||
toolMsgParams: ChatCompletionToolMessageParam;
|
toolMsgParams: ChatCompletionToolMessageParam;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
@@ -158,7 +159,8 @@ export const runToolWithToolChoice = async (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse: [toolRunResponse],
|
dispatchFlowResponse: [toolRunResponse],
|
||||||
toolNodeTokens: 0,
|
toolNodeInputTokens: 0,
|
||||||
|
toolNodeOutputTokens: 0,
|
||||||
completeMessages: requestMessages,
|
completeMessages: requestMessages,
|
||||||
assistantResponses: toolRunResponse.assistantResponses,
|
assistantResponses: toolRunResponse.assistantResponses,
|
||||||
runTimes: toolRunResponse.runTimes,
|
runTimes: toolRunResponse.runTimes,
|
||||||
@@ -176,7 +178,8 @@ export const runToolWithToolChoice = async (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
dispatchFlowResponse: [toolRunResponse],
|
dispatchFlowResponse: [toolRunResponse],
|
||||||
toolNodeTokens: 0,
|
toolNodeInputTokens: 0,
|
||||||
|
toolNodeOutputTokens: 0,
|
||||||
assistantResponses: toolRunResponse.assistantResponses,
|
assistantResponses: toolRunResponse.assistantResponses,
|
||||||
runTimes: toolRunResponse.runTimes
|
runTimes: toolRunResponse.runTimes
|
||||||
}
|
}
|
||||||
@@ -342,59 +345,87 @@ export const runToolWithToolChoice = async (
|
|||||||
return Promise.reject(getEmptyResponseTip());
|
return Promise.reject(getEmptyResponseTip());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the selected tool by LLM.
|
/* Run the selected tool by LLM.
|
||||||
const toolsRunResponse = (
|
Since only reference parameters are passed, if the same tool is run in parallel, it will get the same run parameters
|
||||||
await Promise.all(
|
*/
|
||||||
toolCalls.map(async (tool) => {
|
const toolsRunResponse: ToolRunResponseType = [];
|
||||||
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
for await (const tool of toolCalls) {
|
||||||
|
try {
|
||||||
|
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
||||||
|
|
||||||
if (!toolNode) return;
|
if (!toolNode) continue;
|
||||||
|
|
||||||
const startParams = (() => {
|
const startParams = (() => {
|
||||||
try {
|
try {
|
||||||
return json5.parse(tool.function.arguments);
|
return json5.parse(tool.function.arguments);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
||||||
|
const toolRunResponse = await dispatchWorkFlow({
|
||||||
|
...workflowProps,
|
||||||
|
isToolCall: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||||
|
|
||||||
|
const toolMsgParams: ChatCompletionToolMessageParam = {
|
||||||
|
tool_call_id: tool.id,
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
||||||
|
name: tool.function.name,
|
||||||
|
content: stringToolResponse
|
||||||
|
};
|
||||||
|
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
event: SseResponseEventEnum.toolResponse,
|
||||||
|
data: {
|
||||||
|
tool: {
|
||||||
|
id: tool.id,
|
||||||
|
toolName: '',
|
||||||
|
toolAvatar: '',
|
||||||
|
params: '',
|
||||||
|
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
||||||
}
|
}
|
||||||
})();
|
}
|
||||||
|
});
|
||||||
|
|
||||||
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
toolsRunResponse.push({
|
||||||
const toolRunResponse = await dispatchWorkFlow({
|
toolRunResponse,
|
||||||
...workflowProps,
|
toolMsgParams
|
||||||
isToolCall: true
|
});
|
||||||
});
|
} catch (error) {
|
||||||
|
const err = getErrText(error);
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
event: SseResponseEventEnum.toolResponse,
|
||||||
|
data: {
|
||||||
|
tool: {
|
||||||
|
id: tool.id,
|
||||||
|
toolName: '',
|
||||||
|
toolAvatar: '',
|
||||||
|
params: '',
|
||||||
|
response: sliceStrStartEnd(err, 5000, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
toolsRunResponse.push({
|
||||||
|
toolRunResponse: undefined,
|
||||||
const toolMsgParams: ChatCompletionToolMessageParam = {
|
toolMsgParams: {
|
||||||
tool_call_id: tool.id,
|
tool_call_id: tool.id,
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
||||||
name: tool.function.name,
|
name: tool.function.name,
|
||||||
content: stringToolResponse
|
content: sliceStrStartEnd(err, 5000, 5000)
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
workflowStreamResponse?.({
|
const flatToolsResponseData = toolsRunResponse
|
||||||
event: SseResponseEventEnum.toolResponse,
|
.map((item) => item.toolRunResponse)
|
||||||
data: {
|
.flat()
|
||||||
tool: {
|
.filter(Boolean) as DispatchFlowResponse[];
|
||||||
id: tool.id,
|
|
||||||
toolName: '',
|
|
||||||
toolAvatar: '',
|
|
||||||
params: '',
|
|
||||||
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
toolRunResponse,
|
|
||||||
toolMsgParams
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).filter(Boolean) as ToolRunResponseType;
|
|
||||||
|
|
||||||
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
|
|
||||||
// concat tool responses
|
// concat tool responses
|
||||||
const dispatchFlowResponse = response
|
const dispatchFlowResponse = response
|
||||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
||||||
@@ -428,24 +459,26 @@ export const runToolWithToolChoice = async (
|
|||||||
] as ChatCompletionMessageParam[];
|
] as ChatCompletionMessageParam[];
|
||||||
|
|
||||||
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
|
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
|
||||||
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
|
const inputTokens = await countGptMessagesTokens(requestMessages, tools);
|
||||||
|
const outputTokens = await countGptMessagesTokens(assistantToolMsgParams);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
...
|
...
|
||||||
user
|
user
|
||||||
assistant: tool data
|
assistant: tool data
|
||||||
tool: tool response
|
tool: tool response
|
||||||
*/
|
*/
|
||||||
const completeMessages = [
|
const completeMessages = [
|
||||||
...concatToolMessages,
|
...concatToolMessages,
|
||||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get tool node assistant response
|
Get tool node assistant response
|
||||||
history assistant
|
history assistant
|
||||||
current tool assistant
|
current tool assistant
|
||||||
tool child assistant
|
tool child assistant
|
||||||
*/
|
*/
|
||||||
const toolNodeAssistant = GPTMessages2Chats([
|
const toolNodeAssistant = GPTMessages2Chats([
|
||||||
...assistantToolMsgParams,
|
...assistantToolMsgParams,
|
||||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||||
@@ -463,7 +496,10 @@ export const runToolWithToolChoice = async (
|
|||||||
const runTimes =
|
const runTimes =
|
||||||
(response?.runTimes || 0) +
|
(response?.runTimes || 0) +
|
||||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
|
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
|
||||||
const toolNodeTokens = response ? response.toolNodeTokens + tokens : tokens;
|
const toolNodeInputTokens = response ? response.toolNodeInputTokens + inputTokens : inputTokens;
|
||||||
|
const toolNodeOutputTokens = response
|
||||||
|
? response.toolNodeOutputTokens + outputTokens
|
||||||
|
: outputTokens;
|
||||||
|
|
||||||
// Check stop signal
|
// Check stop signal
|
||||||
const hasStopSignal = flatToolsResponseData.some(
|
const hasStopSignal = flatToolsResponseData.some(
|
||||||
@@ -471,12 +507,12 @@ export const runToolWithToolChoice = async (
|
|||||||
);
|
);
|
||||||
// Check interactive response(Only 1 interaction is reserved)
|
// Check interactive response(Only 1 interaction is reserved)
|
||||||
const workflowInteractiveResponseItem = toolsRunResponse.find(
|
const workflowInteractiveResponseItem = toolsRunResponse.find(
|
||||||
(item) => item.toolRunResponse.workflowInteractiveResponse
|
(item) => item.toolRunResponse?.workflowInteractiveResponse
|
||||||
);
|
);
|
||||||
if (hasStopSignal || workflowInteractiveResponseItem) {
|
if (hasStopSignal || workflowInteractiveResponseItem) {
|
||||||
// Get interactive tool data
|
// Get interactive tool data
|
||||||
const workflowInteractiveResponse =
|
const workflowInteractiveResponse =
|
||||||
workflowInteractiveResponseItem?.toolRunResponse.workflowInteractiveResponse;
|
workflowInteractiveResponseItem?.toolRunResponse?.workflowInteractiveResponse;
|
||||||
|
|
||||||
// Flashback traverses completeMessages, intercepting messages that know the first user
|
// Flashback traverses completeMessages, intercepting messages that know the first user
|
||||||
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
|
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
|
||||||
@@ -496,7 +532,8 @@ export const runToolWithToolChoice = async (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse,
|
dispatchFlowResponse,
|
||||||
toolNodeTokens,
|
toolNodeInputTokens,
|
||||||
|
toolNodeOutputTokens,
|
||||||
completeMessages,
|
completeMessages,
|
||||||
assistantResponses: toolNodeAssistants,
|
assistantResponses: toolNodeAssistants,
|
||||||
runTimes,
|
runTimes,
|
||||||
@@ -512,7 +549,8 @@ export const runToolWithToolChoice = async (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
dispatchFlowResponse,
|
dispatchFlowResponse,
|
||||||
toolNodeTokens,
|
toolNodeInputTokens,
|
||||||
|
toolNodeOutputTokens,
|
||||||
assistantResponses: toolNodeAssistants,
|
assistantResponses: toolNodeAssistants,
|
||||||
runTimes
|
runTimes
|
||||||
}
|
}
|
||||||
@@ -524,14 +562,17 @@ export const runToolWithToolChoice = async (
|
|||||||
content: answer
|
content: answer
|
||||||
};
|
};
|
||||||
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
||||||
const tokens = await countGptMessagesTokens(completeMessages, tools);
|
const inputTokens = await countGptMessagesTokens(requestMessages, tools);
|
||||||
|
const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
|
||||||
|
|
||||||
// concat tool assistant
|
// concat tool assistant
|
||||||
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||||
toolNodeTokens: response ? response.toolNodeTokens + tokens : tokens,
|
toolNodeInputTokens: response ? response.toolNodeInputTokens + inputTokens : inputTokens,
|
||||||
|
toolNodeOutputTokens: response ? response.toolNodeOutputTokens + outputTokens : outputTokens,
|
||||||
|
|
||||||
completeMessages,
|
completeMessages,
|
||||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||||
runTimes: (response?.runTimes || 0) + 1
|
runTimes: (response?.runTimes || 0) + 1
|
||||||
@@ -578,7 +619,8 @@ async function streamResponse({
|
|||||||
text: content
|
text: content
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
} else if (responseChoice?.tool_calls?.[0]) {
|
}
|
||||||
|
if (responseChoice?.tool_calls?.[0]) {
|
||||||
const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0];
|
const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0];
|
||||||
// In a stream response, only one tool is returned at a time. If have id, description is executing a tool
|
// In a stream response, only one tool is returned at a time. If have id, description is executing a tool
|
||||||
if (toolCall.id || callingTool) {
|
if (toolCall.id || callingTool) {
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ export type DispatchToolModuleProps = ModuleDispatchProps<{
|
|||||||
|
|
||||||
export type RunToolResponse = {
|
export type RunToolResponse = {
|
||||||
dispatchFlowResponse: DispatchFlowResponse[];
|
dispatchFlowResponse: DispatchFlowResponse[];
|
||||||
toolNodeTokens: number;
|
toolNodeTokens?: number; // deprecated
|
||||||
|
toolNodeInputTokens: number;
|
||||||
|
toolNodeOutputTokens: number;
|
||||||
completeMessages?: ChatCompletionMessageParam[];
|
completeMessages?: ChatCompletionMessageParam[];
|
||||||
assistantResponses?: AIChatItemValueItemType[];
|
assistantResponses?: AIChatItemValueItemType[];
|
||||||
toolWorkflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
toolWorkflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||||
|
|||||||
@@ -5,13 +5,17 @@ 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 { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { createChatCompletion } from '../../../ai/config';
|
import { createChatCompletion } from '../../../ai/config';
|
||||||
import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d';
|
import type {
|
||||||
|
ChatCompletion,
|
||||||
|
ChatCompletionMessageParam,
|
||||||
|
StreamChatType
|
||||||
|
} from '@fastgpt/global/core/ai/type.d';
|
||||||
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
|
||||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||||
import { postTextCensor } from '../../../../common/api/requestPlusApi';
|
import { postTextCensor } from '../../../../common/api/requestPlusApi';
|
||||||
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
|
||||||
import type { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
import type { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { countMessagesTokens } from '../../../../common/string/tiktoken/index';
|
import { countGptMessagesTokens } from '../../../../common/string/tiktoken/index';
|
||||||
import {
|
import {
|
||||||
chats2GPTMessages,
|
chats2GPTMessages,
|
||||||
chatValue2RuntimePrompt,
|
chatValue2RuntimePrompt,
|
||||||
@@ -214,16 +218,23 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
return Promise.reject(getEmptyResponseTip());
|
return Promise.reject(getEmptyResponseTip());
|
||||||
}
|
}
|
||||||
|
|
||||||
const completeMessages = requestMessages.concat({
|
const AIMessages: ChatCompletionMessageParam[] = [
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
{
|
||||||
content: answerText
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
});
|
content: answerText
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const completeMessages = [...requestMessages, ...AIMessages];
|
||||||
const chatCompleteMessages = GPTMessages2Chats(completeMessages);
|
const chatCompleteMessages = GPTMessages2Chats(completeMessages);
|
||||||
|
|
||||||
const tokens = await countMessagesTokens(chatCompleteMessages);
|
const inputTokens = await countGptMessagesTokens(requestMessages);
|
||||||
|
const outputTokens = await countGptMessagesTokens(AIMessages);
|
||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
const { totalPoints, modelName } = formatModelChars2Points({
|
||||||
model,
|
model,
|
||||||
tokens,
|
inputTokens,
|
||||||
|
outputTokens,
|
||||||
modelType: ModelTypeEnum.llm
|
modelType: ModelTypeEnum.llm
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -232,7 +243,9 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens,
|
tokens: inputTokens + outputTokens,
|
||||||
|
inputTokens: inputTokens,
|
||||||
|
outputTokens: outputTokens,
|
||||||
query: `${userChatInput}`,
|
query: `${userChatInput}`,
|
||||||
maxToken: max_tokens,
|
maxToken: max_tokens,
|
||||||
historyPreview: getHistoryPreview(
|
historyPreview: getHistoryPreview(
|
||||||
@@ -247,7 +260,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
|||||||
moduleName: name,
|
moduleName: name,
|
||||||
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens
|
inputTokens: inputTokens,
|
||||||
|
outputTokens: outputTokens
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: answerText,
|
[DispatchNodeResponseKeyEnum.toolResponses]: answerText,
|
||||||
|
|||||||
@@ -120,14 +120,14 @@ export async function dispatchDatasetSearch(
|
|||||||
// vector
|
// vector
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
const { totalPoints, modelName } = formatModelChars2Points({
|
||||||
model: vectorModel.model,
|
model: vectorModel.model,
|
||||||
tokens,
|
inputTokens: tokens,
|
||||||
modelType: ModelTypeEnum.vector
|
modelType: ModelTypeEnum.vector
|
||||||
});
|
});
|
||||||
const responseData: DispatchNodeResponseType & { totalPoints: number } = {
|
const responseData: DispatchNodeResponseType & { totalPoints: number } = {
|
||||||
totalPoints,
|
totalPoints,
|
||||||
query: concatQueries.join('\n'),
|
query: concatQueries.join('\n'),
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens,
|
inputTokens: tokens,
|
||||||
similarity: usingSimilarityFilter ? similarity : undefined,
|
similarity: usingSimilarityFilter ? similarity : undefined,
|
||||||
limit,
|
limit,
|
||||||
searchMode,
|
searchMode,
|
||||||
@@ -139,19 +139,21 @@ export async function dispatchDatasetSearch(
|
|||||||
totalPoints,
|
totalPoints,
|
||||||
moduleName: node.name,
|
moduleName: node.name,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens
|
inputTokens: tokens
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (aiExtensionResult) {
|
if (aiExtensionResult) {
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
const { totalPoints, modelName } = formatModelChars2Points({
|
||||||
model: aiExtensionResult.model,
|
model: aiExtensionResult.model,
|
||||||
tokens: aiExtensionResult.tokens,
|
inputTokens: aiExtensionResult.inputTokens,
|
||||||
|
outputTokens: aiExtensionResult.outputTokens,
|
||||||
modelType: ModelTypeEnum.llm
|
modelType: ModelTypeEnum.llm
|
||||||
});
|
});
|
||||||
|
|
||||||
responseData.totalPoints += totalPoints;
|
responseData.totalPoints += totalPoints;
|
||||||
responseData.tokens = aiExtensionResult.tokens;
|
responseData.inputTokens = aiExtensionResult.inputTokens;
|
||||||
|
responseData.outputTokens = aiExtensionResult.outputTokens;
|
||||||
responseData.extensionModel = modelName;
|
responseData.extensionModel = modelName;
|
||||||
responseData.extensionResult =
|
responseData.extensionResult =
|
||||||
aiExtensionResult.extensionQueries?.join('\n') ||
|
aiExtensionResult.extensionQueries?.join('\n') ||
|
||||||
@@ -161,7 +163,8 @@ export async function dispatchDatasetSearch(
|
|||||||
totalPoints,
|
totalPoints,
|
||||||
moduleName: 'core.module.template.Query extension',
|
moduleName: 'core.module.template.Query extension',
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens: aiExtensionResult.tokens
|
inputTokens: aiExtensionResult.inputTokens,
|
||||||
|
outputTokens: aiExtensionResult.outputTokens
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ import { dispatchLoopEnd } from './loop/runLoopEnd';
|
|||||||
import { dispatchLoopStart } from './loop/runLoopStart';
|
import { dispatchLoopStart } from './loop/runLoopStart';
|
||||||
import { dispatchFormInput } from './interactive/formInput';
|
import { dispatchFormInput } from './interactive/formInput';
|
||||||
import { dispatchToolParams } from './agent/runTool/toolParams';
|
import { dispatchToolParams } from './agent/runTool/toolParams';
|
||||||
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
|
|
||||||
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||||
@@ -231,9 +232,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
|
|
||||||
if (toolResponses !== undefined) {
|
if (toolResponses !== undefined) {
|
||||||
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
||||||
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) {
|
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
toolRunResponse = toolResponses;
|
toolRunResponse = toolResponses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,6 +564,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
|
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
|
||||||
const skipHandleIds = targetEdges.map((item) => item.sourceHandle);
|
const skipHandleIds = targetEdges.map((item) => item.sourceHandle);
|
||||||
|
|
||||||
|
toolRunResponse = getErrText(error);
|
||||||
|
|
||||||
// Skip all edges and return error
|
// Skip all edges and return error
|
||||||
return {
|
return {
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
|
|||||||
@@ -130,8 +130,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
||||||
{
|
{
|
||||||
moduleName: plugin.name,
|
moduleName: plugin.name,
|
||||||
totalPoints: usagePoints,
|
totalPoints: usagePoints
|
||||||
tokens: 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: output?.pluginOutput
|
[DispatchNodeResponseKeyEnum.toolResponses]: output?.pluginOutput
|
||||||
|
|||||||
@@ -153,8 +153,7 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
|
|||||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
||||||
{
|
{
|
||||||
moduleName: appData.name,
|
moduleName: appData.name,
|
||||||
totalPoints: usagePoints,
|
totalPoints: usagePoints
|
||||||
tokens: 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[DispatchNodeResponseKeyEnum.toolResponses]: text,
|
[DispatchNodeResponseKeyEnum.toolResponses]: text,
|
||||||
|
|||||||
@@ -177,17 +177,17 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
|||||||
if (!httpJsonBody) return {};
|
if (!httpJsonBody) return {};
|
||||||
if (httpContentType === ContentTypes.json) {
|
if (httpContentType === ContentTypes.json) {
|
||||||
httpJsonBody = replaceStringVariables(httpJsonBody);
|
httpJsonBody = replaceStringVariables(httpJsonBody);
|
||||||
|
|
||||||
|
const replaceJsonBody = httpJsonBody.replace(/(".*?")\s*:\s*undefined\b/g, '$1: null');
|
||||||
|
|
||||||
// Json body, parse and return
|
// Json body, parse and return
|
||||||
const jsonParse = json5.parse(
|
const jsonParse = json5.parse(replaceJsonBody);
|
||||||
httpJsonBody.replace(/(".*?")\s*:\s*undefined\b/g, '$1: null')
|
|
||||||
);
|
|
||||||
const removeSignJson = removeUndefinedSign(jsonParse);
|
const removeSignJson = removeUndefinedSign(jsonParse);
|
||||||
return removeSignJson;
|
return removeSignJson;
|
||||||
}
|
}
|
||||||
httpJsonBody = replaceStringVariables(httpJsonBody);
|
httpJsonBody = replaceStringVariables(httpJsonBody);
|
||||||
return httpJsonBody.replaceAll(UNDEFINED_SIGN, 'null');
|
return httpJsonBody.replaceAll(UNDEFINED_SIGN, 'null');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return Promise.reject(`Invalid JSON body: ${httpJsonBody}`);
|
return Promise.reject(`Invalid JSON body: ${httpJsonBody}`);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const dispatchQueryExtension = async ({
|
|||||||
const queryExtensionModel = getLLMModel(model);
|
const queryExtensionModel = getLLMModel(model);
|
||||||
const chatHistories = getHistories(history, histories);
|
const chatHistories = getHistories(history, histories);
|
||||||
|
|
||||||
const { extensionQueries, tokens } = await queryExtension({
|
const { extensionQueries, inputTokens, outputTokens } = await queryExtension({
|
||||||
chatBg: systemPrompt,
|
chatBg: systemPrompt,
|
||||||
query: userChatInput,
|
query: userChatInput,
|
||||||
histories: chatHistories,
|
histories: chatHistories,
|
||||||
@@ -42,7 +42,8 @@ export const dispatchQueryExtension = async ({
|
|||||||
|
|
||||||
const { totalPoints, modelName } = formatModelChars2Points({
|
const { totalPoints, modelName } = formatModelChars2Points({
|
||||||
model: queryExtensionModel.model,
|
model: queryExtensionModel.model,
|
||||||
tokens,
|
inputTokens,
|
||||||
|
outputTokens,
|
||||||
modelType: ModelTypeEnum.llm
|
modelType: ModelTypeEnum.llm
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -59,7 +60,8 @@ export const dispatchQueryExtension = async ({
|
|||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
totalPoints,
|
totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens,
|
inputTokens,
|
||||||
|
outputTokens,
|
||||||
query: userChatInput,
|
query: userChatInput,
|
||||||
textOutput: JSON.stringify(filterSameQueries)
|
textOutput: JSON.stringify(filterSameQueries)
|
||||||
},
|
},
|
||||||
@@ -68,7 +70,8 @@ export const dispatchQueryExtension = async ({
|
|||||||
moduleName: node.name,
|
moduleName: node.name,
|
||||||
totalPoints,
|
totalPoints,
|
||||||
model: modelName,
|
model: modelName,
|
||||||
tokens
|
inputTokens,
|
||||||
|
outputTokens
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[NodeOutputKeyEnum.text]: JSON.stringify(filterSameQueries)
|
[NodeOutputKeyEnum.text]: JSON.stringify(filterSameQueries)
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ OutLinkSchema.virtual('associatedApp', {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
OutLinkSchema.index({ shareId: -1 });
|
OutLinkSchema.index({ shareId: -1 });
|
||||||
|
OutLinkSchema.index({ teamId: 1, tmbId: 1, appId: 1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
43
packages/service/support/permission/auth/org.ts
Normal file
43
packages/service/support/permission/auth/org.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||||
|
import { AuthModeType, AuthResponseType } from '../type';
|
||||||
|
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||||
|
import { authUserPer } from '../user/auth';
|
||||||
|
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Team manager can control org
|
||||||
|
*/
|
||||||
|
export const authOrgMember = async ({
|
||||||
|
orgIds,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
orgIds: string | string[];
|
||||||
|
} & AuthModeType): Promise<AuthResponseType> => {
|
||||||
|
const result = await authUserPer({
|
||||||
|
...props,
|
||||||
|
per: ManagePermissionVal
|
||||||
|
});
|
||||||
|
const { teamId, tmbId, isRoot, tmb } = result;
|
||||||
|
|
||||||
|
if (isRoot) {
|
||||||
|
return {
|
||||||
|
teamId,
|
||||||
|
tmbId,
|
||||||
|
userId: result.userId,
|
||||||
|
appId: result.appId,
|
||||||
|
apikey: result.apikey,
|
||||||
|
isRoot,
|
||||||
|
authType: result.authType,
|
||||||
|
permission: new TeamPermission({ isOwner: true })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmb.permission.hasManagePer) {
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
permission: tmb.permission
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(TeamErrEnum.unAuthTeam);
|
||||||
|
};
|
||||||
@@ -18,7 +18,7 @@ export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
|
|||||||
])
|
])
|
||||||
.lean();
|
.lean();
|
||||||
|
|
||||||
if (!tmb) return Promise.reject(UserErrEnum.unAuthUser);
|
if (!tmb) return Promise.reject(UserErrEnum.binVisitor);
|
||||||
|
|
||||||
await checkTeamAIPoints(tmb.team._id);
|
await checkTeamAIPoints(tmb.team._id);
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ import { authOpenApiKey } from '../openapi/auth';
|
|||||||
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
|
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
|
||||||
import { MongoResourcePermission } from './schema';
|
import { MongoResourcePermission } from './schema';
|
||||||
import { ClientSession } from 'mongoose';
|
import { ClientSession } from 'mongoose';
|
||||||
import {
|
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||||
PermissionValueType,
|
|
||||||
ResourcePermissionType
|
|
||||||
} from '@fastgpt/global/support/permission/type';
|
|
||||||
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
|
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
|
||||||
import { addMinutes } from 'date-fns';
|
import { addMinutes } from 'date-fns';
|
||||||
import { getGroupsByTmbId } from './memberGroup/controllers';
|
import { getGroupsByTmbId } from './memberGroup/controllers';
|
||||||
@@ -21,6 +18,8 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
|||||||
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
|
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
|
||||||
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
||||||
import { UserModelSchema } from '@fastgpt/global/support/user/type';
|
import { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||||
|
import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||||
|
import { getOrgIdSetWithParentByTmbId } from './org/controllers';
|
||||||
|
|
||||||
/** get resource permission for a team member
|
/** get resource permission for a team member
|
||||||
* If there is no permission for the team member, it will return undefined
|
* If there is no permission for the team member, it will return undefined
|
||||||
@@ -67,67 +66,44 @@ export const getResourcePermission = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If there is no personal permission, get the group permission
|
// If there is no personal permission, get the group permission
|
||||||
const groupIdList = (await getGroupsByTmbId({ tmbId, teamId })).map((item) => item._id);
|
const [groupPers, orgPers] = await Promise.all([
|
||||||
|
getGroupsByTmbId({ tmbId, teamId })
|
||||||
|
.then((res) => res.map((item) => item._id))
|
||||||
|
.then((groupIdList) =>
|
||||||
|
MongoResourcePermission.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
resourceType,
|
||||||
|
groupId: {
|
||||||
|
$in: groupIdList
|
||||||
|
},
|
||||||
|
resourceId
|
||||||
|
},
|
||||||
|
'permission'
|
||||||
|
).lean()
|
||||||
|
)
|
||||||
|
.then((perList) => perList.map((item) => item.permission)),
|
||||||
|
getOrgIdSetWithParentByTmbId({ tmbId, teamId })
|
||||||
|
.then((item) => Array.from(item))
|
||||||
|
.then((orgIds) =>
|
||||||
|
MongoResourcePermission.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
resourceType,
|
||||||
|
orgId: {
|
||||||
|
$in: Array.from(orgIds)
|
||||||
|
},
|
||||||
|
resourceId
|
||||||
|
},
|
||||||
|
'permission'
|
||||||
|
).lean()
|
||||||
|
)
|
||||||
|
.then((perList) => perList.map((item) => item.permission))
|
||||||
|
]);
|
||||||
|
|
||||||
if (groupIdList.length === 0) {
|
return concatPer([...groupPers, ...orgPers]);
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the maximum permission of the group
|
|
||||||
const pers = (
|
|
||||||
await MongoResourcePermission.find(
|
|
||||||
{
|
|
||||||
teamId,
|
|
||||||
resourceType,
|
|
||||||
groupId: {
|
|
||||||
$in: groupIdList
|
|
||||||
},
|
|
||||||
resourceId
|
|
||||||
},
|
|
||||||
'permission'
|
|
||||||
).lean()
|
|
||||||
).map((item) => item.permission);
|
|
||||||
|
|
||||||
const groupPer = getGroupPer(pers);
|
|
||||||
|
|
||||||
return groupPer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 仅取 members 不取 groups */
|
|
||||||
export async function getResourceAllClbs({
|
|
||||||
resourceId,
|
|
||||||
teamId,
|
|
||||||
resourceType,
|
|
||||||
session
|
|
||||||
}: {
|
|
||||||
teamId: string;
|
|
||||||
session?: ClientSession;
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
resourceType: 'team';
|
|
||||||
resourceId?: undefined;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
resourceType: Omit<PerResourceTypeEnum, 'team'>;
|
|
||||||
resourceId?: string | null;
|
|
||||||
}
|
|
||||||
)): Promise<ResourcePermissionType[]> {
|
|
||||||
return MongoResourcePermission.find(
|
|
||||||
{
|
|
||||||
resourceType: resourceType,
|
|
||||||
teamId: teamId,
|
|
||||||
resourceId,
|
|
||||||
groupId: {
|
|
||||||
$exists: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
session
|
|
||||||
}
|
|
||||||
).lean();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getResourceClbsAndGroups({
|
export async function getResourceClbsAndGroups({
|
||||||
resourceId,
|
resourceId,
|
||||||
resourceType,
|
resourceType,
|
||||||
@@ -155,10 +131,17 @@ export const getClbsAndGroupsWithInfo = async ({
|
|||||||
resourceType,
|
resourceType,
|
||||||
teamId
|
teamId
|
||||||
}: {
|
}: {
|
||||||
resourceId: ParentIdType;
|
|
||||||
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
|
||||||
teamId: string;
|
teamId: string;
|
||||||
}) =>
|
} & (
|
||||||
|
| {
|
||||||
|
resourceId: ParentIdType;
|
||||||
|
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
resourceType: 'team';
|
||||||
|
resourceId?: undefined;
|
||||||
|
}
|
||||||
|
)) =>
|
||||||
Promise.all([
|
Promise.all([
|
||||||
MongoResourcePermission.find({
|
MongoResourcePermission.find({
|
||||||
teamId,
|
teamId,
|
||||||
@@ -170,7 +153,7 @@ export const getClbsAndGroupsWithInfo = async ({
|
|||||||
})
|
})
|
||||||
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({
|
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({
|
||||||
path: 'tmb',
|
path: 'tmb',
|
||||||
select: 'name userId',
|
select: 'name userId role',
|
||||||
populate: {
|
populate: {
|
||||||
path: 'user',
|
path: 'user',
|
||||||
select: 'avatar'
|
select: 'avatar'
|
||||||
@@ -186,6 +169,16 @@ export const getClbsAndGroupsWithInfo = async ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.populate<{ group: MemberGroupSchemaType }>('group', 'name avatar')
|
.populate<{ group: MemberGroupSchemaType }>('group', 'name avatar')
|
||||||
|
.lean(),
|
||||||
|
MongoResourcePermission.find({
|
||||||
|
teamId,
|
||||||
|
resourceId,
|
||||||
|
resourceType,
|
||||||
|
orgId: {
|
||||||
|
$exists: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.populate<{ org: OrgSchemaType }>({ path: 'org', select: 'name avatar' })
|
||||||
.lean()
|
.lean()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -196,6 +189,7 @@ export const delResourcePermission = ({
|
|||||||
session,
|
session,
|
||||||
tmbId,
|
tmbId,
|
||||||
groupId,
|
groupId,
|
||||||
|
orgId,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
resourceType: PerResourceTypeEnum;
|
resourceType: PerResourceTypeEnum;
|
||||||
@@ -204,15 +198,18 @@ export const delResourcePermission = ({
|
|||||||
session?: ClientSession;
|
session?: ClientSession;
|
||||||
tmbId?: string;
|
tmbId?: string;
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
|
orgId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
// tmbId or groupId only one and not both
|
// either tmbId or groupId or orgId must be provided
|
||||||
if (!!tmbId === !!groupId) {
|
if (!tmbId && !groupId && !orgId) {
|
||||||
return Promise.reject(CommonErrEnum.missingParams);
|
return Promise.reject(CommonErrEnum.missingParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MongoResourcePermission.deleteOne(
|
return MongoResourcePermission.deleteOne(
|
||||||
{
|
{
|
||||||
...(tmbId ? { tmbId } : {}),
|
...(tmbId ? { tmbId } : {}),
|
||||||
...(groupId ? { groupId } : {}),
|
...(groupId ? { groupId } : {}),
|
||||||
|
...(orgId ? { orgId } : {}),
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
@@ -250,7 +247,7 @@ export function authJWT(token: string) {
|
|||||||
}>((resolve, reject) => {
|
}>((resolve, reject) => {
|
||||||
const key = process.env.TOKEN_KEY as string;
|
const key = process.env.TOKEN_KEY as string;
|
||||||
|
|
||||||
jwt.verify(token, key, function (err, decoded: any) {
|
jwt.verify(token, key, (err, decoded: any) => {
|
||||||
if (err || !decoded?.userId) {
|
if (err || !decoded?.userId) {
|
||||||
reject(ERROR_ENUM.unAuthorization);
|
reject(ERROR_ENUM.unAuthorization);
|
||||||
return;
|
return;
|
||||||
@@ -436,7 +433,7 @@ export const authFileToken = (token?: string) =>
|
|||||||
}
|
}
|
||||||
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
||||||
|
|
||||||
jwt.verify(token, key, function (err, decoded: any) {
|
jwt.verify(token, key, (err, decoded: any) => {
|
||||||
if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.fileId) {
|
if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.fileId) {
|
||||||
reject(ERROR_ENUM.unAuthFile);
|
reject(ERROR_ENUM.unAuthFile);
|
||||||
return;
|
return;
|
||||||
@@ -450,10 +447,10 @@ export const authFileToken = (token?: string) =>
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getGroupPer = (groups: PermissionValueType[] = []) => {
|
export const concatPer = (perList: PermissionValueType[] = []) => {
|
||||||
if (groups.length === 0) {
|
if (perList.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Permission().addPer(...groups).value;
|
return new Permission().addPer(...perList).value;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||||
import { MongoResourcePermission } from './schema';
|
import { MongoResourcePermission } from './schema';
|
||||||
import { ClientSession, Model } from 'mongoose';
|
import type { ClientSession, Model } from 'mongoose';
|
||||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
import type { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
import type { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||||
import { getResourceClbsAndGroups } from './controller';
|
import { getResourceClbsAndGroups } from './controller';
|
||||||
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||||
|
|
||||||
export type SyncChildrenPermissionResourceType = {
|
export type SyncChildrenPermissionResourceType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@@ -18,6 +18,7 @@ export type UpdateCollaboratorItem = {
|
|||||||
} & RequireOnlyOne<{
|
} & RequireOnlyOne<{
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
orgId: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
// sync the permission to all children folders.
|
// sync the permission to all children folders.
|
||||||
@@ -161,7 +162,7 @@ export async function resumeInheritPermission({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Delete all the collaborators and then insert the new collaborators.
|
Delete all the collaborators and then insert the new collaborators.
|
||||||
*/
|
*/
|
||||||
export async function syncCollaborators({
|
export async function syncCollaborators({
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
|
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
|
||||||
import { MongoGroupMemberModel } from './groupMemberSchema';
|
import { MongoGroupMemberModel } from './groupMemberSchema';
|
||||||
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
import { parseHeaderCert } from '../controller';
|
||||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
|
||||||
import { MongoResourcePermission } from '../schema';
|
|
||||||
import { getGroupPer, parseHeaderCert } from '../controller';
|
|
||||||
import { MongoMemberGroupModel } from './memberGroupSchema';
|
import { MongoMemberGroupModel } from './memberGroupSchema';
|
||||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
import { ClientSession } from 'mongoose';
|
import { ClientSession } from 'mongoose';
|
||||||
@@ -50,26 +47,32 @@ export const getTeamDefaultGroup = async ({
|
|||||||
export const getGroupsByTmbId = async ({
|
export const getGroupsByTmbId = async ({
|
||||||
tmbId,
|
tmbId,
|
||||||
teamId,
|
teamId,
|
||||||
role
|
role,
|
||||||
|
session
|
||||||
}: {
|
}: {
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
role?: `${GroupMemberRole}`[];
|
role?: `${GroupMemberRole}`[];
|
||||||
|
session?: ClientSession;
|
||||||
}) =>
|
}) =>
|
||||||
(
|
(
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
(
|
(
|
||||||
await MongoGroupMemberModel.find({
|
await MongoGroupMemberModel.find(
|
||||||
tmbId,
|
{
|
||||||
groupId: {
|
tmbId,
|
||||||
$exists: true
|
groupId: {
|
||||||
|
$exists: true
|
||||||
|
},
|
||||||
|
...(role ? { role: { $in: role } } : {})
|
||||||
},
|
},
|
||||||
...(role ? { role: { $in: role } } : {})
|
undefined,
|
||||||
})
|
{ session }
|
||||||
|
)
|
||||||
.populate<{ group: MemberGroupSchemaType }>('group')
|
.populate<{ group: MemberGroupSchemaType }>('group')
|
||||||
.lean()
|
.lean()
|
||||||
).map((item) => item.group),
|
).map((item) => item.group),
|
||||||
role ? [] : getTeamDefaultGroup({ teamId })
|
role ? [] : getTeamDefaultGroup({ teamId, session })
|
||||||
])
|
])
|
||||||
).flat();
|
).flat();
|
||||||
|
|
||||||
@@ -79,46 +82,6 @@ export const getGroupMembersByGroupId = async (groupId: string) => {
|
|||||||
}).lean();
|
}).lean();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get tmb's group permission: the maximum permission of the group
|
|
||||||
* @param tmbId
|
|
||||||
* @param resourceId
|
|
||||||
* @param resourceType
|
|
||||||
* @returns the maximum permission of the group
|
|
||||||
*/
|
|
||||||
export const getGroupPermission = async ({
|
|
||||||
tmbId,
|
|
||||||
resourceId,
|
|
||||||
teamId,
|
|
||||||
resourceType
|
|
||||||
}: {
|
|
||||||
tmbId: string;
|
|
||||||
teamId: string;
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
resourceId?: undefined;
|
|
||||||
resourceType: 'team';
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
resourceId: string;
|
|
||||||
resourceType: Omit<PerResourceTypeEnum, 'team'>;
|
|
||||||
}
|
|
||||||
)) => {
|
|
||||||
const groupIds = (await getGroupsByTmbId({ tmbId, teamId })).map((item) => item._id);
|
|
||||||
const groupPermissions = (
|
|
||||||
await MongoResourcePermission.find({
|
|
||||||
groupId: {
|
|
||||||
$in: groupIds
|
|
||||||
},
|
|
||||||
resourceType,
|
|
||||||
resourceId,
|
|
||||||
teamId
|
|
||||||
})
|
|
||||||
).map((item) => item.permission);
|
|
||||||
|
|
||||||
return getGroupPer(groupPermissions);
|
|
||||||
};
|
|
||||||
|
|
||||||
// auth group member role
|
// auth group member role
|
||||||
export const authGroupMemberRole = async ({
|
export const authGroupMemberRole = async ({
|
||||||
groupId,
|
groupId,
|
||||||
@@ -140,8 +103,12 @@ export const authGroupMemberRole = async ({
|
|||||||
tmbId
|
tmbId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const groupMember = await MongoGroupMemberModel.findOne({ groupId, tmbId });
|
const [groupMember, tmb] = await Promise.all([
|
||||||
const tmb = await getTmbInfoByTmbId({ tmbId });
|
MongoGroupMemberModel.findOne({ groupId, tmbId }),
|
||||||
|
getTmbInfoByTmbId({ tmbId })
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Team admin or role check
|
||||||
if (tmb.permission.hasManagePer || (groupMember && role.includes(groupMember.role))) {
|
if (tmb.permission.hasManagePer || (groupMember && role.includes(groupMember.role))) {
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
|
|||||||
95
packages/service/support/permission/org/controllers.ts
Normal file
95
packages/service/support/permission/org/controllers.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||||
|
import type { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||||
|
import type { ClientSession } from 'mongoose';
|
||||||
|
import { MongoOrgModel } from './orgSchema';
|
||||||
|
import { MongoOrgMemberModel } from './orgMemberSchema';
|
||||||
|
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
|
||||||
|
|
||||||
|
export const getOrgsByTmbId = async ({ teamId, tmbId }: { teamId: string; tmbId: string }) =>
|
||||||
|
MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean();
|
||||||
|
|
||||||
|
export const getOrgIdSetWithParentByTmbId = async ({
|
||||||
|
teamId,
|
||||||
|
tmbId
|
||||||
|
}: {
|
||||||
|
teamId: string;
|
||||||
|
tmbId: string;
|
||||||
|
}) => {
|
||||||
|
const orgMembers = await MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean();
|
||||||
|
|
||||||
|
const orgIds = Array.from(new Set(orgMembers.map((item) => String(item.orgId))));
|
||||||
|
const orgs = await MongoOrgModel.find({ _id: { $in: orgIds } }, 'path').lean();
|
||||||
|
|
||||||
|
const pathIdList = new Set<string>(
|
||||||
|
orgs
|
||||||
|
.map((org) => {
|
||||||
|
const pathIdList = org.path.split('/').filter(Boolean);
|
||||||
|
return pathIdList;
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
);
|
||||||
|
const parentOrgs = await MongoOrgModel.find(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
pathId: { $in: Array.from(pathIdList) }
|
||||||
|
},
|
||||||
|
'_id'
|
||||||
|
).lean();
|
||||||
|
const parentOrgIds = parentOrgs.map((item) => String(item._id));
|
||||||
|
|
||||||
|
return new Set([...orgIds, ...parentOrgIds]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getChildrenByOrg = async ({
|
||||||
|
org,
|
||||||
|
teamId,
|
||||||
|
session
|
||||||
|
}: {
|
||||||
|
org: OrgSchemaType;
|
||||||
|
teamId: string;
|
||||||
|
session?: ClientSession;
|
||||||
|
}) => {
|
||||||
|
return MongoOrgModel.find(
|
||||||
|
{ teamId, path: { $regex: `^${getOrgChildrenPath(org)}` } },
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
session
|
||||||
|
}
|
||||||
|
).lean();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getOrgAndChildren = async ({
|
||||||
|
orgId,
|
||||||
|
teamId,
|
||||||
|
session
|
||||||
|
}: {
|
||||||
|
orgId: string;
|
||||||
|
teamId: string;
|
||||||
|
session?: ClientSession;
|
||||||
|
}) => {
|
||||||
|
const org = await MongoOrgModel.findOne({ _id: orgId, teamId }, undefined, { session }).lean();
|
||||||
|
if (!org) {
|
||||||
|
return Promise.reject(TeamErrEnum.orgNotExist);
|
||||||
|
}
|
||||||
|
const children = await getChildrenByOrg({ org, teamId, session });
|
||||||
|
return { org, children };
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function createRootOrg({
|
||||||
|
teamId,
|
||||||
|
session
|
||||||
|
}: {
|
||||||
|
teamId: string;
|
||||||
|
session?: ClientSession;
|
||||||
|
}) {
|
||||||
|
return MongoOrgModel.create(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
name: 'ROOT',
|
||||||
|
path: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ session }
|
||||||
|
);
|
||||||
|
}
|
||||||
65
packages/service/support/permission/org/orgMemberSchema.ts
Normal file
65
packages/service/support/permission/org/orgMemberSchema.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant';
|
||||||
|
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||||
|
import {
|
||||||
|
TeamCollectionName,
|
||||||
|
TeamMemberCollectionName
|
||||||
|
} from '@fastgpt/global/support/user/team/constant';
|
||||||
|
import { OrgMemberSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||||
|
const { Schema } = connectionMongo;
|
||||||
|
|
||||||
|
export const OrgMemberCollectionName = 'team_org_members';
|
||||||
|
|
||||||
|
export const OrgMemberSchema = new Schema({
|
||||||
|
teamId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: TeamCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
orgId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: OrgCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
tmbId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: TeamMemberCollectionName,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
// role: {
|
||||||
|
// type: String,
|
||||||
|
// enum: Object.values(OrgMemberRole),
|
||||||
|
// required: true,
|
||||||
|
// default: OrgMemberRole.member
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
|
OrgMemberSchema.virtual('org', {
|
||||||
|
ref: OrgCollectionName,
|
||||||
|
localField: 'orgId',
|
||||||
|
foreignField: '_id',
|
||||||
|
justOne: true
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
OrgMemberSchema.index(
|
||||||
|
{
|
||||||
|
teamId: 1,
|
||||||
|
orgId: 1,
|
||||||
|
tmbId: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unique: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
OrgMemberSchema.index({
|
||||||
|
teamId: 1,
|
||||||
|
tmbId: 1
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MongoOrgMemberModel = getMongoModel<OrgMemberSchemaType>(
|
||||||
|
OrgMemberCollectionName,
|
||||||
|
OrgMemberSchema
|
||||||
|
);
|
||||||
77
packages/service/support/permission/org/orgSchema.ts
Normal file
77
packages/service/support/permission/org/orgSchema.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||||
|
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant';
|
||||||
|
import type { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||||
|
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||||
|
import { OrgMemberCollectionName } from './orgMemberSchema';
|
||||||
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
|
const { Schema } = connectionMongo;
|
||||||
|
|
||||||
|
export const OrgSchema = new Schema(
|
||||||
|
{
|
||||||
|
teamId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: TeamCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
pathId: {
|
||||||
|
// path id, only used for path
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: () => getNanoid()
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
required: function (this: OrgSchemaType) {
|
||||||
|
return typeof this.path !== 'string';
|
||||||
|
} // allow empty string, but not null
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
avatar: String,
|
||||||
|
description: String,
|
||||||
|
updateTime: {
|
||||||
|
type: Date,
|
||||||
|
default: () => new Date()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Auto update updateTime
|
||||||
|
timestamps: {
|
||||||
|
updatedAt: 'updateTime'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
OrgSchema.virtual('members', {
|
||||||
|
ref: OrgMemberCollectionName,
|
||||||
|
localField: '_id',
|
||||||
|
foreignField: 'orgId'
|
||||||
|
});
|
||||||
|
// OrgSchema.virtual('permission', {
|
||||||
|
// ref: ResourcePermissionCollectionName,
|
||||||
|
// localField: '_id',
|
||||||
|
// foreignField: 'orgId',
|
||||||
|
// justOne: true
|
||||||
|
// });
|
||||||
|
|
||||||
|
try {
|
||||||
|
OrgSchema.index({
|
||||||
|
teamId: 1,
|
||||||
|
path: 1
|
||||||
|
});
|
||||||
|
OrgSchema.index(
|
||||||
|
{
|
||||||
|
teamId: 1,
|
||||||
|
pathId: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unique: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MongoOrgModel = getMongoModel<OrgSchemaType>(OrgCollectionName, OrgSchema);
|
||||||
@@ -6,6 +6,7 @@ import { connectionMongo, getMongoModel } from '../../common/mongo';
|
|||||||
import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
|
import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
|
||||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||||
import { MemberGroupCollectionName } from './memberGroup/memberGroupSchema';
|
import { MemberGroupCollectionName } from './memberGroup/memberGroupSchema';
|
||||||
|
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant';
|
||||||
const { Schema } = connectionMongo;
|
const { Schema } = connectionMongo;
|
||||||
|
|
||||||
export const ResourcePermissionCollectionName = 'resource_permissions';
|
export const ResourcePermissionCollectionName = 'resource_permissions';
|
||||||
@@ -23,6 +24,10 @@ export const ResourcePermissionSchema = new Schema({
|
|||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: MemberGroupCollectionName
|
ref: MemberGroupCollectionName
|
||||||
},
|
},
|
||||||
|
orgId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: OrgCollectionName
|
||||||
|
},
|
||||||
resourceType: {
|
resourceType: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: Object.values(PerResourceTypeEnum),
|
enum: Object.values(PerResourceTypeEnum),
|
||||||
@@ -51,6 +56,12 @@ ResourcePermissionSchema.virtual('group', {
|
|||||||
foreignField: '_id',
|
foreignField: '_id',
|
||||||
justOne: true
|
justOne: true
|
||||||
});
|
});
|
||||||
|
ResourcePermissionSchema.virtual('org', {
|
||||||
|
ref: OrgCollectionName,
|
||||||
|
localField: 'orgId',
|
||||||
|
foreignField: '_id',
|
||||||
|
justOne: true
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ResourcePermissionSchema.index(
|
ResourcePermissionSchema.index(
|
||||||
@@ -70,6 +81,23 @@ try {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ResourcePermissionSchema.index(
|
||||||
|
{
|
||||||
|
resourceType: 1,
|
||||||
|
teamId: 1,
|
||||||
|
resourceId: 1,
|
||||||
|
orgId: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unique: true,
|
||||||
|
partialFilterExpression: {
|
||||||
|
orgId: {
|
||||||
|
$exists: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
ResourcePermissionSchema.index(
|
ResourcePermissionSchema.index(
|
||||||
{
|
{
|
||||||
resourceType: 1,
|
resourceType: 1,
|
||||||
@@ -87,6 +115,7 @@ try {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Delete tmb permission
|
||||||
ResourcePermissionSchema.index({
|
ResourcePermissionSchema.index({
|
||||||
resourceType: 1,
|
resourceType: 1,
|
||||||
teamId: 1,
|
teamId: 1,
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ export const checkDatasetLimit = async ({
|
|||||||
if (!standardConstants) return;
|
if (!standardConstants) return;
|
||||||
|
|
||||||
if (usedDatasetSize + insertLen >= datasetMaxSize) {
|
if (usedDatasetSize + insertLen >= datasetMaxSize) {
|
||||||
return Promise.reject(
|
return Promise.reject(TeamErrEnum.datasetSizeNotEnough);
|
||||||
`您的知识库容量为: ${datasetMaxSize}组,已使用: ${usedDatasetSize}组,导入当前文件需要: ${insertLen}组,请增加知识库容量后导入。`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usedPoints >= totalPoints) {
|
if (usedPoints >= totalPoints) {
|
||||||
|
|||||||
@@ -3,22 +3,10 @@ const { Schema } = connectionMongo;
|
|||||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||||
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||||
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
|
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
|
||||||
|
import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils';
|
||||||
|
|
||||||
export const userCollectionName = 'users';
|
export const userCollectionName = 'users';
|
||||||
|
|
||||||
const defaultAvatars = [
|
|
||||||
'/imgs/avatar/RoyalBlueAvatar.svg',
|
|
||||||
'/imgs/avatar/PurpleAvatar.svg',
|
|
||||||
'/imgs/avatar/AdoraAvatar.svg',
|
|
||||||
'/imgs/avatar/OrangeAvatar.svg',
|
|
||||||
'/imgs/avatar/RedAvatar.svg',
|
|
||||||
'/imgs/avatar/GrayModernAvatar.svg',
|
|
||||||
'/imgs/avatar/TealAvatar.svg',
|
|
||||||
'/imgs/avatar/GreenAvatar.svg',
|
|
||||||
'/imgs/avatar/BrightBlueAvatar.svg',
|
|
||||||
'/imgs/avatar/BlueAvatar.svg'
|
|
||||||
];
|
|
||||||
|
|
||||||
const UserSchema = new Schema({
|
const UserSchema = new Schema({
|
||||||
status: {
|
status: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -47,7 +35,7 @@ const UserSchema = new Schema({
|
|||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
type: String,
|
type: String,
|
||||||
default: defaultAvatars[Math.floor(Math.random() * defaultAvatars.length)]
|
default: () => getRandomUserAvatar()
|
||||||
},
|
},
|
||||||
|
|
||||||
promotionRate: {
|
promotionRate: {
|
||||||
@@ -78,11 +66,8 @@ const UserSchema = new Schema({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// login
|
|
||||||
UserSchema.index({ username: 1, password: 1 }, { background: true });
|
|
||||||
|
|
||||||
// Admin charts
|
// Admin charts
|
||||||
UserSchema.index({ createTime: -1 }, { background: true });
|
UserSchema.index({ createTime: -1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupS
|
|||||||
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, openaiBaseUrl } from '../../../core/ai/config';
|
||||||
|
import { createRootOrg } from '../../permission/org/controllers';
|
||||||
|
import { refreshSourceAvatar } from '../../../common/file/image/controller';
|
||||||
|
|
||||||
async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemType> {
|
async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemType> {
|
||||||
const tmb = await MongoTeamMember.findOne(match).populate<{ team: TeamSchema }>('team').lean();
|
const tmb = await MongoTeamMember.findOne(match).populate<{ team: TeamSchema }>('team').lean();
|
||||||
@@ -32,6 +34,7 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
|
|||||||
return {
|
return {
|
||||||
userId: String(tmb.userId),
|
userId: String(tmb.userId),
|
||||||
teamId: String(tmb.teamId),
|
teamId: String(tmb.teamId),
|
||||||
|
teamAvatar: tmb.team.avatar,
|
||||||
teamName: tmb.team.name,
|
teamName: tmb.team.name,
|
||||||
memberName: tmb.name,
|
memberName: tmb.name,
|
||||||
avatar: tmb.team.avatar,
|
avatar: tmb.team.avatar,
|
||||||
@@ -77,13 +80,11 @@ export async function createDefaultTeam({
|
|||||||
userId,
|
userId,
|
||||||
teamName = 'My Team',
|
teamName = 'My Team',
|
||||||
avatar = '/icon/logo.svg',
|
avatar = '/icon/logo.svg',
|
||||||
balance,
|
|
||||||
session
|
session
|
||||||
}: {
|
}: {
|
||||||
userId: string;
|
userId: string;
|
||||||
teamName?: string;
|
teamName?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
balance?: number;
|
|
||||||
session: ClientSession;
|
session: ClientSession;
|
||||||
}) {
|
}) {
|
||||||
// auth default team
|
// auth default team
|
||||||
@@ -100,7 +101,6 @@ export async function createDefaultTeam({
|
|||||||
ownerId: userId,
|
ownerId: userId,
|
||||||
name: teamName,
|
name: teamName,
|
||||||
avatar,
|
avatar,
|
||||||
balance,
|
|
||||||
createTime: new Date()
|
createTime: new Date()
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -132,15 +132,11 @@ export async function createDefaultTeam({
|
|||||||
],
|
],
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
console.log('create default team and group', userId);
|
await createRootOrg({ teamId: tmb.teamId, session });
|
||||||
|
console.log('create default team, group and root org', userId);
|
||||||
return tmb;
|
return tmb;
|
||||||
} else {
|
} else {
|
||||||
console.log('default team exist', userId);
|
console.log('default team exist', userId);
|
||||||
await MongoTeam.findByIdAndUpdate(tmb.teamId, {
|
|
||||||
$set: {
|
|
||||||
...(balance !== undefined && { balance })
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +211,8 @@ export async function updateTeam({
|
|||||||
return obj;
|
return obj;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
await MongoTeam.findByIdAndUpdate(
|
// This is where we get the old team
|
||||||
|
const team = await MongoTeam.findByIdAndUpdate(
|
||||||
teamId,
|
teamId,
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
@@ -241,6 +238,8 @@ export async function updateTeam({
|
|||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await refreshSourceAvatar(avatar, team?.avatar, session);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user