V4.8.17 feature (#3485)

* feat: add third party account config (#3443)

* temp

* editor workflow variable style

* add team to dispatch

* i18n

* delete console

* change openai account position

* fix

* fix

* fix

* fix

* fix

* 4.8.17 test (#3461)

* perf: external provider config

* perf: ui

* feat: add template config (#3434)

* change template position

* template config

* delete console

* delete

* fix

* fix

* perf: Mongo visutal field (#3464)

* remve invalid code

* perf: team member visutal code

* perf: virtual search; perf: search test data

* fix: ts

* fix: image response headers

* perf: template code

* perf: auth layout;perf: auto save (#3472)

* perf: auth layout

* perf: auto save

* perf: auto save

* fix: template guide display & http input support external variables (#3475)

* fix: template guide display

* http editor support external workflow variables

* perf: auto save;fix: ifelse checker line break; (#3478)

* perf: auto save

* perf: auto save

* fix: ifelse checker line break

* perf: doc

* perf: doc

* fix: update var type error

* 4.8.17 test (#3479)

* perf: auto save

* perf: auto save

* perf: template code

* 4.8.17 test (#3480)

* perf: auto save

* perf: auto save

* perf: model price model

* feat: add react memo

* perf: model provider filter

* fix: ts (#3481)

* perf: auto save

* perf: auto save

* fix: ts

* simple app tool select (#3473)

* workflow plugin userguide & simple tool ui

* simple tool filter

* reuse component

* change component to hook

* fix

* perf: too selector modal (#3484)

* perf: auto save

* perf: auto save

* perf: markdown render

* perf: too selector

* fix: app version require tmbId

* perf: templates refresh

* perf: templates refresh

* hide auto save error tip

* perf: toolkit guide

---------

Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
Archer
2024-12-27 20:05:12 +08:00
committed by GitHub
parent a209856d48
commit b520988c64
207 changed files with 2943 additions and 1378 deletions

View File

@@ -58,9 +58,9 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- [x] 多库复用,混用
- [x] chunk 记录修改和删除
- [x] 支持手动输入直接分段QA 拆分导入
- [x] 支持 txtmdhtmlpdfdocxpptxcsvxlsx (有需要更多可 PR file loader)
- [x] 支持 url 读取、CSV 批量导入
- [x] 支持 txtmdhtmlpdfdocxpptxcsvxlsx (有需要更多可 PR file loader),支持 url 读取、CSV 批量导入
- [x] 混合检索 & 重排
- [x] API 知识库
- [ ] 自定义文件读取服务
- [ ] 自定义分块服务
@@ -69,7 +69,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- [x] 对话时反馈引用并可修改与删除
- [x] 完整上下文呈现
- [x] 完整模块中间值呈现
- [x] 高级编排 DeBug 模式
- [ ] 高级编排 DeBug 模式
`4` OpenAPI 接口
- [x] completions 接口 (chat 模式对齐 GPT 接口)

View File

@@ -1424,7 +1424,11 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/searchTe
"limit": 5000,
"similarity": 0,
"searchMode": "embedding",
"usingReRank": false
"usingReRank": false,
"datasetSearchUsingExtensionQuery": true,
"datasetSearchExtensionModel": "gpt-4o-mini",
"datasetSearchExtensionBg": ""
}'
```
@@ -1441,6 +1445,9 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/searchTe
- similarity - 最低相关度0~1可选
- searchMode - 搜索模式embedding | fullTextRecall | mixedRecall
- usingReRank - 使用重排
- datasetSearchUsingExtensionQuery - 使用问题优化
- datasetSearchExtensionModel - 问题优化模型
- datasetSearchExtensionBg - 问题优化背景描述
{{% /alert %}}
{{< /markdownify >}}

View File

@@ -7,10 +7,30 @@ toc: true
weight: 807
---
## 运行升级脚本
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv4817' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
会将用户绑定的 OpenAI 账号移动到团队中。
## 完整更新内容
1.
2. 新增 - LLM 模型参数支持关闭 max_tokens 和 temperature
3. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制
4. 优化 - 可用模型展示
1. 新增 - 简易模式工具调用支持数组类型插件。
2. 新增 - 工作流增加异常离开自动保存,避免工作流丢失
3. 新增 - LLM 模型参数支持关闭 max_tokens 和 temperature
4. 新增 - 商业版支持后台配置模板市场。
5. 新增 - 商业版支持后台配置自定义工作流变量,用于与业务系统鉴权打通。
6. 新增 - 搜索测试接口支持问题优化。
7. 优化 - Markdown 大小测试,超出 20 万字符不使用 Markdown 组件,避免崩溃。
8. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制。
9. 优化 - 可用模型展示
10. 优化 - Mongo 查询语句,增加 virtual 字段。
11. 修复 - 文件返回接口缺少 Content-Length 头,导致通过非同源文件上传时,阿里 vision 模型无法识别图片。
12. 修复 - 去除判断器两端字符串隐藏换行符,避免判断器失效。
13. 修复 - 变量更新节点,手动输入更新内容时候,非字符串类型数据类型无法自动转化。

View File

@@ -18,6 +18,14 @@ export type NavbarItemType = {
isActive: boolean;
};
export type ExternalProviderWorkflowVarType = {
name: string;
key: string;
intro: string;
isOpen: boolean;
url?: string;
};
/* fastgpt main */
export type FastGPTConfigFileType = {
feConfigs: FastGPTFeConfigsType;
@@ -84,6 +92,7 @@ export type FastGPTFeConfigsType = {
uploadFileMaxSize?: number;
lafEnv?: string;
navbarItems?: NavbarItemType[];
externalProviderWorkflowVariables?: ExternalProviderWorkflowVarType[];
};
export type SystemEnvType = {

View File

@@ -13,6 +13,7 @@ import { StoreEdgeItemType } from '../workflow/type/edge';
import { AppPermission } from '../../support/permission/app/controller';
import { ParentIdType } from '../../common/parentFolder/type';
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type';
export type AppSchema = {
_id: string;
@@ -184,3 +185,28 @@ export type SystemPluginListItemType = {
name: string;
avatar: string;
};
export type AppTemplateSchemaType = {
templateId: string;
name: string;
intro: string;
avatar: string;
tags: string[];
type: string;
author?: string;
isActive?: boolean;
userGuide?: {
type: 'markdown' | 'link';
content?: string;
link?: string;
};
isQuickTemplate?: boolean;
order?: number;
workflow: WorkflowTemplateBasicType;
};
export type TemplateTypeSchemaType = {
typeName: string;
typeId: string;
typeOrder: number;
};

View File

@@ -1,9 +1,7 @@
import { DatasetCollectionTypeEnum, TrainingModeEnum, TrainingTypeMap } from '../constants';
import { CollectionWithDatasetType, DatasetCollectionSchemaType } from '../type';
import { DatasetCollectionSchemaType } from '../type';
export const getCollectionSourceData = (
collection?: CollectionWithDatasetType | DatasetCollectionSchemaType
) => {
export const getCollectionSourceData = (collection?: DatasetCollectionSchemaType) => {
return {
sourceId:
collection?.fileId ||

View File

@@ -133,11 +133,8 @@ export type DatasetTrainingSchemaType = {
indexes: Omit<DatasetDataIndexItemType, 'dataId'>[];
};
export type CollectionWithDatasetType = Omit<DatasetCollectionSchemaType, 'datasetId'> & {
datasetId: DatasetSchemaType;
};
export type DatasetDataWithCollectionType = Omit<DatasetDataSchemaType, 'collectionId'> & {
collectionId: DatasetCollectionSchemaType;
export type CollectionWithDatasetType = DatasetCollectionSchemaType & {
dataset: DatasetSchemaType;
};
/* ================= dataset ===================== */

View File

@@ -21,13 +21,20 @@ import { ReadFileNodeResponse } from '../template/system/readFiles/type';
import { UserSelectOptionType } from '../template/system/userSelect/type';
import { WorkflowResponseType } from '../../../../service/core/workflow/dispatch/type';
import { AiChatQuoteRoleType } from '../template/system/aiChat/type';
import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type';
export type ExternalProviderType = {
openaiAccount?: OpenaiAccountType;
externalWorkflowVariables?: Record<string, string>;
};
/* workflow props */
export type ChatDispatchProps = {
res?: NextApiResponse;
requestOrigin?: string;
mode: 'test' | 'chat' | 'debug';
user: UserModelSchema;
timezone: string;
externalProvider: ExternalProviderType;
runningAppInfo: {
id: string; // May be the id of the system plug-in (cannot be used directly to look up the table)

View File

@@ -9,6 +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';
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
let limit = 10;
@@ -317,6 +318,8 @@ export function replaceEditorVariable({
}) {
if (typeof text !== 'string') return text;
text = replaceVariable(text, variables);
const variablePattern = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g;
const matches = [...text.matchAll(variablePattern)];
if (matches.length === 0) return text;

View File

@@ -30,7 +30,6 @@ export const WorkflowStart: FlowNodeTemplateType = {
intro: '',
forbidDelete: true,
unique: true,
courseUrl: '/docs/guide/workbench/workflow/input/',
version: '481',
inputs: [{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }],
outputs: [

View File

@@ -69,6 +69,7 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
diagram?: string; // diagram url
courseUrl?: string; // course url
userGuide?: string; // user guide
};
export type NodeTemplateListItemType = {
@@ -87,6 +88,7 @@ export type NodeTemplateListItemType = {
currentCost?: number; // 当前积分消耗
hasTokenFee?: boolean; // 是否配置积分
instructions?: string; // 使用说明
courseUrl?: string; // 教程链接
};
export type NodeTemplateListType = {

View File

@@ -83,11 +83,6 @@ export type OutLinkSchema<T extends OutlinkAppType = undefined> = {
app: T;
};
// to handle MongoDB querying
export type OutLinkWithAppType = Omit<OutLinkSchema, 'appId'> & {
appId: AppSchema;
};
// Edit the Outlink
export type OutLinkEditType<T = undefined> = {
_id?: string;

View File

@@ -1,5 +1,6 @@
import { UserModelSchema } from '../user/type';
import { RequireOnlyOne } from '../../common/type/utils';
import { TeamMemberWithUserSchema } from '../user/team/type';
import { TeamMemberSchema } from '../user/team/type';
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
import { MemberGroupSchemaType } from './memberGroup/type';
@@ -31,11 +32,7 @@ export type ResourcePermissionType = {
}>;
export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
tmbId: TeamMemberWithUserSchema;
};
export type ResourcePerWithGroup = Omit<ResourcePermissionType, 'groupId'> & {
groupId: MemberGroupSchemaType;
tmbId: TeamMemberSchema & { user: UserModelSchema };
};
export type PermissionSchemaType = {

View File

@@ -1,6 +1,6 @@
import { PermissionValueType } from '../../permission/type';
import { TeamMemberRoleEnum } from './constant';
import { LafAccountType, TeamMemberSchema } from './type';
import { LafAccountType, TeamMemberSchema, ThirdPartyAccountType } from './type';
export type AuthTeamRoleProps = {
teamId: string;
@@ -11,14 +11,13 @@ export type CreateTeamProps = {
name: string;
avatar?: string;
defaultTeam?: boolean;
lafAccount?: LafAccountType;
memberName?: string;
};
export type UpdateTeamProps = {
export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & {
name?: string;
avatar?: string;
teamDomain?: string;
lafAccount?: null | LafAccountType;
externalWorkflowVariable?: { key: string; value: string };
};
/* ------------- member ----------- */

View File

@@ -4,6 +4,12 @@ import { LafAccountType } from './type';
import { PermissionValueType, ResourcePermissionType } from '../../permission/type';
import { TeamPermission } from '../../permission/user/controller';
export type ThirdPartyAccountType = {
lafAccount?: LafAccountType;
openaiAccount?: OpenaiAccountType;
externalWorkflowVariables?: Record<string, string>;
};
export type TeamSchema = {
_id: string;
name: string;
@@ -16,9 +22,8 @@ export type TeamSchema = {
lastExportDatasetTime: Date;
lastWebsiteSyncTime: Date;
};
lafAccount: LafAccountType;
notificationAccount?: string;
};
} & ThirdPartyAccountType;
export type tagsType = {
label: string;
@@ -42,16 +47,9 @@ export type TeamMemberSchema = {
defaultTeam: boolean;
};
export type TeamMemberWithUserSchema = Omit<TeamMemberSchema, 'userId'> & {
userId: UserModelSchema;
};
export type TeamMemberWithTeamSchema = Omit<TeamMemberSchema, 'teamId'> & {
teamId: TeamSchema;
};
export type TeamMemberWithTeamAndUserSchema = Omit<TeamMemberWithTeamSchema, 'userId'> & {
userId: UserModelSchema;
export type TeamMemberWithTeamAndUserSchema = TeamMemberSchema & {
team: TeamSchema;
user: UserModelSchema;
};
export type TeamTmbItemType = {
@@ -66,10 +64,9 @@ export type TeamTmbItemType = {
defaultTeam: boolean;
role: `${TeamMemberRoleEnum}`;
status: `${TeamMemberStatusEnum}`;
lafAccount?: LafAccountType;
notificationAccount?: string;
permission: TeamPermission;
};
} & ThirdPartyAccountType;
export type TeamMemberItemType = {
userId: string;
@@ -88,11 +85,16 @@ export type TeamTagItemType = {
};
export type LafAccountType = {
token: string;
appid: string;
token: string;
pat: string;
};
export type OpenaiAccountType = {
key: string;
baseUrl: string;
};
export type TeamInvoiceHeaderType = {
teamName: string;
unifiedCreditCode: string;

View File

@@ -14,10 +14,6 @@ export type UserModelSchema = {
timezone: string;
status: `${UserStatusEnum}`;
lastLoginTmbId?: string;
openaiAccount?: {
key: string;
baseUrl: string;
};
fastgpt_sem?: {
keyword: string;
};
@@ -29,7 +25,6 @@ export type UserType = {
avatar: string;
timezone: string;
promotionRate: UserModelSchema['promotionRate'];
openaiAccount: UserModelSchema['openaiAccount'];
team: TeamTmbItemType;
standardInfo?: standardInfoType;
notificationAccount?: string;

View File

@@ -55,8 +55,7 @@
"maxFiles": 5,
"canSelectFile": true,
"canSelectImg": true,
"required": true,
"toolDescription": "部署的searXNG服务的链接"
"required": true
}
],
"outputs": [

View File

@@ -1,4 +1,3 @@
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
import OpenAI from '@fastgpt/global/core/ai';
import {
ChatCompletionCreateParamsNonStreaming,
@@ -7,13 +6,11 @@ import {
import { getErrText } from '@fastgpt/global/common/error/utils';
import { addLog } from '../../common/system/log';
import { i18nT } from '../../../web/i18n/utils';
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
export const getAIApi = (props?: {
userKey?: UserModelSchema['openaiAccount'];
timeout?: number;
}) => {
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
const { userKey, timeout } = props || {};
const baseUrl =
@@ -29,7 +26,7 @@ export const getAIApi = (props?: {
});
};
export const getAxiosConfig = (props?: { userKey?: UserModelSchema['openaiAccount'] }) => {
export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
const { userKey } = props || {};
const baseUrl =
@@ -57,7 +54,7 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
options
}: {
body: T;
userKey?: UserModelSchema['openaiAccount'];
userKey?: OpenaiAccountType;
timeout?: number;
options?: OpenAI.RequestOptions;
}): Promise<{

View File

@@ -131,6 +131,7 @@ export async function getChildAppPreviewNode({
name: app.name,
intro: app.intro,
courseUrl: app.courseUrl,
userGuide: app.userGuide,
showStatus: app.showStatus,
isTool: true,
version: app.version,

View File

@@ -0,0 +1,51 @@
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
import { connectionMongo, getMongoModel } from '../../../common/mongo/index';
const { Schema } = connectionMongo;
export const collectionName = 'app_templates';
const AppTemplateSchema = new Schema({
templateId: {
type: String,
required: true
},
name: {
type: String
},
intro: {
type: String
},
avatar: {
type: String
},
tags: {
type: [String],
default: undefined
},
type: {
type: String
},
isActive: {
type: Boolean
},
userGuide: {
type: Object
},
isQuickTemplate: {
type: Boolean
},
order: {
type: Number,
default: -1
},
workflow: {
type: Object
}
});
AppTemplateSchema.index({ templateId: 1 });
export const MongoAppTemplate = getMongoModel<AppTemplateSchemaType>(
collectionName,
AppTemplateSchema
);

View File

@@ -0,0 +1,25 @@
import { TemplateTypeSchemaType } from '@fastgpt/global/core/app/type';
import { connectionMongo, getMongoModel } from '../../../common/mongo/index';
const { Schema } = connectionMongo;
export const collectionName = 'app_template_types';
const TemplateTypeSchema = new Schema({
typeName: {
type: String,
required: true
},
typeId: {
type: String,
required: true
},
typeOrder: {
type: Number,
default: 0
}
});
export const MongoTemplateTypes = getMongoModel<TemplateTypeSchemaType>(
collectionName,
TemplateTypeSchema
);

View File

@@ -2,10 +2,16 @@ import { connectionMongo, getMongoModel, type Model } from '../../../common/mong
const { Schema, model, models } = connectionMongo;
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import { chatConfigType } from '../schema';
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
export const AppVersionCollectionName = 'app_versions';
const AppVersionSchema = new Schema({
tmbId: {
type: String,
ref: TeamMemberCollectionName,
required: true
},
appId: {
type: Schema.Types.ObjectId,
ref: AppVersionCollectionName,
@@ -26,16 +32,8 @@ const AppVersionSchema = new Schema({
chatConfig: {
type: chatConfigType
},
isPublish: {
type: Boolean
},
versionName: {
type: String,
default: ''
},
tmbId: {
type: String
}
isPublish: Boolean,
versionName: String
});
try {

View File

@@ -104,9 +104,6 @@ export const loadRequestMessages = async ({
}) => {
// Load image to base64
const loadImageToBase64 = async (messages: ChatCompletionContentPart[]) => {
if (process.env.MULTIPLE_DATA_TO_BASE64 === 'false') {
return messages;
}
return Promise.all(
messages.map(async (item) => {
if (item.type === 'image_url') {
@@ -125,7 +122,7 @@ export const loadRequestMessages = async ({
try {
// If imgUrl is a local path, load image from local, and set url to base64
if (imgUrl.startsWith('/')) {
if (imgUrl.startsWith('/') || process.env.MULTIPLE_DATA_TO_BASE64 === 'true') {
addLog.debug('Load image from local server', {
baseUrl: serverRequestBaseUrl,
requestUrl: imgUrl

View File

@@ -4,11 +4,7 @@ import {
} from '@fastgpt/global/core/dataset/constants';
import type { CreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d';
import { MongoDatasetCollection } from './schema';
import {
CollectionWithDatasetType,
DatasetCollectionSchemaType,
DatasetSchemaType
} from '@fastgpt/global/core/dataset/type';
import { DatasetCollectionSchemaType, DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
import { MongoDatasetTraining } from '../training/schema';
import { MongoDatasetData } from '../data/schema';
import { delImgByRelatedId } from '../../../common/file/image/controller';
@@ -230,7 +226,7 @@ export const delCollectionRelatedSource = async ({
collections,
session
}: {
collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[];
collections: DatasetCollectionSchemaType[];
session: ClientSession;
}) => {
if (collections.length === 0) return;
@@ -264,7 +260,7 @@ export async function delCollection({
session,
delRelatedSource
}: {
collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[];
collections: DatasetCollectionSchemaType[];
session: ClientSession;
delRelatedSource: boolean;
}) {
@@ -274,16 +270,7 @@ export async function delCollection({
if (!teamId) return Promise.reject('teamId is not exist');
const datasetIds = Array.from(
new Set(
collections.map((item) => {
if (typeof item.datasetId === 'string') {
return String(item.datasetId);
}
return String(item.datasetId._id);
})
)
);
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
const collectionIds = collections.map((item) => String(item._id));
// delete training data
@@ -324,7 +311,7 @@ export async function delOnlyCollection({
collections,
session
}: {
collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[];
collections: DatasetCollectionSchemaType[];
session: ClientSession;
}) {
if (collections.length === 0) return;
@@ -333,16 +320,7 @@ export async function delOnlyCollection({
if (!teamId) return Promise.reject('teamId is not exist');
const datasetIds = Array.from(
new Set(
collections.map((item) => {
if (typeof item.datasetId === 'string') {
return String(item.datasetId);
}
return String(item.datasetId._id);
})
)
);
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
const collectionIds = collections.map((item) => String(item._id));
// delete training data

View File

@@ -100,6 +100,13 @@ const DatasetCollectionSchema = new Schema({
}
});
DatasetCollectionSchema.virtual('dataset', {
ref: DatasetCollectionName,
localField: 'datasetId',
foreignField: '_id',
justOne: true
});
try {
// auth file
DatasetCollectionSchema.index({ teamId: 1, fileId: 1 });

View File

@@ -130,7 +130,7 @@ export const collectionTagsToTagLabel = async ({
};
export const syncCollection = async (collection: CollectionWithDatasetType) => {
const dataset = collection.datasetId;
const dataset = collection.dataset;
if (
collection.type !== DatasetCollectionTypeEnum.link &&
@@ -183,7 +183,7 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
teamId: collection.teamId,
tmbId: collection.tmbId,
name: collection.name,
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
parentId: collection.parentId,
type: collection.type,

View File

@@ -1,4 +1,4 @@
import { CollectionWithDatasetType, DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
import { MongoDatasetCollection } from './collection/schema';
import { MongoDataset } from './schema';
import { delCollectionRelatedSource } from './collection/controller';
@@ -49,9 +49,9 @@ export async function findDatasetAndAllChildren({
}
export async function getCollectionWithDataset(collectionId: string) {
const data = (await MongoDatasetCollection.findById(collectionId)
.populate('datasetId')
.lean()) as CollectionWithDatasetType;
const data = await MongoDatasetCollection.findById(collectionId)
.populate<{ dataset: DatasetSchemaType }>('dataset')
.lean();
if (!data) {
return Promise.reject('Collection is not exist');
}

View File

@@ -77,21 +77,32 @@ const DatasetDataSchema = new Schema({
rebuilding: Boolean
});
// list collection and count data; list data; delete collection(relate data)
DatasetDataSchema.index({
teamId: 1,
datasetId: 1,
collectionId: 1,
chunkIndex: 1,
updateTime: -1
DatasetDataSchema.virtual('collection', {
ref: DatasetColCollectionName,
localField: 'collectionId',
foreignField: '_id',
justOne: true
});
// 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 });
try {
// list collection and count data; list data; delete collection(relate data)
DatasetDataSchema.index({
teamId: 1,
datasetId: 1,
collectionId: 1,
chunkIndex: 1,
updateTime: -1
});
// 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 });
} catch (error) {
console.log(error);
}
export const MongoDatasetData = getMongoModel<DatasetDataSchemaType>(
DatasetDataCollectionName,

View File

@@ -8,8 +8,8 @@ import { getVectorsByText } from '../../ai/embedding';
import { getVectorModel } from '../../ai/model';
import { MongoDatasetData } from '../data/schema';
import {
DatasetCollectionSchemaType,
DatasetDataSchemaType,
DatasetDataWithCollectionType,
SearchDataResponseItemType
} from '@fastgpt/global/core/dataset/type';
import { MongoDatasetCollection } from '../collection/schema';
@@ -267,7 +267,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
});
// get q and a
const dataList = (await MongoDatasetData.find(
const dataList = await MongoDatasetData.find(
{
teamId,
datasetId: { $in: datasetIds },
@@ -276,8 +276,11 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
},
'datasetId collectionId updateTime q a chunkIndex indexes'
)
.populate('collectionId', 'name fileId rawLink externalFileId externalFileUrl')
.lean()) as DatasetDataWithCollectionType[];
.populate<{ collection: DatasetCollectionSchemaType }>(
'collection',
'name fileId rawLink externalFileId externalFileUrl'
)
.lean();
// add score to data(It's already sorted. The first one is the one with the most points)
const concatResults = dataList.map((data) => {
@@ -307,8 +310,8 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
a: data.a,
chunkIndex: data.chunkIndex,
datasetId: String(data.datasetId),
collectionId: String(data.collectionId?._id),
...getCollectionSourceData(data.collectionId),
collectionId: String(data.collectionId),
...getCollectionSourceData(data.collection),
score: [{ type: SearchScoreTypeEnum.embedding, value: data.score, index }]
};

View File

@@ -34,7 +34,7 @@ export const pushDataListToTrainingQueueByCollectionId = async ({
session?: ClientSession;
} & PushDatasetDataProps) => {
const {
datasetId: { _id: datasetId, agentModel, vectorModel }
dataset: { _id: datasetId, agentModel, vectorModel }
} = await getCollectionWithDataset(collectionId);
return pushDataListToTrainingQueue({
...props,

View File

@@ -35,7 +35,7 @@ type ActionProps = Props & { cqModel: LLMModelItemType };
/* request openai chat */
export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse> => {
const {
user,
externalProvider,
node: { nodeId, name },
histories,
params: { model, history = 6, agents, userChatInput }
@@ -69,7 +69,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
.filter((item) => item.key !== result.key)
.map((item) => getHandleId(nodeId, 'source', item.key)),
[DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
query: userChatInput,
tokens,
@@ -80,7 +80,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
moduleName: name,
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
tokens
}
@@ -90,7 +90,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
const completions = async ({
cqModel,
user,
externalProvider,
histories,
params: { agents, systemPrompt = '', userChatInput }
}: ActionProps) => {
@@ -131,7 +131,7 @@ const completions = async ({
},
cqModel
),
userKey: user.openaiAccount
userKey: externalProvider.openaiAccount
});
const answer = data.choices?.[0].message?.content || '';

View File

@@ -46,7 +46,7 @@ const agentFunName = 'request_function';
export async function dispatchContentExtract(props: Props): Promise<Response> {
const {
user,
externalProvider,
node: { name },
histories,
params: { content, history = 6, model, description, extractKeys }
@@ -123,7 +123,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
[NodeOutputKeyEnum.contextExtractFields]: JSON.stringify(arg),
...arg,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
query: content,
tokens,
@@ -134,7 +134,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
moduleName: name,
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
tokens
}
@@ -211,7 +211,7 @@ ${description ? `- ${description}` : ''}
};
const toolChoice = async (props: ActionProps) => {
const { user, extractModel } = props;
const { externalProvider, extractModel } = props;
const { filterMessages, agentFunction } = await getFunctionCallSchema(props);
@@ -233,7 +233,7 @@ const toolChoice = async (props: ActionProps) => {
},
extractModel
),
userKey: user.openaiAccount
userKey: externalProvider.openaiAccount
});
const arg: Record<string, any> = (() => {
@@ -263,7 +263,7 @@ const toolChoice = async (props: ActionProps) => {
};
const functionCall = async (props: ActionProps) => {
const { user, extractModel } = props;
const { externalProvider, extractModel } = props;
const { agentFunction, filterMessages } = await getFunctionCallSchema(props);
const functions: ChatCompletionCreateParams.Function[] = [agentFunction];
@@ -281,7 +281,7 @@ const functionCall = async (props: ActionProps) => {
},
extractModel
),
userKey: user.openaiAccount
userKey: externalProvider.openaiAccount
});
try {
@@ -312,7 +312,7 @@ const functionCall = async (props: ActionProps) => {
const completions = async ({
extractModel,
user,
externalProvider,
histories,
params: { content, extractKeys, description = 'No special requirements' }
}: ActionProps) => {
@@ -360,7 +360,7 @@ Human: ${content}`
},
extractModel
),
userKey: user.openaiAccount
userKey: externalProvider.openaiAccount
});
const answer = data.choices?.[0].message?.content || '';

View File

@@ -43,7 +43,7 @@ export const runToolWithFunctionCall = async (
requestOrigin,
runtimeNodes,
runtimeEdges,
user,
externalProvider,
stream,
workflowStreamResponse,
params: { temperature, maxToken, aiChatVision }
@@ -221,7 +221,7 @@ export const runToolWithFunctionCall = async (
getEmptyResponseTip
} = await createChatCompletion({
body: requestBody,
userKey: user.openaiAccount,
userKey: externalProvider.openaiAccount,
options: {
headers: {
Accept: 'application/json, text/plain, */*'

View File

@@ -46,7 +46,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
requestOrigin,
chatConfig,
runningAppInfo: { teamId },
user,
externalProvider,
params: {
model,
systemPrompt,
@@ -153,7 +153,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
})();
// censor model and system key
if (toolModel.censor && !user.openaiAccount?.key) {
if (toolModel.censor && !externalProvider.openaiAccount?.key) {
await postTextCensor({
text: `${systemPrompt}
${userChatInput}
@@ -228,7 +228,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
tokens: toolNodeTokens,
modelType: ModelTypeEnum.llm
});
const toolAIUsage = user.openaiAccount?.key ? 0 : totalPoints;
const toolAIUsage = externalProvider.openaiAccount?.key ? 0 : totalPoints;
// flat child tool response
const childToolResponse = dispatchFlowResponse.map((item) => item.flowResponses).flat();

View File

@@ -51,7 +51,7 @@ export const runToolWithPromptCall = async (
requestOrigin,
runtimeNodes,
runtimeEdges,
user,
externalProvider,
stream,
workflowStreamResponse,
params: { temperature, maxToken, aiChatVision }
@@ -230,7 +230,7 @@ export const runToolWithPromptCall = async (
getEmptyResponseTip
} = await createChatCompletion({
body: requestBody,
userKey: user.openaiAccount,
userKey: externalProvider.openaiAccount,
options: {
headers: {
Accept: 'application/json, text/plain, */*'

View File

@@ -24,11 +24,9 @@ import { AIChatItemType } from '@fastgpt/global/core/chat/type';
import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils';
import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils';
import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
import { addLog } from '../../../../../common/system/log';
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 { i18nT } from '../../../../../../web/i18n/utils';
type ToolRunResponseType = {
toolRunResponse: DispatchFlowResponse;
@@ -92,7 +90,7 @@ export const runToolWithToolChoice = async (
runtimeNodes,
runtimeEdges,
stream,
user,
externalProvider,
workflowStreamResponse,
params: { temperature, maxToken, aiChatVision }
} = workflowProps;
@@ -278,7 +276,7 @@ export const runToolWithToolChoice = async (
getEmptyResponseTip
} = await createChatCompletion({
body: requestBody,
userKey: user.openaiAccount,
userKey: externalProvider.openaiAccount,
options: {
headers: {
Accept: 'application/json, text/plain, */*'

View File

@@ -62,7 +62,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
res,
requestOrigin,
stream = false,
user,
externalProvider,
histories,
node: { name },
query,
@@ -134,7 +134,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
}),
(() => {
// censor model and system key
if (modelConstantsData.censor && !user.openaiAccount?.key) {
if (modelConstantsData.censor && !externalProvider.openaiAccount?.key) {
return postTextCensor({
text: `${systemPrompt}
${userChatInput}
@@ -170,7 +170,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
// console.log(JSON.stringify(requestBody, null, 2), '===');
const { response, isStreamResponse, getEmptyResponseTip } = await createChatCompletion({
body: requestBody,
userKey: user.openaiAccount,
userKey: externalProvider.openaiAccount,
options: {
headers: {
Accept: 'application/json, text/plain, */*'
@@ -230,7 +230,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
return {
answerText,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
tokens,
query: `${userChatInput}`,
@@ -245,7 +245,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
moduleName: name,
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
tokens
}

View File

@@ -126,7 +126,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
runtimeEdges = [],
histories = [],
variables = {},
user,
timezone,
externalProvider,
stream = false,
...props
} = data;
@@ -150,7 +151,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
[DispatchNodeResponseKeyEnum.runTimes]: 1,
[DispatchNodeResponseKeyEnum.assistantResponses]: [],
[DispatchNodeResponseKeyEnum.toolResponses]: null,
newVariables: removeSystemVariable(variables)
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables)
};
}
@@ -180,6 +181,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
variables = {
...getSystemVariable(data),
...externalProvider.externalWorkflowVariables,
...variables
};
@@ -493,11 +495,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
}
// replace {{xx}} variables
let value = replaceVariable(input.value, variables);
// let value = replaceVariable(input.value, variables);
// replace {{$xx.xx$}} variables
value = replaceEditorVariable({
text: value,
let value = replaceEditorVariable({
text: input.value,
nodes: runtimeNodes,
variables
});
@@ -543,7 +545,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
res,
variables,
histories,
user,
timezone,
externalProvider,
stream,
node,
runtimeNodes,
@@ -677,7 +680,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
[DispatchNodeResponseKeyEnum.assistantResponses]:
mergeAssistantResponseAnswerText(chatAssistantResponse),
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse,
newVariables: removeSystemVariable(variables)
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables)
};
} catch (error) {
return Promise.reject(error);
@@ -686,7 +689,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
/* get system variable */
const getSystemVariable = ({
user,
timezone,
runningAppInfo,
chatId,
responseChatItemId,
@@ -707,7 +710,7 @@ const getSystemVariable = ({
chatId,
responseChatItemId,
histories,
cTime: getSystemTime(user.timezone)
cTime: getSystemTime(timezone)
};
};

View File

@@ -22,7 +22,6 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
params,
runtimeEdges,
runtimeNodes,
user,
node: { name }
} = props;
const { loopInputArray = [], childrenNodeIdList = [] } = params;
@@ -86,14 +85,14 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
return {
[DispatchNodeResponseKeyEnum.assistantResponses]: assistantResponses,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: totalPoints,
totalPoints,
loopInput: loopInputArray,
loopResult: outputValueArr,
loopDetail: loopDetail
},
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints,
moduleName: name
}
],

View File

@@ -53,8 +53,8 @@ function checkCondition(condition: VariableConditionEnum, inputValue: any, value
[VariableConditionEnum.isEmpty]: () => isEmpty(inputValue),
[VariableConditionEnum.isNotEmpty]: () => !isEmpty(inputValue),
[VariableConditionEnum.equalTo]: () => String(inputValue) === value,
[VariableConditionEnum.notEqual]: () => String(inputValue) !== value,
[VariableConditionEnum.equalTo]: () => String(inputValue).trim() === value.trim(),
[VariableConditionEnum.notEqual]: () => String(inputValue).trim() !== value.trim(),
// number
[VariableConditionEnum.greaterThan]: () => Number(inputValue) > Number(value),
@@ -67,8 +67,8 @@ function checkCondition(condition: VariableConditionEnum, inputValue: any, value
[VariableConditionEnum.notInclude]: () => !isInclude(inputValue, value),
// string
[VariableConditionEnum.startWith]: () => inputValue?.startsWith(value),
[VariableConditionEnum.endWith]: () => inputValue?.endsWith(value),
[VariableConditionEnum.startWith]: () => inputValue?.trim()?.startsWith(value),
[VariableConditionEnum.endWith]: () => inputValue?.trim()?.endsWith(value),
[VariableConditionEnum.reg]: () => {
if (typeof inputValue !== 'string' || !value) return false;
if (value.startsWith('/')) {
@@ -79,7 +79,7 @@ function checkCondition(condition: VariableConditionEnum, inputValue: any, value
}
const reg = new RegExp(value, 'g');
const result = reg.test(inputValue);
const result = reg.test(inputValue.trim());
return result;
},

View File

@@ -19,7 +19,7 @@ type Props = ModuleDispatchProps<{
type Response = DispatchNodeResultType<{}>;
export const dispatchUpdateVariable = async (props: Props): Promise<Response> => {
const { params, variables, runtimeNodes, workflowStreamResponse, node } = props;
const { params, variables, runtimeNodes, workflowStreamResponse, externalProvider } = props;
const { updateList } = params;
const nodeIds = runtimeNodes.map((node) => node.nodeId);
@@ -41,15 +41,15 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
const value = (() => {
// If first item is empty, it means it is a input value
if (!item.value?.[0]) {
const formatValue = valueTypeFormat(item.value?.[1], item.valueType);
return typeof formatValue === 'string'
? replaceEditorVariable({
text: formatValue,
nodes: runtimeNodes,
variables
})
: formatValue;
const val =
typeof item.value?.[1] === 'string'
? replaceEditorVariable({
text: item.value?.[1],
nodes: runtimeNodes,
variables
})
: item.value?.[1];
return valueTypeFormat(val, item.valueType);
} else {
return getReferenceVariableValue({
value: item.value,
@@ -80,7 +80,7 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
workflowStreamResponse?.({
event: SseResponseEventEnum.updateVariables,
data: removeSystemVariable(variables)
data: removeSystemVariable(variables, externalProvider.externalWorkflowVariables)
});
return {

View File

@@ -14,6 +14,7 @@ import { NextApiResponse } from 'next';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import json5 from 'json5';
export const getWorkflowResponseWrite = ({
res,
@@ -116,11 +117,18 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
return Boolean(value);
}
try {
if (type === WorkflowIOValueTypeEnum.datasetQuote && !Array.isArray(value)) {
return JSON.parse(value);
}
if (type === WorkflowIOValueTypeEnum.selectDataset && !Array.isArray(value)) {
return JSON.parse(value);
if (
type &&
[
WorkflowIOValueTypeEnum.object,
WorkflowIOValueTypeEnum.chatHistory,
WorkflowIOValueTypeEnum.datasetQuote,
WorkflowIOValueTypeEnum.selectApp,
WorkflowIOValueTypeEnum.selectDataset
].includes(type) &&
typeof value !== 'object'
) {
return json5.parse(value);
}
} catch (error) {
return value;
@@ -141,14 +149,23 @@ export const checkQuoteQAValue = (quoteQA?: SearchDataResponseItemType[]) => {
};
/* remove system variable */
export const removeSystemVariable = (variables: Record<string, any>) => {
export const removeSystemVariable = (
variables: Record<string, any>,
removeObj: Record<string, string> = {}
) => {
const copyVariables = { ...variables };
delete copyVariables.userId;
delete copyVariables.appId;
delete copyVariables.chatId;
delete copyVariables.responseChatItemId;
delete copyVariables.histories;
delete copyVariables.cTime;
// delete external provider workflow variables
Object.keys(removeObj).forEach((key) => {
delete copyVariables[key];
});
return copyVariables;
};
export const filterSystemVariables = (variables: Record<string, any>): SystemVariablesType => {

View File

@@ -83,6 +83,13 @@ const OutLinkSchema = new Schema({
}
});
OutLinkSchema.virtual('associatedApp', {
ref: AppCollectionName,
localField: 'appId',
foreignField: '_id',
justOne: true
});
try {
OutLinkSchema.index({ shareId: -1 });
} catch (error) {

View File

@@ -1,18 +1,32 @@
import { TeamMemberWithUserSchema } from '@fastgpt/global/support/user/team/type';
import { MongoTeamMember } from '../../user/team/teamMemberSchema';
import { checkTeamAIPoints } from '../teamLimit';
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
import { UserModelSchema } from '@fastgpt/global/support/user/type';
import { TeamSchema } from '@fastgpt/global/support/user/team/type';
export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
const tmb = (await MongoTeamMember.findById(tmbId, 'teamId userId').populate(
'userId',
'timezone openaiAccount'
)) as TeamMemberWithUserSchema;
const tmb = await MongoTeamMember.findById(tmbId, 'userId teamId')
.populate<{ user: UserModelSchema; team: TeamSchema }>([
{
path: 'user',
select: 'timezone'
},
{
path: 'team',
select: 'openaiAccount externalWorkflowVariables'
}
])
.lean();
if (!tmb) return Promise.reject(UserErrEnum.unAuthUser);
await checkTeamAIPoints(tmb.teamId);
await checkTeamAIPoints(tmb.team._id);
return {
user: tmb.userId
timezone: tmb.user.timezone,
externalProvider: {
openaiAccount: tmb.team.openaiAccount,
externalWorkflowVariables: tmb.team.externalWorkflowVariables
}
};
}

View File

@@ -10,17 +10,17 @@ import { MongoResourcePermission } from './schema';
import { ClientSession } from 'mongoose';
import {
PermissionValueType,
ResourcePermissionType,
ResourcePerWithGroup,
ResourcePerWithTmbWithUser
ResourcePermissionType
} from '@fastgpt/global/support/permission/type';
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
import { addMinutes } from 'date-fns';
import { getGroupsByTmbId } from './memberGroup/controllers';
import { Permission } from '@fastgpt/global/support/permission/controller';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
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';
/** get resource permission for a team member
* If there is no permission for the team member, it will return undefined
@@ -160,32 +160,33 @@ export const getClbsAndGroupsWithInfo = async ({
teamId: string;
}) =>
Promise.all([
(await MongoResourcePermission.find({
MongoResourcePermission.find({
teamId,
resourceId,
resourceType,
tmbId: {
$exists: true
}
}).populate({
path: 'tmbId',
select: 'name userId',
populate: {
path: 'userId',
select: 'avatar'
}
})) as ResourcePerWithTmbWithUser[],
(await MongoResourcePermission.find({
})
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({
path: 'tmb',
select: 'name userId',
populate: {
path: 'user',
select: 'avatar'
}
})
.lean(),
MongoResourcePermission.find({
teamId,
resourceId,
resourceType,
groupId: {
$exists: true
}
}).populate({
path: 'groupId',
select: 'name avatar'
})) as ResourcePerWithGroup[]
})
.populate<{ group: MemberGroupSchemaType }>('group', 'name avatar')
.lean()
]);
export const delResourcePermissionById = (id: string) => {

View File

@@ -3,7 +3,6 @@ import { getResourcePermission, parseHeaderCert } from '../controller';
import {
CollectionWithDatasetType,
DatasetDataItemType,
DatasetFileSchema,
DatasetSchemaType
} from '@fastgpt/global/core/dataset/type';
import { getTmbInfoByTmbId } from '../../user/team/controller';
@@ -12,10 +11,6 @@ import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/per
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
import { getCollectionWithDataset } from '../../../core/dataset/controller';
import { MongoDatasetCollection } from '../../../core/dataset/collection/schema';
import { getFileById } from '../../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { MongoDatasetData } from '../../../core/dataset/data/schema';
import { AuthModeType, AuthResponseType } from '../type';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
@@ -181,7 +176,7 @@ export async function authDatasetCollection({
const { dataset } = await authDatasetByTmbId({
tmbId,
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
per,
isRoot: isRootFromHeader
});

View File

@@ -66,48 +66,19 @@ export const getGroupsByTmbId = async ({
},
...(role ? { role: { $in: role } } : {})
})
.populate('groupId')
.populate<{ group: MemberGroupSchemaType }>('group')
.lean()
).map((item) => {
return {
...(item.groupId as any as MemberGroupSchemaType)
};
}),
).map((item) => item.group),
role ? [] : getTeamDefaultGroup({ teamId })
])
).flat();
export const getTmbByGroupId = async (groupId: string) => {
return (
await MongoGroupMemberModel.find({
groupId
})
.populate('tmbId')
.lean()
).map((item) => {
return {
...(item.tmbId as any as MemberGroupSchemaType)
};
});
};
export const getGroupMembersByGroupId = async (groupId: string) => {
return await MongoGroupMemberModel.find({
groupId
}).lean();
};
export const getGroupMembersWithInfoByGroupId = async (groupId: string) => {
return (
await MongoGroupMemberModel.find({
groupId
})
.populate('tmbId')
.lean()
).map((item) => item.tmbId) as any as TeamMemberSchema[]; // HACK: type casting
};
/**
* Get tmb's group permission: the maximum permission of the group
* @param tmbId

View File

@@ -26,6 +26,13 @@ export const GroupMemberSchema = new Schema({
}
});
GroupMemberSchema.virtual('group', {
ref: MemberGroupCollectionName,
localField: 'groupId',
foreignField: '_id',
justOne: true
});
try {
GroupMemberSchema.index({
groupId: 1

View File

@@ -39,6 +39,19 @@ export const ResourcePermissionSchema = new Schema({
}
});
ResourcePermissionSchema.virtual('tmb', {
ref: TeamMemberCollectionName,
localField: 'tmbId',
foreignField: '_id',
justOne: true
});
ResourcePermissionSchema.virtual('group', {
ref: MemberGroupCollectionName,
localField: 'groupId',
foreignField: '_id',
justOne: true
});
try {
ResourcePermissionSchema.index(
{

View File

@@ -44,7 +44,6 @@ export async function getUserDetail({
avatar: user.avatar,
timezone: user.timezone,
promotionRate: user.promotionRate,
openaiAccount: user.openaiAccount,
team: tmb,
notificationAccount: tmb.notificationAccount,
permission: tmb.permission

View File

@@ -1,4 +1,4 @@
import { TeamTmbItemType, TeamMemberWithTeamSchema } from '@fastgpt/global/support/user/team/type';
import { TeamSchema, TeamTmbItemType } from '@fastgpt/global/support/user/team/type';
import { ClientSession, Types } from '../../../common/mongo';
import {
TeamMemberRoleEnum,
@@ -15,37 +15,41 @@ import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/use
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import { getAIApi, openaiBaseUrl } from '../../../core/ai/config';
async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemType> {
const tmb = (await MongoTeamMember.findOne(match).populate('teamId')) as TeamMemberWithTeamSchema;
const tmb = await MongoTeamMember.findOne(match).populate<{ team: TeamSchema }>('team').lean();
if (!tmb) {
return Promise.reject('member not exist');
}
const Per = await getResourcePermission({
resourceType: PerResourceTypeEnum.team,
teamId: tmb.teamId._id,
teamId: tmb.teamId,
tmbId: tmb._id
});
return {
userId: String(tmb.userId),
teamId: String(tmb.teamId._id),
teamName: tmb.teamId.name,
teamId: String(tmb.teamId),
teamName: tmb.team.name,
memberName: tmb.name,
avatar: tmb.teamId.avatar,
balance: tmb.teamId.balance,
avatar: tmb.team.avatar,
balance: tmb.team.balance,
tmbId: String(tmb._id),
teamDomain: tmb.teamId?.teamDomain,
teamDomain: tmb.team?.teamDomain,
role: tmb.role,
status: tmb.status,
defaultTeam: tmb.defaultTeam,
lafAccount: tmb.teamId.lafAccount,
permission: new TeamPermission({
per: Per ?? TeamDefaultPermissionVal,
isOwner: tmb.role === TeamMemberRoleEnum.owner
}),
notificationAccount: tmb.teamId.notificationAccount
notificationAccount: tmb.team.notificationAccount,
lafAccount: tmb.team.lafAccount,
openaiAccount: tmb.team.openaiAccount,
externalWorkflowVariables: tmb.team.externalWorkflowVariables
};
}
@@ -145,21 +149,87 @@ export async function updateTeam({
name,
avatar,
teamDomain,
lafAccount
lafAccount,
openaiAccount,
externalWorkflowVariable
}: UpdateTeamProps & { teamId: string }) {
// auth openai key
if (openaiAccount?.key) {
console.log('auth user openai key', openaiAccount?.key);
const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl;
openaiAccount.baseUrl = baseUrl;
const ai = getAIApi({
userKey: openaiAccount
});
const response = await ai.chat.completions.create({
model: 'gpt-4o-mini',
max_tokens: 1,
messages: [{ role: 'user', content: 'hi' }]
});
if (response?.choices?.[0]?.message?.content === undefined) {
return Promise.reject('Key response is empty');
}
}
return mongoSessionRun(async (session) => {
const unsetObj = (() => {
const obj: Record<string, 1> = {};
if (lafAccount?.pat === '') {
obj.lafAccount = 1;
}
if (openaiAccount?.key === '') {
obj.openaiAccount = 1;
}
if (externalWorkflowVariable) {
if (externalWorkflowVariable.value === '') {
obj[`externalWorkflowVariables.${externalWorkflowVariable.key}`] = 1;
}
}
if (Object.keys(obj).length === 0) {
return undefined;
}
return {
$unset: obj
};
})();
const setObj = (() => {
const obj: Record<string, any> = {};
if (lafAccount?.pat && lafAccount?.appid) {
obj.lafAccount = lafAccount;
}
if (openaiAccount?.key && openaiAccount?.baseUrl) {
obj.openaiAccount = openaiAccount;
}
if (externalWorkflowVariable) {
if (externalWorkflowVariable.value !== '') {
obj[`externalWorkflowVariables.${externalWorkflowVariable.key}`] =
externalWorkflowVariable.value;
}
}
if (Object.keys(obj).length === 0) {
return undefined;
}
return obj;
})();
await MongoTeam.findByIdAndUpdate(
teamId,
{
name,
avatar,
teamDomain,
lafAccount
$set: {
...(name ? { name } : {}),
...(avatar ? { avatar } : {}),
...(teamDomain ? { teamDomain } : {}),
...setObj
},
...unsetObj
},
{ session }
);
// update default group
// Update member group avatar
if (avatar) {
await MongoMemberGroupModel.updateOne(
{

View File

@@ -3,7 +3,6 @@ const { Schema } = connectionMongo;
import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
import {
TeamMemberRoleMap,
TeamMemberStatusMap,
TeamMemberCollectionName,
TeamCollectionName
@@ -42,6 +41,19 @@ const TeamMemberSchema = new Schema({
}
});
TeamMemberSchema.virtual('team', {
ref: TeamCollectionName,
localField: 'teamId',
foreignField: '_id',
justOne: true
});
TeamMemberSchema.virtual('user', {
ref: userCollectionName,
localField: 'userId',
foreignField: '_id',
justOne: true
});
try {
TeamMemberSchema.index({ teamId: 1 }, { background: true });
TeamMemberSchema.index({ userId: 1 }, { background: true });

View File

@@ -47,6 +47,16 @@ const TeamSchema = new Schema({
type: String
}
},
openaiAccount: {
type: {
key: String,
baseUrl: String
}
},
externalWorkflowVariables: {
type: Object,
default: {}
},
notificationAccount: {
type: String,
required: false

View File

@@ -9,7 +9,6 @@ import {
import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type';
import { WorkerNameEnum, WorkerPool } from './worker/utils';
import { Worker } from 'worker_threads';
import { TemplateMarketItemType } from '@fastgpt/global/core/workflow/type';
declare global {
var systemInitBufferId: string | undefined;
@@ -25,5 +24,4 @@ declare global {
var reRankModels: ReRankModelItemType[];
var workerPoll: Record<WorkerNameEnum, WorkerPool>;
var appMarketTemplates: TemplateMarketItemType[];
}

View File

@@ -0,0 +1,3 @@
# Package 说明
该 package 存放应用模板。

View File

@@ -0,0 +1,10 @@
{
"name": "@fastgpt/templates",
"version": "1.0.0",
"type": "module",
"dependencies": {},
"devDependencies": {
"@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*"
}
}

View File

@@ -0,0 +1,83 @@
import fs from 'fs';
import path from 'path';
import { isProduction } from '@fastgpt/global/common/system/constants';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { MongoAppTemplate } from '@fastgpt/service/core/app/templates/templateSchema';
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
const getTemplateNameList = () => {
const currentFileUrl = new URL(import.meta.url);
const templatesPath = path.join(path.dirname(currentFileUrl.pathname), 'src');
return fs.readdirSync(templatesPath) as string[];
};
const getFileTemplates = (): AppTemplateSchemaType[] => {
const templateNames = getTemplateNameList();
const appMarketTemplates = templateNames.map((name) => {
const fileContent = require(`./src/${name}/template.json`);
return {
...fileContent,
templateId: `${PluginSourceEnum.community}-${name}`,
isActive: true
};
});
return appMarketTemplates;
};
const getAppTemplates = async () => {
const communityTemplates = getFileTemplates();
const dbTemplates = await MongoAppTemplate.find();
// Merge db data to community templates
const communityTemplateConfig = communityTemplates.map((template) => {
const config = dbTemplates.find((t) => t.templateId === template.templateId);
if (config) {
return {
...template,
isActive: config.isActive ?? template.isActive,
tags: config.tags ?? template.tags,
userGuide: config.userGuide ?? template.userGuide,
isQuickTemplate: config.isQuickTemplate ?? template.isQuickTemplate,
order: config.order ?? template.order
};
}
return template;
});
const res = [
...communityTemplateConfig,
...dbTemplates.filter((t) => !isCommunityTemplate(t.templateId))
].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
return res;
};
export const getAppTemplatesAndLoadThem = async (refresh = false) => {
if (isProduction && global.appTemplates && global.appTemplates.length > 0 && !refresh)
return global.appTemplates;
if (!global.appTemplates) {
global.appTemplates = [];
}
try {
const appTemplates = await getAppTemplates();
global.appTemplates = appTemplates;
return appTemplates;
} catch (error) {
// @ts-ignore
global.appTemplates = undefined;
return [];
}
};
export const isCommunityTemplate = (templateId: string) => {
return templateId.startsWith(PluginSourceEnum.community);
};

View File

@@ -2,9 +2,13 @@
"name": "汉语新解",
"intro": "生成汉语释义图",
"author": "",
"avatar": "/appMarketTemplates/Chinese/avatar.svg",
"avatar": "core/app/templates/chinese",
"tags": ["roleplay"],
"type": "advanced",
"userGuide": {
"type": "link",
"content": "https://mp.weixin.qq.com/s/T90-uZqGovYR90fST0o80Q"
},
"workflow": {
"nodes": [
{

View File

@@ -2,7 +2,7 @@
"name": "多轮翻译机器人",
"intro": "通过 4 轮翻译,提高翻译英文的质量",
"author": "",
"avatar": "/appMarketTemplates/TranslateRobot/avatar.svg",
"avatar": "core/app/templates/TranslateRobot",
"tags": ["office-services"],
"type": "advanced",
"workflow": {

View File

@@ -2,7 +2,7 @@
"name": "动物的一生",
"intro": "使用AI生成任何事物的 “人生图”",
"author": "",
"avatar": "/appMarketTemplates/animalLife/avatar.svg",
"avatar": "core/app/templates/animalLife",
"tags": ["roleplay"],
"type": "advanced",
"workflow": {

View File

@@ -2,7 +2,7 @@
"name": "周易占卜",
"intro": "AI占卜,快来算算你的运势~",
"author": "",
"avatar": "/appMarketTemplates/divination/avatar.svg",
"avatar": "core/app/templates/divination",
"tags": ["roleplay"],
"type": "advanced",
"workflow": {

View File

@@ -2,7 +2,7 @@
"name": "Flux 绘图",
"intro": "通过请求 Flux 接口绘图,需要有 api key",
"author": "",
"avatar": "/appMarketTemplates/flux/avatar.svg",
"avatar": "core/app/templates/flux",
"type": "plugin",
"tags": ["image-generation"],
"workflow": {

View File

@@ -2,9 +2,13 @@
"name": "GitHub Issue 总结机器人",
"intro": "定时获取GitHub Issue信息,使用AI进行总结,并推送到飞书群中",
"author": "",
"avatar": "/appMarketTemplates/githubIssue/avatar.svg",
"avatar": "core/app/templates/githubIssue",
"tags": ["office-services"],
"type": "advanced",
"userGuide": {
"type": "link",
"content": "https://mp.weixin.qq.com/s/CBrwSn1jQZO7ybsMSx5GnQ"
},
"workflow": {
"nodes": [
{

View File

@@ -2,7 +2,7 @@
"name": "谷歌搜索",
"intro": "通过请求谷歌搜索,查询相关内容作为模型的参考。",
"author": "",
"avatar": "/appMarketTemplates/google/avatar.svg",
"avatar": "core/app/templates/google",
"tags": ["recommendation", "web-search"],
"type": "advanced",
"workflow": {

View File

@@ -1,10 +1,14 @@
{
"name": "长文翻译专家",
"intro": "使用专有名知识库协助翻译,更适合长文本的翻译机器人",
"intro": "使用专有名知识库协助翻译,更适合长文本的翻译机器人",
"author": "",
"avatar": "/appMarketTemplates/TranslateRobot/avatar.svg",
"avatar": "core/app/templates/TranslateRobot",
"tags": ["office-services"],
"type": "advanced",
"userGuide": {
"type": "link",
"content": "https://mp.weixin.qq.com/s/2kXdaSLOImhH1DTaFU8qXA"
},
"workflow": {
"nodes": [
{

View File

@@ -2,7 +2,7 @@
"name": "Dalle3 绘图",
"intro": "通过请求 Dalle3 接口绘图,需要有 api key",
"author": "",
"avatar": "/appMarketTemplates/plugin-dalle/avatar.svg",
"avatar": "core/app/templates/plugin-dalle",
"type": "plugin",
"tags": ["recommendation", "image-generation"],
"workflow": {

View File

@@ -2,7 +2,7 @@
"name": "飞书 webhook 插件",
"intro": "通过 webhook 给飞书机器人发送一条消息",
"author": "",
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg",
"avatar": "core/app/templates/plugin-feishu",
"type": "plugin",
"tags": ["recommendation", "office-services"],
"workflow": {

View File

@@ -2,9 +2,13 @@
"name": "长字幕反思翻译机器人",
"intro": "利用 AI 自我反思提升翻译质量,同时循环迭代执行 AI 工作流来突破 LLM tokens 限制,实现一个高效的长字幕翻译机器人",
"author": "",
"avatar": "/appMarketTemplates/TranslateRobot/avatar.svg",
"avatar": "core/app/templates/TranslateRobot",
"tags": ["office-services"],
"type": "advanced",
"userGuide": {
"type": "link",
"content": "https://mp.weixin.qq.com/s/uztciEVvumNWNlct0IeJ-w"
},
"workflow": {
"nodes": [
{

View File

@@ -2,7 +2,7 @@
"name": "利好大A",
"intro": "",
"author": "",
"avatar": "/appMarketTemplates/stock/avatar.svg",
"avatar": "core/app/templates/stock",
"tags": ["roleplay"],
"type": "advanced",
"workflow": {

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es2015",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": "."
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../**/*.d.ts"],
"exclude": ["node_modules"]
}

5
packages/templates/type.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
declare global {
var appTemplates: AppTemplateSchemaType[];
}

View File

@@ -28,4 +28,4 @@ const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
);
};
export default Avatar;
export default React.memo(Avatar);

View File

@@ -57,4 +57,4 @@ function DndDrag<T>({ children, renderClone, onDragEndCb, dataList }: Props<T>)
);
}
export default DndDrag;
export default React.memo(DndDrag) as <T>(props: Props<T>) => React.ReactElement;

View File

@@ -17,6 +17,7 @@ export const iconPaths = {
'common/addUser': () => import('./icons/common/addUser.svg'),
'common/administrator': () => import('./icons/common/administrator.svg'),
'common/arrowLeft': () => import('./icons/common/arrowLeft.svg'),
'common/arrowRight': () => import('./icons/common/arrowRight.svg'),
'common/backFill': () => import('./icons/common/backFill.svg'),
'common/backLight': () => import('./icons/common/backLight.svg'),
'common/billing': () => import('./icons/common/billing.svg'),
@@ -48,6 +49,7 @@ export const iconPaths = {
'common/language/China': () => import('./icons/common/language/China.svg'),
'common/language/en': () => import('./icons/common/language/en.svg'),
'common/language/zh': () => import('./icons/common/language/zh.svg'),
'common/layer': () => import('./icons/common/layer.svg'),
'common/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'),
'common/line': () => import('./icons/common/line.svg'),
'common/lineChange': () => import('./icons/common/lineChange.svg'),
@@ -81,13 +83,16 @@ export const iconPaths = {
'common/settingLight': () => import('./icons/common/settingLight.svg'),
'common/solidChevronRight': () => import('./icons/common/solidChevronRight.svg'),
'common/subtract': () => import('./icons/common/subtract.svg'),
'common/templateMarket': () => import('./icons/common/templateMarket.svg'),
'common/text/t': () => import('./icons/common/text/t.svg'),
'common/thirdParty': () => import('./icons/common/thirdParty.svg'),
'common/tickFill': () => import('./icons/common/tickFill.svg'),
'common/toolkit': () => import('./icons/common/toolkit.svg'),
'common/trash': () => import('./icons/common/trash.svg'),
'common/upRightArrowLight': () => import('./icons/common/upRightArrowLight.svg'),
'common/uploadFileFill': () => import('./icons/common/uploadFileFill.svg'),
'common/userInfo': () => import('./icons/common/userInfo.svg'),
'common/variable': () => import('./icons/common/variable.svg'),
'common/viewLight': () => import('./icons/common/viewLight.svg'),
'common/voiceLight': () => import('./icons/common/voiceLight.svg'),
'common/warn': () => import('./icons/common/warn.svg'),
@@ -119,6 +124,17 @@ export const iconPaths = {
'core/app/simpleMode/tts': () => import('./icons/core/app/simpleMode/tts.svg'),
'core/app/simpleMode/variable': () => import('./icons/core/app/simpleMode/variable.svg'),
'core/app/simpleMode/whisper': () => import('./icons/core/app/simpleMode/whisper.svg'),
'core/app/templates/TranslateRobot': () =>
import('./icons/core/app/templates/TranslateRobot.svg'),
'core/app/templates/animalLife': () => import('./icons/core/app/templates/animalLife.svg'),
'core/app/templates/chinese': () => import('./icons/core/app/templates/chinese.svg'),
'core/app/templates/divination': () => import('./icons/core/app/templates/divination.svg'),
'core/app/templates/flux': () => import('./icons/core/app/templates/flux.svg'),
'core/app/templates/githubIssue': () => import('./icons/core/app/templates/githubIssue.svg'),
'core/app/templates/google': () => import('./icons/core/app/templates/google.svg'),
'core/app/templates/plugin-dalle': () => import('./icons/core/app/templates/plugin-dalle.svg'),
'core/app/templates/plugin-feishu': () => import('./icons/core/app/templates/plugin-feishu.svg'),
'core/app/templates/stock': () => import('./icons/core/app/templates/stock.svg'),
'core/app/toolCall': () => import('./icons/core/app/toolCall.svg'),
'core/app/ttsFill': () => import('./icons/core/app/ttsFill.svg'),
'core/app/type/httpPlugin': () => import('./icons/core/app/type/httpPlugin.svg'),
@@ -376,6 +392,7 @@ export const iconPaths = {
'price/right': () => import('./icons/price/right.svg'),
save: () => import('./icons/save.svg'),
stop: () => import('./icons/stop.svg'),
'support/account/laf': () => import('./icons/support/account/laf.svg'),
'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'),
'support/account/plans': () => import('./icons/support/account/plans.svg'),
'support/account/promotionLight': () => import('./icons/support/account/promotionLight.svg'),

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.86193 3.5286C8.60158 3.78895 8.60158 4.21106 8.86193 4.47141L11.7239 7.33334H2.66667C2.29848 7.33334 2 7.63181 2 8C2 8.36819 2.29848 8.66667 2.66667 8.66667H11.7239L8.86193 11.5286C8.60158 11.7889 8.60158 12.2111 8.86193 12.4714C9.12228 12.7318 9.54439 12.7318 9.80474 12.4714L13.8047 8.47141C14.0651 8.21106 14.0651 7.78895 13.8047 7.5286L9.80474 3.5286C9.54439 3.26825 9.12228 3.26825 8.86193 3.5286Z"/>
</svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.79789 1.3894C8.93146 1.36434 9.06853 1.36434 9.2021 1.3894C9.36089 1.41919 9.5023 1.49151 9.56594 1.52407C9.57246 1.5274 9.57817 1.53032 9.58299 1.53273L16.2556 4.86906C16.5097 4.9961 16.6702 5.2558 16.6702 5.53988C16.6702 5.82396 16.5097 6.08366 16.2556 6.2107L9.58299 9.54703C9.57817 9.54944 9.57246 9.55236 9.56594 9.5557C9.5023 9.58825 9.36089 9.66058 9.2021 9.69037C9.06853 9.71543 8.93146 9.71543 8.79789 9.69037C8.6391 9.66058 8.4977 9.58825 8.43405 9.5557C8.42753 9.55236 8.42182 9.54944 8.417 9.54703L1.74434 6.2107C1.49026 6.08366 1.32975 5.82396 1.32975 5.53988C1.32975 5.2558 1.49026 4.9961 1.74434 4.86906L8.417 1.53273C8.42182 1.53032 8.42753 1.5274 8.43405 1.52407C8.49769 1.49151 8.6391 1.41919 8.79789 1.3894ZM9 2.91829L3.7568 5.53988L9 8.16148L14.2432 5.53988L9 2.91829ZM1.40893 8.66459C1.59418 8.29411 2.04468 8.14394 2.41516 8.32918L9 11.6216L15.5848 8.32918C15.9553 8.14394 16.4058 8.29411 16.5911 8.66459C16.7763 9.03508 16.6261 9.48558 16.2556 9.67082L9.58299 13.0072C9.57817 13.0096 9.57246 13.0125 9.56594 13.0158C9.5023 13.0484 9.36089 13.1207 9.2021 13.1505C9.06853 13.1755 8.93146 13.1755 8.79789 13.1505C8.6391 13.1207 8.4977 13.0484 8.43405 13.0158C8.42753 13.0125 8.42182 13.0096 8.417 13.0072L1.74434 9.67082C1.37386 9.48558 1.22369 9.03508 1.40893 8.66459ZM1.40893 12.1247C1.59418 11.7542 2.04468 11.6041 2.41516 11.7893L9 15.0817L15.5848 11.7893C15.9553 11.6041 16.4058 11.7542 16.5911 12.1247C16.7763 12.4952 16.6261 12.9457 16.2556 13.1309L9.58299 16.4673C9.57817 16.4697 9.57247 16.4726 9.56595 16.4759C9.5023 16.5085 9.36089 16.5808 9.2021 16.6106C9.06853 16.6357 8.93146 16.6357 8.79789 16.6106C8.6391 16.5808 8.49769 16.5085 8.43405 16.4759C8.42753 16.4726 8.42182 16.4697 8.417 16.4673L1.74434 13.1309C1.37386 12.9457 1.22369 12.4952 1.40893 12.1247Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" >
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.4583 7.13094L6.60858 6.68746C6.3905 6.63518 6.16154 6.60713 5.92405 6.60713C4.3115 6.60713 3.00427 7.91436 3.00427 9.52691C3.00427 11.1395 4.3115 12.4467 5.92405 12.4467C7.24744 12.4467 8.36881 11.5651 8.72548 10.3539L9.04242 9.2776H13.8426L13.8426 3.93535L8.4583 3.93535V7.13094ZM5.4583 5.13138C3.23625 5.3641 1.50427 7.24324 1.50427 9.52691C1.50427 11.9679 3.48308 13.9467 5.92405 13.9467C7.93065 13.9467 9.62493 12.6095 10.1644 10.7776H13.8426C14.6711 10.7776 15.3426 10.106 15.3426 9.2776V3.93535C15.3426 3.10692 14.6711 2.43535 13.8426 2.43535H8.4583C7.70971 2.43535 7.08919 2.98372 6.97655 3.70068C6.96454 3.77714 6.9583 3.85552 6.9583 3.93535V5.2288C6.62653 5.14926 6.28021 5.10713 5.92405 5.10713C5.76675 5.10713 5.61136 5.11535 5.4583 5.13138Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.1475 9.2776L8.10266 14.5515H14.9726L12.3191 9.95547L13.8417 9.27845C13.8419 9.27828 13.8425 9.27776 13.8426 9.2776C13.8426 9.27913 13.8426 9.27969 13.8426 9.27947L13.8426 9.2776C13.8426 9.27778 13.8427 9.27737 13.8426 9.2776C13.8428 9.27708 13.8435 9.27675 13.8435 9.2766C13.8436 9.27654 13.8436 9.2765 13.8435 9.2766C13.8435 9.27675 13.8428 9.27708 13.8426 9.2776L13.8426 3.93535L8.4583 3.93535L8.45832 9.2776H11.1475ZM14.7236 10.4918C15.0988 10.2191 15.3426 9.77685 15.3426 9.2776V3.93535C15.3426 3.10692 14.6711 2.43535 13.8426 2.43535H8.4583C7.62987 2.43535 6.9583 3.10692 6.9583 3.93535L6.95832 9.2776C6.95832 9.58297 7.04957 9.86703 7.2063 10.104C7.47474 10.5099 7.93527 10.7776 8.45832 10.7776H8.54944L6.80362 13.8015C6.22627 14.8015 6.94796 16.0515 8.10266 16.0515H14.9726C16.1273 16.0515 16.849 14.8015 16.2716 13.8015L14.4515 10.6489C14.548 10.606 14.6391 10.5532 14.7236 10.4918Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.8426 3.93535L8.4583 3.93535L8.45832 9.2776H13.8426L13.8426 3.93535ZM8.4583 2.43535C7.62987 2.43535 6.9583 3.10692 6.9583 3.93535L6.95832 9.2776C6.95832 10.106 7.62989 10.7776 8.45832 10.7776H13.8426C14.6711 10.7776 15.3426 10.106 15.3426 9.2776V3.93535C15.3426 3.10692 14.6711 2.43535 13.8426 2.43535H8.4583Z" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
<path d="M7.275 3.39747C7.275 2.44478 8.04731 1.67247 9 1.67247C9.95269 1.67247 10.725 2.44478 10.725 3.39747C10.725 4.35016 9.95269 5.12247 9 5.12247C8.04731 5.12247 7.275 4.35016 7.275 3.39747Z"/>
<path d="M13.05 12.7336C13.05 11.7809 13.8223 11.0086 14.775 11.0086C15.7277 11.0086 16.5 11.7809 16.5 12.7336C16.5 13.6863 15.7277 14.4586 14.775 14.4586C13.8223 14.4586 13.05 13.6863 13.05 12.7336Z" />
<path d="M1.5 12.7336C1.5 11.7809 2.27231 11.0086 3.225 11.0086C4.17769 11.0086 4.95 11.7809 4.95 12.7336C4.95 13.6863 4.17769 14.4586 3.225 14.4586C2.27231 14.4586 1.5 13.6863 1.5 12.7336Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.47577 4.59792C6.28566 4.20775 5.81199 4.04174 5.44739 4.27718C4.59257 4.82918 3.87495 5.57494 3.35537 6.45819C2.83579 7.34145 2.53261 8.331 2.46543 9.34633C2.43677 9.7794 2.812 10.1128 3.24539 10.0894C3.67878 10.066 4.0047 9.69385 4.04974 9.26218C4.12336 8.55661 4.34733 7.87177 4.71008 7.25511C5.07284 6.63845 5.56261 6.10997 6.14355 5.70284C6.49897 5.45374 6.66587 4.98809 6.47577 4.59792ZM14.7543 10.0953C15.1877 10.1191 15.5632 9.78614 15.535 9.35303C15.4689 8.33764 15.1667 7.34777 14.648 6.46399C14.1294 5.5802 13.4125 4.83371 12.5583 4.28083C12.1939 4.04501 11.7201 4.21054 11.5295 4.60051C11.339 4.99049 11.5055 5.45631 11.8606 5.70577C12.4412 6.11351 12.9304 6.64248 13.2925 7.25951C13.6546 7.87655 13.8779 8.56161 13.9508 9.26726C13.9954 9.69898 14.3209 10.0714 14.7543 10.0953ZM6.03643 15.6186C5.64939 15.4222 5.55425 14.9294 5.79558 14.5686C6.03691 14.2079 6.52325 14.1176 6.91745 14.2992C7.56177 14.596 8.26499 14.753 8.98043 14.7558C9.69587 14.7586 10.4003 14.6072 11.0469 14.3154C11.4426 14.1369 11.9282 14.2311 12.1667 14.5937C12.4051 14.9563 12.3061 15.4484 11.9176 15.6417C11.0066 16.0951 9.99898 16.3315 8.97425 16.3275C7.94951 16.3235 6.94382 16.0791 6.03643 15.6186Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" >
<path d="M2.40921 14.9955C2.74759 15.3952 3.28934 15.595 4.03446 15.595V14.1936C3.74393 14.1936 3.53373 14.0912 3.40384 13.8863C3.27738 13.6815 3.21415 13.3342 3.21415 12.8446V11.3083C3.21415 10.0842 2.95096 9.31726 2.42459 9.00749V8.97752C2.95096 8.66275 3.21415 7.90583 3.21415 6.70673V5.11044C3.21415 4.24109 3.48758 3.80642 4.03446 3.80642V2.40498C3.28251 2.40498 2.73905 2.61482 2.40409 3.03451C2.06913 3.44919 1.90165 4.17115 1.90165 5.20037V6.87161C1.90165 7.82589 1.64188 8.30302 1.12235 8.30302V9.68198C1.64188 9.68198 1.90165 10.1416 1.90165 11.0609V12.8596C1.90165 13.8838 2.07083 14.5958 2.40921 14.9955Z"/>
<path d="M15.5908 14.9955C15.2524 15.3952 14.7107 15.595 13.9655 15.595V14.1936C14.2561 14.1936 14.4663 14.0912 14.5962 13.8863C14.7226 13.6815 14.7859 13.3342 14.7859 12.8446V11.3083C14.7859 10.0842 15.049 9.31726 15.5754 9.00749V8.97752C15.049 8.66275 14.7859 7.90583 14.7859 6.70673V5.11044C14.7859 4.24109 14.5124 3.80642 13.9655 3.80642V2.40498C14.7175 2.40498 15.261 2.61482 15.5959 3.03451C15.9309 3.44919 16.0984 4.17115 16.0984 5.20037V6.87161C16.0984 7.82589 16.3581 8.30302 16.8777 8.30302V9.68198C16.3581 9.68198 16.0984 10.1416 16.0984 11.0609V12.8596C16.0984 13.8838 15.9292 14.5958 15.5908 14.9955Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.12446 15.595C4.12446 15.6447 4.08416 15.685 4.03446 15.685C3.27447 15.685 2.7023 15.4809 2.34052 15.0536C1.98023 14.628 1.81165 13.8876 1.81165 12.8596V11.0609C1.81165 10.6068 1.74697 10.2807 1.62847 10.071C1.51423 9.86886 1.34901 9.77198 1.12235 9.77198C1.07264 9.77198 1.03235 9.73169 1.03235 9.68198V8.30302C1.03235 8.25332 1.07264 8.21303 1.12235 8.21303C1.23797 8.21303 1.33489 8.18665 1.4163 8.13681C1.49791 8.08683 1.56895 8.0102 1.62778 7.90214C1.74691 7.68331 1.81165 7.34353 1.81165 6.87161V5.20037C1.81165 4.16717 1.97862 3.41818 2.33391 2.97816C2.69205 2.52958 3.26679 2.31498 4.03446 2.31498C4.08416 2.31498 4.12446 2.35528 4.12446 2.40498V3.80642C4.12446 3.85613 4.08416 3.89642 4.03446 3.89642C3.78954 3.89642 3.6144 3.99118 3.4954 4.18035C3.372 4.37651 3.30415 4.68229 3.30415 5.11044V6.70673C3.30415 7.84046 3.07112 8.62051 2.56503 8.99216C2.80368 9.1661 2.98111 9.43386 3.10188 9.78579C3.2379 10.1822 3.30415 10.6908 3.30415 11.3083V12.8446C3.30415 13.3312 3.36786 13.6563 3.48014 13.8386C3.59008 14.0116 3.76791 14.1036 4.03446 14.1036C4.08416 14.1036 4.12446 14.1439 4.12446 14.1936V15.595ZM3.40384 13.8863C3.27738 13.6815 3.21415 13.3342 3.21415 12.8446V11.3083C3.21415 10.1321 2.97117 9.37803 2.48522 9.04599C2.47453 9.03868 2.46373 9.03159 2.45281 9.02469C2.44349 9.01881 2.43408 9.01308 2.42459 9.00749V8.97752C2.43392 8.97194 2.44317 8.96622 2.45233 8.96037C2.46331 8.95335 2.47417 8.94613 2.48492 8.93871C2.97107 8.6031 3.21415 7.85911 3.21415 6.70673V5.11044C3.21415 4.24109 3.48758 3.80642 4.03446 3.80642V2.40498C4.00412 2.40498 3.97412 2.40532 3.94446 2.40601C3.23899 2.42226 2.72553 2.63176 2.40409 3.03451C2.06913 3.44919 1.90165 4.17115 1.90165 5.20037V6.87161C1.90165 7.82589 1.64188 8.30302 1.12235 8.30302V9.68198C1.64188 9.68198 1.90165 10.1416 1.90165 11.0609V12.8596C1.90165 13.8838 2.07083 14.5958 2.40921 14.9955C2.73382 15.3789 3.24556 15.5784 3.94446 15.594C3.97412 15.5947 4.00412 15.595 4.03446 15.595V14.1936C3.74393 14.1936 3.53373 14.0912 3.40384 13.8863ZM13.9655 15.685C13.9158 15.685 13.8755 15.6447 13.8755 15.595V14.1936C13.8755 14.1439 13.9158 14.1036 13.9655 14.1036C14.2321 14.1036 14.4099 14.0116 14.5199 13.8386C14.6321 13.6563 14.6959 13.3312 14.6959 12.8446V11.3083C14.6959 10.6908 14.7621 10.1822 14.8981 9.78579C15.0189 9.43386 15.1963 9.16609 15.435 8.99216C14.9289 8.6205 14.6959 7.84046 14.6959 6.70673V5.11044C14.6959 4.68229 14.628 4.37651 14.5046 4.18035C14.3856 3.99118 14.2105 3.89642 13.9655 3.89642C13.9158 3.89642 13.8755 3.85613 13.8755 3.80642V2.40498C13.8755 2.35528 13.9158 2.31498 13.9655 2.31498C14.7332 2.31498 15.3079 2.52957 15.6661 2.97811C16.0214 3.41813 16.1884 4.16713 16.1884 5.20037V6.87161C16.1884 7.34353 16.2531 7.68331 16.3722 7.90214C16.4311 8.0102 16.5021 8.08683 16.5837 8.13681C16.6651 8.18665 16.762 8.21303 16.8777 8.21303C16.9274 8.21303 16.9677 8.25332 16.9677 8.30302V9.68198C16.9677 9.73169 16.9274 9.77198 16.8777 9.77198C16.651 9.77198 16.4858 9.86886 16.3715 10.071C16.253 10.2807 16.1884 10.6068 16.1884 11.0609V12.8596C16.1884 13.8876 16.0198 14.628 15.6595 15.0536C15.2977 15.4809 14.7255 15.685 13.9655 15.685ZM15.5959 3.03451C15.2745 2.63176 14.761 2.42226 14.0556 2.40601C14.0259 2.40532 13.9959 2.40498 13.9655 2.40498V3.80642C14.5124 3.80642 14.7859 4.24109 14.7859 5.11044V6.70673C14.7859 7.85911 15.0289 8.6031 15.5151 8.93871C15.5258 8.94613 15.5367 8.95335 15.5477 8.96037C15.5568 8.96622 15.5661 8.97194 15.5754 8.97752V9.00749C15.5659 9.01308 15.5565 9.01881 15.5472 9.02469C15.5363 9.03158 15.5255 9.03868 15.5148 9.04599C15.0288 9.37802 14.7859 10.1321 14.7859 11.3083V12.8446C14.7859 13.3342 14.7226 13.6815 14.5962 13.8863C14.4663 14.0912 14.2561 14.1936 13.9655 14.1936V15.595C13.9959 15.595 14.0259 15.5947 14.0556 15.594C14.7544 15.5784 15.2662 15.3789 15.5908 14.9955C15.9292 14.5958 16.0984 13.8838 16.0984 12.8596V11.0609C16.0984 10.1416 16.3581 9.68198 16.8777 9.68198V8.30302C16.3581 8.30302 16.0984 7.82589 16.0984 6.87161V5.20037C16.0984 4.17115 15.9309 3.44919 15.5959 3.03451Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.81804 5.81801C6.11093 5.52512 6.58581 5.52512 6.8787 5.81801L9.00001 7.93932L11.1213 5.81801C11.4142 5.52512 11.8891 5.52512 12.182 5.81801C12.4749 6.1109 12.4749 6.58578 12.182 6.87867L10.0607 8.99998L12.182 11.1213C12.4749 11.4142 12.4749 11.8891 12.182 12.182C11.8891 12.4749 11.4142 12.4749 11.1213 12.182L9.00001 10.0606L6.87868 12.182C6.58579 12.4749 6.11091 12.4749 5.81802 12.182C5.52513 11.8891 5.52513 11.4142 5.81802 11.1213L7.93935 8.99998L5.81804 6.87867C5.52515 6.58578 5.52515 6.1109 5.81804 5.81801Z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.05 1.5C6.25106 1.5 1.55005 6.20101 1.55005 12C1.55005 17.799 6.25106 22.5 12.05 22.5C17.849 22.5 22.55 17.799 22.55 12C22.55 6.20101 17.849 1.5 12.05 1.5ZM4.45357 10.6776L5.23449 9.8967C6.52836 8.60283 8.62614 8.60282 9.92001 9.89669L15.3864 15.3631C13.6613 17.0883 10.8642 17.0883 9.13909 15.3631L4.45357 10.6776ZM13.0437 11.4585L13.8246 10.6776C14.8765 9.62569 16.4598 9.42898 17.7104 10.0875C18.2671 10.3806 18.8755 10.6306 19.4995 10.5504C19.4995 10.5504 19.0284 11.5308 18.6367 12.1034C17.8653 13.2312 16.1674 14.5822 16.1674 14.5822L13.0437 11.4585Z" fill="url(#paint0_linear_15358_11488)"/>
<defs>
<linearGradient id="paint0_linear_15358_11488" x1="12.05" y1="1.5" x2="12.05" y2="22.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#00BCA6"/>
<stop offset="1" stop-color="#00AF9A"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 978 B

View File

@@ -58,4 +58,4 @@ const MyNumberInput = (props: Props) => {
);
};
export default MyNumberInput;
export default React.memo(MyNumberInput);

View File

@@ -13,4 +13,4 @@ const SearchInput = (props: InputProps) => {
);
};
export default SearchInput;
export default React.memo(SearchInput);

View File

@@ -17,4 +17,4 @@ const MyBox = ({ text, isLoading, children, size, ...props }: Props, ref: any) =
);
};
export default forwardRef(MyBox);
export default React.memo(forwardRef(MyBox));

View File

@@ -44,4 +44,4 @@ const Loading = ({
);
};
export default Loading;
export default React.memo(Loading);

View File

@@ -77,4 +77,4 @@ const InputSlider = ({
);
};
export default InputSlider;
export default React.memo(InputSlider);

View File

@@ -15,4 +15,4 @@ const QuestionTip = ({ label, maxW, ...props }: Props) => {
);
};
export default QuestionTip;
export default React.memo(QuestionTip);

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