Compare commits

...

37 Commits

Author SHA1 Message Date
Archer
47f7b1a7a3 full text tmp code (#3561)
* full text tmp code

* fix: init

* fix: init

* remove tmp code

* remove tmp code

* 4818-alpha
2025-01-10 18:03:14 +08:00
Archer
fadb3e3ceb feat: tmp upgrade code (#3559)
* feat: tmp upgrade code

* fulltext search test

* update action
2025-01-10 14:41:29 +08:00
Archer
1c0b323b1b share page avatar (#3558)
* feat: init 4818

* share page avatar
2025-01-10 13:37:48 +08:00
heheer
b26345dcaa share link random avatar (#3541)
* share link random avatar

* fix

* delete unused code
2025-01-10 12:18:13 +08:00
Jiangween
cef8487ca1 docs:用户答疑的官方文档补充 (#3540)
* docs:用户答疑的官方文档补充

* 问题回答的内容修补
2025-01-10 11:07:04 +08:00
Archer
ed619edd47 perf: org permission (#3556) 2025-01-10 10:48:54 +08:00
a.e.
93cf5bb372 fix: MemberModal UI (#3553)
* fix: adapt MemberModal title and icon

* fix: adapt member modal

* fix: search input placeholder

* fix: add button text
2025-01-10 09:24:06 +08:00
Archer
f556fbf0d5 4.8.18 test (#3543)
* perf: login check

* doc

* perf: llm model config

* perf: team clb config
2025-01-07 14:21:05 +08:00
a.e.
07cc849877 refactor: team permission manager (#3535)
* perf: classify org, group and member

* refactor: team per manager

* fix: missing functions
2025-01-07 09:19:23 +08:00
Archer
2066094047 perf: async read file (#3531) 2025-01-06 12:43:33 +08:00
Archer
2bf1fce32a perf: file encoding;perf: leave team code;@c121914yu perf: full text search code (#3528)
* perf: text encoding

* perf: leave team code

* perf: full text search code

* fix: http status

* perf: embedding search and vector avatar
2025-01-06 12:43:33 +08:00
Archer
5465ca642f 4.8.18 test (#3524)
* perf: remove local token

* perf: index
2025-01-06 12:43:32 +08:00
Archer
c0d0c629c5 perf: full text collection and search code;perf: rename function (#3519)
* perf: full text collection and search code

* perf: rename function

* perf: notify modal

* remove invalid code

* perf: sso login

* perf: pay process
2025-01-06 12:43:32 +08:00
heheer
20c6c202d2 fix qrcode script (#3520)
* fix qrcode script

* i18n
2025-01-06 12:43:32 +08:00
Finley Ge
c8f60b47d0 feat: support wecom sso (#3518)
* feat: support wecom sso

* chore: remove unused wecom js-sdk dependency
2025-01-06 12:43:31 +08:00
heheer
8b2be89350 optimize payment process (#3517) 2025-01-06 12:43:31 +08:00
a.e.
a401d4d612 refactor: org pathId (#3516) 2025-01-06 12:43:30 +08:00
Jiangween
d24d29ac48 perf(plugin): improve searXNG empty result handling and documentation (#3507)
* perf(plugin): improve searXNG empty result handling and documentation

* 修改了文档和代码部分无搜索的结果的反馈
2025-01-06 12:43:29 +08:00
Archer
9abbc16036 perf: notify account (#3515) 2025-01-06 12:43:28 +08:00
heheer
28710ac05b force show update notification modal & fix login page text (#3512)
* fix login page English text

* update notification modal
2025-01-06 12:43:28 +08:00
Archer
3412d7009d perf: password check;perf: image upload check;perf: sso login check (#3509)
* perf: password check

* perf: image upload check

* perf: sso login check
2025-01-06 12:43:27 +08:00
a.e.
c3480b0ffa feat: permission manage UI for org (#3503) 2025-01-06 12:43:26 +08:00
Finley Ge
f89212f35f feat: support dataset changeOwner (#3483)
* feat: support dataset changeOwner

* chore: update dataset change owner api
2025-01-06 12:43:25 +08:00
Archer
03d1c6a651 i18n (#3501)
* name

* i18n
2025-01-06 12:43:25 +08:00
Archer
5d1d4ff64f perf: org permission check (#3500) 2025-01-06 12:43:24 +08:00
a.e.
fd9600c6f8 feat: org auth for app & dataset (#3498)
* feat: auth org resource permission

* feat: org auth support for app & dataset
2025-01-06 12:43:23 +08:00
Archer
efecfd44c3 perf: Team org ui (#3499)
* perf: org ui

* perf: org ui
2025-01-06 12:43:22 +08:00
a.e.
1fc77a126a feat: org CRUD (#3380)
* feat: add org schema

* feat: org manage UI

* feat: OrgInfoModal

* feat: org tree view

* feat: org management

* fix: init root org

* feat: org permission for app

* feat: org support for dataset

* fix: disable org role control

* styles: opt type signatures

* fix: remove unused permission

* feat: delete org collaborator
2025-01-06 12:43:22 +08:00
Archer
bb669ca3ff fix: plugin cost (#3533) 2025-01-06 12:43:04 +08:00
Archer
72ed72e595 fix: charts plugins (#3530) 2025-01-05 14:41:34 +08:00
Archer
e5735fddd1 Update 4817.md (#3523) 2025-01-03 16:39:55 +08:00
Archer
fa92ef86b9 fix: system title (#3522) 2025-01-03 15:30:46 +08:00
Archer
f39ea04178 feat: new provider (#3513) 2025-01-02 15:38:06 +08:00
Archer
bd4893ced9 更新 README.md (#3511) 2025-01-02 10:31:25 +08:00
Archer
b75e807f24 fix: tool choice run same tool will error (#3502) 2024-12-31 10:58:52 +08:00
Archer
b2fdefdc0c update config.json (#3497) 2024-12-30 13:53:12 +08:00
Archer
896fec4b82 update doc (#3496) 2024-12-30 13:51:05 +08:00
228 changed files with 5627 additions and 2686 deletions

View File

@@ -36,7 +36,7 @@ jobs:
password: ${{ secrets.GH_PAT }}
- name: Set DOCKER_REPO_TAGGED based on branch or tag
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
env:
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
@@ -44,7 +44,7 @@ jobs:
docker buildx build \
-f projects/app/Dockerfile \
--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" \
--push \
--cache-from=type=local,src=/tmp/.buildx-cache \

View File

@@ -215,4 +215,4 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。
2. 未经商业授权,任何形式的商用服务均需保留相关版权信息。
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/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

@@ -168,6 +168,7 @@ weight: 707
"reRankModels": [],
"audioSpeechModels": [
{
"provider": "OpenAI",
"model": "tts-1",
"name": "OpenAI TTS1",
"charsPointsPrice": 0,
@@ -182,6 +183,7 @@ weight: 707
}
],
"whisperModel": {
"provider": "OpenAI",
"model": "whisper-1",
"name": "Whisper1",
"charsPointsPrice": 0
@@ -201,7 +203,9 @@ weight: 707
- OpenAI
- Claude
- Gemini
- Meta
- MistralAI
- AliCloud - 阿里云
- Qwen - 通义千问
- Doubao - 豆包
- ChatGLM - 智谱
@@ -213,7 +217,10 @@ weight: 707
- Baichuan - 百川
- Yi - 零一万物
- Ernie - 文心一言
- StepFun - 阶跃星辰
- Ollama
- BAAI - 智源研究院
- FishAudio
- Other - 其他

View File

@@ -19,6 +19,10 @@ images: []
## 二、通用问题
### 本地部署的限制
具体内容参考https://fael3z0zfze.feishu.cn/wiki/OFpAw8XzAi36Guk8dfucrCKUnjg。
### 能否纯本地运行
可以。需要准备好向量模型和LLM模型。

View File

@@ -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" />

View File

@@ -1,5 +1,5 @@
---
title: 'V4.8.17(进行中)'
title: 'V4.8.17(包含升级脚本)'
description: 'FastGPT V4.8.17 更新说明'
icon: 'upgrade'
draft: false
@@ -11,8 +11,8 @@ weight: 807
### 1. 更新镜像:
- 更新 fastgpt 镜像 tag: v4.8.17-alpha
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.17-alpha
- 更新 fastgpt 镜像 tag: v4.8.17-fix-title
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.17
- Sandbox 镜像无需更新
@@ -49,4 +49,4 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4817' \
12. 修复 - 文件返回接口缺少 Content-Length 头,导致通过非同源文件上传时,阿里 vision 模型无法识别图片。
13. 修复 - 去除判断器两端字符串隐藏换行符,避免判断器失效。
14. 修复 - 变量更新节点,手动输入更新内容时候,非字符串类型数据类型无法自动转化。
15. 修复 - 豆包模型无法工具调用。
15. 修复 - 豆包模型无法工具调用。

View 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. 修复 - 插件计费错误。

View File

@@ -13,4 +13,12 @@ weight: 908
但是,当连续问题之间的关联性较小,模型判断的准确度可能会受到限制。在这种情况下,我们可以引入全局变量的概念来记录分类结果。在后续的问题分类阶段,首先检查全局变量是否存有分类结果。如果有,那么直接沿用该结果;若没有,则让模型自行判断。
建议:构建批量运行脚本进行测试,评估问题分类的准确性。
建议:构建批量运行脚本进行测试,评估问题分类的准确性。
## 系统编排配置中的定时执行,如果用户打开分享的连接,停留在那个页面,定时执行触发问题
发布后,后台生效。
## AI对话回答要求中的Markdown语法取消
在针对知识库的回答要求里有, 要给它配置提示词,不然他就是默认的,默认的里面就有该语法。

View File

@@ -14,4 +14,51 @@ weight: 910
## 知识库配置里的文件处理模型是什么?与索引模型有什么区别?
* **文件处理模型**:用于数据处理的【增强处理】和【问答拆分】。在【增强处理】中,生成相关问题和摘要,在【问答拆分】中执行问答对生成。
* **索引模型**:用于向量化,即通过对文本数据进行处理和组织,构建出一个能够快速查询的数据结构。
* **索引模型**:用于向量化,即通过对文本数据进行处理和组织,构建出一个能够快速查询的数据结构。
## 基于知识库的查询但是问题相关的答案过多。ai回答到一半就不继续回答。
FastGPT回复长度计算公式:
最大回复=min(配置的最大回复(内置的限制),最大上下文(输入和输出的总和)-历史记录)
18K模型->输入与输出的和
输出增多->输入减小
所以可以:
1. 检查配置的最大回复(回复上限)
2. 减小输入来增大输出,即减小历史记录,在工作流其实也就是“聊天记录”
配置的最大回复:
![](/imgs/dataset1.png)
![](/imgs/dataset2.png)
1. 私有化部署的时候,后台配模型参数,可以在配置最大上文时候,预留一些空间,比如 128000 的模型,可以只配置 120000, 剩余的空间后续会被安排给输出
## 受到模型上下文的限制,有时候达不到聊天记录的轮次,连续对话字数过多就会报上下文不够的错误。
FastGPT回复长度计算公式:
最大回复=min(配置的最大回复(内置的限制),最大上下文(输入和输出的总和)-历史记录)
18K模型->输入与输出的和
输出增多->输入减小
所以可以:
1. 检查配置的最大回复(回复上限)
2. 减小输入来增大输出,即减小历史记录,在工作流其实也就是“聊天记录”
配置的最大回复:
![](/imgs/dataset1.png)
![](/imgs/dataset2.png)
1. 私有化部署的时候,后台配模型参数,可以在配置最大上文时候,预留一些空间,比如 128000 的模型,可以只配置 120000, 剩余的空间后续会被安排给输出

View File

@@ -160,6 +160,18 @@ default_doi_resolver: 'oadoi.org'
}
```
* 搜索结果为空时会返回友好提示:
```Bash
{
"result": "[]",
"error": {
"message": "No search results",
"code": 500
}
}
```
* 失败时通过 Promise.reject 可能返回错误信息:
```Bash

View File

@@ -114,15 +114,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.16 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.16 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.16 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.16 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -72,15 +72,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.16 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.16 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.16 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.16 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -53,15 +53,15 @@ services:
wait $$!
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.16 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.16 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.16 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.16 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -1,14 +1,20 @@
import { i18nT } from '../../../../web/i18n/utils';
import { ErrType } from '../errorCode';
/* dataset: 507000 */
const startCode = 507000;
export enum CommonErrEnum {
invalidParams = 'invalidParams',
fileNotFound = 'fileNotFound',
unAuthFile = 'unAuthFile',
missingParams = 'missingParams',
inheritPermissionError = 'inheritPermissionError'
}
const datasetErr = [
{
statusText: CommonErrEnum.fileNotFound,
message: i18nT('common:error.invalid_params')
},
{
statusText: CommonErrEnum.fileNotFound,
message: 'error.fileNotFound'

View File

@@ -1,9 +1,11 @@
import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
import type { ErrType } from '../errorCode';
/* team: 500000 */
export enum TeamErrEnum {
notUser = 'notUser',
teamOverSize = 'teamOverSize',
unAuthTeam = 'unAuthTeam',
teamMemberOverSize = 'teamMemberOverSize',
aiPointsNotEnough = 'aiPointsNotEnough',
datasetSizeNotEnough = 'datasetSizeNotEnough',
datasetAmountNotEnough = 'datasetAmountNotEnough',
@@ -14,11 +16,22 @@ export enum TeamErrEnum {
groupNameEmpty = 'groupNameEmpty',
groupNameDuplicate = 'groupNameDuplicate',
groupNotExist = 'groupNotExist',
orgMemberNotExist = 'orgMemberNotExist',
orgMemberDuplicated = 'orgMemberDuplicated',
orgNotExist = 'orgNotExist',
orgParentNotExist = 'orgParentNotExist',
cannotMoveToSubPath = 'cannotMoveToSubPath',
cannotModifyRootOrg = 'cannotModifyRootOrg',
cannotDeleteNonEmptyOrg = 'cannotDeleteNonEmptyOrg',
cannotDeleteDefaultGroup = 'cannotDeleteDefaultGroup',
userNotActive = 'userNotActive'
}
const teamErr = [
{
statusText: TeamErrEnum.notUser,
message: i18nT('common:code_error.team_error.not_user')
},
{
statusText: TeamErrEnum.teamOverSize,
message: i18nT('common:code_error.team_error.over_size')
@@ -71,6 +84,34 @@ const teamErr = [
{
statusText: TeamErrEnum.userNotActive,
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')
}
];

View File

@@ -2,25 +2,16 @@ import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils';
/* team: 503000 */
export enum UserErrEnum {
unAuthUser = 'unAuthUser',
unAuthRole = 'unAuthRole',
binVisitor = 'binVisitor',
balanceNotEnough = 'balanceNotEnough',
unAuthSso = 'unAuthSso'
}
const errList = [
{
statusText: UserErrEnum.unAuthUser,
message: i18nT('common:code_error.user_error.un_auth_user')
},
{
statusText: UserErrEnum.binVisitor,
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,
message: i18nT('common:code_error.user_error.balance_not_enough')

View File

@@ -1,10 +1,7 @@
import { MongoImageTypeEnum } from './image/constants';
import { OutLinkChatAuthProps } from '../../support/permission/chat.d';
export type preUploadImgProps = OutLinkChatAuthProps & {
type: `${MongoImageTypeEnum}`;
expiredTime?: Date;
// expiredTime?: Date;
metadata?: Record<string, any>;
};
export type UploadImgProps = preUploadImgProps & {

View File

@@ -1,61 +1,5 @@
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 FolderImgUrl = '/imgs/files/folder.svg';
export const HttpPluginImgUrl = '/imgs/app/httpPluginFill.svg';

View File

@@ -1,12 +1,8 @@
import { MongoImageTypeEnum } from './constants';
export type MongoImageSchemaType = {
_id: string;
teamId: string;
binary: Buffer;
createTime: Date;
expiredTime?: Date;
type: `${MongoImageTypeEnum}`;
metadata?: {
mime?: string; // image mime type.

View File

@@ -2,6 +2,7 @@ import { detect } from 'jschardet';
import { documentFileType, imageFileType } from './constants';
import { ChatFileTypeEnum } from '../../core/chat/constants';
import { UserChatItemValueItemType } from '../../core/chat/type';
import * as fs from 'fs';
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B';
@@ -16,6 +17,22 @@ export const formatFileSize = (bytes: number): string => {
export const detectFileEncoding = (buffer: Buffer) => {
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
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {

View File

@@ -25,17 +25,22 @@ export const simpleText = (text = '') => {
return text;
};
/*
replace {{variable}} to value
*/
export const valToStr = (val: any) => {
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>) {
if (typeof text !== 'string') return text;
for (const key in obj) {
const val = obj[key];
const formatVal = typeof val === 'object' ? JSON.stringify(val) : String(val);
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), formatVal);
const formatVal = valToStr(val);
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), () => formatVal);
}
return text || '';
}

View File

@@ -1,6 +1,9 @@
export const HUMAN_ICON = `/icon/human.svg`;
export const LOGO_ICON = `/icon/logo.svg`;
export const HUGGING_FACE_ICON = `/imgs/model/huggingface.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';

View File

@@ -73,6 +73,11 @@ export type FastGPTFeConfigsType = {
google?: string;
wechat?: string;
dingtalk?: string;
wecom?: {
corpid?: string;
agentid?: string;
secret?: string;
};
microsoft?: {
clientId?: string;
tenantId?: string;

View File

@@ -53,6 +53,7 @@ export type VectorModelItemType = PriceType & {
};
export type ReRankModelItemType = PriceType & {
provider: ModelProviderIdType;
model: string;
name: string;
requestUrl: string;

View File

@@ -4,20 +4,25 @@ export type ModelProviderIdType =
| 'OpenAI'
| 'Claude'
| 'Gemini'
| 'Meta'
| 'MistralAI'
| 'Groq'
| 'AliCloud'
| 'Qwen'
| 'Doubao'
| 'ChatGLM'
| 'DeepSeek'
| 'Ernie'
| 'Moonshot'
| 'MiniMax'
| 'SparkDesk'
| 'Hunyuan'
| 'Baichuan'
| 'StepFun'
| 'Yi'
| 'Ernie'
| 'Ollama'
| 'BAAI'
| 'FishAudio'
| 'Other';
export type ModelProviderType = {
@@ -42,6 +47,11 @@ export const ModelProviderList: ModelProviderType[] = [
name: 'Gemini',
avatar: 'model/gemini'
},
{
id: 'Meta',
name: 'Meta',
avatar: 'model/meta'
},
{
id: 'MistralAI',
name: 'MistralAI',
@@ -52,6 +62,11 @@ export const ModelProviderList: ModelProviderType[] = [
name: 'Groq',
avatar: 'model/groq'
},
{
id: 'AliCloud',
name: i18nT('common:model_alicloud'),
avatar: 'model/alicloud'
},
{
id: 'Qwen',
name: i18nT('common:model_qwen'),
@@ -67,6 +82,11 @@ export const ModelProviderList: ModelProviderType[] = [
name: i18nT('common:model_chatglm'),
avatar: 'model/chatglm'
},
{
id: 'Ernie',
name: i18nT('common:model_ernie'),
avatar: 'model/ernie'
},
{
id: 'DeepSeek',
name: 'DeepSeek',
@@ -97,21 +117,32 @@ export const ModelProviderList: ModelProviderType[] = [
name: i18nT('common:model_baichuan'),
avatar: 'model/baichuan'
},
{
id: 'StepFun',
name: i18nT('common:model_stepfun'),
avatar: 'model/stepfun'
},
{
id: 'Yi',
name: i18nT('common:model_yi'),
avatar: 'model/yi'
},
{
id: 'Ernie',
name: i18nT('common:model_ernie'),
avatar: 'model/ernie'
},
{
id: 'Ollama',
name: 'Ollama',
avatar: 'model/ollama'
},
{
id: 'BAAI',
name: i18nT('common:model_baai'),
avatar: 'model/BAAI'
},
{
id: 'FishAudio',
name: 'FishAudio',
avatar: 'model/fishaudio'
},
{
id: 'Other',
name: i18nT('common:model_other'),

View File

@@ -1,6 +1,6 @@
import { RequireOnlyOne } from '../../common/type/utils';
import type { RequireOnlyOne } from '../../common/type/utils';
import {
UpdateClbPermissionProps,
type UpdateClbPermissionProps,
UpdatePermissionBody
} from '../../support/permission/collaborator';
import { PermissionValueType } from '../../support/permission/type';
@@ -14,4 +14,5 @@ export type AppCollaboratorDeleteParams = {
} & RequireOnlyOne<{
tmbId: string;
groupId: string;
orgId: string;
}>;

View File

@@ -11,4 +11,5 @@ export type DatasetCollaboratorDeleteParams = {
} & RequireOnlyOne<{
tmbId: string;
groupId: string;
orgId: string;
}>;

View File

@@ -112,6 +112,15 @@ export type DatasetDataSchemaType = {
rebuilding?: boolean;
};
export type DatasetDataTextSchemaType = {
_id: string;
teamId: string;
datasetId: string;
collectionId: string;
dataId: string;
fullTextToken: string;
};
export type DatasetTrainingSchemaType = {
_id: string;
userId: string;

View File

@@ -9,7 +9,7 @@ import { isValidReferenceValueFormat } from '../utils';
import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
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 => {
let limit = 10;
@@ -343,11 +343,7 @@ export function replaceEditorVariable({
if (input) return getReferenceVariableValue({ value: input.value, nodes, variables });
})();
const formatVal = (() => {
if (variableVal === undefined) return 'undefined';
if (variableVal === null) return 'null';
return typeof variableVal === 'object' ? JSON.stringify(variableVal) : String(variableVal);
})();
const formatVal = valToStr(variableVal);
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, 'g');
text = text.replace(regex, () => formatVal);

View File

@@ -13,6 +13,7 @@
"next": "14.2.5",
"openai": "4.61.0",
"openapi-types": "^12.1.3",
"json5": "^2.2.3",
"timezones-list": "^3.0.2"
},
"devDependencies": {

View File

@@ -10,22 +10,18 @@ export type CollaboratorItemType = {
} & RequireOnlyOne<{
tmbId: string;
groupId: string;
orgId: string;
}>;
export type UpdateClbPermissionProps = {
members?: string[];
groups?: string[];
orgs?: string[];
permission: PermissionValueType;
};
export type DeleteClbPermissionProps = RequireOnlyOne<{
tmbId: string;
groupId: string;
}>;
export type UpdatePermissionBody = {
permission: PermissionValueType;
} & RequireOnlyOne<{
memberId: string;
groupId: string;
export type DeletePermissionQuery = RequireOnlyOne<{
tmbId?: string;
groupId?: string;
orgId?: string;
}>;

View File

@@ -1,8 +1,9 @@
import { UserModelSchema } from '../user/type';
import { RequireOnlyOne } from '../../common/type/utils';
import { TeamMemberSchema } from '../user/team/type';
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
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.
// It is spired by the permission system in Linux.
@@ -29,6 +30,7 @@ export type ResourcePermissionType = {
} & RequireOnlyOne<{
tmbId: string;
groupId: string;
orgId: string;
}>;
export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {

View File

@@ -17,5 +17,6 @@ export enum OAuthEnum {
wechat = 'wechat',
microsoft = 'microsoft',
dingtalk = 'dingtalk',
wecom = 'wecom',
sso = 'sso'
}

View File

@@ -0,0 +1,3 @@
export function checkIsWecomTerminal() {
return /wxwork/i.test(navigator.userAgent);
}

View 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;
// };

View 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'
// }

View 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;
};

View File

@@ -55,10 +55,11 @@ export type TeamMemberWithTeamAndUserSchema = TeamMemberSchema & {
export type TeamTmbItemType = {
userId: string;
teamId: string;
teamAvatar?: string;
teamName: string;
memberName: string;
avatar: string;
balance: number;
balance?: number;
tmbId: string;
teamDomain: string;
defaultTeam: boolean;

View 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)];
};

View File

@@ -35,24 +35,26 @@ export const list = [...staticPluginList, ...packagePluginList];
/* Get plugins */
export const getCommunityPlugins = () => {
return list.map<SystemPluginTemplateItemType>((name) => {
const config = require(`./src/${name}/template.json`);
return Promise.all(
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 parentId =
parentIdList.length > 0 ? `${PluginSourceEnum.community}-${parentIdList.join('/')}` : null;
const parentIdList = name.split('/').slice(0, -1);
const parentId =
parentIdList.length > 0 ? `${PluginSourceEnum.community}-${parentIdList.join('/')}` : null;
return {
...config,
id: `${PluginSourceEnum.community}-${name}`,
isFolder,
parentId,
isActive: true,
isOfficial: true
};
});
return {
...config,
id: `${PluginSourceEnum.community}-${name}`,
isFolder,
parentId,
isActive: true,
isOfficial: true
};
})
);
};
export const getSystemPluginTemplates = () => {

View File

@@ -4,8 +4,8 @@ import { SystemPluginSpecialResponse } from '../../../type.d';
type Props = {
title: string;
xAxis: string;
yAxis: string;
xAxis: string[];
yAxis: string[];
chartType: string;
};
@@ -27,7 +27,12 @@ type Option = {
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会出现生成图片无法正常展示有高手可以帮忙解决
const chart = echarts.init(undefined, undefined, {
renderer: 'svg', // 必须使用 SVG 模式
@@ -36,21 +41,11 @@ const generateChart = async (title: string, xAxis: string, yAxis: string, chartT
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 = {
backgroundColor: '#f5f5f5',
title: { text: title },
tooltip: {},
xAxis: { data: parsedXAxis },
xAxis: { data: xAxis },
yAxis: {},
series: [] // 初始化为空数组
};
@@ -58,18 +53,18 @@ const generateChart = async (title: string, xAxis: string, yAxis: string, chartT
// 根据 chartType 生成不同的图表
switch (chartType) {
case '柱状图':
option.series.push({ name: 'Sample', type: 'bar', data: parsedYAxis });
option.series.push({ name: 'Sample', type: 'bar', data: yAxis.map(Number) });
break;
case '折线图':
option.series.push({ name: 'Sample', type: 'line', data: parsedYAxis });
option.series.push({ name: 'Sample', type: 'line', data: yAxis.map(Number) });
break;
case '饼图':
option.series.push({
name: 'Sample',
type: 'pie',
data: parsedYAxis.map((value, index) => ({
value,
name: parsedXAxis[index] // 使用 xAxis 作为饼图的名称
data: yAxis.map((value, index) => ({
value: Number(value),
name: xAxis[index] // 使用 xAxis 作为饼图的名称
}))
});
break;

View File

@@ -1,6 +1,6 @@
{
"author": "silencezhang",
"version": "4812",
"version": "4817",
"name": "基础图表",
"avatar": "core/workflow/template/baseChart",
"intro": "根据数据生成图表可根据chartType生成柱状图折线图饼图",
@@ -68,7 +68,7 @@
"canEdit": true,
"key": "yAxis",
"label": "yAxis",
"description": "y轴数据例如['1', '2', '3']",
"description": "y轴数据例如[1,2,3]",
"defaultValue": "",
"list": [
{
@@ -77,7 +77,7 @@
}
],
"required": true,
"toolDescription": "y轴数据例如['1', '2', '3']"
"toolDescription": "y轴数据例如[1,2,3]"
},
{
"renderTypeList": ["select", "reference"],
@@ -145,8 +145,8 @@
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 2122.252754006148,
"y": -63.5218674613718
"x": 2128.8138851197145,
"y": -63.52186746137181
},
"version": "481",
"inputs": [
@@ -154,10 +154,12 @@
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "相对路径URL",
"label": "相对路径URL",
"key": "图表 url",
"label": "图表 url",
"description": "可用使用markdown格式展示图片![图片](url)",
"value": ["ws0DFKJnCPhk", "bzaYjKyQFOw2"]
"value": ["ws0DFKJnCPhk", "bzaYjKyQFOw2"],
"isToolOutput": true,
"required": true
}
],
"outputs": []
@@ -170,8 +172,8 @@
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1216.5166647574395,
"y": -206.30162946606856
"x": 1264.2009472531117,
"y": -455.0773486762623
},
"version": "481",
"inputs": [
@@ -275,7 +277,7 @@
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"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": "",
"required": false,
"valueDesc": "",
@@ -306,126 +308,6 @@
"description": "",
"debugLabel": "",
"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": [

View File

@@ -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 {
result: JSON.stringify(results.slice(0, 10))
};

View File

@@ -4,7 +4,7 @@ import fsp from 'fs/promises';
import fs from 'fs';
import { DatasetFileSchema } from '@fastgpt/global/core/dataset/type';
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 { MongoRawTextBuffer } from '../../buffer/rawText/schema';
import { readRawContentByFileBuffer } from '../read/utils';
@@ -36,7 +36,6 @@ export async function uploadFile({
path,
filename,
contentType,
encoding,
metadata = {}
}: {
bucketName: `${BucketNameEnum}`;
@@ -45,7 +44,6 @@ export async function uploadFile({
path: string;
filename: string;
contentType?: string;
encoding: string;
metadata?: Record<string, any>;
}) {
if (!path) return Promise.reject(`filePath is empty`);
@@ -59,7 +57,7 @@ export async function uploadFile({
// Add default metadata
metadata.teamId = teamId;
metadata.uid = uid;
metadata.encoding = encoding;
metadata.encoding = await detectFileEncodingByPath(path);
// create a gridfs bucket
const bucket = getGridBucket(bucketName);

View File

@@ -1,43 +1,92 @@
import { UploadImgProps } from '@fastgpt/global/common/file/api';
import { imageBaseUrl } from '@fastgpt/global/common/file/image/constants';
import { MongoImage } from './schema';
import { ClientSession } from '../../../common/mongo';
import { ClientSession, Types } from '../../../common/mongo';
import { guessBase64ImageType } from '../utils';
import { readFromSecondary } from '../../mongo/utils';
import { addHours } from 'date-fns';
export const maxImgSize = 1024 * 1024 * 12;
const base64MimeRegex = /data:image\/([^\)]+);base64/;
export async function uploadMongoImg({
type,
base64Img,
teamId,
expiredTime,
metadata,
shareId
shareId,
forever = false
}: UploadImgProps & {
teamId: string;
forever?: Boolean;
}) {
if (base64Img.length > maxImgSize) {
return Promise.reject('Image too large');
}
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 binary = Buffer.from(base64Data, 'base64');
const extension = mime.split('/')[1];
const { _id } = await MongoImage.create({
type,
teamId,
binary,
expiredTime,
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}`;
}
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 }) {
const formatId = id.replace(/\.[^/.]+$/, '');

View File

@@ -1,8 +1,7 @@
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 { mongoImageTypeMap } from '@fastgpt/global/common/file/image/constants';
const { Schema, model, models } = connectionMongo;
const { Schema } = connectionMongo;
const ImageSchema = new Schema({
teamId: {
@@ -14,27 +13,15 @@ const ImageSchema = new Schema({
type: Date,
default: () => new Date()
},
expiredTime: {
type: Date
},
binary: {
type: Buffer
},
type: {
type: String,
enum: Object.keys(mongoImageTypeMap),
required: true
},
metadata: {
type: Object
}
expiredTime: Date,
binary: Buffer,
metadata: Object
});
try {
// tts expired60 Minutes
ImageSchema.index({ expiredTime: 1 }, { expireAfterSeconds: 60 * 60 });
ImageSchema.index({ type: 1 });
ImageSchema.index({ createTime: 1 });
// delete related img
ImageSchema.index({ teamId: 1, 'metadata.relatedId': 1 });
} catch (error) {

View File

@@ -1,5 +1,4 @@
import { uploadMongoImg } from '../image/controller';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
import FormData from 'form-data';
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
@@ -8,7 +7,6 @@ import type { ReadFileResponse } from '../../../worker/readFile/type';
import axios from 'axios';
import { addLog } from '../../system/log';
import { batchRun } from '@fastgpt/global/common/fn/utils';
import { addHours } from 'date-fns';
import { matchMdImgTextAndUpload } from '@fastgpt/global/common/string/markdown';
export type readRawTextByLocalFileParams = {
@@ -22,7 +20,7 @@ export const readRawTextByLocalFile = async (params: readRawTextByLocalFileParam
const extension = path?.split('.')?.pop()?.toLowerCase() || '';
const buffer = fs.readFileSync(path);
const buffer = await fs.promises.readFile(path);
const { rawText } = await readRawContentByFileBuffer({
extension,
@@ -114,10 +112,9 @@ export const readRawContentByFileBuffer = async ({
if (imageList) {
await batchRun(imageList, async (item) => {
const src = await uploadMongoImg({
type: MongoImageTypeEnum.collectionImage,
base64Img: `data:${item.mime};base64,${item.base64}`,
teamId,
expiredTime: addHours(new Date(), 1),
// expiredTime: addHours(new Date(), 1),
metadata: {
...metadata,
mime: item.mime

View File

@@ -3,10 +3,13 @@ import NextCors from 'nextjs-cors';
export async function withNextCors(req: NextApiRequest, res: NextApiResponse) {
const methods = ['GET', 'eHEAD', 'PUT', 'PATCH', 'POST', 'DELETE'];
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',');
const origin = req.headers.origin;
await NextCors(req, res, {
methods,
origin: origin,
origin: allowedOrigins || origin,
optionsSuccessStatus: 200
});
}

View File

@@ -9,10 +9,10 @@ import { jsonRes } from '../response';
// unit: times/s
// how to use?
// 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) => {
const ip = requestIp.getClientIp(req);
if (!ip || process.env.USE_IP_LIMIT !== 'true') {
if (!ip || (process.env.USE_IP_LIMIT !== 'true' && !force)) {
return;
}
try {
@@ -22,10 +22,9 @@ export function useReqFrequencyLimit(seconds: number, limit: number) {
expiredTime: addSeconds(new Date(), seconds)
});
} catch (_) {
res.status(429);
jsonRes(res, {
code: 429,
message: ERROR_ENUM.tooManyRequest
error: ERROR_ENUM.tooManyRequest
});
}
};

View File

@@ -33,7 +33,7 @@ export const jsonRes = <T = any>(
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

View File

@@ -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
}

View 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
}

View File

@@ -0,0 +1,6 @@
{
"provider": "BAAI",
"model": "bge-reranker-v2-m3",
"name": "bge-reranker-v2-m3",
"charsPointsPrice": 0
}

View File

@@ -0,0 +1,6 @@
{
"provider": "OpenAI",
"model": "whisper-1",
"name": "whisper-1",
"charsPointsPrice": 0
}

View 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"
}
]
}

View File

@@ -1,11 +1,15 @@
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
import { PluginRuntimeType } from '@fastgpt/global/core/plugin/type';
import { splitCombinePluginId } from './controller';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
/*
Plugin points calculation:
1. Return 0 if error
2. Add configured points if commercial plugin
3. Add sum of child nodes points
1. 系统插件/商业版插件:
- 有错误:返回 0
- 无错误:返回 单次积分 + 子流程积分(可配置)
2. 个人插件
- 返回 子流程积分
*/
export const computedPluginUsage = async ({
plugin,
@@ -16,13 +20,16 @@ export const computedPluginUsage = async ({
childrenUsage: ChatNodeUsageType[];
error?: boolean;
}) => {
if (error) {
return 0;
const { source } = await splitCombinePluginId(plugin.id);
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);
const pluginCurrentCose = plugin.currentCost ?? 0;
return plugin.hasTokenFee ? pluginCurrentCose + childrenIUsages : pluginCurrentCose;
return childrenUsages;
};

View File

@@ -86,24 +86,21 @@ const ChatItemSchema = new Schema({
});
try {
ChatItemSchema.index({ dataId: 1 }, { background: true });
ChatItemSchema.index({ dataId: 1 });
/* delete by app;
delete by chat id;
get chat list;
get chat logs;
close custom feedback;
*/
ChatItemSchema.index({ appId: 1, chatId: 1, dataId: 1 }, { background: true });
ChatItemSchema.index({ appId: 1, chatId: 1, dataId: 1 });
// admin charts
ChatItemSchema.index({ time: -1, obj: 1 }, { background: true });
ChatItemSchema.index({ time: -1, obj: 1 });
// timer, clear history
ChatItemSchema.index({ teamId: 1, time: -1 }, { background: true });
ChatItemSchema.index({ teamId: 1, time: -1 });
// Admin charts
ChatItemSchema.index(
{ obj: 1, time: -1 },
{ background: true, partialFilterExpression: { obj: 'Human' } }
);
ChatItemSchema.index({ obj: 1, time: -1 }, { partialFilterExpression: { obj: 'Human' } });
} catch (error) {
console.log(error);
}

View File

@@ -81,19 +81,19 @@ const ChatSchema = new Schema({
});
try {
ChatSchema.index({ chatId: 1 }, { background: true });
ChatSchema.index({ chatId: 1 });
// 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;
ChatSchema.index({ appId: 1, chatId: 1 }, { background: true });
ChatSchema.index({ appId: 1, chatId: 1 });
// 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
ChatSchema.index({ shareId: 1, outLinkUid: 1, updateTime: -1 }, { background: true });
ChatSchema.index({ shareId: 1, outLinkUid: 1, updateTime: -1 });
// timer, clear history
ChatSchema.index({ teamId: 1, updateTime: -1 }, { background: true });
ChatSchema.index({ teamId: 1, updateTime: -1 });
} catch (error) {
console.log(error);
}

View File

@@ -24,6 +24,7 @@ import { pushDataListToTrainingQueue } from '../training/controller';
import { MongoImage } from '../../../common/file/image/schema';
import { hashStr } from '@fastgpt/global/common/string/tools';
import { addDays } from 'date-fns';
import { MongoDatasetDataText } from '../data/dataTextSchema';
export const createCollectionAndInsertData = async ({
dataset,
@@ -240,12 +241,12 @@ export const delCollectionRelatedSource = async ({
.map((item) => item?.metadata?.relatedImgId || '')
.filter(Boolean);
// delete files
// Delete files
await delFileByFileIdList({
bucketName: BucketNameEnum.dataset,
fileIdList
});
// delete images
// Delete images
await delImgByRelatedId({
teamId,
relateIds: relatedImageIds,
@@ -273,7 +274,7 @@ export async function delCollection({
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
const collectionIds = collections.map((item) => String(item._id));
// delete training data
// Delete training data
await MongoDatasetTraining.deleteMany({
teamId,
datasetIds: { $in: datasetIds },
@@ -285,11 +286,16 @@ export async function delCollection({
await delCollectionRelatedSource({ collections, session });
}
// delete dataset.datas
// Delete dataset_datas
await MongoDatasetData.deleteMany(
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
{ session }
);
// Delete dataset_data_texts
await MongoDatasetDataText.deleteMany(
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
{ session }
);
// delete collections
await MongoDatasetCollection.deleteMany(

View File

@@ -6,6 +6,7 @@ import { ClientSession } from '../../common/mongo';
import { MongoDatasetTraining } from './training/schema';
import { MongoDatasetData } from './data/schema';
import { deleteDatasetDataVector } from '../../common/vectorStore/controller';
import { MongoDatasetDataText } from './data/dataTextSchema';
/* ============= dataset ========== */
/* find all datasetId by top datasetId */
@@ -92,7 +93,7 @@ export async function delDatasetRelevantData({
{ session }
).lean();
// image and file
// Delete Image and file
await delCollectionRelatedSource({ collections, session });
// delete collections
@@ -101,9 +102,15 @@ export async function delDatasetRelevantData({
datasetId: { $in: datasetIds }
}).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 } });
// no session delete: delete files, vector data
// Delete vector data
await deleteDatasetDataVector({ teamId, datasetIds });
}

View 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
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
import {
@@ -39,10 +39,6 @@ const DatasetDataSchema = new Schema({
type: String,
default: ''
},
fullTextToken: {
type: String,
default: ''
},
indexes: {
type: [
{
@@ -71,17 +67,11 @@ const DatasetDataSchema = new Schema({
type: Number,
default: 0
},
inited: {
type: Boolean
},
rebuilding: Boolean
});
rebuilding: Boolean,
DatasetDataSchema.virtual('collection', {
ref: DatasetColCollectionName,
localField: 'collectionId',
foreignField: '_id',
justOne: true
// Abandon
fullTextToken: String,
initFullText: Boolean
});
try {
@@ -93,13 +83,15 @@ try {
chunkIndex: 1,
updateTime: -1
});
// full text index
DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// FullText tmp full text index
// DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// Recall vectors after data matching
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
DatasetDataSchema.index({ initFullText: 1 });
} catch (error) {
console.log(error);
}

View File

@@ -8,8 +8,8 @@ import { getVectorsByText } from '../../ai/embedding';
import { getVectorModel } from '../../ai/model';
import { MongoDatasetData } from '../data/schema';
import {
DatasetCollectionSchemaType,
DatasetDataSchemaType,
DatasetDataTextSchemaType,
SearchDataResponseItemType
} from '@fastgpt/global/core/dataset/type';
import { MongoDatasetCollection } from '../collection/schema';
@@ -23,6 +23,7 @@ import { Types } from '../../../common/mongo';
import json5 from 'json5';
import { MongoDatasetCollectionTags } from '../tag/schema';
import { readFromSecondary } from '../../../common/mongo/utils';
import { MongoDatasetDataText } from '../data/dataTextSchema';
type SearchDatasetDataProps = {
teamId: string;
@@ -266,57 +267,60 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
filterCollectionIdList
});
// get q and a
const dataList = await MongoDatasetData.find(
{
teamId,
datasetId: { $in: datasetIds },
collectionId: { $in: Array.from(new Set(results.map((item) => item.collectionId))) },
'indexes.dataId': { $in: results.map((item) => item.id?.trim()) }
},
'datasetId collectionId updateTime q a chunkIndex indexes'
)
.populate<{ collection: DatasetCollectionSchemaType }>(
'collection',
'name fileId rawLink externalFileId externalFileUrl'
)
.lean();
// Get data and collections
const collectionIdList = Array.from(new Set(results.map((item) => item.collectionId)));
const [dataList, collections] = await Promise.all([
MongoDatasetData.find(
{
teamId,
datasetId: { $in: datasetIds },
collectionId: { $in: collectionIdList },
'indexes.dataId': { $in: results.map((item) => item.id?.trim()) }
},
'_id datasetId collectionId updateTime q a chunkIndex indexes',
{ ...readFromSecondary }
).lean(),
MongoDatasetCollection.find(
{
_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 concatResults = dataList.map((data) => {
const dataIdList = data.indexes.map((item) => item.dataId);
const formatResult = results
.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) =>
data.indexes.some((index) => index.dataId === item.id)
);
if (!data) {
console.log('Data is not found', item);
return;
}
const maxScoreResult = results.find((item) => {
return dataIdList.includes(item.id);
});
const score = item?.score || 0;
return {
...data,
score: maxScoreResult?.score || 0
};
});
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(collection),
score: [{ type: SearchScoreTypeEnum.embedding, value: score, index }]
};
concatResults.sort((a, b) => b.score - a.score);
const formatResult = concatResults.map((data, index) => {
if (!data.collectionId) {
console.log('Collection is not found', data);
}
const 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 result;
})
.filter(Boolean) as SearchDataResponseItemType[];
return {
embeddingRecallResults: formatResult,
@@ -344,88 +348,224 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
};
}
let searchResults = (
const searchResults = (
await Promise.all(
datasetIds.map(async (id) => {
return MongoDatasetData.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))
return MongoDatasetData.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))
: {}),
...(forbidCollectionIdList && forbidCollectionIdList.length > 0
? {
collectionId: {
$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: {
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
}
...readFromSecondary
}
]);
);
})
)
).flat() as (DatasetDataSchemaType & { score: number })[];
// resort
searchResults.sort((a, b) => b.score - a.score);
searchResults.slice(0, limit);
// Get data and collections
const collections = await MongoDatasetCollection.find(
{
_id: { $in: searchResults.map((item) => item.collectionId) }
},
'_id name fileId rawLink'
);
'_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));
return {
id: String(item._id),
datasetId: String(item.datasetId),
collectionId: String(item.collectionId),
updateTime: item.updateTime,
...getCollectionSourceData(collection),
q: item.q,
a: item.a,
chunkIndex: item.chunkIndex,
indexes: item.indexes,
score: [{ type: SearchScoreTypeEnum.fullText, value: item.score, index }]
};
}),
fullTextRecallResults: searchResults
.map((data, index) => {
const collection = collections.find(
(col) => String(col._id) === String(data.collectionId)
);
if (!collection) {
console.log('Collection is not found', data);
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: 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
};
};
@@ -496,7 +636,8 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
forbidCollectionIdList,
filterCollectionIdList
}),
fullTextRecall({
// FullText tmp
fullTextRecall2({
query,
limit: fullTextLimit,
filterCollectionIdList,

View File

@@ -27,9 +27,10 @@ import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { getErrText } from '@fastgpt/global/common/error/utils';
type ToolRunResponseType = {
toolRunResponse: DispatchFlowResponse;
toolRunResponse?: DispatchFlowResponse;
toolMsgParams: ChatCompletionToolMessageParam;
}[];
@@ -344,59 +345,87 @@ export const runToolWithToolChoice = async (
return Promise.reject(getEmptyResponseTip());
}
// Run the selected tool by LLM.
const toolsRunResponse = (
await Promise.all(
toolCalls.map(async (tool) => {
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
/* Run the selected tool by LLM.
Since only reference parameters are passed, if the same tool is run in parallel, it will get the same run parameters
*/
const toolsRunResponse: ToolRunResponseType = [];
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 = (() => {
try {
return json5.parse(tool.function.arguments);
} catch (error) {
return {};
const startParams = (() => {
try {
return json5.parse(tool.function.arguments);
} catch (error) {
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);
const toolRunResponse = await dispatchWorkFlow({
...workflowProps,
isToolCall: true
});
toolsRunResponse.push({
toolRunResponse,
toolMsgParams
});
} 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);
const toolMsgParams: ChatCompletionToolMessageParam = {
toolsRunResponse.push({
toolRunResponse: undefined,
toolMsgParams: {
tool_call_id: tool.id,
role: ChatCompletionRequestMessageRoleEnum.Tool,
name: tool.function.name,
content: stringToolResponse
};
content: sliceStrStartEnd(err, 5000, 5000)
}
});
}
}
workflowStreamResponse?.({
event: SseResponseEventEnum.toolResponse,
data: {
tool: {
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();
const flatToolsResponseData = toolsRunResponse
.map((item) => item.toolRunResponse)
.flat()
.filter(Boolean) as DispatchFlowResponse[];
// concat tool responses
const dispatchFlowResponse = response
? response.dispatchFlowResponse.concat(flatToolsResponseData)
@@ -434,22 +463,22 @@ export const runToolWithToolChoice = async (
const outputTokens = await countGptMessagesTokens(assistantToolMsgParams);
/*
...
user
assistant: tool data
tool: tool response
*/
...
user
assistant: tool data
tool: tool response
*/
const completeMessages = [
...concatToolMessages,
...toolsRunResponse.map((item) => item?.toolMsgParams)
];
/*
Get tool node assistant response
history assistant
current tool assistant
tool child assistant
*/
Get tool node assistant response
history assistant
current tool assistant
tool child assistant
*/
const toolNodeAssistant = GPTMessages2Chats([
...assistantToolMsgParams,
...toolsRunResponse.map((item) => item?.toolMsgParams)
@@ -478,12 +507,12 @@ export const runToolWithToolChoice = async (
);
// Check interactive response(Only 1 interaction is reserved)
const workflowInteractiveResponseItem = toolsRunResponse.find(
(item) => item.toolRunResponse.workflowInteractiveResponse
(item) => item.toolRunResponse?.workflowInteractiveResponse
);
if (hasStopSignal || workflowInteractiveResponseItem) {
// Get interactive tool data
const workflowInteractiveResponse =
workflowInteractiveResponseItem?.toolRunResponse.workflowInteractiveResponse;
workflowInteractiveResponseItem?.toolRunResponse?.workflowInteractiveResponse;
// Flashback traverses completeMessages, intercepting messages that know the first user
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');

View File

@@ -72,6 +72,7 @@ import { dispatchLoopEnd } from './loop/runLoopEnd';
import { dispatchLoopStart } from './loop/runLoopStart';
import { dispatchFormInput } from './interactive/formInput';
import { dispatchToolParams } from './agent/runTool/toolParams';
import { getErrText } from '@fastgpt/global/common/error/utils';
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
@@ -231,9 +232,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
if (toolResponses !== undefined) {
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) {
return;
}
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) return;
toolRunResponse = toolResponses;
}
@@ -565,6 +564,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
const skipHandleIds = targetEdges.map((item) => item.sourceHandle);
toolRunResponse = getErrText(error);
// Skip all edges and return error
return {
[DispatchNodeResponseKeyEnum.nodeResponse]: {

View File

@@ -177,17 +177,17 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
if (!httpJsonBody) return {};
if (httpContentType === ContentTypes.json) {
httpJsonBody = replaceStringVariables(httpJsonBody);
const replaceJsonBody = httpJsonBody.replace(/(".*?")\s*:\s*undefined\b/g, '$1: null');
// Json body, parse and return
const jsonParse = json5.parse(
httpJsonBody.replace(/(".*?")\s*:\s*undefined\b/g, '$1: null')
);
const jsonParse = json5.parse(replaceJsonBody);
const removeSignJson = removeUndefinedSign(jsonParse);
return removeSignJson;
}
httpJsonBody = replaceStringVariables(httpJsonBody);
return httpJsonBody.replaceAll(UNDEFINED_SIGN, 'null');
} catch (error) {
console.log(error);
return Promise.reject(`Invalid JSON body: ${httpJsonBody}`);
}
})();

View File

@@ -92,6 +92,7 @@ OutLinkSchema.virtual('associatedApp', {
try {
OutLinkSchema.index({ shareId: -1 });
OutLinkSchema.index({ teamId: 1, tmbId: 1, appId: 1 });
} catch (error) {
console.log(error);
}

View 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);
};

View File

@@ -18,7 +18,7 @@ export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
])
.lean();
if (!tmb) return Promise.reject(UserErrEnum.unAuthUser);
if (!tmb) return Promise.reject(UserErrEnum.binVisitor);
await checkTeamAIPoints(tmb.team._id);

View File

@@ -8,10 +8,7 @@ import { authOpenApiKey } from '../openapi/auth';
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
import { MongoResourcePermission } from './schema';
import { ClientSession } from 'mongoose';
import {
PermissionValueType,
ResourcePermissionType
} from '@fastgpt/global/support/permission/type';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
import { addMinutes } from 'date-fns';
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 { TeamMemberSchema } from '@fastgpt/global/support/user/team/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
* 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
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 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;
return concatPer([...groupPers, ...orgPers]);
};
/* 仅取 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({
resourceId,
resourceType,
@@ -155,10 +131,17 @@ export const getClbsAndGroupsWithInfo = async ({
resourceType,
teamId
}: {
resourceId: ParentIdType;
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
teamId: string;
}) =>
} & (
| {
resourceId: ParentIdType;
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
}
| {
resourceType: 'team';
resourceId?: undefined;
}
)) =>
Promise.all([
MongoResourcePermission.find({
teamId,
@@ -170,7 +153,7 @@ export const getClbsAndGroupsWithInfo = async ({
})
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({
path: 'tmb',
select: 'name userId',
select: 'name userId role',
populate: {
path: 'user',
select: 'avatar'
@@ -186,6 +169,16 @@ export const getClbsAndGroupsWithInfo = async ({
}
})
.populate<{ group: MemberGroupSchemaType }>('group', 'name avatar')
.lean(),
MongoResourcePermission.find({
teamId,
resourceId,
resourceType,
orgId: {
$exists: true
}
})
.populate<{ org: OrgSchemaType }>({ path: 'org', select: 'name avatar' })
.lean()
]);
@@ -196,6 +189,7 @@ export const delResourcePermission = ({
session,
tmbId,
groupId,
orgId,
...props
}: {
resourceType: PerResourceTypeEnum;
@@ -204,15 +198,18 @@ export const delResourcePermission = ({
session?: ClientSession;
tmbId?: string;
groupId?: string;
orgId?: string;
}) => {
// tmbId or groupId only one and not both
if (!!tmbId === !!groupId) {
// either tmbId or groupId or orgId must be provided
if (!tmbId && !groupId && !orgId) {
return Promise.reject(CommonErrEnum.missingParams);
}
return MongoResourcePermission.deleteOne(
{
...(tmbId ? { tmbId } : {}),
...(groupId ? { groupId } : {}),
...(orgId ? { orgId } : {}),
...props
},
{ session }
@@ -250,7 +247,7 @@ export function authJWT(token: string) {
}>((resolve, reject) => {
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) {
reject(ERROR_ENUM.unAuthorization);
return;
@@ -436,7 +433,7 @@ export const authFileToken = (token?: string) =>
}
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) {
reject(ERROR_ENUM.unAuthFile);
return;
@@ -450,10 +447,10 @@ export const authFileToken = (token?: string) =>
});
});
export const getGroupPer = (groups: PermissionValueType[] = []) => {
if (groups.length === 0) {
export const concatPer = (perList: PermissionValueType[] = []) => {
if (perList.length === 0) {
return undefined;
}
return new Permission().addPer(...groups).value;
return new Permission().addPer(...perList).value;
};

View File

@@ -1,11 +1,11 @@
import { mongoSessionRun } from '../../common/mongo/sessionRun';
import { MongoResourcePermission } from './schema';
import { ClientSession, Model } from 'mongoose';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import type { ClientSession, Model } from 'mongoose';
import type { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import type { PermissionValueType } from '@fastgpt/global/support/permission/type';
import { getResourceClbsAndGroups } from './controller';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
export type SyncChildrenPermissionResourceType = {
_id: string;
@@ -18,6 +18,7 @@ export type UpdateCollaboratorItem = {
} & RequireOnlyOne<{
tmbId: string;
groupId: string;
orgId: string;
}>;
// 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.
*/
export async function syncCollaborators({

View File

@@ -1,9 +1,6 @@
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
import { MongoGroupMemberModel } from './groupMemberSchema';
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { MongoResourcePermission } from '../schema';
import { getGroupPer, parseHeaderCert } from '../controller';
import { parseHeaderCert } from '../controller';
import { MongoMemberGroupModel } from './memberGroupSchema';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import { ClientSession } from 'mongoose';
@@ -50,26 +47,32 @@ export const getTeamDefaultGroup = async ({
export const getGroupsByTmbId = async ({
tmbId,
teamId,
role
role,
session
}: {
tmbId: string;
teamId: string;
role?: `${GroupMemberRole}`[];
session?: ClientSession;
}) =>
(
await Promise.all([
(
await MongoGroupMemberModel.find({
tmbId,
groupId: {
$exists: true
await MongoGroupMemberModel.find(
{
tmbId,
groupId: {
$exists: true
},
...(role ? { role: { $in: role } } : {})
},
...(role ? { role: { $in: role } } : {})
})
undefined,
{ session }
)
.populate<{ group: MemberGroupSchemaType }>('group')
.lean()
).map((item) => item.group),
role ? [] : getTeamDefaultGroup({ teamId })
role ? [] : getTeamDefaultGroup({ teamId, session })
])
).flat();
@@ -79,46 +82,6 @@ export const getGroupMembersByGroupId = async (groupId: string) => {
}).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
export const authGroupMemberRole = async ({
groupId,
@@ -140,8 +103,12 @@ export const authGroupMemberRole = async ({
tmbId
};
}
const groupMember = await MongoGroupMemberModel.findOne({ groupId, tmbId });
const tmb = await getTmbInfoByTmbId({ tmbId });
const [groupMember, tmb] = await Promise.all([
MongoGroupMemberModel.findOne({ groupId, tmbId }),
getTmbInfoByTmbId({ tmbId })
]);
// Team admin or role check
if (tmb.permission.hasManagePer || (groupMember && role.includes(groupMember.role))) {
return {
...result,

View 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 }
);
}

View 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
);

View 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);

View File

@@ -6,6 +6,7 @@ import { connectionMongo, getMongoModel } from '../../common/mongo';
import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { MemberGroupCollectionName } from './memberGroup/memberGroupSchema';
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant';
const { Schema } = connectionMongo;
export const ResourcePermissionCollectionName = 'resource_permissions';
@@ -23,6 +24,10 @@ export const ResourcePermissionSchema = new Schema({
type: Schema.Types.ObjectId,
ref: MemberGroupCollectionName
},
orgId: {
type: Schema.Types.ObjectId,
ref: OrgCollectionName
},
resourceType: {
type: String,
enum: Object.values(PerResourceTypeEnum),
@@ -51,6 +56,12 @@ ResourcePermissionSchema.virtual('group', {
foreignField: '_id',
justOne: true
});
ResourcePermissionSchema.virtual('org', {
ref: OrgCollectionName,
localField: 'orgId',
foreignField: '_id',
justOne: true
});
try {
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(
{
resourceType: 1,
@@ -87,6 +115,7 @@ try {
}
);
// Delete tmb permission
ResourcePermissionSchema.index({
resourceType: 1,
teamId: 1,

View File

@@ -19,9 +19,7 @@ export const checkDatasetLimit = async ({
if (!standardConstants) return;
if (usedDatasetSize + insertLen >= datasetMaxSize) {
return Promise.reject(
`您的知识库容量为: ${datasetMaxSize}组,已使用: ${usedDatasetSize}组,导入当前文件需要: ${insertLen}组,请增加知识库容量后导入。`
);
return Promise.reject(TeamErrEnum.datasetSizeNotEnough);
}
if (usedPoints >= totalPoints) {

View File

@@ -3,22 +3,10 @@ const { Schema } = connectionMongo;
import { hashStr } from '@fastgpt/global/common/string/tools';
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils';
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({
status: {
type: String,
@@ -47,7 +35,7 @@ const UserSchema = new Schema({
},
avatar: {
type: String,
default: defaultAvatars[Math.floor(Math.random() * defaultAvatars.length)]
default: () => getRandomUserAvatar()
},
promotionRate: {
@@ -78,11 +66,8 @@ const UserSchema = new Schema({
});
try {
// login
UserSchema.index({ username: 1, password: 1 }, { background: true });
// Admin charts
UserSchema.index({ createTime: -1 }, { background: true });
UserSchema.index({ createTime: -1 });
} catch (error) {
console.log(error);
}

View File

@@ -16,6 +16,8 @@ import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupS
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
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> {
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 {
userId: String(tmb.userId),
teamId: String(tmb.teamId),
teamAvatar: tmb.team.avatar,
teamName: tmb.team.name,
memberName: tmb.name,
avatar: tmb.team.avatar,
@@ -77,13 +80,11 @@ export async function createDefaultTeam({
userId,
teamName = 'My Team',
avatar = '/icon/logo.svg',
balance,
session
}: {
userId: string;
teamName?: string;
avatar?: string;
balance?: number;
session: ClientSession;
}) {
// auth default team
@@ -100,7 +101,6 @@ export async function createDefaultTeam({
ownerId: userId,
name: teamName,
avatar,
balance,
createTime: new Date()
}
],
@@ -132,15 +132,11 @@ export async function createDefaultTeam({
],
{ 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;
} else {
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;
})();
await MongoTeam.findByIdAndUpdate(
// This is where we get the old team
const team = await MongoTeam.findByIdAndUpdate(
teamId,
{
$set: {
@@ -241,6 +238,8 @@ export async function updateTeam({
},
{ session }
);
await refreshSourceAvatar(avatar, team?.avatar, session);
}
});
}

View File

@@ -23,10 +23,6 @@ const TeamMemberSchema = new Schema({
type: String,
default: 'Member'
},
role: {
type: String
// enum: Object.keys(TeamMemberRoleMap) // disable enum validation for old data
},
status: {
type: String,
enum: Object.keys(TeamMemberStatusMap)
@@ -38,6 +34,12 @@ const TeamMemberSchema = new Schema({
defaultTeam: {
type: Boolean,
default: false
},
// Abandoned
role: {
type: String
// enum: Object.keys(TeamMemberRoleMap) // disable enum validation for old data
}
});

View File

@@ -21,10 +21,7 @@ const TeamSchema = new Schema({
type: Date,
default: () => Date.now()
},
balance: {
type: Number,
default: 0
},
balance: Number,
teamDomain: {
type: String
},

View File

@@ -61,11 +61,11 @@ const UsageSchema = new Schema({
});
try {
UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true });
UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 });
// timer task. clear dead team
// UsageSchema.index({ teamId: 1, time: -1 }, { background: true });
// UsageSchema.index({ teamId: 1, time: -1 });
UsageSchema.index({ time: 1 }, { background: true, expireAfterSeconds: 360 * 24 * 60 * 60 });
UsageSchema.index({ time: 1 }, { expireAfterSeconds: 360 * 24 * 60 * 60 });
} catch (error) {
console.log(error);
}

View File

@@ -1,9 +1,27 @@
import TurndownService from 'turndown';
import { ImageType } from '../readFile/type';
import { matchMdImgTextAndUpload } from '@fastgpt/global/common/string/markdown';
import { getNanoid } from '@fastgpt/global/common/string/tools';
// @ts-ignore
const turndownPluginGfm = require('joplin-turndown-plugin-gfm');
const processBase64Images = (htmlContent: string) => {
const base64Regex = /src="data:([^;]+);base64,([^"]+)"/g;
const images: ImageType[] = [];
const processedHtml = htmlContent.replace(base64Regex, (match, mime, base64Data) => {
const uuid = `IMAGE_${getNanoid(12)}_IMAGE`;
images.push({
uuid,
base64: base64Data,
mime
});
return `src="${uuid}"`;
});
return { processedHtml, images };
};
export const html2md = (
html: string
): {
@@ -25,11 +43,14 @@ export const html2md = (
turndownService.remove(['i', 'script', 'iframe', 'style']);
turndownService.use(turndownPluginGfm.gfm);
const { text, imageList } = matchMdImgTextAndUpload(html);
// Base64 img to id, otherwise it will occupy memory when going to md
const { processedHtml, images } = processBase64Images(html);
const md = turndownService.turndown(processedHtml);
const { text, imageList } = matchMdImgTextAndUpload(md);
return {
rawText: turndownService.turndown(text),
imageList
rawText: text,
imageList: [...images, ...imageList]
};
} catch (error) {
console.log('html 2 markdown error', error);

View File

@@ -24,7 +24,11 @@ export const readFileRawText = ({ buffer, encoding }: ReadRawTextByBuffer): Read
return buffer.toString(encoding as BufferEncoding);
}
return iconv.decode(buffer, encoding);
if (encoding) {
return iconv.decode(buffer, encoding);
}
return buffer.toString('utf-8');
} catch (error) {
return buffer.toString('utf-8');
}

View File

@@ -44,13 +44,15 @@ const parsePowerPoint = async ({
}
// Returning an array of all the xml contents read using fs.readFileSync
const xmlContentArray = files.map((file) => {
try {
return fs.readFileSync(`${decompressPath}/${file.path}`, encoding);
} catch (err) {
return fs.readFileSync(`${decompressPath}/${file.path}`, 'utf-8');
}
});
const xmlContentArray = await Promise.all(
files.map((file) => {
try {
return fs.promises.readFile(`${decompressPath}/${file.path}`, encoding);
} catch (err) {
return fs.promises.readFile(`${decompressPath}/${file.path}`, 'utf-8');
}
})
);
let responseArr: string[] = [];

View File

@@ -12,24 +12,24 @@ const getTemplateNameList = () => {
return fs.readdirSync(templatesPath) as string[];
};
const getFileTemplates = (): AppTemplateSchemaType[] => {
const getFileTemplates = async (): Promise<AppTemplateSchemaType[]> => {
const templateNames = getTemplateNameList();
const appMarketTemplates = templateNames.map((name) => {
const fileContent = require(`./src/${name}/template.json`);
return Promise.all(
templateNames.map<Promise<AppTemplateSchemaType>>(async (name) => {
const fileContent = (await import(`./src/${name}/template.json`))?.default;
return {
...fileContent,
templateId: `${PluginSourceEnum.community}-${name}`,
isActive: true
};
});
return appMarketTemplates;
return {
...fileContent,
templateId: `${PluginSourceEnum.community}-${name}`,
isActive: true
};
})
);
};
const getAppTemplates = async () => {
const communityTemplates = getFileTemplates();
const communityTemplates = await getFileTemplates();
const dbTemplates = await MongoAppTemplate.find();

View File

@@ -18,7 +18,6 @@ const MyIconButton = ({
}: Props) => {
return (
<Flex
mr={1}
p={1}
color={'myGray.500'}
rounded={'sm'}

View File

@@ -31,6 +31,7 @@ export const iconPaths = {
'common/customTitleLight': () => import('./icons/common/customTitleLight.svg'),
'common/data': () => import('./icons/common/data.svg'),
'common/dingtalkFill': () => import('./icons/common/dingtalkFill.svg'),
'common/downArrowFill': () => import('./icons/common/downArrowFill.svg'),
'common/editor/resizer': () => import('./icons/common/editor/resizer.svg'),
'common/errorFill': () => import('./icons/common/errorFill.svg'),
'common/file/move': () => import('./icons/common/file/move.svg'),
@@ -95,8 +96,10 @@ export const iconPaths = {
'common/variable': () => import('./icons/common/variable.svg'),
'common/viewLight': () => import('./icons/common/viewLight.svg'),
'common/voiceLight': () => import('./icons/common/voiceLight.svg'),
'common/wallet': () => import('./icons/common/wallet.svg'),
'common/warn': () => import('./icons/common/warn.svg'),
'common/wechatFill': () => import('./icons/common/wechatFill.svg'),
'common/wecom': () => import('./icons/common/wecom.svg'),
configmap: () => import('./icons/configmap.svg'),
copy: () => import('./icons/copy.svg'),
'core/app/aiFill': () => import('./icons/core/app/aiFill.svg'),
@@ -346,6 +349,8 @@ export const iconPaths = {
history: () => import('./icons/history.svg'),
infoRounded: () => import('./icons/infoRounded.svg'),
kbTest: () => import('./icons/kbTest.svg'),
key: () => import('./icons/key.svg'),
keyPrimary: () => import('./icons/keyPrimary.svg'),
menu: () => import('./icons/menu.svg'),
minus: () => import('./icons/minus.svg'),
'modal/AddClb': () => import('./icons/modal/AddClb.svg'),
@@ -357,16 +362,20 @@ export const iconPaths = {
'modal/selectSource': () => import('./icons/modal/selectSource.svg'),
'modal/setting': () => import('./icons/modal/setting.svg'),
'modal/teamPlans': () => import('./icons/modal/teamPlans.svg'),
'model/BAAI': () => import('./icons/model/BAAI.svg'),
'model/alicloud': () => import('./icons/model/alicloud.svg'),
'model/baichuan': () => import('./icons/model/baichuan.svg'),
'model/chatglm': () => import('./icons/model/chatglm.svg'),
'model/claude': () => import('./icons/model/claude.svg'),
'model/deepseek': () => import('./icons/model/deepseek.svg'),
'model/doubao': () => import('./icons/model/doubao.svg'),
'model/ernie': () => import('./icons/model/ernie.svg'),
'model/fishaudio': () => import('./icons/model/fishaudio.svg'),
'model/gemini': () => import('./icons/model/gemini.svg'),
'model/groq': () => import('./icons/model/groq.svg'),
'model/huggingface': () => import('./icons/model/huggingface.svg'),
'model/hunyuan': () => import('./icons/model/hunyuan.svg'),
'model/meta': () => import('./icons/model/meta.svg'),
'model/minimax': () => import('./icons/model/minimax.svg'),
'model/mistral': () => import('./icons/model/mistral.svg'),
'model/moonshot': () => import('./icons/model/moonshot.svg'),
@@ -374,6 +383,7 @@ export const iconPaths = {
'model/openai': () => import('./icons/model/openai.svg'),
'model/qwen': () => import('./icons/model/qwen.svg'),
'model/sparkDesk': () => import('./icons/model/sparkDesk.svg'),
'model/stepfun': () => import('./icons/model/stepfun.svg'),
'model/yi': () => import('./icons/model/yi.svg'),
more: () => import('./icons/more.svg'),
moreLine: () => import('./icons/moreLine.svg'),
@@ -403,7 +413,6 @@ export const iconPaths = {
'support/bill/shoppingCart': () => import('./icons/support/bill/shoppingCart.svg'),
'support/bill/wallet': () => import('./icons/support/bill/wallet.svg'),
'support/outlink/apikeyFill': () => import('./icons/support/outlink/apikeyFill.svg'),
'support/outlink/apikeyLight': () => import('./icons/support/outlink/apikeyLight.svg'),
'support/outlink/iframeLight': () => import('./icons/support/outlink/iframeLight.svg'),
'support/outlink/share': () => import('./icons/support/outlink/share.svg'),
'support/outlink/shareLight': () => import('./icons/support/outlink/shareLight.svg'),
@@ -411,7 +420,6 @@ export const iconPaths = {
'support/permission/privateLight': () => import('./icons/support/permission/privateLight.svg'),
'support/permission/publicLight': () => import('./icons/support/permission/publicLight.svg'),
'support/team/group': () => import('./icons/support/team/group.svg'),
'support/team/key': () => import('./icons/support/team/key.svg'),
'support/team/memberLight': () => import('./icons/support/team/memberLight.svg'),
'support/usage/usageRecordLight': () => import('./icons/support/usage/usageRecordLight.svg'),
'support/user/informLight': () => import('./icons/support/user/informLight.svg'),

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/solid/chevron-down">
<path id="Rectangle 3101" d="M11.4695 5.33325C12.6574 5.33325 13.2523 6.76944 12.4123 7.60939L9.01223 11.0095C8.49154 11.5302 7.64732 11.5302 7.12662 11.0095L3.72653 7.60939C2.88657 6.76944 3.48146 5.33325 4.66933 5.33325H11.4695Z" fill="#667085"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 390 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99996 1.45117L10.0088 1.45127C11.0658 1.46249 12.1863 1.68237 13.0713 2.24383C13.9975 2.83154 14.6351 3.78214 14.6351 5.09344C14.6351 5.52986 14.5203 5.93227 14.3316 6.29147C16.4572 7.72689 18.083 10.1445 18.083 12.9349C18.083 14.9037 17.1234 16.3526 15.6002 17.2692C14.1238 18.1577 12.142 18.5394 10.0036 18.5488L9.9963 18.5488C7.85793 18.5394 5.87613 18.1577 4.39966 17.2692C2.87651 16.3526 1.91687 14.9037 1.91687 12.9349C1.91687 10.1445 3.54266 7.72689 5.66829 6.29147C5.4796 5.93227 5.36483 5.52986 5.36483 5.09344C5.36483 3.78214 6.00237 2.83154 6.92865 2.24383C7.81357 1.68237 8.93415 1.46249 9.99111 1.45127L9.99996 1.45117ZM7.82155 3.65114C7.32958 3.96329 7.0315 4.40688 7.0315 5.09344C7.0315 5.3213 7.13449 5.58907 7.40685 5.87306C7.81463 6.29825 7.73085 7.01936 7.18438 7.31745C5.1323 8.4368 3.58354 10.5741 3.58354 12.9349C3.58354 14.2641 4.18779 15.1965 5.25903 15.8411C6.37626 16.5135 8.018 16.873 9.99996 16.8821C11.9819 16.873 13.6237 16.5135 14.7409 15.8411C15.8121 15.1965 16.4164 14.2641 16.4164 12.9349C16.4164 10.5741 14.8676 8.4368 12.8155 7.31745C12.2691 7.01936 12.1853 6.29825 12.5931 5.87306C12.8654 5.58907 12.9684 5.3213 12.9684 5.09344C12.9684 4.40688 12.6703 3.96329 12.1784 3.65114C11.6468 3.31389 10.87 3.12837 9.99996 3.11794C9.12994 3.12837 8.35309 3.31389 7.82155 3.65114Z" />
<path d="M9.68737 8.6035C9.79475 8.3133 10.2052 8.3133 10.3126 8.6035L10.9425 10.3057C10.9762 10.397 11.0482 10.4689 11.1394 10.5027L12.8417 11.1326C13.1319 11.24 13.1319 11.6504 12.8417 11.7578L11.1394 12.3877C11.0482 12.4215 10.9762 12.4934 10.9425 12.5846L10.3126 14.2869C10.2052 14.5771 9.79475 14.5771 9.68737 14.2869L9.05748 12.5846C9.02372 12.4934 8.95178 12.4215 8.86054 12.3877L7.1583 11.7578C6.86809 11.6504 6.86809 11.24 7.1583 11.1326L8.86054 10.5027C8.95178 10.4689 9.02372 10.397 9.05748 10.3057L9.68737 8.6035Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,7 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.9278 14.6745C12.9329 14.6814 12.9381 14.6882 12.9449 14.6934C12.9552 14.7037 12.9673 14.714 12.9793 14.7209C13.0033 14.7432 13.0256 14.7655 13.0497 14.7878C13.5031 15.2412 13.7882 15.7994 13.9049 16.385C13.9067 16.4176 13.9101 16.4503 13.9152 16.4829C13.9204 16.519 13.929 16.555 13.9376 16.5894C13.9822 16.7542 14.0698 16.9088 14.1986 17.0376C14.5936 17.4326 15.2359 17.4326 15.6326 17.0376C16.0276 16.6426 16.0276 16.0003 15.6326 15.6036C15.4935 15.4645 15.3235 15.3735 15.1432 15.3322C15.1226 15.3271 15.1037 15.3237 15.0831 15.3202C15.0624 15.3168 15.0418 15.3134 15.0212 15.3116C14.4219 15.2 13.8483 14.9115 13.3846 14.4478C13.3519 14.4152 13.3193 14.3808 13.2867 14.3465C13.1922 14.252 13.0394 14.252 12.9432 14.3465C12.8556 14.4409 12.8505 14.58 12.9278 14.6745Z" fill="#FB6500"/>
<path d="M16.1323 15.4541C16.1391 15.449 16.146 15.4438 16.1511 15.4369C16.1615 15.4266 16.1718 15.4146 16.1786 15.4026C16.201 15.3786 16.2233 15.3562 16.2456 15.3322C16.699 14.8788 17.2571 14.5937 17.8428 14.4769C17.8754 14.4752 17.908 14.4718 17.9407 14.4666C17.9767 14.4615 18.0128 14.4529 18.0471 14.4443C18.212 14.3996 18.3666 14.3121 18.4954 14.1833C18.8904 13.7883 18.8904 13.146 18.4954 12.7493C18.1004 12.3543 17.4581 12.3543 17.0614 12.7493C16.9223 12.8884 16.8312 13.0584 16.79 13.2387C16.7849 13.2593 16.7814 13.2782 16.778 13.2988C16.7746 13.3194 16.7711 13.34 16.7694 13.3606C16.6578 13.96 16.3693 14.5336 15.9056 14.9973C15.8729 15.0299 15.8386 15.0626 15.8042 15.0952C15.7098 15.1896 15.7098 15.3425 15.8042 15.4387C15.897 15.5245 16.0361 15.5314 16.1323 15.4541Z" fill="#0082EF"/>
<path d="M16.9103 12.2496C16.9052 12.2427 16.9 12.2358 16.8932 12.2307C16.8828 12.2204 16.8708 12.2101 16.8588 12.2032C16.8348 12.1809 16.8124 12.1585 16.7884 12.1362C16.335 11.6828 16.0499 11.1247 15.9331 10.5391C15.9314 10.5064 15.928 10.4738 15.9228 10.4412C15.9177 10.4051 15.9091 10.369 15.9005 10.3347C15.8559 10.1698 15.7683 10.0153 15.6395 9.88646C15.2445 9.49146 14.6022 9.49146 14.2055 9.88646C13.8087 10.2815 13.8105 10.9238 14.2055 11.3205C14.3446 11.4596 14.5146 11.5506 14.6949 11.5918C14.7155 11.597 14.7344 11.6004 14.755 11.6038C14.7756 11.6073 14.7962 11.6107 14.8168 11.6124C15.4162 11.724 15.9898 12.0126 16.4535 12.4763C16.4861 12.5089 16.5188 12.5432 16.5514 12.5776C16.6459 12.672 16.7987 12.672 16.8949 12.5776C16.9825 12.4848 16.9876 12.3457 16.9103 12.2496Z" fill="#2DBC00"/>
<path d="M13.7056 11.4716C13.6988 11.4767 13.6919 11.4819 13.6867 11.4887C13.6764 11.4991 13.6661 11.5111 13.6593 11.5231C13.6369 11.5471 13.6146 11.5695 13.5923 11.5935C13.1389 12.0469 12.5807 12.332 11.9951 12.4488C11.9625 12.4505 11.9299 12.4539 11.8972 12.4591C11.8612 12.4642 11.8251 12.4728 11.7907 12.4814C11.6259 12.526 11.4713 12.6136 11.3425 12.7424C10.9475 13.1374 10.9475 13.7797 11.3425 14.1764C11.7375 14.5732 12.3798 14.5714 12.7765 14.1764C12.9156 14.0373 13.0066 13.8673 13.0479 13.687C13.053 13.6664 13.0565 13.6475 13.0599 13.6269C13.0633 13.6063 13.0668 13.5857 13.0685 13.5651C13.1801 12.9657 13.4686 12.3921 13.9323 11.9284C13.9649 11.8958 13.9993 11.8631 14.0336 11.8305C14.1281 11.7361 14.1281 11.5832 14.0336 11.487C13.9409 11.3994 13.8018 11.3943 13.7056 11.4716Z" fill="#FFCC00"/>
<path d="M15.2136 6.51859C14.9319 5.93984 14.5524 5.4023 14.0887 4.92143C12.9123 3.70553 11.267 2.92241 9.45521 2.71804C9.13063 2.68026 8.80776 2.66309 8.49691 2.66309C8.20153 2.66309 7.8924 2.68026 7.57984 2.71461C5.75942 2.91211 4.10559 3.69179 2.92231 4.90769C2.45519 5.38856 2.07393 5.92438 1.79056 6.50142C1.40415 7.28282 1.20837 8.11575 1.20837 8.97272C1.20837 10.077 1.54498 11.1641 2.18041 12.119C2.53934 12.6599 3.15416 13.3572 3.67968 13.7848L3.38772 14.9887L3.30357 15.3287C3.28812 15.3614 3.27781 15.3974 3.26922 15.4335C3.26407 15.4558 3.26235 15.4781 3.26064 15.5022C3.25892 15.5194 3.25549 15.5365 3.25549 15.5554C3.25549 15.8645 3.50622 16.117 3.81707 16.117C3.91839 16.117 4.01285 16.0878 4.09356 16.0414C4.097 16.0397 4.09872 16.038 4.10215 16.038L4.13822 16.0174L6.21796 14.9732C6.66619 15.102 7.10927 15.1845 7.57812 15.236C7.88209 15.2703 8.19122 15.2858 8.49691 15.2858C8.80776 15.2858 9.13063 15.2669 9.45521 15.2308C10.0941 15.1587 10.7089 15.011 11.2911 14.8015C11.2275 14.7809 11.1657 14.7534 11.1056 14.7191C10.7484 14.5147 10.5646 14.1266 10.6007 13.7419C10.1834 13.8741 9.74888 13.9686 9.29893 14.0201C9.02587 14.051 8.75624 14.0665 8.4952 14.0665C8.23931 14.0665 7.9817 14.0527 7.72581 14.0235C7.67257 14.0184 7.61934 14.0098 7.5661 14.0029C7.21575 13.9565 6.87056 13.8844 6.53567 13.7865C6.46698 13.7642 6.39485 13.7539 6.321 13.7539C6.20594 13.7539 6.09431 13.7848 5.98096 13.8432C5.9655 13.8501 5.95176 13.8569 5.93631 13.8655L4.60191 14.6521H4.60019C4.57271 14.6675 4.55726 14.6744 4.5418 14.6744C4.49543 14.6744 4.45765 14.6349 4.45765 14.5868L4.50745 14.3876C4.52119 14.3344 4.54008 14.2605 4.56412 14.1712C4.62252 13.9497 4.70151 13.6457 4.76162 13.419C4.77536 13.3726 4.78738 13.3177 4.78738 13.2559C4.78738 13.0841 4.70495 12.921 4.56756 12.8214C4.49715 12.7698 4.42845 12.7166 4.35461 12.6565C4.24298 12.5655 4.13478 12.471 4.03174 12.3731C3.7415 12.0983 3.48733 11.7978 3.27438 11.4767C2.77119 10.721 2.50499 9.86232 2.50499 8.99505C2.50499 8.32184 2.65956 7.66752 2.96181 7.04926C3.18851 6.58729 3.4942 6.15794 3.87031 5.77153C4.84406 4.77202 6.21109 4.13144 7.72409 3.96657C7.98685 3.93738 8.24618 3.92364 8.49348 3.92364C8.7528 3.92364 9.02243 3.9391 9.29721 3.97001C10.8016 4.14175 12.1618 4.78576 13.1287 5.78356C13.5031 6.16996 13.807 6.60103 14.032 7.06472C14.3308 7.67954 14.482 8.3287 14.482 8.99505C14.482 9.06374 14.4768 9.13415 14.4734 9.20285C14.8632 8.96413 15.3802 9.0105 15.7168 9.34882C15.7339 9.366 15.7477 9.38489 15.7631 9.40206C15.7734 9.25952 15.7803 9.11698 15.7803 8.97444C15.7837 8.12434 15.5914 7.29828 15.2136 6.51859Z" fill="#0079DE"/>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,9 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon/line/key">
<g id="Vector">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2109 12.395C14.7199 12.395 16.75 10.3644 16.75 7.86409C16.75 5.36381 14.7199 3.33317 12.2109 3.33317C9.70193 3.33317 7.67177 5.36381 7.67177 7.86409C7.67177 8.2826 7.72817 8.68531 7.83285 9.0663C8.02882 9.7796 7.86557 10.6069 7.27775 11.1964L3.33532 15.1502L3.33531 16.6665H5.02967L5.07424 16.622L5.52315 18.2974C5.43021 18.321 5.33398 18.3332 5.23648 18.3332H2.66864C2.11635 18.3332 1.66864 17.8855 1.66864 17.3332L1.66865 14.9435C1.66865 14.6347 1.79111 14.3384 2.00918 14.1197L6.09755 10.0196C6.23063 9.88611 6.27567 9.68959 6.22574 9.50784C6.08191 8.98436 6.00511 8.43318 6.00511 7.86409C6.00511 4.44126 8.78354 1.6665 12.2109 1.6665C15.6383 1.6665 18.4167 4.44126 18.4167 7.86409C18.4167 11.2869 15.6383 14.0617 12.2109 14.0617C11.651 14.0617 11.1084 13.9876 10.5923 13.8488C10.4113 13.8 10.216 13.8454 10.0833 13.9779L8.90842 15.1503L7.72991 13.9718L8.90598 12.7982C9.49218 12.2132 10.3143 12.048 11.0254 12.2393C11.4015 12.3405 11.7985 12.395 12.2109 12.395Z" fill="#3370FF"/>
<path d="M6.13784 16.7879H6.85411C7.26832 16.7879 7.60417 16.4521 7.60417 16.0379L7.60411 16.0288V15.3216H8.43437C8.84858 15.3216 9.18437 14.9858 9.18437 14.5716C9.18437 14.1574 8.84858 13.8216 8.43437 13.8216H6.96804C6.94888 13.8216 6.92989 13.8223 6.9111 13.8237C6.89229 13.8223 6.87328 13.8216 6.85411 13.8216C6.4399 13.8216 6.10411 14.1574 6.10411 14.5716V15.2879H5.38784C4.97363 15.2879 4.63784 15.6237 4.63784 16.0379V17.5695C4.63784 17.9837 4.97363 18.3195 5.38784 18.3195C5.80206 18.3195 6.13784 17.9837 6.13784 17.5695V16.7879Z" fill="#3370FF"/>
<path d="M14.4642 7.19029C14.4642 8.11076 13.718 8.85695 12.7975 8.85695C11.877 8.85695 11.1308 8.11076 11.1308 7.19029C11.1308 6.26981 11.877 5.52362 12.7975 5.52362C13.718 5.52362 14.4642 6.26981 14.4642 7.19029Z" fill="#3370FF"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.8421 18.067L13.3433 29.38H17.1204L19.8133 21.8408L22.501 29.38H26.3705L21.8759 18.067H17.8421ZM43.9918 29.3552H47.6953V18.0419H43.9918V29.3552ZM10.4405 24.3003C10.0338 23.8233 9.568 23.4314 8.70827 23.2344C9.27395 23.0491 9.53849 22.8553 9.82969 22.5371C10.2706 22.0593 10.4939 21.4839 10.4939 20.8153C10.4939 20.0204 10.182 19.351 9.56515 18.8106C8.94862 18.272 8.06187 18 6.9088 18H0.000355556V20.4788H6.01067C6.01067 20.4788 6.94436 20.5244 6.93084 21.4074C6.9184 22.3645 5.984 22.3543 5.984 22.3543H0V24.8418H5.5936C5.99716 24.8418 6.63893 24.7581 6.97067 25.0086C7.23307 25.2078 7.33938 25.3655 7.33938 25.6998C7.33938 26.0607 7.20924 26.3484 6.95004 26.5629C6.68764 26.7759 6.44516 26.8276 5.60249 26.8385H0.000355556V29.313H6.36907C6.64107 29.313 7.19431 29.2584 8.02738 29.1564C8.6528 29.0799 9.12035 28.9576 9.43004 28.7861C9.93244 28.5162 10.326 28.144 10.614 27.6761C10.902 27.2092 11.0464 26.6787 11.0464 26.0938C11.0464 25.3731 10.8427 24.7763 10.4405 24.3003ZM35.1371 21.7752L37.824 29.313H41.6949L37.1993 18H33.1652L28.6674 29.313H32.4434L35.1371 21.7752Z" fill="#231F20"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1735795333775" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4413" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M512 64a448 448 0 1 1 0 896A448 448 0 0 1 512 64z" fill="#FF6A00" p-id="4414"></path><path d="M324.8 602.624a26.752 26.752 0 0 1-21.312-25.92v-142.72a27.712 27.712 0 0 1 21.376-25.984l132.416-28.672 13.952-56.896H317.312a97.6 97.6 0 0 0-98.24 96.96v169.344c0.384 54.08 44.16 97.856 98.24 98.176h153.92l-13.888-56.512-132.544-27.776zM710.4 322.432c54.016 0.128 97.92 43.584 98.56 97.6v170.176a98.368 98.368 0 0 1-98.56 98.048H555.328l14.08-56.832 132.608-28.736a27.84 27.84 0 0 0 21.376-25.92v-142.72a26.88 26.88 0 0 0-21.376-25.984l-132.544-28.8-14.08-56.832zM570.368 497.92v13.952H457.28v-13.952h113.088z" fill="#FFFFFF" p-id="4415"></path></svg>

After

Width:  |  Height:  |  Size: 978 B

View File

@@ -0,0 +1,5 @@
<svg viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="128" height="128" fill="white"/>
<path opacity="0.35" fill-rule="evenodd" clip-rule="evenodd" d="M117.928 62.5487C119.067 62.5487 120 63.1367 120 63.8553V68.2933C120 69.012 119.067 69.6 117.928 69.6C116.789 69.6 115.856 69.012 115.856 68.2933V63.8507C115.856 63.1367 116.789 62.5487 117.928 62.5487ZM109.696 64.2847C110.835 64.2847 111.768 64.8727 111.768 65.5913V75.368C111.768 76.082 110.835 76.67 109.696 76.67C108.553 76.67 107.624 76.082 107.624 75.3633V65.5867C107.624 64.8727 108.557 64.2847 109.696 64.2847ZM101.459 66.31C102.598 66.31 103.531 66.8933 103.531 67.612V78.602C103.531 79.3207 102.598 79.9087 101.459 79.9087C100.321 79.9087 99.3873 79.3207 99.3873 78.602V67.612C99.3873 66.8933 100.321 66.31 101.459 66.31ZM93.2273 67.9853C94.366 67.9853 95.2993 68.5687 95.2993 69.2873V80.1607C95.2993 80.8747 94.366 81.4627 93.2273 81.4627C92.0886 81.4627 91.1553 80.8747 91.1553 80.156V69.2827C91.1553 68.5687 92.0886 67.9853 93.2273 67.9853ZM84.9906 69.46C86.134 69.46 87.0626 70.048 87.0626 70.7667V80.1793C87.0626 80.898 86.1293 81.486 84.9906 81.486C83.852 81.486 82.9186 80.898 82.9186 80.1793V70.7667C82.9186 70.048 83.852 69.46 84.9906 69.46ZM76.8006 75.466C77.9393 75.466 78.8726 76.0493 78.8726 76.7727V79.7593C78.8726 80.4733 77.9393 81.0567 76.8006 81.0567C75.662 81.0567 74.7286 80.4733 74.7286 79.7547V76.768C74.7286 76.0493 75.662 75.466 76.8006 75.466Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M117.928 50.6067C119.067 50.6067 120 51.19 120 51.9087V59.2587C120 59.9773 119.067 60.5653 117.928 60.5653C116.789 60.5653 115.856 59.9773 115.856 59.2587V51.9087C115.856 51.19 116.789 50.602 117.928 50.602V50.6067ZM109.696 45.6693C110.835 45.6693 111.768 46.2527 111.768 46.976V60.0893C111.768 60.8033 110.835 61.3867 109.696 61.3867C108.553 61.3867 107.624 60.8033 107.624 60.0847V46.9713C107.624 46.2527 108.557 45.6693 109.696 45.6693ZM101.459 43.812C102.598 43.812 103.531 44.4 103.531 45.1187V62.2313C103.531 62.9453 102.598 63.538 101.459 63.538C100.321 63.538 99.3873 62.9453 99.3873 62.2313V45.114C99.3873 44.4 100.321 43.812 101.459 43.812ZM93.2273 43C94.366 43 95.2993 43.588 95.2993 44.3067V64.3267C95.2993 65.0453 94.366 65.6333 93.2273 65.6333C92.0887 65.6333 91.1553 65.0453 91.1553 64.3267V44.3067C91.1553 43.588 92.0887 43 93.2273 43ZM84.9907 43.5087C86.134 43.5087 87.0627 44.0967 87.0627 44.8153V65.1387C87.0627 65.8573 86.1293 66.4453 84.9907 66.4453C83.852 66.4453 82.9187 65.8573 82.9187 65.1387V44.8107C82.9187 44.0967 83.852 43.504 84.9907 43.504V43.5087ZM76.8007 44.7827C77.9393 44.7827 78.8727 45.366 78.8727 46.0893V71.448C78.8727 72.162 77.9393 72.7547 76.8007 72.7547C75.662 72.7547 74.7287 72.1667 74.7287 71.448V46.08C74.7287 45.3613 75.662 44.7733 76.8007 44.7733V44.7827ZM68.396 46.9433C69.5347 46.9433 70.468 47.5267 70.468 48.2453V77.0013C70.468 77.72 69.5347 78.308 68.396 78.308C67.2573 78.308 66.324 77.72 66.324 77.0013V48.2453C66.324 47.5267 67.2573 46.9387 68.396 46.9387V46.9433ZM60.164 50.602C61.3027 50.602 62.236 51.19 62.236 51.9087V79.5727C62.236 80.2913 61.3027 80.8793 60.164 80.8793C59.0207 80.8793 58.092 80.2913 58.092 79.5727V51.9087C58.092 51.19 59.0253 50.602 60.164 50.602ZM51.9273 55.3713C53.0707 55.3713 53.9993 55.9593 53.9993 56.678V80.7627C53.9993 81.4813 53.066 82.0693 51.9273 82.0693C50.7887 82.0693 49.8553 81.4813 49.8553 80.7627V56.6733C49.8553 55.9547 50.7887 55.3667 51.9273 55.3667V55.3713ZM43.6953 58.666C44.834 58.666 45.7673 59.254 45.7673 59.9727V83.698C45.7673 84.412 44.834 85 43.6953 85C42.5567 85 41.6233 84.412 41.6233 83.6933V59.9727C41.6233 59.254 42.5567 58.666 43.6953 58.666ZM35.5007 61.396C36.644 61.396 37.5727 61.984 37.5727 62.7027V75.326C37.5727 76.0447 36.6393 76.6327 35.5007 76.6327C34.362 76.6327 33.4287 76.0447 33.4287 75.326V62.698C33.4287 61.984 34.362 61.396 35.5007 61.396ZM27.0213 49.0853C28.16 49.0853 29.0933 49.6733 29.0933 50.392V67.99C29.0933 68.7087 28.16 69.2967 27.0213 69.2967C25.8827 69.2967 24.9493 68.7087 24.9493 67.99V50.392C24.9493 49.6733 25.8827 49.0853 27.0213 49.0853ZM18.556 50.238C19.6947 50.238 20.6233 50.826 20.6233 51.5447V56.958C20.6233 57.672 19.69 58.26 18.556 58.26C17.4127 58.26 16.484 57.672 16.484 56.9533V51.54C16.484 50.826 17.4173 50.238 18.556 50.238ZM10.072 49.51C11.2107 49.51 12.144 50.098 12.144 50.8167V53.2573C12.144 53.9713 11.2107 54.5593 10.072 54.5593C8.93333 54.5593 8 53.9713 8 53.2527V50.8167C8 50.098 8.93333 49.51 10.072 49.51ZM35.5053 47.4473C36.644 47.4473 37.5727 48.0353 37.5727 48.754V50.5833C37.5727 51.302 36.6393 51.89 35.5007 51.89C34.362 51.89 33.4287 51.302 33.4287 50.5833V48.7493C33.4287 48.0307 34.362 47.4427 35.5007 47.4427L35.5053 47.4473Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -0,0 +1,77 @@
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_28116_23027)">
<path d="M13.7936 8H13.7456L13.6836 13.23H13.7276C17.1576 13.23 19.8196 15.944 25.6076 25.722L25.9576 26.316L25.9816 26.356L29.2216 21.48L29.1976 21.442C28.4896 20.2828 27.7574 19.1386 27.0016 18.01C26.2575 16.8963 25.4736 15.8096 24.6516 14.752C20.8256 9.864 17.6256 8 13.7936 8Z" fill="url(#paint0_linear_28116_23027)"/>
<path d="M13.7455 8C9.89953 8.02 6.49353 10.516 4.03953 14.34C4.03284 14.3513 4.02617 14.3627 4.01953 14.374L8.52753 16.836L8.54953 16.802C9.98553 14.636 11.7695 13.254 13.6855 13.232H13.7275L13.7915 8H13.7455Z" fill="url(#paint1_linear_28116_23027)"/>
<path d="M4.03906 14.3401L4.01706 14.3741C2.40106 16.8941 1.19706 19.9901 0.549062 23.3281L0.539062 23.3721L5.60706 24.5721L5.61506 24.5281C6.15506 21.5941 7.18706 18.8721 8.52706 16.8381L8.54906 16.8041L4.03906 14.3401Z" fill="url(#paint2_linear_28116_23027)"/>
<path d="M5.614 24.5279L0.548 23.3279L0.538 23.3719C0.184 25.2079 0.004 27.0739 0 28.9439V28.9899L5.196 29.4559V29.4099C5.17709 27.7734 5.31776 26.139 5.616 24.5299L5.614 24.5279Z" fill="url(#paint3_linear_28116_23027)"/>
<path d="M5.35425 31.074C5.26107 30.5367 5.20825 29.9932 5.19625 29.448V29.404L0.000246211 28.936V28.984C-0.00558496 30.0921 0.0921736 31.1982 0.292246 32.288L5.36225 31.118C5.35954 31.1034 5.35687 31.0887 5.35425 31.074Z" fill="url(#paint4_linear_28116_23027)"/>
<path d="M6.54116 33.78C5.97316 33.16 5.57316 32.268 5.36316 31.124L5.35516 31.082L0.285156 32.252L0.293156 32.294C0.677156 34.314 1.42916 35.994 2.50516 37.268L2.53316 37.302L6.56916 33.812C6.5591 33.8014 6.5511 33.7907 6.54116 33.78Z" fill="url(#paint5_linear_28116_23027)"/>
<path d="M21.5599 19.3081C18.5039 24.0081 16.6519 26.9581 16.6519 26.9581C12.5819 33.3581 11.1739 34.7921 8.90991 34.7921C8.46467 34.8037 8.02219 34.7189 7.61277 34.5436C7.20336 34.3682 6.8367 34.1064 6.53791 33.7761L2.50391 37.2641L2.53191 37.2981C4.01991 39.0361 6.11591 40.0001 8.71191 40.0001C12.6379 40.0001 15.4599 38.1441 20.4799 29.3401L24.0119 23.0801C23.2283 21.8007 22.412 20.5429 21.5599 19.3081Z" fill="#0082FB"/>
<path d="M27.0043 11.8919L26.9723 11.9239C26.1723 12.7839 25.4003 13.7399 24.6523 14.7559C25.4083 15.7219 26.1883 16.8039 27.0023 18.0159C27.9623 16.5299 28.8583 15.3259 29.7363 14.4019L29.7683 14.3699L27.0043 11.8919Z" fill="url(#paint6_linear_28116_23027)"/>
<path d="M41.8367 11.426C39.7067 9.266 37.1667 8 34.4507 8C31.5867 8 29.1767 9.574 27.0047 11.888L26.9727 11.92L29.7367 14.4L29.7687 14.366C31.1987 12.872 32.5847 12.126 34.1207 12.126C35.7727 12.126 37.3207 12.906 38.6607 14.276L38.6907 14.308L41.8687 11.458L41.8367 11.426Z" fill="#0082FB"/>
<path d="M47.9962 28.25C47.8762 21.316 45.4562 15.118 41.8682 11.458L41.8362 11.426L38.6602 14.274L38.6902 14.306C41.3902 17.09 43.2442 22.266 43.4122 28.248V28.294H47.9962V28.25Z" fill="url(#paint7_linear_28116_23027)"/>
<path d="M47.996 28.2999V28.2539H43.412V28.2979C43.42 28.5779 43.424 28.8619 43.424 29.1459C43.424 30.7759 43.182 32.0939 42.688 33.0459L42.666 33.0899L46.082 36.6539L46.108 36.6139C47.348 34.6939 48 32.0279 48 28.7939C48 28.6279 48 28.4639 47.996 28.2999Z" fill="url(#paint8_linear_28116_23027)"/>
<path d="M42.688 33.04L42.666 33.08C42.238 33.884 41.628 34.42 40.832 34.654L42.388 39.578C42.6879 39.4767 42.9806 39.3551 43.264 39.214C44.3715 38.6542 45.3134 37.8144 45.996 36.778L46.084 36.648L46.108 36.608L42.688 33.04Z" fill="url(#paint9_linear_28116_23027)"/>
<path d="M39.8406 34.7861C39.3166 34.7861 38.8566 34.7081 38.4046 34.5061L36.8086 39.5501C37.7066 39.8561 38.6626 39.9941 39.7286 39.9941C40.7126 39.9941 41.6146 39.8481 42.4326 39.5641L40.8726 34.6401C40.5386 34.7401 40.1926 34.7901 39.8406 34.7861Z" fill="url(#paint10_linear_28116_23027)"/>
<path d="M36.6453 33.0679L36.6173 33.0339L32.9453 36.8619L32.9773 36.8959C34.2513 38.2599 35.4693 39.1059 36.8513 39.5699L38.4453 34.5299C37.8633 34.2799 37.2993 33.8239 36.6453 33.0679Z" fill="url(#paint11_linear_28116_23027)"/>
<path d="M36.619 33.03C35.519 31.746 34.155 29.606 32.013 26.15L29.221 21.478L29.199 21.438L25.959 26.314L25.983 26.354L27.961 29.69C29.879 32.91 31.441 35.238 32.947 36.86L32.979 36.892L36.647 33.064C36.6376 33.0527 36.6283 33.0414 36.619 33.03Z" fill="url(#paint12_linear_28116_23027)"/>
</g>
<defs>
<linearGradient id="paint0_linear_28116_23027" x1="1192.97" y1="1645.34" x2="229.443" y2="378.706" gradientUnits="userSpaceOnUse">
<stop offset="0.0006" stop-color="#0867DF"/>
<stop offset="0.4539" stop-color="#0668E1"/>
<stop offset="0.8591" stop-color="#0064E0"/>
</linearGradient>
<linearGradient id="paint1_linear_28116_23027" x1="215.779" y1="678.423" x2="903.276" y2="155.167" gradientUnits="userSpaceOnUse">
<stop offset="0.1323" stop-color="#0064DF"/>
<stop offset="0.9988" stop-color="#0064E0"/>
</linearGradient>
<linearGradient id="paint2_linear_28116_23027" x1="307.026" y1="926.288" x2="587.306" y2="218.601" gradientUnits="userSpaceOnUse">
<stop offset="0.0147" stop-color="#0072EC"/>
<stop offset="0.6881" stop-color="#0064DF"/>
</linearGradient>
<linearGradient id="paint3_linear_28116_23027" x1="264.132" y1="576.012" x2="298.323" y2="120.222" gradientUnits="userSpaceOnUse">
<stop offset="0.0731" stop-color="#007CF6"/>
<stop offset="0.9943" stop-color="#0072EC"/>
</linearGradient>
<linearGradient id="paint4_linear_28116_23027" x1="279.668" y1="224.361" x2="269.842" y2="151.011" gradientUnits="userSpaceOnUse">
<stop offset="0.0731" stop-color="#007FF9"/>
<stop offset="1" stop-color="#007CF6"/>
</linearGradient>
<linearGradient id="paint5_linear_28116_23027" x1="237.123" y1="108.844" x2="387.116" y2="428.013" gradientUnits="userSpaceOnUse">
<stop offset="0.0731" stop-color="#007FF9"/>
<stop offset="1" stop-color="#0082FB"/>
</linearGradient>
<linearGradient id="paint6_linear_28116_23027" x1="202.73" y1="433.72" x2="383.614" y2="182.812" gradientUnits="userSpaceOnUse">
<stop offset="0.2799" stop-color="#007FF8"/>
<stop offset="0.9141" stop-color="#0082FB"/>
</linearGradient>
<linearGradient id="paint7_linear_28116_23027" x1="447.222" y1="116.598" x2="849.003" y2="1599.3" gradientUnits="userSpaceOnUse">
<stop stop-color="#0082FB"/>
<stop offset="0.9995" stop-color="#0081FA"/>
</linearGradient>
<linearGradient id="paint8_linear_28116_23027" x1="362.999" y1="67.4063" x2="127.282" y2="545.316" gradientUnits="userSpaceOnUse">
<stop offset="0.0619" stop-color="#0081FA"/>
<stop offset="1" stop-color="#0080F9"/>
</linearGradient>
<linearGradient id="paint9_linear_28116_23027" x1="200.6" y1="420.874" x2="390.793" y2="290.929" gradientUnits="userSpaceOnUse">
<stop stop-color="#027AF3"/>
<stop offset="1" stop-color="#0080F9"/>
</linearGradient>
<linearGradient id="paint10_linear_28116_23027" x1="151.724" y1="308.912" x2="498.607" y2="308.912" gradientUnits="userSpaceOnUse">
<stop stop-color="#0377EF"/>
<stop offset="0.9994" stop-color="#0279F1"/>
</linearGradient>
<linearGradient id="paint11_linear_28116_23027" x1="254.612" y1="263.742" x2="449.915" y2="379.036" gradientUnits="userSpaceOnUse">
<stop offset="0.0019" stop-color="#0471E9"/>
<stop offset="1" stop-color="#0377EF"/>
</linearGradient>
<linearGradient id="paint12_linear_28116_23027" x1="370.69" y1="326.175" x2="1008.64" y2="1130.72" gradientUnits="userSpaceOnUse">
<stop offset="0.2765" stop-color="#0867DF"/>
<stop offset="1" stop-color="#0471E9"/>
</linearGradient>
<clipPath id="clip0_28116_23027">
<rect width="48" height="48" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" style="shape-rendering:geometricPrecision;text-rendering:geometricPrecision;image-rendering:optimizeQuality;fill-rule:evenodd;clip-rule:evenodd"><path style="opacity:.718" fill="#0080ff" d="M46.5-.5h3c0 1.333.667 2 2 2v3c-1.676.683-2.343 2.017-2 4h-3v-4c-.427-1.238-.427-2.238 0-3v-2z"/><path style="opacity:.596" fill="#0084ff" d="M46.5 1.5c-.427.762-.427 1.762 0 3h-4v-3h4z"/><path style="opacity:.605" fill="#009dff" d="M7.5 4.5a10.173 10.173 0 0 0-3 3v-4c1.291-.237 2.291.096 3 1z"/><path style="opacity:.749" fill="#09f" d="M7.5 4.5v10a5.728 5.728 0 0 1-3-1v-6a10.173 10.173 0 0 1 3-3z"/><path style="opacity:.741" fill="#0196ff" d="M15.5 6.5a303.97 303.97 0 0 1-4 3v-3h4z"/><path style="opacity:.938" fill="#0091ff" d="M15.5 6.5h8c-1.965 2.978-4.632 4.978-8 6v3h-4v-6a303.97 303.97 0 0 0 4-3z"/><path style="opacity:.963" fill="#008cff" d="M23.5 6.5h9a147.885 147.885 0 0 0-18.5 17c-.77-1.098-1.603-1.098-2.5 0v-8h4v-3c3.368-1.022 6.035-3.022 8-6z"/><path style="opacity:.932" fill="#0086ff" d="M32.5 6.5h10c-5.818 2.99-10.484 6.658-14 11h-6v5a109.794 109.794 0 0 1-11 9v-8c.897-1.098 1.73-1.098 2.5 0a147.885 147.885 0 0 1 18.5-17z"/><path style="opacity:.853" fill="#0080ff" d="M42.5 6.5h3v8a30.943 30.943 0 0 1-2-2 17.853 17.853 0 0 0-5 5h-10c3.516-4.342 8.182-8.01 14-11z"/><path style="opacity:.749" fill="#007aff" d="M45.5 14.5v3h-7a17.853 17.853 0 0 1 5-5c.682.743 1.349 1.41 2 2z"/><path style="opacity:.721" fill="#0092ff" d="M4.5 13.5c.891.61 1.891.943 3 1v7c-.617.11-1.117.444-1.5 1-.278-.916-.778-1.582-1.5-2v-7z"/><path style="opacity:.618" fill="#008dff" d="M4.5 20.5c.722.418 1.222 1.084 1.5 2 .383-.556.883-.89 1.5-1v6h-3v-7z"/><path style="opacity:.941" fill="#0080ff" d="M22.5 22.5v8A273.02 273.02 0 0 1 7.5 43c.556.383.89.883 1 1.5h-9v-3a19.582 19.582 0 0 1 6-4.5 18.492 18.492 0 0 0 2-3.5c1.983.343 3.317-.324 4-2a109.794 109.794 0 0 0 11-9z"/><path style="opacity:.693" fill="#0078ff" d="M35.5 27.5v1c-3.515.845-6.181 2.845-8 6v-7h8z"/><path style="opacity:.716" fill="#016eff" d="M35.5 28.5v-1h16v1c-1.527-.073-2.527.594-3 2h-8c.11-.617.444-1.117 1-1.5a18.436 18.436 0 0 0-6-.5z"/><path style="opacity:.967" fill="#0073ff" d="M35.5 28.5a18.436 18.436 0 0 1 6 .5c-.556.383-.89.883-1 1.5-.992-.172-1.658.162-2 1-3.85 2.76-7.517 5.76-11 9v-6c1.819-3.155 4.485-5.155 8-6z"/><path style="opacity:.874" fill="#0069ff" d="M51.5 28.5v2h-3c.473-1.406 1.473-2.073 3-2z"/><path style="opacity:.921" fill="#0079ff" d="M22.5 30.5v8c-2.938 1.122-4.938 3.122-6 6h-8c-.11-.617-.444-1.117-1-1.5a273.02 273.02 0 0 0 15-12.5z"/><path style="opacity:.924" fill="#006dff" d="M38.5 31.5v8a73.126 73.126 0 0 0-11 9v-8c3.483-3.24 7.15-6.24 11-9z"/><path style="opacity:.878" fill="#0085ff" d="M-.5 33.5h8a18.492 18.492 0 0 1-2 3.5 19.582 19.582 0 0 0-6 4.5v-8z"/><path style="opacity:.788" fill="#0074ff" d="M22.5 38.5v6h-6c1.062-2.878 3.062-4.878 6-6z"/><path style="opacity:.93" fill="#0067ff" d="M38.5 39.5v9c-2.212-.547-3.879.453-5 3h-6v-3a73.126 73.126 0 0 1 11-9z"/><path style="opacity:.883" fill="#0062ff" d="M38.5 48.5v3h-5c1.121-2.547 2.788-3.547 5-3z"/></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

Some files were not shown because too many files have changed in this diff Show More