feat: more sub plan info;fix: emprt index (#4997)

* feat: more sub plan info

* fix: emprt index

* doc
This commit is contained in:
Archer
2025-06-10 19:01:10 +08:00
committed by GitHub
parent ccae96a981
commit a848c2e3ba
26 changed files with 333 additions and 166 deletions

View File

@@ -1,10 +1,10 @@
---
title: 'V4.9.11(进行中)'
title: 'V4.9.12(进行中)'
description: 'FastGPT V4.9.12 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 789
weight: 788
---
## 🚀 新增内容
@@ -17,7 +17,9 @@ weight: 789
1. 密码校验时,增加更多的特殊字符
2. 后端全量计算知识库 chunk 参数,避免自动模式下部分参数未正确使用默认值。
3. 将文本分块移至 worker 线程,避免阻塞。
4. 展示更多套餐用量信息。
## 🐛 修复
1. 自定义问答提取提示词被覆盖。
1. 自定义问答提取提示词被覆盖。
2. 模板导入时,存在空 indexes 时,导致数据插入失败。

View File

@@ -45,6 +45,9 @@ export type TeamSubSchema = {
nextMode: `${SubModeEnum}`;
currentSubLevel: StandardSubLevelEnum;
nextSubLevel: StandardSubLevelEnum;
maxTeamMember?: number;
maxApp?: number;
maxDataset?: number;
totalPoints: number;
surplusPoints: number;
@@ -52,7 +55,7 @@ export type TeamSubSchema = {
currentExtraDatasetSize: number;
};
export type FeTeamPlanStatusType = {
export type TeamPlanStatusType = {
[SubTypeEnum.standard]?: TeamSubSchema;
standardConstants?: TeamStandardSubPlanItemType;
@@ -61,5 +64,11 @@ export type FeTeamPlanStatusType = {
// standard + extra
datasetMaxSize: number;
usedDatasetSize: number;
};
export type ClientTeamPlanStatusType = TeamPlanStatusType & {
usedMember: number;
usedAppAmount: number;
usedDatasetSize: number;
usedDatasetIndexSize: number;
};

View File

@@ -18,7 +18,7 @@ import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import type { ClientSession } from '../../../common/mongo';
import { createOrGetCollectionTags } from './utils';
import { rawText2Chunks } from '../read';
import { checkDatasetLimit } from '../../../support/permission/teamLimit';
import { checkDatasetIndexLimit } from '../../../support/permission/teamLimit';
import { predictDataLimitLength } from '../../../../global/core/dataset/utils';
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
import { createTrainingUsage } from '../../../support/wallet/usage/controller';
@@ -166,7 +166,7 @@ export const createCollectionAndInsertData = async ({
})();
// 2. auth limit
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId,
insertLen: predictDataLimitLength(trainingMode, chunks)
});

View File

@@ -199,7 +199,7 @@ export const rawText2Chunks = async ({
.map((item) => ({
q: item[0] || '',
a: item[1] || '',
indexes: item.slice(2),
indexes: item.slice(2).filter((item) => item.trim()),
imageIdList
}))
.filter((item) => item.q || item.a);

View File

@@ -83,8 +83,7 @@ const TrainingDataSchema = new Schema({
enum: Object.values(DatasetDataIndexTypeEnum)
},
text: {
type: String,
required: true
type: String
}
}
],

View File

@@ -5,28 +5,9 @@ import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { SystemErrEnum } from '@fastgpt/global/common/error/code/system';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
export const checkDatasetLimit = async ({
teamId,
insertLen = 0
}: {
teamId: string;
insertLen?: number;
}) => {
const { standardConstants, totalPoints, usedPoints, datasetMaxSize, usedDatasetSize } =
await getTeamPlanStatus({ teamId });
if (!standardConstants) return;
if (usedDatasetSize + insertLen >= datasetMaxSize) {
return Promise.reject(TeamErrEnum.datasetSizeNotEnough);
}
if (usedPoints >= totalPoints) {
return Promise.reject(TeamErrEnum.aiPointsNotEnough);
}
return;
};
import { MongoTeamMember } from '../user/team/teamMemberSchema';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
import { getVectorCountByTeamId } from '../../common/vectorDB/controller';
export const checkTeamAIPoints = async (teamId: string) => {
const { standardConstants, totalPoints, usedPoints } = await getTeamPlanStatus({
@@ -45,6 +26,72 @@ export const checkTeamAIPoints = async (teamId: string) => {
};
};
export const checkTeamMemberLimit = async (teamId: string, newCount: number) => {
const [{ standardConstants }, memberCount] = await Promise.all([
getTeamStandPlan({
teamId
}),
MongoTeamMember.countDocuments({
teamId,
status: { $ne: TeamMemberStatusEnum.leave }
})
]);
if (standardConstants && newCount + memberCount > standardConstants.maxTeamMember) {
return Promise.reject(TeamErrEnum.teamOverSize);
}
};
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
const [{ standardConstants }, appCount] = await Promise.all([
getTeamStandPlan({ teamId }),
MongoApp.countDocuments({
teamId,
type: {
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
}
})
]);
if (standardConstants && appCount + amount >= standardConstants.maxAppAmount) {
return Promise.reject(TeamErrEnum.appAmountNotEnough);
}
// System check
if (global?.licenseData?.maxApps && typeof global?.licenseData?.maxApps === 'number') {
const totalApps = await MongoApp.countDocuments({
type: {
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
}
});
if (totalApps >= global.licenseData.maxApps) {
return Promise.reject(SystemErrEnum.licenseAppAmountLimit);
}
}
};
export const checkDatasetIndexLimit = async ({
teamId,
insertLen = 0
}: {
teamId: string;
insertLen?: number;
}) => {
const [{ standardConstants, totalPoints, usedPoints, datasetMaxSize }, usedDatasetIndexSize] =
await Promise.all([getTeamPlanStatus({ teamId }), getVectorCountByTeamId(teamId)]);
if (!standardConstants) return;
if (usedDatasetIndexSize + insertLen >= datasetMaxSize) {
return Promise.reject(TeamErrEnum.datasetSizeNotEnough);
}
if (usedPoints >= totalPoints) {
return Promise.reject(TeamErrEnum.aiPointsNotEnough);
}
return;
};
export const checkTeamDatasetLimit = async (teamId: string) => {
const [{ standardConstants }, datasetCount] = await Promise.all([
getTeamStandPlan({ teamId }),
@@ -74,30 +121,12 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
}
};
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
const [{ standardConstants }, appCount] = await Promise.all([
getTeamStandPlan({ teamId }),
MongoApp.countDocuments({
teamId,
type: {
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
}
})
]);
export const checkTeamWebSyncPermission = async (teamId: string) => {
const { standardConstants } = await getTeamStandPlan({
teamId
});
if (standardConstants && appCount + amount >= standardConstants.maxAppAmount) {
return Promise.reject(TeamErrEnum.appAmountNotEnough);
}
// System check
if (global?.licenseData?.maxApps && typeof global?.licenseData?.maxApps === 'number') {
const totalApps = await MongoApp.countDocuments({
type: {
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
}
});
if (totalApps >= global.licenseData.maxApps) {
return Promise.reject(SystemErrEnum.licenseAppAmountLimit);
}
if (standardConstants && !standardConstants?.permissionWebsiteSync) {
return Promise.reject(TeamErrEnum.websiteSyncNotEnough);
}
};

View File

@@ -52,6 +52,9 @@ const SubSchema = new Schema({
type: String,
enum: Object.values(StandardSubLevelEnum)
},
maxTeamMember: Number,
maxApp: Number,
maxDataset: Number,
// stand sub and extra points sub. Plan total points
totalPoints: {

View File

@@ -6,10 +6,9 @@ import {
} from '@fastgpt/global/support/wallet/sub/constants';
import { MongoTeamSub } from './schema';
import {
type FeTeamPlanStatusType,
type TeamPlanStatusType,
type TeamSubSchema
} from '@fastgpt/global/support/wallet/sub/type.d';
import { getVectorCountByTeamId } from '../../../common/vectorDB/controller';
import dayjs from 'dayjs';
import { type ClientSession } from '../../../common/mongo';
import { addMonths } from 'date-fns';
@@ -44,12 +43,21 @@ export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
const standardPlans = global.subPlans?.standard;
const standard = plans[0];
const standardConstants =
standard?.currentSubLevel && standardPlans
? standardPlans[standard.currentSubLevel]
: undefined;
return {
[SubTypeEnum.standard]: standard,
standardConstants:
standard?.currentSubLevel && standardPlans
? standardPlans[standard.currentSubLevel]
: undefined
standardConstants: standardConstants
? {
...standardConstants,
maxTeamMember: standard?.maxTeamMember || standardConstants.maxTeamMember,
maxAppAmount: standard?.maxApp || standardConstants.maxAppAmount,
maxDatasetAmount: standard?.maxDataset || standardConstants.maxDatasetAmount
}
: undefined
};
};
@@ -111,14 +119,11 @@ export const getTeamPlanStatus = async ({
teamId
}: {
teamId: string;
}): Promise<FeTeamPlanStatusType> => {
}): Promise<TeamPlanStatusType> => {
const standardPlans = global.subPlans?.standard;
/* Get all plans and datasetSize */
const [plans, usedDatasetSize] = await Promise.all([
MongoTeamSub.find({ teamId }).lean(),
getVectorCountByTeamId(teamId)
]);
const plans = await MongoTeamSub.find({ teamId }).lean();
/* Get all standardPlans and active standardPlan */
const teamStandardPlans = sortStandPlans(
@@ -158,17 +163,25 @@ export const getTeamPlanStatus = async ({
standardMaxDatasetSize +
extraDatasetSize.reduce((acc, cur) => acc + (cur.currentExtraDatasetSize || 0), 0);
const standardConstants =
standardPlan?.currentSubLevel && standardPlans
? standardPlans[standardPlan.currentSubLevel]
: undefined;
return {
[SubTypeEnum.standard]: standardPlan,
standardConstants:
standardPlan?.currentSubLevel && standardPlans
? standardPlans[standardPlan.currentSubLevel]
: undefined,
standardConstants: standardConstants
? {
...standardConstants,
maxTeamMember: standardPlan?.maxTeamMember || standardConstants.maxTeamMember,
maxAppAmount: standardPlan?.maxApp || standardConstants.maxAppAmount,
maxDatasetAmount: standardPlan?.maxDataset || standardConstants.maxDatasetAmount
}
: undefined,
totalPoints,
usedPoints: totalPoints - surplusPoints,
datasetMaxSize: totalDatasetSize,
usedDatasetSize
datasetMaxSize: totalDatasetSize
};
};

View File

@@ -14,7 +14,10 @@ export const getSafeEnv = () => {
return {
LOG_LEVEL: process.env.LOG_LEVEL,
STORE_LOG_LEVEL: process.env.STORE_LOG_LEVEL,
NODE_ENV: process.env.NODE_ENV
NODE_ENV: process.env.NODE_ENV,
HTTP_PROXY: process.env.HTTP_PROXY,
HTTPS_PROXY: process.env.HTTPS_PROXY,
NO_PROXY: process.env.NO_PROXY
};
};

View File

@@ -6,6 +6,7 @@
"ai_points_calculation_standard": "AI points",
"ai_points_usage": "AI points",
"ai_points_usage_tip": "Each time the AI model is called, a certain amount of AI points will be consumed. \nFor specific calculation standards, please refer to the \"Billing Standards\" above.",
"app_amount": "App amount",
"avatar": "Avatar",
"avatar_selection_exception": "Abnormal avatar selection",
"balance": "balance",
@@ -21,6 +22,7 @@
"contact_us": "Contact us",
"current_package": "Current plan",
"current_token_price": "Current points price",
"dataset_amount": "Dataset amount",
"effective_time": "Effective time",
"email_label": "Mail",
"exchange": "Exchange",
@@ -34,6 +36,7 @@
"help_document": "Help documentation",
"knowledge_base_capacity": "Dataset usages",
"manage": "Manage",
"member_amount": "Member amount",
"member_name": "Name",
"month": "moon",
"new_password": "New Password",

View File

@@ -215,7 +215,6 @@
"core.app.Interval timer run": "Scheduled Execution",
"core.app.Interval timer tip": "Can Execute App on Schedule",
"core.app.Make a brief introduction of your app": "Give Your AI App an Introduction",
"core.app.name": "name",
"core.app.Name and avatar": "Avatar & Name",
"core.app.Publish": "Publish",
"core.app.Publish Confirm": "Confirm to Publish App? This Will Immediately Update the App Status on All Publishing Channels.",
@@ -261,6 +260,7 @@
"core.app.have_saved": "Saved",
"core.app.logs.Source And Time": "Source & Time",
"core.app.more": "View More",
"core.app.name": "name",
"core.app.no_app": "No Apps Yet, Create One Now!",
"core.app.not_saved": "Not Saved",
"core.app.outLink.Can Drag": "Icon Can Be Dragged",
@@ -1145,10 +1145,10 @@
"support.wallet.subscription.Upgrade plan": "Upgrade Package",
"support.wallet.subscription.ai_model": "AI Language Model",
"support.wallet.subscription.function.History store": "{{amount}} Days of Chat History Retention",
"support.wallet.subscription.function.Max app": "{{amount}} Apps & Plugins",
"support.wallet.subscription.function.Max dataset": "{{amount}} Datasets",
"support.wallet.subscription.function.Max app": "{{amount}} App limit",
"support.wallet.subscription.function.Max dataset": "{{amount}} Dataset limit",
"support.wallet.subscription.function.Max dataset size": "{{amount}} Dataset Indexes",
"support.wallet.subscription.function.Max members": "{{amount}} Team Members",
"support.wallet.subscription.function.Max members": "{{amount}} Member limit",
"support.wallet.subscription.function.Points": "{{amount}} AI Points",
"support.wallet.subscription.mode.Month": "Month",
"support.wallet.subscription.mode.Period": "Subscription Period",

View File

@@ -6,6 +6,7 @@
"ai_points_calculation_standard": "AI 积分",
"ai_points_usage": "AI 积分使用量",
"ai_points_usage_tip": "每次调用 AI 模型时,都会消耗一定的 AI 积分。具体的计算标准可参考上方的“计费标准”。",
"app_amount": "应用数量",
"avatar": "头像",
"avatar_selection_exception": "头像选择异常",
"balance": "余额",
@@ -21,6 +22,7 @@
"contact_us": "联系我们",
"current_package": "当前套餐",
"current_token_price": "当前积分价格",
"dataset_amount": "知识库数量",
"effective_time": "生效时间",
"email_label": "邮箱",
"exchange": "兑换",
@@ -34,6 +36,7 @@
"help_document": "帮助文档",
"knowledge_base_capacity": "知识库容量",
"manage": "管理",
"member_amount": "成员数量",
"member_name": "成员名",
"month": "月",
"new_password": "新密码",
@@ -53,7 +56,9 @@
"please_bind_contact": "请绑定联系方式",
"please_bind_notification_receiving_path": "请先绑定通知接收途径",
"purchase_extra_package": "购买额外套餐",
"redeem_coupon": "兑换码",
"reminder_create_bound_notification_account": "提醒创建者绑定通知账号",
"reset_password": "重置密码",
"resource_usage": "资源用量",
"select_avatar": "点击选择头像",
"standard_package_and_extra_resource_package": "包含标准套餐与额外资源包",
@@ -65,7 +70,6 @@
"type": "类型",
"unlimited": "无限制",
"update_password": "修改密码",
"reset_password": "重置密码",
"update_success_tip": "更新数据成功",
"upgrade_package": "升级套餐",
"usage_balance": "使用余额: 使用余额",
@@ -74,6 +78,5 @@
"user_team_team_name": "团队",
"verification_code": "验证码",
"you_can_convert": "您可以兑换",
"yuan": "元",
"redeem_coupon": "兑换码"
"yuan": "元"
}

View File

@@ -1145,10 +1145,10 @@
"support.wallet.subscription.Upgrade plan": "升级套餐",
"support.wallet.subscription.ai_model": "AI语言模型",
"support.wallet.subscription.function.History store": "{{amount}} 天对话记录保留",
"support.wallet.subscription.function.Max app": "{{amount}} 个应用&插件",
"support.wallet.subscription.function.Max dataset": "{{amount}} 个知识库",
"support.wallet.subscription.function.Max app": "{{amount}} 个应用上限",
"support.wallet.subscription.function.Max dataset": "{{amount}} 个知识库上限",
"support.wallet.subscription.function.Max dataset size": "{{amount}} 组知识库索引",
"support.wallet.subscription.function.Max members": "{{amount}} 个团队成员",
"support.wallet.subscription.function.Max members": "{{amount}} 个团队成员上限",
"support.wallet.subscription.function.Points": "{{amount}} AI 积分",
"support.wallet.subscription.mode.Month": "按月",
"support.wallet.subscription.mode.Period": "订阅周期",

View File

@@ -6,6 +6,7 @@
"ai_points_calculation_standard": "AI 積分",
"ai_points_usage": "AI 積分使用量",
"ai_points_usage_tip": "每次呼叫 AI 模型時,都會消耗一定的 AI 積分。\n具體的計算標準可參考上方的「計費標準」。",
"app_amount": "應用數量",
"avatar": "頭像",
"avatar_selection_exception": "頭像選擇異常",
"balance": "餘額",
@@ -21,6 +22,7 @@
"contact_us": "聯絡我們",
"current_package": "目前套餐",
"current_token_price": "目前積分價格",
"dataset_amount": "知識庫數量",
"effective_time": "生效時間",
"email_label": "信箱",
"exchange": "兌換",
@@ -34,6 +36,7 @@
"help_document": "幫助文件",
"knowledge_base_capacity": "知識庫容量",
"manage": "管理",
"member_amount": "成員數量",
"member_name": "成員名",
"month": "月",
"new_password": "新密碼",

View File

@@ -215,7 +215,6 @@
"core.app.Interval timer run": "排程執行",
"core.app.Interval timer tip": "可排程執行應用程式",
"core.app.Make a brief introduction of your app": "為您的 AI 應用程式寫一段介紹",
"core.app.name": "名稱",
"core.app.Name and avatar": "頭像與名稱",
"core.app.Publish": "發布",
"core.app.Publish Confirm": "確認發布應用程式?這將立即更新所有發布管道的應用程式狀態。",
@@ -261,6 +260,7 @@
"core.app.have_saved": "已儲存",
"core.app.logs.Source And Time": "來源與時間",
"core.app.more": "檢視更多",
"core.app.name": "名稱",
"core.app.no_app": "還沒有應用程式,快來建立一個吧!",
"core.app.not_saved": "未儲存",
"core.app.outLink.Can Drag": "圖示可拖曳",
@@ -1145,8 +1145,8 @@
"support.wallet.subscription.Upgrade plan": "升級方案",
"support.wallet.subscription.ai_model": "AI 語言模型",
"support.wallet.subscription.function.History store": "{{amount}} 天對話紀錄保留",
"support.wallet.subscription.function.Max app": "{{amount}} 個應用程式與外掛程式",
"support.wallet.subscription.function.Max dataset": "{{amount}} 個知識庫",
"support.wallet.subscription.function.Max app": "{{amount}} 個應用上限",
"support.wallet.subscription.function.Max dataset": "{{amount}} 個知識庫上限",
"support.wallet.subscription.function.Max dataset size": "{{amount}} 組知識庫索引",
"support.wallet.subscription.function.Max members": "{{amount}} 個團隊成員",
"support.wallet.subscription.function.Points": "{{amount}} AI 點數",

View File

@@ -112,7 +112,7 @@ const RechargeModal = ({
<Box>{`${t('common:support.user.team.Dataset usage')}:`}</Box>
<Box
ml={2}
>{`${teamPlanStatus?.usedDatasetSize} / ${teamPlanStatus?.datasetMaxSize || t('account_info:unlimited')}`}</Box>
>{`${teamPlanStatus?.usedDatasetIndexSize} / ${teamPlanStatus?.datasetMaxSize || t('account_info:unlimited')}`}</Box>
<Box ml={5}>{`${t('common:support.wallet.subscription.AI points usage')}:`}</Box>
<Box
ml={2}

View File

@@ -8,6 +8,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import dynamic from 'next/dynamic';
import type { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
const ModelPriceModal = dynamic(() =>
import('@/components/core/ai/ModelTable').then((mod) => mod.ModelPriceModal)
@@ -15,10 +16,12 @@ const ModelPriceModal = dynamic(() =>
const StandardPlanContentList = ({
level,
mode
mode,
standplan
}: {
level: `${StandardSubLevelEnum}`;
mode: `${SubModeEnum}`;
standplan?: TeamSubSchema;
}) => {
const { t } = useTranslation();
const { subPlans } = useSystemStore();
@@ -31,9 +34,9 @@ const StandardPlanContentList = ({
price: plan.price * (mode === SubModeEnum.month ? 1 : 10),
level: level as `${StandardSubLevelEnum}`,
...standardSubLevelMap[level as `${StandardSubLevelEnum}`],
maxTeamMember: plan.maxTeamMember,
maxAppAmount: plan.maxAppAmount,
maxDatasetAmount: plan.maxDatasetAmount,
maxTeamMember: standplan?.maxTeamMember || plan.maxTeamMember,
maxAppAmount: standplan?.maxApp || plan.maxAppAmount,
maxDatasetAmount: standplan?.maxDataset || plan.maxDatasetAmount,
chatHistoryStoreDuration: plan.chatHistoryStoreDuration,
maxDatasetSize: plan.maxDatasetSize,
permissionCustomApiKey: plan.permissionCustomApiKey,
@@ -43,7 +46,14 @@ const StandardPlanContentList = ({
permissionWebsiteSync: plan.permissionWebsiteSync,
permissionTeamOperationLog: plan.permissionTeamOperationLog
};
}, [subPlans?.standard, level, mode]);
}, [
subPlans?.standard,
level,
mode,
standplan?.maxTeamMember,
standplan?.maxApp,
standplan?.maxDataset
]);
return planContent ? (
<Grid gap={4} fontSize={'sm'} fontWeight={500}>

View File

@@ -1,5 +1,5 @@
'use client';
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import {
Box,
Flex,
@@ -70,10 +70,6 @@ const Info = () => {
const standardPlan = teamPlanStatus?.standardConstants;
const { isOpen: isOpenContact, onClose: onCloseContact, onOpen: onOpenContact } = useDisclosure();
useMount(() => {
initUserInfo();
});
return (
<AccountContainer>
<Box py={[3, '28px']} px={[5, 10]} mx={'auto'}>
@@ -402,55 +398,77 @@ const PlanUsage = () => {
}
});
const datasetUsageMap = useMemo(() => {
const valueColorSchema = useCallback((val: number) => {
if (val < 50) return 'green';
if (val < 80) return 'yellow';
return 'red';
}, []);
const datasetIndexUsageMap = useMemo(() => {
if (!teamPlanStatus) {
return {
colorScheme: 'green',
value: 0,
maxSize: t('account_info:unlimited'),
usedSize: 0
max: t('account_info:unlimited'),
rate: 0
};
}
const rate = teamPlanStatus.usedDatasetSize / teamPlanStatus.datasetMaxSize;
const colorScheme = (() => {
if (rate < 0.5) return 'green';
if (rate < 0.8) return 'yellow';
return 'red';
})();
const rate = teamPlanStatus.usedDatasetIndexSize / teamPlanStatus.datasetMaxSize;
return {
colorScheme,
value: rate * 100,
maxSize: teamPlanStatus.datasetMaxSize || t('account_info:unlimited'),
usedSize: teamPlanStatus.usedDatasetSize
value: teamPlanStatus.usedDatasetIndexSize,
rate: rate * 100,
max: teamPlanStatus.datasetMaxSize || 1
};
}, [teamPlanStatus, t]);
}, [t, teamPlanStatus]);
const aiPointsUsageMap = useMemo(() => {
if (!teamPlanStatus) {
return {
colorScheme: 'green',
value: 0,
maxSize: t('account_info:unlimited'),
usedSize: 0
max: t('account_info:unlimited'),
rate: 0
};
}
const rate = teamPlanStatus.usedPoints / teamPlanStatus.totalPoints;
const colorScheme = (() => {
if (rate < 0.5) return 'green';
if (rate < 0.8) return 'yellow';
return 'red';
})();
return {
colorScheme,
value: rate * 100,
max: teamPlanStatus.totalPoints ? teamPlanStatus.totalPoints : t('account_info:unlimited'),
used: teamPlanStatus.usedPoints ? Math.round(teamPlanStatus.usedPoints) : 0
value: Math.round(teamPlanStatus.usedPoints),
max: teamPlanStatus.totalPoints,
rate: (teamPlanStatus.usedPoints / teamPlanStatus.totalPoints) * 100
};
}, [teamPlanStatus, t]);
}, [t, teamPlanStatus]);
const limitData = useMemo(() => {
if (!teamPlanStatus) {
return [];
}
return [
{
label: t('account_info:member_amount'),
value: teamPlanStatus.usedMember,
max: teamPlanStatus?.standardConstants?.maxTeamMember || t('account_info:unlimited'),
rate:
(teamPlanStatus.usedMember / (teamPlanStatus?.standardConstants?.maxTeamMember || 1)) *
100
},
{
label: t('account_info:app_amount'),
value: teamPlanStatus.usedAppAmount,
max: teamPlanStatus?.standardConstants?.maxAppAmount || t('account_info:unlimited'),
rate:
(teamPlanStatus.usedAppAmount / (teamPlanStatus?.standardConstants?.maxAppAmount || 1)) *
100
},
{
label: t('account_info:dataset_amount'),
value: teamPlanStatus.usedDatasetSize,
max: teamPlanStatus?.standardConstants?.maxDatasetAmount || t('account_info:unlimited'),
rate:
(teamPlanStatus.usedDatasetSize /
(teamPlanStatus?.standardConstants?.maxDatasetAmount || 1)) *
100
}
];
}, [t, teamPlanStatus]);
return standardPlan ? (
<Box mt={[6, 0]}>
@@ -531,6 +549,7 @@ const PlanUsage = () => {
<StandardPlanContentList
level={standardPlan?.currentSubLevel}
mode={standardPlan.currentMode}
standplan={standardPlan}
/>
</Box>
</Box>
@@ -574,15 +593,15 @@ const PlanUsage = () => {
{t('account_info:knowledge_base_capacity')}
</Box>
<Box color={'myGray.600'} ml={2}>
{datasetUsageMap.usedSize}/{datasetUsageMap.maxSize}
{datasetIndexUsageMap.value}/{datasetIndexUsageMap.max}
</Box>
</Flex>
</Flex>
<Box mt={3}>
<Box mt={1}>
<Progress
size={'sm'}
value={datasetUsageMap.value}
colorScheme={datasetUsageMap.colorScheme}
value={datasetIndexUsageMap.rate}
colorScheme={valueColorSchema(datasetIndexUsageMap.rate)}
borderRadius={'md'}
isAnimated
hasStripe
@@ -591,7 +610,7 @@ const PlanUsage = () => {
/>
</Box>
</Box>
<Box mt="9" width={'100%'} fontSize={'sm'}>
<Box mt="6" width={'100%'} fontSize={'sm'}>
<Flex alignItems={'center'}>
<Flex alignItems={'center'}>
<Box fontWeight={'bold'} color={'myGray.900'}>
@@ -599,15 +618,15 @@ const PlanUsage = () => {
</Box>
<QuestionTip ml={1} label={t('account_info:ai_points_usage_tip')}></QuestionTip>
<Box color={'myGray.600'} ml={2}>
{aiPointsUsageMap.used}/{aiPointsUsageMap.max}
{aiPointsUsageMap.value}/{aiPointsUsageMap.max}
</Box>
</Flex>
</Flex>
<Box mt={3}>
<Box mt={1}>
<Progress
size={'sm'}
value={aiPointsUsageMap.value}
colorScheme={aiPointsUsageMap.colorScheme}
value={aiPointsUsageMap.rate}
colorScheme={valueColorSchema(aiPointsUsageMap.rate)}
borderRadius={'md'}
isAnimated
hasStripe
@@ -616,6 +635,42 @@ const PlanUsage = () => {
/>
</Box>
</Box>
<MyDivider />
{limitData.map((item) => {
return (
<Box
key={item.label}
_notFirst={{
mt: 4
}}
width={'100%'}
fontSize={'sm'}
>
<Flex alignItems={'center'}>
<Box fontWeight={'bold'} color={'myGray.900'}>
{item.label}
</Box>
<Box color={'myGray.600'} ml={2}>
{item.value}/{item.max}
</Box>
</Flex>
<Box mt={1}>
<Progress
size={'sm'}
value={item.rate}
colorScheme={valueColorSchema(item.rate)}
borderRadius={'md'}
isAnimated
hasStripe
borderWidth={'1px'}
borderColor={'borderColor.low'}
/>
</Box>
</Box>
);
})}
</Box>
{isOpenStandardModal && <StandDetailModal onClose={onCloseStandardModal} />}
{isOpenRedeemCouponModal && (

View File

@@ -12,7 +12,7 @@ import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controll
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
import type { InsertOneDatasetDataProps } from '@/global/core/dataset/api';
import { simpleText } from '@fastgpt/global/common/string/tools';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { checkDatasetIndexLimit } from '@fastgpt/service/support/permission/teamLimit';
import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
@@ -41,9 +41,9 @@ async function handler(req: NextApiRequest) {
per: WritePermissionVal
});
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId,
insertLen: 1
insertLen: 1 + (indexes?.length || 0)
});
const [

View File

@@ -2,7 +2,7 @@
import type { NextApiResponse } from 'next';
import type { PushDatasetDataProps } from '@fastgpt/global/core/dataset/api.d';
import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { checkDatasetIndexLimit } from '@fastgpt/service/support/permission/teamLimit';
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
import { NextAPI } from '@/service/middleware/entry';
@@ -35,7 +35,7 @@ async function handler(req: ApiRequestProps<PushDatasetDataProps>, res: NextApiR
});
// auth dataset limit
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId,
insertLen: predictDataLimitLength(getTrainingModeByCollection(collection), data)
});

View File

@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { checkDatasetIndexLimit } from '@fastgpt/service/support/permission/teamLimit';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
@@ -19,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const numberSize = Number(size);
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId,
insertLen: numberSize
});

View File

@@ -2,17 +2,53 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getTeamPlanStatus } from '@fastgpt/service/support/wallet/sub/utils';
import { NextAPI } from '@/service/middleware/entry';
import type { ClientTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { getVectorCountByTeamId } from '@fastgpt/service/common/vectorDB/controller';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
async function handler(
req: NextApiRequest,
res: NextApiResponse<any>
): Promise<ClientTeamPlanStatusType | undefined> {
try {
const { teamId } = await authCert({
req,
authToken: true
});
return getTeamPlanStatus({
teamId
});
const [planStatus, usedMember, usedAppAmount, usedDatasetSize, usedDatasetIndexSize] =
await Promise.all([
getTeamPlanStatus({
teamId
}),
MongoTeamMember.countDocuments({
teamId,
status: { $ne: TeamMemberStatusEnum.leave }
}),
MongoApp.countDocuments({
teamId,
type: {
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
}
}),
MongoDataset.countDocuments({
type: { $ne: DatasetTypeEnum.folder }
}),
getVectorCountByTeamId(teamId)
]);
return {
...planStatus,
usedMember,
usedAppAmount,
usedDatasetSize,
usedDatasetIndexSize
};
} catch (error) {}
}

View File

@@ -73,13 +73,11 @@ const formatIndexes = async ({
};
// If index not type, set it to custom
indexes = indexes
.map((item) => ({
text: typeof item.text === 'string' ? item.text : String(item.text),
type: item.type || DatasetDataIndexTypeEnum.custom,
dataId: item.dataId
}))
.filter((item) => !!item.text.trim());
indexes = indexes.map((item) => ({
text: typeof item.text === 'string' ? item.text : String(item.text),
type: item.type || DatasetDataIndexTypeEnum.custom,
dataId: item.dataId
}));
// Recompute default indexes, Merge ids of the same index, reduce the number of rebuilds
const defaultIndexes = await getDefaultIndex({ q, a, indexSize });
@@ -134,7 +132,7 @@ const formatIndexes = async ({
)
).flat();
return chekcIndexes;
return chekcIndexes.filter((item) => !!item.text.trim());
};
/* insert data.
* 1. create data id

View File

@@ -20,7 +20,7 @@ import { delay } from '@fastgpt/service/common/bullmq';
import { rawText2Chunks, readDatasetSourceRawText } from '@fastgpt/service/core/dataset/read';
import { getLLMModel } from '@fastgpt/service/core/ai/model';
import { getLLMMaxChunkSize } from '@fastgpt/global/core/dataset/training/utils';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { checkDatasetIndexLimit } from '@fastgpt/service/support/permission/teamLimit';
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
import { getTrainingModeByCollection } from '@fastgpt/service/core/dataset/collection/utils';
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
@@ -258,7 +258,7 @@ export const datasetParseQueue = async (): Promise<any> => {
// Check dataset limit
try {
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId: data.teamId,
insertLen: predictDataLimitLength(trainingMode, chunks)
});

View File

@@ -15,7 +15,10 @@ import type {
TeamMemberItemType,
TeamMemberSchema
} from '@fastgpt/global/support/user/team/type.d';
import type { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
import type {
ClientTeamPlanStatusType,
TeamSubSchema
} from '@fastgpt/global/support/wallet/sub/type';
import type { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
import type { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import type {
@@ -100,7 +103,7 @@ export const checkTeamDatasetSizeLimit = (size: number) =>
/* plans */
export const getTeamPlanStatus = () =>
GET<FeTeamPlanStatusType>(`/support/user/team/plan/getTeamPlanStatus`, { maxQuantity: 1 });
GET<ClientTeamPlanStatusType>(`/support/user/team/plan/getTeamPlanStatus`, { maxQuantity: 1 });
export const getTeamPlans = () =>
GET<TeamSubSchema[]>(`/proApi/support/user/team/plan/getTeamPlans`);

View File

@@ -1,13 +1,11 @@
import { create, devtools, persist, immer } from '@fastgpt/web/common/zustand';
import type { UserUpdateParams } from '@/types/user';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { getTokenLogin, putUserInfo } from '@/web/support/user/api';
import type { OrgType } from '@fastgpt/global/support/user/team/org/type';
import type { UserType } from '@fastgpt/global/support/user/type.d';
import type { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import type { ClientTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import { getTeamPlanStatus } from './team/api';
import { getGroupList } from './team/group/api';
type State = {
systemMsgReadId: string;
@@ -22,7 +20,7 @@ type State = {
setUserInfo: (user: UserType | null) => void;
updateUserInfo: (user: UserUpdateParams) => Promise<void>;
teamPlanStatus: FeTeamPlanStatusType | null;
teamPlanStatus: ClientTeamPlanStatusType | null;
initTeamPlanStatus: () => Promise<any>;
teamOrgs: OrgType[];