V4.9.7 feature (#4669)
* update doc * feat: Add coupon redemption feature for team subscriptions (#4595) * feat: Add coupon redemption feature for team subscriptions - Introduced `TeamCouponSub` and `TeamCouponSchema` types - Added `redeemCoupon` API endpoint - Updated UI to include a modal for coupon redemption - Added new icon and translations for "Redeem coupon" * perf: remove field teamId * perf: use dynamic import * refactor: move to page component * perf: coupon code * perf: mcp server * perf: test * auto layout (#4634) * fix 4.9.6 (#4631) * fix debug quote list * delete next text node match * fix extract default boolean value * export latest 100 chat items * fix quote item ui * doc * fix doc * feat: auto layout * perf: auto layout * fix: auto layout null * add start node --------- Co-authored-by: heheer <heheer@sealos.io> * fix: share link (#4644) * Add workflow run duration;Get audio duration (#4645) * add duration * get audio duration * Custom config path (#4649) * feat: 通过环境变量DATA_PATH获取配置文件目录 (#4622) 通过环境变量DATA_PATH获取配置文件目录,以应对不同的部署方式的多样化需求 * feat: custom configjson path * doc --------- Co-authored-by: John Chen <sss1991@163.com> * 程序api调用场景下,如果大量调用带有图片或视频,产生的聊天记录会导致后台mongo数据库异常。这个修改给api客户端一个禁止生成聊天记录的选项,避免这个后果。 (#3964) * update special chatId * perf: vector db rename * update operationLog (#4647) * update operationLog * combine operationLogMap * solve operationI18nLogMap bug * remoce log * feat: Rerank usage (#4654) * refresh concat when update (#4655) * fix: refresh code * perf: timer lock * Fix operationLog (#4657) * perf: http streamable mcp * add alipay (#4630) * perf: subplan ui * perf: pay code * hiden bank tip * Fix: pay error (#4665) * fix quote number (#4666) * remove log --------- Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: John Chen <sss1991@163.com> Co-authored-by: gaord <bengao168@msn.com> Co-authored-by: gggaaallleee <91131304+gggaaallleee@users.noreply.github.com>
This commit is contained in:
@@ -62,6 +62,14 @@ WORKFLOW_MAX_LOOP_TIMES=50
|
||||
# 启用内网 IP 检查
|
||||
CHECK_INTERNAL_IP=false
|
||||
|
||||
# 特殊配置
|
||||
# 自定义跨域,不配置时,默认都允许跨域(逗号分割)
|
||||
ALLOWED_ORIGINS=
|
||||
# 是否展示兑换码功能
|
||||
SHOW_COUPON=false
|
||||
# 自定义 config.json 路径
|
||||
CONFIG_JSON_PATH=
|
||||
|
||||
# 对话日志推送服务
|
||||
# # 日志服务地址
|
||||
# CHAT_LOG_URL=http://localhost:8080
|
||||
@@ -69,5 +77,4 @@ CHECK_INTERNAL_IP=false
|
||||
# CHAT_LOG_INTERVAL=10000
|
||||
# # 日志来源ID前缀
|
||||
# CHAT_LOG_SOURCE_ID_PREFIX=fastgpt-
|
||||
# 自定义跨域,不配置时,默认都允许跨域(逗号分割)
|
||||
ALLOWED_ORIGINS=
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"@chakra-ui/react": "2.10.7",
|
||||
"@chakra-ui/styled-system": "2.9.1",
|
||||
"@chakra-ui/system": "2.6.1",
|
||||
"@dagrejs/dagre": "^1.1.4",
|
||||
"@emotion/react": "11.11.1",
|
||||
"@emotion/styled": "11.11.0",
|
||||
"@fastgpt/global": "workspace:*",
|
||||
|
||||
9
projects/app/public/imgs/modal/wallet.svg
Normal file
9
projects/app/public/imgs/modal/wallet.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon/line/wallet">
|
||||
<g id="Vector">
|
||||
<path d="M3.35205 3.84415C1.90167 4.09989 0.93322 5.48298 1.18896 6.93337L1.66743 9.6469V6.69182C1.66743 5.58725 2.56286 4.69182 3.66743 4.69182H6.22302L14.2787 3.27139C15.0039 3.14352 15.6954 3.62775 15.8233 4.35294L15.883 4.69182H16.3327C16.6749 4.69182 16.9971 4.7778 17.2788 4.92933L17.1364 4.12141C16.8806 2.67103 15.4975 1.70257 14.0471 1.95832L3.35205 3.84415Z" fill="#3370FF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.834106 6.69182C0.834106 5.12702 2.10263 3.85849 3.66744 3.85849H16.3327C17.8975 3.85849 19.166 5.12702 19.166 6.69182V15.2493C19.166 16.8141 17.8975 18.0827 16.3327 18.0827H3.66744C2.10263 18.0827 0.834106 16.8141 0.834106 15.2493V6.69182ZM3.66744 5.52516C3.02311 5.52516 2.50077 6.04749 2.50077 6.69182V15.2493C2.50077 15.8937 3.02311 16.416 3.66744 16.416H16.3327C16.977 16.416 17.4993 15.8937 17.4993 15.2493V6.69182C17.4993 6.04749 16.977 5.52516 16.3327 5.52516H3.66744Z" fill="#3370FF"/>
|
||||
<path d="M15.7837 11.0202C15.7837 11.7539 15.1889 12.3487 14.4553 12.3487C13.7216 12.3487 13.1268 11.7539 13.1268 11.0202C13.1268 10.2866 13.7216 9.69182 14.4553 9.69182C15.1889 9.69182 15.7837 10.2866 15.7837 11.0202Z" fill="#3370FF"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -8,7 +8,8 @@ import {
|
||||
PopoverBody,
|
||||
PopoverArrow,
|
||||
Box,
|
||||
Flex
|
||||
Flex,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
@@ -32,6 +33,8 @@ const A = ({ children, ...props }: any) => {
|
||||
manual: true
|
||||
});
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
// empty href link
|
||||
if (!props.href && typeof children?.[0] === 'string') {
|
||||
const text = useMemo(() => String(children), [children]);
|
||||
@@ -71,77 +74,95 @@ const A = ({ children, ...props }: any) => {
|
||||
direction="rtl"
|
||||
placement="bottom"
|
||||
strategy={'fixed'}
|
||||
onOpen={() => runAsync(String(children))}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onOpen={() => {
|
||||
onOpen();
|
||||
runAsync(String(children));
|
||||
}}
|
||||
gutter={4}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<Button variant={'unstyled'} minH={0} minW={0} h={'auto'}>
|
||||
<MyTooltip label={t('common:read_quote')}>
|
||||
<Box
|
||||
w={5}
|
||||
h={5}
|
||||
border={'1px solid'}
|
||||
w={'14px'}
|
||||
h={'14px'}
|
||||
borderRadius={'full'}
|
||||
borderColor={'myGray.200'}
|
||||
color={'myGray.500'}
|
||||
bg={'rgba(0, 0, 0, 0.08)'}
|
||||
color={'myGray.600'}
|
||||
fontSize={'10px'}
|
||||
display={'flex'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
ml={0.5}
|
||||
transform={'translateY(-2px)'}
|
||||
transform={'translateY(-3px)'}
|
||||
>
|
||||
{index}
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent boxShadow={'lg'} w={'400px'} py={4}>
|
||||
<MyBox isLoading={loading} minH={'224px'}>
|
||||
<PopoverContent boxShadow={'lg'} w={'500px'} py={4}>
|
||||
<MyBox isLoading={loading}>
|
||||
<PopoverArrow />
|
||||
<PopoverBody
|
||||
px={4}
|
||||
py={0}
|
||||
fontSize={'sm'}
|
||||
maxW={'400px'}
|
||||
maxH={'224px'}
|
||||
overflow={'auto'}
|
||||
>
|
||||
<Box
|
||||
alignItems={'center'}
|
||||
fontSize={'xs'}
|
||||
border={'sm'}
|
||||
borderRadius={'sm'}
|
||||
overflow={'hidden'}
|
||||
display={'inline-flex'}
|
||||
height={6}
|
||||
>
|
||||
<Flex
|
||||
color={'myGray.500'}
|
||||
bg={'myGray.150'}
|
||||
w={4}
|
||||
justifyContent={'center'}
|
||||
fontSize={'10px'}
|
||||
h={'full'}
|
||||
<PopoverBody py={0} px={0} fontSize={'sm'}>
|
||||
<Flex px={4} pb={1} justifyContent={'space-between'}>
|
||||
<Box
|
||||
alignItems={'center'}
|
||||
fontSize={'xs'}
|
||||
border={'sm'}
|
||||
borderRadius={'sm'}
|
||||
overflow={'hidden'}
|
||||
display={'inline-flex'}
|
||||
height={6}
|
||||
mr={1}
|
||||
flexShrink={0}
|
||||
>
|
||||
{index}
|
||||
</Flex>
|
||||
<Flex px={1.5}>
|
||||
<MyIcon name={icon as any} mr={1} flexShrink={0} w={'12px'} />
|
||||
<Box
|
||||
className={'textEllipsis'}
|
||||
wordBreak={'break-all'}
|
||||
flex={'1 0 0'}
|
||||
fontSize={'mini'}
|
||||
color={'myGray.900'}
|
||||
<Flex
|
||||
color={'myGray.500'}
|
||||
bg={'myGray.150'}
|
||||
w={4}
|
||||
justifyContent={'center'}
|
||||
fontSize={'10px'}
|
||||
h={'full'}
|
||||
alignItems={'center'}
|
||||
mr={1}
|
||||
flexShrink={0}
|
||||
>
|
||||
{sourceData.sourceName}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box>
|
||||
{index}
|
||||
</Flex>
|
||||
<Flex px={1.5}>
|
||||
<MyIcon name={icon as any} mr={1} flexShrink={0} w={'12px'} />
|
||||
<Box
|
||||
className={'textEllipsis'}
|
||||
wordBreak={'break-all'}
|
||||
flex={'1 0 0'}
|
||||
fontSize={'mini'}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
{sourceData.sourceName}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
color={'primary.600'}
|
||||
size={'xs'}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
eventBus.emit(EventNameEnum.openQuoteReader, {
|
||||
// quoteId: String(children),
|
||||
sourceId: sourceData.sourceId,
|
||||
sourceName: sourceData.sourceName,
|
||||
datasetId: quoteData?.collection.datasetId,
|
||||
collectionId: quoteData?.collection._id
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('common:all_quotes')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box h={'300px'} overflow={'auto'} px={4}>
|
||||
<Markdown source={quoteData?.q} />
|
||||
{quoteData?.a && <Markdown source={quoteData?.a} />}
|
||||
</Box>
|
||||
|
||||
@@ -94,6 +94,7 @@ const ModelTable = () => {
|
||||
typeLabel: t('common:model.type.embedding'),
|
||||
priceLabel: (
|
||||
<Flex color={'myGray.700'}>
|
||||
{`${t('common:common.Input')}: `}
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice || 0}
|
||||
</Box>
|
||||
@@ -131,7 +132,17 @@ const ModelTable = () => {
|
||||
const formatRerankModelList = reRankModelList.map((item) => ({
|
||||
...item,
|
||||
typeLabel: t('common:model.type.reRank'),
|
||||
priceLabel: <Flex color={'myGray.700'}>- </Flex>,
|
||||
priceLabel: item.charsPointsPrice ? (
|
||||
<Flex color={'myGray.700'}>
|
||||
{`${t('common:common.Input')}: `}
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice}
|
||||
</Box>
|
||||
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
|
||||
</Flex>
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
tagColor: 'red'
|
||||
}));
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const DatasetParamsModal = ({
|
||||
defaultValues: {
|
||||
searchMode,
|
||||
embeddingWeight: embeddingWeight || 0.5,
|
||||
usingReRank: !!usingReRank && teamPlanStatus?.standardConstants?.permissionReRank !== false,
|
||||
usingReRank: usingReRank || true,
|
||||
rerankModel: rerankModel || defaultModels?.rerank?.model,
|
||||
rerankWeight: rerankWeight || 0.5,
|
||||
limit,
|
||||
@@ -246,11 +246,6 @@ const DatasetParamsModal = ({
|
||||
<Box color={'myGray.500'} fontSize={'sm'}>
|
||||
{t('common:core.ai.Not deploy rerank model')}
|
||||
</Box>
|
||||
) : teamPlanStatus?.standardConstants &&
|
||||
!teamPlanStatus?.standardConstants?.permissionReRank ? (
|
||||
<Box color={'myGray.500'} fontSize={'sm'}>
|
||||
{t('common:support.team.limit.No permission rerank')}
|
||||
</Box>
|
||||
) : (
|
||||
<Switch {...register('usingReRank')} />
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Box, BoxProps, Card, Flex } from '@chakra-ui/react';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import ChatController, { type ChatControllerProps } from './ChatController';
|
||||
import ChatAvatar from './ChatAvatar';
|
||||
import { MessageCardStyle } from '../constants';
|
||||
@@ -26,6 +26,8 @@ import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time';
|
||||
import dayjs from 'dayjs';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||
import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
|
||||
|
||||
const colorMap = {
|
||||
[ChatStatusEnum.loading]: {
|
||||
@@ -141,6 +143,18 @@ const ChatItem = (props: Props) => {
|
||||
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
|
||||
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
|
||||
const showNodeStatus = useContextSelector(ChatItemContext, (v) => v.showNodeStatus);
|
||||
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||
const isShowReadRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
|
||||
|
||||
const { totalQuoteList: quoteList = [] } = useMemo(
|
||||
() => addStatisticalDataToHistoryItem(chat),
|
||||
[chat]
|
||||
);
|
||||
|
||||
const isChatLog = chatType === 'log';
|
||||
|
||||
const { copyData } = useCopyData();
|
||||
@@ -208,6 +222,59 @@ const ChatItem = (props: Props) => {
|
||||
return groupedValues;
|
||||
}, [chat.obj, chat.value, isChatting]);
|
||||
|
||||
const handleOpenQuoteReader = useCallback(
|
||||
({
|
||||
collectionId,
|
||||
sourceId,
|
||||
sourceName,
|
||||
datasetId
|
||||
}: {
|
||||
collectionId?: string;
|
||||
sourceId?: string;
|
||||
sourceName?: string;
|
||||
datasetId?: string;
|
||||
}) => {
|
||||
if (!setQuoteData) return;
|
||||
|
||||
const collectionIdList = collectionId
|
||||
? [collectionId]
|
||||
: [...new Set(quoteList.map((item) => item.collectionId))];
|
||||
|
||||
setQuoteData({
|
||||
rawSearch: quoteList,
|
||||
metadata:
|
||||
collectionId && isShowReadRawSource
|
||||
? {
|
||||
appId: appId,
|
||||
chatId: chatId,
|
||||
chatItemDataId: chat.dataId,
|
||||
collectionId: collectionId,
|
||||
sourceId: sourceId || '',
|
||||
sourceName: sourceName || '',
|
||||
datasetId: datasetId || '',
|
||||
outLinkAuthData
|
||||
}
|
||||
: {
|
||||
appId: appId,
|
||||
chatId: chatId,
|
||||
chatItemDataId: chat.dataId,
|
||||
collectionIdList,
|
||||
sourceId: sourceId,
|
||||
sourceName: sourceName,
|
||||
outLinkAuthData
|
||||
}
|
||||
});
|
||||
},
|
||||
[setQuoteData, quoteList, isShowReadRawSource, appId, chatId, chat.dataId, outLinkAuthData]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on(EventNameEnum.openQuoteReader, handleOpenQuoteReader);
|
||||
return () => {
|
||||
eventBus.off(EventNameEnum.openQuoteReader);
|
||||
};
|
||||
}, [handleOpenQuoteReader]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
_hover={{
|
||||
|
||||
@@ -34,17 +34,19 @@ const QuoteList = React.memo(function QuoteList({
|
||||
|
||||
const { data: quoteList } = useRequest2(
|
||||
async () =>
|
||||
await getQuoteDataList({
|
||||
datasetDataIdList: rawSearch.map((item) => item.id),
|
||||
collectionIdList: [...new Set(rawSearch.map((item) => item.collectionId))],
|
||||
chatItemDataId,
|
||||
appId,
|
||||
chatId: RawSourceBoxProps.chatId,
|
||||
...outLinkAuthData
|
||||
}),
|
||||
!!chatItemDataId
|
||||
? await getQuoteDataList({
|
||||
datasetDataIdList: rawSearch.map((item) => item.id),
|
||||
collectionIdList: [...new Set(rawSearch.map((item) => item.collectionId))],
|
||||
chatItemDataId,
|
||||
appId,
|
||||
chatId: RawSourceBoxProps.chatId,
|
||||
...outLinkAuthData
|
||||
})
|
||||
: [],
|
||||
{
|
||||
refreshDeps: [rawSearch, RawSourceBoxProps.chatId],
|
||||
manual: !chatItemDataId
|
||||
manual: false
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
|
||||
import { useSize } from 'ahooks';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||
|
||||
const ContextModal = dynamic(() => import('./ContextModal'));
|
||||
const WholeResponseModal = dynamic(() => import('../../../components/WholeResponseModal'));
|
||||
@@ -30,23 +30,18 @@ const ResponseTags = ({
|
||||
const { t } = useTranslation();
|
||||
const quoteListRef = React.useRef<HTMLDivElement>(null);
|
||||
const dataId = historyItem.dataId;
|
||||
const chatTime = historyItem.time || new Date();
|
||||
|
||||
const chatTime = historyItem.time || new Date();
|
||||
const durationSeconds = historyItem.durationSeconds || 0;
|
||||
const {
|
||||
totalQuoteList: quoteList = [],
|
||||
llmModuleAccount = 0,
|
||||
totalRunningTime: runningTime = 0,
|
||||
historyPreviewLength = 0
|
||||
} = useMemo(() => addStatisticalDataToHistoryItem(historyItem), [historyItem]);
|
||||
|
||||
const [quoteFolded, setQuoteFolded] = useState<boolean>(true);
|
||||
|
||||
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
|
||||
const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
|
||||
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
|
||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||
|
||||
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
|
||||
|
||||
const notSharePage = useMemo(() => chatType !== 'share', [chatType]);
|
||||
|
||||
@@ -66,7 +61,6 @@ const ResponseTags = ({
|
||||
? quoteListRef.current.scrollHeight > (isPc ? 50 : 55)
|
||||
: true;
|
||||
|
||||
const isShowReadRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
|
||||
const sourceList = useMemo(() => {
|
||||
return Object.values(
|
||||
quoteList.reduce((acc: Record<string, SearchDataResponseItemType[]>, cur) => {
|
||||
@@ -86,11 +80,20 @@ const ResponseTags = ({
|
||||
}));
|
||||
}, [quoteList]);
|
||||
|
||||
const openQuoteReader = (item?: {
|
||||
collectionId?: string;
|
||||
sourceId?: string;
|
||||
sourceName?: string;
|
||||
datasetId?: string;
|
||||
}) => {
|
||||
eventBus.emit(EventNameEnum.openQuoteReader, item);
|
||||
};
|
||||
|
||||
const notEmptyTags =
|
||||
quoteList.length > 0 ||
|
||||
(llmModuleAccount === 1 && notSharePage) ||
|
||||
(llmModuleAccount > 1 && notSharePage) ||
|
||||
(isPc && runningTime > 0) ||
|
||||
(isPc && durationSeconds > 0) ||
|
||||
notSharePage;
|
||||
|
||||
return !showTags ? null : (
|
||||
@@ -158,35 +161,7 @@ const ResponseTags = ({
|
||||
cursor={'pointer'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (isShowReadRawSource) {
|
||||
setQuoteData({
|
||||
rawSearch: quoteList,
|
||||
metadata: {
|
||||
appId,
|
||||
chatId,
|
||||
chatItemDataId: dataId,
|
||||
collectionId: item.collectionId,
|
||||
sourceId: item.sourceId || '',
|
||||
sourceName: item.sourceName,
|
||||
datasetId: item.datasetId,
|
||||
outLinkAuthData
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setQuoteData({
|
||||
rawSearch: quoteList,
|
||||
metadata: {
|
||||
appId,
|
||||
chatId,
|
||||
chatItemDataId: dataId,
|
||||
collectionIdList: [item.collectionId],
|
||||
sourceId: item.sourceId || '',
|
||||
sourceName: item.sourceName,
|
||||
outLinkAuthData
|
||||
}
|
||||
});
|
||||
}
|
||||
openQuoteReader(item);
|
||||
}}
|
||||
height={6}
|
||||
>
|
||||
@@ -241,17 +216,7 @@ const ResponseTags = ({
|
||||
cursor={'pointer'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
setQuoteData({
|
||||
rawSearch: quoteList,
|
||||
metadata: {
|
||||
appId,
|
||||
chatId,
|
||||
chatItemDataId: dataId,
|
||||
collectionIdList: [...new Set(quoteList.map((item) => item.collectionId))],
|
||||
outLinkAuthData
|
||||
}
|
||||
});
|
||||
openQuoteReader();
|
||||
}}
|
||||
>
|
||||
{t('chat:citations', { num: quoteList.length })}
|
||||
@@ -279,10 +244,10 @@ const ResponseTags = ({
|
||||
{t('chat:multiple_AI_conversations')}
|
||||
</MyTag>
|
||||
)}
|
||||
{isPc && runningTime > 0 && (
|
||||
{isPc && durationSeconds > 0 && (
|
||||
<MyTooltip label={t('chat:module_runtime_and')}>
|
||||
<MyTag colorSchema="purple" type="borderSolid" cursor={'default'}>
|
||||
{runningTime}s
|
||||
{durationSeconds.toFixed(2)}s
|
||||
</MyTag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
|
||||
@@ -221,7 +221,8 @@ const ChatBox = ({
|
||||
interactive,
|
||||
autoTTSResponse,
|
||||
variables,
|
||||
nodeResponse
|
||||
nodeResponse,
|
||||
durationSeconds
|
||||
}: generatingMessageProps & { autoTTSResponse?: boolean }) => {
|
||||
setChatRecords((state) =>
|
||||
state.map((item, index) => {
|
||||
@@ -342,6 +343,13 @@ const ChatBox = ({
|
||||
...item,
|
||||
value: item.value.concat(val)
|
||||
};
|
||||
} else if (event === SseResponseEventEnum.workflowDuration && durationSeconds) {
|
||||
return {
|
||||
...item,
|
||||
durationSeconds: item.durationSeconds
|
||||
? +(item.durationSeconds + durationSeconds).toFixed(2)
|
||||
: durationSeconds
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
@@ -17,6 +17,7 @@ export type generatingMessageProps = {
|
||||
interactive?: WorkflowInteractiveResponseType;
|
||||
variables?: Record<string, any>;
|
||||
nodeResponse?: ChatHistoryItemResType;
|
||||
durationSeconds?: number;
|
||||
};
|
||||
|
||||
export type StartChatFnProps = {
|
||||
|
||||
@@ -252,29 +252,39 @@ export const WholeResponseContent = ({
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Row
|
||||
label={t('common:core.chat.response.module similarity')}
|
||||
value={activeModule?.similarity}
|
||||
/>
|
||||
<Row label={t('common:core.chat.response.module limit')} value={activeModule?.limit} />
|
||||
<Row label={t('chat:response_embedding_model')} value={activeModule?.embeddingModel} />
|
||||
<Row
|
||||
label={t('chat:response_embedding_model_tokens')}
|
||||
value={`${activeModule?.embeddingTokens}`}
|
||||
/>
|
||||
{activeModule?.searchUsingReRank !== undefined && (
|
||||
<Row
|
||||
label={t('common:core.chat.response.search using reRank')}
|
||||
rawDom={
|
||||
<Box border={'base'} borderRadius={'md'} p={2}>
|
||||
{activeModule?.searchUsingReRank ? (
|
||||
activeModule?.rerankModel ? (
|
||||
<Box>{`${activeModule.rerankModel}: ${activeModule.rerankWeight}`}</Box>
|
||||
<>
|
||||
<Row
|
||||
label={t('common:core.chat.response.search using reRank')}
|
||||
rawDom={
|
||||
<Box border={'base'} borderRadius={'md'} p={2}>
|
||||
{activeModule?.searchUsingReRank ? (
|
||||
activeModule?.rerankModel ? (
|
||||
<Box>{`${activeModule.rerankModel}: ${activeModule.rerankWeight}`}</Box>
|
||||
) : (
|
||||
'True'
|
||||
)
|
||||
) : (
|
||||
'True'
|
||||
)
|
||||
) : (
|
||||
`False`
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
`False`
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
<Row
|
||||
label={t('chat:response_rerank_tokens')}
|
||||
value={`${activeModule?.reRankInputTokens}`}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{activeModule.queryExtensionResult && (
|
||||
<>
|
||||
|
||||
@@ -17,9 +17,11 @@ type FormType = {
|
||||
|
||||
const UpdateContactModal = ({
|
||||
onClose,
|
||||
onSuccess,
|
||||
mode
|
||||
}: {
|
||||
onClose: () => void;
|
||||
onSuccess?: (val: string) => void;
|
||||
mode: 'contact' | 'notification_account';
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -37,20 +39,22 @@ const UpdateContactModal = ({
|
||||
const verifyCode = watch('verifyCode');
|
||||
|
||||
const { runAsync: onSubmit, loading: isLoading } = useRequest2(
|
||||
(data: FormType) => {
|
||||
async (data: FormType) => {
|
||||
if (mode === 'contact') {
|
||||
return updateContact(data);
|
||||
await updateContact(data);
|
||||
} else {
|
||||
return updateNotificationAccount({
|
||||
await updateNotificationAccount({
|
||||
account: data.contact,
|
||||
verifyCode: data.verifyCode
|
||||
});
|
||||
}
|
||||
return data.contact;
|
||||
},
|
||||
{
|
||||
onSuccess() {
|
||||
onSuccess(data) {
|
||||
initUserInfo();
|
||||
onClose();
|
||||
onSuccess?.(data);
|
||||
},
|
||||
successToast: t('common:support.user.info.bind_notification_success'),
|
||||
errorToast: t('common:support.user.info.bind_notification_error')
|
||||
|
||||
@@ -1,36 +1,98 @@
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, ModalBody } from '@chakra-ui/react';
|
||||
import { checkBalancePayResult } from '@/web/support/wallet/bill/api';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { Box, ModalBody, Flex, Button } from '@chakra-ui/react';
|
||||
import { checkBalancePayResult, putUpdatePayment } from '@/web/support/wallet/bill/api';
|
||||
import LightTip from '@fastgpt/web/components/common/LightTip';
|
||||
import QRCode from 'qrcode';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import {
|
||||
BillPayWayEnum,
|
||||
BillStatusEnum,
|
||||
QR_CODE_SIZE
|
||||
} from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { CreateBillResponse } from '@fastgpt/global/support/wallet/bill/api';
|
||||
|
||||
export type QRPayProps = {
|
||||
readPrice: number;
|
||||
codeUrl: string;
|
||||
billId: string;
|
||||
export type QRPayProps = CreateBillResponse & {
|
||||
tip?: string;
|
||||
};
|
||||
|
||||
const qrCodeSize = 168;
|
||||
|
||||
const QRCodePayModal = ({
|
||||
tip,
|
||||
readPrice,
|
||||
codeUrl,
|
||||
billId,
|
||||
payment,
|
||||
qrCode,
|
||||
iframeCode,
|
||||
markdown,
|
||||
onSuccess
|
||||
}: QRPayProps & { tip?: string; onSuccess?: () => any }) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const canvasRef = useRef<HTMLDivElement>(null);
|
||||
const toast = useToast();
|
||||
const { feConfigs } = useSystemStore();
|
||||
|
||||
const isAlipayConfigured = feConfigs.payConfig?.alipay;
|
||||
const isWxConfigured = feConfigs.payConfig?.wx;
|
||||
const isBankConfigured = feConfigs.payConfig?.bank;
|
||||
|
||||
const [payWayRenderData, setPayWayRenderData] = useState<{
|
||||
qrCode?: string;
|
||||
iframeCode?: string;
|
||||
markdown?: string;
|
||||
}>({
|
||||
qrCode,
|
||||
iframeCode,
|
||||
markdown
|
||||
});
|
||||
|
||||
const [selectedPayment, setSelectedPayment] = useState(payment);
|
||||
const { runAsync: handlePaymentChange, loading: isUpdating } = useRequest2(
|
||||
async (newPayment: BillPayWayEnum) => {
|
||||
if (newPayment === selectedPayment) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await putUpdatePayment({ billId, payWay: newPayment });
|
||||
setPayWayRenderData(response);
|
||||
setSelectedPayment(newPayment);
|
||||
},
|
||||
{
|
||||
refreshDeps: [billId, selectedPayment]
|
||||
}
|
||||
);
|
||||
|
||||
// Check pay result
|
||||
useRequest2(() => checkBalancePayResult(billId), {
|
||||
manual: false,
|
||||
pollingInterval: 2000,
|
||||
onSuccess: ({ status, description }) => {
|
||||
if (status === BillStatusEnum.SUCCESS) {
|
||||
toast.toast({
|
||||
description: t('common:pay_success'),
|
||||
status: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
onSuccess?.();
|
||||
} else {
|
||||
console.log(status, description);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// UI render
|
||||
// Draw QR code
|
||||
const drawCode = useCallback(() => {
|
||||
if (!payWayRenderData.qrCode) return;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
QRCode.toCanvas(canvas, codeUrl, {
|
||||
width: qrCodeSize,
|
||||
|
||||
QRCode.toCanvas(canvas, payWayRenderData.qrCode, {
|
||||
width: QR_CODE_SIZE,
|
||||
margin: 0,
|
||||
color: {
|
||||
dark: '#000000',
|
||||
@@ -41,38 +103,125 @@ const QRCodePayModal = ({
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.innerHTML = '';
|
||||
canvasRef.current.appendChild(canvas);
|
||||
} else {
|
||||
drawCode();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('QRCode generation error:', err);
|
||||
});
|
||||
}, [codeUrl]);
|
||||
|
||||
.catch(console.error);
|
||||
}, [payWayRenderData.qrCode]);
|
||||
useEffect(() => {
|
||||
drawCode();
|
||||
}, [drawCode]);
|
||||
|
||||
useRequest2(() => checkBalancePayResult(billId), {
|
||||
manual: false,
|
||||
pollingInterval: 2000,
|
||||
onSuccess: (res) => {
|
||||
if (res) {
|
||||
onSuccess?.();
|
||||
// Payment Button
|
||||
const getPaymentButtonStyles = (isActive: boolean) => ({
|
||||
baseStyle: {
|
||||
display: 'flex',
|
||||
padding: '13px 22px 13px 19px',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flex: '1 0 0',
|
||||
borderRadius: '7.152px',
|
||||
border: isActive ? '1px solid #3370FF' : '1px solid #E8EBF0',
|
||||
background: '#FFF',
|
||||
_hover: {
|
||||
background: isActive ? '#FFF' : '#F7F8FA',
|
||||
border: isActive ? '1px solid #3370FF' : '1px solid #E8EBF0'
|
||||
},
|
||||
_active: {
|
||||
background: '#FFF',
|
||||
borderColor: '#3370FF'
|
||||
}
|
||||
},
|
||||
errorToast: ''
|
||||
}
|
||||
});
|
||||
const renderPaymentContent = () => {
|
||||
if (payWayRenderData.qrCode) {
|
||||
return <Box ref={canvasRef} display={'inline-block'} h={`${QR_CODE_SIZE}px`} />;
|
||||
}
|
||||
if (payWayRenderData.iframeCode) {
|
||||
return (
|
||||
<iframe
|
||||
srcDoc={payWayRenderData.iframeCode}
|
||||
style={{
|
||||
width: QR_CODE_SIZE + 5,
|
||||
height: QR_CODE_SIZE + 5,
|
||||
border: 'none',
|
||||
display: 'inline-block'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (payWayRenderData.markdown) {
|
||||
return <Markdown source={payWayRenderData.markdown} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
|
||||
<ModalBody textAlign={'center'} pb={10} whiteSpace={'pre-wrap'}>
|
||||
{tip && <LightTip text={tip} mb={8} textAlign={'left'} />}
|
||||
<Box ref={canvasRef} display={'inline-block'} h={`${qrCodeSize}px`}></Box>
|
||||
<Box mt={5} textAlign={'center'}>
|
||||
{t('common:pay.wechat', { price: readPrice })}
|
||||
<MyModal
|
||||
isLoading={isUpdating}
|
||||
isOpen
|
||||
title={t('common:user.Pay')}
|
||||
iconSrc="/imgs/modal/wallet.svg"
|
||||
w={'600px'}
|
||||
>
|
||||
<ModalBody textAlign={'center'} padding={['16px 24px', '32px 52px']}>
|
||||
{tip && <LightTip text={tip} mb={6} textAlign={'left'} />}
|
||||
<Box>{t('common:pay_money')}</Box>
|
||||
<Box color="primary.600" fontSize="32px" fontWeight="600" lineHeight="40px" mb={6}>
|
||||
¥{readPrice.toFixed(2)}
|
||||
</Box>
|
||||
|
||||
{renderPaymentContent()}
|
||||
|
||||
{selectedPayment !== BillPayWayEnum.bank && (
|
||||
<Box
|
||||
mt={5}
|
||||
textAlign={'center'}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
gap={1}
|
||||
>
|
||||
<MyIcon name={'common/info'} w={4} h={4} />
|
||||
{t('common:pay.noclose')}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Flex justifyContent="center" gap={3} mt={6}>
|
||||
{isWxConfigured && (
|
||||
<Button
|
||||
flex={1}
|
||||
h={10}
|
||||
onClick={() => handlePaymentChange(BillPayWayEnum.wx)}
|
||||
color={'myGray.900'}
|
||||
leftIcon={<MyIcon name={'common/wechat'} />}
|
||||
sx={getPaymentButtonStyles(selectedPayment === BillPayWayEnum.wx).baseStyle}
|
||||
>
|
||||
{t('common:pay.wx_payment')}
|
||||
</Button>
|
||||
)}
|
||||
{isAlipayConfigured && (
|
||||
<Button
|
||||
flex={1}
|
||||
h={10}
|
||||
color={'myGray.900'}
|
||||
onClick={() => handlePaymentChange(BillPayWayEnum.alipay)}
|
||||
leftIcon={<MyIcon name={'common/alipay'} />}
|
||||
sx={getPaymentButtonStyles(selectedPayment === BillPayWayEnum.alipay).baseStyle}
|
||||
>
|
||||
{t('common:pay_alipay_payment')}
|
||||
</Button>
|
||||
)}
|
||||
{isBankConfigured && (
|
||||
<Button
|
||||
flex={1}
|
||||
h={10}
|
||||
color={'myGray.900'}
|
||||
onClick={() => handlePaymentChange(BillPayWayEnum.bank)}
|
||||
sx={getPaymentButtonStyles(selectedPayment === BillPayWayEnum.bank).baseStyle}
|
||||
>
|
||||
{t('common:pay_corporate_payment')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
|
||||
@@ -38,9 +38,9 @@ const StandardPlanContentList = ({
|
||||
permissionCustomApiKey: plan.permissionCustomApiKey,
|
||||
permissionCustomCopyright: plan.permissionCustomCopyright,
|
||||
trainingWeight: plan.trainingWeight,
|
||||
permissionReRank: plan.permissionReRank,
|
||||
totalPoints: plan.totalPoints * (mode === SubModeEnum.month ? 1 : 12),
|
||||
permissionWebsiteSync: plan.permissionWebsiteSync
|
||||
permissionWebsiteSync: plan.permissionWebsiteSync,
|
||||
permissionTeamOperationLog: plan.permissionTeamOperationLog
|
||||
};
|
||||
}, [subPlans?.standard, level, mode]);
|
||||
|
||||
@@ -113,18 +113,20 @@ const StandardPlanContentList = ({
|
||||
})}
|
||||
</Box>
|
||||
</Flex>
|
||||
{!!planContent.permissionReRank && (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'price/right'} w={'16px'} mr={3} />
|
||||
<Box color={'myGray.600'}>{t('common:support.wallet.subscription.rerank')}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{!!planContent.permissionWebsiteSync && (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'price/right'} w={'16px'} mr={3} />
|
||||
<Box color={'myGray.600'}>{t('common:support.wallet.subscription.web_site_sync')}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{!!planContent.permissionTeamOperationLog && (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'price/right'} w={'16px'} mr={3} />
|
||||
<Box color={'myGray.600'}>
|
||||
{t('common:support.wallet.subscription.team_operation_log')}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</Grid>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -45,9 +45,6 @@ export function addStatisticalDataToHistoryItem(historyItem: ChatItemType) {
|
||||
.map((item) => item.quoteList)
|
||||
.flat()
|
||||
.filter(Boolean) as SearchDataResponseItemType[],
|
||||
totalRunningTime: Number(
|
||||
historyItem.responseData?.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
|
||||
),
|
||||
historyPreviewLength: flatResData.find(isLLMNode)?.historyPreview?.length
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export async function register() {
|
||||
import('@fastgpt/service/common/mongo/index'),
|
||||
import('@fastgpt/service/common/system/tools'),
|
||||
import('@/service/common/system'),
|
||||
import('@fastgpt/service/common/vectorStore/controller'),
|
||||
import('@fastgpt/service/common/vectorDB/controller'),
|
||||
import('@/service/mongo'),
|
||||
import('@/service/core/app/plugin'),
|
||||
import('@/service/common/system/volumnMongoWatch'),
|
||||
|
||||
@@ -20,13 +20,14 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
BillStatusEnum,
|
||||
BillTypeEnum,
|
||||
billPayWayMap,
|
||||
billStatusMap,
|
||||
billTypeMap
|
||||
} from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
@@ -68,39 +69,33 @@ const BillTable = () => {
|
||||
defaultRequest: false
|
||||
});
|
||||
|
||||
const { mutate: handleRefreshPayOrder, isLoading: isRefreshing } = useRequest({
|
||||
mutationFn: async (payId: string) => {
|
||||
try {
|
||||
const data = await checkBalancePayResult(payId);
|
||||
const { runAsync: handleRefreshPayOrder, loading: isRefreshing } = useRequest2(
|
||||
async (payId: string) => {
|
||||
const { status, description } = await checkBalancePayResult(payId);
|
||||
if (status === BillStatusEnum.SUCCESS) {
|
||||
toast({
|
||||
title: data,
|
||||
title: t('common:pay_success'),
|
||||
status: 'success'
|
||||
});
|
||||
} catch (error: any) {
|
||||
} else {
|
||||
toast({
|
||||
title: error?.message,
|
||||
title: t(description as any),
|
||||
status: 'warning'
|
||||
});
|
||||
console.log(error);
|
||||
}
|
||||
try {
|
||||
|
||||
if (status === BillStatusEnum.SUCCESS || status === BillStatusEnum.CLOSED) {
|
||||
getData(1);
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getData(1);
|
||||
}, [billType]);
|
||||
|
||||
return (
|
||||
<MyBox
|
||||
isLoading={isLoading || isRefreshing}
|
||||
position={'relative'}
|
||||
h={'100%'}
|
||||
minH={'50vh'}
|
||||
overflow={'overlay'}
|
||||
>
|
||||
<MyBox isLoading={isLoading} position={'relative'} h={'100%'} minH={'50vh'}>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<Thead>
|
||||
@@ -135,7 +130,12 @@ const BillTable = () => {
|
||||
<Td>{t(billStatusMap[item.status]?.label as any)}</Td>
|
||||
<Td>
|
||||
{item.status === 'NOTPAY' && (
|
||||
<Button mr={4} onClick={() => handleRefreshPayOrder(item._id)} size={'sm'}>
|
||||
<Button
|
||||
isLoading={isRefreshing}
|
||||
mr={4}
|
||||
onClick={() => handleRefreshPayOrder(item._id)}
|
||||
size={'sm'}
|
||||
>
|
||||
{t('account_bill:update')}
|
||||
</Button>
|
||||
)}
|
||||
@@ -210,11 +210,13 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
|
||||
<Box>{t(billPayWayMap[bill.metadata.payWay]?.label as any)}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
<Flex alignItems={'center'} pb={4}>
|
||||
<FormLabel flex={'0 0 120px'}>{t('account_bill:support_wallet_amount')}:</FormLabel>
|
||||
<Box>{t('account_bill:yuan', { amount: formatStorePrice2Read(bill.price) })}</Box>
|
||||
</Flex>
|
||||
{bill.metadata && (
|
||||
{!!bill.price && (
|
||||
<Flex alignItems={'center'} pb={4}>
|
||||
<FormLabel flex={'0 0 120px'}>{t('account_bill:support_wallet_amount')}:</FormLabel>
|
||||
<Box>{t('account_bill:yuan', { amount: formatStorePrice2Read(bill.price) })}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{bill.metadata && !!bill.price && (
|
||||
<Flex alignItems={'center'} pb={4}>
|
||||
<FormLabel flex={'0 0 120px'}>{t('account_bill:has_invoice')}:</FormLabel>
|
||||
{bill.metadata.payWay === 'balance' ? (
|
||||
@@ -239,7 +241,7 @@ function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: ()
|
||||
{bill.metadata?.month !== undefined && (
|
||||
<Flex alignItems={'center'} pb={4}>
|
||||
<FormLabel flex={'0 0 120px'}>{t('account_bill:subscription_mode_month')}:</FormLabel>
|
||||
<Box>{bill.metadata?.month}</Box>
|
||||
<Box>{`${bill.metadata?.month} ${t('account_bill:month')}`}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{bill.metadata?.datasetSize !== undefined && (
|
||||
|
||||
@@ -221,7 +221,7 @@ const InvoiceHeaderForm = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<MyBox isLoading={isLoading} pt={['1rem', '3.5rem']}>
|
||||
<MyBox isLoading={isLoading} pt={'1rem'}>
|
||||
<Flex w={'100%'} overflow={'auto'} justify={'center'} flexDir={'column'} align={'center'}>
|
||||
<InvoiceHeaderSingleForm inputForm={inputForm} />
|
||||
<Flex w={'100%'} justify={'center'} mt={'3rem'}>
|
||||
|
||||
@@ -22,6 +22,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import dayjs from 'dayjs';
|
||||
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
|
||||
const InvoiceTable = () => {
|
||||
const { t } = useTranslation();
|
||||
const [invoiceDetailData, setInvoiceDetailData] = useState<InvoiceSchemaType | ''>('');
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { redeemCoupon } from '@/web/support/user/team/api';
|
||||
import { Button, Input, VStack, Text, ModalBody, Box, ModalFooter } from '@chakra-ui/react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const RedeemCouponModal = ({
|
||||
onClose,
|
||||
onSuccess
|
||||
}: {
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [couponCode, setCouponCode] = React.useState('');
|
||||
|
||||
const { runAsync: redeemCouponAsync, loading } = useRequest2(redeemCoupon, {
|
||||
manual: true,
|
||||
onSuccess: () => {
|
||||
onSuccess();
|
||||
onClose();
|
||||
},
|
||||
successToast: t('common:common.Success')
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
iconSrc="support/account/coupon"
|
||||
title={t('account_info:redeem_coupon')}
|
||||
>
|
||||
<ModalBody>
|
||||
<Box fontWeight={500} color={'myGray.900'} mb={'1'}>
|
||||
{t('account_info:redeem_coupon')}
|
||||
</Box>
|
||||
<Input
|
||||
placeholder={t('account_info:redeem_coupon')}
|
||||
value={couponCode}
|
||||
onChange={(e) => setCouponCode(e.target.value)}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} onClick={onClose}>
|
||||
{t('account_info:cancel')}
|
||||
</Button>
|
||||
<Button ml={2} isLoading={loading} onClick={() => redeemCouponAsync(couponCode)}>
|
||||
{t('account_info:confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RedeemCouponModal;
|
||||
@@ -127,11 +127,11 @@ export const ModelEditModal = ({
|
||||
);
|
||||
|
||||
const priceUnit = useMemo(() => {
|
||||
if (isLLMModel || isEmbeddingModel) return '/ 1k Tokens';
|
||||
if (isLLMModel || isEmbeddingModel || isRerankModel) return '/ 1k Tokens';
|
||||
if (isTTSModel) return `/ 1k ${t('common:unit.character')}`;
|
||||
if (isSTTModel) return `/ 60 ${t('common:unit.seconds')}`;
|
||||
return '';
|
||||
}, [isLLMModel, isEmbeddingModel, isTTSModel, t, isSTTModel]);
|
||||
}, [isLLMModel, isEmbeddingModel, isTTSModel, t, isSTTModel, isRerankModel]);
|
||||
|
||||
const { runAsync: updateModel, loading: updatingModel } = useRequest2(
|
||||
async (data: SystemModelItemType) => {
|
||||
|
||||
@@ -141,6 +141,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
typeLabel: t('common:model.type.embedding'),
|
||||
priceLabel: (
|
||||
<Flex color={'myGray.700'}>
|
||||
{`${t('common:common.Input')}: `}
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice || 0}
|
||||
</Box>
|
||||
@@ -184,7 +185,17 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
.map((item) => ({
|
||||
...item,
|
||||
typeLabel: t('common:model.type.reRank'),
|
||||
priceLabel: <Flex color={'myGray.700'}>- </Flex>,
|
||||
priceLabel: item.charsPointsPrice ? (
|
||||
<Flex color={'myGray.700'}>
|
||||
{`${t('common:common.Input')}: `}
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice}
|
||||
</Box>
|
||||
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
|
||||
</Flex>
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
tagColor: 'red'
|
||||
}));
|
||||
|
||||
|
||||
@@ -190,8 +190,9 @@ function EditModal({
|
||||
/>
|
||||
{isOpenContact && (
|
||||
<UpdateContact
|
||||
onClose={() => {
|
||||
onCloseContact();
|
||||
onClose={onCloseContact}
|
||||
onSuccess={(val) => {
|
||||
setValue('notificationAccount', val);
|
||||
}}
|
||||
mode="notification_account"
|
||||
/>
|
||||
|
||||
@@ -17,7 +17,7 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import { getOperationLogs } from '@/web/support/user/team/operantionLog/api';
|
||||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||
import { operationLogI18nMap } from '@fastgpt/service/support/operationLog/constants';
|
||||
import { operationLogMap } from '@fastgpt/service/support/operationLog/constants';
|
||||
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
|
||||
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
|
||||
import UserBox from '@fastgpt/web/components/common/UserBox';
|
||||
@@ -52,7 +52,7 @@ function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
const eventOptions = useMemo(
|
||||
() =>
|
||||
Object.values(OperationLogEventEnum).map((event) => ({
|
||||
label: t(operationLogI18nMap[event].typeLabel),
|
||||
label: t(operationLogMap[event].typeLabel),
|
||||
value: event
|
||||
})),
|
||||
[t]
|
||||
@@ -158,7 +158,7 @@ function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{operationLogs?.map((log) => {
|
||||
const i18nData = operationLogI18nMap[log.event];
|
||||
const i18nData = operationLogMap[log.event];
|
||||
const metadata = { ...log.metadata };
|
||||
|
||||
if (log.event === OperationLogEventEnum.ASSIGN_PERMISSION) {
|
||||
|
||||
@@ -124,7 +124,7 @@ const EditForm = ({
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [selectedModel]);
|
||||
}, [selectedModel, setAppForm]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,34 +1,305 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, HStack, StackProps } from '@chakra-ui/react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { nodeTemplate2FlowNode } from '@/web/core/workflow/utils';
|
||||
import { CommentNode } from '@fastgpt/global/core/workflow/template/system/comment';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { useReactFlow } from 'reactflow';
|
||||
import { Node, useReactFlow } from 'reactflow';
|
||||
import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext';
|
||||
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
||||
import { WorkflowContext } from '../../context';
|
||||
import dagre from '@dagrejs/dagre';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { WorkflowStatusContext } from '../../context/workflowStatusContext';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
|
||||
const ContextMenu = () => {
|
||||
const { t } = useTranslation();
|
||||
const setNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setNodes);
|
||||
const menu = useContextSelector(WorkflowEventContext, (v) => v.menu);
|
||||
const menu = useContextSelector(WorkflowEventContext, (v) => v.menu!);
|
||||
const setMenu = useContextSelector(WorkflowEventContext, (ctx) => ctx.setMenu);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const setEdges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setEdges);
|
||||
const getParentNodeSizeAndPosition = useContextSelector(
|
||||
WorkflowStatusContext,
|
||||
(v) => v.getParentNodeSizeAndPosition
|
||||
);
|
||||
|
||||
const { screenToFlowPosition } = useReactFlow();
|
||||
const newNode = nodeTemplate2FlowNode({
|
||||
template: CommentNode,
|
||||
position: screenToFlowPosition({ x: menu?.left ?? 0, y: menu?.top ?? 0 }),
|
||||
t
|
||||
});
|
||||
const { fitView, screenToFlowPosition } = useReactFlow();
|
||||
|
||||
const allUnFolded = useMemo(() => {
|
||||
return !!menu ? nodeList.some((node) => node.isFolded) : false;
|
||||
}, [nodeList, menu]);
|
||||
|
||||
return !!menu ? (
|
||||
const onLayout = useCallback(() => {
|
||||
const updateChildNodesPosition = ({
|
||||
startNode,
|
||||
nodes,
|
||||
edges
|
||||
}: {
|
||||
startNode: Node<FlowNodeItemType>;
|
||||
nodes: Node<FlowNodeItemType>[];
|
||||
edges: any[];
|
||||
}) => {
|
||||
const startPosition = { x: startNode.position.x, y: startNode.position.y };
|
||||
|
||||
const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
|
||||
dagreGraph.setGraph({
|
||||
rankdir: 'LR',
|
||||
nodesep: 80, // Horizontal space
|
||||
ranksep: 120 // Vertical space
|
||||
});
|
||||
|
||||
nodes.forEach((node) => {
|
||||
dagreGraph.setNode(node.id, { width: node.width!, height: node.height! });
|
||||
});
|
||||
|
||||
// Find connected nodes
|
||||
const connectedNodeIds = new Set<string>();
|
||||
edges.forEach((edge) => {
|
||||
connectedNodeIds.add(edge.source);
|
||||
connectedNodeIds.add(edge.target);
|
||||
|
||||
dagreGraph.setEdge(edge.source, edge.target);
|
||||
});
|
||||
|
||||
dagre.layout(dagreGraph);
|
||||
const layoutedStartNode = dagreGraph.node(startNode.data.nodeId);
|
||||
const offsetX = startPosition.x - (layoutedStartNode.x - startNode.width! / 2);
|
||||
const offsetY = startPosition.y - (layoutedStartNode.y - startNode.height! / 2);
|
||||
|
||||
nodes.forEach((node) => {
|
||||
if (!connectedNodeIds.has(node.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeWithPosition = dagreGraph.node(node.id);
|
||||
|
||||
node.position = {
|
||||
x: nodeWithPosition.x - node.width! / 2 + offsetX,
|
||||
y: nodeWithPosition.y - node.height! / 2 + offsetY
|
||||
};
|
||||
});
|
||||
};
|
||||
const updateParentNodesPosition = ({
|
||||
startNode,
|
||||
nodes,
|
||||
edges
|
||||
}: {
|
||||
startNode: Node<FlowNodeItemType>;
|
||||
nodes: Node<FlowNodeItemType>[];
|
||||
edges: any[];
|
||||
}) => {
|
||||
const startPosition = { x: startNode.position.x, y: startNode.position.y };
|
||||
|
||||
const childNodeIdsSet = new Set(
|
||||
nodes.filter((node) => !!node.data.parentNodeId).map((node) => node.data.nodeId)
|
||||
);
|
||||
|
||||
const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
|
||||
dagreGraph.setGraph({
|
||||
rankdir: 'LR',
|
||||
nodesep: 80, // Horizontal space
|
||||
ranksep: 120 // Vertical space
|
||||
});
|
||||
|
||||
nodes.forEach((node) => {
|
||||
if (childNodeIdsSet.has(node.data.nodeId)) return;
|
||||
dagreGraph.setNode(node.id, { width: node.width!, height: node.height! });
|
||||
});
|
||||
|
||||
// Find connected nodes
|
||||
const connectedNodeIds = new Set<string>();
|
||||
edges.forEach((edge) => {
|
||||
if (childNodeIdsSet.has(edge.source)) return;
|
||||
if (childNodeIdsSet.has(edge.target)) return;
|
||||
|
||||
connectedNodeIds.add(edge.source);
|
||||
connectedNodeIds.add(edge.target);
|
||||
|
||||
dagreGraph.setEdge(edge.source, edge.target);
|
||||
});
|
||||
|
||||
dagre.layout(dagreGraph);
|
||||
const layoutedStartNode = dagreGraph.node(startNode.data.nodeId);
|
||||
const offsetX = startPosition.x - (layoutedStartNode.x - startNode.width! / 2);
|
||||
const offsetY = startPosition.y - (layoutedStartNode.y - startNode.height! / 2);
|
||||
|
||||
nodes.forEach((node) => {
|
||||
if (!connectedNodeIds.has(node.id) || childNodeIdsSet.has(node.data.nodeId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeWithPosition = dagreGraph.node(node.id);
|
||||
const targetX = nodeWithPosition.x - node.width! / 2 + offsetX;
|
||||
const targetY = nodeWithPosition.y - node.height! / 2 + offsetY;
|
||||
const diffX = targetX - node.position.x;
|
||||
const diffY = targetY - node.position.y;
|
||||
node.position = {
|
||||
x: targetX,
|
||||
y: targetY
|
||||
};
|
||||
|
||||
// Update child nodes position
|
||||
nodes.forEach((childNode) => {
|
||||
if (childNode.data.parentNodeId === node.data.nodeId) {
|
||||
childNode.position = {
|
||||
x: childNode.position.x + diffX,
|
||||
y: childNode.position.y + diffY
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
setNodes((nodes) => {
|
||||
let newNodes = cloneDeep(nodes);
|
||||
|
||||
setEdges((edges) => {
|
||||
const childNodesIdSet = new Set();
|
||||
|
||||
// 1. Layout child nodes
|
||||
const childNodesMap: Record<string, Node<FlowNodeItemType>[]> = {};
|
||||
newNodes.forEach((node) => {
|
||||
const parentId = node.data.parentNodeId;
|
||||
if (parentId) {
|
||||
childNodesIdSet.add(parentId);
|
||||
if (!childNodesMap[parentId]) {
|
||||
childNodesMap[parentId] = [];
|
||||
}
|
||||
childNodesMap[parentId].push(node);
|
||||
}
|
||||
});
|
||||
const childNodesArr = Object.values(childNodesMap);
|
||||
if (childNodesArr.length > 0) {
|
||||
childNodesArr.forEach((childNodes) => {
|
||||
updateChildNodesPosition({
|
||||
startNode: childNodes[0],
|
||||
nodes: childNodes,
|
||||
edges
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Reset parent node size and position
|
||||
const parentNodes = newNodes.filter((node) => childNodesIdSet.has(node.data.nodeId));
|
||||
parentNodes.forEach((node) => {
|
||||
const res = getParentNodeSizeAndPosition({
|
||||
nodes: newNodes,
|
||||
parentId: node.data.nodeId
|
||||
});
|
||||
if (!res) return;
|
||||
const { parentX, parentY, nodeWidth, nodeHeight, childHeight, childWidth } = res;
|
||||
|
||||
node.position = {
|
||||
x: parentX,
|
||||
y: parentY
|
||||
};
|
||||
node.width = nodeWidth;
|
||||
node.height = nodeHeight;
|
||||
node.data.inputs.forEach((input) => {
|
||||
if (input.key === NodeInputKeyEnum.nodeHeight) {
|
||||
input.value = childHeight;
|
||||
} else if (input.key === NodeInputKeyEnum.nodeWidth) {
|
||||
input.value = childWidth;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 3. Layout parent node
|
||||
updateParentNodesPosition({
|
||||
startNode:
|
||||
newNodes.find((node) =>
|
||||
[
|
||||
FlowNodeTypeEnum.systemConfig,
|
||||
FlowNodeTypeEnum.pluginConfig,
|
||||
FlowNodeTypeEnum.workflowStart,
|
||||
FlowNodeTypeEnum.pluginInput
|
||||
].includes(node.data.flowNodeType)
|
||||
) || newNodes[0],
|
||||
nodes: newNodes,
|
||||
edges
|
||||
});
|
||||
return edges;
|
||||
});
|
||||
|
||||
return newNodes;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
fitView();
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onAddComment = useCallback(() => {
|
||||
const newNode = nodeTemplate2FlowNode({
|
||||
template: CommentNode,
|
||||
position: screenToFlowPosition({ x: menu?.left ?? 0, y: (menu?.top ?? 0) + 100 }),
|
||||
t
|
||||
});
|
||||
|
||||
setNodes((state) => {
|
||||
const newState = state
|
||||
.map((node) => ({
|
||||
...node,
|
||||
selected: false
|
||||
}))
|
||||
// @ts-ignore
|
||||
.concat(newNode);
|
||||
return newState;
|
||||
});
|
||||
}, [menu]);
|
||||
|
||||
const onFold = useCallback(() => {
|
||||
setNodes((state) => {
|
||||
return state.map((node) => ({
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
isFolded: !allUnFolded
|
||||
}
|
||||
}));
|
||||
});
|
||||
}, [allUnFolded]);
|
||||
|
||||
const ContextMenuItem = useCallback(
|
||||
({
|
||||
icon,
|
||||
label,
|
||||
onClick,
|
||||
...props
|
||||
}: {
|
||||
icon: string;
|
||||
label: string;
|
||||
onClick: () => any;
|
||||
} & StackProps) => {
|
||||
return (
|
||||
<HStack
|
||||
px={2}
|
||||
py={1}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'sm'}
|
||||
_hover={{ bg: 'myGray.50', color: 'primary.500' }}
|
||||
onClick={() => {
|
||||
onClick();
|
||||
setMenu(null);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<MyIcon name={icon as any} w={'1rem'} ml={1} />
|
||||
<Box fontSize={'sm'} fontWeight={'500'}>
|
||||
{label}
|
||||
</Box>
|
||||
</HStack>
|
||||
);
|
||||
},
|
||||
[setMenu]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box position="relative">
|
||||
<Box
|
||||
position="absolute"
|
||||
@@ -55,61 +326,26 @@ const ContextMenu = () => {
|
||||
p={1}
|
||||
zIndex={10}
|
||||
>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
px={2}
|
||||
py={1}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'sm'}
|
||||
_hover={{ bg: 'myGray.50', color: 'primary.500' }}
|
||||
onClick={() => {
|
||||
setMenu(null);
|
||||
setNodes((state) => {
|
||||
const newState = state
|
||||
.map((node) => ({
|
||||
...node,
|
||||
selected: false
|
||||
}))
|
||||
// @ts-ignore
|
||||
.concat(newNode);
|
||||
return newState;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MyIcon name="comment" w={'1rem'} ml={1} />
|
||||
<Box fontSize={'12px'} fontWeight={'500'} ml={1.5}>
|
||||
{t('workflow:context_menu.add_comment')}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex
|
||||
mt={1}
|
||||
alignItems={'center'}
|
||||
px={2}
|
||||
py={1}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'sm'}
|
||||
_hover={{ bg: 'myGray.50', color: 'primary.500' }}
|
||||
onClick={() => {
|
||||
setMenu(null);
|
||||
setNodes((state) => {
|
||||
return state.map((node) => ({
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
isFolded: !allUnFolded
|
||||
}
|
||||
}));
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MyIcon name="common/select" w={'1rem'} ml={1} />
|
||||
<Box fontSize={'12px'} fontWeight={'500'} ml={1.5}>
|
||||
{allUnFolded ? t('workflow:unFoldAll') : t('workflow:foldAll')}
|
||||
</Box>
|
||||
</Flex>
|
||||
<ContextMenuItem
|
||||
mb={1}
|
||||
icon="alignLeft"
|
||||
label={t('workflow:auto_align')}
|
||||
onClick={onLayout}
|
||||
/>
|
||||
<ContextMenuItem
|
||||
mb={1}
|
||||
icon="comment"
|
||||
label={t('workflow:context_menu.add_comment')}
|
||||
onClick={onAddComment}
|
||||
/>
|
||||
<ContextMenuItem
|
||||
icon="common/select"
|
||||
label={allUnFolded ? t('workflow:unFoldAll') : t('workflow:foldAll')}
|
||||
onClick={onFold}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ContextMenu);
|
||||
|
||||
@@ -73,6 +73,7 @@ const Workflow = () => {
|
||||
WorkflowEventContext,
|
||||
(v) => v.workflowControlMode
|
||||
);
|
||||
const menu = useContextSelector(WorkflowEventContext, (v) => v.menu);
|
||||
|
||||
const {
|
||||
handleNodesChange,
|
||||
@@ -163,7 +164,7 @@ const Workflow = () => {
|
||||
: {})}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
>
|
||||
<ContextMenu />
|
||||
{!!menu && <ContextMenu />}
|
||||
<FlowController />
|
||||
<HelperLines horizontal={helperLineHorizontal} vertical={helperLineVertical} />
|
||||
</ReactFlow>
|
||||
|
||||
@@ -26,7 +26,7 @@ export const SelectDatasetRender = React.memo(function SelectDatasetRender({
|
||||
searchMode: DatasetSearchModeEnum.embedding,
|
||||
limit: 5,
|
||||
similarity: 0.5,
|
||||
usingReRank: false
|
||||
usingReRank: true
|
||||
});
|
||||
|
||||
const {
|
||||
|
||||
@@ -27,7 +27,7 @@ const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => {
|
||||
embeddingWeight: 0.5,
|
||||
limit: 3000,
|
||||
similarity: 0.5,
|
||||
usingReRank: false,
|
||||
usingReRank: true,
|
||||
rerankModel: defaultModels.llm?.model,
|
||||
rerankWeight: 0.6,
|
||||
datasetSearchUsingExtensionQuery: true,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AppContext } from '../../context';
|
||||
import { compareSnapshot } from '@/web/core/workflow/utils';
|
||||
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getNodesBounds, Node } from 'reactflow';
|
||||
import { Node } from 'reactflow';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import {
|
||||
@@ -20,6 +20,22 @@ type WorkflowStatusContextType = {
|
||||
isSaved: boolean;
|
||||
leaveSaveSign: React.MutableRefObject<boolean>;
|
||||
resetParentNodeSizeAndPosition: (parentId: string) => void;
|
||||
getParentNodeSizeAndPosition: ({
|
||||
nodes,
|
||||
parentId
|
||||
}: {
|
||||
nodes: Node<FlowNodeItemType>[];
|
||||
parentId: string;
|
||||
}) =>
|
||||
| {
|
||||
parentX: number;
|
||||
parentY: number;
|
||||
childWidth: number;
|
||||
childHeight: number;
|
||||
nodeWidth: number;
|
||||
nodeHeight: number;
|
||||
}
|
||||
| undefined;
|
||||
};
|
||||
|
||||
export const WorkflowStatusContext = createContext<WorkflowStatusContextType>({
|
||||
@@ -94,30 +110,70 @@ const WorkflowStatusContextProvider = ({ children }: { children: ReactNode }) =>
|
||||
|
||||
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (state) => state.onNodesChange);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const getParentNodeSizeAndPosition = useMemoizedFn(
|
||||
({ nodes, parentId }: { nodes: Node<FlowNodeItemType>[]; parentId: string }) => {
|
||||
const { childNodes, loopNode } = nodes.reduce(
|
||||
(acc, node) => {
|
||||
if (node.data.parentNodeId === parentId) {
|
||||
acc.childNodes.push(node);
|
||||
}
|
||||
if (node.id === parentId) {
|
||||
acc.loopNode = node;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ childNodes: [] as Node[], loopNode: undefined as Node<FlowNodeItemType> | undefined }
|
||||
);
|
||||
|
||||
if (!loopNode) return;
|
||||
const loopChilWidth =
|
||||
loopNode.data.inputs.find((node) => node.key === NodeInputKeyEnum.nodeWidth)?.value ?? 0;
|
||||
const loopChilHeight =
|
||||
loopNode.data.inputs.find((node) => node.key === NodeInputKeyEnum.nodeHeight)?.value ?? 0;
|
||||
|
||||
// 初始化为第一个节点的边界
|
||||
let minX = childNodes[0].position.x;
|
||||
let minY = childNodes[0].position.y;
|
||||
let maxX = childNodes[0].position.x + (childNodes[0].width || 0);
|
||||
let maxY = childNodes[0].position.y + (childNodes[0].height || 0);
|
||||
|
||||
// 遍历所有节点找出最小/最大边界
|
||||
childNodes.forEach((node) => {
|
||||
const nodeWidth = node.width || 0;
|
||||
const nodeHeight = node.height || 0;
|
||||
|
||||
minX = Math.min(minX, node.position.x);
|
||||
minY = Math.min(minY, node.position.y);
|
||||
maxX = Math.max(maxX, node.position.x + nodeWidth);
|
||||
maxY = Math.max(maxY, node.position.y + nodeHeight);
|
||||
});
|
||||
|
||||
const childWidth = Math.max(maxX - minX + 80, 840);
|
||||
const childHeight = Math.max(maxY - minY + 80, 600);
|
||||
|
||||
const diffWidth = childWidth - loopChilWidth;
|
||||
const diffHeight = childHeight - loopChilHeight;
|
||||
const targetNodeWidth = (loopNode.width ?? 0) + diffWidth;
|
||||
const targetNodeHeight = (loopNode.height ?? 0) + diffHeight;
|
||||
|
||||
const offsetHeight =
|
||||
loopNode.data.inputs.find((input) => input.key === NodeInputKeyEnum.loopNodeInputHeight)
|
||||
?.value ?? 83;
|
||||
|
||||
return {
|
||||
parentX: Math.round(minX - 70),
|
||||
parentY: Math.round(minY - offsetHeight - 240),
|
||||
childWidth,
|
||||
childHeight,
|
||||
nodeWidth: targetNodeWidth,
|
||||
nodeHeight: targetNodeHeight
|
||||
};
|
||||
}
|
||||
);
|
||||
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
|
||||
const { childNodes, loopNode } = nodes.reduce(
|
||||
(acc, node) => {
|
||||
if (node.data.parentNodeId === parentId) {
|
||||
acc.childNodes.push(node);
|
||||
}
|
||||
if (node.id === parentId) {
|
||||
acc.loopNode = node;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ childNodes: [] as Node[], loopNode: undefined as Node<FlowNodeItemType> | undefined }
|
||||
);
|
||||
|
||||
if (!loopNode) return;
|
||||
|
||||
const rect = getNodesBounds(childNodes);
|
||||
// Calculate parent node size with minimum width/height constraints
|
||||
const width = Math.max(rect.width + 80, 840);
|
||||
const height = Math.max(rect.height + 80, 600);
|
||||
|
||||
const offsetHeight =
|
||||
loopNode.data.inputs.find((input) => input.key === NodeInputKeyEnum.loopNodeInputHeight)
|
||||
?.value ?? 83;
|
||||
const res = getParentNodeSizeAndPosition({ nodes, parentId });
|
||||
if (!res) return;
|
||||
const { parentX, parentY, childWidth, childHeight } = res;
|
||||
|
||||
// Update parentNode size and position
|
||||
onChangeNode({
|
||||
@@ -126,7 +182,7 @@ const WorkflowStatusContextProvider = ({ children }: { children: ReactNode }) =>
|
||||
key: NodeInputKeyEnum.nodeWidth,
|
||||
value: {
|
||||
...Input_Template_Node_Width,
|
||||
value: width
|
||||
value: childWidth
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
@@ -135,7 +191,7 @@ const WorkflowStatusContextProvider = ({ children }: { children: ReactNode }) =>
|
||||
key: NodeInputKeyEnum.nodeHeight,
|
||||
value: {
|
||||
...Input_Template_Node_Height,
|
||||
value: height
|
||||
value: childHeight
|
||||
}
|
||||
});
|
||||
// Update parentNode position
|
||||
@@ -144,8 +200,8 @@ const WorkflowStatusContextProvider = ({ children }: { children: ReactNode }) =>
|
||||
id: parentId,
|
||||
type: 'position',
|
||||
position: {
|
||||
x: Math.round(rect.x - 70),
|
||||
y: Math.round(rect.y - offsetHeight - 240)
|
||||
x: parentX,
|
||||
y: parentY
|
||||
}
|
||||
}
|
||||
]);
|
||||
@@ -155,9 +211,10 @@ const WorkflowStatusContextProvider = ({ children }: { children: ReactNode }) =>
|
||||
return {
|
||||
isSaved,
|
||||
leaveSaveSign,
|
||||
resetParentNodeSizeAndPosition
|
||||
resetParentNodeSizeAndPosition,
|
||||
getParentNodeSizeAndPosition
|
||||
};
|
||||
}, [isSaved, resetParentNodeSizeAndPosition]);
|
||||
}, [isSaved, resetParentNodeSizeAndPosition, getParentNodeSizeAndPosition]);
|
||||
return (
|
||||
<WorkflowStatusContext.Provider value={contextValue}>{children}</WorkflowStatusContext.Provider>
|
||||
);
|
||||
|
||||
@@ -35,6 +35,7 @@ import { getAppFolderPath } from '@/web/core/app/api/app';
|
||||
import { AppFolderTypeList } from '@fastgpt/global/core/app/constants';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { postCreateMcpServer, putUpdateMcpServer } from '../../../web/support/mcp/api';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
|
||||
export type EditMcForm = {
|
||||
id?: string;
|
||||
@@ -62,7 +63,7 @@ const SelectAppModal = ({
|
||||
{
|
||||
appId: string;
|
||||
toolName: string;
|
||||
toolAlias: string;
|
||||
appName: string;
|
||||
avatar: string;
|
||||
description: string;
|
||||
}[]
|
||||
@@ -76,7 +77,7 @@ const SelectAppModal = ({
|
||||
data.map((item) => ({
|
||||
appId: item.id,
|
||||
toolName: item.name,
|
||||
toolAlias: item.name,
|
||||
appName: item.name,
|
||||
avatar: item.avatar,
|
||||
description: selectedApps.find((app) => app.appId === item.id)?.description || ''
|
||||
}))
|
||||
@@ -176,7 +177,7 @@ const SelectAppModal = ({
|
||||
{
|
||||
appId: item._id,
|
||||
toolName: item.name,
|
||||
toolAlias: item.name,
|
||||
appName: item.name,
|
||||
avatar: item.avatar,
|
||||
description: item.intro
|
||||
}
|
||||
@@ -281,7 +282,7 @@ const EditMcpModal = ({
|
||||
apps: data.apps.map((item) => ({
|
||||
appId: item.appId,
|
||||
toolName: item.toolName,
|
||||
toolAlias: item.toolAlias,
|
||||
appName: item.appName,
|
||||
description: item.description
|
||||
}))
|
||||
}),
|
||||
@@ -299,7 +300,7 @@ const EditMcpModal = ({
|
||||
apps: data.apps.map((item) => ({
|
||||
appId: item.appId,
|
||||
toolName: item.toolName,
|
||||
toolAlias: item.toolAlias,
|
||||
appName: item.appName,
|
||||
description: item.description
|
||||
}))
|
||||
}),
|
||||
@@ -317,7 +318,7 @@ const EditMcpModal = ({
|
||||
iconSrc="key"
|
||||
title={isEdit ? t('dashboard_mcp:edit_mcp') : t('dashboard_mcp:create_mcp')}
|
||||
w={'100%'}
|
||||
maxW={['90vw', '600px']}
|
||||
maxW={['90vw', '800px']}
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
>
|
||||
@@ -339,8 +340,11 @@ const EditMcpModal = ({
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>
|
||||
{t('dashboard_mcp:tool_name')}
|
||||
<QuestionTip label={t('dashboard_mcp:tool_name_tip')} />
|
||||
</Th>
|
||||
<Th>{t('dashboard_mcp:app_name')}</Th>
|
||||
<Th>{t('dashboard_mcp:app_tool_name')}</Th>
|
||||
<Th>{t('dashboard_mcp:app_description')}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
@@ -349,15 +353,15 @@ const EditMcpModal = ({
|
||||
{apps.map((app, index) => {
|
||||
return (
|
||||
<Tr key={app.id} fontWeight={500} fontSize={'mini'} color={'myGray.900'}>
|
||||
<Td>{app.toolName}</Td>
|
||||
<Td>
|
||||
<Input
|
||||
{...register(`apps.${index}.toolAlias`)}
|
||||
placeholder={app.toolName}
|
||||
{...register(`apps.${index}.toolName`, { required: true })}
|
||||
placeholder={t('dashboard_mcp:tool_name_placeholder')}
|
||||
bg={'myGray.50'}
|
||||
w={'100%'}
|
||||
/>
|
||||
</Td>
|
||||
<Td>{app.appName}</Td>
|
||||
<Td>
|
||||
<Input
|
||||
{...register(`apps.${index}.description`, { required: true })}
|
||||
@@ -413,7 +417,7 @@ const EditMcpModal = ({
|
||||
e.map((item) => ({
|
||||
appId: item.appId,
|
||||
toolName: item.toolName,
|
||||
toolAlias: item.toolAlias,
|
||||
appName: item.appName,
|
||||
description: item.description
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -71,7 +71,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
searchParams: {
|
||||
searchMode: DatasetSearchModeEnum.embedding,
|
||||
embeddingWeight: 0.5,
|
||||
usingReRank: false,
|
||||
usingReRank: true,
|
||||
rerankModel: defaultModels?.rerank?.model,
|
||||
rerankWeight: 0.5,
|
||||
limit: 5000,
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { Box, Flex, Grid, Button, VStack } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
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 { postCreatePayBill } from '@/web/support/wallet/bill/api';
|
||||
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
|
||||
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
const ExtraPlan = ({ onPaySuccess }: { onPaySuccess?: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { subPlans } = useSystemStore();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [qrPayData, setQRPayData] = useState<QRPayProps>();
|
||||
|
||||
// extra dataset
|
||||
@@ -26,40 +25,33 @@ const ExtraPlan = ({ onPaySuccess }: { onPaySuccess?: () => void }) => {
|
||||
month: 1
|
||||
}
|
||||
});
|
||||
const onclickBuyDatasetSize = useCallback(
|
||||
const { runAsync: onclickBuyDatasetSize, loading: isLoadingBuyDatasetSize } = useRequest2(
|
||||
async ({ datasetSize, month }: { datasetSize: number; month: number }) => {
|
||||
try {
|
||||
datasetSize = Math.ceil(datasetSize);
|
||||
month = Math.ceil(month);
|
||||
datasetSize = Math.ceil(datasetSize);
|
||||
month = Math.ceil(month);
|
||||
|
||||
const datasetSizePayAmount = datasetSize * month * extraDatasetPrice;
|
||||
if (datasetSizePayAmount === 0) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('common:support.wallet.amount_0')
|
||||
});
|
||||
}
|
||||
setLoading(true);
|
||||
|
||||
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'
|
||||
const datasetSizePayAmount = datasetSize * month * extraDatasetPrice;
|
||||
if (datasetSizePayAmount === 0) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('common:support.wallet.amount_0')
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
|
||||
const res = await postCreatePayBill({
|
||||
type: BillTypeEnum.extraDatasetSub,
|
||||
month,
|
||||
extraDatasetSize: datasetSize
|
||||
});
|
||||
setQRPayData({
|
||||
tip: t('common:button.extra_dataset_size_tip'),
|
||||
...res
|
||||
});
|
||||
},
|
||||
[extraDatasetPrice, toast]
|
||||
{
|
||||
manual: true,
|
||||
refreshDeps: [extraDatasetPrice]
|
||||
}
|
||||
);
|
||||
|
||||
// extra ai points
|
||||
@@ -70,41 +62,34 @@ const ExtraPlan = ({ onPaySuccess }: { onPaySuccess?: () => void }) => {
|
||||
month: 1
|
||||
}
|
||||
});
|
||||
const onclickBuyExtraPoints = useCallback(
|
||||
const { runAsync: onclickBuyExtraPoints, loading: isLoadingBuyExtraPoints } = useRequest2(
|
||||
async ({ points }: { points: number }) => {
|
||||
try {
|
||||
points = Math.ceil(points);
|
||||
points = Math.ceil(points);
|
||||
|
||||
const month = 1;
|
||||
const payAmount = points * month * extraPointsPrice;
|
||||
const month = 1;
|
||||
const payAmount = points * month * extraPointsPrice;
|
||||
|
||||
if (payAmount === 0) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('common:support.wallet.amount_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'
|
||||
if (payAmount === 0) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('common:support.wallet.amount_0')
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
|
||||
const res = await postCreatePayBill({
|
||||
type: BillTypeEnum.extraPoints,
|
||||
extraPoints: points
|
||||
});
|
||||
|
||||
setQRPayData({
|
||||
tip: t('common:button.extra_points_tip'),
|
||||
...res
|
||||
});
|
||||
},
|
||||
[extraPointsPrice, toast]
|
||||
{
|
||||
manual: true,
|
||||
refreshDeps: [extraPointsPrice]
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -184,7 +169,7 @@ const ExtraPlan = ({ onPaySuccess }: { onPaySuccess?: () => void }) => {
|
||||
mt={6}
|
||||
w={'100%'}
|
||||
variant={'primaryGhost'}
|
||||
isLoading={loading}
|
||||
isLoading={isLoadingBuyDatasetSize}
|
||||
onClick={handleSubmitDatasetSize(onclickBuyDatasetSize)}
|
||||
color={'primary.700'}
|
||||
>
|
||||
@@ -264,7 +249,7 @@ const ExtraPlan = ({ onPaySuccess }: { onPaySuccess?: () => void }) => {
|
||||
mt={6}
|
||||
w={'100%'}
|
||||
variant={'primaryGhost'}
|
||||
isLoading={loading}
|
||||
isLoading={isLoadingBuyExtraPoints}
|
||||
onClick={handleSubmitExtraPoints(onclickBuyExtraPoints)}
|
||||
color={'primary.700'}
|
||||
>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constant
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
|
||||
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
|
||||
import { getWxPayQRCode } from '@/web/support/wallet/bill/api';
|
||||
import { postCreatePayBill } from '@/web/support/wallet/bill/api';
|
||||
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList';
|
||||
|
||||
@@ -53,9 +53,9 @@ const Standard = ({
|
||||
permissionCustomApiKey: value.permissionCustomApiKey,
|
||||
permissionCustomCopyright: value.permissionCustomCopyright,
|
||||
trainingWeight: value.trainingWeight,
|
||||
permissionReRank: value.permissionReRank,
|
||||
totalPoints: value.totalPoints * (selectSubMode === SubModeEnum.month ? 1 : 12),
|
||||
permissionWebsiteSync: value.permissionWebsiteSync
|
||||
permissionWebsiteSync: value.permissionWebsiteSync,
|
||||
permissionTeamOperationLog: value.permissionTeamOperationLog
|
||||
};
|
||||
})
|
||||
: [];
|
||||
@@ -65,13 +65,9 @@ const Standard = ({
|
||||
const [qrPayData, setQRPayData] = useState<QRPayProps>();
|
||||
|
||||
/* Get pay code */
|
||||
const { runAsync: onPay, loading: isLoading } = useRequest2(getWxPayQRCode, {
|
||||
const { runAsync: onPay, loading: isLoading } = useRequest2(postCreatePayBill, {
|
||||
onSuccess(res) {
|
||||
setQRPayData({
|
||||
readPrice: res.readPrice,
|
||||
codeUrl: res.codeUrl,
|
||||
billId: res.billId
|
||||
});
|
||||
setQRPayData(res);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
||||
import { putUpdateMemberName } from '@/web/support/user/team/api';
|
||||
import { putUpdateMemberName, redeemCoupon } from '@/web/support/user/team/api';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import {
|
||||
StandardSubLevelEnum,
|
||||
@@ -47,6 +47,9 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useMount } from 'ahooks';
|
||||
import MyDivider from '@fastgpt/web/components/common/MyDivider';
|
||||
|
||||
const RedeemCouponModal = dynamic(() => import('@/pageComponents/account/info/RedeemCouponModal'), {
|
||||
ssr: false
|
||||
});
|
||||
const StandDetailModal = dynamic(
|
||||
() => import('@/pageComponents/account/info/standardDetailModal'),
|
||||
{ ssr: false }
|
||||
@@ -353,8 +356,8 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
||||
const PlanUsage = () => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { userInfo, initUserInfo, teamPlanStatus } = useUserStore();
|
||||
const { subPlans } = useSystemStore();
|
||||
const { userInfo, initUserInfo, teamPlanStatus, initTeamPlanStatus } = useUserStore();
|
||||
const { subPlans, feConfigs } = useSystemStore();
|
||||
const { reset } = useForm<UserUpdateParams>({
|
||||
defaultValues: userInfo as UserType
|
||||
});
|
||||
@@ -365,6 +368,12 @@ const PlanUsage = () => {
|
||||
onOpen: onOpenStandardModal
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
isOpen: isOpenRedeemCouponModal,
|
||||
onClose: onCloseRedeemCouponModal,
|
||||
onOpen: onOpenRedeemCouponModal
|
||||
} = useDisclosure();
|
||||
|
||||
const planName = useMemo(() => {
|
||||
if (!teamPlanStatus?.standard?.currentSubLevel) return '';
|
||||
return standardSubLevelMap[teamPlanStatus.standard.currentSubLevel].label;
|
||||
@@ -460,14 +469,19 @@ const PlanUsage = () => {
|
||||
</Flex>
|
||||
<ModelPriceModal>
|
||||
{({ onOpen }) => (
|
||||
<Button ml={4} size={'sm'} onClick={onOpen}>
|
||||
<Button ml={3} size={'sm'} onClick={onOpen}>
|
||||
{t('account_info:billing_standard')}
|
||||
</Button>
|
||||
)}
|
||||
</ModelPriceModal>
|
||||
<Button ml={4} variant={'whitePrimary'} size={'sm'} onClick={onOpenStandardModal}>
|
||||
<Button ml={3} variant={'whitePrimary'} size={'sm'} onClick={onOpenStandardModal}>
|
||||
{t('account_info:package_details')}
|
||||
</Button>
|
||||
{userInfo?.permission.isOwner && feConfigs?.show_coupon && (
|
||||
<Button ml={3} variant={'whitePrimary'} size={'sm'} onClick={onOpenRedeemCouponModal}>
|
||||
{t('account_info:redeem_coupon')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
<Box
|
||||
mt={[3, 6]}
|
||||
@@ -603,6 +617,12 @@ const PlanUsage = () => {
|
||||
</Box>
|
||||
</Box>
|
||||
{isOpenStandardModal && <StandDetailModal onClose={onCloseStandardModal} />}
|
||||
{isOpenRedeemCouponModal && (
|
||||
<RedeemCouponModal
|
||||
onClose={onCloseRedeemCouponModal}
|
||||
onSuccess={() => initTeamPlanStatus()}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,8 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { TeamContext, TeamModalContextProvider } from '@/pageComponents/account/team/context';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
const MemberTable = dynamic(() => import('@/pageComponents/account/team/MemberTable'));
|
||||
const PermissionManage = dynamic(
|
||||
@@ -48,7 +50,19 @@ const Team = () => {
|
||||
const { teamTab = TeamTabEnum.member } = router.query as { teamTab: `${TeamTabEnum}` };
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
const { userInfo, teamPlanStatus } = useUserStore();
|
||||
const standardPlan = teamPlanStatus?.standard;
|
||||
const level = standardPlan?.currentSubLevel;
|
||||
const { subPlans } = useSystemStore();
|
||||
const planContent = useMemo(() => {
|
||||
const plan = level !== undefined ? subPlans?.standard?.[level] : undefined;
|
||||
|
||||
if (!plan) return;
|
||||
return {
|
||||
permissionTeamOperationLog: plan.permissionTeamOperationLog
|
||||
};
|
||||
}, [subPlans?.standard, level]);
|
||||
const { toast } = useToast();
|
||||
|
||||
const { setEditTeamData, teamSize } = useContextSelector(TeamContext, (v) => v);
|
||||
|
||||
@@ -65,6 +79,13 @@ const Team = () => {
|
||||
px={'1rem'}
|
||||
value={teamTab}
|
||||
onChange={(e) => {
|
||||
if (e === TeamTabEnum.operationLog && !planContent?.permissionTeamOperationLog) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('common:not_permission')
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorDB/pg/controller';
|
||||
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { MongoDatasetDataText } from '@fastgpt/service/core/dataset/data/dataTex
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller';
|
||||
import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorDB/controller';
|
||||
|
||||
// 删了库,没删集合
|
||||
const checkInvalidCollection = async () => {
|
||||
|
||||
@@ -5,8 +5,8 @@ import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection
|
||||
import { DatasetCollectionDataProcessModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/data/constants';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
import { PG_ADDRESS } from '@fastgpt/service/common/vectorStore/constants';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorDB/pg/controller';
|
||||
import { PG_ADDRESS } from '@fastgpt/service/common/vectorDB/constants';
|
||||
|
||||
// 所有 trainingType=auto 的 collection,都改成 trainingType=chunk
|
||||
const updateCollections = async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { MilvusCtrl } from '@fastgpt/service/common/vectorStore/milvus/class';
|
||||
import { DatasetVectorTableName } from '@fastgpt/service/common/vectorStore/constants';
|
||||
import { MilvusCtrl } from '@fastgpt/service/common/vectorDB/milvus/index';
|
||||
import { DatasetVectorTableName } from '@fastgpt/service/common/vectorDB/constants';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
|
||||
@@ -63,14 +63,6 @@ export type Props = {
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.on('close', () => {
|
||||
res.end();
|
||||
});
|
||||
res.on('error', () => {
|
||||
console.log('error: ', 'request error');
|
||||
res.end();
|
||||
});
|
||||
|
||||
let {
|
||||
nodes = [],
|
||||
edges = [],
|
||||
@@ -170,39 +162,40 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
/* start process */
|
||||
const { flowResponses, assistantResponses, newVariables, flowUsages } = await dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'test',
|
||||
timezone,
|
||||
externalProvider,
|
||||
uid: tmbId,
|
||||
const { flowResponses, assistantResponses, newVariables, flowUsages, durationSeconds } =
|
||||
await dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'test',
|
||||
timezone,
|
||||
externalProvider,
|
||||
uid: tmbId,
|
||||
|
||||
runningAppInfo: {
|
||||
id: appId,
|
||||
teamId: app.teamId,
|
||||
tmbId: app.tmbId
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
runningAppInfo: {
|
||||
id: appId,
|
||||
teamId: app.teamId,
|
||||
tmbId: app.tmbId
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream: true,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite,
|
||||
version: 'v2',
|
||||
responseDetail: true
|
||||
});
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream: true,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite,
|
||||
version: 'v2',
|
||||
responseDetail: true
|
||||
});
|
||||
|
||||
workflowResponseWrite({
|
||||
event: SseResponseEventEnum.answer,
|
||||
@@ -238,7 +231,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables
|
||||
newVariables,
|
||||
durationSeconds
|
||||
});
|
||||
} else {
|
||||
await saveChat({
|
||||
@@ -252,7 +246,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
isUpdateUseTime: false, // owner update use time
|
||||
newTitle,
|
||||
source: ChatSourceEnum.test,
|
||||
content: [userQuestion, aiResponse]
|
||||
content: [userQuestion, aiResponse],
|
||||
durationSeconds
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -53,11 +53,11 @@ async function handler(
|
||||
const isOutLink = authType === GetChatTypeEnum.outLink;
|
||||
|
||||
const fieldMap = {
|
||||
[GetChatTypeEnum.normal]: `dataId obj value adminFeedback userBadFeedback userGoodFeedback time hideInUI ${
|
||||
[GetChatTypeEnum.normal]: `dataId obj value adminFeedback userBadFeedback userGoodFeedback time hideInUI durationSeconds ${
|
||||
DispatchNodeResponseKeyEnum.nodeResponse
|
||||
} ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`,
|
||||
[GetChatTypeEnum.outLink]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI ${DispatchNodeResponseKeyEnum.nodeResponse}`,
|
||||
[GetChatTypeEnum.team]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI ${DispatchNodeResponseKeyEnum.nodeResponse}`
|
||||
[GetChatTypeEnum.outLink]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI durationSeconds ${DispatchNodeResponseKeyEnum.nodeResponse}`,
|
||||
[GetChatTypeEnum.team]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI durationSeconds ${DispatchNodeResponseKeyEnum.nodeResponse}`
|
||||
};
|
||||
|
||||
const { total, histories } = await getChatItems({
|
||||
|
||||
@@ -11,9 +11,8 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { DatasetCollectionItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { collectionTagsToTagLabel } from '@fastgpt/service/core/dataset/collection/utils';
|
||||
import { getVectorCountByCollectionId } from '@fastgpt/service/common/vectorStore/controller';
|
||||
import { getVectorCountByCollectionId } from '@fastgpt/service/common/vectorDB/controller';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
import { Types } from 'mongoose';
|
||||
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
|
||||
|
||||
async function handler(req: NextApiRequest): Promise<DatasetCollectionItemType> {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import type { SearchTestProps, SearchTestResponse } from '@/global/core/dataset/api.d';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
||||
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
|
||||
import { pushGenerateVectorUsage, pushRerankUsage } from '@/service/support/wallet/usage/push';
|
||||
import {
|
||||
deepRagSearch,
|
||||
defaultSearchDatasetData
|
||||
} from '@fastgpt/service/core/dataset/search/controller';
|
||||
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
|
||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
import {
|
||||
checkTeamAIPoints,
|
||||
checkTeamReRankPermission
|
||||
} from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
@@ -58,6 +55,8 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
// auth balance
|
||||
await checkTeamAIPoints(teamId);
|
||||
|
||||
const rerankModelData = getRerankModel(rerankModel);
|
||||
|
||||
const searchData = {
|
||||
histories: [],
|
||||
teamId,
|
||||
@@ -69,11 +68,19 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
datasetIds: [datasetId],
|
||||
searchMode,
|
||||
embeddingWeight,
|
||||
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)),
|
||||
rerankModel: getRerankModel(rerankModel),
|
||||
usingReRank,
|
||||
rerankModel: rerankModelData,
|
||||
rerankWeight
|
||||
};
|
||||
const { searchRes, tokens, queryExtensionResult, deepSearchResult, ...result } = datasetDeepSearch
|
||||
const {
|
||||
searchRes,
|
||||
embeddingTokens,
|
||||
reRankInputTokens,
|
||||
usingReRank: searchUsingReRank,
|
||||
queryExtensionResult,
|
||||
deepSearchResult,
|
||||
...result
|
||||
} = datasetDeepSearch
|
||||
? await deepRagSearch({
|
||||
...searchData,
|
||||
datasetDeepSearchModel,
|
||||
@@ -88,10 +95,10 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
});
|
||||
|
||||
// push bill
|
||||
const { totalPoints } = pushGenerateVectorUsage({
|
||||
const { totalPoints: embeddingTotalPoints } = pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
inputTokens: tokens,
|
||||
inputTokens: reRankInputTokens,
|
||||
model: dataset.vectorModel,
|
||||
source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt,
|
||||
|
||||
@@ -106,10 +113,19 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
deepSearchOutputTokens: deepSearchResult.outputTokens
|
||||
})
|
||||
});
|
||||
const { totalPoints: reRankTotalPoints } = searchUsingReRank
|
||||
? pushRerankUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
inputTokens: reRankInputTokens,
|
||||
model: rerankModelData.model
|
||||
})
|
||||
: { totalPoints: 0 };
|
||||
|
||||
if (apikey) {
|
||||
updateApiKeyUsage({
|
||||
apikey,
|
||||
totalPoints: totalPoints
|
||||
totalPoints: embeddingTotalPoints + reRankTotalPoints
|
||||
});
|
||||
}
|
||||
|
||||
@@ -117,6 +133,7 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
|
||||
list: searchRes,
|
||||
duration: `${((Date.now() - start) / 1000).toFixed(3)}s`,
|
||||
queryExtensionModel: queryExtensionResult?.model,
|
||||
usingReRank: searchUsingReRank,
|
||||
...result
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ToolType } from '@fastgpt/global/core/app/type';
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import getMCPClient from '@fastgpt/service/core/app/mcp';
|
||||
import { MCPClient } from '@fastgpt/service/core/app/mcp';
|
||||
|
||||
export type getMCPToolsQuery = {};
|
||||
|
||||
@@ -15,14 +15,9 @@ async function handler(
|
||||
): Promise<getMCPToolsResponse> {
|
||||
const { url } = req.body;
|
||||
|
||||
const mcpClient = getMCPClient({ url });
|
||||
const mcpClient = new MCPClient({ url });
|
||||
|
||||
try {
|
||||
const tools = await mcpClient.getTools();
|
||||
return tools;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return mcpClient.getTools();
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import getMCPClient from '@fastgpt/service/core/app/mcp';
|
||||
import { MCPClient } from '@fastgpt/service/core/app/mcp';
|
||||
|
||||
export type RunMCPToolQuery = {};
|
||||
|
||||
@@ -18,14 +18,9 @@ async function handler(
|
||||
): Promise<RunMCPToolResponse> {
|
||||
const { url, toolName, params } = req.body;
|
||||
|
||||
const mcpClient = getMCPClient({ url });
|
||||
const mcpClient = new MCPClient({ url });
|
||||
|
||||
try {
|
||||
const result = await mcpClient.toolCall(toolName, params);
|
||||
return result;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return mcpClient.toolCall(toolName, params);
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
@@ -85,30 +85,31 @@ const dispatchApp = async (app: AppSchema, variables: Record<string, any>) => {
|
||||
|
||||
const chatId = getNanoid();
|
||||
|
||||
const { flowUsages, assistantResponses, newVariables, flowResponses } = await dispatchWorkFlow({
|
||||
chatId,
|
||||
timezone,
|
||||
externalProvider,
|
||||
mode: 'chat',
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
uid: String(app.tmbId),
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
chatConfig,
|
||||
histories: [],
|
||||
stream: false,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
||||
});
|
||||
const { flowUsages, assistantResponses, newVariables, flowResponses, durationSeconds } =
|
||||
await dispatchWorkFlow({
|
||||
chatId,
|
||||
timezone,
|
||||
externalProvider,
|
||||
mode: 'chat',
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
uid: String(app.tmbId),
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
chatConfig,
|
||||
histories: [],
|
||||
stream: false,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
||||
});
|
||||
|
||||
// Save chat
|
||||
const aiResponse: AIChatItemType & { dataId?: string } = {
|
||||
@@ -128,7 +129,8 @@ const dispatchApp = async (app: AppSchema, variables: Record<string, any>) => {
|
||||
isUpdateUseTime: false, // owner update use time
|
||||
newTitle,
|
||||
source: ChatSourceEnum.mcp,
|
||||
content: [userQuestion, aiResponse]
|
||||
content: [userQuestion, aiResponse],
|
||||
durationSeconds
|
||||
});
|
||||
|
||||
// Push usage
|
||||
@@ -184,7 +186,7 @@ async function handler(
|
||||
const app = appList.find((app) => {
|
||||
const mcpApp = mcp.apps.find((mcpApp) => String(mcpApp.appId) === String(app._id))!;
|
||||
|
||||
return toolName === mcpApp.toolAlias || toolName === mcpApp.toolName;
|
||||
return toolName === mcpApp.toolName;
|
||||
});
|
||||
|
||||
if (!app) {
|
||||
|
||||
@@ -8,10 +8,10 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller';
|
||||
import { Tool } from '@modelcontextprotocol/sdk/types';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
|
||||
export type listToolsQuery = { key: string };
|
||||
|
||||
@@ -19,7 +19,9 @@ export type listToolsBody = {};
|
||||
|
||||
export type listToolsResponse = {};
|
||||
|
||||
const pluginNodes2InputSchema = (nodes: StoreNodeItemType[]) => {
|
||||
export const pluginNodes2InputSchema = (
|
||||
nodes: { flowNodeType: FlowNodeTypeEnum; inputs: FlowNodeInputItemType[] }[]
|
||||
) => {
|
||||
const pluginInput = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput);
|
||||
|
||||
const schema: Tool['inputSchema'] = {
|
||||
@@ -47,7 +49,10 @@ const pluginNodes2InputSchema = (nodes: StoreNodeItemType[]) => {
|
||||
|
||||
return schema;
|
||||
};
|
||||
const workflow2InputSchema = (chatConfig?: AppChatConfigType) => {
|
||||
export const workflow2InputSchema = (chatConfig?: {
|
||||
fileSelectConfig?: AppChatConfigType['fileSelectConfig'];
|
||||
variables?: AppChatConfigType['variables'];
|
||||
}) => {
|
||||
const schema: Tool['inputSchema'] = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -138,7 +143,7 @@ async function handler(
|
||||
);
|
||||
|
||||
return {
|
||||
name: mcpApp.toolAlias || mcpApp.toolName,
|
||||
name: mcpApp.toolName,
|
||||
description: mcpApp.description,
|
||||
inputSchema: isPlugin
|
||||
? pluginNodes2InputSchema(version.nodes)
|
||||
@@ -150,5 +155,3 @@ async function handler(
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
export { pluginNodes2InputSchema, workflow2InputSchema, handler };
|
||||
|
||||
@@ -56,15 +56,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
...req.body
|
||||
});
|
||||
|
||||
// auth app
|
||||
// const app = await MongoApp.findById(appId, 'modules').lean();
|
||||
// if (!app) {
|
||||
// throw new Error('app not found');
|
||||
// }
|
||||
// if (!whisperConfig?.open) {
|
||||
// throw new Error('Whisper is not open in the app');
|
||||
// }
|
||||
|
||||
const result = await aiTranscriptions({
|
||||
model: getDefaultSTTModel(),
|
||||
fileStream: fs.createReadStream(file.path)
|
||||
@@ -73,7 +64,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
pushWhisperUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
duration
|
||||
duration: result?.usage?.total_tokens || duration
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
|
||||
@@ -93,14 +93,6 @@ type AuthResponseType = {
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.on('close', () => {
|
||||
res.end();
|
||||
});
|
||||
res.on('error', () => {
|
||||
console.log('error: ', 'request error');
|
||||
res.end();
|
||||
});
|
||||
|
||||
let {
|
||||
chatId,
|
||||
appId,
|
||||
@@ -266,42 +258,43 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
/* start flow controller */
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'chat',
|
||||
timezone,
|
||||
externalProvider,
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables, durationSeconds } =
|
||||
await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'chat',
|
||||
timezone,
|
||||
externalProvider,
|
||||
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
uid: String(outLinkUserId || tmbId),
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
uid: String(outLinkUserId || tmbId),
|
||||
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite
|
||||
});
|
||||
}
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite
|
||||
});
|
||||
}
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
|
||||
// save chat
|
||||
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
|
||||
@@ -339,7 +332,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables
|
||||
newVariables,
|
||||
durationSeconds
|
||||
});
|
||||
} else {
|
||||
await saveChat({
|
||||
@@ -360,7 +354,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
metadata: {
|
||||
originIp,
|
||||
...metadata
|
||||
}
|
||||
},
|
||||
durationSeconds
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -93,14 +93,6 @@ type AuthResponseType = {
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.on('close', () => {
|
||||
res.end();
|
||||
});
|
||||
res.on('error', () => {
|
||||
console.log('error: ', 'request error');
|
||||
res.end();
|
||||
});
|
||||
|
||||
let {
|
||||
chatId,
|
||||
appId,
|
||||
@@ -265,44 +257,46 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
/* start flow controller */
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'chat',
|
||||
timezone,
|
||||
externalProvider,
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables, durationSeconds } =
|
||||
await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
requestOrigin: req.headers.origin,
|
||||
mode: 'chat',
|
||||
timezone,
|
||||
externalProvider,
|
||||
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
uid: String(outLinkUserId || tmbId),
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId,
|
||||
tmbId
|
||||
},
|
||||
uid: String(outLinkUserId || tmbId),
|
||||
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite,
|
||||
version: 'v2',
|
||||
responseDetail
|
||||
});
|
||||
}
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes,
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges, interactive),
|
||||
variables,
|
||||
query: removeEmptyUserInput(userQuestion.value),
|
||||
lastInteractive: interactive,
|
||||
chatConfig,
|
||||
histories: newHistories,
|
||||
stream,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES,
|
||||
workflowStreamResponse: workflowResponseWrite,
|
||||
version: 'v2',
|
||||
responseAllData,
|
||||
responseDetail
|
||||
});
|
||||
}
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
|
||||
// save chat
|
||||
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
|
||||
@@ -340,7 +334,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables
|
||||
newVariables,
|
||||
durationSeconds
|
||||
});
|
||||
} else {
|
||||
await saveChat({
|
||||
@@ -361,7 +356,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
metadata: {
|
||||
originIp,
|
||||
...metadata
|
||||
}
|
||||
},
|
||||
durationSeconds
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import {
|
||||
deleteDatasetDataVector,
|
||||
getVectorDataByTime
|
||||
} from '@fastgpt/service/common/vectorStore/controller';
|
||||
} from '@fastgpt/service/common/vectorDB/controller';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { MongoDatasetDataText } from '@fastgpt/service/core/dataset/data/dataTextSchema';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
|
||||
@@ -38,8 +38,9 @@ export const readConfigData = async (name: string) => {
|
||||
}
|
||||
return `data/${name}`;
|
||||
}
|
||||
// production path
|
||||
return `/app/data/${name}`;
|
||||
// Fallback to default production path
|
||||
const envPath = process.env.CONFIG_JSON_PATH || '/app/data';
|
||||
return `${envPath}/${name}`;
|
||||
})();
|
||||
|
||||
const content = await fs.promises.readFile(filename, 'utf-8');
|
||||
@@ -126,7 +127,8 @@ export async function initSystemConfig() {
|
||||
...defaultFeConfigs,
|
||||
...(dbConfig.feConfigs || {}),
|
||||
isPlus: !!FastGPTProUrl,
|
||||
show_aiproxy: !!process.env.AIPROXY_API_ENDPOINT
|
||||
show_aiproxy: !!process.env.AIPROXY_API_ENDPOINT,
|
||||
show_coupon: process.env.SHOW_COUPON === 'true'
|
||||
},
|
||||
systemEnv: {
|
||||
...fileRes.systemEnv,
|
||||
|
||||
@@ -62,32 +62,34 @@ export const getScheduleTriggerApp = async () => {
|
||||
}
|
||||
];
|
||||
|
||||
const { flowUsages, assistantResponses, flowResponses } = await retryFn(() => {
|
||||
return dispatchWorkFlow({
|
||||
chatId,
|
||||
timezone,
|
||||
externalProvider,
|
||||
mode: 'chat',
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
uid: String(app.tmbId),
|
||||
runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)),
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges),
|
||||
variables: {},
|
||||
query: userQuery,
|
||||
chatConfig,
|
||||
histories: [],
|
||||
stream: false,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
||||
});
|
||||
});
|
||||
const { flowUsages, assistantResponses, flowResponses, durationSeconds } = await retryFn(
|
||||
() => {
|
||||
return dispatchWorkFlow({
|
||||
chatId,
|
||||
timezone,
|
||||
externalProvider,
|
||||
mode: 'chat',
|
||||
runningAppInfo: {
|
||||
id: String(app._id),
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
runningUserInfo: {
|
||||
teamId: String(app.teamId),
|
||||
tmbId: String(app.tmbId)
|
||||
},
|
||||
uid: String(app.tmbId),
|
||||
runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)),
|
||||
runtimeEdges: storeEdges2RuntimeEdges(edges),
|
||||
variables: {},
|
||||
query: userQuery,
|
||||
chatConfig,
|
||||
histories: [],
|
||||
stream: false,
|
||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Save chat
|
||||
await saveChat({
|
||||
@@ -111,7 +113,8 @@ export const getScheduleTriggerApp = async () => {
|
||||
value: assistantResponses,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: flowResponses
|
||||
}
|
||||
]
|
||||
],
|
||||
durationSeconds
|
||||
});
|
||||
createChatUsage({
|
||||
appName: app.name,
|
||||
|
||||
@@ -4,9 +4,9 @@ import {
|
||||
PatchIndexesProps,
|
||||
UpdateDatasetDataProps
|
||||
} from '@fastgpt/global/core/dataset/controller';
|
||||
import { insertDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller';
|
||||
import { insertDatasetDataVector } from '@fastgpt/service/common/vectorDB/controller';
|
||||
import { jiebaSplit } from '@fastgpt/service/common/string/jieba/index';
|
||||
import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller';
|
||||
import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorDB/controller';
|
||||
import { DatasetDataIndexItemType, DatasetDataItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { getEmbeddingModel, getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import {
|
||||
deleteDatasetDataVector,
|
||||
insertDatasetDataVector
|
||||
} from '@fastgpt/service/common/vectorStore/controller';
|
||||
} from '@fastgpt/service/common/vectorDB/controller';
|
||||
import { getEmbeddingModel } from '@fastgpt/service/core/ai/model';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type';
|
||||
|
||||
@@ -279,3 +279,39 @@ export const pushWhisperUsage = ({
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
export const pushRerankUsage = ({
|
||||
teamId,
|
||||
tmbId,
|
||||
model,
|
||||
inputTokens
|
||||
}: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
model: string;
|
||||
inputTokens: number;
|
||||
}) => {
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model,
|
||||
inputTokens,
|
||||
modelType: ModelTypeEnum.rerank
|
||||
});
|
||||
|
||||
createUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
appName: modelName,
|
||||
totalPoints,
|
||||
source: UsageSourceEnum.fastgpt,
|
||||
list: [
|
||||
{
|
||||
moduleName: modelName,
|
||||
amount: totalPoints,
|
||||
model: modelName,
|
||||
inputTokens
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return { totalPoints };
|
||||
};
|
||||
|
||||
@@ -215,11 +215,6 @@ export const streamFetch = ({
|
||||
event,
|
||||
...parseJson
|
||||
});
|
||||
} else if (event === SseResponseEventEnum.flowNodeStatus) {
|
||||
onMessage({
|
||||
event,
|
||||
...parseJson
|
||||
});
|
||||
} else if (event === SseResponseEventEnum.flowNodeResponse) {
|
||||
onMessage({
|
||||
event,
|
||||
@@ -240,6 +235,15 @@ export const streamFetch = ({
|
||||
useSystemStore.getState().setNotSufficientModalType(TeamErrEnum.aiPointsNotEnough);
|
||||
}
|
||||
errMsg = getErrText(parseJson, '流响应错误');
|
||||
} else if (
|
||||
[SseResponseEventEnum.workflowDuration, SseResponseEventEnum.flowNodeStatus].includes(
|
||||
event as any
|
||||
)
|
||||
) {
|
||||
onMessage({
|
||||
event,
|
||||
...parseJson
|
||||
});
|
||||
}
|
||||
},
|
||||
onclose() {
|
||||
|
||||
@@ -167,13 +167,25 @@ export const useSpeech = (props?: OutLinkChatAuthProps & { appId?: string }) =>
|
||||
if (MediaRecorder.isTypeSupported('video/webm; codecs=vp9')) {
|
||||
return {
|
||||
options: { mimeType: 'video/webm; codecs=vp9' },
|
||||
filename: 'recording.mp3'
|
||||
filename: 'recording.webm'
|
||||
};
|
||||
}
|
||||
if (MediaRecorder.isTypeSupported('video/webm')) {
|
||||
return {
|
||||
options: { type: 'video/webm' },
|
||||
filename: 'recording.mp3'
|
||||
filename: 'recording.webm'
|
||||
};
|
||||
}
|
||||
if (MediaRecorder.isTypeSupported('audio/webm')) {
|
||||
return {
|
||||
options: { mimeType: 'audio/webm' },
|
||||
filename: 'recording.webm'
|
||||
};
|
||||
}
|
||||
if (MediaRecorder.isTypeSupported('audio/mp4')) {
|
||||
return {
|
||||
options: { mimeType: 'audio/mp4' },
|
||||
filename: 'recording.m4a'
|
||||
};
|
||||
}
|
||||
if (MediaRecorder.isTypeSupported('video/mp4')) {
|
||||
@@ -182,9 +194,16 @@ export const useSpeech = (props?: OutLinkChatAuthProps & { appId?: string }) =>
|
||||
filename: 'recording.mp4'
|
||||
};
|
||||
}
|
||||
if (MediaRecorder.isTypeSupported('audio/mp3')) {
|
||||
return {
|
||||
options: { mimeType: 'audio/mp3' },
|
||||
filename: 'recording.mp3'
|
||||
};
|
||||
}
|
||||
// 默认回退选项
|
||||
return {
|
||||
options: { type: 'video/webm' },
|
||||
filename: 'recording.mp3'
|
||||
options: { type: 'audio/webm' },
|
||||
filename: 'recording.webm'
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export enum EventNameEnum {
|
||||
sendQuestion = 'sendQuestion',
|
||||
editQuestion = 'editQuestion'
|
||||
editQuestion = 'editQuestion',
|
||||
openQuoteReader = 'openQuoteReader'
|
||||
}
|
||||
|
||||
export const eventBus = {
|
||||
|
||||
@@ -104,6 +104,9 @@ export const getTeamPlanStatus = () =>
|
||||
export const getTeamPlans = () =>
|
||||
GET<TeamSubSchema[]>(`/proApi/support/user/team/plan/getTeamPlans`);
|
||||
|
||||
export const redeemCoupon = (couponCode: string) =>
|
||||
GET(`/proApi/support/wallet/coupon/redeem`, { key: couponCode });
|
||||
|
||||
export const getTeamInvoiceHeader = () =>
|
||||
GET<TeamInvoiceHeaderType>(`/proApi/support/user/team/invoiceAccount/getTeamInvoiceHeader`);
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { GET, POST } from '@/web/common/api/request';
|
||||
import type { CreateBillProps, CreateBillResponse } from '@fastgpt/global/support/wallet/bill/api';
|
||||
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { GET, POST, PUT } from '@/web/common/api/request';
|
||||
import type {
|
||||
CheckPayResultResponse,
|
||||
CreateBillProps,
|
||||
CreateBillResponse,
|
||||
CreateOrderResponse,
|
||||
UpdatePaymentProps
|
||||
} from '@fastgpt/global/support/wallet/bill/api';
|
||||
import { BillStatusEnum, BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import type { BillSchemaType } from '@fastgpt/global/support/wallet/bill/type.d';
|
||||
import type { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
|
||||
|
||||
@@ -10,15 +16,22 @@ export const getBills = (
|
||||
}>
|
||||
) => POST<PaginationResponse<BillSchemaType>>(`/proApi/support/wallet/bill/list`, data);
|
||||
|
||||
export const getWxPayQRCode = (data: CreateBillProps) =>
|
||||
export const postCreatePayBill = (data: CreateBillProps) =>
|
||||
POST<CreateBillResponse>(`/proApi/support/wallet/bill/create`, data);
|
||||
|
||||
export const checkBalancePayResult = (payId: string) =>
|
||||
GET<string>(`/proApi/support/wallet/bill/checkPayResult`, { payId }).then((data) => {
|
||||
try {
|
||||
GET('/common/system/unlockTask');
|
||||
} catch (error) {}
|
||||
return data;
|
||||
});
|
||||
GET<CheckPayResultResponse>(`/proApi/support/wallet/bill/pay/checkPayResult`, { payId }).then(
|
||||
(data) => {
|
||||
try {
|
||||
if (data.status === BillStatusEnum.SUCCESS) {
|
||||
GET('/common/system/unlockTask');
|
||||
}
|
||||
} catch (error) {}
|
||||
return data;
|
||||
}
|
||||
);
|
||||
|
||||
export const putUpdatePayment = (data: UpdatePaymentProps) =>
|
||||
PUT<CreateOrderResponse>(`/proApi/support/wallet/bill/pay/updatePayment`, data);
|
||||
|
||||
export const balanceConversion = () => GET<string>(`/proApi/support/wallet/bill/balanceConversion`);
|
||||
|
||||
Reference in New Issue
Block a user