{name} |
{apiKey} |
- {usage} |
+
+ {Math.round(usagePoints)}/
+ {feConfigs?.isPlus && limit?.maxUsagePoints && limit?.maxUsagePoints > -1
+ ? `${limit?.maxUsagePoints}`
+ : t('common.Unlimited')}
+ |
{feConfigs?.isPlus && (
<>
-
- {limit?.credit && limit?.credit > -1
- ? `${limit?.credit}`
- : t('common.Unlimited')}
- |
{limit?.expiredTime
? dayjs(limit?.expiredTime).format('YYYY/MM/DD\nHH:mm')
@@ -334,15 +333,15 @@ function EditKeyModal({
<>
- {t('common.Max credit')}:
-
+ {t('support.outlink.Max usage points')}:
+
import('./EditModal'));
const InviteModal = dynamic(() => import('./InviteModal'));
+const TeamTagsAsync = dynamic(() => import('../TeamTagsAsync'));
const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
- const theme = useTheme();
const { t } = useTranslation();
const { Loading } = useLoading();
const { toast } = useToast();
+ const { teamPlanStatus } = useUserStore();
+ const { feConfigs } = useSystemStore();
+ const [teamsTags, setTeamTags] = useState();
const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm();
const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({
@@ -61,6 +67,11 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
const { userInfo, initUserInfo } = useUserStore();
const [editTeamData, setEditTeamData] = useState();
const { isOpen: isOpenInvite, onOpen: onOpenInvite, onClose: onCloseInvite } = useDisclosure();
+ const {
+ isOpen: isOpenTeamTagsAsync,
+ onOpen: onOpenTeamTagsAsync,
+ onClose: onCloseTeamTagsAsync
+ } = useDisclosure();
const {
data: myTeams = [],
@@ -76,6 +87,8 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
mutationFn: async (teamId: string) => {
const token = await putSwitchTeam(teamId);
token && setToken(token);
+ // get team tags
+ await getTeamsTags(teamId);
return initUserInfo();
},
errorToast: t('user.team.Switch Team Failed')
@@ -86,6 +99,11 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
['getMembers', userInfo?.team?.teamId],
() => {
if (!userInfo?.team?.teamId) return [];
+ // get team tags
+ getTeamsTags(userInfo.team.teamId).then((res: any) => {
+ setTeamTags(res);
+ });
+
return getTeamMembers(userInfo.team.teamId);
}
);
@@ -108,7 +126,9 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
mutationFn: async (teamId?: string) => {
if (!teamId) return;
// change to personal team
+ // get members
await onSwitchTeam(defaultTeam.teamId);
+
return delLeaveTeam(teamId);
},
onSuccess() {
@@ -184,6 +204,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
bg: 'myGray.100'
}
})}
+ onClick={() => onSwitchTeam(team.teamId)}
>
void }) => {
: {})}
>
{team.teamName}
+ {/* {userInfo?.team?.teamId === team.teamId && (
+
+ {teamsTags.slice(0, 3).map((item: any, index) => {
+ return (
+
+ {item.label}
+
+ );
+ })}
+
+ )} */}
{userInfo?.team?.teamId === team.teamId ? (
@@ -229,7 +261,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
borderBottomColor={'myGray.100'}
mb={3}
>
-
+
{userInfo.team.teamName}
{userInfo.team.role === TeamMemberRoleEnum.owner && (
@@ -258,25 +290,50 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
{members.length}
- {userInfo.team.role === TeamMemberRoleEnum.owner && (
+ {userInfo.team.role === TeamMemberRoleEnum.owner &&
+ teamPlanStatus?.standardConstants &&
+ teamPlanStatus.standardConstants.maxTeamMember > members.length && (
+
+ }
+ onClick={() => {
+ if (userInfo.team.maxSize <= members.length) {
+ toast({
+ status: 'warning',
+ title: t('user.team.Over Max Member Tip', { max: userInfo.team.maxSize })
+ });
+ } else {
+ onOpenInvite();
+ }
+ }}
+ >
+ {t('user.team.Invite Member')}
+
+ )}
+ {userInfo.team.role === TeamMemberRoleEnum.owner && feConfigs?.show_team_chat && (
}
+ leftIcon={}
onClick={() => {
if (userInfo.team.maxSize <= members.length) {
toast({
status: 'warning',
- title: t('user.team.Over Max Member Tip', { max: userInfo.team.maxSize })
+ title: t('user.team.Team Tags Async', { max: userInfo.team.maxSize })
});
} else {
- onOpenInvite();
+ onOpenTeamTagsAsync();
}
}}
>
- {t('user.team.Invite Member')}
+ {t('user.team.Team Tags Async')}
)}
@@ -435,6 +492,13 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
onSuccess={refetchMembers}
/>
)}
+ {isOpenTeamTagsAsync && (
+
+ )}
>
diff --git a/projects/app/src/components/support/user/team/TeamTagsAsync/index.tsx b/projects/app/src/components/support/user/team/TeamTagsAsync/index.tsx
new file mode 100644
index 00000000000..7b7a99fa362
--- /dev/null
+++ b/projects/app/src/components/support/user/team/TeamTagsAsync/index.tsx
@@ -0,0 +1,182 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import MyModal from '@/components/MyModal';
+import {
+ Box,
+ Button,
+ Flex,
+ ModalBody,
+ Tag,
+ ModalFooter,
+ Input,
+ HStack,
+ Avatar
+} from '@chakra-ui/react';
+import { AttachmentIcon, CopyIcon, DragHandleIcon } from '@chakra-ui/icons';
+import { putUpdateTeamTags, updateTags } from '@/web/support/user/team/api';
+import { useForm } from 'react-hook-form';
+import { useTranslation } from 'next-i18next';
+import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
+import { useRequest } from '@/web/common/hooks/useRequest';
+import { RepeatIcon } from '@chakra-ui/icons';
+import MyIcon from '@fastgpt/web/components/common/Icon';
+import { useToast } from '@fastgpt/web/hooks/useToast';
+import { useCopyData } from '@/web/common/hooks/useCopyData';
+
+const TeamTagsAsync = ({
+ teamsTags,
+ teamInfo,
+ onClose
+}: {
+ teamsTags: Array;
+ teamInfo: any;
+ onClose: () => void;
+}) => {
+ const { t } = useTranslation();
+ const { toast } = useToast();
+ const [_teamsTags, setTeamTags] = useState>(teamsTags);
+
+ const { register, setValue, getValues, handleSubmit } = useForm({
+ defaultValues: { ...teamInfo }
+ });
+ const { copyData } = useCopyData();
+ const baseUrl = global.feConfigs?.customSharePageDomain || location?.origin;
+ const linkUrl = `${baseUrl}/chat/team?shareTeamId=${teamInfo?._id}${
+ getValues('showHistory') ? '' : '&showHistory=0'
+ }`;
+
+ // tags Async
+ const { mutate: onclickAsync, isLoading: creating } = useRequest({
+ mutationFn: async (data: any) => {
+ return putUpdateTeamTags({ tagsUrl: data.tagsUrl, teamId: teamInfo?._id });
+ },
+ onSuccess(id: string) {
+ onClose();
+ },
+ successToast: t('user.team.Team Tags Async Success'),
+ errorToast: t('common.Create Failed')
+ });
+ const asyncTags = async () => {
+ console.log('getValues', getValues());
+ const res: Array = await updateTags(teamInfo?._id, getValues().tagsUrl);
+ setTeamTags(res);
+ toast({ status: 'success', title: '团队标签同步成功' });
+ };
+ useEffect(() => {
+ console.log('teamInfo', teamInfo);
+ }, []);
+
+ // 获取
+ return (
+ <>
+
+ {teamInfo?.name}
+
+ {'填写标签同步链接,点击同步按钮即可同步'}
+
+
+ }
+ >
+
+
+
+ {t('同步链接')}
+
+
+
+
+
+ {t('分享链接')}
+
+ {/* code */}
+
+
+
+ {linkUrl}
+
+ {
+ copyData(linkUrl);
+ }}
+ />
+
+
+
+
+
+ {t('标签列表')}
+
+
+ {_teamsTags.map((item, index) => {
+ return (
+
+
+ {item.label}
+
+ );
+ })}
+
+ } onClick={asyncTags}>
+ 立即同步
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default React.memo(TeamTagsAsync);
diff --git a/projects/app/src/components/support/wallet/Price.tsx b/projects/app/src/components/support/wallet/Price.tsx
deleted file mode 100644
index f102ec2a203..00000000000
--- a/projects/app/src/components/support/wallet/Price.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import React from 'react';
-import { Box, CloseButton } from '@chakra-ui/react';
-import { useSystemStore } from '@/web/common/system/useSystemStore';
-import ReactDOM from 'react-dom';
-
-import Markdown from '@/components/Markdown';
-
-const Price = ({ onClose }: { onClose: () => void }) => {
- const { llmModelList, vectorModelList, audioSpeechModelList, whisperModel } = useSystemStore();
-
- const list = [
- {
- title: 'AI语言模型',
- describe: '',
- md: `
-| 模型 | 输入价格(¥) | 输出价格(¥) |
-| --- | --- | --- |
-${llmModelList
- ?.map((item) => `| ${item.name} | ${item.inputPrice}/1k tokens | ${item.outputPrice}/1k tokens |`)
- .join('\n')}`
- },
- {
- title: '索引模型(文档训练 & 文档检索)',
- describe: '',
- md: `
-| 模型 | 价格(¥) |
-| --- | --- |
-${vectorModelList?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 |`).join('\n')}
- `
- },
- {
- title: '语音播放',
- describe: '',
- md: `
-| 模型 | 价格(¥) |
-| --- | --- |
-${audioSpeechModelList
- ?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 | - |`)
- .join('\n')}`
- },
- ...(whisperModel
- ? [
- {
- title: '语音输入',
- describe: '',
- md: `
-| 模型 | 价格(¥) |
-| --- | --- |
-| ${whisperModel.name} | ${whisperModel.inputPrice}/分钟 | - |`
- }
- ]
- : [])
- ];
-
- return ReactDOM.createPortal(
-
-
-
-
- {list.map((item) => (
-
-
- {item.title}
-
-
-
-
-
- ))}
-
-
- ,
- // @ts-ignore
- document.querySelector('body')
- );
-};
-
-export default Price;
diff --git a/projects/app/src/components/support/wallet/QRCodePayModal.tsx b/projects/app/src/components/support/wallet/QRCodePayModal.tsx
new file mode 100644
index 00000000000..52147915449
--- /dev/null
+++ b/projects/app/src/components/support/wallet/QRCodePayModal.tsx
@@ -0,0 +1,84 @@
+import MyModal from '@/components/MyModal';
+import React, { useEffect } from 'react';
+import { useTranslation } from 'next-i18next';
+import { Box, ModalBody, ModalFooter } from '@chakra-ui/react';
+import { useQuery } from '@tanstack/react-query';
+import { checkBalancePayResult } from '@/web/support/wallet/bill/api';
+import { useToast } from '@fastgpt/web/hooks/useToast';
+import { useRouter } from 'next/router';
+import { getErrText } from '@fastgpt/global/common/error/utils';
+
+export type QRPayProps = {
+ readPrice: number;
+ codeUrl: string;
+ billId: string;
+};
+
+const QRCodePayModal = ({
+ readPrice,
+ codeUrl,
+ billId,
+ onSuccess
+}: QRPayProps & { onSuccess?: () => any }) => {
+ const router = useRouter();
+ const { t } = useTranslation();
+ const { toast } = useToast();
+ const dom = document.getElementById('payQRCode');
+
+ useEffect(() => {
+ if (dom && window.QRCode) {
+ new window.QRCode(dom, {
+ text: codeUrl,
+ width: 128,
+ height: 128,
+ colorDark: '#000000',
+ colorLight: '#ffffff',
+ correctLevel: window.QRCode.CorrectLevel.H
+ });
+ }
+ }, [dom]);
+
+ useQuery(
+ [billId],
+ () => {
+ if (!billId) return null;
+ return checkBalancePayResult(billId);
+ },
+ {
+ enabled: !!billId,
+ refetchInterval: 3000,
+ onSuccess: async (res) => {
+ if (!res) return;
+
+ try {
+ await onSuccess?.();
+ toast({
+ title: res,
+ status: 'success'
+ });
+ } catch (error) {
+ toast({
+ title: getErrText(error),
+ status: 'error'
+ });
+ }
+
+ setTimeout(() => {
+ router.reload();
+ }, 1000);
+ }
+ }
+ );
+
+ return (
+
+
+ 请微信扫码支付: {readPrice}元,请勿关闭页面
+
+
+
+
+ );
+};
+
+export default React.memo(QRCodePayModal);
diff --git a/projects/app/src/components/support/wallet/StandardPlanContentList.tsx b/projects/app/src/components/support/wallet/StandardPlanContentList.tsx
new file mode 100644
index 00000000000..6a5eda48769
--- /dev/null
+++ b/projects/app/src/components/support/wallet/StandardPlanContentList.tsx
@@ -0,0 +1,116 @@
+import { useSystemStore } from '@/web/common/system/useSystemStore';
+import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants';
+import React, { useMemo } from 'react';
+import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
+import { Box, Flex, Grid } from '@chakra-ui/react';
+import MyIcon from '@fastgpt/web/components/common/Icon';
+import { useTranslation } from 'next-i18next';
+
+const StandardPlanContentList = ({
+ level,
+ mode
+}: {
+ level: `${StandardSubLevelEnum}`;
+ mode: `${SubModeEnum}`;
+}) => {
+ const { t } = useTranslation();
+ const { subPlans } = useSystemStore();
+
+ const planContent = useMemo(() => {
+ const plan = subPlans?.standard?.[level];
+ if (!plan) return;
+ return {
+ 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,
+ chatHistoryStoreDuration: plan.chatHistoryStoreDuration,
+ maxDatasetSize: plan.maxDatasetSize,
+ permissionCustomApiKey: plan.permissionCustomApiKey,
+ permissionCustomCopyright: plan.permissionCustomCopyright,
+ trainingWeight: plan.trainingWeight,
+ permissionReRank: plan.permissionReRank,
+ totalPoints: plan.totalPoints * (mode === SubModeEnum.month ? 1 : 12),
+ permissionWebsiteSync: plan.permissionWebsiteSync
+ };
+ }, [subPlans?.standard, level, mode]);
+
+ return planContent ? (
+
+
+
+
+ {t('support.wallet.subscription.function.Max members', {
+ amount: planContent.maxTeamMember
+ })}
+
+
+
+
+
+ {t('support.wallet.subscription.function.Max app', {
+ amount: planContent.maxAppAmount
+ })}
+
+
+
+
+
+ {t('support.wallet.subscription.function.Max dataset', {
+ amount: planContent.maxDatasetAmount
+ })}
+
+
+
+
+
+ {t('support.wallet.subscription.function.History store', {
+ amount: planContent.chatHistoryStoreDuration
+ })}
+
+
+
+
+
+ {t('support.wallet.subscription.function.Max dataset size', {
+ amount: planContent.maxDatasetSize
+ })}
+
+
+
+
+
+
+ {t('support.wallet.subscription.function.Points', {
+ amount: planContent.totalPoints
+ })}
+
+
+
+
+
+
+ {t('support.wallet.subscription.Training weight', {
+ weight: planContent.trainingWeight
+ })}
+
+
+ {!!planContent.permissionReRank && (
+
+
+ 检索结果重排
+
+ )}
+ {!!planContent.permissionWebsiteSync && (
+
+
+ Web站点同步
+
+ )}
+
+ ) : null;
+};
+
+export default StandardPlanContentList;
diff --git a/projects/app/src/components/support/wallet/SubDatasetModal.tsx b/projects/app/src/components/support/wallet/SubDatasetModal.tsx
deleted file mode 100644
index dcc100b1078..00000000000
--- a/projects/app/src/components/support/wallet/SubDatasetModal.tsx
+++ /dev/null
@@ -1,240 +0,0 @@
-import React, { useState } from 'react';
-import MyModal from '@/components/MyModal';
-import { useTranslation } from 'next-i18next';
-import {
- Box,
- Flex,
- ModalBody,
- NumberInput,
- NumberInputField,
- NumberInputStepper,
- NumberIncrementStepper,
- NumberDecrementStepper,
- ModalFooter,
- Button
-} from '@chakra-ui/react';
-import { useQuery } from '@tanstack/react-query';
-import {
- getTeamDatasetValidSub,
- posCheckTeamDatasetSizeSub,
- postUpdateTeamDatasetSizeSub,
- putTeamDatasetSubStatus
-} from '@/web/support/wallet/sub/api';
-import Markdown from '@/components/Markdown';
-import MyTooltip from '@/components/MyTooltip';
-import { QuestionOutlineIcon } from '@chakra-ui/icons';
-import { useConfirm } from '@/web/common/hooks/useConfirm';
-import { useRequest } from '@/web/common/hooks/useRequest';
-import { useRouter } from 'next/router';
-import { useSystemStore } from '@/web/common/system/useSystemStore';
-import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
-import MySelect from '@/components/Select';
-import {
- SubStatusEnum,
- SubTypeEnum,
- subSelectMap
-} from '@fastgpt/global/support/wallet/sub/constants';
-import { SubDatasetSizePreviewCheckResponse } from '@fastgpt/global/support/wallet/sub/api.d';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
-import { useUserStore } from '@/web/support/user/useUserStore';
-
-const SubDatasetModal = ({ onClose }: { onClose: () => void }) => {
- const { subPlans } = useSystemStore();
- const datasetStorePrice = subPlans?.extraDatasetSize?.price || 0;
-
- const { t } = useTranslation();
- const router = useRouter();
- const { ConfirmModal, openConfirm } = useConfirm({});
- const { userInfo } = useUserStore();
- const [datasetSize, setDatasetSize] = useState(0);
- const [isRenew, setIsRenew] = useState('false');
-
- const { data: teamSubPlan } = useQuery(['getTeamDatasetValidSub'], getTeamDatasetValidSub, {
- onSuccess(res) {
- setIsRenew(res?.extraDatasetSize?.status === SubStatusEnum.active ? 'true' : 'false');
- setDatasetSize((res?.extraDatasetSize?.nextExtraDatasetSize || 0) / 1000);
- }
- });
-
- const { mutate: onClickUpdateSub, isLoading: isPaying } = useRequest({
- mutationFn: () => postUpdateTeamDatasetSizeSub({ size: datasetSize }),
- onSuccess() {
- setTimeout(() => {
- router.reload();
- }, 100);
- },
- successToast: t('common.Update success'),
- errorToast: t('common.error.Update error')
- });
-
- const { mutate: onClickPreviewCheck, isLoading: isFetchingPreviewCheck } = useRequest({
- mutationFn: () =>
- posCheckTeamDatasetSizeSub({
- size: datasetSize
- }),
- onSuccess(res: SubDatasetSizePreviewCheckResponse) {
- if (!res.payForNewSub) {
- onClickUpdateSub('');
- return;
- } else {
- openConfirm(
- () => {
- if (!res.balanceEnough) return;
- onClickUpdateSub('');
- },
- undefined,
-
-
- 当前额外容量:
- {teamSubPlan?.extraDatasetSize?.currentExtraDatasetSize || 0}条
-
-
- 新的额外容量:
- {res.newSubSize}条
-
-
- 新套餐价格:
- {formatStorePrice2Read(res.newPlanPrice)}元
-
-
- 本次需支付:
- {formatStorePrice2Read(res.payPrice)}元
-
-
- 有效时长:
- 30天
-
-
- 账号余额:
- {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}元
-
- {!res.balanceEnough && (
-
- 账号余额不足,请先充值余额再购买额外容量。
-
- )}
-
- )();
- }
- },
- errorToast: t('common.error.Update error')
- });
- const { mutate: onUpdateStatus } = useRequest({
- mutationFn: (e: 'true' | 'false') => {
- setIsRenew(e);
- return putTeamDatasetSubStatus({
- status: subSelectMap[e],
- type: SubTypeEnum.extraDatasetSize
- });
- },
- successToast: t('common.Update success'),
- errorToast: t('common.error.Update error')
- });
-
- const isLoading = isPaying || isFetchingPreviewCheck;
-
- return (
-
-
- <>
-
- {t('support.user.Price')}
-
-
-
-
-
- >
-
- {t('support.wallet.subscription.Current dataset store')}:
-
- {teamSubPlan?.extraDatasetSize?.currentExtraDatasetSize || 0}
- {t('core.dataset.data.unit')}
-
-
- {teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize !== undefined && (
-
- {t('support.wallet.subscription.Next sub dataset size')}:
-
- {teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize || 0}
- {t('core.dataset.data.unit')}
-
-
- )}
- {!!teamSubPlan?.extraDatasetSize?.startTime && (
-
- 订阅开始时间:
- {formatTime2YMDHM(teamSubPlan?.extraDatasetSize?.startTime)}
-
- )}
- {!!teamSubPlan?.extraDatasetSize?.expiredTime && (
-
- 订阅到期时间:
- {formatTime2YMDHM(teamSubPlan?.extraDatasetSize?.expiredTime)}
-
- )}
-
- 是否自动续费:
-
-
-
- {t('support.wallet.subscription.Update extra dataset size')}
-
- {
- setDatasetSize(Number(e));
- }}
- >
-
-
-
-
-
-
- 000{t('core.dataset.data.unit')}
-
-
-
-
-
- {datasetSize * 1000 !== teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize && (
-
- )}
-
-
-
-
- );
-};
-
-export default SubDatasetModal;
diff --git a/projects/app/src/constants/app.ts b/projects/app/src/constants/app.ts
index 53651df4311..de53fbab3d9 100644
--- a/projects/app/src/constants/app.ts
+++ b/projects/app/src/constants/app.ts
@@ -15,7 +15,8 @@ export const defaultApp: AppDetailType = {
tmbId: '',
permission: 'private',
isOwner: false,
- canWrite: false
+ canWrite: false,
+ teamTags: ['']
};
export const defaultOutLinkForm: OutLinkEditType = {
@@ -23,7 +24,7 @@ export const defaultOutLinkForm: OutLinkEditType = {
responseDetail: false,
limit: {
QPM: 100,
- credit: -1
+ maxUsagePoints: -1
}
};
diff --git a/projects/app/src/global/core/chat/api.d.ts b/projects/app/src/global/core/chat/api.d.ts
index db20bed4d87..654005c81cd 100644
--- a/projects/app/src/global/core/chat/api.d.ts
+++ b/projects/app/src/global/core/chat/api.d.ts
@@ -14,6 +14,12 @@ export type InitChatProps = {
chatId?: string;
loadCustomFeedbacks?: boolean;
};
+/* ---------- chat ----------- */
+export type chatByTeamProps = {
+ teamId?: string;
+ appId?: string;
+ outLinkUid?: string;
+};
export type InitOutLinkChatProps = {
chatId?: string;
shareId?: string;
@@ -39,6 +45,7 @@ export type InitChatResponse = {
/* ---------- history ----------- */
export type getHistoriesProps = {
appId?: string;
+ authToken?: string;
// share chat
shareId?: string;
outLinkUid?: string; // authToken/uid
diff --git a/projects/app/src/global/core/prompt/AIChat.ts b/projects/app/src/global/core/prompt/AIChat.ts
index a89f3cd8706..ea2a18f809d 100644
--- a/projects/app/src/global/core/prompt/AIChat.ts
+++ b/projects/app/src/global/core/prompt/AIChat.ts
@@ -4,42 +4,34 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
{
title: '标准模板',
desc: '标准提示词,用于结构不固定的知识库。',
- value: `
-{{q}}
-{{a}}
-`
+ value: `{{q}}
+{{a}}`
},
{
title: '问答模板',
desc: '适合 QA 问答结构的知识库,可以让AI较为严格的按预设内容回答',
- value: `
-<问题>
+ value: `
{{q}}
-问题>
-<答案>
+
+
{{a}}
-答案>
-`
+`
},
{
title: '标准严格模板',
desc: '在标准模板基础上,对模型的回答做更严格的要求。',
- value: `
-{{q}}
-{{a}}
-`
+ value: `{{q}}
+{{a}}`
},
{
title: '严格问答模板',
desc: '在问答模板基础上,对模型的回答做更严格的要求。',
- value: `
-<问题>
+ value: `
{{q}}
-问题>
-<答案>
+
+
{{a}}
-答案>
-`
+`
}
];
@@ -47,14 +39,16 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
{
title: '标准模板',
desc: '',
- value: `使用 标记中的内容作为你的知识:
+ value: `使用 标记中的内容作为你的知识:
+
{{quote}}
+
回答要求:
- 如果你不清楚答案,你需要澄清。
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
+- 避免提及你是从 获取的知识。
+- 保持答案与 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
@@ -65,7 +59,9 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
desc: '',
value: `使用 标记中的问答对进行回答。
+
{{quote}}
+
回答要求:
- 选择其中一个或多个问答对进行回答。
@@ -78,18 +74,20 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
{
title: '标准严格模板',
desc: '',
- value: `忘记你已有的知识,仅使用 标记中的内容作为你的知识:
+ value: `忘记你已有的知识,仅使用 标记中的内容作为你的知识:
+
{{quote}}
+
思考流程:
-1. 判断问题是否与 标记中的内容有关。
+1. 判断问题是否与 标记中的内容有关。
2. 如果有关,你按下面的要求回答。
3. 如果无关,你直接拒绝回答本次问题。
回答要求:
-- 避免提及你是从 获取的知识。
-- 保持答案与 中描述的一致。
+- 避免提及你是从 获取的知识。
+- 保持答案与 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
@@ -100,7 +98,9 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
desc: '',
value: `忘记你已有的知识,仅使用 标记中的问答对进行回答。
+
{{quote}}
+}
思考流程:
1. 判断问题是否与 标记中的内容有关。
diff --git a/projects/app/src/pages/_error.tsx b/projects/app/src/pages/_error.tsx
index 1c119ad0aab..f2acb97a9d5 100644
--- a/projects/app/src/pages/_error.tsx
+++ b/projects/app/src/pages/_error.tsx
@@ -2,6 +2,7 @@ import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useSystemStore } from '@/web/common/system/useSystemStore';
+import { Box } from '@chakra-ui/react';
function Error() {
const router = useRouter();
@@ -24,11 +25,12 @@ function Error() {
}, []);
return (
-
- 部分系统不兼容,导致页面崩溃。如果可以,请联系作者,反馈下具体操作和页面。 大部分是 苹果 的
- safari 浏览器导致,可以尝试更换 chrome
- 浏览器。或者是因为开了中文翻译导致,请检查并关闭中文翻译。
-
+
+ {`出现未捕获的异常。
+1. 私有部署用户,90%由于配置文件不正确导致。
+2. 部分系统不兼容相关API。大部分是苹果的safari 浏览器导致,可以尝试更换 chrome。
+3. 请关闭浏览器翻译功能,部分翻译导致页面崩溃。`}
+
);
}
diff --git a/projects/app/src/pages/account/components/BillDetail.tsx b/projects/app/src/pages/account/components/BillDetail.tsx
deleted file mode 100644
index 6d089c2cf02..00000000000
--- a/projects/app/src/pages/account/components/BillDetail.tsx
+++ /dev/null
@@ -1,160 +0,0 @@
-import React, { useMemo } from 'react';
-import {
- ModalBody,
- Flex,
- Box,
- Table,
- Thead,
- Tbody,
- Tr,
- Th,
- Td,
- TableContainer
-} from '@chakra-ui/react';
-import { BillItemType } from '@fastgpt/global/support/wallet/bill/type.d';
-import dayjs from 'dayjs';
-import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
-import MyModal from '@/components/MyModal';
-import { useTranslation } from 'next-i18next';
-
-const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void }) => {
- const { t } = useTranslation();
- const filterBillList = useMemo(
- () => bill.list.filter((item) => item && item.moduleName),
- [bill.list]
- );
-
- const {
- hasModel,
- hasTokens,
- hasInputTokens,
- hasOutputTokens,
- hasCharsLen,
- hasDuration,
- hasDataLen,
- hasDatasetSize
- } = useMemo(() => {
- let hasModel = false;
- let hasTokens = false;
- let hasInputTokens = false;
- let hasOutputTokens = false;
- let hasCharsLen = false;
- let hasDuration = false;
- let hasDataLen = false;
- let hasDatasetSize = false;
-
- bill.list.forEach((item) => {
- if (item.model !== undefined) {
- hasModel = true;
- }
- if (typeof item.tokenLen === 'number') {
- hasTokens = true;
- }
- if (typeof item.inputTokens === 'number') {
- hasInputTokens = true;
- }
- if (typeof item.outputTokens === 'number') {
- hasOutputTokens = true;
- }
- if (typeof item.charsLength === 'number') {
- hasCharsLen = true;
- }
- if (typeof item.duration === 'number') {
- hasDuration = true;
- }
- if (typeof item.datasetSize === 'number') {
- hasDatasetSize = true;
- }
- });
-
- return {
- hasModel,
- hasTokens,
- hasInputTokens,
- hasOutputTokens,
- hasCharsLen,
- hasDuration,
- hasDataLen,
- hasDatasetSize
- };
- }, [bill.list]);
-
- return (
-
-
- {/*
- {t('wallet.bill.bill username')}:
- {t(bill.memberName)}
- */}
-
- {t('wallet.bill.Number')}:
- {bill.id}
-
-
- {t('wallet.bill.Time')}:
- {dayjs(bill.time).format('YYYY/MM/DD HH:mm:ss')}
-
-
- {t('wallet.bill.App name')}:
- {t(bill.appName) || '-'}
-
-
- {t('wallet.bill.Source')}:
- {t(BillSourceMap[bill.source]?.label)}
-
-
- {t('wallet.bill.Total')}:
- {bill.total}元
-
-
-
- {t('wallet.bill.Bill Module')}
-
-
-
-
-
- {t('wallet.bill.Module name')} |
- {hasModel && {t('wallet.bill.Ai model')} | }
- {hasTokens && {t('wallet.bill.Token Length')} | }
- {hasInputTokens && {t('wallet.bill.Input Token Length')} | }
- {hasOutputTokens && {t('wallet.bill.Output Token Length')} | }
- {hasCharsLen && {t('wallet.bill.Text Length')} | }
- {hasDuration && {t('wallet.bill.Duration')} | }
- {hasDatasetSize && (
- {t('support.wallet.subscription.type.extraDatasetSize')} |
- )}
- 费用(¥) |
-
-
-
- {filterBillList.map((item, i) => (
-
- {t(item.moduleName)} |
- {hasModel && {item.model ?? '-'} | }
- {hasTokens && {item.tokenLen ?? '-'} | }
- {hasInputTokens && {item.inputTokens ?? '-'} | }
- {hasOutputTokens && {item.outputTokens ?? '-'} | }
- {hasCharsLen && {item.charsLength ?? '-'} | }
- {hasDuration && {item.duration ?? '-'} | }
- {hasDatasetSize && {item.datasetSize ?? '-'} | }
- {formatStorePrice2Read(item.amount)} |
-
- ))}
-
-
-
-
-
-
- );
-};
-
-export default BillDetail;
diff --git a/projects/app/src/pages/account/components/BillTable.tsx b/projects/app/src/pages/account/components/BillTable.tsx
index 8ea7eddd516..e5c20dce08f 100644
--- a/projects/app/src/pages/account/components/BillTable.tsx
+++ b/projects/app/src/pages/account/components/BillTable.tsx
@@ -1,5 +1,6 @@
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useState, useCallback, useMemo, useEffect } from 'react';
import {
+ Button,
Table,
Thead,
Tbody,
@@ -9,43 +10,38 @@ import {
TableContainer,
Flex,
Box,
- Button
+ ModalBody
} from '@chakra-ui/react';
-import { BillSourceEnum, BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants';
-import { getUserBills } from '@/web/support/wallet/bill/api';
-import type { BillItemType } from '@fastgpt/global/support/wallet/bill/type';
-import { usePagination } from '@/web/common/hooks/usePagination';
-import { useLoading } from '@/web/common/hooks/useLoading';
+import { getBills, checkBalancePayResult } from '@/web/support/wallet/bill/api';
+import type { BillSchemaType } from '@fastgpt/global/support/wallet/bill/type.d';
import dayjs from 'dayjs';
+import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
+import { useToast } from '@fastgpt/web/hooks/useToast';
import MyIcon from '@fastgpt/web/components/common/Icon';
-import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
-import { addDays } from 'date-fns';
-import dynamic from 'next/dynamic';
-import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
import MySelect from '@/components/Select';
-import { useQuery } from '@tanstack/react-query';
-import { useUserStore } from '@/web/support/user/useUserStore';
-import { getTeamMembers } from '@/web/support/user/team/api';
-import Avatar from '@/components/Avatar';
-const BillDetail = dynamic(() => import('./BillDetail'));
+import {
+ BillTypeEnum,
+ billPayWayMap,
+ billStatusMap,
+ billTypeMap
+} from '@fastgpt/global/support/wallet/bill/constants';
+import { usePagination } from '@/web/common/hooks/usePagination';
+import MyBox from '@/components/common/MyBox';
+import { useRequest } from '@/web/common/hooks/useRequest';
+import MyModal from '@/components/MyModal';
+import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants';
const BillTable = () => {
const { t } = useTranslation();
- const { Loading } = useLoading();
- const [dateRange, setDateRange] = useState({
- from: addDays(new Date(), -7),
- to: new Date()
- });
- const [billSource, setBillSource] = useState<`${BillSourceEnum}` | ''>('');
- const { isPc } = useSystemStore();
- const { userInfo } = useUserStore();
- const [billDetail, setBillDetail] = useState();
+ const { toast } = useToast();
+ const [billType, setBillType] = useState<`${BillTypeEnum}` | ''>('');
+ const [billDetail, setBillDetail] = useState();
- const sourceList = useMemo(
+ const billTypeList = useMemo(
() => [
{ label: t('common.All'), value: '' },
- ...Object.entries(BillSourceMap).map(([key, value]) => ({
+ ...Object.entries(billTypeMap).map(([key, value]) => ({
label: t(value.label),
value: key
}))
@@ -53,134 +49,199 @@ const BillTable = () => {
[t]
);
- const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId);
- const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => {
- if (!userInfo?.team?.teamId) return [];
- return getTeamMembers(userInfo.team.teamId);
- });
- const tmbList = useMemo(
- () =>
- members.map((item) => ({
- label: (
-
-
- {item.memberName}
-
- ),
- value: item.tmbId
- })),
- [members]
- );
-
const {
data: bills,
isLoading,
Pagination,
- getData
- } = usePagination({
- api: getUserBills,
- pageSize: isPc ? 20 : 10,
+ getData,
+ total
+ } = usePagination({
+ api: getBills,
+ pageSize: 20,
params: {
- dateStart: dateRange.from || new Date(),
- dateEnd: addDays(dateRange.to || new Date(), 1),
- source: billSource,
- teamMemberId: selectTmbId
+ type: billType
},
defaultRequest: false
});
+ const { mutate: handleRefreshPayOrder, isLoading: isRefreshing } = useRequest({
+ mutationFn: async (payId: string) => {
+ try {
+ const data = await checkBalancePayResult(payId);
+ toast({
+ title: data,
+ status: 'success'
+ });
+ } catch (error: any) {
+ toast({
+ title: error?.message,
+ status: 'warning'
+ });
+ console.log(error);
+ }
+ try {
+ getData(1);
+ } catch (error) {}
+ }
+ });
+
useEffect(() => {
getData(1);
- }, [billSource, selectTmbId]);
+ }, [billType]);
return (
-
-
- {tmbList.length > 1 && userInfo?.team?.canWrite && (
-
-
- {t('support.user.team.member')}
-
-
-
- )}
-
-
- getData(1)}
- />
-
-
-
-
+
+
- {/* {t('user.team.Member Name')} | */}
- {t('user.Time')} |
+ # |
{
- setBillSource(e);
+ setBillType(e);
}}
w={'130px'}
>
|
- {t('user.Application Name')} |
- {t('user.Total Amount')} |
+ {t('user.Time')} |
+ {t('support.wallet.Amount')} |
+ {t('support.wallet.bill.Status')} |
|
- {bills.map((item) => (
-
- {/* {item.memberName} | */}
- {dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')} |
- {t(BillSourceMap[item.source]?.label)} |
- {t(item.appName) || '-'} |
- {item.total}元 |
+ {bills.map((item, i) => (
+
+ {i + 1} |
+ {t(billTypeMap[item.type]?.label)} |
+
+ {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
+ |
+ {formatStorePrice2Read(item.price)}元 |
+ {t(billStatusMap[item.status]?.label)} |
- |
))}
+ {total >= 20 && (
+
+
+
+ )}
+ {!isLoading && bills.length === 0 && (
+
+
+
+ {t('support.wallet.noBill')}
+
+
+ )}
- {!isLoading && bills.length === 0 && (
-
-
-
- 无使用记录~
-
-
+ {!!billDetail && (
+ setBillDetail(undefined)} />
)}
-
-
- {!!billDetail && setBillDetail(undefined)} />}
-
+
);
};
-export default React.memo(BillTable);
+export default BillTable;
+
+function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) {
+ const { t } = useTranslation();
+
+ return (
+
+
+
+ {t('support.wallet.bill.Number')}:
+ {bill.orderId}
+
+
+ {t('support.wallet.usage.Time')}:
+ {dayjs(bill.createTime).format('YYYY/MM/DD HH:mm:ss')}
+
+
+ {t('support.wallet.bill.Status')}:
+ {t(billStatusMap[bill.status]?.label)}
+
+ {!!bill.metadata?.payWay && (
+
+ {t('support.wallet.bill.payWay.Way')}:
+ {t(billPayWayMap[bill.metadata.payWay]?.label)}
+
+ )}
+
+ {t('support.wallet.Amount')}:
+ {formatStorePrice2Read(bill.price)}元
+
+
+ {t('support.wallet.bill.Type')}:
+ {t(billTypeMap[bill.type]?.label)}
+
+ {!!bill.metadata?.subMode && (
+
+ {t('support.wallet.subscription.mode.Period')}:
+ {t(subModeMap[bill.metadata.subMode]?.label)}
+
+ )}
+ {!!bill.metadata?.standSubLevel && (
+
+ {t('support.wallet.subscription.Stand plan level')}:
+ {t(standardSubLevelMap[bill.metadata.standSubLevel]?.label)}
+
+ )}
+ {bill.metadata?.month !== undefined && (
+
+ {t('support.wallet.subscription.Month amount')}:
+ {bill.metadata?.month}
+
+ )}
+ {bill.metadata?.datasetSize !== undefined && (
+
+ {t('support.wallet.subscription.Extra dataset size')}:
+ {bill.metadata?.datasetSize}
+
+ )}
+ {bill.metadata?.extraPoints !== undefined && (
+
+ {t('support.wallet.subscription.Extra ai points')}:
+ {bill.metadata.extraPoints}
+
+ )}
+
+
+ );
+}
diff --git a/projects/app/src/pages/account/components/Individuation.tsx b/projects/app/src/pages/account/components/Individuation.tsx
new file mode 100644
index 00000000000..9ac344b573a
--- /dev/null
+++ b/projects/app/src/pages/account/components/Individuation.tsx
@@ -0,0 +1,92 @@
+import { Box, Card, Flex, Select } from '@chakra-ui/react';
+import React, { useCallback, useRef } from 'react';
+import MyIcon from '@fastgpt/web/components/common/Icon';
+import { useTranslation } from 'next-i18next';
+import { timezoneList } from '@fastgpt/global/common/time/timezone';
+import { useUserStore } from '@/web/support/user/useUserStore';
+import { UserType } from '@fastgpt/global/support/user/type';
+import { useToast } from '@fastgpt/web/hooks/useToast';
+import { useForm } from 'react-hook-form';
+import { UserUpdateParams } from '@/types/user';
+import { langMap, setLngStore } from '@/web/common/utils/i18n';
+import MySelect from '@/components/Select';
+import { useRouter } from 'next/router';
+
+const Individuation = () => {
+ const { t, i18n } = useTranslation();
+ const timezones = useRef(timezoneList());
+ const { userInfo, updateUserInfo, initUserInfo } = useUserStore();
+ const { toast } = useToast();
+ const router = useRouter();
+
+ const { reset } = useForm({
+ defaultValues: userInfo as UserType
+ });
+
+ const onclickSave = useCallback(
+ async (data: UserType) => {
+ await updateUserInfo({
+ timezone: data.timezone
+ });
+ reset(data);
+ toast({
+ title: t('dataset.data.Update Success Tip'),
+ status: 'success'
+ });
+ },
+ [reset, t, toast, updateUserInfo]
+ );
+
+ return (
+
+
+
+ {t('support.account.Individuation')}
+
+
+
+
+ {t('user.Language')}:
+
+ ({
+ label: lang.label,
+ value: key
+ }))}
+ onchange={(val: any) => {
+ const lang = val;
+ setLngStore(lang);
+ router.replace(
+ {
+ query: router.query
+ },
+ router.asPath,
+ { locale: lang }
+ );
+ }}
+ />
+
+
+
+ {t('user.Timezone')}:
+
+
+
+
+ );
+};
+
+export default Individuation;
diff --git a/projects/app/src/pages/account/components/Info.tsx b/projects/app/src/pages/account/components/Info.tsx
index a957475beb4..646977b52f2 100644
--- a/projects/app/src/pages/account/components/Info.tsx
+++ b/projects/app/src/pages/account/components/Info.tsx
@@ -1,15 +1,14 @@
-import React, { useCallback, useMemo, useRef } from 'react';
+import React, { useCallback, useMemo } from 'react';
import {
Box,
Flex,
Button,
useDisclosure,
useTheme,
- Divider,
- Select,
Input,
Link,
- Progress
+ Progress,
+ Grid
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { UserUpdateParams } from '@/types/user';
@@ -22,35 +21,72 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
-import { timezoneList } from '@fastgpt/global/common/time/timezone';
import Avatar from '@/components/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@/components/MyTooltip';
-import { langMap, setLngStore } from '@/web/common/utils/i18n';
import { useRouter } from 'next/router';
-import MySelect from '@/components/Select';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
+import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
import { putUpdateMemberName } from '@/web/support/user/team/api';
import { getDocPath } from '@/web/common/system/doc';
-import { getTeamDatasetValidSub } from '@/web/support/wallet/sub/api';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
+import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
+import { formatTime2YMD } from '@fastgpt/global/common/string/time';
+import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
+import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList';
+const StandDetailModal = dynamic(() => import('./standardDetailModal'));
const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu'));
const PayModal = dynamic(() => import('./PayModal'));
const UpdatePswModal = dynamic(() => import('./UpdatePswModal'));
const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal'));
-const SubDatasetModal = dynamic(() => import('@/components/support/wallet/SubDatasetModal'));
-const UserInfo = () => {
+const Account = () => {
+ const { isPc } = useSystemStore();
+ const { teamPlanStatus } = useUserStore();
+ const standardPlan = teamPlanStatus?.standardConstants;
+
+ const { initUserInfo } = useUserStore();
+
+ useQuery(['init'], initUserInfo);
+
+ return (
+
+ {isPc ? (
+
+
+
+
+
+
+
+ {!!standardPlan && (
+
+
+
+ )}
+
+ ) : (
+ <>
+
+ {!!standardPlan && }
+
+ >
+ )}
+
+ );
+};
+
+export default React.memo(Account);
+
+const MyInfo = () => {
const theme = useTheme();
- const router = useRouter();
- const { feConfigs, systemVersion } = useSystemStore();
- const { t, i18n } = useTranslation();
- const { userInfo, updateUserInfo, initUserInfo } = useUserStore();
- const timezones = useRef(timezoneList());
+ const { feConfigs } = useSystemStore();
+ const { t } = useTranslation();
+ const { userInfo, updateUserInfo } = useUserStore();
const { reset } = useForm({
defaultValues: userInfo as UserType
});
+ const { isPc } = useSystemStore();
const { toast } = useToast();
const {
@@ -63,13 +99,6 @@ const UserInfo = () => {
onClose: onCloseUpdatePsw,
onOpen: onOpenUpdatePsw
} = useDisclosure();
- const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure();
- const {
- isOpen: isOpenSubDatasetModal,
- onClose: onCloseSubDatasetModal,
- onOpen: onOpenSubDatasetModal
- } = useDisclosure();
-
const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png',
multiple: false
@@ -117,81 +146,73 @@ const UserInfo = () => {
[onclickSave, t, toast, userInfo]
);
- useQuery(['init'], initUserInfo, {
- onSuccess(res) {
- reset(res);
- }
- });
-
- const {
- data: teamSubPlan = { totalPoints: 0, usedPoints: 0, datasetMaxSize: 800, usedDatasetSize: 0 }
- } = useQuery(['getTeamDatasetValidSub'], getTeamDatasetValidSub);
- const datasetUsageMap = useMemo(() => {
- const rate = teamSubPlan.usedDatasetSize / teamSubPlan.datasetMaxSize;
-
- const colorScheme = (() => {
- if (rate < 0.5) return 'green';
- if (rate < 0.8) return 'yellow';
- return 'red';
- })();
+ return (
+
+ {/* user info */}
+ {isPc && (
+
+
+ {t('support.user.User self info')}
+
+ )}
- return {
- colorScheme,
- value: rate * 100,
- maxSize: teamSubPlan.datasetMaxSize || t('common.Unlimited'),
- usedSize: teamSubPlan.usedDatasetSize
- };
- }, [teamSubPlan.usedDatasetSize, teamSubPlan.datasetMaxSize, t]);
+
+ {isPc ? (
+
+ {t('support.user.Avatar')}:
- return (
-
-
-
-
+
+
+
+
+
+ ) : (
+
-
-
-
+
+
+
+
+
-
-
- {t('user.Replace')}
-
-
-
+
+
+ {t('user.Replace')}
+
+
+ )}
{feConfigs.isPlus && (
-
+
{t('user.Member Name')}:
{
@@ -204,109 +225,269 @@ const UserInfo = () => {
/>
)}
-
+
{t('user.Account')}:
{userInfo?.username}
-
+ {feConfigs.isPlus && (
+
+ {t('user.Password')}:
+ *****
+
+ {t('user.Change')}
+
+
+ )}
+
{t('user.Team')}:
-
- {t('user.Language')}:
-
- ({
- label: lang.label,
- value: key
- }))}
- onchange={(val: any) => {
- const lang = val;
- setLngStore(lang);
- router.replace(router.basePath, router.asPath, { locale: lang });
- }}
- />
+ {feConfigs.isPlus && (
+
+
+
+ {t('user.team.Balance')}:
+
+
+ {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)} 元
+
+ {feConfigs?.show_pay && userInfo?.team?.canWrite && (
+
+ {t('user.Pay')}
+
+ )}
+
+ )}
+
+ {isOpenPayModal && }
+ {isOpenUpdatePsw && }
+
+
+ );
+};
+const PlanUsage = () => {
+ const { isPc } = useSystemStore();
+ const router = useRouter();
+ const { t } = useTranslation();
+ const { userInfo, initUserInfo, teamPlanStatus } = useUserStore();
+ const { reset } = useForm({
+ defaultValues: userInfo as UserType
+ });
+
+ const {
+ isOpen: isOpenStandardModal,
+ onClose: onCloseStandardModal,
+ onOpen: onOpenStandardModal
+ } = useDisclosure();
+
+ const planName = useMemo(() => {
+ if (!teamPlanStatus?.standard?.currentSubLevel) return '';
+ return standardSubLevelMap[teamPlanStatus.standard.currentSubLevel].label;
+ }, [teamPlanStatus?.standard?.currentSubLevel]);
+ const standardPlan = teamPlanStatus?.standard;
+
+ useQuery(['init'], initUserInfo, {
+ onSuccess(res) {
+ reset(res);
+ }
+ });
+
+ const datasetUsageMap = useMemo(() => {
+ if (!teamPlanStatus) {
+ return {
+ colorScheme: 'green',
+ value: 0,
+ maxSize: t('common.Unlimited'),
+ usedSize: 0
+ };
+ }
+ const rate = teamPlanStatus.usedDatasetSize / teamPlanStatus.datasetMaxSize;
+
+ const colorScheme = (() => {
+ if (rate < 0.5) return 'green';
+ if (rate < 0.8) return 'yellow';
+ return 'red';
+ })();
+
+ return {
+ colorScheme,
+ value: rate * 100,
+ maxSize: teamPlanStatus.datasetMaxSize || t('common.Unlimited'),
+ usedSize: teamPlanStatus.usedDatasetSize
+ };
+ }, [teamPlanStatus, t]);
+ const aiPointsUsageMap = useMemo(() => {
+ if (!teamPlanStatus) {
+ return {
+ colorScheme: 'green',
+ value: 0,
+ maxSize: t('common.Unlimited'),
+ usedSize: 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('common.Unlimited'),
+ used: teamPlanStatus.usedPoints ? Math.round(teamPlanStatus.usedPoints) : 0
+ };
+ }, [teamPlanStatus, t]);
+
+ return standardPlan ? (
+
+
+
+
+ {t('support.wallet.subscription.Team plan and usage')}
-
- {t('user.Timezone')}:
-
-
-
- {t('user.Password')}:
- *****
-
- {t('user.Change')}
+ router.push(AI_POINT_USAGE_CARD_ROUTE)}>
+ {t('support.user.Price')}
+
+
+ {t('support.wallet.Standard Plan Detail')}
+
+
+
+
+
+
+ {t('support.wallet.subscription.Current plan')}
+
+
+ {t(planName)}
+
+
+ {t('common.Expired Time')}:
+ {formatTime2YMD(standardPlan?.expiredTime)}
+
+
+ router.push('/price')}>
+ {t('support.wallet.subscription.Upgrade plan')}
- {feConfigs.isPlus && (
- <>
-
-
-
- {t('user.team.Balance')}:
-
-
- {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)} 元
-
- {feConfigs?.show_pay && userInfo?.team?.canWrite && (
-
- {t('user.Pay')}
-
- )}
-
-
- {feConfigs?.show_pay && (
-
-
-
- {t('support.user.team.Dataset usage')}: {datasetUsageMap.usedSize}/
- {datasetUsageMap.maxSize}
-
- {userInfo?.team?.canWrite && (
-
- {t('support.wallet.Buy more')}
-
- )}
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {t('support.user.team.Dataset usage')}
+
+ {datasetUsageMap.usedSize}/{datasetUsageMap.maxSize}
- )}
- >
- )}
+
+
+
+
+
+
+
+
+
+ {t('support.wallet.subscription.AI points')}
+
+ {aiPointsUsageMap.used}/{aiPointsUsageMap.max}
+
+
+
+
+
+
+
+
+
+ {isOpenStandardModal && }
+
+ ) : null;
+};
+const Other = () => {
+ const theme = useTheme();
+ const { toast } = useToast();
+ const { feConfigs, systemVersion } = useSystemStore();
+ const { t } = useTranslation();
+ const { userInfo, updateUserInfo, initUserInfo, teamPlanStatus } = useUserStore();
+ const { reset } = useForm({
+ defaultValues: userInfo as UserType
+ });
+
+ const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure();
+ const onclickSave = useCallback(
+ async (data: UserType) => {
+ await updateUserInfo({
+ avatar: data.avatar,
+ timezone: data.timezone,
+ openaiAccount: data.openaiAccount
+ });
+ reset(data);
+ toast({
+ title: '更新数据成功',
+ status: 'success'
+ });
+ },
+ [reset, toast, updateUserInfo]
+ );
+
+ return (
+
+
{feConfigs?.docUrl && (
{
)}
- {feConfigs?.chatbotUrl && (
-
+
+
+ {t('common.system.Help Chatbot')}
+
+
+
+ {feConfigs?.show_openai_account && (
+
-
+
- {t('common.system.Help Chatbot')}
+ OpenAI/OneAPI 账号
-
- )}
- {feConfigs?.show_openai_account && (
- <>
-
-
-
-
-
-
- OpenAI/OneAPI 账号
-
-
-
-
- >
+
+
)}
-
+
- {isOpenPayModal && }
- {isOpenUpdatePsw && }
{isOpenOpenai && userInfo && (
{
onClose={onCloseOpenai}
/>
)}
- {isOpenSubDatasetModal && }
-
);
};
-
-export default React.memo(UserInfo);
diff --git a/projects/app/src/pages/account/components/OpenAIAccountModal.tsx b/projects/app/src/pages/account/components/OpenAIAccountModal.tsx
index 5b8ed0757b0..81d22f85d36 100644
--- a/projects/app/src/pages/account/components/OpenAIAccountModal.tsx
+++ b/projects/app/src/pages/account/components/OpenAIAccountModal.tsx
@@ -37,8 +37,9 @@ const OpenAIAccountModal = ({
>
- 可以填写 OpenAI/OneAPI 的相关秘钥。如果你填写了该内容,在线上平台使用 OpenAI Chat
- 模型不会计费(不包含知识库训练、索引生成)。请注意你的 Key 是否有访问对应模型的权限。
+ 可以填写 OpenAI/OneAPI
+ 的相关秘钥。如果你填写了该内容,在线上平台使用【AI对话】、【问题分类】和【内容提取】将会走你填写的Key,不会计费。请注意你的
+ Key 是否有访问对应模型的权限。GPT模型可以选择 FastAI。
API Key:
diff --git a/projects/app/src/pages/account/components/PayModal.tsx b/projects/app/src/pages/account/components/PayModal.tsx
index a397f409fac..b61e3211cc8 100644
--- a/projects/app/src/pages/account/components/PayModal.tsx
+++ b/projects/app/src/pages/account/components/PayModal.tsx
@@ -1,37 +1,46 @@
-import React, { useState, useCallback } from 'react';
+import React, { useState, useCallback, useMemo } from 'react';
import { ModalFooter, ModalBody, Button, Input, Box, Grid } from '@chakra-ui/react';
-import { getPayCode, checkPayResult } from '@/web/support/wallet/pay/api';
+import { getWxPayQRCode } from '@/web/support/wallet/bill/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
-import { useQuery } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next';
-import Markdown from '@/components/Markdown';
import MyModal from '@/components/MyModal';
+import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
-const PayModal = ({ onClose }: { onClose: () => void }) => {
- const router = useRouter();
+import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
+import { useSystemStore } from '@/web/common/system/useSystemStore';
+
+const PayModal = ({
+ onClose,
+ defaultValue,
+ onSuccess
+}: {
+ defaultValue?: number;
+ onClose: () => void;
+ onSuccess?: () => any;
+}) => {
const { t } = useTranslation();
const { toast } = useToast();
- const [inputVal, setInputVal] = useState('');
+ const { subPlans } = useSystemStore();
+ const [inputVal, setInputVal] = useState(defaultValue);
const [loading, setLoading] = useState(false);
- const [payId, setPayId] = useState('');
+ const [qrPayData, setQRPayData] = useState();
const handleClickPay = useCallback(async () => {
if (!inputVal || inputVal <= 0 || isNaN(+inputVal)) return;
setLoading(true);
try {
// 获取支付二维码
- const res = await getPayCode(inputVal);
- new window.QRCode(document.getElementById('payQRCode'), {
- text: res.codeUrl,
- width: 128,
- height: 128,
- colorDark: '#000000',
- colorLight: '#ffffff',
- correctLevel: window.QRCode.CorrectLevel.H
+ const res = await getWxPayQRCode({
+ type: BillTypeEnum.balance,
+ balance: inputVal
+ });
+ setQRPayData({
+ readPrice: res.readPrice,
+ codeUrl: res.codeUrl,
+ billId: res.billId
});
- setPayId(res.payId);
} catch (err) {
toast({
title: getErrText(err),
@@ -41,84 +50,57 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
setLoading(false);
}, [inputVal, toast]);
- useQuery(
- [payId],
- () => {
- if (!payId) return null;
- return checkPayResult(payId);
- },
- {
- enabled: !!payId,
- refetchInterval: 3000,
- onSuccess(res) {
- if (!res) return;
- toast({
- title: res,
- status: 'success'
- });
- router.reload();
- }
- }
- );
+ const payList = useMemo(() => {
+ const list = Object.values(subPlans?.standard || {});
+ const priceList = list.map((item) => item.price);
+ return priceList.concat(priceList.map((item) => item * 10)).filter(Boolean);
+ }, [subPlans?.standard]);
return (
-
+
- {!payId && (
- <>
-
- {[10, 20, 50, 100, 200, 500].map((item) => (
- setInputVal(item)}
- >
- {item}元
-
- ))}
-
-
- {
- setInputVal(Math.floor(+e.target.value));
- }}
- >
-
- >
- )}
- {/* 付费二维码 */}
-
- {payId && 请微信扫码支付: {inputVal}元,请勿关闭页面}
-
+
+ 该余额仅用于自动续费标准套餐。如需购买额外套餐,可直接下单,无需充值余额。
-
-
-
- {!payId && (
- <>
-
- {t('common.Close')}
-
+
+ {payList.map((item) => (
setInputVal(item)}
>
- 获取充值二维码
+ {item}元
- >
- )}
+ ))}
+
+
+ {
+ setInputVal(Math.floor(+e.target.value));
+ }}
+ >
+
+
+
+
+
+ {t('common.Close')}
+
+
+ 获取充值二维码
+
+
+ {!!qrPayData && }
);
};
diff --git a/projects/app/src/pages/account/components/PayRecordTable.tsx b/projects/app/src/pages/account/components/PayRecordTable.tsx
deleted file mode 100644
index e5567764b2c..00000000000
--- a/projects/app/src/pages/account/components/PayRecordTable.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import React, { useState, useCallback } from 'react';
-import {
- Button,
- Table,
- Thead,
- Tbody,
- Tr,
- Th,
- Td,
- TableContainer,
- Flex,
- Box
-} from '@chakra-ui/react';
-import { getPayOrders, checkPayResult } from '@/web/support/wallet/pay/api';
-import type { PaySchema } from '@fastgpt/global/support/wallet/pay/type.d';
-import dayjs from 'dayjs';
-import { useQuery } from '@tanstack/react-query';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
-import { useToast } from '@fastgpt/web/hooks/useToast';
-import { useLoading } from '@/web/common/hooks/useLoading';
-import MyIcon from '@fastgpt/web/components/common/Icon';
-
-const PayRecordTable = () => {
- const { Loading, setIsLoading } = useLoading();
- const [payOrders, setPayOrders] = useState([]);
- const { toast } = useToast();
-
- const { isInitialLoading, refetch } = useQuery(['initPayOrder'], getPayOrders, {
- onSuccess(res) {
- setPayOrders(res);
- }
- });
-
- const handleRefreshPayOrder = useCallback(
- async (payId: string) => {
- setIsLoading(true);
-
- try {
- const data = await checkPayResult(payId);
- toast({
- title: data,
- status: 'success'
- });
- } catch (error: any) {
- toast({
- title: error?.message,
- status: 'warning'
- });
- console.log(error);
- }
- try {
- refetch();
- } catch (error) {}
-
- setIsLoading(false);
- },
- [refetch, setIsLoading, toast]
- );
-
- return (
-
- {!isInitialLoading && payOrders.length === 0 ? (
-
-
-
- 无支付记录~
-
-
- ) : (
-
-
-
-
- 订单号 |
- 时间 |
- 金额 |
- 状态 |
- |
-
-
-
- {payOrders.map((item) => (
-
- {item.orderId} |
-
- {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'}
- |
- {formatStorePrice2Read(item.price)}元 |
- {item.status} |
-
- {item.status === 'NOTPAY' && (
- handleRefreshPayOrder(item._id)} size={'sm'}>
- 更新
-
- )}
- |
-
- ))}
-
-
-
- )}
-
-
- );
-};
-
-export default PayRecordTable;
diff --git a/projects/app/src/pages/account/components/UsageDetail.tsx b/projects/app/src/pages/account/components/UsageDetail.tsx
new file mode 100644
index 00000000000..d75cd5c2ce9
--- /dev/null
+++ b/projects/app/src/pages/account/components/UsageDetail.tsx
@@ -0,0 +1,119 @@
+import React, { useMemo } from 'react';
+import {
+ ModalBody,
+ Flex,
+ Box,
+ Table,
+ Thead,
+ Tbody,
+ Tr,
+ Th,
+ Td,
+ TableContainer
+} from '@chakra-ui/react';
+import { UsageItemType } from '@fastgpt/global/support/wallet/usage/type.d';
+import dayjs from 'dayjs';
+import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
+import MyModal from '@/components/MyModal';
+import { useTranslation } from 'next-i18next';
+import { formatNumber } from '@fastgpt/global/common/math/tools';
+
+const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () => void }) => {
+ const { t } = useTranslation();
+ const filterBillList = useMemo(
+ () => usage.list.filter((item) => item && item.moduleName),
+ [usage.list]
+ );
+
+ const { hasModel, hasCharsLen, hasDuration } = useMemo(() => {
+ let hasModel = false;
+ let hasCharsLen = false;
+ let hasDuration = false;
+ let hasDataLen = false;
+
+ usage.list.forEach((item) => {
+ if (item.model !== undefined) {
+ hasModel = true;
+ }
+
+ if (typeof item.charsLength === 'number') {
+ hasCharsLen = true;
+ }
+ if (typeof item.duration === 'number') {
+ hasDuration = true;
+ }
+ });
+
+ return {
+ hasModel,
+ hasCharsLen,
+ hasDuration,
+ hasDataLen
+ };
+ }, [usage.list]);
+
+ return (
+
+
+
+ {t('support.wallet.bill.Number')}:
+ {usage.id}
+
+
+ {t('support.wallet.usage.Time')}:
+ {dayjs(usage.time).format('YYYY/MM/DD HH:mm:ss')}
+
+
+ {t('support.wallet.usage.App name')}:
+ {t(usage.appName) || '-'}
+
+
+ {t('support.wallet.usage.Source')}:
+ {t(UsageSourceMap[usage.source]?.label)}
+
+
+ {t('support.wallet.usage.Total points')}:
+ {formatNumber(usage.totalPoints)}
+
+
+
+ {t('support.wallet.usage.Bill Module')}
+
+
+
+
+
+ {t('support.wallet.usage.Module name')} |
+ {hasModel && {t('support.wallet.usage.Ai model')} | }
+ {hasCharsLen && {t('support.wallet.usage.Text Length')} | }
+ {hasDuration && {t('support.wallet.usage.Duration')} | }
+
+ {t('support.wallet.usage.Total points')} |
+
+
+
+ {filterBillList.map((item, i) => (
+
+ {t(item.moduleName)} |
+ {hasModel && {item.model ?? '-'} | }
+ {hasCharsLen && {item.charsLength ?? '-'} | }
+ {hasDuration && {item.duration ?? '-'} | }
+ {formatNumber(item.amount)} |
+
+ ))}
+
+
+
+
+
+
+ );
+};
+
+export default UsageDetail;
diff --git a/projects/app/src/pages/account/components/UsageTable.tsx b/projects/app/src/pages/account/components/UsageTable.tsx
new file mode 100644
index 00000000000..d73f779def6
--- /dev/null
+++ b/projects/app/src/pages/account/components/UsageTable.tsx
@@ -0,0 +1,189 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import {
+ Table,
+ Thead,
+ Tbody,
+ Tr,
+ Th,
+ Td,
+ TableContainer,
+ Flex,
+ Box,
+ Button
+} from '@chakra-ui/react';
+import { UsageSourceEnum, UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
+import { getUserUsages } from '@/web/support/wallet/usage/api';
+import type { UsageItemType } from '@fastgpt/global/support/wallet/usage/type';
+import { usePagination } from '@/web/common/hooks/usePagination';
+import { useLoading } from '@/web/common/hooks/useLoading';
+import dayjs from 'dayjs';
+import MyIcon from '@fastgpt/web/components/common/Icon';
+import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
+import { addDays } from 'date-fns';
+import dynamic from 'next/dynamic';
+import { useSystemStore } from '@/web/common/system/useSystemStore';
+import { useTranslation } from 'next-i18next';
+import MySelect from '@/components/Select';
+import { useQuery } from '@tanstack/react-query';
+import { useUserStore } from '@/web/support/user/useUserStore';
+import { getTeamMembers } from '@/web/support/user/team/api';
+import Avatar from '@/components/Avatar';
+import { formatNumber } from '../../../../../../packages/global/common/math/tools';
+const UsageDetail = dynamic(() => import('./UsageDetail'));
+
+const UsageTable = () => {
+ const { t } = useTranslation();
+ const { Loading } = useLoading();
+ const [dateRange, setDateRange] = useState({
+ from: addDays(new Date(), -7),
+ to: new Date()
+ });
+ const [usageSource, setUsageSource] = useState<`${UsageSourceEnum}` | ''>('');
+ const { isPc } = useSystemStore();
+ const { userInfo } = useUserStore();
+ const [usageDetail, setUsageDetail] = useState();
+
+ const sourceList = useMemo(
+ () => [
+ { label: t('common.All'), value: '' },
+ ...Object.entries(UsageSourceMap).map(([key, value]) => ({
+ label: t(value.label),
+ value: key
+ }))
+ ],
+ [t]
+ );
+
+ const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId);
+ const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => {
+ if (!userInfo?.team?.teamId) return [];
+ return getTeamMembers(userInfo.team.teamId);
+ });
+ const tmbList = useMemo(
+ () =>
+ members.map((item) => ({
+ label: (
+
+
+ {item.memberName}
+
+ ),
+ value: item.tmbId
+ })),
+ [members]
+ );
+
+ const {
+ data: usages,
+ isLoading,
+ Pagination,
+ getData
+ } = usePagination({
+ api: getUserUsages,
+ pageSize: isPc ? 20 : 10,
+ params: {
+ dateStart: dateRange.from || new Date(),
+ dateEnd: addDays(dateRange.to || new Date(), 1),
+ source: usageSource,
+ teamMemberId: selectTmbId
+ },
+ defaultRequest: false
+ });
+
+ useEffect(() => {
+ getData(1);
+ }, [usageSource, selectTmbId]);
+
+ return (
+
+
+ {tmbList.length > 1 && userInfo?.team?.canWrite && (
+
+
+ {t('support.user.team.member')}
+
+
+
+ )}
+
+
+ getData(1)}
+ />
+
+
+
+
+
+
+
+ {/* {t('user.team.Member Name')} | */}
+ {t('user.Time')} |
+
+ {
+ setUsageSource(e);
+ }}
+ w={'130px'}
+ >
+ |
+ {t('user.Application Name')} |
+ {t('support.wallet.usage.Total points')} |
+ |
+
+
+
+ {usages.map((item) => (
+
+ {/* {item.memberName} | */}
+ {dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')} |
+ {t(UsageSourceMap[item.source]?.label) || '-'} |
+ {t(item.appName) || '-'} |
+ {formatNumber(item.totalPoints) || 0} |
+
+ setUsageDetail(item)}>
+ 详情
+
+ |
+
+ ))}
+
+
+
+
+ {!isLoading && usages.length === 0 && (
+
+
+
+ 无使用记录~
+
+
+ )}
+
+
+ {!!usageDetail && (
+ setUsageDetail(undefined)} />
+ )}
+
+ );
+};
+
+export default React.memo(UsageTable);
diff --git a/projects/app/src/pages/account/components/standardDetailModal.tsx b/projects/app/src/pages/account/components/standardDetailModal.tsx
new file mode 100644
index 00000000000..da86ed901a5
--- /dev/null
+++ b/projects/app/src/pages/account/components/standardDetailModal.tsx
@@ -0,0 +1,103 @@
+import React from 'react';
+import {
+ ModalBody,
+ ModalFooter,
+ Table,
+ Thead,
+ Tbody,
+ Tr,
+ Th,
+ Td,
+ TableContainer,
+ ModalCloseButton
+} from '@chakra-ui/react';
+import MyModal from '@/components/MyModal';
+import { useTranslation } from 'next-i18next';
+import { useQuery } from '@tanstack/react-query';
+import { useLoading } from '@/web/common/hooks/useLoading';
+import MyIcon from '@fastgpt/web/components/common/Icon';
+import { getTeamPlans } from '@/web/support/user/team/api';
+import { subTypeMap, standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
+import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
+import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
+import { useSystemStore } from '@/web/common/system/useSystemStore';
+const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
+ const { t } = useTranslation();
+ const { Loading } = useLoading();
+ const { subPlans } = useSystemStore();
+ const { data: teamPlans = [], isLoading } = useQuery(['getTeamPlans'], getTeamPlans);
+
+ return (
+
+
+
+
+
+
+
+ {t('support.standard.type')} |
+ {t('support.standard.storage')} |
+ {t('support.standard.AI Bonus Points')} |
+ {t('support.standard.Start Time')} |
+ {t('support.standard.Expired Time')} |
+ |
+
+
+
+ {teamPlans.map(
+ ({
+ _id,
+ type,
+ currentSubLevel,
+ currentExtraDatasetSize,
+ surplusPoints = 0,
+ totalPoints = 0,
+ startTime,
+ expiredTime
+ }: TeamSubSchema) => {
+ const standardPlan = currentSubLevel
+ ? subPlans?.standard?.[currentSubLevel]
+ : undefined;
+ const datasetSize = standardPlan?.maxDatasetSize || currentExtraDatasetSize;
+
+ return (
+
+
+
+ {t(subTypeMap[type]?.label)}
+ {currentSubLevel && `(${t(standardSubLevelMap[currentSubLevel]?.label)})`}
+ |
+ {datasetSize ? `${datasetSize}组` : '-'} |
+
+ {totalPoints
+ ? `${Math.round(totalPoints - surplusPoints)} / ${totalPoints} 积分`
+ : '-'}
+ |
+ {formatTime2YMDHM(startTime)} |
+ {formatTime2YMDHM(expiredTime)} |
+
+ );
+ }
+ )}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default StandDetailModal;
diff --git a/projects/app/src/pages/account/index.tsx b/projects/app/src/pages/account/index.tsx
index 774a41562e1..9a1941118bf 100644
--- a/projects/app/src/pages/account/index.tsx
+++ b/projects/app/src/pages/account/index.tsx
@@ -14,19 +14,19 @@ import { useTranslation } from 'next-i18next';
import Script from 'next/script';
const Promotion = dynamic(() => import('./components/Promotion'));
+const UsageTable = dynamic(() => import('./components/UsageTable'));
const BillTable = dynamic(() => import('./components/BillTable'));
-const PayRecordTable = dynamic(() => import('./components/PayRecordTable'));
const InformTable = dynamic(() => import('./components/InformTable'));
const ApiKeyTable = dynamic(() => import('./components/ApiKeyTable'));
-const PriceBox = dynamic(() => import('@/components/support/wallet/Price'));
+const Individuation = dynamic(() => import('./components/Individuation'));
enum TabEnum {
'info' = 'info',
'promotion' = 'promotion',
+ 'usage' = 'usage',
'bill' = 'bill',
- 'price' = 'price',
- 'pay' = 'pay',
'inform' = 'inform',
+ 'individuation' = 'individuation',
'apikey' = 'apikey',
'loginout' = 'loginout'
}
@@ -45,27 +45,18 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
...(feConfigs?.isPlus
? [
{
- icon: 'support/bill/billRecordLight',
+ icon: 'support/usage/usageRecordLight',
label: t('user.Usage Record'),
- id: TabEnum.bill
+ id: TabEnum.usage
}
]
: []),
...(feConfigs?.show_pay && userInfo?.team.canWrite
? [
{
- icon: 'support/pay/payRecordLight',
- label: t('user.Recharge Record'),
- id: TabEnum.pay
- }
- ]
- : []),
- ...(feConfigs?.show_pay
- ? [
- {
- icon: 'support/pay/priceLight',
- label: t('support.user.Price'),
- id: TabEnum.price
+ icon: 'support/bill/payRecordLight',
+ label: t('support.wallet.Bills'),
+ id: TabEnum.bill
}
]
: []),
@@ -88,6 +79,11 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
}
]
: []),
+ {
+ icon: 'support/user/individuation',
+ label: t('support.account.Individuation'),
+ id: TabEnum.individuation
+ },
...(feConfigs.isPlus
? [
{
@@ -108,11 +104,6 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
const { openConfirm, ConfirmModal } = useConfirm({
content: '确认退出登录?'
});
- const {
- isOpen: isOpenPriceBox,
- onOpen: onOpenPriceBox,
- onClose: onClosePriceBox
- } = useDisclosure();
const router = useRouter();
const theme = useTheme();
@@ -124,8 +115,6 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
setUserInfo(null);
router.replace('/login');
})();
- } else if (tab === TabEnum.price) {
- onOpenPriceBox();
} else {
router.replace({
query: {
@@ -134,7 +123,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
});
}
},
- [onOpenPriceBox, openConfirm, router, setUserInfo]
+ [openConfirm, router, setUserInfo]
);
return (
@@ -178,16 +167,15 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
{currentTab === TabEnum.info && }
{currentTab === TabEnum.promotion && }
+ {currentTab === TabEnum.usage && }
{currentTab === TabEnum.bill && }
- {currentTab === TabEnum.pay && }
+ {currentTab === TabEnum.individuation && }
{currentTab === TabEnum.inform && }
{currentTab === TabEnum.apikey && }
-
- {isOpenPriceBox && }
>
);
};
diff --git a/projects/app/src/pages/api/admin/initv468.ts b/projects/app/src/pages/api/admin/initv468.ts
new file mode 100644
index 00000000000..02caa11f14c
--- /dev/null
+++ b/projects/app/src/pages/api/admin/initv468.ts
@@ -0,0 +1,99 @@
+import type { NextApiRequest, NextApiResponse } from 'next';
+import { jsonRes } from '@fastgpt/service/common/response';
+import { connectToDatabase } from '@/service/mongo';
+import { authCert } from '@fastgpt/service/support/permission/auth/common';
+import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
+import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants';
+import { MongoImage } from '@fastgpt/service/common/file/image/schema';
+import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type';
+import { delay } from '@fastgpt/global/common/system/utils';
+import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
+import { getNanoid } from '@fastgpt/global/common/string/tools';
+import { MongoApp } from '@fastgpt/service/core/app/schema';
+import { ModuleItemType } from '@fastgpt/global/core/module/type';
+import { DYNAMIC_INPUT_KEY, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
+
+let success = 0;
+let deleteImg = 0;
+
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ try {
+ // 设置所有app为 inited = false
+ const result = await MongoApp.updateMany({}, { $set: { inited: false } });
+ console.log(result);
+
+ await initApp();
+
+ jsonRes(res, {
+ message: 'success'
+ });
+ } catch (error) {
+ console.log(error);
+
+ jsonRes(res, {
+ code: 500,
+ error
+ });
+ }
+}
+
+const systemKeys: string[] = [
+ ModuleInputKeyEnum.switch,
+ ModuleInputKeyEnum.httpMethod,
+ ModuleInputKeyEnum.httpReqUrl,
+ ModuleInputKeyEnum.httpHeaders,
+ DYNAMIC_INPUT_KEY,
+ ModuleInputKeyEnum.addInputParam
+];
+const initApp = async (): Promise => {
+ const app = await MongoApp.findOne({ inited: false }).sort({ updateTime: -1 });
+ if (!app) {
+ return;
+ }
+
+ try {
+ const modules = JSON.parse(JSON.stringify(app.modules)) as ModuleItemType[];
+ let update = false;
+ // 找到http模块
+ modules.forEach((module) => {
+ if (module.flowType === 'httpRequest') {
+ const method = module.inputs.find((input) => input.key === ModuleInputKeyEnum.httpMethod);
+ if (method?.value === 'POST') {
+ module.inputs.forEach((input) => {
+ // 更新非系统字段的key
+ if (!systemKeys.includes(input.key)) {
+ // 更新output的target
+ modules.forEach((item) => {
+ item.outputs.forEach((output) => {
+ output.targets.forEach((target) => {
+ if (target.moduleId === module.moduleId && target.key === input.key) {
+ target.key = `data.${input.key}`;
+ }
+ });
+ });
+ });
+ // 更新key
+ input.key = `data.${input.key}`;
+ update = true;
+ }
+ });
+ }
+ }
+ });
+
+ if (update) {
+ console.log('update http app');
+ app.modules = modules;
+ }
+ app.inited = true;
+ await app.save();
+
+ console.log(++success);
+ return initApp();
+ } catch (error) {
+ console.log(error);
+
+ await delay(1000);
+ return initApp();
+ }
+};
diff --git a/projects/app/src/pages/api/admin/initv469.ts b/projects/app/src/pages/api/admin/initv469.ts
new file mode 100644
index 00000000000..54bf086ca91
--- /dev/null
+++ b/projects/app/src/pages/api/admin/initv469.ts
@@ -0,0 +1,35 @@
+import type { NextApiRequest, NextApiResponse } from 'next';
+import { jsonRes } from '@fastgpt/service/common/response';
+import { connectToDatabase } from '@/service/mongo';
+import { authCert } from '@fastgpt/service/support/permission/auth/common';
+import { MongoUsage } from '@fastgpt/service/support/wallet/usage/schema';
+import { connectionMongo } from '@fastgpt/service/common/mongo';
+
+/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ try {
+ await connectToDatabase();
+ await authCert({ req, authRoot: true });
+
+ // 检查 usage 是否有记录
+ const totalUsages = await MongoUsage.countDocuments();
+ if (totalUsages === 0) {
+ // 重命名 bills 集合成 usages
+ await connectionMongo.connection.db.renameCollection('bills', 'usages', {
+ // 强制
+ dropTarget: true
+ });
+ }
+
+ jsonRes(res, {
+ message: 'success'
+ });
+ } catch (error) {
+ console.log(error);
+
+ jsonRes(res, {
+ code: 500,
+ error
+ });
+ }
+}
diff --git a/projects/app/src/pages/api/common/file/upload.ts b/projects/app/src/pages/api/common/file/upload.ts
index 3f3d135e5d6..efa9b2ad791 100644
--- a/projects/app/src/pages/api/common/file/upload.ts
+++ b/projects/app/src/pages/api/common/file/upload.ts
@@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
let filePaths: string[] = [];
try {
- const { userId, teamId, tmbId } = await authCert({ req, authToken: true });
+ const { teamId, tmbId } = await authCert({ req, authToken: true });
const { file, bucketName, metadata } = await upload.doUpload(req, res);
diff --git a/projects/app/src/pages/api/common/system/getInitData.ts b/projects/app/src/pages/api/common/system/getInitData.ts
index 9cdb7148cf7..40dd50bb970 100644
--- a/projects/app/src/pages/api/common/system/getInitData.ts
+++ b/projects/app/src/pages/api/common/system/getInitData.ts
@@ -6,8 +6,6 @@ import type { InitDateResponse } from '@/global/common/api/systemRes';
import type { FastGPTConfigFileType } from '@fastgpt/global/common/system/types/index.d';
import { getTikTokenEnc } from '@fastgpt/global/common/string/tiktoken';
import { initHttpAgent } from '@fastgpt/service/common/middle/httpAgent';
-import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants';
-import { getSimpleTemplatesFromPlus } from '@/service/core/app/utils';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { getFastGPTConfigFromDB } from '@fastgpt/service/common/system/config/controller';
import { connectToDatabase } from '@/service/mongo';
@@ -15,6 +13,7 @@ import { PluginTemplateType } from '@fastgpt/global/core/plugin/type';
import { readConfigData } from '@/service/common/system';
import { exit } from 'process';
import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
+import { initFastGPTConfig } from '@fastgpt/service/common/system/tools';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
await getInitConfig();
@@ -125,15 +124,8 @@ export async function initSystemConfig() {
};
// set config
- global.feConfigs = config.feConfigs;
+ initFastGPTConfig(config);
global.systemEnv = config.systemEnv;
- global.subPlans = config.subPlans;
-
- global.llmModels = config.llmModels;
- global.vectorModels = config.vectorModels;
- global.reRankModels = config.reRankModels;
- global.audioSpeechModels = config.audioSpeechModels;
- global.whisperModel = config.whisperModel;
console.log({
feConfigs: global.feConfigs,
diff --git a/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts b/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts
index 722c4983de6..a5132908517 100644
--- a/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts
+++ b/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import type { CreateQuestionGuideParams } from '@/global/core/ai/api.d';
-import { pushQuestionGuideBill } from '@/service/support/wallet/bill/push';
+import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push';
import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide';
import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common';
@@ -19,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const qgModel = global.llmModels[0];
- const { result, inputTokens, outputTokens } = await createQuestionGuide({
+ const { result, charsLength } = await createQuestionGuide({
messages,
model: qgModel.model
});
@@ -28,9 +28,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
data: result
});
- pushQuestionGuideBill({
- inputTokens,
- outputTokens,
+ pushQuestionGuideUsage({
+ charsLength,
teamId,
tmbId
});
diff --git a/projects/app/src/pages/api/core/app/create.ts b/projects/app/src/pages/api/core/app/create.ts
index 26e3bfe24f1..aa3f9a83853 100644
--- a/projects/app/src/pages/api/core/app/create.ts
+++ b/projects/app/src/pages/api/core/app/create.ts
@@ -6,6 +6,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants';
+import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -25,12 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true });
// 上限校验
- const authCount = await MongoApp.countDocuments({
- teamId
- });
- if (authCount >= 50) {
- throw new Error('每个团队上限 50 个应用');
- }
+ await checkTeamAppLimit(teamId);
// 创建模型
const response = await MongoApp.create({
diff --git a/projects/app/src/pages/api/core/app/data/totalUsage.ts b/projects/app/src/pages/api/core/app/data/totalUsage.ts
deleted file mode 100644
index 137c2edaa93..00000000000
--- a/projects/app/src/pages/api/core/app/data/totalUsage.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import type { NextApiRequest, NextApiResponse } from 'next';
-import { jsonRes } from '@fastgpt/service/common/response';
-import { connectToDatabase } from '@/service/mongo';
-import { authCert } from '@fastgpt/service/support/permission/auth/common';
-import { Types } from '@fastgpt/service/common/mongo';
-import { MongoBill } from '@fastgpt/service/support/wallet/bill/schema';
-
-export default async function handler(req: NextApiRequest, res: NextApiResponse) {
- try {
- await connectToDatabase();
- const { appId, start, end } = req.body as { appId: string; start: number; end: number };
- const { userId } = await authCert({ req, authToken: true });
-
- const result = await MongoBill.aggregate([
- {
- $match: {
- appId: new Types.ObjectId(appId),
- userId: new Types.ObjectId(userId),
- time: { $gte: new Date(start) }
- }
- },
- {
- $group: {
- _id: {
- year: { $year: '$time' },
- month: { $month: '$time' },
- day: { $dayOfMonth: '$time' }
- },
- total: { $sum: '$total' }
- }
- },
- {
- $project: {
- _id: 0,
- date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } },
- total: 1
- }
- },
- { $sort: { date: 1 } }
- ]);
-
- jsonRes(res, {
- data: result
- });
- } catch (err) {
- jsonRes(res, {
- code: 500,
- error: err
- });
- }
-}
diff --git a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts
index 3a23b484271..668b9c0dede 100644
--- a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts
+++ b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts
@@ -8,7 +8,6 @@ import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
import type { ModuleItemType } from '@fastgpt/global/core/module/type';
import { FormatForm2ModulesProps } from '@fastgpt/global/core/app/api';
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
-import { getLLMModel } from '@/service/core/ai/model';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -92,7 +91,7 @@ function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] {
},
{
key: 'model',
- type: 'selectChatModel',
+ type: 'selectLLMModel',
label: 'core.module.input.label.aiModel',
required: true,
valueType: 'string',
@@ -471,7 +470,7 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
},
{
key: 'model',
- type: 'selectChatModel',
+ type: 'selectLLMModel',
label: 'core.module.input.label.aiModel',
required: true,
valueType: 'string',
diff --git a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts
index 8b9a182637e..0aedf254d03 100644
--- a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts
+++ b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts
@@ -7,7 +7,6 @@ import { jsonRes } from '@fastgpt/service/common/response';
import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
import type { ModuleItemType } from '@fastgpt/global/core/module/type';
import { FormatForm2ModulesProps } from '@fastgpt/global/core/app/api';
-import { getLLMModel } from '@/service/core/ai/model';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -88,7 +87,7 @@ function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
},
{
key: 'model',
- type: 'selectChatModel',
+ type: 'selectLLMModel',
label: 'core.module.input.label.aiModel',
required: true,
valueType: 'string',
@@ -498,7 +497,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
},
{
key: 'model',
- type: 'selectChatModel',
+ type: 'selectLLMModel',
label: 'core.module.input.label.aiModel',
required: true,
valueType: 'string',
diff --git a/projects/app/src/pages/api/core/app/update.ts b/projects/app/src/pages/api/core/app/update.ts
index d8c00fb3f21..df90ee75d2e 100644
--- a/projects/app/src/pages/api/core/app/update.ts
+++ b/projects/app/src/pages/api/core/app/update.ts
@@ -6,13 +6,13 @@ import type { AppUpdateParams } from '@fastgpt/global/core/app/api';
import { authApp } from '@fastgpt/service/support/permission/auth/app';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
-import { getLLMModel } from '@/service/core/ai/model';
+import { getLLMModel } from '@fastgpt/service/core/ai/model';
/* 获取我的模型 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
- const { name, avatar, type, simpleTemplateId, intro, modules, permission } =
+ const { name, avatar, type, simpleTemplateId, intro, modules, permission, teamTags } =
req.body as AppUpdateParams;
const { appId } = req.query as { appId: string };
@@ -65,6 +65,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
avatar,
intro,
permission,
+ teamTags: teamTags,
...(modules && {
modules
})
diff --git a/projects/app/src/pages/api/core/app/updateTeamTasg.ts b/projects/app/src/pages/api/core/app/updateTeamTasg.ts
new file mode 100644
index 00000000000..0647c6eb38b
--- /dev/null
+++ b/projects/app/src/pages/api/core/app/updateTeamTasg.ts
@@ -0,0 +1,82 @@
+import type { NextApiRequest, NextApiResponse } from 'next';
+import { jsonRes } from '@fastgpt/service/common/response';
+import { connectToDatabase } from '@/service/mongo';
+import { MongoApp } from '@fastgpt/service/core/app/schema';
+import type { AppUpdateParams } from '@fastgpt/global/core/app/api';
+import { authApp } from '@fastgpt/service/support/permission/auth/app';
+import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
+import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
+import { getLLMModel } from '@fastgpt/service/core/ai/model';
+
+/* 获取我的模型 */
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ try {
+ await connectToDatabase();
+ const { name, avatar, type, simpleTemplateId, intro, modules, permission, teamTags } =
+ req.body as AppUpdateParams;
+ const { appId } = req.query as { appId: string };
+
+ if (!appId) {
+ throw new Error('appId is empty');
+ }
+
+ // 凭证校验
+ await authApp({ req, authToken: true, appId, per: permission ? 'owner' : 'w' });
+
+ // check modules
+ // 1. dataset search limit, less than model quoteMaxToken
+ if (modules) {
+ let maxTokens = 3000;
+
+ modules.forEach((item) => {
+ if (item.flowType === FlowNodeTypeEnum.chatNode) {
+ const model =
+ item.inputs.find((item) => item.key === ModuleInputKeyEnum.aiModel)?.value || '';
+ const chatModel = getLLMModel(model);
+ const quoteMaxToken = chatModel.quoteMaxToken || 3000;
+
+ maxTokens = Math.max(maxTokens, quoteMaxToken);
+ }
+ });
+
+ modules.forEach((item) => {
+ if (item.flowType === FlowNodeTypeEnum.datasetSearchNode) {
+ item.inputs.forEach((input) => {
+ if (input.key === ModuleInputKeyEnum.datasetMaxTokens) {
+ const val = input.value as number;
+ if (val > maxTokens) {
+ input.value = maxTokens;
+ }
+ }
+ });
+ }
+ });
+ }
+
+ // 更新模型
+ await MongoApp.findOneAndUpdate(
+ {
+ _id: appId
+ },
+ {
+ name,
+ type,
+ simpleTemplateId,
+ avatar,
+ intro,
+ permission,
+ teamTags: teamTags,
+ ...(modules && {
+ modules
+ })
+ }
+ );
+
+ jsonRes(res);
+ } catch (err) {
+ jsonRes(res, {
+ code: 500,
+ error: err
+ });
+ }
+}
diff --git a/projects/app/src/pages/api/core/chat/chatTest.ts b/projects/app/src/pages/api/core/chat/chatTest.ts
index f9b7aeddca9..bee667a46f8 100644
--- a/projects/app/src/pages/api/core/chat/chatTest.ts
+++ b/projects/app/src/pages/api/core/chat/chatTest.ts
@@ -4,13 +4,13 @@ import { sseErrRes } from '@fastgpt/service/common/response';
import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant';
import { responseWrite } from '@fastgpt/service/common/response';
import type { ModuleItemType } from '@fastgpt/global/core/module/type.d';
-import { pushChatBill } from '@/service/support/wallet/bill/push';
-import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
+import { pushChatUsage } from '@/service/support/wallet/usage/push';
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
import { authApp } from '@fastgpt/service/support/permission/auth/app';
import { dispatchModules } from '@/service/moduleDispatch';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
-import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
+import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
export type Props = {
history: ChatItemType[];
@@ -50,13 +50,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
]);
// auth balance
- const user = await getUserAndAuthBalance({
- tmbId,
- minBalance: 0
- });
+ const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
/* start process */
- const { responseData } = await dispatchModules({
+ const { responseData, moduleDispatchBills } = await dispatchModules({
res,
mode: 'test',
teamId,
@@ -85,13 +82,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
res.end();
- pushChatBill({
+ pushChatUsage({
appName,
appId,
teamId,
tmbId,
- source: BillSourceEnum.fastgpt,
- response: responseData
+ source: UsageSourceEnum.fastgpt,
+ moduleDispatchBills
});
} catch (err: any) {
res.status(500);
diff --git a/projects/app/src/pages/api/core/chat/clearHistories.ts b/projects/app/src/pages/api/core/chat/clearHistories.ts
index 99947245f59..8a5424053fc 100644
--- a/projects/app/src/pages/api/core/chat/clearHistories.ts
+++ b/projects/app/src/pages/api/core/chat/clearHistories.ts
@@ -14,10 +14,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await connectToDatabase();
const { appId, shareId, outLinkUid } = req.query as ClearHistoriesProps;
+ let chatAppId = appId;
+
const match = await (async () => {
if (shareId && outLinkUid) {
- const { uid } = await authOutLink({ shareId, outLinkUid });
+ const { appId, uid } = await authOutLink({ shareId, outLinkUid });
+ chatAppId = appId;
return {
shareId,
outLinkUid: uid
@@ -41,11 +44,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const idList = list.map((item) => item.chatId);
await MongoChatItem.deleteMany({
- appId,
+ appId: chatAppId,
chatId: { $in: idList }
});
await MongoChat.deleteMany({
- appId,
+ appId: chatAppId,
chatId: { $in: idList }
});
diff --git a/projects/app/src/pages/api/core/chat/getHistories.ts b/projects/app/src/pages/api/core/chat/getHistories.ts
index c81f8cae69d..97ca87fc659 100644
--- a/projects/app/src/pages/api/core/chat/getHistories.ts
+++ b/projects/app/src/pages/api/core/chat/getHistories.ts
@@ -28,6 +28,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
};
}
+ if (appId && outLinkUid) {
+ return {
+ shareId,
+ outLinkUid: outLinkUid,
+ source: ChatSourceEnum.team
+ };
+ }
if (appId) {
const { tmbId } = await authCert({ req, authToken: true });
return {
@@ -36,6 +43,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
source: ChatSourceEnum.online
};
}
+
return Promise.reject('Params are error');
})();
diff --git a/projects/app/src/pages/api/core/chat/item/getSpeech.ts b/projects/app/src/pages/api/core/chat/item/getSpeech.ts
index 17d9a647b94..f5763be81c0 100644
--- a/projects/app/src/pages/api/core/chat/item/getSpeech.ts
+++ b/projects/app/src/pages/api/core/chat/item/getSpeech.ts
@@ -3,10 +3,10 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { GetChatSpeechProps } from '@/global/core/chat/api.d';
import { text2Speech } from '@fastgpt/service/core/ai/audio/speech';
-import { pushAudioSpeechBill } from '@/service/support/wallet/bill/push';
+import { pushAudioSpeechUsage } from '@/service/support/wallet/usage/push';
import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common';
-import { authType2BillSource } from '@/service/support/wallet/bill/utils';
-import { getAudioSpeechModel } from '@/service/core/ai/model';
+import { authType2UsageSource } from '@/service/support/wallet/usage/utils';
+import { getAudioSpeechModel } from '@fastgpt/service/core/ai/model';
import { MongoTTSBuffer } from '@fastgpt/service/common/buffer/tts/schema';
/*
@@ -54,12 +54,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
speed: ttsConfig.speed,
onSuccess: async ({ model, buffer }) => {
try {
- pushAudioSpeechBill({
+ pushAudioSpeechUsage({
model: model,
charsLength: input.length,
tmbId,
teamId,
- source: authType2BillSource({ authType })
+ source: authType2UsageSource({ authType })
});
await MongoTTSBuffer.create({
diff --git a/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts b/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts
new file mode 100644
index 00000000000..224d9a07468
--- /dev/null
+++ b/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts
@@ -0,0 +1,37 @@
+import type { NextApiRequest, NextApiResponse } from 'next';
+import { jsonRes } from '@fastgpt/service/common/response';
+import { connectToDatabase } from '@/service/mongo';
+import type { chatByTeamProps } from '@/global/core/chat/api.d';
+import axios from 'axios';
+import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
+import { getChatItems } from '@fastgpt/service/core/chat/controller';
+import { selectShareResponse } from '@/utils/service/core/chat';
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ try {
+ await connectToDatabase();
+
+ let { teamId, appId, outLinkUid } = req.query as chatByTeamProps;
+
+ const history = await MongoChatItem.find({
+ appId: appId,
+ outLinkUid: outLinkUid,
+ teamId: teamId
+ });
+
+ jsonRes(res, {
+ data: history
+ });
+ } catch (err) {
+ jsonRes(res, {
+ code: 500,
+ data: req.query,
+ error: err
+ });
+ }
+}
+
+export const config = {
+ api: {
+ responseLimit: '10mb'
+ }
+};
diff --git a/projects/app/src/pages/api/core/chat/team/init.ts b/projects/app/src/pages/api/core/chat/team/init.ts
new file mode 100644
index 00000000000..47b5f0e6f4b
--- /dev/null
+++ b/projects/app/src/pages/api/core/chat/team/init.ts
@@ -0,0 +1,91 @@
+import type { NextApiRequest, NextApiResponse } from 'next';
+import { jsonRes } from '@fastgpt/service/common/response';
+import { connectToDatabase } from '@/service/mongo';
+import { getGuideModule } from '@fastgpt/global/core/module/utils';
+import { getChatModelNameListByModules } from '@/service/core/app/module';
+import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
+import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d';
+import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
+import { MongoApp } from '@fastgpt/service/core/app/schema';
+import { getChatItems } from '@fastgpt/service/core/chat/controller';
+import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
+
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ try {
+ await connectToDatabase();
+
+ let { appId, chatId, outLinkUid } = req.query as {
+ chatId?: string;
+ appId?: string;
+ outLinkUid?: string;
+ };
+
+ if (!appId) {
+ return jsonRes(res, {
+ code: 501,
+ message: "You don't have an app yet"
+ });
+ }
+
+ // auth app permission
+ const [chat, app] = await Promise.all([
+ // authApp({
+ // req,
+ // authToken: false,
+ // appId,
+ // per: 'r'
+ // }),
+ chatId ? MongoChat.findOne({ appId, chatId }) : undefined,
+ MongoApp.findById(appId).lean()
+ ]);
+ if (!app) {
+ throw new Error(AppErrEnum.unExist);
+ }
+
+ // auth chat permission
+ // if (chat && chat.outLinkUid !== outLinkUid) {
+ // throw new Error(ChatErrEnum.unAuthChat);
+ // }
+ // // auth chat permission
+ // if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) {
+ // throw new Error(ChatErrEnum.unAuthChat);
+ // }
+
+ // get app and history
+ const { history } = await getChatItems({
+ appId,
+ chatId,
+ limit: 30,
+ field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${ModuleOutputKeyEnum.responseData}`
+ });
+
+ jsonRes(res, {
+ data: {
+ chatId,
+ appId,
+ title: chat?.title || '新对话',
+ userAvatar: undefined,
+ variables: chat?.variables || {},
+ history,
+ app: {
+ userGuideModule: getGuideModule(app.modules),
+ chatModels: getChatModelNameListByModules(app.modules),
+ name: app.name,
+ avatar: app.avatar,
+ intro: app.intro
+ }
+ }
+ });
+ } catch (err) {
+ jsonRes(res, {
+ code: 500,
+ error: err
+ });
+ }
+}
+
+export const config = {
+ api: {
+ responseLimit: '10mb'
+ }
+};
diff --git a/projects/app/src/pages/api/core/chat/teamInit.ts b/projects/app/src/pages/api/core/chat/teamInit.ts
new file mode 100644
index 00000000000..a3087e15c6f
--- /dev/null
+++ b/projects/app/src/pages/api/core/chat/teamInit.ts
@@ -0,0 +1,81 @@
+import type { NextApiRequest, NextApiResponse } from 'next';
+import { jsonRes } from '@fastgpt/service/common/response';
+import { connectToDatabase } from '@/service/mongo';
+import { authApp } from '@fastgpt/service/support/permission/auth/app';
+import { getGuideModule } from '@fastgpt/global/core/module/utils';
+import { getChatModelNameListByModules } from '@/service/core/app/module';
+import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
+import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d';
+import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
+import { getChatItems } from '@fastgpt/service/core/chat/controller';
+import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
+
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ try {
+ await connectToDatabase();
+
+ let { appId, chatId, loadCustomFeedbacks } = req.query as InitChatProps;
+
+ if (!appId) {
+ return jsonRes(res, {
+ code: 501,
+ message: "You don't have an app yet"
+ });
+ }
+
+ // auth app permission
+ const [{ app, tmbId }, chat] = await Promise.all([
+ authApp({
+ req,
+ authToken: true,
+ appId,
+ per: 'r'
+ }),
+ chatId ? MongoChat.findOne({ appId, chatId }) : undefined
+ ]);
+
+ // // auth chat permission
+ // if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) {
+ // throw new Error(ChatErrEnum.unAuthChat);
+ // }
+
+ // get app and history
+ const { history } = await getChatItems({
+ appId,
+ chatId,
+ limit: 30,
+ field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${
+ ModuleOutputKeyEnum.responseData
+ } ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`
+ });
+
+ jsonRes(res, {
+ data: {
+ chatId,
+ appId,
+ title: chat?.title || '新对话',
+ userAvatar: undefined,
+ variables: chat?.variables || {},
+ history,
+ app: {
+ userGuideModule: getGuideModule(app.modules),
+ chatModels: getChatModelNameListByModules(app.modules),
+ name: app.name,
+ avatar: app.avatar,
+ intro: app.intro
+ }
+ }
+ });
+ } catch (err) {
+ jsonRes(res, {
+ code: 500,
+ error: err
+ });
+ }
+}
+
+export const config = {
+ api: {
+ responseLimit: '10mb'
+ }
+};
diff --git a/projects/app/src/pages/api/core/chat/updateHistory.ts b/projects/app/src/pages/api/core/chat/updateHistory.ts
index 634336c9aad..634604894e3 100644
--- a/projects/app/src/pages/api/core/chat/updateHistory.ts
+++ b/projects/app/src/pages/api/core/chat/updateHistory.ts
@@ -24,6 +24,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await MongoChat.findOneAndUpdate(
{ appId, chatId },
{
+ updateTime: new Date(),
...(customTitle !== undefined && { customTitle }),
...(top !== undefined && { top })
}
diff --git a/projects/app/src/pages/api/core/dataset/allDataset.ts b/projects/app/src/pages/api/core/dataset/allDataset.ts
index e1d854018e7..0783b178e0a 100644
--- a/projects/app/src/pages/api/core/dataset/allDataset.ts
+++ b/projects/app/src/pages/api/core/dataset/allDataset.ts
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
-import { getVectorModel } from '@/service/core/ai/model';
+import { getVectorModel } from '@fastgpt/service/core/ai/model';
import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d';
import { mongoRPermission } from '@fastgpt/global/support/permission/utils';
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
diff --git a/projects/app/src/pages/api/core/dataset/collection/create/link.ts b/projects/app/src/pages/api/core/dataset/collection/create/link.ts
index 15f0ac26a81..2b25d2b6798 100644
--- a/projects/app/src/pages/api/core/dataset/collection/create/link.ts
+++ b/projects/app/src/pages/api/core/dataset/collection/create/link.ts
@@ -11,13 +11,12 @@ import {
TrainingModeEnum,
DatasetCollectionTypeEnum
} from '@fastgpt/global/core/dataset/constants';
-import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
+import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
-import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller';
-import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
-import { getLLMModel, getVectorModel } from '@/service/core/ai/model';
+import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller';
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
+import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
import { reloadCollectionChunks } from '@fastgpt/service/core/dataset/collection/utils';
-import { getStandardSubPlan } from '@/service/support/wallet/sub/utils';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -43,8 +42,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
// 1. check dataset limit
await checkDatasetLimit({
teamId,
- insertLen: predictDataLimitLength(trainingType, new Array(10)),
- standardPlans: getStandardSubPlan()
+ insertLen: predictDataLimitLength(trainingType, new Array(10))
});
const { _id: collectionId } = await mongoSessionRun(async (session) => {
@@ -66,11 +64,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
// 3. create bill and start sync
- const { billId } = await createTrainingBill({
+ const { billId } = await createTrainingUsage({
teamId,
tmbId,
appName: 'core.dataset.collection.Sync Collection',
- billSource: BillSourceEnum.training,
+ billSource: UsageSourceEnum.training,
vectorModel: getVectorModel(dataset.vectorModel).name,
agentModel: getLLMModel(dataset.agentModel).name,
session
diff --git a/projects/app/src/pages/api/core/dataset/collection/create/text.ts b/projects/app/src/pages/api/core/dataset/collection/create/text.ts
index b24a1724cf4..af700c1010f 100644
--- a/projects/app/src/pages/api/core/dataset/collection/create/text.ts
+++ b/projects/app/src/pages/api/core/dataset/collection/create/text.ts
@@ -12,14 +12,13 @@ import {
DatasetCollectionTypeEnum
} from '@fastgpt/global/core/dataset/constants';
import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter';
-import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
+import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller';
import { hashStr } from '@fastgpt/global/common/string/tools';
-import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller';
-import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
-import { getLLMModel, getVectorModel } from '@/service/core/ai/model';
-import { getStandardSubPlan } from '@/service/support/wallet/sub/utils';
+import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller';
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
+import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -53,8 +52,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
// 2. check dataset limit
await checkDatasetLimit({
teamId,
- insertLen: predictDataLimitLength(trainingType, chunks),
- standardPlans: getStandardSubPlan()
+ insertLen: predictDataLimitLength(trainingType, chunks)
});
// 3. create collection and training bill
@@ -74,11 +72,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
hashRawText: hashStr(text),
rawTextLength: text.length
}),
- createTrainingBill({
+ createTrainingUsage({
teamId,
tmbId,
appName: name,
- billSource: BillSourceEnum.training,
+ billSource: UsageSourceEnum.training,
vectorModel: getVectorModel(dataset.vectorModel)?.name,
agentModel: getLLMModel(dataset.agentModel)?.name
})
diff --git a/projects/app/src/pages/api/core/dataset/collection/sync/link.ts b/projects/app/src/pages/api/core/dataset/collection/sync/link.ts
index 7c0104c9912..39f2543e149 100644
--- a/projects/app/src/pages/api/core/dataset/collection/sync/link.ts
+++ b/projects/app/src/pages/api/core/dataset/collection/sync/link.ts
@@ -12,9 +12,9 @@ import {
DatasetCollectionTypeEnum
} from '@fastgpt/global/core/dataset/constants';
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
-import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller';
-import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
-import { getLLMModel, getVectorModel } from '@/service/core/ai/model';
+import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller';
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
+import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
@@ -56,11 +56,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
await mongoSessionRun(async (session) => {
// create training bill
- const { billId } = await createTrainingBill({
+ const { billId } = await createTrainingUsage({
teamId: collection.teamId,
tmbId,
appName: 'core.dataset.collection.Sync Collection',
- billSource: BillSourceEnum.training,
+ billSource: UsageSourceEnum.training,
vectorModel: vectorModelData.name,
agentModel: agentModelData.name,
session
diff --git a/projects/app/src/pages/api/core/dataset/create.ts b/projects/app/src/pages/api/core/dataset/create.ts
index c1dccb8c1bf..6e3fc50e010 100644
--- a/projects/app/src/pages/api/core/dataset/create.ts
+++ b/projects/app/src/pages/api/core/dataset/create.ts
@@ -6,7 +6,8 @@ import type { CreateDatasetParams } from '@/global/core/dataset/api.d';
import { createDefaultCollection } from '@fastgpt/service/core/dataset/collection/controller';
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
-import { getLLMModel, getVectorModel, getDatasetModel } from '@/service/core/ai/model';
+import { getLLMModel, getVectorModel, getDatasetModel } from '@fastgpt/service/core/ai/model';
+import { checkTeamDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -31,13 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}
// check limit
- const authCount = await MongoDataset.countDocuments({
- teamId,
- type: DatasetTypeEnum.dataset
- });
- if (authCount >= 50) {
- throw new Error('每个团队上限 50 个知识库');
- }
+ await checkTeamDatasetLimit(teamId);
const { _id } = await MongoDataset.create({
name,
diff --git a/projects/app/src/pages/api/core/dataset/data/delete.ts b/projects/app/src/pages/api/core/dataset/data/delete.ts
index b945d3e5637..708f21adac2 100644
--- a/projects/app/src/pages/api/core/dataset/data/delete.ts
+++ b/projects/app/src/pages/api/core/dataset/data/delete.ts
@@ -3,8 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { withNextCors } from '@fastgpt/service/common/middle/cors';
import { connectToDatabase } from '@/service/mongo';
import { authDatasetData } from '@/service/support/permission/auth/dataset';
-import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
-import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller';
+import { deleteDatasetData } from '@/service/core/dataset/data/controller';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -26,19 +25,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
per: 'w'
});
- // update mongo data update time
- await MongoDatasetData.findByIdAndUpdate(dataId, {
- updateTime: new Date()
- });
-
- // delete vector data
- await deleteDatasetDataVector({
- teamId,
- idList: datasetData.indexes.map((item) => item.dataId)
- });
-
- // delete mongo data
- await MongoDatasetData.findByIdAndDelete(dataId);
+ await deleteDatasetData(datasetData);
jsonRes(res, {
data: 'success'
diff --git a/projects/app/src/pages/api/core/dataset/data/insertData.ts b/projects/app/src/pages/api/core/dataset/data/insertData.ts
index a8ec06bf183..81c0b6e2863 100644
--- a/projects/app/src/pages/api/core/dataset/data/insertData.ts
+++ b/projects/app/src/pages/api/core/dataset/data/insertData.ts
@@ -7,17 +7,15 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { withNextCors } from '@fastgpt/service/common/middle/cors';
import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken';
-import { getVectorModel } from '@/service/core/ai/model';
+import { getVectorModel } from '@fastgpt/service/core/ai/model';
import { hasSameValue } from '@/service/core/dataset/data/utils';
import { insertData2Dataset } from '@/service/core/dataset/data/controller';
import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset';
import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller';
-import { authTeamBalance } from '@/service/support/permission/auth/bill';
-import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push';
+import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
import { InsertOneDatasetDataProps } from '@/global/core/dataset/api';
import { simpleText } from '@fastgpt/global/common/string/tools';
-import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
-import { getStandardSubPlan } from '@/service/support/wallet/sub/utils';
+import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -43,8 +41,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
await checkDatasetLimit({
teamId,
- insertLen: 1,
- standardPlans: getStandardSubPlan()
+ insertLen: 1
});
// auth collection and get dataset
@@ -52,7 +49,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
{
datasetId: { _id: datasetId, vectorModel }
}
- ] = await Promise.all([getCollectionWithDataset(collectionId), authTeamBalance(teamId)]);
+ ] = await Promise.all([getCollectionWithDataset(collectionId)]);
// format data
const formatQ = simpleText(q);
@@ -90,7 +87,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
indexes: formatIndexes
});
- pushGenerateVectorBill({
+ pushGenerateVectorUsage({
teamId,
tmbId,
charsLength,
diff --git a/projects/app/src/pages/api/core/dataset/data/pushData.ts b/projects/app/src/pages/api/core/dataset/data/pushData.ts
index 8e50ef333f0..186d58fc6f4 100644
--- a/projects/app/src/pages/api/core/dataset/data/pushData.ts
+++ b/projects/app/src/pages/api/core/dataset/data/pushData.ts
@@ -8,10 +8,9 @@ import type {
PushDatasetDataResponse
} from '@fastgpt/global/core/dataset/api.d';
import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset';
-import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
+import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller';
-import { getStandardSubPlan } from '@/service/support/wallet/sub/utils';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -38,8 +37,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// auth dataset limit
await checkDatasetLimit({
teamId,
- insertLen: predictDataLimitLength(collection.trainingType, data),
- standardPlans: getStandardSubPlan()
+ insertLen: predictDataLimitLength(collection.trainingType, data)
});
jsonRes(res, {
diff --git a/projects/app/src/pages/api/core/dataset/data/update.ts b/projects/app/src/pages/api/core/dataset/data/update.ts
index 7f7102d4b7b..eaf3cab3d17 100644
--- a/projects/app/src/pages/api/core/dataset/data/update.ts
+++ b/projects/app/src/pages/api/core/dataset/data/update.ts
@@ -4,9 +4,9 @@ import { withNextCors } from '@fastgpt/service/common/middle/cors';
import { connectToDatabase } from '@/service/mongo';
import { updateData2Dataset } from '@/service/core/dataset/data/controller';
import { authDatasetData } from '@/service/support/permission/auth/dataset';
-import { authTeamBalance } from '@/service/support/permission/auth/bill';
-import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push';
+import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
import { UpdateDatasetDataProps } from '@/global/core/dataset/api';
+import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -29,7 +29,10 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
});
// auth team balance
- await authTeamBalance(teamId);
+ await checkDatasetLimit({
+ teamId,
+ insertLen: 1
+ });
const { charsLength } = await updateData2Dataset({
dataId: id,
@@ -39,7 +42,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
model: vectorModel
});
- pushGenerateVectorBill({
+ pushGenerateVectorUsage({
teamId,
tmbId,
charsLength,
diff --git a/projects/app/src/pages/api/core/dataset/detail.ts b/projects/app/src/pages/api/core/dataset/detail.ts
index 7ff8b475c38..310133bbfed 100644
--- a/projects/app/src/pages/api/core/dataset/detail.ts
+++ b/projects/app/src/pages/api/core/dataset/detail.ts
@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
-import { getLLMModel, getVectorModel } from '@/service/core/ai/model';
+import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
diff --git a/projects/app/src/pages/api/core/dataset/list.ts b/projects/app/src/pages/api/core/dataset/list.ts
index 07f54546ccf..e0f0429faeb 100644
--- a/projects/app/src/pages/api/core/dataset/list.ts
+++ b/projects/app/src/pages/api/core/dataset/list.ts
@@ -6,7 +6,7 @@ import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { mongoRPermission } from '@fastgpt/global/support/permission/utils';
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
-import { getVectorModel } from '@/service/core/ai/model';
+import { getVectorModel } from '@fastgpt/service/core/ai/model';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
diff --git a/projects/app/src/pages/api/core/dataset/searchTest.ts b/projects/app/src/pages/api/core/dataset/searchTest.ts
index 6358ba381f5..893f9f48d63 100644
--- a/projects/app/src/pages/api/core/dataset/searchTest.ts
+++ b/projects/app/src/pages/api/core/dataset/searchTest.ts
@@ -4,14 +4,16 @@ import { withNextCors } from '@fastgpt/service/common/middle/cors';
import type { SearchTestProps, SearchTestResponse } from '@/global/core/dataset/api.d';
import { connectToDatabase } from '@/service/mongo';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
-import { authTeamBalance } from '@/service/support/permission/auth/bill';
-import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push';
+import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
import { searchDatasetData } from '@/service/core/dataset/data/controller';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
-import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
-import { getLLMModel } from '@/service/core/ai/model';
-import { queryExtension } from '@fastgpt/service/core/ai/functions/queryExtension';
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
+import { getLLMModel } from '@fastgpt/service/core/ai/model';
import { datasetSearchQueryExtension } from '@fastgpt/service/core/dataset/search/utils';
+import {
+ checkTeamAIPoints,
+ checkTeamReRankPermission
+} from '@fastgpt/service/support/permission/teamLimit';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -43,7 +45,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
per: 'r'
});
// auth balance
- await authTeamBalance(teamId);
+ await checkTeamAIPoints(teamId);
// query extension
const extensionModel =
@@ -65,28 +67,27 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
similarity,
datasetIds: [datasetId],
searchMode,
- usingReRank
+ usingReRank: usingReRank && (await checkTeamReRankPermission(teamId))
});
// push bill
- const { total } = pushGenerateVectorBill({
+ const { totalPoints } = pushGenerateVectorUsage({
teamId,
tmbId,
charsLength,
model: dataset.vectorModel,
- source: apikey ? BillSourceEnum.api : BillSourceEnum.fastgpt,
+ source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt,
...(aiExtensionResult &&
extensionModel && {
extensionModel: extensionModel.name,
- extensionInputTokens: aiExtensionResult.inputTokens,
- extensionOutputTokens: aiExtensionResult.outputTokens
+ extensionCharsLength: aiExtensionResult.charsLength
})
});
if (apikey) {
updateApiKeyUsage({
apikey,
- usage: total
+ totalPoints: totalPoints
});
}
diff --git a/projects/app/src/pages/api/core/plugin/create.ts b/projects/app/src/pages/api/core/plugin/create.ts
index c0f4ab035e3..71232711d2f 100644
--- a/projects/app/src/pages/api/core/plugin/create.ts
+++ b/projects/app/src/pages/api/core/plugin/create.ts
@@ -4,6 +4,7 @@ import { connectToDatabase } from '@/service/mongo';
import type { CreateOnePluginParams } from '@fastgpt/global/core/plugin/controller';
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
+import { checkTeamPluginLimit } from '@fastgpt/service/support/permission/teamLimit';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -11,6 +12,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true });
const body = req.body as CreateOnePluginParams;
+ await checkTeamPluginLimit(teamId);
+
const { _id } = await MongoPlugin.create({
...body,
teamId,
diff --git a/projects/app/src/pages/api/plugins/textEditor/index.ts b/projects/app/src/pages/api/plugins/textEditor/index.ts
index c7aa2c06a19..35ad8de80d4 100644
--- a/projects/app/src/pages/api/plugins/textEditor/index.ts
+++ b/projects/app/src/pages/api/plugins/textEditor/index.ts
@@ -34,7 +34,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
const textResult = replaceVariable(text, obj);
-
res.json({
text: textResult
});
diff --git a/projects/app/src/pages/api/support/user/account/update.ts b/projects/app/src/pages/api/support/user/account/update.ts
index 7b29daad33a..af55c441d4d 100644
--- a/projects/app/src/pages/api/support/user/account/update.ts
+++ b/projects/app/src/pages/api/support/user/account/update.ts
@@ -5,6 +5,7 @@ import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { UserUpdateParams } from '@/types/user';
import { getAIApi, openaiBaseUrl } from '@fastgpt/service/core/ai/config';
import { connectToDatabase } from '@/service/mongo';
+import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
/* update user info */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -12,8 +13,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
await connectToDatabase();
const { avatar, timezone, openaiAccount } = req.body as UserUpdateParams;
- const { userId } = await authCert({ req, authToken: true });
-
+ const { tmbId } = await authCert({ req, authToken: true });
+ const tmb = await MongoTeamMember.findById(tmbId);
+ if (!tmb) {
+ throw new Error('can not find it');
+ }
+ const userId = tmb.userId;
// auth key
if (openaiAccount?.key) {
console.log('auth user openai key', openaiAccount?.key);
diff --git a/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts b/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts
index ad4eec2bfce..86712d7499a 100644
--- a/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts
+++ b/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts
@@ -3,6 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { connectToDatabase } from '@/service/mongo';
+import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -13,8 +14,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
throw new Error('Params is missing');
}
- const { userId } = await authCert({ req, authToken: true });
-
+ const { tmbId } = await authCert({ req, authToken: true });
+ const tmb = await MongoTeamMember.findById(tmbId);
+ if (!tmb) {
+ throw new Error('can not find it');
+ }
+ const userId = tmb.userId;
// auth old password
const user = await MongoUser.findOne({
_id: userId,
diff --git a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts
index 4d22ffec3a8..4075be2e9b5 100644
--- a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts
+++ b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts
@@ -2,8 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
-import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
-import { getStandardSubPlan } from '@/service/support/wallet/sub/utils';
+import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -23,8 +22,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
await checkDatasetLimit({
teamId,
- insertLen: numberSize,
- standardPlans: getStandardSubPlan()
+ insertLen: numberSize
});
jsonRes(res);
diff --git a/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts b/projects/app/src/pages/api/support/user/team/plan/getTeamPlanStatus.ts
similarity index 59%
rename from projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts
rename to projects/app/src/pages/api/support/user/team/plan/getTeamPlanStatus.ts
index 83eb283fd5c..f138d9990fa 100644
--- a/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts
+++ b/projects/app/src/pages/api/support/user/team/plan/getTeamPlanStatus.ts
@@ -2,24 +2,21 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
-import { getTeamSubPlanStatus } from '@fastgpt/service/support/wallet/sub/utils';
-import { getStandardSubPlan } from '@/service/support/wallet/sub/utils';
-import { FeTeamSubType } from '@fastgpt/global/support/wallet/sub/type';
+import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
+import { getTeamPlanStatus } from '@fastgpt/service/support/wallet/sub/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
- // 凭证校验
const { teamId } = await authCert({
req,
authToken: true
});
- jsonRes(res, {
- data: await getTeamSubPlanStatus({
- teamId,
- standardPlans: getStandardSubPlan()
+ jsonRes(res, {
+ data: await getTeamPlanStatus({
+ teamId
})
});
} catch (err) {
diff --git a/projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts b/projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts
similarity index 62%
rename from projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts
rename to projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts
index fec583d695a..db5119185d8 100644
--- a/projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts
+++ b/projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts
@@ -1,16 +1,16 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
-import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
-import { CreateTrainingBillProps } from '@fastgpt/global/support/wallet/bill/api.d';
-import { getLLMModel, getVectorModel } from '@/service/core/ai/model';
-import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller';
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
+import { CreateTrainingUsageProps } from '@fastgpt/global/support/wallet/usage/api.d';
+import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
+import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
- const { name, datasetId } = req.body as CreateTrainingBillProps;
+ const { name, datasetId } = req.body as CreateTrainingUsageProps;
const { teamId, tmbId, dataset } = await authDataset({
req,
@@ -20,11 +20,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
per: 'w'
});
- const { billId } = await createTrainingBill({
+ const { billId } = await createTrainingUsage({
teamId,
tmbId,
appName: name,
- billSource: BillSourceEnum.training,
+ billSource: UsageSourceEnum.training,
vectorModel: getVectorModel(dataset.vectorModel).name,
agentModel: getLLMModel(dataset.agentModel).name
});
diff --git a/projects/app/src/pages/api/v1/audio/transcriptions.ts b/projects/app/src/pages/api/v1/audio/transcriptions.ts
index 189ab13c4b0..1c6997d5137 100644
--- a/projects/app/src/pages/api/v1/audio/transcriptions.ts
+++ b/projects/app/src/pages/api/v1/audio/transcriptions.ts
@@ -6,7 +6,7 @@ import { getUploadModel } from '@fastgpt/service/common/file/multer';
import { removeFilesByPaths } from '@fastgpt/service/common/file/utils';
import fs from 'fs';
import { getAIApi } from '@fastgpt/service/core/ai/config';
-import { pushWhisperBill } from '@/service/support/wallet/bill/push';
+import { pushWhisperUsage } from '@/service/support/wallet/usage/push';
const upload = getUploadModel({
maxSize: 2
@@ -40,7 +40,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
model: global.whisperModel.model
});
- pushWhisperBill({
+ pushWhisperUsage({
teamId,
tmbId,
duration
diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts
index f703072ab74..5f9640fd5dc 100644
--- a/projects/app/src/pages/api/v1/chat/completions.ts
+++ b/projects/app/src/pages/api/v1/chat/completions.ts
@@ -13,16 +13,16 @@ import { gptMessage2ChatType, textAdaptGptResponse } from '@/utils/adapt';
import { getChatItems } from '@fastgpt/service/core/chat/controller';
import { saveChat } from '@/service/utils/chat/saveChat';
import { responseWrite } from '@fastgpt/service/common/response';
-import { pushChatBill } from '@/service/support/wallet/bill/push';
+import { pushChatUsage } from '@/service/support/wallet/usage/push';
import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink';
-import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
+import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
import requestIp from 'request-ip';
-import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools';
-
+import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
+import { authTeamShareChatStart } from '@/service/support/permission/auth/teamChat';
import { selectShareResponse } from '@/utils/service/core/chat';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
import { connectToDatabase } from '@/service/mongo';
-import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
+import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { autChatCrud } from '@/service/support/permission/auth/chat';
@@ -35,9 +35,14 @@ type FastGptShareChatProps = {
shareId?: string;
outLinkUid?: string;
};
+type FastGptTeamShareChatProps = {
+ shareTeamId?: string;
+ outLinkUid?: string;
+};
export type Props = ChatCompletionCreateParams &
FastGptWebChatProps &
- FastGptShareChatProps & {
+ FastGptShareChatProps &
+ FastGptTeamShareChatProps & {
messages: ChatMessageItemType[];
stream?: boolean;
detail?: boolean;
@@ -60,6 +65,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
const {
chatId,
appId,
+ shareTeamId,
shareId,
outLinkUid,
stream = false,
@@ -67,7 +73,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
messages = [],
variables = {}
} = req.body as Props;
-
try {
const originIp = requestIp.getClientIp(req);
@@ -95,92 +100,122 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (!question) {
throw new Error('Question is empty');
}
-
/* auth app permission */
- const { user, app, responseDetail, authType, apikey, canWrite, uid } = await (async () => {
- if (shareId && outLinkUid) {
- const { user, appId, authType, responseDetail, uid } = await authOutLinkChatStart({
- shareId,
- ip: originIp,
- outLinkUid,
- question: question.value
- });
- const app = await MongoApp.findById(appId);
+ const { teamId, tmbId, user, app, responseDetail, authType, apikey, canWrite, outLinkUserId } =
+ await (async () => {
+ if (shareId && outLinkUid) {
+ const { teamId, tmbId, user, appId, authType, responseDetail, uid } =
+ await authOutLinkChatStart({
+ shareId,
+ ip: originIp,
+ outLinkUid,
+ question: question.value
+ });
+ const app = await MongoApp.findById(appId);
+
+ if (!app) {
+ return Promise.reject('app is empty');
+ }
- if (!app) {
- return Promise.reject('app is empty');
+ return {
+ teamId,
+ tmbId,
+ user,
+ app,
+ responseDetail,
+ apikey: '',
+ authType,
+ canWrite: false,
+ outLinkUserId: uid
+ };
}
+ // team Apps share
+ if (shareTeamId && appId && outLinkUid) {
+ const { user, uid, tmbId } = await authTeamShareChatStart({
+ teamId: shareTeamId,
+ ip: originIp,
+ outLinkUid,
+ question: question.value
+ });
+ const app = await MongoApp.findById(appId);
+ if (!app) {
+ return Promise.reject('app is empty');
+ }
- return {
- user,
- app,
- responseDetail,
- apikey: '',
+ return {
+ teamId: shareTeamId,
+ tmbId,
+ user,
+ app,
+ responseDetail: detail,
+ authType: AuthUserTypeEnum.token,
+ apikey: '',
+ canWrite: false,
+ outLinkUserId: uid
+ };
+ }
+
+ const {
+ appId: apiKeyAppId,
+ teamId,
+ tmbId,
authType,
- canWrite: false,
- uid
- };
- }
+ apikey
+ } = await authCert({
+ req,
+ authToken: true,
+ authApiKey: true
+ });
- const {
- appId: apiKeyAppId,
- tmbId,
- authType,
- apikey
- } = await authCert({
- req,
- authToken: true,
- authApiKey: true
- });
+ const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
- const user = await getUserAndAuthBalance({
- tmbId,
- minBalance: 0
- });
+ // openapi key
+ if (authType === AuthUserTypeEnum.apikey) {
+ if (!apiKeyAppId) {
+ return Promise.reject(
+ 'Key is error. You need to use the app key rather than the account key.'
+ );
+ }
+ const app = await MongoApp.findById(apiKeyAppId);
+
+ if (!app) {
+ return Promise.reject('app is empty');
+ }
- // openapi key
- if (authType === AuthUserTypeEnum.apikey) {
- if (!apiKeyAppId) {
- return Promise.reject(
- 'Key is error. You need to use the app key rather than the account key.'
- );
+ return {
+ teamId,
+ tmbId,
+ user,
+ app,
+ responseDetail: detail,
+ apikey,
+ authType,
+ canWrite: true
+ };
}
- const app = await MongoApp.findById(apiKeyAppId);
- if (!app) {
- return Promise.reject('app is empty');
+ // token auth
+ if (!appId) {
+ return Promise.reject('appId is empty');
}
+ const { app, canWrite } = await authApp({
+ req,
+ authToken: true,
+ appId,
+ per: 'r'
+ });
return {
+ teamId,
+ tmbId,
user,
app,
responseDetail: detail,
apikey,
authType,
- canWrite: true
+ canWrite: canWrite || false
};
- }
-
- // token auth
- if (!appId) {
- return Promise.reject('appId is empty');
- }
- const { app, canWrite } = await authApp({
- req,
- authToken: true,
- appId,
- per: 'r'
- });
-
- return {
- user,
- app,
- responseDetail: detail,
- apikey,
- authType,
- canWrite: canWrite || false
- };
- })();
+ })();
// auth chat permission
await autChatCrud({
@@ -190,6 +225,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
appId: app._id,
chatId,
shareId,
+ shareTeamId,
outLinkUid,
per: 'w'
});
@@ -201,16 +237,17 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
limit: 30,
field: `dataId obj value`
});
+
const concatHistories = history.concat(chatMessages);
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
/* start flow controller */
- const { responseData, answerText } = await dispatchModules({
+ const { responseData, moduleDispatchBills, answerText } = await dispatchModules({
res,
mode: 'chat',
user,
- teamId: String(user.team.teamId),
- tmbId: String(user.team.tmbId),
+ teamId: String(teamId),
+ tmbId: String(tmbId),
appId: String(app._id),
chatId,
responseChatItemId,
@@ -229,12 +266,12 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
await saveChat({
chatId,
appId: app._id,
- teamId: user.team.teamId,
- tmbId: user.team.tmbId,
+ teamId,
+ tmbId: tmbId,
variables,
- updateUseTime: !shareId && String(user.team.tmbId) === String(app.tmbId), // owner update use time
+ updateUseTime: !shareId && String(tmbId) === String(app.tmbId), // owner update use time
shareId,
- outLinkUid: uid,
+ outLinkUid: outLinkUserId,
source: (() => {
if (shareId) {
return ChatSourceEnum.share;
@@ -305,29 +342,29 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}
// add record
- const { total } = pushChatBill({
+ const { totalPoints } = pushChatUsage({
appName: app.name,
appId: app._id,
- teamId: user.team.teamId,
- tmbId: user.team.tmbId,
- source: getBillSourceByAuthType({ shareId, authType }),
- response: responseData
+ teamId,
+ tmbId: tmbId,
+ source: getUsageSourceByAuthType({ shareId, authType }),
+ moduleDispatchBills
});
if (shareId) {
- pushResult2Remote({ outLinkUid, shareId, responseData });
- updateOutLinkUsage({
+ pushResult2Remote({ outLinkUid, shareId, appName: app.name, responseData });
+ addOutLinkUsage({
shareId,
- total
+ totalPoints
});
}
if (apikey) {
updateApiKeyUsage({
apikey,
- usage: total
+ totalPoints
});
}
- } catch (err: any) {
+ } catch (err) {
if (stream) {
sseErrRes(res, err);
res.end();
diff --git a/projects/app/src/pages/api/v1/embeddings.ts b/projects/app/src/pages/api/v1/embeddings.ts
index a363ecb7814..2c251a80a36 100644
--- a/projects/app/src/pages/api/v1/embeddings.ts
+++ b/projects/app/src/pages/api/v1/embeddings.ts
@@ -2,13 +2,13 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { withNextCors } from '@fastgpt/service/common/middle/cors';
-import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push';
+import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
import { connectToDatabase } from '@/service/mongo';
-import { authTeamBalance } from '@/service/support/permission/auth/bill';
import { getVectorsByText } from '@fastgpt/service/core/ai/embedding';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
-import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools';
-import { getVectorModel } from '@/service/core/ai/model';
+import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
+import { getVectorModel } from '@fastgpt/service/core/ai/model';
+import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
type Props = {
input: string | string[];
@@ -34,7 +34,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
authApiKey: true
});
- await authTeamBalance(teamId);
+ await checkTeamAIPoints(teamId);
const { charsLength, vectors } = await getVectorsByText({
input: query,
@@ -55,19 +55,19 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}
});
- const { total } = pushGenerateVectorBill({
+ const { totalPoints } = pushGenerateVectorUsage({
teamId,
tmbId,
charsLength,
model,
billId,
- source: getBillSourceByAuthType({ authType })
+ source: getUsageSourceByAuthType({ authType })
});
if (apikey) {
updateApiKeyUsage({
apikey,
- usage: total
+ totalPoints: totalPoints
});
}
} catch (err) {
diff --git a/projects/app/src/pages/api/v1/rerank.ts b/projects/app/src/pages/api/v1/rerank.ts
deleted file mode 100644
index c73ab0b6cbf..00000000000
--- a/projects/app/src/pages/api/v1/rerank.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import type { NextApiRequest, NextApiResponse } from 'next';
-import { jsonRes } from '@fastgpt/service/common/response';
-import { authCert } from '@fastgpt/service/support/permission/auth/common';
-import { withNextCors } from '@fastgpt/service/common/middle/cors';
-import { pushReRankBill } from '@/service/support/wallet/bill/push';
-import { connectToDatabase } from '@/service/mongo';
-import { authTeamBalance } from '@/service/support/permission/auth/bill';
-import { PostReRankProps, PostReRankResponse } from '@fastgpt/global/core/ai/api';
-import { reRankRecall } from '@/service/core/ai/rerank';
-import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
-
-export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
- let { query, inputs } = req.body as PostReRankProps;
- try {
- await connectToDatabase();
- const { teamId, tmbId, apikey } = await authCert({
- req,
- authApiKey: true
- });
- await authTeamBalance(teamId);
-
- // max 150 length
- inputs = inputs.slice(0, 150);
-
- const result = await reRankRecall({ query, inputs });
-
- const { total } = pushReRankBill({
- teamId,
- tmbId,
- source: 'api',
- inputs
- });
-
- if (apikey) {
- updateApiKeyUsage({
- apikey,
- usage: total
- });
- }
-
- jsonRes(res, {
- data: result
- });
- } catch (err) {
- jsonRes(res, {
- code: 500,
- error: err
- });
- }
-});
diff --git a/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx b/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx
deleted file mode 100644
index 8822483efbe..00000000000
--- a/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx
+++ /dev/null
@@ -1,201 +0,0 @@
-import React, { useEffect, useMemo, useRef } from 'react';
-import * as echarts from 'echarts';
-import { useSystemStore } from '@/web/common/system/useSystemStore';
-import { getAppTotalUsage } from '@/web/core/app/api';
-import { useQuery } from '@tanstack/react-query';
-import dayjs from 'dayjs';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
-import Loading from '@/components/Loading';
-import { Box } from '@chakra-ui/react';
-
-const map = {
- blue: {
- backgroundColor: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- {
- offset: 0,
- color: 'rgba(3, 190, 232, 0.42)' // 0% 处的颜色
- },
- {
- offset: 1,
- color: 'rgba(0, 182, 240, 0)'
- }
- ],
- global: false // 缺省为 false
- },
- lineColor: '#36ADEF'
- },
- deepBlue: {
- backgroundColor: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- {
- offset: 0,
- color: 'rgba(47, 112, 237, 0.42)' // 0% 处的颜色
- },
- {
- offset: 1,
- color: 'rgba(94, 159, 235, 0)'
- }
- ],
- global: false
- },
- lineColor: '#3293EC'
- },
- purple: {
- backgroundColor: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- {
- offset: 0,
- color: 'rgba(211, 190, 255, 0.42)' // 0% 处的颜色
- },
- {
- offset: 1,
- color: 'rgba(52, 60, 255, 0)'
- }
- ],
- global: false // 缺省为 false
- },
- lineColor: '#8172D8'
- },
- green: {
- backgroundColor: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- {
- offset: 0,
- color: 'rgba(4, 209, 148, 0.42)' // 0% 处的颜色
- },
- {
- offset: 1,
- color: 'rgba(19, 217, 181, 0)'
- }
- ],
- global: false // 缺省为 false
- },
- lineColor: '#00A9A6',
- max: 100
- }
-};
-
-const TokenUsage = ({ appId }: { appId: string }) => {
- const { screenWidth } = useSystemStore();
-
- const Dom = useRef(null);
- const myChart = useRef();
- const { data = [] } = useQuery(['init'], () => getAppTotalUsage({ appId }));
-
- const option = useMemo(
- () => ({
- xAxis: {
- type: 'category',
- show: false,
- boundaryGap: false,
- data: data.map((item) => item.date)
- },
- yAxis: {
- type: 'value',
- splitNumber: 3,
- min: 0
- },
- grid: {
- show: false,
- left: 5,
- right: 5,
- top: 0,
- bottom: 5
- },
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'line'
- },
- formatter: (e: any[]) => {
- const data = e[0];
- if (!data) return '';
-
- return `
-
- ${dayjs(data.axisValue).format('YYYY/MM/DD')}
- ${formatStorePrice2Read(e[0]?.value || 0)}元
-
-`;
- }
- },
- series: [
- {
- data: data.map((item) => item.total),
- type: 'line',
- showSymbol: true,
- animationDuration: 1000,
- animationEasingUpdate: 'linear',
- areaStyle: {
- color: map['blue'].backgroundColor
- },
- lineStyle: {
- width: '1',
- color: map['blue'].lineColor
- },
- itemStyle: {
- width: 1.5,
- color: map['blue'].lineColor
- },
- emphasis: {
- // highlight
- disabled: true
- }
- }
- ]
- }),
- [data]
- );
-
- // init chart
- useEffect(() => {
- if (!Dom.current || myChart?.current?.getOption()) return;
- myChart.current = echarts.init(Dom.current);
- myChart.current && myChart.current.setOption(option);
-
- setTimeout(() => {
- myChart.current?.resize();
- }, 500);
- }, []);
-
- // data changed, update
- useEffect(() => {
- if (!myChart.current || !myChart?.current?.getOption()) return;
- myChart.current.setOption(option);
- }, [data, option]);
-
- // resize chart
- useEffect(() => {
- if (!myChart.current || !myChart.current.getOption()) return;
- myChart.current.resize();
- }, [screenWidth]);
-
- return (
-
-
-
- );
-};
-
-export default React.memo(TokenUsage);
diff --git a/projects/app/src/pages/app/detail/components/OutLink/Share.tsx b/projects/app/src/pages/app/detail/components/OutLink/Share.tsx
index ee7980ea318..048028f339d 100644
--- a/projects/app/src/pages/app/detail/components/OutLink/Share.tsx
+++ b/projects/app/src/pages/app/detail/components/OutLink/Share.tsx
@@ -36,7 +36,6 @@ import { useForm } from 'react-hook-form';
import { defaultOutLinkForm } from '@/constants/app';
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d';
import { useRequest } from '@/web/common/hooks/useRequest';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
import { OutLinkTypeEnum } from '@fastgpt/global/support/outLink/constant';
import { useTranslation } from 'next-i18next';
import { useToast } from '@fastgpt/web/hooks/useToast';
@@ -94,7 +93,7 @@ const Share = ({ appId }: { appId: string }) => {
{t('common.Name')} |
- {t('common.Price used')} |
+ {t('support.outlink.Usage points')} |
{t('core.app.share.Is response quote')} |
{feConfigs?.isPlus && (
<>
@@ -112,11 +111,11 @@ const Share = ({ appId }: { appId: string }) => {
{item.name} |
- {formatStorePrice2Read(item.total)}
+ {Math.round(item.usagePoints)}
{feConfigs?.isPlus
? `${
- item.limit && item.limit.credit > -1
- ? ` / ¥${item.limit.credit}`
+ item.limit?.maxUsagePoints && item.limit.maxUsagePoints > -1
+ ? ` / ${item.limit.maxUsagePoints}`
: ` / ${t('common.Unlimited')}`
}`
: ''}
@@ -315,15 +314,15 @@ function EditLinkModal({
- {t('common.Max credit')}
-
+ {t('support.outlink.Max usage points')}
+
import('../InfoModal'));
const AppCard = ({ appId }: { appId: string }) => {
@@ -19,7 +22,9 @@ const AppCard = ({ appId }: { appId: string }) => {
const { t } = useTranslation();
const { toast } = useToast();
const { appDetail } = useAppStore();
+ const { feConfigs } = useSystemStore();
const [settingAppInfo, setSettingAppInfo] = useState();
+ const [TeamTagsSet, setTeamTagsSet] = useState();
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
content: t('app.Confirm Del App Tip')
@@ -123,6 +128,17 @@ const AppCard = ({ appId }: { appId: string }) => {
>
{t('core.app.navbar.Publish')}
+ {appDetail.canWrite && feConfigs?.show_team_chat && (
+ }
+ onClick={() => setTeamTagsSet(appDetail)}
+ >
+ {t('common.Team Tags Set')}
+
+ )}
{appDetail.isOwner && (
{
-
{settingAppInfo && (
setSettingAppInfo(undefined)} />
)}
+ {TeamTagsSet && (
+ setTeamTagsSet(undefined)} />
+ )}
>
);
};
diff --git a/projects/app/src/pages/app/detail/components/SimpleEdit/tagsEditModal.tsx b/projects/app/src/pages/app/detail/components/SimpleEdit/tagsEditModal.tsx
new file mode 100644
index 00000000000..37e0737d7a6
--- /dev/null
+++ b/projects/app/src/pages/app/detail/components/SimpleEdit/tagsEditModal.tsx
@@ -0,0 +1,103 @@
+import React, { useCallback, useState, useEffect } from 'react';
+import MyModal from '@/components/MyModal';
+import { useTranslation } from 'next-i18next';
+import { Button, Flex, Box, ModalFooter, ModalBody } from '@chakra-ui/react';
+import TagsEdit from '@/components/TagEdit';
+import { useToast } from '@fastgpt/web/hooks/useToast';
+import { AppSchema } from '@fastgpt/global/core/app/type.d';
+import { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
+import { useAppStore } from '@/web/core/app/store/useAppStore';
+import { useRequest } from '@/web/common/hooks/useRequest';
+import { getTeamsTags } from '@/web/support/user/team/api';
+const TagsEditModal = ({ appDetail, onClose }: { appDetail?: any; onClose: () => void }) => {
+ const { t } = useTranslation();
+ const [teamsTags, setTeamTags] = useState>([]);
+ const [selectedTags, setSelectedTags] = useState(appDetail?.teamTags);
+ const { toast } = useToast();
+ const { replaceAppDetail } = useAppStore();
+
+ // submit config
+ const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
+ mutationFn: async () => {
+ await replaceAppDetail(appDetail._id, {
+ teamTags: selectedTags
+ });
+ },
+ onSuccess() {
+ onClose();
+ toast({
+ title: t('common.Update Success'),
+ status: 'success'
+ });
+ },
+ errorToast: t('common.Update Failed')
+ });
+ //
+
+ // // 点击选择标签
+ // const clickTag = (tagId :Number) => {
+ // const index = selectedTags.indexOf(tagId);
+ // if (index === -1) {
+ // // 如果 num 不在数组 arr 中,添加它
+ // setSelectedTags([tagId,...selectedTags])
+ // } else {
+ // const _selectedTags = [...selectedTags];
+ // _selectedTags.splice(index, 1);
+ // console.log('_selectedTags',_selectedTags);
+ // // 如果 num 已经在数组 arr 中,移除它
+ // setSelectedTags(_selectedTags);
+ // }
+ // }
+
+ useEffect(() => {
+ // get team tags
+ getTeamsTags(appDetail?.teamId).then((res: any) => {
+ setTeamTags(res?.list);
+ });
+ }, []);
+
+ return (
+
+
+ {/*
+ {teamsTags.map((item,index) => {
+ return -1 ? 'green':'blue' }
+ onClick={() => clickTag(item._id)}
+ >
+ {item.label}
+
+ })}
+ */}
+
+
+ {t('团队标签')}
+
+ ) => setSelectedTags(item)}
+ />
+
+
+
+ {t('common.Close')}
+
+ saveSubmitSuccess(e)}>
+ {t('common.Save')}
+
+
+
+
+ );
+};
+export default TagsEditModal;
diff --git a/projects/app/src/pages/app/list/component/CreateModal.tsx b/projects/app/src/pages/app/list/component/CreateModal.tsx
index e0b10735740..fa3039bd6b5 100644
--- a/projects/app/src/pages/app/list/component/CreateModal.tsx
+++ b/projects/app/src/pages/app/list/component/CreateModal.tsx
@@ -8,8 +8,12 @@ import {
Input,
Grid,
useTheme,
- Card
+ Card,
+ Text,
+ HStack,
+ Tag
} from '@chakra-ui/react';
+import { AddIcon } from '@chakra-ui/icons';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useForm } from 'react-hook-form';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
diff --git a/projects/app/src/pages/app/list/index.tsx b/projects/app/src/pages/app/list/index.tsx
index 88db3bac803..ace56978cbe 100644
--- a/projects/app/src/pages/app/list/index.tsx
+++ b/projects/app/src/pages/app/list/index.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback } from 'react';
+import React, { useCallback, useState, useEffect } from 'react';
import { Box, Grid, Flex, IconButton, Button, useDisclosure } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { useQuery } from '@tanstack/react-query';
@@ -8,7 +8,6 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useTranslation } from 'next-i18next';
-
import MyIcon from '@fastgpt/web/components/common/Icon';
import PageContainer from '@/components/PageContainer';
import Avatar from '@/components/Avatar';
@@ -24,6 +23,7 @@ const MyApps = () => {
const router = useRouter();
const { userInfo } = useUserStore();
const { myApps, loadMyApps } = useAppStore();
+ const [teamsTags, setTeamTags] = useState([]);
const { openConfirm, ConfirmModal } = useConfirm({
title: '删除提示',
content: '确认删除该应用所有信息?'
@@ -65,11 +65,9 @@ const MyApps = () => {
{t('app.My Apps')}
- {userInfo?.team?.canWrite && (
- } variant={'primaryOutline'} onClick={onOpenCreateModal}>
- {t('common.New Create')}
-
- )}
+ } variant={'primaryOutline'} onClick={onOpenCreateModal}>
+ {t('common.New Create')}
+
{
))}
+ {/* (
+
+ ) */}
+
{myApps.length === 0 && (
diff --git a/projects/app/src/pages/chat/team.tsx b/projects/app/src/pages/chat/team.tsx
new file mode 100644
index 00000000000..e153f741417
--- /dev/null
+++ b/projects/app/src/pages/chat/team.tsx
@@ -0,0 +1,547 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import Head from 'next/head';
+import { getTeamChatInfo } from '@/web/core/chat/api';
+import { useRouter } from 'next/router';
+import {
+ Box,
+ Flex,
+ useDisclosure,
+ Drawer,
+ DrawerOverlay,
+ DrawerContent,
+ useTheme
+} from '@chakra-ui/react';
+import Avatar from '@/components/Avatar';
+import { useToast } from '@fastgpt/web/hooks/useToast';
+import { useQuery } from '@tanstack/react-query';
+import { useSystemStore } from '@/web/common/system/useSystemStore';
+import SideBar from '@/components/SideBar';
+import PageContainer from '@/components/PageContainer';
+import { getChatListById } from '@/web/core/chat/api';
+import ChatHistorySlider from './components/ChatHistorySlider';
+import ChatHeader from './components/ChatHeader';
+import { serviceSideProps } from '@/web/common/utils/i18n';
+import { useTranslation } from 'next-i18next';
+import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
+import { useChatStore } from '@/web/core/chat/storeChat';
+import { customAlphabet } from 'nanoid';
+import { useLoading } from '@/web/common/hooks/useLoading';
+const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
+import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
+import { streamFetch } from '@/web/common/api/fetch';
+import { useTeamShareChatStore } from '@/web/core/chat/storeTeamChat';
+import type {
+ ChatHistoryItemType,
+ chatAppListSchema,
+ teamInfoType
+} from '@fastgpt/global/core/chat/type.d';
+import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
+import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
+import { POST } from '@/web/common/api/request';
+const OutLink = ({
+ shareTeamId,
+ appId,
+ chatId,
+ authToken
+}: {
+ shareTeamId: string;
+ appId: string;
+ chatId: string;
+ authToken: string;
+}) => {
+ type routerQueryType = {
+ chatId?: string;
+ appId?: string;
+ shareTeamId: string;
+ authToken?: string;
+ };
+ const { t } = useTranslation();
+ const router = useRouter();
+ const { toast } = useToast();
+ const theme = useTheme();
+ const [myApps, setMyApps] = useState>([]);
+ const { isPc } = useSystemStore();
+ const ChatBoxRef = useRef(null);
+ const [teamInfo, setTeamInfo] = useState();
+ const { Loading, setIsLoading } = useLoading();
+ const forbidRefresh = useRef(false);
+
+ const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
+ const {
+ histories,
+ loadHistories,
+ lastChatAppId,
+ setLastChatAppId,
+ lastChatId,
+ setLastChatId,
+ pushHistory,
+ updateHistory,
+ delOneHistory,
+ chatData,
+ setChatData,
+ delOneHistoryItem,
+ clearHistories
+ } = useChatStore();
+ const {
+ localUId,
+ teamShareChatHistory, // abandon
+ clearLocalHistory // abandon
+ } = useTeamShareChatStore();
+
+ const outLinkUid: string = authToken || localUId;
+
+ // 纯网络获取流程
+ const loadApps = useCallback(async () => {
+ try {
+ if (!shareTeamId) {
+ toast({
+ status: 'error',
+ title: t('core.chat.You need to a chat app')
+ });
+ return;
+ }
+ // 根据获取历史记录列表
+ const res = await getChatListById({ shareTeamId, authToken });
+ const { apps, teamInfo } = res;
+ setMyApps(apps);
+ setTeamInfo(teamInfo);
+ if (apps.length <= 0) {
+ return toast({
+ status: 'error',
+ title: t('core.chat.You need to a chat app')
+ });
+ }
+ if (!apps.find((obj) => obj._id === appId)) {
+ toast({
+ status: 'warning',
+ title: 'you do not have this App'
+ });
+ router.replace({
+ query: {
+ appId: apps[0]?._id,
+ shareTeamId,
+ authToken: authToken
+ } as routerQueryType
+ });
+ }
+ } catch (error: any) {
+ toast({
+ status: 'warning',
+ title: error?.message
+ });
+ }
+ }, [appId, authToken, router, shareTeamId, t, toast]);
+
+ const startChat = useCallback(
+ async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
+ const prompts = messages.slice(-2);
+ const completionChatId = chatId ? chatId : nanoid();
+
+ const { responseText, responseData } = await streamFetch({
+ data: {
+ messages: prompts,
+ variables,
+ appId,
+ shareTeamId,
+ outLinkUid: outLinkUid,
+ chatId: completionChatId
+ },
+ onMessage: generatingMessage,
+ abortCtrl: controller
+ });
+
+ const newTitle =
+ chatContentReplaceBlock(prompts[0].content).slice(0, 20) ||
+ prompts[1]?.value?.slice(0, 20) ||
+ t('core.chat.New Chat');
+
+ // new chat
+ if (completionChatId !== chatId) {
+ const newHistory: ChatHistoryItemType = {
+ chatId: completionChatId,
+ updateTime: new Date(),
+ title: newTitle,
+ appId,
+ top: false
+ };
+ pushHistory(newHistory);
+ if (controller.signal.reason !== 'leave') {
+ forbidRefresh.current = true;
+ router.replace({
+ query: {
+ chatId: completionChatId,
+ appId,
+ shareTeamId,
+ authToken: authToken
+ } as routerQueryType
+ });
+ }
+ } else {
+ // update chat
+ const currentChat = histories.find((item) => item.chatId === chatId);
+ currentChat &&
+ updateHistory({
+ ...currentChat,
+ updateTime: new Date(),
+ title: newTitle
+ });
+ }
+ // update chat window
+ setChatData((state) => ({
+ ...state,
+ title: newTitle,
+ history: ChatBoxRef.current?.getChatHistories() || state.history
+ }));
+
+ return { responseText, responseData, isNewChat: forbidRefresh.current };
+ },
+ [appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
+ );
+
+ const { isFetching } = useQuery(['init', appId, shareTeamId], async () => {
+ console.log('res', 3);
+ if (!shareTeamId) {
+ toast({
+ status: 'error',
+ title: t('core.chat.You need to a chat app')
+ });
+ return;
+ }
+ return shareTeamId && loadApps();
+ });
+
+ useQuery(['loadHistories', appId], () => {
+ if (shareTeamId && appId) {
+ return loadHistories({ appId, outLinkUid });
+ }
+ return;
+ });
+ // 初始化聊天框
+ useQuery(['init', { appId, chatId }], () => {
+ if (!shareTeamId) {
+ toast({
+ status: 'error',
+ title: t('core.chat.You need to a chat app')
+ });
+ return;
+ }
+ if (myApps.length > 0 && myApps.findIndex((obj) => obj._id === appId) === -1) {
+ toast({
+ status: 'warning',
+ title: 'you do not have this App'
+ });
+ return;
+ }
+ // pc: redirect to latest model chat
+ if (!appId && lastChatAppId) {
+ return router.replace({
+ query: {
+ appId: lastChatAppId,
+ chatId: lastChatId,
+ shareTeamId,
+ authToken: authToken
+ } as routerQueryType
+ });
+ }
+ if (!appId && myApps[0]) {
+ return router.replace({
+ query: {
+ appId: myApps[0]._id,
+ chatId: lastChatId,
+ shareTeamId,
+ authToken: authToken
+ } as routerQueryType
+ });
+ }
+ if (!appId) {
+ (async () => {
+ const { apps = [] } = await getChatListById({ shareTeamId, authToken });
+ setMyApps(apps);
+ if (apps.length === 0) {
+ toast({
+ status: 'error',
+ title: t('core.chat.You need to a chat app')
+ });
+ } else {
+ router.replace({
+ query: {
+ appId: apps[0]._id,
+ chatId: lastChatId,
+ shareTeamId,
+ authToken: authToken
+ } as routerQueryType
+ });
+ }
+ })();
+ return;
+ }
+
+ // store id
+ appId && setLastChatAppId(appId);
+ setLastChatId(chatId);
+ return loadChatInfo({
+ appId,
+ chatId,
+ loading: appId !== chatData.appId
+ });
+ });
+
+ // get chat app info
+ const loadChatInfo = useCallback(
+ async ({
+ appId,
+ chatId,
+ loading = false
+ }: {
+ appId: string;
+ chatId: string;
+ loading?: boolean;
+ }) => {
+ try {
+ if (!shareTeamId) {
+ toast({
+ status: 'error',
+ title: t('core.chat.You need to a chat app')
+ });
+ return;
+ }
+ loading && setIsLoading(true);
+ const res = await getTeamChatInfo({ appId, chatId, outLinkUid });
+ console.log('res', res);
+ const history = res.history.map((item) => ({
+ ...item,
+ status: ChatStatusEnum.finish
+ }));
+
+ setChatData({
+ ...res,
+ history
+ });
+
+ // have records.
+ ChatBoxRef.current?.resetHistory(history);
+ ChatBoxRef.current?.resetVariables(res.variables);
+ if (res.history.length > 0) {
+ setTimeout(() => {
+ ChatBoxRef.current?.scrollToBottom('auto');
+ }, 500);
+ }
+ } catch (e: any) {
+ // reset all chat tore
+ setLastChatAppId('');
+ setLastChatId('');
+ toast({
+ title: t('core.chat.Failed to initialize chat'),
+ status: 'error'
+ });
+ if (e?.code === 501) {
+ //router.replace('/app/list');
+ } else if (chatId) {
+ router.replace({
+ query: {
+ ...router.query,
+ chatId: ''
+ } as routerQueryType
+ });
+ }
+ }
+ setIsLoading(false);
+ return null;
+ },
+ [setIsLoading, setChatData, router, setLastChatAppId, setLastChatId, toast]
+ );
+ // 监测路由改变
+ useEffect(() => {
+ const activeHistory = teamShareChatHistory.filter((item) => !item.delete);
+ if (!localUId || !shareTeamId || activeHistory.length === 0) return;
+ (async () => {
+ try {
+ await POST('/core/chat/initLocalShareHistoryV464', {
+ outLinkUid: localUId,
+ chatIds: teamShareChatHistory.map((item) => item.chatId)
+ });
+ clearLocalHistory();
+ // router.reload();
+ } catch (error) {
+ toast({
+ status: 'warning',
+ title: t('core.shareChat.Init Error')
+ });
+ }
+ })();
+ }, [clearLocalHistory, localUId, router, teamShareChatHistory, shareTeamId, t, toast]);
+
+ return (
+
+ {/* pc show myself apps */}
+
+
+
+ {myApps &&
+ myApps.map((item) => (
+ {
+ router.replace({
+ query: {
+ appId: item._id,
+ shareTeamId,
+ authToken: authToken
+ } as routerQueryType
+ });
+ }
+ })}
+ >
+
+
+ {item.name}
+
+
+ ))}
+
+
+
+
+
+ {((children: React.ReactNode) => {
+ return isPc || !appId ? (
+ {children}
+ ) : (
+
+
+ {children}
+
+ );
+ })(
+ ({
+ id: item.chatId,
+ title: item.title,
+ customTitle: item.customTitle,
+ top: item.top
+ }))}
+ onChangeChat={(chatId) => {
+ router.replace({
+ query: {
+ chatId: chatId || '',
+ appId,
+ shareTeamId,
+ authToken: authToken
+ } as routerQueryType
+ });
+ if (!isPc) {
+ onCloseSlider();
+ }
+ }}
+ onDelHistory={(e) => delOneHistory({ ...e, appId })}
+ onClearHistory={() => {
+ clearHistories({ appId });
+ router.replace({
+ query: {
+ appId,
+ shareTeamId,
+ authToken: authToken
+ } as routerQueryType
+ });
+ }}
+ onSetHistoryTop={(e) => {
+ updateHistory({ ...e, appId });
+ }}
+ onSetCustomTitle={async (e) => {
+ updateHistory({
+ appId,
+ chatId: e.chatId,
+ title: e.title,
+ customTitle: e.title
+ });
+ }}
+ />
+ )}
+ {/* chat container */}
+
+ {/* header */}
+
+ {/* chat box */}
+
+ {}}
+ onStartChat={startChat}
+ onDelMessage={(e) =>
+ delOneHistoryItem({ ...e, appId: chatData.appId, chatId, outLinkUid })
+ }
+ appId={chatData.appId}
+ shareTeamId={shareTeamId}
+ chatId={chatId}
+ outLinkUid={outLinkUid}
+ />
+
+
+
+
+
+ );
+};
+
+export async function getServerSideProps(context: any) {
+ const shareTeamId = context?.query?.shareTeamId || '';
+ const appId = context?.query?.appId || '';
+ const chatId = context?.query?.chatId || '';
+ const authToken: string = context?.query?.authToken || '';
+
+ return {
+ props: {
+ shareTeamId,
+ appId,
+ chatId,
+ authToken,
+ ...(await serviceSideProps(context))
+ }
+ };
+}
+
+export default OutLink;
diff --git a/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx b/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx
index 00a8d479d23..fb6c5525ccf 100644
--- a/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx
+++ b/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx
@@ -1,6 +1,5 @@
import React, { useContext, useCallback, createContext, useState, useMemo, useEffect } from 'react';
-import { formatModelPrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter';
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
import { useTranslation } from 'next-i18next';
@@ -34,7 +33,7 @@ type useImportStoreType = {
totalChunkChars: number;
totalChunks: number;
chunkSize: number;
- predictPrice: number;
+ predictPoints: number;
priceTip: string;
uploadRate: number;
splitSources2Chunks: () => void;
@@ -54,7 +53,7 @@ const StateContext = createContext({
totalChunkChars: 0,
totalChunks: 0,
chunkSize: 0,
- predictPrice: 0,
+ predictPoints: 0,
priceTip: '',
uploadRate: 50,
splitSources2Chunks: () => {}
@@ -105,10 +104,9 @@ const Provider = ({
chunkSize: embeddingChunkSize,
showChunkInput: true,
showPromptInput: false,
- inputPrice: vectorModel.inputPrice,
- outputPrice: 0,
+ charsPointsPrice: vectorModel.charsPointsPrice,
priceTip: t('core.dataset.import.Embedding Estimated Price Tips', {
- price: vectorModel.inputPrice
+ price: vectorModel.charsPointsPrice
}),
uploadRate: 150
},
@@ -120,10 +118,9 @@ const Provider = ({
chunkSize: agentModel.maxContext * 0.55 || 6000,
showChunkInput: false,
showPromptInput: true,
- inputPrice: agentModel.inputPrice,
- outputPrice: agentModel.outputPrice,
+ charsPointsPrice: agentModel.charsPointsPrice,
priceTip: t('core.dataset.import.QA Estimated Price Tips', {
- price: agentModel?.inputPrice
+ price: agentModel?.charsPointsPrice
}),
uploadRate: 30
}
@@ -151,15 +148,12 @@ const Provider = ({
() => sources.reduce((sum, file) => sum + file.chunkChars, 0),
[sources]
);
- const predictPrice = useMemo(() => {
+ const predictPoints = useMemo(() => {
if (mode === TrainingModeEnum.qa) {
- const inputTotal = totalChunkChars * selectModelStaticParam.inputPrice;
- const outputTotal = totalChunkChars * 0.5 * selectModelStaticParam.inputPrice;
-
- return formatModelPrice2Read(inputTotal + outputTotal);
+ return +(((totalChunkChars * 1.5) / 1000) * agentModel.charsPointsPrice).toFixed(2);
}
- return formatModelPrice2Read(totalChunkChars * selectModelStaticParam.inputPrice);
- }, [mode, selectModelStaticParam.inputPrice, totalChunkChars]);
+ return +((totalChunkChars / 1000) * vectorModel.charsPointsPrice).toFixed(2);
+ }, [agentModel.charsPointsPrice, mode, totalChunkChars, vectorModel.charsPointsPrice]);
const totalChunks = useMemo(
() => sources.reduce((sum, file) => sum + file.chunks.length, 0),
[sources]
@@ -178,7 +172,8 @@ const Provider = ({
return {
...file,
chunkChars: chars,
- chunks: chunks.map((chunk) => ({
+ chunks: chunks.map((chunk, i) => ({
+ chunkIndex: i,
q: chunk,
a: ''
}))
@@ -198,7 +193,7 @@ const Provider = ({
totalChunkChars,
totalChunks,
chunkSize,
- predictPrice,
+ predictPoints,
splitSources2Chunks
};
return {children};
diff --git a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx
index 2f3eef5cf1e..d34e536a276 100644
--- a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx
+++ b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx
@@ -46,7 +46,7 @@ function DataProcess({
maxChunkSize,
totalChunkChars,
totalChunks,
- predictPrice,
+ predictPoints,
showRePreview,
splitSources2Chunks,
priceTip
@@ -275,7 +275,7 @@ function DataProcess({
{feConfigs?.show_pay && (
- {t('core.dataset.import.Estimated Price', { amount: predictPrice, unit: '元' })}
+ {t('core.dataset.import.Estimated points', { points: predictPoints })}
)}
diff --git a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx
index f7e0475d6c0..29796336c4d 100644
--- a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx
+++ b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx
@@ -16,7 +16,7 @@ import { useImportStore, type FormType } from '../Provider';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRequest } from '@/web/common/hooks/useRequest';
-import { postCreateTrainingBill } from '@/web/support/wallet/bill/api';
+import { postCreateTrainingUsage } from '@/web/support/wallet/usage/api';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { chunksUpload, fileCollectionCreate } from '@/web/core/dataset/utils';
import { ImportSourceItemType } from '@/web/core/dataset/type';
@@ -54,11 +54,6 @@ const Upload = ({ showPreviewChunks }: { showPreviewChunks: boolean }) => {
// Batch create collection and upload chunks
for await (const item of uploadList) {
- const billId = await postCreateTrainingBill({
- name: item.sourceName,
- datasetId: datasetDetail._id
- });
-
// create collection
const collectionId = await (async () => {
const commonParams = {
@@ -125,6 +120,12 @@ const Upload = ({ showPreviewChunks }: { showPreviewChunks: boolean }) => {
})();
if (!collectionId) continue;
+ if (item.link) continue;
+
+ const billId = await postCreateTrainingUsage({
+ name: item.sourceName,
+ datasetId: datasetDetail._id
+ });
// upload chunks
const chunks = item.chunks;
diff --git a/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx b/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx
index 14bfb5ddbf3..d857cd80d9b 100644
--- a/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx
+++ b/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx
@@ -19,7 +19,6 @@ import { useRequest } from '@/web/common/hooks/useRequest';
import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { getDefaultIndex } from '@fastgpt/global/core/dataset/utils';
-import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { DatasetDataIndexItemType } from '@fastgpt/global/core/dataset/type';
import SideTabs from '@/components/SideTabs';
import DeleteIcon from '@fastgpt/web/components/common/Icon/delete';
@@ -118,8 +117,7 @@ const InputDataModal = ({
} else if (defaultValue) {
reset({
q: defaultValue.q,
- a: defaultValue.a,
- indexes: [getDefaultIndex({ dataId: `${Date.now()}` })]
+ a: defaultValue.a
});
}
},
@@ -149,10 +147,7 @@ const InputDataModal = ({
return Promise.reject(t('dataset.data.input is empty'));
}
if (countPromptTokens(e.q) >= maxToken) {
- return toast({
- title: t('core.dataset.data.Too Long'),
- status: 'warning'
- });
+ return Promise.reject(t('core.dataset.data.Too Long'));
}
const data = { ...e };
@@ -162,9 +157,11 @@ const InputDataModal = ({
q: e.q,
a: e.a,
// remove dataId
- indexes: e.indexes.map((index) =>
- index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a }) : index
- )
+ indexes:
+ e.indexes?.map((index) => ({
+ ...index,
+ dataId: undefined
+ })) || []
});
return {
@@ -178,7 +175,7 @@ const InputDataModal = ({
...e,
q: '',
a: '',
- indexes: [getDefaultIndex({ q: e.q, a: e.a, dataId: `${Date.now()}` })]
+ indexes: []
});
onSuccess(e);
@@ -194,9 +191,10 @@ const InputDataModal = ({
await putDatasetDataById({
id: dataId,
...e,
- indexes: e.indexes.map((index) =>
- index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a }) : index
- )
+ indexes:
+ e.indexes?.map((index) =>
+ index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a, dataId: index.dataId }) : index
+ ) || []
});
return {
@@ -269,7 +267,7 @@ const InputDataModal = ({
{currentTab === TabEnum.content && }
{currentTab === TabEnum.index && (
- {indexes.map((index, i) => (
+ {indexes?.map((index, i) => (
@@ -331,7 +329,6 @@ const InputDataModal = ({
onClick={() =>
appendIndexes({
defaultIndex: false,
- type: DatasetDataIndexTypeEnum.chunk,
text: '',
dataId: `${Date.now()}`
})
@@ -383,45 +380,47 @@ const InputTab = ({
const [inputType, setInputType] = useState(InputTypeEnum.q);
return (
-
-
-
- *
-
- {t('core.dataset.data.Main Content')}
-
-
-
-
- ),
- value: InputTypeEnum.q
- },
- {
- label: (
-
- {t('core.dataset.data.Auxiliary Data')}
-
-
-
-
- ),
- value: InputTypeEnum.a
- }
- ]}
- value={inputType}
- onChange={(e) => setInputType(e as InputTypeEnum)}
- />
+
+
+
+
+ *
+
+ {t('core.dataset.data.Main Content')}
+
+
+
+
+ ),
+ value: InputTypeEnum.q
+ },
+ {
+ label: (
+
+ {t('core.dataset.data.Auxiliary Data')}
+
+
+
+
+ ),
+ value: InputTypeEnum.a
+ }
+ ]}
+ value={inputType}
+ onChange={(e) => setInputType(e as InputTypeEnum)}
+ />
+
-
+
{inputType === InputTypeEnum.q && (
)}
-
+
);
};
diff --git a/projects/app/src/pages/dataset/detail/index.tsx b/projects/app/src/pages/dataset/detail/index.tsx
index b8488ab0060..4c24f9f2523 100644
--- a/projects/app/src/pages/dataset/detail/index.tsx
+++ b/projects/app/src/pages/dataset/detail/index.tsx
@@ -27,6 +27,8 @@ import {
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useRequest } from '@/web/common/hooks/useRequest';
import DatasetTypeTag from '@/components/core/dataset/DatasetTypeTag';
+import Head from 'next/head';
+import MyBox from '@/components/common/MyBox';
const DataCard = dynamic(() => import('./components/DataCard'));
const Test = dynamic(() => import('./components/Test'));
@@ -145,9 +147,17 @@ const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${T
return (
<>
-
+
+ {datasetDetail?.name}
+
-
+
{isPc ? (
}
)}
-
+
-
+
>
);
};
diff --git a/projects/app/src/pages/price/components/ExtraPlan.tsx b/projects/app/src/pages/price/components/ExtraPlan.tsx
index 407ffca3c12..f331c1d0342 100644
--- a/projects/app/src/pages/price/components/ExtraPlan.tsx
+++ b/projects/app/src/pages/price/components/ExtraPlan.tsx
@@ -7,92 +7,110 @@ import {
NumberIncrementStepper,
NumberInputField,
NumberInputStepper,
- Button,
- useDisclosure,
- ModalBody,
- ModalFooter
+ Button
} from '@chakra-ui/react';
-import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
import { useTranslation } from 'next-i18next';
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useState } from 'react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
-import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
-import MySelect from '@/components/Select';
-import {
- SubStatusEnum,
- SubTypeEnum,
- subSelectMap
-} from '@fastgpt/global/support/wallet/sub/constants';
-import { useRequest } from '@/web/common/hooks/useRequest';
-import {
- posCheckTeamDatasetSizeSub,
- postUpdateTeamDatasetSizeSub,
- putTeamDatasetSubStatus
-} from '@/web/support/wallet/sub/api';
-import { SubDatasetSizePreviewCheckResponse } from '@fastgpt/global/support/wallet/sub/api.d';
import { useRouter } from 'next/router';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
-import { useConfirm } from '@/web/common/hooks/useConfirm';
-import { useUserStore } from '@/web/support/user/useUserStore';
-import MyModal from '@/components/MyModal';
+import { useForm } from 'react-hook-form';
+import { useToast } from '@fastgpt/web/hooks/useToast';
+import { getErrText } from '@fastgpt/global/common/error/utils';
+import { getWxPayQRCode } from '@/web/support/wallet/bill/api';
+import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
+import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
-const ExtraPlan = ({ extraDatasetSize }: { extraDatasetSize?: TeamSubSchema }) => {
+const ExtraPlan = () => {
const { t } = useTranslation();
+ const { toast } = useToast();
const { subPlans } = useSystemStore();
- const extraDatasetPrice = subPlans?.extraDatasetSize?.price || 0;
- const [datasetSize, setDatasetSize] = useState(0);
- const [isRenew, setIsRenew] = useState('false');
- const router = useRouter();
- const { userInfo } = useUserStore();
+ const [loading, setLoading] = useState(false);
+ const [qrPayData, setQRPayData] = useState();
- const [confirmPayExtraDatasetSizeData, setConfirmPayExtraDatasetSizeData] =
- useState();
-
- useEffect(() => {
- setDatasetSize((extraDatasetSize?.nextExtraDatasetSize || 0) / 1000);
- setIsRenew(extraDatasetSize?.status === SubStatusEnum.active ? 'true' : 'false');
- }, [extraDatasetSize]);
+ // extra dataset
+ const extraDatasetPrice = subPlans?.extraDatasetSize?.price || 0;
+ const { register: registerDatasetSize, handleSubmit: handleSubmitDatasetSize } = useForm({
+ defaultValues: {
+ datasetSize: 0,
+ month: 1
+ }
+ });
+ const onclickBuyDatasetSize = useCallback(
+ async ({ datasetSize, month }: { datasetSize: number; month: number }) => {
+ try {
+ const datasetSizePayAmount = datasetSize * month * extraDatasetPrice;
+ if (datasetSizePayAmount === 0) {
+ return toast({
+ status: 'warning',
+ title: '购买数量不能为0'
+ });
+ }
+ setLoading(true);
- const { mutate: onUpdateExtraDatasetSizeStatus } = useRequest({
- mutationFn: (e: 'true' | 'false') => {
- setIsRenew(e);
- return putTeamDatasetSubStatus({
- status: subSelectMap[e],
- type: SubTypeEnum.extraDatasetSize
- });
+ const res = await getWxPayQRCode({
+ type: BillTypeEnum.extraDatasetSub,
+ month,
+ extraDatasetSize: datasetSize
+ });
+ setQRPayData({
+ readPrice: res.readPrice,
+ codeUrl: res.codeUrl,
+ billId: res.billId
+ });
+ } catch (err) {
+ toast({
+ title: getErrText(err),
+ status: 'error'
+ });
+ }
+ setLoading(false);
},
- successToast: t('common.Update success'),
- errorToast: t('common.error.Update error')
- });
+ [extraDatasetPrice, toast]
+ );
- const { mutate: onClickUpdateExtraDatasetPlan, isLoading: isPayingExtraDatasetSize } = useRequest(
- {
- mutationFn: () => postUpdateTeamDatasetSizeSub({ size: datasetSize }),
- onSuccess() {
- setTimeout(() => {
- router.reload();
- }, 100);
- },
- successToast: t('common.Update success'),
- errorToast: t('common.error.Update error')
+ // extra ai points
+ const extraPointsPrice = subPlans?.extraPoints?.price || 0;
+ const { register: registerExtraPoints, handleSubmit: handleSubmitExtraPoints } = useForm({
+ defaultValues: {
+ points: 0,
+ month: 1
}
- );
- const { mutate: onClickPreviewCheck, isLoading: isFetchingPreviewCheck } = useRequest({
- mutationFn: () =>
- posCheckTeamDatasetSizeSub({
- size: datasetSize
- }),
- onSuccess(res: SubDatasetSizePreviewCheckResponse) {
- if (!res.payForNewSub) {
- onClickUpdateExtraDatasetPlan('');
- return;
- } else {
- setConfirmPayExtraDatasetSizeData(res);
+ });
+ const onclickBuyExtraPoints = useCallback(
+ async ({ points }: { points: number }) => {
+ try {
+ const month = 1;
+ const payAmount = points * month * extraPointsPrice;
+
+ if (payAmount === 0) {
+ return toast({
+ status: 'warning',
+ title: '购买数量不能为0'
+ });
+ }
+ setLoading(true);
+
+ const res = await getWxPayQRCode({
+ type: BillTypeEnum.extraPoints,
+ extraPoints: points
+ });
+
+ setQRPayData({
+ readPrice: res.readPrice,
+ codeUrl: res.codeUrl,
+ billId: res.billId
+ });
+ } catch (err) {
+ toast({
+ title: getErrText(err),
+ status: 'error'
+ });
}
+ setLoading(false);
},
- errorToast: t('common.error.Update error')
- });
+ [extraPointsPrice, toast]
+ );
return (
-
+
{t('support.wallet.subscription.Extra plan tip')}
-
+
-
+
{t('support.wallet.subscription.Extra dataset size')}
-
- ¥{extraDatasetPrice}/1k组{' '}
+
+ ¥{extraDatasetPrice}/1000组{' '}
/{t('common.month')}
-
+
-
- {t('support.wallet.subscription.Current dataset store')}:{' '}
-
-
- {extraDatasetSize?.currentExtraDatasetSize || 0}
- {t('core.dataset.data.unit')}
-
+
+ 购买资源包
- {extraDatasetSize?.nextExtraDatasetSize !== undefined && (
-
-
- {t('support.wallet.subscription.Next sub dataset size')}:
-
-
- {extraDatasetSize?.nextExtraDatasetSize || 0}
- {t('core.dataset.data.unit')}
+
+
+ {t('support.wallet.subscription.Month amount')}
+
+
+
+
+
+
+
+
+
+
+ {t('common.month')}
- )}
- {!!extraDatasetSize?.startTime && (
-
- 订阅开始时间:
- {formatTime2YMDHM(extraDatasetSize?.startTime)}
-
- )}
- {!!extraDatasetSize?.expiredTime && (
-
- 订阅到期时间:
- {formatTime2YMDHM(extraDatasetSize?.expiredTime)}
-
- )}
-
- 是否自动续费:
- {
- if (!extraDatasetSize) return;
- onUpdateExtraDatasetSizeStatus(e);
- }}
- />
-
+
{t('support.wallet.subscription.Update extra dataset size')}
@@ -197,13 +197,18 @@ const ExtraPlan = ({ extraDatasetSize }: { extraDatasetSize?: TeamSubSchema }) =
min={0}
max={10000}
step={1}
- value={datasetSize}
position={'relative'}
- onChange={(e) => {
- setDatasetSize(Number(e));
- }}
>
-
+
@@ -214,89 +219,125 @@ const ExtraPlan = ({ extraDatasetSize }: { extraDatasetSize?: TeamSubSchema }) =
-
- {t('common.change')}
-
+
+ {t('support.wallet.Buy')}
+
-
-
- {/* extra dataset size modal */}
- {!!confirmPayExtraDatasetSizeData && (
- setConfirmPayExtraDatasetSizeData(undefined)}
- title={t('support.wallet.Confirm pay')}
- iconSrc="common/confirm/rightTip"
+ {/* points */}
+
-
-
-
- 当前额外容量
+
+
+
+ {t('support.wallet.subscription.Extra ai points')}
- {extraDatasetSize?.currentExtraDatasetSize || 0}条
-
-
-
- 新的额外容量
+
+ ¥{extraPointsPrice}/1000积分{' '}
+
+ /{t('common.month')}
+
- {confirmPayExtraDatasetSizeData.newSubSize}条
-
+
+
+
+
-
- 新套餐价格
-
- {formatStorePrice2Read(confirmPayExtraDatasetSizeData.newPlanPrice)}元
+
+ 购买资源包
-
-
- 有效时长
+ {/*
+
+ {t('support.wallet.subscription.Month amount')}
- 30天
-
-
- {/*
- 账号余额:
- {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}元
- */}
-
-
- 账号余额:
- {confirmPayExtraDatasetSizeData.balanceEnough ? (
- <>
-
- {formatStorePrice2Read(userInfo?.team?.balance).toFixed(2)}元
+
+
+
+
+
+
+
+
+
+ {t('common.month')}
- onClickUpdateExtraDatasetPlan('')}
+
+ */}
+
+
+ {t('support.wallet.subscription.Update extra ai points')}
+
+
+
- 支付{formatStorePrice2Read(confirmPayExtraDatasetSizeData.payPrice).toFixed(2)}元
-
- >
- ) : (
- <>
-
- 余额不足
+
+
+
+
+
+
+
+ 000积分
- router.push('/account')}
- >
- 去充值
-
- >
- )}
-
-
- )}
+
+
+
+
+ {t('support.wallet.Buy')}
+
+
+
+
+ {!!qrPayData && }
);
};
diff --git a/projects/app/src/pages/price/components/FAQ.tsx b/projects/app/src/pages/price/components/FAQ.tsx
index 2012264171b..89b5cca4068 100644
--- a/projects/app/src/pages/price/components/FAQ.tsx
+++ b/projects/app/src/pages/price/components/FAQ.tsx
@@ -4,7 +4,40 @@ import { useTranslation } from 'next-i18next';
const FAQ = () => {
const { t } = useTranslation();
- const faqs = [{ title: '怎么付费', describe: '2222' }];
+ const faqs = [
+ {
+ title: '订阅套餐会自动续费么?',
+ desc: '当前套餐过期后,系统会自动根据“未来套餐”进行续费,系统会尝试从账户余额进行扣费,如果您需要自动续费,请在账户余额中预留额度。'
+ },
+ {
+ title: '能否切换订阅套餐?',
+ desc: '当前套餐价格大于新套餐时,无法立即切换,将会在当前套餐过期后以“续费”形式进行切换。\n当前套餐价格小于新套餐时,系统会自动计算当前套餐剩余余额,您可支付差价进行套餐切换。'
+ },
+ {
+ title: '什么是AI积分?',
+ desc: '每次调用AI模型时,都会消耗一定的AI积分。具体的计算标准可参考上方的“AI 积分计算标准”。\n1 字符=1中英文字符和标点符号,会去掉换行和空格符号,计算字符时包含对话上下文与知识库引用。'
+ },
+ {
+ title: 'AI积分会过期么?',
+ desc: '会过期。当前套餐过期后,AI积分将会清空,并更新为新套餐的AI积分。年度套餐的AI积分时长为1年,而不是每个月。'
+ },
+ {
+ title: '知识库存储怎么计算?',
+ desc: '1条知识库存储等于1条知识库索引。一条知识库数据可以包含1条或多条知识库索引。'
+ },
+ {
+ title: '知识库索引超出会删除么?',
+ desc: '不会。但知识库索引超出时,无法插入和更新知识库内容。'
+ },
+ {
+ title: '额外资源包可以叠加么?',
+ desc: '可以的。每次购买的资源包都是独立的,在其有效期内将会叠加使用。AI积分会优先扣除最先过期的资源包。'
+ },
+ {
+ title: '免费版数据会清除么?',
+ desc: '免费版用户15天无使用记录后,会自动清除所有知识库内容。'
+ }
+ ];
return (
{
{t('support.wallet.subscription.FAQ')}
-
-
- 怎么付费
+ {faqs.map((item, i) => (
+
+ {item.title}
+
+ {item.desc}
+
- 2222
-
+ ))}
);
diff --git a/projects/app/src/pages/price/components/Points.tsx b/projects/app/src/pages/price/components/Points.tsx
index 1cc6141db4b..98292329c03 100644
--- a/projects/app/src/pages/price/components/Points.tsx
+++ b/projects/app/src/pages/price/components/Points.tsx
@@ -15,7 +15,7 @@ const Points = () => {
alignItems={'center'}
position={'relative'}
>
-
+
{t('support.wallet.subscription.Ai points')}
@@ -42,7 +42,7 @@ const Points = () => {
{llmModelList?.map((item, i) => (
{item.name}
- 5积分 / 1000字符
+ {item.charsPointsPrice}积分 / 1000字符
))}
@@ -67,7 +67,7 @@ const Points = () => {
{vectorModelList?.map((item, i) => (
{item.name}
- 5积分 / 1000字符
+ {item.charsPointsPrice}积分 / 1000字符
))}
@@ -89,7 +89,7 @@ const Points = () => {
{audioSpeechModelList?.map((item, i) => (
{item.name}
- 5积分 / 1000字符
+ {item.charsPointsPrice}积分 / 1000字符
))}
@@ -110,7 +110,7 @@ const Points = () => {
{whisperModel?.name}
- {whisperModel?.inputPrice}积分 / 分钟
+ {whisperModel?.charsPointsPrice}积分 / 分钟
diff --git a/projects/app/src/pages/price/components/Standard.tsx b/projects/app/src/pages/price/components/Standard.tsx
index 4023cd0e4a0..94164f3af34 100644
--- a/projects/app/src/pages/price/components/Standard.tsx
+++ b/projects/app/src/pages/price/components/Standard.tsx
@@ -1,9 +1,8 @@
import React, { useMemo, useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
-import { Box, Button, Flex, Grid } from '@chakra-ui/react';
+import { Box, Button, Flex, Grid, ModalBody, ModalFooter } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants';
-import { useUserStore } from '@/web/support/user/useUserStore';
import { postCheckStandardSub, postUpdateStandardSub } from '@/web/support/wallet/sub/api';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
@@ -11,9 +10,22 @@ import { StandardSubPlanParams } from '@fastgpt/global/support/wallet/sub/api';
import { useRequest } from '@/web/common/hooks/useRequest';
import { StandardSubPlanUpdateResponse } from '@fastgpt/global/support/wallet/sub/api.d';
import { useToast } from '@fastgpt/web/hooks/useToast';
-import { useConfirm } from '@/web/common/hooks/useConfirm';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
+import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
+import MyModal from '@/components/MyModal';
+import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
+import { getWxPayQRCode } from '@/web/support/wallet/bill/api';
+import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
+import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList';
+import { useRouter } from 'next/router';
+
+type ConfirmPayModalProps = {
+ teamBalance: number;
+ totalPrice: number;
+ payPrice: number;
+
+ planProps: StandardSubPlanParams;
+};
const Standard = ({
standardPlan,
@@ -23,10 +35,9 @@ const Standard = ({
refetchTeamSubPlan: () => void;
}) => {
const { t } = useTranslation();
+ const router = useRouter();
const { subPlans, feConfigs } = useSystemStore();
- const { toast } = useToast();
- const { ConfirmModal, openConfirm } = useConfirm({});
-
+ const [confirmPayData, setConfirmPayData] = useState();
const [selectSubMode, setSelectSubMode] = useState<`${SubModeEnum}`>(SubModeEnum.month);
const standardSubList = useMemo(() => {
@@ -41,12 +52,12 @@ const Standard = ({
maxDatasetAmount: value.maxDatasetAmount,
chatHistoryStoreDuration: value.chatHistoryStoreDuration,
maxDatasetSize: value.maxDatasetSize,
- customApiKey: value.customApiKey,
- customCopyright: value.customCopyright,
+ permissionCustomApiKey: value.permissionCustomApiKey,
+ permissionCustomCopyright: value.permissionCustomCopyright,
trainingWeight: value.trainingWeight,
- reRankWeight: value.reRankWeight,
+ permissionReRank: value.permissionReRank,
totalPoints: value.totalPoints * (selectSubMode === SubModeEnum.month ? 1 : 12),
- websiteSyncInterval: value.websiteSyncInterval
+ permissionWebsiteSync: value.permissionWebsiteSync
};
})
: [];
@@ -56,6 +67,7 @@ const Standard = ({
mutationFn: (data: StandardSubPlanParams) => postUpdateStandardSub(data),
onSuccess() {
refetchTeamSubPlan();
+ router.reload();
},
successToast: t('support.wallet.subscription.Standard update success'),
errorToast: t('support.wallet.subscription.Standard update fail')
@@ -64,41 +76,21 @@ const Standard = ({
const { mutate: onclickPreCheckStandPlan, isLoading: isCheckingStandardPlan } = useRequest({
mutationFn: (data: StandardSubPlanParams) => postCheckStandardSub(data),
onSuccess(res: StandardSubPlanUpdateResponse) {
- if (!res.balanceEnough) {
- return toast({
- status: 'warning',
- title: t('support.wallet.Balance not enough tip')
- });
- }
if (res.payPrice === undefined) {
onclickUpdateStandardPlan({
level: res.nextSubLevel,
mode: res.nextMode
});
- } else if (res.payPrice > 0) {
- openConfirm(
- () =>
- onclickUpdateStandardPlan({
- level: res.nextSubLevel,
- mode: res.nextMode
- }),
- undefined,
- t('support.wallet.subscription.Standard plan pay confirm', {
- payPrice: formatStorePrice2Read(res.payPrice).toFixed(2)
- })
- )();
} else {
- openConfirm(
- () =>
- onclickUpdateStandardPlan({
- level: res.nextSubLevel,
- mode: res.nextMode
- }),
- undefined,
- t('support.wallet.subscription.Refund plan and pay confirm', {
- amount: formatStorePrice2Read(Math.abs(res.payPrice)).toFixed(2)
- })
- )();
+ setConfirmPayData({
+ teamBalance: res.teamBalance,
+ totalPrice: res.planPrice,
+ payPrice: res.payPrice,
+ planProps: {
+ level: res.nextSubLevel,
+ mode: res.nextMode
+ }
+ });
}
}
});
@@ -137,149 +129,116 @@ const Standard = ({
gridTemplateColumns={['1fr', 'repeat(2,1fr)', 'repeat(4,1fr)']}
gap={[4, 6, 8]}
w={'100%'}
+ maxW={'1440px'}
>
- {standardSubList.map((item) => (
-
-
- {t(item.label)}
-
-
- ¥{item.price}
-
-
- {t(item.desc, { title: feConfigs?.systemTitle })}
-
- {(() => {
- if (item.level === StandardSubLevelEnum.free && selectSubMode === SubModeEnum.year) {
- return (
-
- {t('support.wallet.subscription.Nonsupport')}
-
- );
- }
- if (
- item.level === standardPlan?.currentSubLevel &&
- selectSubMode === standardPlan?.currentMode
- ) {
- return (
-
- {t('support.wallet.subscription.Current plan')}
-
- );
- }
- if (
- item.level === standardPlan?.nextSubLevel &&
- selectSubMode === standardPlan?.nextMode
- ) {
+ {standardSubList.map((item) => {
+ const isCurrentPlan =
+ item.level === standardPlan?.currentSubLevel &&
+ selectSubMode === standardPlan?.currentMode;
+
+ return (
+
+
+ {t(item.label)}
+
+
+ ¥{item.price}
+
+
+ {t(item.desc, { title: feConfigs?.systemTitle })}
+
+ {(() => {
+ if (
+ item.level === StandardSubLevelEnum.free &&
+ selectSubMode === SubModeEnum.year
+ ) {
+ return (
+
+ {t('support.wallet.subscription.Nonsupport')}
+
+ );
+ }
+ if (
+ item.level === standardPlan?.nextSubLevel &&
+ selectSubMode === standardPlan?.nextMode
+ ) {
+ return (
+
+ {t('support.wallet.subscription.Next plan')}
+
+ );
+ }
+ if (isCurrentPlan) {
+ return (
+
+ onclickPreCheckStandPlan({
+ level: item.level,
+ mode: selectSubMode
+ })
+ }
+ >
+ {t('support.wallet.subscription.Current plan')}
+
+ );
+ }
+
return (
-
- {t('support.wallet.subscription.Next plan')}
+
+ onclickPreCheckStandPlan({
+ level: item.level,
+ mode: selectSubMode
+ })
+ }
+ >
+ {t('support.wallet.subscription.Buy now')}
);
- }
- return (
-
- onclickPreCheckStandPlan({
- level: item.level,
- mode: selectSubMode
- })
- }
- >
- {t('support.wallet.subscription.Buy now')}
-
- );
- })()}
+ })()}
- {/* function list */}
-
-
-
-
- {t('support.wallet.subscription.function.Max members', {
- amount: item.maxTeamMember
- })}
-
-
-
-
-
- {t('support.wallet.subscription.function.Max app', {
- amount: item.maxAppAmount
- })}
-
-
-
-
-
- {t('support.wallet.subscription.function.Max dataset', {
- amount: item.maxDatasetAmount
- })}
-
-
-
-
-
- {t('support.wallet.subscription.function.History store', {
- amount: item.chatHistoryStoreDuration
- })}
-
-
-
-
-
- {t('support.wallet.subscription.function.Max dataset size', {
- amount: item.maxDatasetSize
- })}
-
-
-
-
-
- {t('support.wallet.subscription.function.Points', {
- amount: item.totalPoints
- })}
-
-
-
-
-
- {t('support.wallet.subscription.Training weight', {
- weight: item.trainingWeight
- })}
-
-
- {!!item.customApiKey && (
-
-
- 个人API Key
-
- )}
- {!!item.websiteSyncInterval && (
-
-
- {item.websiteSyncInterval} h/次 web站点同步
-
- )}
-
-
- ))}
+ {/* function list */}
+
+
+ );
+ })}
-
+ {!!confirmPayData && (
+ setConfirmPayData(undefined)}
+ onConfirmPay={() => onclickUpdateStandardPlan(confirmPayData.planProps)}
+ />
+ )}
);
};
@@ -338,3 +297,87 @@ const RowTabs = ({
);
};
+
+const ConfirmPayModal = ({
+ teamBalance,
+ totalPrice,
+ payPrice,
+ onClose,
+ onConfirmPay
+}: ConfirmPayModalProps & { onClose: () => void; onConfirmPay: () => void }) => {
+ const { t } = useTranslation();
+ const [qrPayData, setQRPayData] = useState();
+
+ const formatPayPrice = Math.ceil(formatStorePrice2Read(payPrice));
+ const formatTeamBalance = Math.floor(formatStorePrice2Read(teamBalance));
+
+ const { mutate: handleClickPay, isLoading } = useRequest({
+ mutationFn: async (amount: number) => {
+ // 获取支付二维码
+ return getWxPayQRCode({
+ type: BillTypeEnum.balance,
+ balance: amount
+ });
+ },
+ onSuccess(res) {
+ setQRPayData({
+ readPrice: res.readPrice,
+ codeUrl: res.codeUrl,
+ billId: res.billId
+ });
+ }
+ });
+
+ return (
+
+
+
+ 新套餐价格
+ {formatStorePrice2Read(totalPrice)}元
+
+
+ 旧套餐余额
+ {Math.floor(formatStorePrice2Read(totalPrice - payPrice))}元
+
+
+ 需支付
+ {formatPayPrice}元
+
+
+
+ 账号余额:
+
+ {formatTeamBalance}元
+
+ {teamBalance >= payPrice ? (
+
+ 确认支付
+
+ ) : (
+ {
+ handleClickPay(Math.ceil(formatStorePrice2Read(payPrice - teamBalance)));
+ }}
+ >
+ 余额不足,去充值
+
+ )}
+
+
+ {!!qrPayData && }
+
+ );
+};
diff --git a/projects/app/src/pages/price/index.tsx b/projects/app/src/pages/price/index.tsx
index 264e77a1c2d..78add702c46 100644
--- a/projects/app/src/pages/price/index.tsx
+++ b/projects/app/src/pages/price/index.tsx
@@ -3,48 +3,56 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
import { Box, Image } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useUserStore } from '@/web/support/user/useUserStore';
-import { getTeamDatasetValidSub } from '@/web/support/wallet/sub/api';
+import { getTeamPlanStatus } from '@/web/support/user/team/api';
import { useQuery } from '@tanstack/react-query';
import StandardPlan from './components/Standard';
import ExtraPlan from './components/ExtraPlan';
import PointsCard from './components/Points';
import FAQ from './components/FAQ';
+import { getToken } from '@/web/support/user/auth';
+import Script from 'next/script';
const PriceBox = () => {
const { t } = useTranslation();
const { userInfo } = useUserStore();
const { data: teamSubPlan, refetch: refetchTeamSubPlan } = useQuery(
- ['getTeamDatasetValidSub'],
- getTeamDatasetValidSub,
+ ['getTeamPlanStatus'],
+ getTeamPlanStatus,
{
- enabled: !!userInfo
+ enabled: !!getToken() || !!userInfo
}
);
return (
-
- {/* standard sub */}
-
-
-
-
- {/* points */}
-
-
- {/* question */}
-
-
+ <>
+
+
+ {/* standard sub */}
+
+
+
+
+ {/* points */}
+
+
+ {/* question */}
+
+
+ >
);
};
diff --git a/projects/app/src/pages/tools/index.tsx b/projects/app/src/pages/tools/index.tsx
index 4ce14692a5f..667a871f735 100644
--- a/projects/app/src/pages/tools/index.tsx
+++ b/projects/app/src/pages/tools/index.tsx
@@ -40,6 +40,15 @@ const Tools = () => {
link: getDocPath('/docs/intro')
}
]
+ : []),
+ ...(feConfigs?.show_pay
+ ? [
+ {
+ icon: 'support/bill/priceLight',
+ label: '计费说明',
+ link: '/price'
+ }
+ ]
: [])
];
diff --git a/projects/app/src/pages/tools/price.tsx b/projects/app/src/pages/tools/price.tsx
deleted file mode 100644
index 6a997eb3407..00000000000
--- a/projects/app/src/pages/tools/price.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import Price from '@/components/support/wallet/Price';
-import { useRouter } from 'next/router';
-import { serviceSideProps } from '@/web/common/utils/i18n';
-
-const PriceBox = () => {
- const router = useRouter();
- return ;
-};
-
-export default PriceBox;
-
-export async function getServerSideProps(context: any) {
- return {
- props: { ...(await serviceSideProps(context)) }
- };
-}
diff --git a/projects/app/src/service/common/system/cron.ts b/projects/app/src/service/common/system/cron.ts
index 8f837f40d7c..fa2f4096d27 100644
--- a/projects/app/src/service/common/system/cron.ts
+++ b/projects/app/src/service/common/system/cron.ts
@@ -1,6 +1,5 @@
import { initSystemConfig } from '@/pages/api/common/system/getInitData';
-import { generateQA } from '@/service/events/generateQA';
-import { generateVector } from '@/service/events/generateVector';
+import { startQueue } from '@/service/utils/tools';
import { setCron } from '@fastgpt/service/common/system/cron';
export const startCron = () => {
@@ -17,7 +16,6 @@ export const setUpdateSystemConfigCron = () => {
export const setTrainingQueueCron = () => {
setCron('*/1 * * * *', () => {
- generateVector();
- generateQA();
+ startQueue();
});
};
diff --git a/projects/app/src/service/core/dataset/data/controller.ts b/projects/app/src/service/core/dataset/data/controller.ts
index 37bc16e41c6..d832ebb4003 100644
--- a/projects/app/src/service/core/dataset/data/controller.ts
+++ b/projects/app/src/service/core/dataset/data/controller.ts
@@ -6,11 +6,9 @@ import {
} from '@fastgpt/global/core/dataset/controller';
import {
insertDatasetDataVector,
- recallFromVectorStore,
- updateDatasetDataVector
+ recallFromVectorStore
} from '@fastgpt/service/common/vectorStore/controller';
import {
- DatasetDataIndexTypeEnum,
DatasetSearchModeEnum,
DatasetSearchModeMap,
SearchScoreTypeEnum
@@ -22,6 +20,7 @@ import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/con
import { getVectorsByText } from '@fastgpt/service/core/ai/embedding';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import {
+ DatasetDataItemType,
DatasetDataSchemaType,
DatasetDataWithCollectionType,
SearchDataResponseItemType
@@ -34,8 +33,9 @@ import type {
PushDatasetDataResponse
} from '@fastgpt/global/core/dataset/api.d';
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
-import { getVectorModel } from '../../ai/model';
-import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
+import { getVectorModel } from '@fastgpt/service/core/ai/model';
+import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
+import { startQueue } from '@/service/utils/tools';
export async function pushDataToTrainingQueue(
props: {
@@ -49,6 +49,8 @@ export async function pushDataToTrainingQueue(
datasetModelList: global.llmModels
});
+ startQueue();
+
return result;
}
@@ -78,7 +80,7 @@ export async function insertData2Dataset({
return Promise.reject("teamId and tmbId can't be the same");
}
- const qaStr = `${q}\n${a}`.trim();
+ const qaStr = getDefaultIndex({ q, a }).text;
// empty indexes check, if empty, create default index
indexes =
@@ -86,10 +88,16 @@ export async function insertData2Dataset({
? indexes.map((index) => ({
...index,
dataId: undefined,
- defaultIndex: indexes?.length === 1 && index.text === qaStr ? true : index.defaultIndex
+ defaultIndex: index.text.trim() === qaStr
}))
: [getDefaultIndex({ q, a })];
+ if (!indexes.find((index) => index.defaultIndex)) {
+ indexes.unshift(getDefaultIndex({ q, a }));
+ }
+
+ indexes = indexes.slice(0, 6);
+
// insert to vector store
const result = await Promise.all(
indexes.map((item) =>
@@ -113,7 +121,7 @@ export async function insertData2Dataset({
a,
fullTextToken: jiebaSplit({ text: qaStr }),
chunkIndex,
- indexes: indexes.map((item, i) => ({
+ indexes: indexes?.map((item, i) => ({
...item,
dataId: result[i].insertId
}))
@@ -128,8 +136,10 @@ export async function insertData2Dataset({
/**
* update data
* 1. compare indexes
- * 2. update pg data
- * 3. update mongo data
+ * 2. insert new pg data
+ * session run:
+ * 3. update mongo data(session run)
+ * 4. delete old pg data
*/
export async function updateData2Dataset({
dataId,
@@ -141,31 +151,30 @@ export async function updateData2Dataset({
if (!Array.isArray(indexes)) {
return Promise.reject('indexes is required');
}
- const qaStr = `${q}\n${a}`.trim();
+ const qaStr = getDefaultIndex({ q, a }).text;
// patch index and update pg
const mongoData = await MongoDatasetData.findById(dataId);
if (!mongoData) return Promise.reject('core.dataset.error.Data not found');
- // make sure have one index
- if (indexes.length === 0) {
- const databaseDefaultIndex = mongoData.indexes.find((index) => index.defaultIndex);
-
- indexes = [
- getDefaultIndex({
- q,
- a,
- dataId: databaseDefaultIndex ? String(databaseDefaultIndex.dataId) : undefined
- })
- ];
+ // remove defaultIndex
+ let formatIndexes = indexes.map((index) => ({
+ ...index,
+ text: index.text.trim(),
+ defaultIndex: index.text.trim() === qaStr
+ }));
+ if (!formatIndexes.find((index) => index.defaultIndex)) {
+ const defaultIndex = mongoData.indexes.find((index) => index.defaultIndex);
+ formatIndexes.unshift(defaultIndex ? defaultIndex : getDefaultIndex({ q, a }));
}
+ formatIndexes = formatIndexes.slice(0, 6);
// patch indexes, create, update, delete
const patchResult: PatchIndexesProps[] = [];
// find database indexes in new Indexes, if have not, delete it
for (const item of mongoData.indexes) {
- const index = indexes.find((index) => index.dataId === item.dataId);
+ const index = formatIndexes.find((index) => index.dataId === item.dataId);
if (!index) {
patchResult.push({
type: 'delete',
@@ -173,35 +182,34 @@ export async function updateData2Dataset({
});
}
}
- for (const item of indexes) {
+ for (const item of formatIndexes) {
const index = mongoData.indexes.find((index) => index.dataId === item.dataId);
// in database, update
if (index) {
- // manual update index
- if (index.text !== item.text) {
- patchResult.push({
- type: 'update',
- index: item
- });
- } else if (index.defaultIndex && index.text !== qaStr) {
- // update default index
+ // default index update
+ if (index.defaultIndex && index.text !== qaStr) {
patchResult.push({
type: 'update',
index: {
- ...item,
- type:
- item.type === DatasetDataIndexTypeEnum.qa && !a
- ? DatasetDataIndexTypeEnum.chunk
- : item.type,
+ //@ts-ignore
+ ...index.toObject(),
text: qaStr
}
});
- } else {
+ continue;
+ }
+ // custom index update
+ if (index.text !== item.text) {
patchResult.push({
- type: 'unChange',
+ type: 'update',
index: item
});
+ continue;
}
+ patchResult.push({
+ type: 'unChange',
+ index: item
+ });
} else {
// not in database, create
patchResult.push({
@@ -215,10 +223,12 @@ export async function updateData2Dataset({
mongoData.updateTime = new Date();
await mongoData.save();
- // update vector
- const result = await Promise.all(
- patchResult.map(async (item) => {
- if (item.type === 'create') {
+ // insert vector
+ const clonePatchResult2Insert: PatchIndexesProps[] = JSON.parse(JSON.stringify(patchResult));
+ const insertResult = await Promise.all(
+ clonePatchResult2Insert.map(async (item) => {
+ // insert new vector and update dateId
+ if (item.type === 'create' || item.type === 'update') {
const result = await insertDatasetDataVector({
query: item.index.text,
model: getVectorModel(model),
@@ -229,50 +239,54 @@ export async function updateData2Dataset({
item.index.dataId = result.insertId;
return result;
}
- if (item.type === 'update' && item.index.dataId) {
- const result = await updateDatasetDataVector({
- teamId: mongoData.teamId,
- datasetId: mongoData.datasetId,
- collectionId: mongoData.collectionId,
- id: item.index.dataId,
- query: item.index.text,
- model: getVectorModel(model)
- });
- item.index.dataId = result.insertId;
-
- return result;
- }
- if (item.type === 'delete' && item.index.dataId) {
- await deleteDatasetDataVector({
- teamId: mongoData.teamId,
- id: item.index.dataId
- });
- return {
- charsLength: 0
- };
- }
return {
charsLength: 0
};
})
);
-
- const charsLength = result.reduce((acc, cur) => acc + cur.charsLength, 0);
- const newIndexes = patchResult.filter((item) => item.type !== 'delete').map((item) => item.index);
-
- // update mongo other data
- mongoData.q = q || mongoData.q;
- mongoData.a = a ?? mongoData.a;
- mongoData.fullTextToken = jiebaSplit({ text: mongoData.q + mongoData.a });
- // @ts-ignore
- mongoData.indexes = newIndexes;
- await mongoData.save();
+ const charsLength = insertResult.reduce((acc, cur) => acc + cur.charsLength, 0);
+ // console.log(clonePatchResult2Insert);
+ await mongoSessionRun(async (session) => {
+ // update mongo
+ const newIndexes = clonePatchResult2Insert
+ .filter((item) => item.type !== 'delete')
+ .map((item) => item.index);
+ // update mongo other data
+ mongoData.q = q || mongoData.q;
+ mongoData.a = a ?? mongoData.a;
+ mongoData.fullTextToken = jiebaSplit({ text: mongoData.q + mongoData.a });
+ // @ts-ignore
+ mongoData.indexes = newIndexes;
+ await mongoData.save({ session });
+
+ // delete vector
+ const deleteIdList = patchResult
+ .filter((item) => item.type === 'delete' || item.type === 'update')
+ .map((item) => item.index.dataId)
+ .filter(Boolean);
+ if (deleteIdList.length > 0) {
+ await deleteDatasetDataVector({
+ teamId: mongoData.teamId,
+ idList: deleteIdList as string[]
+ });
+ }
+ });
return {
charsLength
};
}
+export const deleteDatasetData = async (data: DatasetDataItemType) => {
+ await mongoSessionRun(async (session) => {
+ await MongoDatasetData.findByIdAndDelete(data.id, { session });
+ await deleteDatasetDataVector({
+ teamId: data.teamId,
+ idList: data.indexes.map((item) => item.dataId)
+ });
+ });
+};
+
type SearchDatasetDataProps = {
teamId: string;
model: string;
@@ -371,14 +385,18 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
const formatResult = concatResults
.map((data, index) => {
+ if (!data.collectionId) {
+ console.log('Collection is not found', data);
+ }
+
const result: SearchDataResponseItemType = {
id: String(data._id),
q: data.q,
a: data.a,
chunkIndex: data.chunkIndex,
datasetId: String(data.datasetId),
- collectionId: String(data.collectionId._id),
- sourceName: data.collectionId.name || '',
+ collectionId: String(data.collectionId?._id),
+ sourceName: data.collectionId?.name || '',
sourceId: data.collectionId?.fileId || data.collectionId?.rawLink,
score: [{ type: SearchScoreTypeEnum.embedding, value: data.score, index }]
};
@@ -481,7 +499,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
}))
});
- if (!Array.isArray(results)) {
+ if (results.length === 0) {
usingReRank = false;
return [];
}
diff --git a/projects/app/src/service/core/dataset/data/sql.ts b/projects/app/src/service/core/dataset/data/sql.ts
deleted file mode 100644
index f3542a059a0..00000000000
--- a/projects/app/src/service/core/dataset/data/sql.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export function getLikeSql(searchText?: string) {
- return searchText ? `AND (index ILIKE '%${searchText}%' OR content ILIKE '%${searchText}%')` : '';
-}
diff --git a/projects/app/src/service/events/generateQA.ts b/projects/app/src/service/events/generateQA.ts
index 9af87b4c81f..45754b7f0e9 100644
--- a/projects/app/src/service/events/generateQA.ts
+++ b/projects/app/src/service/events/generateQA.ts
@@ -1,25 +1,22 @@
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
-import { pushQABill } from '@/service/support/wallet/bill/push';
-import { DatasetDataIndexTypeEnum, TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
-import { sendOneInform } from '../support/user/inform/api';
+import { pushQAUsage } from '@/service/support/wallet/usage/push';
+import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
import { getAIApi } from '@fastgpt/service/core/ai/config';
import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d';
import { addLog } from '@fastgpt/service/common/system/log';
import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter';
import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { Prompt_AgentQA } from '@/global/core/prompt/agent';
-import { getErrText } from '@fastgpt/global/common/error/utils';
-import { authTeamBalance } from '../support/permission/auth/bill';
import type { PushDatasetDataChunkProps } from '@fastgpt/global/core/dataset/api.d';
-import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
-import { lockTrainingDataByTeamId } from '@fastgpt/service/core/dataset/training/controller';
import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller';
-import { getLLMModel } from '../core/ai/model';
+import { getLLMModel } from '@fastgpt/service/core/ai/model';
+import { checkInvalidChunkAndLock, checkTeamAiPointsAndLock } from './utils';
+import { countGptMessagesChars } from '@fastgpt/service/core/chat/utils';
const reduceQueue = () => {
global.qaQueueLen = global.qaQueueLen > 0 ? global.qaQueueLen - 1 : 0;
- return global.vectorQueueLen === 0;
+ return global.qaQueueLen === 0;
};
export async function generateQA(): Promise {
@@ -86,26 +83,11 @@ export async function generateQA(): Promise {
reduceQueue();
return generateQA();
}
+ console.log('Start QA Training');
// auth balance
- try {
- await authTeamBalance(data.teamId);
- } catch (error: any) {
- if (error?.statusText === UserErrEnum.balanceNotEnough) {
- // send inform and lock data
- try {
- sendOneInform({
- type: 'system',
- title: '文本训练任务中止',
- content:
- '该团队账号余额不足,文本训练任务中止,重新充值后将会继续。暂停的任务将在 7 天后被删除。',
- tmbId: data.tmbId
- });
- console.log('余额不足,暂停【QA】生成任务');
- lockTrainingDataByTeamId(data.teamId);
- } catch (error) {}
- }
-
+ if (!(await checkTeamAiPointsAndLock(data.teamId, data.tmbId))) {
+ console.log('balance not enough');
reduceQueue();
return generateQA();
}
@@ -137,6 +119,12 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
const qaArr = formatSplitText(answer, text); // 格式化后的QA对
+ addLog.info(`QA Training Finish`, {
+ time: `${(Date.now() - startTime) / 1000}s`,
+ splitLength: qaArr.length,
+ usage: chatResponse.usage
+ });
+
// get vector and insert
const { insertLen } = await pushDataToTrainingQueue({
teamId: data.teamId,
@@ -153,18 +141,12 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
// delete data from training
await MongoDatasetTraining.findByIdAndDelete(data._id);
- addLog.info(`QA Training Finish`, {
- time: `${(Date.now() - startTime) / 1000}s`,
- splitLength: qaArr.length,
- usage: chatResponse.usage
- });
-
// add bill
if (insertLen > 0) {
- pushQABill({
+ pushQAUsage({
teamId: data.teamId,
tmbId: data.tmbId,
- charsLength: `${prompt}${answer}`.length,
+ charsLength: countGptMessagesChars(messages).length,
billId: data.billId,
model
});
@@ -176,32 +158,8 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
generateQA();
} catch (err: any) {
reduceQueue();
- // log
- if (err?.response) {
- addLog.info('openai error: 生成QA错误', {
- status: err.response?.status,
- stateusText: err.response?.statusText,
- data: err.response?.data
- });
- } else {
- console.log(err);
- addLog.error(getErrText(err, '生成 QA 错误'));
- }
- // message error or openai account error
- if (
- err?.message === 'invalid message format' ||
- err.response?.data?.error?.type === 'invalid_request_error' ||
- err?.code === 500
- ) {
- addLog.info('invalid message format', {
- text
- });
- try {
- await MongoDatasetTraining.findByIdAndUpdate(data._id, {
- lockTime: new Date('2998/5/5')
- });
- } catch (error) {}
+ if (await checkInvalidChunkAndLock({ err, data, errText: 'QA模型调用失败' })) {
return generateQA();
}
@@ -230,7 +188,6 @@ function formatSplitText(text: string, rawText: string) {
indexes: [
{
defaultIndex: true,
- type: DatasetDataIndexTypeEnum.qa,
text: `${q}\n${a.trim().replace(/\n\s*/g, '\n')}`
}
]
@@ -248,7 +205,6 @@ function formatSplitText(text: string, rawText: string) {
indexes: [
{
defaultIndex: true,
- type: DatasetDataIndexTypeEnum.chunk,
text: chunk
}
]
diff --git a/projects/app/src/service/events/generateVector.ts b/projects/app/src/service/events/generateVector.ts
index 3a8a64bac9b..6b11322613b 100644
--- a/projects/app/src/service/events/generateVector.ts
+++ b/projects/app/src/service/events/generateVector.ts
@@ -1,13 +1,9 @@
import { insertData2Dataset } from '@/service/core/dataset/data/controller';
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
-import { sendOneInform } from '../support/user/inform/api';
-import { addLog } from '@fastgpt/service/common/system/log';
-import { getErrText } from '@fastgpt/global/common/error/utils';
-import { authTeamBalance } from '@/service/support/permission/auth/bill';
-import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push';
-import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
-import { lockTrainingDataByTeamId } from '@fastgpt/service/core/dataset/training/controller';
+import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
+import { checkInvalidChunkAndLock, checkTeamAiPointsAndLock } from './utils';
+import { delay } from '@fastgpt/global/common/system/utils';
const reduceQueue = () => {
global.vectorQueueLen = global.vectorQueueLen > 0 ? global.vectorQueueLen - 1 : 0;
@@ -19,7 +15,6 @@ const reduceQueue = () => {
export async function generateVector(): Promise {
if (global.vectorQueueLen >= global.systemEnv.vectorMaxProcess) return;
global.vectorQueueLen++;
-
const start = Date.now();
// get training data
@@ -92,24 +87,7 @@ export async function generateVector(): Promise {
}
// auth balance
- try {
- await authTeamBalance(data.teamId);
- } catch (error: any) {
- if (error?.statusText === UserErrEnum.balanceNotEnough) {
- // send inform and lock data
- try {
- sendOneInform({
- type: 'system',
- title: '文本训练任务中止',
- content:
- '该团队账号余额不足,文本训练任务中止,重新充值后将会继续。暂停的任务将在 7 天后被删除。',
- tmbId: data.tmbId
- });
- console.log('余额不足,暂停【向量】生成任务');
- lockTrainingDataByTeamId(data.teamId);
- } catch (error) {}
- }
-
+ if (!(await checkTeamAiPointsAndLock(data.teamId, data.tmbId))) {
reduceQueue();
return generateVector();
}
@@ -124,7 +102,7 @@ export async function generateVector(): Promise {
return;
}
- // insert data to pg
+ // insert to dataset
const { charsLength } = await insertData2Dataset({
teamId: data.teamId,
tmbId: data.tmbId,
@@ -137,8 +115,8 @@ export async function generateVector(): Promise {
model: data.model
});
- // push bill
- pushGenerateVectorBill({
+ // push usage
+ pushGenerateVectorUsage({
teamId: data.teamId,
tmbId: data.tmbId,
charsLength,
@@ -154,34 +132,8 @@ export async function generateVector(): Promise {
console.log(`embedding finished, time: ${Date.now() - start}ms`);
} catch (err: any) {
reduceQueue();
- // log
- if (err?.response) {
- addLog.info('openai error: 生成向量错误', {
- status: err.response?.status,
- stateusText: err.response?.statusText,
- data: err.response?.data
- });
- } else {
- console.log(err);
- addLog.error(getErrText(err, '生成向量错误'));
- }
-
- // message error or openai account error
- if (
- err?.message === 'invalid message format' ||
- err.response?.data?.error?.type === 'invalid_request_error' ||
- err?.code === 500
- ) {
- addLog.info('Lock training data');
- console.log(err?.code);
- console.log(err.response?.data?.error?.type);
- console.log(err?.message);
- try {
- await MongoDatasetTraining.findByIdAndUpdate(data._id, {
- lockTime: new Date('2998/5/5')
- });
- } catch (error) {}
+ if (await checkInvalidChunkAndLock({ err, data, errText: '向量模型调用失败' })) {
return generateVector();
}
diff --git a/projects/app/src/service/events/utils.ts b/projects/app/src/service/events/utils.ts
new file mode 100644
index 00000000000..6953d9003a9
--- /dev/null
+++ b/projects/app/src/service/events/utils.ts
@@ -0,0 +1,69 @@
+import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
+import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
+import { sendOneInform } from '../support/user/inform/api';
+import { lockTrainingDataByTeamId } from '@fastgpt/service/core/dataset/training/controller';
+import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type';
+import { addLog } from '@fastgpt/service/common/system/log';
+import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
+import { getErrText } from '@fastgpt/global/common/error/utils';
+
+export const checkTeamAiPointsAndLock = async (teamId: string, tmbId: string) => {
+ try {
+ await checkTeamAIPoints(teamId);
+ return true;
+ } catch (error: any) {
+ if (error === TeamErrEnum.aiPointsNotEnough) {
+ // send inform and lock data
+ try {
+ sendOneInform({
+ type: 'system',
+ title: '文本训练任务中止',
+ content:
+ '该团队账号AI积分不足,文本训练任务中止,重新充值后将会继续。暂停的任务将在 7 天后被删除。',
+ tmbId: tmbId
+ });
+ console.log('余额不足,暂停【向量】生成任务');
+ lockTrainingDataByTeamId(teamId);
+ } catch (error) {}
+ }
+ return false;
+ }
+};
+
+export const checkInvalidChunkAndLock = async ({
+ err,
+ errText,
+ data
+}: {
+ err: any;
+ errText: string;
+ data: DatasetTrainingSchemaType;
+}) => {
+ if (err?.response) {
+ addLog.info(`openai error: ${errText}`, {
+ status: err.response?.status,
+ stateusText: err.response?.statusText,
+ data: err.response?.data
+ });
+ } else {
+ console.log(err);
+ addLog.error(getErrText(err, errText));
+ }
+
+ if (
+ err?.message === 'invalid message format' ||
+ err?.type === 'invalid_request_error' ||
+ err?.code === 500
+ ) {
+ addLog.info('Lock training data');
+ console.log(err);
+
+ try {
+ await MongoDatasetTraining.findByIdAndUpdate(data._id, {
+ lockTime: new Date('2998/5/5')
+ });
+ } catch (error) {}
+ return true;
+ }
+ return false;
+};
diff --git a/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts b/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts
index e7e350a0b6f..1e5a575a238 100644
--- a/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts
+++ b/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts
@@ -1,17 +1,20 @@
import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt';
-import { ChatContextFilter } from '@fastgpt/service/core/chat/utils';
+import { ChatContextFilter, countMessagesChars } from '@fastgpt/service/core/chat/utils';
import type { moduleDispatchResType, ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { getAIApi } from '@fastgpt/service/core/ai/config';
-import type { ClassifyQuestionAgentItemType } from '@fastgpt/global/core/module/type.d';
+import type {
+ ClassifyQuestionAgentItemType,
+ ModuleDispatchResponse
+} from '@fastgpt/global/core/module/type.d';
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import type { ModuleDispatchProps } from '@fastgpt/global/core/module/type.d';
import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { Prompt_CQJson } from '@/global/core/prompt/agent';
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
-import { ModelTypeEnum, getLLMModel } from '@/service/core/ai/model';
+import { ModelTypeEnum, getLLMModel } from '@fastgpt/service/core/ai/model';
import { getHistories } from '../utils';
-import { formatModelPrice2Store } from '@/service/support/wallet/bill/utils';
+import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
type Props = ModuleDispatchProps<{
[ModuleInputKeyEnum.aiModel]: string;
@@ -20,10 +23,9 @@ type Props = ModuleDispatchProps<{
[ModuleInputKeyEnum.userChatInput]: string;
[ModuleInputKeyEnum.agents]: ClassifyQuestionAgentItemType[];
}>;
-type CQResponse = {
- [ModuleOutputKeyEnum.responseData]: moduleDispatchResType;
+type CQResponse = ModuleDispatchResponse<{
[key: string]: any;
-};
+}>;
const agentFunName = 'classify_question';
@@ -31,6 +33,7 @@ const agentFunName = 'classify_question';
export const dispatchClassifyQuestion = async (props: Props): Promise => {
const {
user,
+ module: { name },
histories,
params: { model, history = 6, agents, userChatInput }
} = props as Props;
@@ -43,7 +46,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise {
+ const { arg, charsLength } = await (async () => {
if (cqModel.toolChoice) {
return toolChoice({
...props,
@@ -60,25 +63,31 @@ export const dispatchClassifyQuestion = async (props: Props): Promise item.key === arg?.type) || agents[agents.length - 1];
- const { total, modelName } = formatModelPrice2Store({
+ const { totalPoints, modelName } = formatModelChars2Points({
model: cqModel.model,
- inputLen: inputTokens,
- outputLen: outputTokens,
- type: ModelTypeEnum.llm
+ charsLength,
+ modelType: ModelTypeEnum.llm
});
return {
[result.key]: true,
[ModuleOutputKeyEnum.responseData]: {
- price: user.openaiAccount?.key ? 0 : total,
+ totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
query: userChatInput,
- inputTokens,
- outputTokens,
+ charsLength,
cqList: agents,
cqResult: result.value,
contextTotalLen: chatHistories.length + 2
- }
+ },
+ [ModuleOutputKeyEnum.moduleDispatchBills]: [
+ {
+ moduleName: name,
+ totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
+ model: modelName,
+ charsLength
+ }
+ ]
};
};
@@ -149,11 +158,13 @@ ${systemPrompt}
const arg = JSON.parse(
response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || ''
);
+ const functionChars =
+ agentFunction.description.length +
+ agentFunction.parameters.properties.type.description.length;
return {
arg,
- inputTokens: response.usage?.prompt_tokens || 0,
- outputTokens: response.usage?.completion_tokens || 0
+ charsLength: countMessagesChars(messages) + functionChars
};
} catch (error) {
console.log(agentFunction.parameters);
@@ -163,8 +174,7 @@ ${systemPrompt}
return {
arg: {},
- inputTokens: 0,
- outputTokens: 0
+ charsLength: 0
};
}
}
@@ -206,8 +216,7 @@ async function completions({
agents.find((item) => answer.includes(item.key) || answer.includes(item.value))?.key || '';
return {
- inputTokens: data.usage?.prompt_tokens || 0,
- outputTokens: data.usage?.completion_tokens || 0,
+ charsLength: countMessagesChars(messages),
arg: { type: id }
};
}
diff --git a/projects/app/src/service/moduleDispatch/agent/extract.ts b/projects/app/src/service/moduleDispatch/agent/extract.ts
index 75cbf55a0fd..53dd21c6a4d 100644
--- a/projects/app/src/service/moduleDispatch/agent/extract.ts
+++ b/projects/app/src/service/moduleDispatch/agent/extract.ts
@@ -1,17 +1,20 @@
import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt';
-import { ChatContextFilter } from '@fastgpt/service/core/chat/utils';
+import { ChatContextFilter, countMessagesChars } from '@fastgpt/service/core/chat/utils';
import type { moduleDispatchResType, ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { getAIApi } from '@fastgpt/service/core/ai/config';
-import type { ContextExtractAgentItemType } from '@fastgpt/global/core/module/type';
+import type {
+ ContextExtractAgentItemType,
+ ModuleDispatchResponse
+} from '@fastgpt/global/core/module/type';
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import type { ModuleDispatchProps } from '@fastgpt/global/core/module/type.d';
import { Prompt_ExtractJson } from '@/global/core/prompt/agent';
import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { getHistories } from '../utils';
-import { ModelTypeEnum, getLLMModel } from '@/service/core/ai/model';
-import { formatModelPrice2Store } from '@/service/support/wallet/bill/utils';
+import { ModelTypeEnum, getLLMModel } from '@fastgpt/service/core/ai/model';
+import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
type Props = ModuleDispatchProps<{
[ModuleInputKeyEnum.history]?: ChatItemType[];
@@ -20,18 +23,18 @@ type Props = ModuleDispatchProps<{
[ModuleInputKeyEnum.description]: string;
[ModuleInputKeyEnum.aiModel]: string;
}>;
-type Response = {
+type Response = ModuleDispatchResponse<{
[ModuleOutputKeyEnum.success]?: boolean;
[ModuleOutputKeyEnum.failed]?: boolean;
[ModuleOutputKeyEnum.contextExtractFields]: string;
- [ModuleOutputKeyEnum.responseData]: moduleDispatchResType;
-};
+}>;
const agentFunName = 'extract_json_data';
export async function dispatchContentExtract(props: Props): Promise {
const {
user,
+ module: { name },
histories,
params: { content, history = 6, model, description, extractKeys }
} = props;
@@ -43,7 +46,7 @@ export async function dispatchContentExtract(props: Props): Promise {
const extractModel = getLLMModel(model);
const chatHistories = getHistories(history, histories);
- const { arg, inputTokens, outputTokens } = await (async () => {
+ const { arg, charsLength } = await (async () => {
if (extractModel.toolChoice) {
return toolChoice({
...props,
@@ -80,11 +83,10 @@ export async function dispatchContentExtract(props: Props): Promise {
}
}
- const { total, modelName } = formatModelPrice2Store({
+ const { totalPoints, modelName } = formatModelChars2Points({
model: extractModel.model,
- inputLen: inputTokens,
- outputLen: outputTokens,
- type: ModelTypeEnum.llm
+ charsLength,
+ modelType: ModelTypeEnum.llm
});
return {
@@ -93,15 +95,22 @@ export async function dispatchContentExtract(props: Props): Promise {
[ModuleOutputKeyEnum.contextExtractFields]: JSON.stringify(arg),
...arg,
[ModuleOutputKeyEnum.responseData]: {
- price: user.openaiAccount?.key ? 0 : total,
+ totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
query: content,
- inputTokens,
- outputTokens,
+ charsLength,
extractDescription: description,
extractResult: arg,
contextTotalLen: chatHistories.length + 2
- }
+ },
+ [ModuleOutputKeyEnum.moduleDispatchBills]: [
+ {
+ moduleName: name,
+ totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
+ model: modelName,
+ charsLength
+ }
+ ]
};
}
@@ -193,10 +202,12 @@ ${description || '根据用户要求获取适当的 JSON 字符串。'}
}
})();
+ const functionChars =
+ description.length + extractKeys.reduce((sum, item) => sum + item.desc.length, 0);
+
return {
rawResponse: response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || '',
- inputTokens: response.usage?.prompt_tokens || 0,
- outputTokens: response.usage?.completion_tokens || 0,
+ charsLength: countMessagesChars(messages) + functionChars,
arg
};
}
@@ -238,8 +249,6 @@ Human: ${content}`
stream: false
});
const answer = data.choices?.[0].message?.content || '';
- const inputTokens = data.usage?.prompt_tokens || 0;
- const outputTokens = data.usage?.completion_tokens || 0;
// parse response
const start = answer.indexOf('{');
@@ -248,8 +257,7 @@ Human: ${content}`
if (start === -1 || end === -1)
return {
rawResponse: answer,
- inputTokens,
- outputTokens,
+ charsLength: countMessagesChars(messages),
arg: {}
};
@@ -261,15 +269,14 @@ Human: ${content}`
try {
return {
rawResponse: answer,
- inputTokens,
- outputTokens,
+ charsLength: countMessagesChars(messages),
+
arg: JSON.parse(jsonStr) as Record
};
} catch (error) {
return {
rawResponse: answer,
- inputTokens,
- outputTokens,
+ charsLength: countMessagesChars(messages),
arg: {}
};
}
diff --git a/projects/app/src/service/moduleDispatch/chat/oneapi.ts b/projects/app/src/service/moduleDispatch/chat/oneapi.ts
index b1c6512e16f..6f049bc5ce4 100644
--- a/projects/app/src/service/moduleDispatch/chat/oneapi.ts
+++ b/projects/app/src/service/moduleDispatch/chat/oneapi.ts
@@ -1,16 +1,16 @@
import type { NextApiResponse } from 'next';
-import { ChatContextFilter } from '@fastgpt/service/core/chat/utils';
+import { ChatContextFilter, countMessagesChars } from '@fastgpt/service/core/chat/utils';
import type { moduleDispatchResType, ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant';
import { textAdaptGptResponse } from '@/utils/adapt';
import { getAIApi } from '@fastgpt/service/core/ai/config';
import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d';
-import { formatModelPrice2Store } from '@/service/support/wallet/bill/utils';
+import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { postTextCensor } from '@/service/common/censor';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constant';
-import type { ModuleItemType } from '@fastgpt/global/core/module/type.d';
+import type { ModuleDispatchResponse, ModuleItemType } from '@fastgpt/global/core/module/type.d';
import { countMessagesTokens, sliceMessagesTB } from '@fastgpt/global/common/string/tiktoken';
import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt';
import { Prompt_QuotePromptList, Prompt_QuoteTemplateList } from '@/global/core/prompt/AIChat';
@@ -18,7 +18,7 @@ import type { AIChatModuleProps } from '@fastgpt/global/core/module/node/type.d'
import { replaceVariable } from '@fastgpt/global/common/string/tools';
import type { ModuleDispatchProps } from '@fastgpt/global/core/module/type.d';
import { responseWrite, responseWriteController } from '@fastgpt/service/common/response';
-import { getLLMModel, ModelTypeEnum } from '@/service/core/ai/model';
+import { getLLMModel, ModelTypeEnum } from '@fastgpt/service/core/ai/model';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { formatStr2ChatContent } from '@fastgpt/service/core/chat/utils';
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
@@ -32,11 +32,10 @@ export type ChatProps = ModuleDispatchProps<
[ModuleInputKeyEnum.aiChatDatasetQuote]?: SearchDataResponseItemType[];
}
>;
-export type ChatResponse = {
+export type ChatResponse = ModuleDispatchResponse<{
[ModuleOutputKeyEnum.answerText]: string;
- [ModuleOutputKeyEnum.responseData]: moduleDispatchResType;
[ModuleOutputKeyEnum.history]: ChatItemType[];
-};
+}>;
/* request openai chat */
export const dispatchChatCompletion = async (props: ChatProps): Promise => {
@@ -46,7 +45,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise {
+ const { answerText, completeMessages } = await (async () => {
if (stream) {
// sse response
const { answer } = await streamResponse({
@@ -172,17 +171,6 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise = {};
- filterQuoteQA.forEach((item) => {
- if (sortQuoteQAMap[item.collectionId]) {
- sortQuoteQAMap[item.collectionId].push(item);
- } else {
- sortQuoteQAMap[item.collectionId] = [item];
- }
- });
- const sortQuoteQAList = Object.values(sortQuoteQAMap);
-
- sortQuoteQAList.forEach((qaList) => {
- qaList.sort((a, b) => a.chunkIndex - b.chunkIndex);
- });
-
- const flatQuoteList = sortQuoteQAList.flat();
-
const quoteText =
- flatQuoteList.length > 0
- ? `${flatQuoteList.map((item, index) => getValue(item, index)).join('\n')}`
+ filterQuoteQA.length > 0
+ ? `${filterQuoteQA.map((item, index) => getValue(item, index).trim()).join('\n------\n')}`
: '';
return {
- filterQuoteQA: flatQuoteList,
+ filterQuoteQA: filterQuoteQA,
quoteText
};
}
diff --git a/projects/app/src/service/moduleDispatch/dataset/search.ts b/projects/app/src/service/moduleDispatch/dataset/search.ts
index 54ba4feb087..3f37e1e2b14 100644
--- a/projects/app/src/service/moduleDispatch/dataset/search.ts
+++ b/projects/app/src/service/moduleDispatch/dataset/search.ts
@@ -1,15 +1,19 @@
import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d';
-import { formatModelPrice2Store } from '@/service/support/wallet/bill/utils';
+import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
-import type { ModuleDispatchProps } from '@fastgpt/global/core/module/type.d';
-import { ModelTypeEnum, getLLMModel, getVectorModel } from '@/service/core/ai/model';
+import type {
+ ModuleDispatchProps,
+ ModuleDispatchResponse
+} from '@fastgpt/global/core/module/type.d';
+import { ModelTypeEnum, getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
import { searchDatasetData } from '@/service/core/dataset/data/controller';
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
-import { queryExtension } from '@fastgpt/service/core/ai/functions/queryExtension';
import { getHistories } from '../utils';
import { datasetSearchQueryExtension } from '@fastgpt/service/core/dataset/search/utils';
+import { ChatModuleBillType } from '@fastgpt/global/support/wallet/bill/type';
+import { checkTeamReRankPermission } from '@fastgpt/service/support/permission/teamLimit';
type DatasetSearchProps = ModuleDispatchProps<{
[ModuleInputKeyEnum.datasetSelectList]: SelectedDatasetType;
@@ -22,12 +26,11 @@ type DatasetSearchProps = ModuleDispatchProps<{
[ModuleInputKeyEnum.datasetSearchExtensionModel]: string;
[ModuleInputKeyEnum.datasetSearchExtensionBg]: string;
}>;
-export type DatasetSearchResponse = {
- [ModuleOutputKeyEnum.responseData]: moduleDispatchResType;
+export type DatasetSearchResponse = ModuleDispatchResponse<{
[ModuleOutputKeyEnum.datasetIsEmpty]?: boolean;
[ModuleOutputKeyEnum.datasetUnEmpty]?: boolean;
[ModuleOutputKeyEnum.datasetQuoteQA]: SearchDataResponseItemType[];
-};
+}>;
export async function dispatchDatasetSearch(
props: DatasetSearchProps
@@ -35,6 +38,7 @@ export async function dispatchDatasetSearch(
const {
teamId,
histories,
+ module,
params: {
datasets = [],
similarity,
@@ -73,6 +77,8 @@ export async function dispatchDatasetSearch(
histories: getHistories(6, histories)
});
+ // console.log(concatQueries, rewriteQuery, aiExtensionResult);
+
// get vector
const vectorModel = getVectorModel(datasets[0]?.vectorModel?.model);
@@ -91,18 +97,18 @@ export async function dispatchDatasetSearch(
limit,
datasetIds: datasets.map((item) => item.datasetId),
searchMode,
- usingReRank
+ usingReRank: usingReRank && (await checkTeamReRankPermission(teamId))
});
// count bill results
// vector
- const { total, modelName } = formatModelPrice2Store({
+ const { totalPoints, modelName } = formatModelChars2Points({
model: vectorModel.model,
- inputLen: charsLength,
- type: ModelTypeEnum.vector
+ charsLength,
+ modelType: ModelTypeEnum.vector
});
- const responseData: moduleDispatchResType & { price: number } = {
- price: total,
+ const responseData: moduleDispatchResType & { totalPoints: number } = {
+ totalPoints,
query: concatQueries.join('\n'),
model: modelName,
charsLength,
@@ -111,28 +117,42 @@ export async function dispatchDatasetSearch(
searchMode,
searchUsingReRank: searchUsingReRank
};
+ const moduleDispatchBills: ChatModuleBillType[] = [
+ {
+ totalPoints,
+ moduleName: module.name,
+ model: modelName,
+ charsLength
+ }
+ ];
if (aiExtensionResult) {
- const { total, modelName } = formatModelPrice2Store({
+ const { totalPoints, modelName } = formatModelChars2Points({
model: aiExtensionResult.model,
- inputLen: aiExtensionResult.inputTokens,
- outputLen: aiExtensionResult.outputTokens,
- type: ModelTypeEnum.llm
+ charsLength: aiExtensionResult.charsLength,
+ modelType: ModelTypeEnum.llm
});
- responseData.price += total;
- responseData.inputTokens = aiExtensionResult.inputTokens;
- responseData.outputTokens = aiExtensionResult.outputTokens;
+ responseData.totalPoints += totalPoints;
+ responseData.charsLength = aiExtensionResult.charsLength;
responseData.extensionModel = modelName;
responseData.extensionResult =
aiExtensionResult.extensionQueries?.join('\n') ||
JSON.stringify(aiExtensionResult.extensionQueries);
+
+ moduleDispatchBills.push({
+ totalPoints,
+ moduleName: 'core.module.template.Query extension',
+ model: modelName,
+ charsLength: aiExtensionResult.charsLength
+ });
}
return {
isEmpty: searchRes.length === 0 ? true : undefined,
unEmpty: searchRes.length > 0 ? true : undefined,
quoteQA: searchRes,
- responseData
+ responseData,
+ moduleDispatchBills
};
}
diff --git a/projects/app/src/service/moduleDispatch/index.ts b/projects/app/src/service/moduleDispatch/index.ts
index 5c79497a5a3..1acdbe24b5a 100644
--- a/projects/app/src/service/moduleDispatch/index.ts
+++ b/projects/app/src/service/moduleDispatch/index.ts
@@ -23,11 +23,12 @@ import { dispatchContentExtract } from './agent/extract';
import { dispatchHttpRequest } from './tools/http';
import { dispatchHttp468Request } from './tools/http468';
import { dispatchAppRequest } from './tools/runApp';
-import { dispatchCFR } from './tools/cfr';
+import { dispatchQueryExtension } from './tools/queryExternsion';
import { dispatchRunPlugin } from './plugin/run';
import { dispatchPluginInput } from './plugin/runInput';
import { dispatchPluginOutput } from './plugin/runOutput';
import { valueTypeFormat } from './utils';
+import { ChatModuleBillType } from '@fastgpt/global/support/wallet/bill/type';
const callbackMap: Record<`${FlowNodeTypeEnum}`, Function> = {
[FlowNodeTypeEnum.historyNode]: dispatchHistory,
@@ -44,7 +45,7 @@ const callbackMap: Record<`${FlowNodeTypeEnum}`, Function> = {
[FlowNodeTypeEnum.pluginModule]: dispatchRunPlugin,
[FlowNodeTypeEnum.pluginInput]: dispatchPluginInput,
[FlowNodeTypeEnum.pluginOutput]: dispatchPluginOutput,
- [FlowNodeTypeEnum.cfr]: dispatchCFR,
+ [FlowNodeTypeEnum.queryExtension]: dispatchQueryExtension,
// none
[FlowNodeTypeEnum.userGuide]: () => Promise.resolve()
@@ -82,16 +83,19 @@ export async function dispatchModules({
// let storeData: Record = {}; // after module used
let chatResponse: ChatHistoryItemResType[] = []; // response request and save to database
let chatAnswerText = ''; // AI answer
+ let chatModuleBills: ChatModuleBillType[] = [];
let runningTime = Date.now();
function pushStore(
{ inputs = [] }: RunningModuleItemType,
{
answerText = '',
- responseData
+ responseData,
+ moduleDispatchBills
}: {
answerText?: string;
responseData?: ChatHistoryItemResType | ChatHistoryItemResType[];
+ moduleDispatchBills?: ChatModuleBillType[];
}
) {
const time = Date.now();
@@ -105,6 +109,9 @@ export async function dispatchModules({
});
}
}
+ if (moduleDispatchBills) {
+ chatModuleBills = chatModuleBills.concat(moduleDispatchBills);
+ }
runningTime = time;
const isResponseAnswerText =
@@ -158,6 +165,7 @@ export async function dispatchModules({
const filterModules = nextRunModules.filter((module) => {
if (set.has(module.moduleId)) return false;
set.add(module.moduleId);
+ ``;
return true;
});
@@ -199,8 +207,7 @@ export async function dispatchModules({
user,
stream,
detail,
- outputs: module.outputs,
- inputs: module.inputs,
+ module,
params
};
@@ -237,10 +244,11 @@ export async function dispatchModules({
? params[ModuleOutputKeyEnum.userChatInput]
: undefined,
...dispatchRes,
- [ModuleOutputKeyEnum.responseData]: formatResponseData
+ [ModuleOutputKeyEnum.responseData]: formatResponseData,
+ [ModuleOutputKeyEnum.moduleDispatchBills]:
+ dispatchRes[ModuleOutputKeyEnum.moduleDispatchBills]
});
}
-
// start process width initInput
const initModules = runningModules.filter((item) => initRunningModuleType[item.flowType]);
@@ -266,7 +274,8 @@ export async function dispatchModules({
return {
[ModuleOutputKeyEnum.answerText]: chatAnswerText,
- [ModuleOutputKeyEnum.responseData]: chatResponse
+ [ModuleOutputKeyEnum.responseData]: chatResponse,
+ [ModuleOutputKeyEnum.moduleDispatchBills]: chatModuleBills
};
}
diff --git a/projects/app/src/service/moduleDispatch/plugin/run.ts b/projects/app/src/service/moduleDispatch/plugin/run.ts
index 3e192aa685b..244c2589800 100644
--- a/projects/app/src/service/moduleDispatch/plugin/run.ts
+++ b/projects/app/src/service/moduleDispatch/plugin/run.ts
@@ -1,4 +1,7 @@
-import type { ModuleDispatchProps } from '@fastgpt/global/core/module/type.d';
+import type {
+ ModuleDispatchProps,
+ ModuleDispatchResponse
+} from '@fastgpt/global/core/module/type.d';
import { dispatchModules } from '../index';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import {
@@ -6,7 +9,6 @@ import {
ModuleInputKeyEnum,
ModuleOutputKeyEnum
} from '@fastgpt/global/core/module/constants';
-import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d';
import { getPluginRuntimeById } from '@fastgpt/service/core/plugin/controller';
import { authPluginCanUse } from '@fastgpt/service/support/permission/auth/plugin';
@@ -14,10 +16,9 @@ type RunPluginProps = ModuleDispatchProps<{
[ModuleInputKeyEnum.pluginId]: string;
[key: string]: any;
}>;
-type RunPluginResponse = {
+type RunPluginResponse = ModuleDispatchResponse<{
[ModuleOutputKeyEnum.answerText]: string;
- [ModuleOutputKeyEnum.responseData]?: moduleDispatchResType;
-};
+}>;
export const dispatchRunPlugin = async (props: RunPluginProps): Promise => {
const {
@@ -58,7 +59,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise ({
...module,
@@ -76,9 +77,9 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise sum + (item.price || 0), 0),
+ totalPoints: responseData.reduce((sum, item) => sum + (item.totalPoints || 0), 0),
runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0),
pluginOutput: output?.pluginOutput,
pluginDetail:
@@ -89,6 +90,14 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise sum + (item.totalPoints || 0), 0),
+ model: plugin.name,
+ charsLength: 0
+ }
+ ],
...(output ? output.pluginOutput : {})
};
};
diff --git a/projects/app/src/service/moduleDispatch/plugin/runOutput.ts b/projects/app/src/service/moduleDispatch/plugin/runOutput.ts
index 492f949be84..04cc9bd9383 100644
--- a/projects/app/src/service/moduleDispatch/plugin/runOutput.ts
+++ b/projects/app/src/service/moduleDispatch/plugin/runOutput.ts
@@ -14,7 +14,7 @@ export const dispatchPluginOutput = (props: PluginOutputProps): PluginOutputResp
return {
responseData: {
- price: 0,
+ totalPoints: 0,
pluginOutput: params
}
};
diff --git a/projects/app/src/service/moduleDispatch/tools/cfr.ts b/projects/app/src/service/moduleDispatch/tools/cfr.ts
deleted file mode 100644
index 0aa6f0b5d16..00000000000
--- a/projects/app/src/service/moduleDispatch/tools/cfr.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import type { ChatItemType, moduleDispatchResType } from '@fastgpt/global/core/chat/type.d';
-import type { ModuleDispatchProps } from '@fastgpt/global/core/module/type.d';
-import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
-import { ModelTypeEnum, getLLMModel } from '@/service/core/ai/model';
-import { formatModelPrice2Store } from '@/service/support/wallet/bill/utils';
-import { queryCfr } from '@fastgpt/service/core/ai/functions/cfr';
-import { getHistories } from '../utils';
-
-type Props = ModuleDispatchProps<{
- [ModuleInputKeyEnum.aiModel]: string;
- [ModuleInputKeyEnum.aiSystemPrompt]?: string;
- [ModuleInputKeyEnum.history]?: ChatItemType[] | number;
- [ModuleInputKeyEnum.userChatInput]: string;
-}>;
-type Response = {
- [ModuleOutputKeyEnum.text]: string;
- [ModuleOutputKeyEnum.responseData]?: moduleDispatchResType;
-};
-
-export const dispatchCFR = async ({
- histories,
- params: { model, systemPrompt, history, userChatInput }
-}: Props): Promise => {
- if (!userChatInput) {
- return Promise.reject('Question is empty');
- }
-
- // none
- // first chat and no system prompt
- if (systemPrompt === 'none' || (histories.length === 0 && !systemPrompt)) {
- return {
- [ModuleOutputKeyEnum.text]: userChatInput
- };
- }
-
- const cfrModel = getLLMModel(model);
- const chatHistories = getHistories(history, histories);
-
- const { cfrQuery, inputTokens, outputTokens } = await queryCfr({
- chatBg: systemPrompt,
- query: userChatInput,
- histories: chatHistories,
- model: cfrModel.model
- });
-
- const { total, modelName } = formatModelPrice2Store({
- model: cfrModel.model,
- inputLen: inputTokens,
- outputLen: outputTokens,
- type: ModelTypeEnum.llm
- });
-
- return {
- [ModuleOutputKeyEnum.responseData]: {
- price: total,
- model: modelName,
- inputTokens,
- outputTokens,
- query: userChatInput,
- textOutput: cfrQuery
- },
- [ModuleOutputKeyEnum.text]: cfrQuery
- };
-};
diff --git a/projects/app/src/service/moduleDispatch/tools/http.ts b/projects/app/src/service/moduleDispatch/tools/http.ts
index bedf7486332..a6ff7076e6c 100644
--- a/projects/app/src/service/moduleDispatch/tools/http.ts
+++ b/projects/app/src/service/moduleDispatch/tools/http.ts
@@ -1,5 +1,8 @@
import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d';
-import type { ModuleDispatchProps } from '@fastgpt/global/core/module/type.d';
+import type {
+ ModuleDispatchProps,
+ ModuleDispatchResponse
+} from '@fastgpt/global/core/module/type.d';
import {
DYNAMIC_INPUT_KEY,
ModuleInputKeyEnum,
@@ -16,11 +19,10 @@ type HttpRequestProps = ModuleDispatchProps<{
[ModuleInputKeyEnum.httpHeaders]: string;
[key: string]: any;
}>;
-type HttpResponse = {
+type HttpResponse = ModuleDispatchResponse<{
[ModuleOutputKeyEnum.failed]?: boolean;
- [ModuleOutputKeyEnum.responseData]: moduleDispatchResType;
[key: string]: any;
-};
+}>;
const flatDynamicParams = (params: Record) => {
const dynamicParams = params[DYNAMIC_INPUT_KEY];
@@ -38,7 +40,7 @@ export const dispatchHttpRequest = async (props: HttpRequestProps): Promise;
[key: string]: any;
}>;
-type HttpResponse = {
+type HttpResponse = ModuleDispatchResponse<{
[ModuleOutputKeyEnum.failed]?: boolean;
- [ModuleOutputKeyEnum.responseData]: moduleDispatchResType;
[key: string]: any;
-};
+}>;
const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
@@ -38,7 +39,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise 0 ? params : undefined,
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
headers: Object.keys(headers).length > 0 ? headers : undefined,
@@ -131,8 +132,8 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise 0 ? params : undefined,
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
headers: Object.keys(headers).length > 0 ? headers : undefined,
diff --git a/projects/app/src/service/moduleDispatch/tools/queryExternsion.ts b/projects/app/src/service/moduleDispatch/tools/queryExternsion.ts
new file mode 100644
index 00000000000..469f712bb72
--- /dev/null
+++ b/projects/app/src/service/moduleDispatch/tools/queryExternsion.ts
@@ -0,0 +1,77 @@
+import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
+import type {
+ ModuleDispatchProps,
+ ModuleDispatchResponse
+} from '@fastgpt/global/core/module/type.d';
+import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
+import { ModelTypeEnum, getLLMModel } from '@fastgpt/service/core/ai/model';
+import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
+import { queryExtension } from '@fastgpt/service/core/ai/functions/queryExtension';
+import { getHistories } from '../utils';
+import { hashStr } from '@fastgpt/global/common/string/tools';
+
+type Props = ModuleDispatchProps<{
+ [ModuleInputKeyEnum.aiModel]: string;
+ [ModuleInputKeyEnum.aiSystemPrompt]?: string;
+ [ModuleInputKeyEnum.history]?: ChatItemType[] | number;
+ [ModuleInputKeyEnum.userChatInput]: string;
+}>;
+type Response = ModuleDispatchResponse<{
+ [ModuleOutputKeyEnum.text]: string;
+}>;
+
+export const dispatchQueryExtension = async ({
+ histories,
+ module,
+ params: { model, systemPrompt, history, userChatInput }
+}: Props): Promise => {
+ if (!userChatInput) {
+ return Promise.reject('Question is empty');
+ }
+
+ const queryExtensionModel = getLLMModel(model);
+ const chatHistories = getHistories(history, histories);
+
+ const { extensionQueries, charsLength } = await queryExtension({
+ chatBg: systemPrompt,
+ query: userChatInput,
+ histories: chatHistories,
+ model: queryExtensionModel.model
+ });
+
+ extensionQueries.unshift(userChatInput);
+
+ const { totalPoints, modelName } = formatModelChars2Points({
+ model: queryExtensionModel.model,
+ charsLength,
+ modelType: ModelTypeEnum.llm
+ });
+
+ const set = new Set();
+ const filterSameQueries = extensionQueries.filter((item) => {
+ // 删除所有的标点符号与空格等,只对文本进行比较
+ const str = hashStr(item.replace(/[^\p{L}\p{N}]/gu, ''));
+ if (set.has(str)) return false;
+ set.add(str);
+ return true;
+ });
+
+ return {
+ [ModuleOutputKeyEnum.responseData]: {
+ totalPoints,
+ model: modelName,
+ charsLength,
+ query: userChatInput,
+ textOutput: JSON.stringify(filterSameQueries)
+ },
+ [ModuleOutputKeyEnum.moduleDispatchBills]: [
+ {
+ moduleName: module.name,
+ totalPoints,
+ model: modelName,
+ charsLength
+ }
+ ],
+ [ModuleOutputKeyEnum.text]: JSON.stringify(filterSameQueries)
+ };
+};
diff --git a/projects/app/src/service/moduleDispatch/tools/runApp.ts b/projects/app/src/service/moduleDispatch/tools/runApp.ts
index a8db9fe3164..2e60fb4d820 100644
--- a/projects/app/src/service/moduleDispatch/tools/runApp.ts
+++ b/projects/app/src/service/moduleDispatch/tools/runApp.ts
@@ -1,5 +1,8 @@
import type { moduleDispatchResType, ChatItemType } from '@fastgpt/global/core/chat/type.d';
-import type { ModuleDispatchProps } from '@fastgpt/global/core/module/type.d';
+import type {
+ ModuleDispatchProps,
+ ModuleDispatchResponse
+} from '@fastgpt/global/core/module/type.d';
import { SelectAppItemType } from '@fastgpt/global/core/module/type';
import { dispatchModules } from '../index';
import { MongoApp } from '@fastgpt/service/core/app/schema';
@@ -15,21 +18,21 @@ type Props = ModuleDispatchProps<{
[ModuleInputKeyEnum.history]?: ChatItemType[] | number;
app: SelectAppItemType;
}>;
-type Response = {
- [ModuleOutputKeyEnum.responseData]: moduleDispatchResType[];
+type Response = ModuleDispatchResponse<{
[ModuleOutputKeyEnum.answerText]: string;
[ModuleOutputKeyEnum.history]: ChatItemType[];
-};
+}>;
export const dispatchAppRequest = async (props: Props): Promise => {
const {
res,
- user,
+ teamId,
stream,
detail,
histories,
params: { userChatInput, history, app }
} = props;
+ let start = Date.now();
if (!userChatInput) {
return Promise.reject('Input is empty');
@@ -37,7 +40,7 @@ export const dispatchAppRequest = async (props: Props): Promise => {
const appData = await MongoApp.findOne({
_id: app.id,
- teamId: user.team.teamId
+ teamId
});
if (!appData) {
@@ -56,7 +59,7 @@ export const dispatchAppRequest = async (props: Props): Promise => {
const chatHistories = getHistories(history, histories);
- const { responseData, answerText } = await dispatchModules({
+ const { responseData, moduleDispatchBills, answerText } = await dispatchModules({
...props,
appId: app.id,
modules: appData.modules,
@@ -78,7 +81,18 @@ export const dispatchAppRequest = async (props: Props): Promise => {
]);
return {
- responseData,
+ [ModuleOutputKeyEnum.responseData]: {
+ moduleLogo: appData.avatar,
+ query: userChatInput,
+ textOutput: answerText,
+ totalPoints: responseData.reduce((sum, item) => sum + (item.totalPoints || 0), 0)
+ },
+ [ModuleOutputKeyEnum.moduleDispatchBills]: [
+ {
+ moduleName: appData.name,
+ totalPoints: responseData.reduce((sum, item) => sum + (item.totalPoints || 0), 0)
+ }
+ ],
answerText: answerText,
history: completeMessages
};
diff --git a/projects/app/src/service/mongo.ts b/projects/app/src/service/mongo.ts
index 1500015b923..7378f713abd 100644
--- a/projects/app/src/service/mongo.ts
+++ b/projects/app/src/service/mongo.ts
@@ -1,5 +1,5 @@
import { startQueue } from './utils/tools';
-import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
+import { PRICE_SCALE } from '@fastgpt/global/support/wallet/constants';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { connectMongo } from '@fastgpt/service/common/mongo/init';
import { hashStr } from '@fastgpt/global/common/string/tools';
diff --git a/projects/app/src/service/support/permission/auth/bill.ts b/projects/app/src/service/support/permission/auth/bill.ts
deleted file mode 100644
index 3b9d8108568..00000000000
--- a/projects/app/src/service/support/permission/auth/bill.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { GET } from '@fastgpt/service/common/api/plusRequest';
-import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
-
-export const authTeamBalance = async (teamId: string) => {
- if (FastGPTProUrl) {
- return GET('/support/permission/authBalance', { teamId });
- }
- return true;
-};
diff --git a/projects/app/src/service/support/permission/auth/chat.ts b/projects/app/src/service/support/permission/auth/chat.ts
index 6e68dfa9d2d..199b6457ea3 100644
--- a/projects/app/src/service/support/permission/auth/chat.ts
+++ b/projects/app/src/service/support/permission/auth/chat.ts
@@ -6,7 +6,6 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
-
/*
outLink: Must be the owner
token: team owner and chat owner have all permissions
@@ -15,12 +14,14 @@ export async function autChatCrud({
appId,
chatId,
shareId,
+ shareTeamId,
outLinkUid,
per = 'owner',
...props
}: AuthModeType & {
appId: string;
chatId?: string;
+ shareTeamId?: string;
shareId?: string;
outLinkUid?: string;
}): Promise<{
@@ -28,7 +29,7 @@ export async function autChatCrud({
isOutLink: boolean;
uid?: string;
}> {
- const isOutLink = Boolean(shareId && outLinkUid);
+ const isOutLink = Boolean((shareId || shareTeamId) && outLinkUid);
if (!chatId) return { isOutLink, uid: outLinkUid };
const chat = await MongoChat.findOne({ appId, chatId }).lean();
@@ -46,6 +47,11 @@ export async function autChatCrud({
}
return Promise.reject(ChatErrEnum.unAuthChat);
}
+ if (shareTeamId && outLinkUid) {
+ if (chat.teamId == shareTeamId && chat.outLinkUid === outLinkUid) {
+ return { uid: outLinkUid };
+ }
+ }
// req auth
const { teamId, tmbId, role } = await authUserRole(props);
diff --git a/projects/app/src/service/support/permission/auth/dataset.ts b/projects/app/src/service/support/permission/auth/dataset.ts
index f7c0491c669..6aec479e027 100644
--- a/projects/app/src/service/support/permission/auth/dataset.ts
+++ b/projects/app/src/service/support/permission/auth/dataset.ts
@@ -24,6 +24,7 @@ export async function authDatasetData({
const data: DatasetDataItemType = {
id: String(datasetData._id),
+ teamId: datasetData.teamId,
q: datasetData.q,
a: datasetData.a,
chunkIndex: datasetData.chunkIndex,
diff --git a/projects/app/src/service/support/permission/auth/outLink.ts b/projects/app/src/service/support/permission/auth/outLink.ts
index 5a32e4ea4fb..bd921159a39 100644
--- a/projects/app/src/service/support/permission/auth/outLink.ts
+++ b/projects/app/src/service/support/permission/auth/outLink.ts
@@ -6,7 +6,7 @@ import type {
AuthOutLinkResponse
} from '@fastgpt/global/support/outLink/api.d';
import { authOutLinkValid } from '@fastgpt/service/support/permission/auth/outLink';
-import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
+import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
@@ -58,13 +58,15 @@ export async function authOutLinkChatStart({
// get outLink and app
const { shareChat, appId } = await authOutLinkValid({ shareId });
- // check balance and chat limit
- const [user, { uid }] = await Promise.all([
- getUserAndAuthBalance({ tmbId: shareChat.tmbId, minBalance: 0 }),
+ // check ai points and chat limit
+ const [{ user }, { uid }] = await Promise.all([
+ getUserChatInfoAndAuthTeamPoints(shareChat.tmbId),
authOutLinkChatLimit({ outLink: shareChat, ip, outLinkUid, question })
]);
return {
+ teamId: shareChat.teamId,
+ tmbId: shareChat.tmbId,
authType: AuthUserTypeEnum.token,
responseDetail: shareChat.responseDetail,
user,
diff --git a/projects/app/src/service/support/permission/auth/team.ts b/projects/app/src/service/support/permission/auth/team.ts
new file mode 100644
index 00000000000..7abd366f89f
--- /dev/null
+++ b/projects/app/src/service/support/permission/auth/team.ts
@@ -0,0 +1,43 @@
+import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
+import { TeamMemberWithUserSchema } from '@fastgpt/global/support/user/team/type';
+import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
+import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
+import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
+import axios from 'axios';
+
+export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
+ const tmb = (await MongoTeamMember.findById(tmbId, 'teamId userId').populate(
+ 'userId',
+ 'timezone openaiAccount'
+ )) as TeamMemberWithUserSchema;
+ if (!tmb) return Promise.reject(UserErrEnum.unAuthUser);
+
+ await checkTeamAIPoints(tmb.teamId);
+
+ return {
+ user: tmb.userId
+ };
+}
+
+type UserInfoType = {
+ data: {
+ uid: string;
+ tags: string[];
+ };
+};
+
+export async function getShareTeamUid(shareTeamId: string, authToken: string) {
+ try {
+ const teamInfo = await MongoTeam.findById(shareTeamId);
+ const tagsUrl = teamInfo?.tagsUrl;
+ const { data: userInfo } = await axios.post(tagsUrl + `/getUserInfo`, { autoken: authToken });
+
+ const uid = userInfo?.data?.uid;
+ if (uid) {
+ throw new Error('uid null');
+ }
+ return uid;
+ } catch (err) {
+ return '';
+ }
+}
diff --git a/projects/app/src/service/support/permission/auth/teamChat.ts b/projects/app/src/service/support/permission/auth/teamChat.ts
new file mode 100644
index 00000000000..0fcdb6370cf
--- /dev/null
+++ b/projects/app/src/service/support/permission/auth/teamChat.ts
@@ -0,0 +1,36 @@
+import { POST } from '@fastgpt/service/common/api/plusRequest';
+import type { AuthOutLinkChatProps } from '@fastgpt/global/support/outLink/api.d';
+import type { chatAppListSchema } from '@fastgpt/global/core/chat/type.d';
+import { getUserChatInfoAndAuthTeamPoints } from './team';
+import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
+import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
+
+export function authChatTeamInfo(data: { shareTeamId: string; authToken: string }) {
+ return POST('/core/chat/init', data);
+}
+
+export async function authTeamShareChatStart({
+ teamId,
+ ip,
+ outLinkUid,
+ question
+}: AuthOutLinkChatProps & {
+ teamId: string;
+}) {
+ // get outLink and app
+ const { teamInfo, uid } = await authChatTeamInfo({ shareTeamId: teamId, authToken: outLinkUid });
+ // check balance and chat limit
+ const tmb = await MongoTeamMember.findOne({ teamId, userId: String(teamInfo.ownerId) });
+
+ if (!tmb) {
+ throw new Error('can not find it');
+ }
+
+ const { user } = await getUserChatInfoAndAuthTeamPoints(String(tmb._id));
+
+ return {
+ user,
+ tmbId: String(tmb._id),
+ uid: uid
+ };
+}
diff --git a/projects/app/src/service/support/wallet/bill/controller.ts b/projects/app/src/service/support/wallet/bill/controller.ts
deleted file mode 100644
index e6edfcd6b20..00000000000
--- a/projects/app/src/service/support/wallet/bill/controller.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ConcatBillProps, CreateBillProps } from '@fastgpt/global/support/wallet/bill/api';
-import { addLog } from '@fastgpt/service/common/system/log';
-import { POST } from '@fastgpt/service/common/api/plusRequest';
-import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
-
-export function createBill(data: CreateBillProps) {
- if (!FastGPTProUrl) return;
- if (data.total === 0) {
- addLog.info('0 Bill', data);
- }
- try {
- POST('/support/wallet/bill/createBill', data);
- } catch (error) {}
-}
-export function concatBill(data: ConcatBillProps) {
- if (!FastGPTProUrl) return;
- if (data.total === 0) {
- addLog.info('0 Bill', data);
- }
- try {
- POST('/support/wallet/bill/concatBill', data);
- } catch (error) {}
-}
diff --git a/projects/app/src/service/support/wallet/bill/push.ts b/projects/app/src/service/support/wallet/bill/push.ts
deleted file mode 100644
index 4bb3819cdf6..00000000000
--- a/projects/app/src/service/support/wallet/bill/push.ts
+++ /dev/null
@@ -1,327 +0,0 @@
-import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
-import { ModelTypeEnum } from '@/service/core/ai/model';
-import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
-import { addLog } from '@fastgpt/service/common/system/log';
-import { PostReRankProps } from '@fastgpt/global/core/ai/api';
-import { createBill, concatBill } from './controller';
-import { formatModelPrice2Store } from '@/service/support/wallet/bill/utils';
-
-export const pushChatBill = ({
- appName,
- appId,
- teamId,
- tmbId,
- source,
- response
-}: {
- appName: string;
- appId: string;
- teamId: string;
- tmbId: string;
- source: `${BillSourceEnum}`;
- response: ChatHistoryItemResType[];
-}) => {
- const total = response.reduce((sum, item) => sum + (item.price || 0), 0);
-
- createBill({
- teamId,
- tmbId,
- appName,
- appId,
- total,
- source,
- list: response.map((item) => ({
- moduleName: item.moduleName,
- amount: item.price || 0,
- model: item.model,
- inputTokens: item.inputTokens,
- outputTokens: item.outputTokens,
- charsLength: item.charsLength
- }))
- });
- addLog.info(`finish completions`, {
- source,
- teamId,
- tmbId,
- price: formatStorePrice2Read(total)
- });
- return { total };
-};
-
-export const pushQABill = async ({
- teamId,
- tmbId,
- model,
- charsLength,
- billId
-}: {
- teamId: string;
- tmbId: string;
- model: string;
- charsLength: number;
- billId: string;
-}) => {
- // 计算价格
- const { total } = formatModelPrice2Store({
- model,
- inputLen: charsLength,
- type: ModelTypeEnum.llm
- });
-
- concatBill({
- billId,
- teamId,
- tmbId,
- total,
- charsLength,
- listIndex: 1
- });
-
- return { total };
-};
-
-export const pushGenerateVectorBill = ({
- billId,
- teamId,
- tmbId,
- charsLength,
- model,
- source = BillSourceEnum.fastgpt,
- extensionModel,
- extensionInputTokens,
- extensionOutputTokens
-}: {
- billId?: string;
- teamId: string;
- tmbId: string;
- charsLength: number;
- model: string;
- source?: `${BillSourceEnum}`;
-
- extensionModel?: string;
- extensionInputTokens?: number;
- extensionOutputTokens?: number;
-}) => {
- const { total: totalVector, modelName: vectorModelName } = formatModelPrice2Store({
- model,
- inputLen: charsLength,
- type: ModelTypeEnum.vector
- });
-
- const { extensionTotal, extensionModelName } = (() => {
- if (!extensionModel || !extensionInputTokens || !extensionOutputTokens)
- return {
- extensionTotal: 0,
- extensionModelName: ''
- };
- const { total, modelName } = formatModelPrice2Store({
- model: extensionModel,
- inputLen: extensionInputTokens,
- outputLen: extensionOutputTokens,
- type: ModelTypeEnum.llm
- });
- return {
- extensionTotal: total,
- extensionModelName: modelName
- };
- })();
-
- const total = totalVector + extensionTotal;
-
- // 插入 Bill 记录
- if (billId) {
- concatBill({
- teamId,
- tmbId,
- total: totalVector,
- billId,
- charsLength,
- listIndex: 0
- });
- } else {
- createBill({
- teamId,
- tmbId,
- appName: 'wallet.moduleName.index',
- total,
- source,
- list: [
- {
- moduleName: 'wallet.moduleName.index',
- amount: totalVector,
- model: vectorModelName,
- charsLength
- },
- ...(extensionModel !== undefined
- ? [
- {
- moduleName: 'core.module.template.Query extension',
- amount: extensionTotal,
- model: extensionModelName,
- inputTokens: extensionInputTokens,
- outputTokens: extensionOutputTokens
- }
- ]
- : [])
- ]
- });
- }
- return { total };
-};
-
-export const pushQuestionGuideBill = ({
- inputTokens,
- outputTokens,
- teamId,
- tmbId
-}: {
- inputTokens: number;
- outputTokens: number;
- teamId: string;
- tmbId: string;
-}) => {
- const qgModel = global.llmModels[0];
- const { total, modelName } = formatModelPrice2Store({
- inputLen: inputTokens,
- outputLen: outputTokens,
- model: qgModel.model,
- type: ModelTypeEnum.llm
- });
-
- createBill({
- teamId,
- tmbId,
- appName: 'wallet.bill.Next Step Guide',
- total,
- source: BillSourceEnum.fastgpt,
- list: [
- {
- moduleName: 'wallet.bill.Next Step Guide',
- amount: total,
- model: modelName,
- inputTokens,
- outputTokens
- }
- ]
- });
-};
-
-export function pushAudioSpeechBill({
- appName = 'wallet.bill.Audio Speech',
- model,
- charsLength,
- teamId,
- tmbId,
- source = BillSourceEnum.fastgpt
-}: {
- appName?: string;
- model: string;
- charsLength: number;
- teamId: string;
- tmbId: string;
- source: `${BillSourceEnum}`;
-}) {
- const { total, modelName } = formatModelPrice2Store({
- model,
- inputLen: charsLength,
- type: ModelTypeEnum.audioSpeech
- });
-
- createBill({
- teamId,
- tmbId,
- appName,
- total,
- source,
- list: [
- {
- moduleName: appName,
- amount: total,
- model: modelName,
- charsLength
- }
- ]
- });
-}
-
-export function pushWhisperBill({
- teamId,
- tmbId,
- duration
-}: {
- teamId: string;
- tmbId: string;
- duration: number;
-}) {
- const whisperModel = global.whisperModel;
-
- if (!whisperModel) return;
-
- const { total, modelName } = formatModelPrice2Store({
- model: whisperModel.model,
- inputLen: duration,
- type: ModelTypeEnum.whisper,
- multiple: 60
- });
-
- const name = 'wallet.bill.Whisper';
-
- createBill({
- teamId,
- tmbId,
- appName: name,
- total,
- source: BillSourceEnum.fastgpt,
- list: [
- {
- moduleName: name,
- amount: total,
- model: modelName,
- duration
- }
- ]
- });
-}
-
-export function pushReRankBill({
- teamId,
- tmbId,
- source,
- inputs
-}: {
- teamId: string;
- tmbId: string;
- source: `${BillSourceEnum}`;
- inputs: PostReRankProps['inputs'];
-}) {
- const reRankModel = global.reRankModels[0];
- if (!reRankModel) return { total: 0 };
-
- const charsLength = inputs.reduce((sum, item) => sum + item.text.length, 0);
-
- const { total, modelName } = formatModelPrice2Store({
- model: reRankModel.model,
- inputLen: charsLength,
- type: ModelTypeEnum.rerank
- });
- const name = 'wallet.bill.ReRank';
-
- createBill({
- teamId,
- tmbId,
- appName: name,
- total,
- source,
- list: [
- {
- moduleName: name,
- amount: total,
- model: modelName,
- charsLength
- }
- ]
- });
-
- return { total };
-}
diff --git a/projects/app/src/service/support/wallet/bill/utils.ts b/projects/app/src/service/support/wallet/bill/utils.ts
deleted file mode 100644
index d880ce76ed7..00000000000
--- a/projects/app/src/service/support/wallet/bill/utils.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { ModelTypeEnum, getModelMap } from '@/service/core/ai/model';
-import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
-import { BillSourceEnum, PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
-
-export function authType2BillSource({
- authType,
- shareId,
- source
-}: {
- authType?: `${AuthUserTypeEnum}`;
- shareId?: string;
- source?: `${BillSourceEnum}`;
-}) {
- if (source) return source;
- if (shareId) return BillSourceEnum.shareLink;
- if (authType === AuthUserTypeEnum.apikey) return BillSourceEnum.api;
- return BillSourceEnum.fastgpt;
-}
-
-export const formatModelPrice2Store = ({
- model,
- inputLen = 0,
- outputLen = 0,
- type,
- multiple = 1000
-}: {
- model: string;
- inputLen: number;
- outputLen?: number;
- type: `${ModelTypeEnum}`;
- multiple?: number;
-}) => {
- const modelData = getModelMap?.[type]?.(model);
- if (!modelData)
- return {
- inputTotal: 0,
- outputTotal: 0,
- total: 0,
- modelName: ''
- };
- const inputTotal = modelData.inputPrice
- ? Math.ceil(modelData.inputPrice * (inputLen / multiple) * PRICE_SCALE)
- : 0;
- const outputTotal = modelData.outputPrice
- ? Math.ceil(modelData.outputPrice * (outputLen / multiple) * PRICE_SCALE)
- : 0;
-
- return {
- modelName: modelData.name,
- inputTotal: inputTotal,
- outputTotal: outputTotal,
- total: inputTotal + outputTotal
- };
-};
diff --git a/projects/app/src/service/support/wallet/usage/controller.ts b/projects/app/src/service/support/wallet/usage/controller.ts
new file mode 100644
index 00000000000..ba287cef1ac
--- /dev/null
+++ b/projects/app/src/service/support/wallet/usage/controller.ts
@@ -0,0 +1,23 @@
+import { ConcatUsageProps, CreateUsageProps } from '@fastgpt/global/support/wallet/usage/api';
+import { addLog } from '@fastgpt/service/common/system/log';
+import { POST } from '@fastgpt/service/common/api/plusRequest';
+import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
+
+export function createUsage(data: CreateUsageProps) {
+ if (!FastGPTProUrl) return;
+ if (data.totalPoints === 0) {
+ addLog.info('0 totalPoints', data);
+ }
+ try {
+ POST('/support/wallet/usage/createUsage', data);
+ } catch (error) {}
+}
+export function concatUsage(data: ConcatUsageProps) {
+ if (!FastGPTProUrl) return;
+ if (data.totalPoints === 0) {
+ addLog.info('0 totalPoints', data);
+ }
+ try {
+ POST('/support/wallet/usage/concatUsage', data);
+ } catch (error) {}
+}
diff --git a/projects/app/src/service/support/wallet/usage/push.ts b/projects/app/src/service/support/wallet/usage/push.ts
new file mode 100644
index 00000000000..93ee28bcc96
--- /dev/null
+++ b/projects/app/src/service/support/wallet/usage/push.ts
@@ -0,0 +1,274 @@
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
+import { ModelTypeEnum } from '@fastgpt/service/core/ai/model';
+import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
+import { addLog } from '@fastgpt/service/common/system/log';
+import { createUsage, concatUsage } from './controller';
+import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
+import { ChatModuleBillType } from '@fastgpt/global/support/wallet/bill/type';
+
+export const pushChatUsage = ({
+ appName,
+ appId,
+ teamId,
+ tmbId,
+ source,
+ moduleDispatchBills
+}: {
+ appName: string;
+ appId: string;
+ teamId: string;
+ tmbId: string;
+ source: `${UsageSourceEnum}`;
+ moduleDispatchBills: ChatModuleBillType[];
+}) => {
+ const totalPoints = moduleDispatchBills.reduce((sum, item) => sum + (item.totalPoints || 0), 0);
+
+ createUsage({
+ teamId,
+ tmbId,
+ appName,
+ appId,
+ totalPoints,
+ source,
+ list: moduleDispatchBills.map((item) => ({
+ moduleName: item.moduleName,
+ amount: item.totalPoints || 0,
+ model: item.model,
+ charsLength: item.charsLength
+ }))
+ });
+ addLog.info(`finish completions`, {
+ source,
+ teamId,
+ tmbId,
+ totalPoints
+ });
+ return { totalPoints };
+};
+
+export const pushQAUsage = async ({
+ teamId,
+ tmbId,
+ model,
+ charsLength,
+ billId
+}: {
+ teamId: string;
+ tmbId: string;
+ model: string;
+ charsLength: number;
+ billId: string;
+}) => {
+ // 计算价格
+ const { totalPoints } = formatModelChars2Points({
+ model,
+ modelType: ModelTypeEnum.llm,
+ charsLength
+ });
+
+ concatUsage({
+ billId,
+ teamId,
+ tmbId,
+ totalPoints,
+ charsLength,
+ listIndex: 1
+ });
+
+ return { totalPoints };
+};
+
+export const pushGenerateVectorUsage = ({
+ billId,
+ teamId,
+ tmbId,
+ charsLength,
+ model,
+ source = UsageSourceEnum.fastgpt,
+ extensionModel,
+ extensionCharsLength
+}: {
+ billId?: string;
+ teamId: string;
+ tmbId: string;
+ charsLength: number;
+ model: string;
+ source?: `${UsageSourceEnum}`;
+
+ extensionModel?: string;
+ extensionCharsLength?: number;
+}) => {
+ const { totalPoints: totalVector, modelName: vectorModelName } = formatModelChars2Points({
+ modelType: ModelTypeEnum.vector,
+ model,
+ charsLength
+ });
+
+ const { extensionTotalPoints, extensionModelName } = (() => {
+ if (!extensionModel || !extensionCharsLength)
+ return {
+ extensionTotalPoints: 0,
+ extensionModelName: ''
+ };
+ const { totalPoints, modelName } = formatModelChars2Points({
+ modelType: ModelTypeEnum.llm,
+ model: extensionModel,
+ charsLength: extensionCharsLength
+ });
+ return {
+ extensionTotalPoints: totalPoints,
+ extensionModelName: modelName
+ };
+ })();
+
+ const totalPoints = totalVector + extensionTotalPoints;
+
+ // 插入 Bill 记录
+ if (billId) {
+ concatUsage({
+ teamId,
+ tmbId,
+ totalPoints,
+ billId,
+ charsLength,
+ listIndex: 0
+ });
+ } else {
+ createUsage({
+ teamId,
+ tmbId,
+ appName: 'support.wallet.moduleName.index',
+ totalPoints,
+ source,
+ list: [
+ {
+ moduleName: 'support.wallet.moduleName.index',
+ amount: totalVector,
+ model: vectorModelName,
+ charsLength
+ },
+ ...(extensionModel !== undefined
+ ? [
+ {
+ moduleName: 'core.module.template.Query extension',
+ amount: extensionTotalPoints,
+ model: extensionModelName,
+ charsLength: extensionCharsLength
+ }
+ ]
+ : [])
+ ]
+ });
+ }
+ return { totalPoints };
+};
+
+export const pushQuestionGuideUsage = ({
+ charsLength,
+ teamId,
+ tmbId
+}: {
+ charsLength: number;
+ teamId: string;
+ tmbId: string;
+}) => {
+ const qgModel = global.llmModels[0];
+ const { totalPoints, modelName } = formatModelChars2Points({
+ charsLength,
+ model: qgModel.model,
+ modelType: ModelTypeEnum.llm
+ });
+
+ createUsage({
+ teamId,
+ tmbId,
+ appName: 'core.app.Next Step Guide',
+ totalPoints,
+ source: UsageSourceEnum.fastgpt,
+ list: [
+ {
+ moduleName: 'core.app.Next Step Guide',
+ amount: totalPoints,
+ model: modelName,
+ charsLength
+ }
+ ]
+ });
+};
+
+export function pushAudioSpeechUsage({
+ appName = 'support.wallet.bill.Audio Speech',
+ model,
+ charsLength,
+ teamId,
+ tmbId,
+ source = UsageSourceEnum.fastgpt
+}: {
+ appName?: string;
+ model: string;
+ charsLength: number;
+ teamId: string;
+ tmbId: string;
+ source: `${UsageSourceEnum}`;
+}) {
+ const { totalPoints, modelName } = formatModelChars2Points({
+ model,
+ charsLength,
+ modelType: ModelTypeEnum.audioSpeech
+ });
+
+ createUsage({
+ teamId,
+ tmbId,
+ appName,
+ totalPoints,
+ source,
+ list: [
+ {
+ moduleName: appName,
+ amount: totalPoints,
+ model: modelName,
+ charsLength
+ }
+ ]
+ });
+}
+
+export function pushWhisperUsage({
+ teamId,
+ tmbId,
+ duration
+}: {
+ teamId: string;
+ tmbId: string;
+ duration: number;
+}) {
+ const whisperModel = global.whisperModel;
+
+ if (!whisperModel) return;
+
+ const { totalPoints, modelName } = formatModelChars2Points({
+ model: whisperModel.model,
+ charsLength: duration,
+ modelType: ModelTypeEnum.whisper,
+ multiple: 60
+ });
+
+ const name = 'support.wallet.bill.Whisper';
+
+ createUsage({
+ teamId,
+ tmbId,
+ appName: name,
+ totalPoints,
+ source: UsageSourceEnum.fastgpt,
+ list: [
+ {
+ moduleName: name,
+ amount: totalPoints,
+ model: modelName,
+ duration
+ }
+ ]
+ });
+}
diff --git a/projects/app/src/service/support/wallet/usage/utils.ts b/projects/app/src/service/support/wallet/usage/utils.ts
new file mode 100644
index 00000000000..d5d2faea140
--- /dev/null
+++ b/projects/app/src/service/support/wallet/usage/utils.ts
@@ -0,0 +1,44 @@
+import { ModelTypeEnum, getModelMap } from '@fastgpt/service/core/ai/model';
+import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
+
+export function authType2UsageSource({
+ authType,
+ shareId,
+ source
+}: {
+ authType?: `${AuthUserTypeEnum}`;
+ shareId?: string;
+ source?: `${UsageSourceEnum}`;
+}) {
+ if (source) return source;
+ if (shareId) return UsageSourceEnum.shareLink;
+ if (authType === AuthUserTypeEnum.apikey) return UsageSourceEnum.api;
+ return UsageSourceEnum.fastgpt;
+}
+
+export const formatModelChars2Points = ({
+ model,
+ charsLength = 0,
+ modelType,
+ multiple = 1000
+}: {
+ model: string;
+ charsLength: number;
+ modelType: `${ModelTypeEnum}`;
+ multiple?: number;
+}) => {
+ const modelData = getModelMap?.[modelType]?.(model);
+ if (!modelData)
+ return {
+ totalPoints: 0,
+ modelName: ''
+ };
+
+ const totalPoints = (modelData.charsPointsPrice || 0) * (charsLength / multiple);
+
+ return {
+ modelName: modelData.name,
+ totalPoints
+ };
+};
diff --git a/projects/app/src/service/utils/chat/saveChat.ts b/projects/app/src/service/utils/chat/saveChat.ts
index f80c1a69cfd..7ddf50abb74 100644
--- a/projects/app/src/service/utils/chat/saveChat.ts
+++ b/projects/app/src/service/utils/chat/saveChat.ts
@@ -5,6 +5,7 @@ import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { addLog } from '@fastgpt/service/common/system/log';
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
+import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
type Props = {
chatId: string;
@@ -46,61 +47,54 @@ export async function saveChat({
...chat?.metadata,
...metadata
};
-
- const promise: any[] = [
- MongoChatItem.insertMany(
- content.map((item) => ({
- chatId,
- teamId,
- tmbId,
- appId,
- ...item
- }))
- )
- ];
-
const title =
chatContentReplaceBlock(content[0].value).slice(0, 20) ||
content[1]?.value?.slice(0, 20) ||
'Chat';
- if (chat) {
- promise.push(
- MongoChat.updateOne(
- { appId, chatId },
- {
- title,
- updateTime: new Date(),
- metadata: metadataUpdate
- }
- )
- );
- } else {
- promise.push(
- MongoChat.create({
+ await mongoSessionRun(async (session) => {
+ await MongoChatItem.insertMany(
+ content.map((item) => ({
chatId,
teamId,
tmbId,
appId,
- variables,
- title,
- source,
- shareId,
- outLinkUid,
- metadata: metadataUpdate
- })
+ ...item
+ })),
+ { session }
);
- }
+
+ if (chat) {
+ chat.title = title;
+ chat.updateTime = new Date();
+ chat.metadata = metadataUpdate;
+ await chat.save({ session });
+ } else {
+ await MongoChat.create(
+ [
+ {
+ chatId,
+ teamId,
+ tmbId,
+ appId,
+ variables,
+ title,
+ source,
+ shareId,
+ outLinkUid,
+ metadata: metadataUpdate
+ }
+ ],
+ { session }
+ );
+ }
+ });
if (updateUseTime && source === ChatSourceEnum.online) {
- promise.push(
- MongoApp.findByIdAndUpdate(appId, {
- updateTime: new Date()
- })
- );
+ MongoApp.findByIdAndUpdate(appId, {
+ updateTime: new Date()
+ });
}
-
- await Promise.all(promise);
} catch (error) {
addLog.error(`update chat history error`, error);
}
diff --git a/projects/app/src/types/i18n.d.ts b/projects/app/src/types/i18n.d.ts
index ac61588b901..4db8b6a81f6 100644
--- a/projects/app/src/types/i18n.d.ts
+++ b/projects/app/src/types/i18n.d.ts
@@ -1,5 +1,5 @@
import 'i18next';
-// import common from '../../public/locales/en/common.json';
+//import common from '../../public/locales/en/common.json';
interface I18nNamespaces {
common: any;
diff --git a/projects/app/src/types/index.d.ts b/projects/app/src/types/index.d.ts
index 68a0a23fcd3..e2e8fc97c23 100644
--- a/projects/app/src/types/index.d.ts
+++ b/projects/app/src/types/index.d.ts
@@ -22,20 +22,12 @@ export type PagingData = {
export type RequestPaging = { pageNum: number; pageSize: number; [key]: any };
declare global {
- var feConfigs: FastGPTFeConfigsType;
var systemEnv: SystemEnvType;
var systemInitd: boolean;
- var subPlans: SubPlanType | undefined;
var qaQueueLen: number;
var vectorQueueLen: number;
- var llmModels: LLMModelItemType[];
- var vectorModels: VectorModelItemType[];
- var audioSpeechModels: AudioSpeechModelType[];
- var whisperModel: WhisperModelType;
- var reRankModels: ReRankModelItemType[];
-
var systemVersion: string;
var simpleModeTemplates: AppSimpleEditConfigTemplateType[];
diff --git a/projects/app/src/types/user.d.ts b/projects/app/src/types/user.d.ts
index cad5159724b..1494965232a 100644
--- a/projects/app/src/types/user.d.ts
+++ b/projects/app/src/types/user.d.ts
@@ -1,6 +1,5 @@
-import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
+import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
-import { BillSchema } from '@fastgpt/global/support/wallet/bill/type.d';
export interface UserUpdateParams {
balance?: number;
diff --git a/projects/app/src/utils/tools.ts b/projects/app/src/utils/tools.ts
index 834294e2b92..76e7fcfe8f8 100644
--- a/projects/app/src/utils/tools.ts
+++ b/projects/app/src/utils/tools.ts
@@ -53,7 +53,7 @@ export const formatTimeToChatTime = (time: Date) => {
// 如果传入时间小于60秒,返回刚刚
if (now.diff(target, 'second') < 60) {
- return 'common.time.Just now';
+ return '刚刚';
}
// 如果时间是今天,展示几时:几分
@@ -63,12 +63,12 @@ export const formatTimeToChatTime = (time: Date) => {
// 如果是昨天,展示昨天
if (now.subtract(1, 'day').isSame(target, 'day')) {
- return 'common.time.Yesterday';
+ return '昨天';
}
// 如果是前天,展示前天
if (now.subtract(2, 'day').isSame(target, 'day')) {
- return 'common.time.The day before yesterday';
+ return '前天';
}
// 如果是今年,展示某月某日
diff --git a/projects/app/src/web/common/api/fetch.ts b/projects/app/src/web/common/api/fetch.ts
index 74469be56db..ef1dab066b2 100644
--- a/projects/app/src/web/common/api/fetch.ts
+++ b/projects/app/src/web/common/api/fetch.ts
@@ -34,7 +34,7 @@ export const streamFetch = ({
// response data
let responseText = '';
- let remainText = '';
+ let remainTextList: string[] = [];
let errMsg = '';
let responseData: ChatHistoryItemResType[] = [];
let finished = false;
@@ -60,22 +60,23 @@ export const streamFetch = ({
function animateResponseText() {
// abort message
if (abortCtrl.signal.aborted) {
+ const remainText = remainTextList.join('');
onMessage({ text: remainText });
responseText += remainText;
return finish();
}
- if (remainText) {
- const fetchCount = Math.max(1, Math.round(remainText.length / 60));
- const fetchText = remainText.slice(0, fetchCount);
+ if (remainTextList.length > 0) {
+ const fetchCount = Math.max(1, Math.round(remainTextList.length / 60));
+ const fetchText = remainTextList.slice(0, fetchCount).join('');
onMessage({ text: fetchText });
responseText += fetchText;
- remainText = remainText.slice(fetchCount);
+ remainTextList = remainTextList.slice(fetchCount);
}
- if (finished && !remainText) {
+ if (finished && remainTextList.length === 0) {
return finish();
}
@@ -125,7 +126,10 @@ export const streamFetch = ({
try {
failedFinish(await res.clone().json());
} catch {
- failedFinish(await res.clone().text());
+ const errText = await res.clone().text();
+ if (!errText.startsWith('event: error')) {
+ failedFinish();
+ }
}
}
},
@@ -145,11 +149,13 @@ export const streamFetch = ({
if (event === sseResponseEventEnum.answer) {
const text: string = parseJson?.choices?.[0]?.delta?.content || '';
- remainText += text;
+
+ for (const item of text) {
+ remainTextList.push(item);
+ }
} else if (event === sseResponseEventEnum.response) {
const text: string = parseJson?.choices?.[0]?.delta?.content || '';
- onMessage({ text });
- responseText += text;
+ remainTextList.push(text);
} else if (
event === sseResponseEventEnum.moduleStatus &&
parseJson?.name &&
diff --git a/projects/app/src/web/core/app/api.ts b/projects/app/src/web/core/app/api.ts
index e60e4e08abf..72cf960a7f9 100644
--- a/projects/app/src/web/core/app/api.ts
+++ b/projects/app/src/web/core/app/api.ts
@@ -15,6 +15,7 @@ export const getMyApps = () => GET('/core/app/list');
*/
export const postCreateApp = (data: CreateAppParams) => POST('/core/app/create', data);
+export const getMyAppsByTags = (data: {}) => POST(`/proApi/core/chat/team/getApps`, data);
/**
* 根据 ID 删除模型
*/
@@ -30,7 +31,12 @@ export const getModelById = (id: string) => GET(`/core/app/detail
*/
export const putAppById = (id: string, data: AppUpdateParams) =>
PUT(`/core/app/update?appId=${id}`, data);
+export const replaceAppById = (id: string, data: AppUpdateParams) =>
+ PUT(`/core/app/updateTeamTasg?appId=${id}`, data);
+// updateTeamTasg
+export const putAppTagsById = (id: string, data: AppUpdateParams) =>
+ PUT(`/core/app/updateTeamTasg?appId=${id}`, data);
/* 共享市场 */
/**
* 获取共享市场模型
diff --git a/projects/app/src/web/core/app/store/useAppStore.ts b/projects/app/src/web/core/app/store/useAppStore.ts
index 2bc8c685ea7..d04653ba8c1 100644
--- a/projects/app/src/web/core/app/store/useAppStore.ts
+++ b/projects/app/src/web/core/app/store/useAppStore.ts
@@ -1,7 +1,7 @@
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
-import { getMyApps, getModelById, putAppById } from '@/web/core/app/api';
+import { getMyApps, getModelById, putAppById, replaceAppById } from '@/web/core/app/api';
import { defaultApp } from '@/constants/app';
import type { AppUpdateParams } from '@fastgpt/global/core/app/api.d';
import { AppDetailType, AppListItemType } from '@fastgpt/global/core/app/type.d';
@@ -12,6 +12,7 @@ type State = {
appDetail: AppDetailType;
loadAppDetail: (id: string, init?: boolean) => Promise;
updateAppDetail(appId: string, data: AppUpdateParams): Promise;
+ replaceAppDetail(appId: string, data: AppUpdateParams): Promise;
clearAppModules(): void;
};
@@ -47,6 +48,15 @@ export const useAppStore = create()(
};
});
},
+ async replaceAppDetail(appId: string, data: AppUpdateParams) {
+ await replaceAppById(appId, { ...get().appDetail, ...data });
+ set((state) => {
+ state.appDetail = {
+ ...state.appDetail,
+ ...data
+ };
+ });
+ },
clearAppModules() {
set((state) => {
state.appDetail = {
diff --git a/projects/app/src/web/core/app/templates.ts b/projects/app/src/web/core/app/templates.ts
index 804f562596f..98b73bb2000 100644
--- a/projects/app/src/web/core/app/templates.ts
+++ b/projects/app/src/web/core/app/templates.ts
@@ -121,13 +121,13 @@ export const appTemplates: (AppItemType & {
},
{
key: 'model',
- type: 'selectChatModel',
+ type: 'selectLLMModel',
label: 'core.module.input.label.aiModel',
required: true,
valueType: 'string',
showTargetInApp: false,
showTargetInPlugin: false,
- value: 'gpt-3.5-turbo-16k',
+ value: 'gpt-3.5-turbo',
connected: false
},
{
@@ -569,7 +569,7 @@ export const appTemplates: (AppItemType & {
},
{
key: 'model',
- type: 'selectChatModel',
+ type: 'selectLLMModel',
label: 'core.module.input.label.aiModel',
required: true,
valueType: 'string',
@@ -882,13 +882,13 @@ export const appTemplates: (AppItemType & {
},
{
key: 'model',
- type: 'selectChatModel',
+ type: 'selectLLMModel',
label: 'core.module.input.label.aiModel',
required: true,
valueType: 'string',
showTargetInApp: false,
showTargetInPlugin: false,
- value: 'gpt-3.5-turbo-16k',
+ value: 'gpt-3.5-turbo',
connected: false
},
{
@@ -1115,7 +1115,7 @@ export const appTemplates: (AppItemType & {
},
{
key: 'model',
- type: 'selectCQModel',
+ type: 'selectLLMModel',
valueType: 'string',
label: 'core.module.input.label.Classify model',
required: true,
@@ -1345,7 +1345,7 @@ export const appTemplates: (AppItemType & {
},
{
key: 'model',
- type: 'selectChatModel',
+ type: 'selectLLMModel',
label: 'core.module.input.label.aiModel',
required: true,
valueType: 'string',
diff --git a/projects/app/src/web/core/chat/api.ts b/projects/app/src/web/core/chat/api.ts
index 601eaa00932..0ad81d9e54d 100644
--- a/projects/app/src/web/core/chat/api.ts
+++ b/projects/app/src/web/core/chat/api.ts
@@ -1,5 +1,6 @@
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
-import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
+import type { ChatHistoryItemType, chatAppListSchema } from '@fastgpt/global/core/chat/type.d';
+
import type {
CloseCustomFeedbackParams,
InitChatProps,
@@ -16,13 +17,31 @@ import type {
} from '@/global/core/chat/api.d';
import { UpdateChatFeedbackProps } from '@fastgpt/global/core/chat/api';
+/**
+ * 根据队伍ID和获取
+ */
+export const getChatListById = (data: { shareTeamId: string; authToken: string }) =>
+ POST(`/proApi/core/chat/init`, data);
+
+/**
+ * 获取团队分享的对话列表 initTeamChat
+ * @param data
+ * @returns
+ */
+export const getinitTeamChat = (data: { teamId: string; authToken: string; appId: string }) =>
+ GET(`/proApi/core/chat/initTeamChat`, data);
+
/**
* 获取初始化聊天内容
*/
export const getInitChatInfo = (data: InitChatProps) =>
GET(`/core/chat/init`, data);
+export const getInitChatInfoTeam = (data: InitChatProps) =>
+ GET(`/core/chat/init`, data);
export const getInitOutLinkChatInfo = (data: InitOutLinkChatProps) =>
GET(`/core/chat/outLink/init`, data);
+export const getTeamChatInfo = (data: { appId: string; chatId: string; outLinkUid?: string }) =>
+ GET(`/core/chat/team/init`, data);
/**
* get current window history(appid or shareId)
diff --git a/projects/app/src/web/core/chat/storeTeamChat.ts b/projects/app/src/web/core/chat/storeTeamChat.ts
new file mode 100644
index 00000000000..82521d91bd6
--- /dev/null
+++ b/projects/app/src/web/core/chat/storeTeamChat.ts
@@ -0,0 +1,42 @@
+import { create } from 'zustand';
+import { devtools, persist } from 'zustand/middleware';
+import { immer } from 'zustand/middleware/immer';
+import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
+import { customAlphabet } from 'nanoid';
+const nanoid = customAlphabet(
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSYZ1234567890_',
+ 24
+);
+
+type State = {
+ localUId: string;
+ teamShareChatHistory: (ChatHistoryItemType & { delete?: boolean })[];
+ clearLocalHistory: (shareId?: string) => void;
+};
+
+export const useTeamShareChatStore = create()(
+ devtools(
+ persist(
+ immer((set, get) => ({
+ localUId: `shareChat-${Date.now()}-${nanoid()}`,
+ teamShareChatHistory: [], // old version field
+ clearLocalHistory() {
+ // abandon
+ set((state) => {
+ state.teamShareChatHistory = state.teamShareChatHistory.map((item) => ({
+ ...item,
+ delete: true
+ }));
+ });
+ }
+ })),
+ {
+ name: 'shareChatStore',
+ partialize: (state) => ({
+ localUId: state.localUId,
+ shareChatHistory: state.teamShareChatHistory
+ })
+ }
+ )
+ )
+);
diff --git a/projects/app/src/web/core/dataset/store/dataset.ts b/projects/app/src/web/core/dataset/store/dataset.ts
index 9084a7a6675..4f458fe9aa6 100644
--- a/projects/app/src/web/core/dataset/store/dataset.ts
+++ b/projects/app/src/web/core/dataset/store/dataset.ts
@@ -12,7 +12,7 @@ import {
import { defaultDatasetDetail } from '@/constants/dataset';
import type { DatasetUpdateBody } from '@fastgpt/global/core/dataset/api.d';
import { DatasetStatusEnum } from '@fastgpt/global/core/dataset/constants';
-import { postCreateTrainingBill } from '@/web/support/wallet/bill/api';
+import { postCreateTrainingUsage } from '@/web/support/wallet/usage/api';
import { checkTeamWebSyncLimit } from '@/web/support/user/team/api';
type State = {
@@ -89,19 +89,17 @@ export const useDatasetStore = create()(
async startWebsiteSync() {
await checkTeamWebSyncLimit();
- const [_, billId] = await Promise.all([
+ const billId = await postCreateTrainingUsage({
+ name: 'core.dataset.training.Website Sync',
+ datasetId: get().datasetDetail._id
+ });
+
+ return postWebsiteSync({ datasetId: get().datasetDetail._id, billId }).then(() => {
get().updateDataset({
id: get().datasetDetail._id,
status: DatasetStatusEnum.syncing
- }),
- postCreateTrainingBill({
- name: 'core.dataset.training.Website Sync',
- datasetId: get().datasetDetail._id
- })
- ]);
- try {
- postWebsiteSync({ datasetId: get().datasetDetail._id, billId });
- } catch (error) {}
+ });
+ });
}
})),
{
diff --git a/projects/app/src/web/core/dataset/utils.ts b/projects/app/src/web/core/dataset/utils.ts
index 8bd91851ff3..6334fbad97f 100644
--- a/projects/app/src/web/core/dataset/utils.ts
+++ b/projects/app/src/web/core/dataset/utils.ts
@@ -68,12 +68,6 @@ export async function chunksUpload({
});
}
- // add chunk index
- chunks = chunks.map((chunk) => ({
- ...chunk,
- chunkIndex: chunk.chunkIndex
- }));
-
let successInsert = 0;
let retryTimes = 10;
for (let i = 0; i < chunks.length; i += rate) {
diff --git a/projects/app/src/web/core/modules/template/system.ts b/projects/app/src/web/core/modules/template/system.ts
index 45f4001b533..7370a4e0f71 100644
--- a/projects/app/src/web/core/modules/template/system.ts
+++ b/projects/app/src/web/core/modules/template/system.ts
@@ -13,7 +13,7 @@ import { RunAppModule } from '@fastgpt/global/core/module/template/system/runApp
import { PluginInputModule } from '@fastgpt/global/core/module/template/system/pluginInput';
import { PluginOutputModule } from '@fastgpt/global/core/module/template/system/pluginOutput';
import { RunPluginModule } from '@fastgpt/global/core/module/template/system/runPlugin';
-import { AiCFR } from '@fastgpt/global/core/module/template/system/coreferenceResolution';
+import { AiQueryExtension } from '@fastgpt/global/core/module/template/system/queryExtension';
import type {
FlowModuleTemplateType,
@@ -31,7 +31,8 @@ export const appSystemModuleTemplates: FlowModuleTemplateType[] = [
RunAppModule,
ClassifyQuestionModule,
ContextExtractModule,
- HttpModule468
+ HttpModule468,
+ AiQueryExtension
];
export const pluginSystemModuleTemplates: FlowModuleTemplateType[] = [
PluginInputModule,
@@ -43,7 +44,8 @@ export const pluginSystemModuleTemplates: FlowModuleTemplateType[] = [
RunAppModule,
ClassifyQuestionModule,
ContextExtractModule,
- HttpModule468
+ HttpModule468,
+ AiQueryExtension
];
export const moduleTemplatesFlat: FlowModuleTemplateType[] = [
@@ -61,7 +63,7 @@ export const moduleTemplatesFlat: FlowModuleTemplateType[] = [
PluginInputModule,
PluginOutputModule,
RunPluginModule,
- AiCFR
+ AiQueryExtension
];
export const moduleTemplatesList: moduleTemplateListType = [
diff --git a/projects/app/src/web/styles/theme.ts b/projects/app/src/web/styles/theme.ts
index 56debed7292..e8ef455292b 100644
--- a/projects/app/src/web/styles/theme.ts
+++ b/projects/app/src/web/styles/theme.ts
@@ -217,7 +217,7 @@ const Button = defineStyleConfig({
const Input: ComponentStyleConfig = {
baseStyle: {
- fontsize: '14px'
+ fontsize: '1rem'
},
sizes: {
sm: defineStyle({
@@ -368,12 +368,11 @@ export const theme = extendTheme({
styles: {
global: {
'html, body': {
+ fontSize: '14px',
color: 'myGray.900',
- fontSize: 'md',
fontWeight: 400,
height: '100%',
overflow: 'hidden'
- // lineHeight: 'unset'
},
a: {
color: 'primary.600'
@@ -472,16 +471,16 @@ export const theme = extendTheme({
body: 'PingFang,Noto Sans,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'
},
fontSizes: {
- xs: '12px',
- sm: '13px',
- md: '14px',
- lg: '16px',
- xl: '18px',
- '2xl': '20px',
- '3xl': '24px',
- '4xl': '28px',
- '5xl': '32px',
- '6xl': '36px'
+ xs: '0.8rem',
+ sm: '0.93rem',
+ md: '1rem',
+ lg: '1.15rem',
+ xl: '1.3rem',
+ '2xl': '1.45rem',
+ '3xl': '1.6rem',
+ '4xl': '1.75rem',
+ '5xl': '1.9rem',
+ '6xl': '2.05rem'
},
borders: {
sm: '1px solid #E8EBF0',
diff --git a/projects/app/src/web/support/user/team/api.ts b/projects/app/src/web/support/user/team/api.ts
index 1c30a99f463..0250a26175e 100644
--- a/projects/app/src/web/support/user/team/api.ts
+++ b/projects/app/src/web/support/user/team/api.ts
@@ -8,11 +8,13 @@ import {
UpdateTeamMemberProps,
UpdateTeamProps
} from '@fastgpt/global/support/user/team/controller.d';
+import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
import {
TeamItemType,
TeamMemberItemType,
TeamMemberSchema
} from '@fastgpt/global/support/user/team/type.d';
+import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
/* --------------- team ---------------- */
export const getTeamList = (status: `${TeamMemberSchema['status']}`) =>
@@ -23,6 +25,14 @@ export const putUpdateTeam = (data: UpdateTeamProps) =>
PUT(`/proApi/support/user/team/update`, data);
export const putSwitchTeam = (teamId: string) =>
PUT(`/proApi/support/user/team/switch`, { teamId });
+export const updateTags = (teamId: string, tagsUrl: string) =>
+ POST(`/proApi/support/user/team/tags/asyncTags`, { teamId, tagsUrl });
+export const getTeamsTags = (teamId: string) =>
+ GET(`/proApi/support/user/team/tags/list`, { teamId });
+export const putUpdateTeamTags = (data: any) =>
+ PUT(`/proApi/support/user/team/tags/updateUrl`, data);
+export const insertTeamsTags = (tags: Array) =>
+ POST(`/proApi/support/user/team/tags/create`, tags);
/* --------------- team member ---------------- */
export const getTeamMembers = (teamId: string) =>
@@ -46,3 +56,9 @@ export const checkTeamExportDatasetLimit = (datasetId: string) =>
export const checkTeamWebSyncLimit = () => GET(`/support/user/team/limit/webSyncLimit`);
export const checkTeamDatasetSizeLimit = (size: number) =>
GET(`/support/user/team/limit/datasetSizeLimit`, { size });
+
+/* plans */
+export const getTeamPlanStatus = () =>
+ GET(`/support/user/team/plan/getTeamPlanStatus`, { maxQuantity: 1 });
+export const getTeamPlans = () =>
+ GET(`/proApi/support/user/team/plan/getTeamPlans`);
diff --git a/projects/app/src/web/support/user/useUserStore.ts b/projects/app/src/web/support/user/useUserStore.ts
index 7d0109d67c0..bd5f130ab0c 100644
--- a/projects/app/src/web/support/user/useUserStore.ts
+++ b/projects/app/src/web/support/user/useUserStore.ts
@@ -3,14 +3,18 @@ import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import type { UserUpdateParams } from '@/types/user';
import type { UserType } from '@fastgpt/global/support/user/type.d';
-import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
import { getTokenLogin, putUserInfo } from '@/web/support/user/api';
+import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
+import { getTeamPlanStatus } from './team/api';
+import { useSystemStore } from '@/web/common/system/useSystemStore';
type State = {
userInfo: UserType | null;
initUserInfo: () => Promise;
setUserInfo: (user: UserType | null) => void;
updateUserInfo: (user: UserUpdateParams) => Promise;
+ teamPlanStatus: FeTeamPlanStatusType | null;
+ initTeamPlanStatus: () => Promise;
};
export const useUserStore = create()(
@@ -19,19 +23,22 @@ export const useUserStore = create()(
immer((set, get) => ({
userInfo: null,
async initUserInfo() {
+ get().initTeamPlanStatus();
+
const res = await getTokenLogin();
get().setUserInfo(res);
+ //设置html的fontsize
+ const html = document?.querySelector('html');
+ if (html) {
+ // html.style.fontSize = '16px';
+ }
+
return res;
},
setUserInfo(user: UserType | null) {
set((state) => {
- state.userInfo = user
- ? {
- ...user,
- balance: formatStorePrice2Read(user.balance)
- }
- : null;
+ state.userInfo = user ? user : null;
});
},
async updateUserInfo(user: UserUpdateParams) {
@@ -51,6 +58,15 @@ export const useUserStore = create()(
});
return Promise.reject(error);
}
+ },
+ teamPlanStatus: null,
+ initTeamPlanStatus() {
+ return getTeamPlanStatus().then((res) => {
+ set((state) => {
+ state.teamPlanStatus = res;
+ });
+ return res;
+ });
}
})),
{
diff --git a/projects/app/src/web/support/wallet/bill/api.ts b/projects/app/src/web/support/wallet/bill/api.ts
index 43dede5eab2..ab4a17009d1 100644
--- a/projects/app/src/web/support/wallet/bill/api.ts
+++ b/projects/app/src/web/support/wallet/bill/api.ts
@@ -1,10 +1,22 @@
+import { RequestPaging } from '@/types';
import { GET, POST } from '@/web/common/api/request';
-import { CreateTrainingBillProps } from '@fastgpt/global/support/wallet/bill/api.d';
-import type { PagingData, RequestPaging } from '@/types';
-import type { BillItemType } from '@fastgpt/global/support/wallet/bill/type';
+import { CreateBillProps, CreateBillResponse } from '@fastgpt/global/support/wallet/bill/api';
+import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
+import type { BillSchemaType } from '@fastgpt/global/support/wallet/bill/type.d';
-export const getUserBills = (data: RequestPaging) =>
- POST>(`/proApi/support/wallet/bill/getBill`, data);
+export const getBills = (
+ data: RequestPaging & {
+ type?: `${BillTypeEnum}`;
+ }
+) => POST(`/proApi/support/wallet/bill/list`, data);
-export const postCreateTrainingBill = (data: CreateTrainingBillProps) =>
- POST(`/support/wallet/bill/createTrainingBill`, data);
+export const getWxPayQRCode = (data: CreateBillProps) =>
+ POST(`/proApi/support/wallet/bill/create`, data);
+
+export const checkBalancePayResult = (payId: string) =>
+ GET(`/proApi/support/wallet/bill/checkPayResult`, { payId }).then((data) => {
+ try {
+ GET('/common/system/unlockTask');
+ } catch (error) {}
+ return data;
+ });
diff --git a/projects/app/src/web/support/wallet/pay/api.ts b/projects/app/src/web/support/wallet/pay/api.ts
deleted file mode 100644
index a57c83774ce..00000000000
--- a/projects/app/src/web/support/wallet/pay/api.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { GET } from '@/web/common/api/request';
-import type { PaySchema } from '@fastgpt/global/support/wallet/pay/type.d';
-export const getPayOrders = () => GET(`/proApi/support/wallet/pay/getPayOrders`);
-
-export const getPayCode = (amount: number) =>
- GET<{
- codeUrl: string;
- payId: string;
- }>(`/proApi/support/wallet/pay/getPayCode`, { amount });
-
-export const checkPayResult = (payId: string) =>
- GET(`/proApi/support/wallet/pay/checkPayResult`, { payId }).then((data) => {
- try {
- GET('/common/system/unlockTask');
- } catch (error) {}
- return data;
- });
diff --git a/projects/app/src/web/support/wallet/sub/api.ts b/projects/app/src/web/support/wallet/sub/api.ts
index 421a817f2cb..32236cb92ab 100644
--- a/projects/app/src/web/support/wallet/sub/api.ts
+++ b/projects/app/src/web/support/wallet/sub/api.ts
@@ -1,30 +1,16 @@
import { GET, POST, PUT, DELETE } from '@/web/common/api/request';
import {
StandardSubPlanParams,
- StandardSubPlanUpdateResponse,
- SubDatasetSizeParams,
- SubDatasetSizePreviewCheckResponse
+ StandardSubPlanUpdateResponse
} from '@fastgpt/global/support/wallet/sub/api';
import { SubStatusEnum, SubTypeEnum } from '@fastgpt/global/support/wallet/sub/constants';
-import { FeTeamSubType } from '@fastgpt/global/support/wallet/sub/type';
export const putTeamDatasetSubStatus = (data: {
status: `${SubStatusEnum}`;
type: `${SubTypeEnum}`;
}) => POST('/proApi/support/wallet/sub/updateStatus', data);
-export const getTeamDatasetValidSub = () =>
- GET(`/support/wallet/sub/getTeamSubStatus`);
-
export const postCheckStandardSub = (data: StandardSubPlanParams) =>
POST('/proApi/support/wallet/sub/standard/preCheck', data);
export const postUpdateStandardSub = (data: StandardSubPlanParams) =>
POST('/proApi/support/wallet/sub/standard/update', data);
-
-export const posCheckTeamDatasetSizeSub = (data: SubDatasetSizeParams) =>
- POST(
- '/proApi/support/wallet/sub/extraDatasetSize/preCheck',
- data
- );
-export const postUpdateTeamDatasetSizeSub = (data: SubDatasetSizeParams) =>
- POST('/proApi/support/wallet/sub/extraDatasetSize/update', data);
diff --git a/projects/app/src/web/support/wallet/sub/constants.ts b/projects/app/src/web/support/wallet/sub/constants.ts
new file mode 100644
index 00000000000..483ca31ac7c
--- /dev/null
+++ b/projects/app/src/web/support/wallet/sub/constants.ts
@@ -0,0 +1,2 @@
+export const AI_POINT_USAGE_CARD_ROUTE = '/price#point-card';
+export const EXTRA_PLAN_CARD_ROUTE = '/price#extra-plan';
diff --git a/projects/app/src/web/support/wallet/usage/api.ts b/projects/app/src/web/support/wallet/usage/api.ts
new file mode 100644
index 00000000000..41b32be3d20
--- /dev/null
+++ b/projects/app/src/web/support/wallet/usage/api.ts
@@ -0,0 +1,10 @@
+import { GET, POST } from '@/web/common/api/request';
+import { CreateTrainingUsageProps } from '@fastgpt/global/support/wallet/usage/api.d';
+import type { PagingData, RequestPaging } from '@/types';
+import type { UsageItemType } from '@fastgpt/global/support/wallet/usage/type';
+
+export const getUserUsages = (data: RequestPaging) =>
+ POST>(`/proApi/support/wallet/usage/getUsage`, data);
+
+export const postCreateTrainingUsage = (data: CreateTrainingUsageProps) =>
+ POST(`/support/wallet/usage/createTrainingUsage`, data);
| |