4.7-alpha2 (#1027)
* feat: stop toolCall and rename some field. (#46) * perf: node delete tip;pay tip * fix: toolCall cannot save child answer * feat: stop tool * fix: team modal * fix feckbackMoal auth bug (#47) * 简单的支持提示词运行tool。优化workflow模板 (#49) * remove templates * fix: request body undefined * feat: prompt tool run * feat: workflow tamplates modal * perf: plugin start * 4.7 (#50) * fix docker-compose download url (#994) original code is a bad url with '404 NOT FOUND' return. fix docker-compose download url, add 'v' before docker-compose version * Update ai_settings.md (#1000) * Update configuration.md * Update configuration.md * Fix history in classifyQuestion and extract modules (#1012) * Fix history in classifyQuestion and extract modules * Add chatValue2RuntimePrompt import and update text formatting * flow controller to packages * fix: rerank select * modal ui * perf: modal code path * point not sufficient * feat: http url support variable * fix http key * perf: prompt * perf: ai setting modal * simple edit ui --------- Co-authored-by: entorick <entorick11@qq.com> Co-authored-by: liujianglc <liujianglc@163.com> Co-authored-by: Fengrui Liu <liufengrui.work@bytedance.com> * fix team share redirect to login (#51) * feat: support openapi import plugins (#48) * feat: support openapi import plugins * feat: import from url * fix: add body params parse * fix build * fix * fix * fix * tool box ui (#52) * fix: training queue * feat: simple edit tool select * perf: simple edit dataset prompt * fix: chatbox tool ux * feat: quote prompt module * perf: plugin tools sign * perf: model avatar * tool selector ui * feat: max histories * perf: http plugin import (#53) * perf: plugin http import * chatBox ui * perf: name * fix: Node template card (#54) * fix: ts * setting modal * package * package * feat: add plugins search (#57) * feat: add plugins search * perf: change http plugin header input * Yjl (#56) * perf: prompt tool call * perf: chat box ux * doc * doc * price tip * perf: tool selector * ui' * fix: vector queue * fix: empty tool and empty response * fix: empty msg * perf: pg index * perf: ui tip * doc * tool tip --------- Co-authored-by: yst <77910600+yu-and-liu@users.noreply.github.com> Co-authored-by: entorick <entorick11@qq.com> Co-authored-by: liujianglc <liujianglc@163.com> Co-authored-by: Fengrui Liu <liufengrui.work@bytedance.com> Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
@@ -14,7 +14,7 @@ const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
|
||||
w={w}
|
||||
h={w}
|
||||
p={'1px'}
|
||||
src={src || LOGO_ICON}
|
||||
src={src}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ModalBody, Box, useTheme } from '@chakra-ui/react';
|
||||
import MyModal from '../MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { DispatchNodeResponseType } from '@fastgpt/global/core/module/runtime/type.d';
|
||||
|
||||
const ContextModal = ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { ModalBody, Textarea, ModalFooter, Button } from '@chakra-ui/react';
|
||||
import MyModal from '../MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { updateChatUserFeedback } from '@/web/core/chat/api';
|
||||
@@ -9,6 +9,8 @@ const FeedbackModal = ({
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
teamId,
|
||||
teamToken,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
onSuccess,
|
||||
@@ -18,6 +20,8 @@ const FeedbackModal = ({
|
||||
chatId: string;
|
||||
chatItemId: string;
|
||||
shareId?: string;
|
||||
teamId?: string;
|
||||
teamToken?: string;
|
||||
outLinkUid?: string;
|
||||
onSuccess: (e: string) => void;
|
||||
onClose: () => void;
|
||||
@@ -33,6 +37,8 @@ const FeedbackModal = ({
|
||||
chatId,
|
||||
chatItemId,
|
||||
shareId,
|
||||
teamId,
|
||||
teamToken,
|
||||
outLinkUid,
|
||||
userBadFeedback: val
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { ModalBody, Box, useTheme } from '@chakra-ui/react';
|
||||
|
||||
import MyModal from '../MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import QuoteItem from '../core/dataset/QuoteItem';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ModalBody, ModalFooter, Button } from '@chakra-ui/react';
|
||||
import MyModal from '../MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const ReadFeedbackModal = ({
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslation } from 'next-i18next';
|
||||
import { moduleTemplatesFlat } from '@fastgpt/global/core/module/template/constants';
|
||||
|
||||
import Tabs from '../Tabs';
|
||||
import MyModal from '../MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import Markdown from '../Markdown';
|
||||
|
||||
@@ -9,9 +9,7 @@ import {
|
||||
AccordionButton,
|
||||
AccordionPanel,
|
||||
AccordionIcon,
|
||||
Button,
|
||||
Image,
|
||||
Grid
|
||||
Image
|
||||
} from '@chakra-ui/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import ChatController, { type ChatControllerProps } from './ChatController';
|
||||
@@ -88,7 +86,7 @@ const ChatItem = ({
|
||||
return (
|
||||
<>
|
||||
{files.length > 0 && <FilesBlock files={files} />}
|
||||
<Markdown source={text} isChatting={false} />
|
||||
<Markdown source={text} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -100,13 +98,26 @@ const ChatItem = ({
|
||||
if (value.text) {
|
||||
let source = value.text?.content || '';
|
||||
|
||||
if (isLastChild && !isChatting && questionGuides.length > 0) {
|
||||
if (!source && chat.value.length > 1) return <></>;
|
||||
|
||||
if (
|
||||
isLastChild &&
|
||||
!isChatting &&
|
||||
questionGuides.length > 0 &&
|
||||
i === chat.value.length - 1
|
||||
) {
|
||||
source = `${source}
|
||||
\`\`\`${CodeClassName.questionGuide}
|
||||
${JSON.stringify(questionGuides)}`;
|
||||
}
|
||||
|
||||
return <Markdown key={key} source={source} isChatting={isLastChild && isChatting} />;
|
||||
return (
|
||||
<Markdown
|
||||
key={key}
|
||||
source={source}
|
||||
showAnimation={isLastChild && isChatting && i === chat.value.length - 1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (value.type === ChatItemValueTypeEnum.tool && value.tools) {
|
||||
return (
|
||||
@@ -233,4 +244,4 @@ ${toolResponse}`}
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatItem;
|
||||
export default React.memo(ChatItem);
|
||||
|
||||
@@ -18,7 +18,7 @@ const WelcomeBox = ({ appAvatar, welcomeText }: { appAvatar?: string; welcomeTex
|
||||
bg={'white'}
|
||||
boxShadow={'0 0 8px rgba(0,0,0,0.15)'}
|
||||
>
|
||||
<Markdown source={`~~~guide \n${welcomeText}`} isChatting={false} />
|
||||
<Markdown source={`~~~guide \n${welcomeText}`} />
|
||||
</Card>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -257,10 +257,7 @@ const ChatBox = (
|
||||
};
|
||||
return {
|
||||
...item,
|
||||
value:
|
||||
lastValue && lastValue.text
|
||||
? item.value.slice(0, -1).concat(val)
|
||||
: item.value.concat(val)
|
||||
value: item.value.concat(val)
|
||||
};
|
||||
} else if (
|
||||
event === SseResponseEventEnum.toolParams &&
|
||||
@@ -441,6 +438,7 @@ const ChatBox = (
|
||||
chatController.current = abortSignal;
|
||||
|
||||
const messages = chats2GPTMessages({ messages: newChatList, reserveId: true });
|
||||
|
||||
const {
|
||||
responseData,
|
||||
responseText,
|
||||
@@ -537,10 +535,9 @@ const ChatBox = (
|
||||
setLoading(true);
|
||||
const index = chatHistories.findIndex((item) => item.dataId === dataId);
|
||||
const delHistory = chatHistories.slice(index);
|
||||
|
||||
try {
|
||||
await Promise.all(
|
||||
delHistory.map(async (item) => {
|
||||
delHistory.map((item) => {
|
||||
if (item.dataId) {
|
||||
return onDelMessage({ contentId: item.dataId });
|
||||
}
|
||||
@@ -563,14 +560,29 @@ const ChatBox = (
|
||||
},
|
||||
[chatHistories, onDelMessage, sendPrompt, setLoading, toast]
|
||||
);
|
||||
// delete one message
|
||||
// delete one message(One human and the ai response)
|
||||
const delOneMessage = useCallback(
|
||||
(dataId?: string) => {
|
||||
if (!dataId || !onDelMessage) return;
|
||||
return () => {
|
||||
setChatHistories((state) => state.filter((chat) => chat.dataId !== dataId));
|
||||
onDelMessage({
|
||||
contentId: dataId
|
||||
setChatHistories((state) => {
|
||||
let aiIndex = -1;
|
||||
|
||||
return state.filter((chat, i) => {
|
||||
if (chat.dataId === dataId) {
|
||||
aiIndex = i + 1;
|
||||
onDelMessage({
|
||||
contentId: dataId
|
||||
});
|
||||
return false;
|
||||
} else if (aiIndex === i && chat.obj === ChatRoleEnum.AI && chat.dataId) {
|
||||
onDelMessage({
|
||||
contentId: chat.dataId
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
};
|
||||
},
|
||||
@@ -630,6 +642,8 @@ const ChatBox = (
|
||||
updateChatUserFeedback({
|
||||
appId,
|
||||
chatId,
|
||||
teamId,
|
||||
teamToken,
|
||||
chatItemId: chat.dataId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
@@ -654,6 +668,8 @@ const ChatBox = (
|
||||
);
|
||||
updateChatUserFeedback({
|
||||
appId,
|
||||
teamId,
|
||||
teamToken,
|
||||
chatId,
|
||||
chatItemId: chat.dataId,
|
||||
userGoodFeedback: undefined
|
||||
@@ -687,6 +703,8 @@ const ChatBox = (
|
||||
chatId,
|
||||
chatItemId: chat.dataId,
|
||||
shareId,
|
||||
teamId,
|
||||
teamToken,
|
||||
outLinkUid
|
||||
});
|
||||
} catch (error) {}
|
||||
@@ -869,7 +887,6 @@ const ChatBox = (
|
||||
avatar={appAvatar}
|
||||
chat={item}
|
||||
isChatting={isChatting}
|
||||
onDelete={delOneMessage(item.dataId)}
|
||||
{...(item.obj === 'AI' && {
|
||||
setChatHistories,
|
||||
showVoiceIcon,
|
||||
@@ -955,6 +972,8 @@ const ChatBox = (
|
||||
{!!feedbackId && chatId && appId && (
|
||||
<FeedbackModal
|
||||
appId={appId}
|
||||
teamId={teamId}
|
||||
teamToken={teamToken}
|
||||
chatId={chatId}
|
||||
chatItemId={feedbackId}
|
||||
shareId={shareId}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Button, ModalFooter, ModalBody } from '@chakra-ui/react';
|
||||
import MyModal from '../MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Markdown from '../Markdown';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
@@ -10,12 +10,7 @@ const CommunityModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { feConfigs } = useSystemStore();
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
iconSrc="/imgs/modal/concat.svg"
|
||||
title={t('home.Community')}
|
||||
>
|
||||
<MyModal isOpen={true} onClose={onClose} iconSrc="modal/concat" title={t('home.Community')}>
|
||||
<ModalBody textAlign={'center'}>
|
||||
<Markdown source={feConfigs?.concatMd || ''} />
|
||||
</ModalBody>
|
||||
|
||||
@@ -12,10 +12,8 @@ import dynamic from 'next/dynamic';
|
||||
import Auth from './auth';
|
||||
import Navbar from './navbar';
|
||||
import NavbarPhone from './navbarPhone';
|
||||
const UpdateInviteModal = dynamic(
|
||||
() => import('@/components/support/user/team/UpdateInviteModal'),
|
||||
{ ssr: false }
|
||||
);
|
||||
const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal'));
|
||||
const NotSufficientModal = dynamic(() => import('@/components/support/wallet/NotSufficientModal'));
|
||||
|
||||
const pcUnShowLayoutRoute: Record<string, boolean> = {
|
||||
'/': true,
|
||||
@@ -44,7 +42,7 @@ const Layout = ({ children }: { children: JSX.Element }) => {
|
||||
const router = useRouter();
|
||||
const { colorMode, setColorMode } = useColorMode();
|
||||
const { Loading } = useLoading();
|
||||
const { loading, setScreenWidth, isPc, feConfigs } = useSystemStore();
|
||||
const { loading, setScreenWidth, isPc, feConfigs, isNotSufficientModal } = useSystemStore();
|
||||
const { userInfo } = useUserStore();
|
||||
|
||||
const isChatPage = useMemo(
|
||||
@@ -118,6 +116,7 @@ const Layout = ({ children }: { children: JSX.Element }) => {
|
||||
)}
|
||||
|
||||
{!!userInfo && <UpdateInviteModal />}
|
||||
{isNotSufficientModal && !isHideNavbar && <NotSufficientModal />}
|
||||
</Box>
|
||||
<Loading loading={loading} zIndex={999999} />
|
||||
</>
|
||||
|
||||
@@ -36,10 +36,10 @@ export enum CodeClassName {
|
||||
|
||||
const Markdown = ({
|
||||
source = '',
|
||||
isChatting = false
|
||||
showAnimation = false
|
||||
}: {
|
||||
source?: string;
|
||||
isChatting?: boolean;
|
||||
showAnimation?: boolean;
|
||||
}) => {
|
||||
const components = useMemo<any>(
|
||||
() => ({
|
||||
@@ -60,7 +60,7 @@ const Markdown = ({
|
||||
return (
|
||||
<ReactMarkdown
|
||||
className={`markdown ${styles.markdown}
|
||||
${isChatting ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''}
|
||||
${showAnimation ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''}
|
||||
`}
|
||||
remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]}
|
||||
rehypePlugins={[RehypeKatex]}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import React from 'react';
|
||||
import { ModalContentProps } from '@chakra-ui/react';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import CustomModal from '@fastgpt/web/components/common/CustomModal';
|
||||
|
||||
export interface MyModalProps extends ModalContentProps {
|
||||
iconSrc?: string;
|
||||
title?: any;
|
||||
isCentered?: boolean;
|
||||
isOpen: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
const MyModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
iconSrc,
|
||||
title,
|
||||
children,
|
||||
isCentered,
|
||||
w = 'auto',
|
||||
maxW = ['90vw', '600px'],
|
||||
...props
|
||||
}: MyModalProps) => {
|
||||
const { isPc } = useSystemStore();
|
||||
return (
|
||||
<CustomModal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
iconSrc={iconSrc}
|
||||
title={title}
|
||||
isCentered={isPc ? isCentered : true}
|
||||
w={w}
|
||||
maxW={maxW}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</CustomModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyModal;
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import MyModal from '../MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { Box, Button, Flex, Grid, useTheme } from '@chakra-ui/react';
|
||||
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
|
||||
import { ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
|
||||
70
projects/app/src/components/Select/AIModelSelector.tsx
Normal file
70
projects/app/src/components/Select/AIModelSelector.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
|
||||
import MySelect, { SelectProps } from '@fastgpt/web/components/common/MySelect';
|
||||
import { HUGGING_FACE_ICON, LOGO_ICON } from '@fastgpt/global/common/system/constants';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import Avatar from '../Avatar';
|
||||
|
||||
const AIModelSelector = ({ list, ...props }: SelectProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { feConfigs, llmModelList, vectorModelList } = useSystemStore();
|
||||
const router = useRouter();
|
||||
|
||||
const avatarList = list.map((item) => {
|
||||
const modelData =
|
||||
llmModelList.find((model) => model.model === item.value) ||
|
||||
vectorModelList.find((model) => model.model === item.value);
|
||||
|
||||
return {
|
||||
value: item.value,
|
||||
label: (
|
||||
<Flex alignItems={'center'} py={1}>
|
||||
<Avatar
|
||||
borderRadius={'0'}
|
||||
mr={2}
|
||||
src={modelData?.avatar || HUGGING_FACE_ICON}
|
||||
fallbackSrc={HUGGING_FACE_ICON}
|
||||
w={'18px'}
|
||||
/>
|
||||
<Box>{item.label}</Box>
|
||||
</Flex>
|
||||
)
|
||||
};
|
||||
});
|
||||
|
||||
const expandList = useMemo(() => {
|
||||
return feConfigs.show_pay
|
||||
? avatarList.concat({
|
||||
label: (
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar borderRadius={'0'} mr={2} src={LOGO_ICON} w={'18px'} />
|
||||
<Box>{t('support.user.Price')}</Box>
|
||||
</Flex>
|
||||
),
|
||||
value: 'price'
|
||||
})
|
||||
: avatarList;
|
||||
}, [feConfigs.show_pay, avatarList, t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MySelect
|
||||
list={expandList}
|
||||
{...props}
|
||||
onchange={(e) => {
|
||||
if (e === 'price') {
|
||||
router.push(AI_POINT_USAGE_CARD_ROUTE);
|
||||
return;
|
||||
}
|
||||
props.onchange?.(e);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AIModelSelector;
|
||||
@@ -1,40 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
|
||||
import MySelect, { SelectProps } from '@fastgpt/web/components/common/MySelect';
|
||||
|
||||
const SelectAiModel = ({ list, ...props }: SelectProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const router = useRouter();
|
||||
|
||||
const expandList = useMemo(() => {
|
||||
return feConfigs.show_pay
|
||||
? list.concat({
|
||||
label: t('support.user.Price'),
|
||||
value: 'price'
|
||||
})
|
||||
: list;
|
||||
}, [feConfigs.show_pay, list, t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MySelect
|
||||
list={expandList}
|
||||
{...props}
|
||||
onchange={(e) => {
|
||||
if (e === 'price') {
|
||||
router.push(AI_POINT_USAGE_CARD_ROUTE);
|
||||
return;
|
||||
}
|
||||
props.onchange?.(e);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectAiModel;
|
||||
@@ -10,8 +10,8 @@ type Props = BoxProps & {
|
||||
const MyBox = ({ text, isLoading, children, ...props }: Props) => {
|
||||
return (
|
||||
<Box position={'relative'} {...props}>
|
||||
{children}
|
||||
{isLoading && <Loading fixed={false} text={text} />}
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,9 +9,16 @@ const ParentPaths = (props: {
|
||||
rootName?: string;
|
||||
FirstPathDom?: React.ReactNode;
|
||||
onClick: (parentId: string) => void;
|
||||
fontSize?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { paths = [], rootName = t('common.folder.Root Path'), FirstPathDom, onClick } = props;
|
||||
const {
|
||||
paths = [],
|
||||
rootName = t('common.folder.Root Path'),
|
||||
FirstPathDom,
|
||||
onClick,
|
||||
fontSize
|
||||
} = props;
|
||||
const concatPaths = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -30,7 +37,7 @@ const ParentPaths = (props: {
|
||||
{concatPaths.map((item, i) => (
|
||||
<Flex key={item.parentId || i} alignItems={'center'}>
|
||||
<Box
|
||||
fontSize={['sm', 'lg']}
|
||||
fontSize={['sm', fontSize || 'lg']}
|
||||
py={1}
|
||||
px={[1, 2]}
|
||||
borderRadius={'md'}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
|
||||
type Props = TextareaProps & {
|
||||
title?: string;
|
||||
|
||||
242
projects/app/src/components/core/ai/AISettingModal/index.tsx
Normal file
242
projects/app/src/components/core/ai/AISettingModal/index.tsx
Normal file
@@ -0,0 +1,242 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import {
|
||||
Box,
|
||||
BoxProps,
|
||||
Button,
|
||||
Flex,
|
||||
Link,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Switch
|
||||
} from '@chakra-ui/react';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MySlider from '@/components/Slider';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import type { SettingAIDataType } from '@fastgpt/global/core/module/node/type.d';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
|
||||
const AIChatSettingsModal = ({
|
||||
onClose,
|
||||
onSuccess,
|
||||
defaultData,
|
||||
llmModels = []
|
||||
}: {
|
||||
onClose: () => void;
|
||||
onSuccess: (e: SettingAIDataType) => void;
|
||||
defaultData: SettingAIDataType;
|
||||
llmModels?: LLMModelItemType[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const { feConfigs, llmModelList } = useSystemStore();
|
||||
|
||||
const { handleSubmit, getValues, setValue, watch } = useForm({
|
||||
defaultValues: defaultData
|
||||
});
|
||||
const model = watch('model');
|
||||
const showResponseAnswerText = watch(ModuleInputKeyEnum.aiChatIsResponseText) !== undefined;
|
||||
const showMaxHistoriesSlider = watch('maxHistories') !== undefined;
|
||||
const selectedModel = llmModelList.find((item) => item.model === model) || llmModelList[0];
|
||||
|
||||
const tokenLimit = useMemo(() => {
|
||||
return llmModelList.find((item) => item.model === model)?.maxResponse || 4096;
|
||||
}, [llmModelList, model]);
|
||||
|
||||
const onChangeModel = (e: string) => {
|
||||
setValue('model', e);
|
||||
|
||||
// update max tokens
|
||||
const modelData = llmModelList.find((item) => item.model === e);
|
||||
if (modelData) {
|
||||
setValue('maxToken', modelData.maxResponse / 2);
|
||||
}
|
||||
|
||||
setRefresh(!refresh);
|
||||
};
|
||||
|
||||
const LabelStyles: BoxProps = {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontSize: ['sm', 'md'],
|
||||
width: ['80px', '90px']
|
||||
};
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
iconSrc="/imgs/module/AI.png"
|
||||
onClose={onClose}
|
||||
title={
|
||||
<>
|
||||
{t('core.ai.AI settings')}
|
||||
{feConfigs?.docUrl && (
|
||||
<Link
|
||||
href={getDocPath('/docs/course/ai_settings/')}
|
||||
target={'_blank'}
|
||||
ml={1}
|
||||
textDecoration={'underline'}
|
||||
fontWeight={'normal'}
|
||||
fontSize={'md'}
|
||||
>
|
||||
{t('common.Read intro')}
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
w={'500px'}
|
||||
>
|
||||
<ModalBody overflowY={'auto'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box {...LabelStyles} mr={2}>
|
||||
{t('core.ai.Model')}
|
||||
</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
<AIModelSelector
|
||||
width={'100%'}
|
||||
value={model}
|
||||
list={llmModels.map((item) => ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
}))}
|
||||
onchange={onChangeModel}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
{feConfigs && (
|
||||
<Flex mt={8}>
|
||||
<Box {...LabelStyles} mr={2}>
|
||||
{t('core.ai.Ai point price')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
{t('support.wallet.Ai point every thousand tokens', {
|
||||
points: selectedModel?.charsPointsPrice || 0
|
||||
})}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
<Flex mt={8}>
|
||||
<Box {...LabelStyles} mr={2}>
|
||||
{t('core.ai.Max context')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
{selectedModel?.maxContext || 4096}Tokens
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={8}>
|
||||
<Box {...LabelStyles} mr={2}>
|
||||
{t('core.ai.Support tool')}
|
||||
<QuestionTip ml={1} label={t('core.module.template.AI support tool tip')} />
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
{selectedModel?.usedInToolCall ? '支持' : '不支持'}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={8}>
|
||||
<Box {...LabelStyles} mr={2}>
|
||||
{t('core.app.Temperature')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: t('core.app.deterministic'), value: 0 },
|
||||
{ label: t('core.app.Random'), value: 10 }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={0}
|
||||
max={10}
|
||||
value={getValues(ModuleInputKeyEnum.aiChatTemperature)}
|
||||
onChange={(e) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatTemperature, e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={8}>
|
||||
<Box {...LabelStyles} mr={2}>
|
||||
{t('core.app.Max tokens')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: `${tokenLimit}`, value: tokenLimit }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={100}
|
||||
max={tokenLimit}
|
||||
step={50}
|
||||
value={getValues(ModuleInputKeyEnum.aiChatMaxToken)}
|
||||
onChange={(val) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatMaxToken, val);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
{showMaxHistoriesSlider && (
|
||||
<Flex mt={8}>
|
||||
<Box {...LabelStyles} mr={2}>
|
||||
{t('core.app.Max histories')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: 0, value: 0 },
|
||||
{ label: 30, value: 30 }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={0}
|
||||
max={30}
|
||||
value={getValues('maxHistories') ?? 6}
|
||||
onChange={(e) => {
|
||||
setValue('maxHistories', e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{showResponseAnswerText && (
|
||||
<Flex mt={8} alignItems={'center'}>
|
||||
<Box {...LabelStyles}>
|
||||
{t('core.app.Ai response')}
|
||||
<MyTooltip label={t('core.module.template.AI response switch tip')}>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<Switch
|
||||
isChecked={getValues(ModuleInputKeyEnum.aiChatIsResponseText)}
|
||||
size={'lg'}
|
||||
onChange={(e) => {
|
||||
const value = e.target.checked;
|
||||
setValue(ModuleInputKeyEnum.aiChatIsResponseText, value);
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button ml={4} onClick={handleSubmit(onSuccess)}>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AIChatSettingsModal;
|
||||
@@ -0,0 +1,83 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { LLMModelTypeEnum, llmModelTypeFilterMap } from '@fastgpt/global/core/ai/constants';
|
||||
import { Box, Button, useDisclosure } from '@chakra-ui/react';
|
||||
import { SettingAIDataType } from '@fastgpt/global/core/module/node/type';
|
||||
import AISettingModal from '@/components/core/ai/AISettingModal';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import { HUGGING_FACE_ICON } from '@fastgpt/global/common/system/constants';
|
||||
|
||||
type Props = {
|
||||
llmModelType?: `${LLMModelTypeEnum}`;
|
||||
defaultData: SettingAIDataType;
|
||||
onChange: (e: SettingAIDataType) => void;
|
||||
};
|
||||
|
||||
const SettingLLMModel = ({ llmModelType = LLMModelTypeEnum.all, defaultData, onChange }: Props) => {
|
||||
const { llmModelList } = useSystemStore();
|
||||
|
||||
const model = defaultData.model;
|
||||
|
||||
const modelList = llmModelList.filter((model) => {
|
||||
if (!llmModelType) return true;
|
||||
const filterField = llmModelTypeFilterMap[llmModelType];
|
||||
if (!filterField) return true;
|
||||
//@ts-ignore
|
||||
return !!model[filterField];
|
||||
});
|
||||
|
||||
const selectedModel = modelList.find((item) => item.model === model) || modelList[0];
|
||||
|
||||
const {
|
||||
isOpen: isOpenAIChatSetting,
|
||||
onOpen: onOpenAIChatSetting,
|
||||
onClose: onCloseAIChatSetting
|
||||
} = useDisclosure();
|
||||
|
||||
useEffect(() => {
|
||||
if (!model && modelList.length > 0) {
|
||||
onChange({
|
||||
...defaultData,
|
||||
model: modelList[0].model
|
||||
});
|
||||
}
|
||||
}, [defaultData, model, modelList, onChange]);
|
||||
|
||||
return (
|
||||
<Box position={'relative'}>
|
||||
<Button
|
||||
w={'100%'}
|
||||
justifyContent={'flex-start'}
|
||||
variant={'whitePrimary'}
|
||||
_active={{
|
||||
transform: 'none'
|
||||
}}
|
||||
leftIcon={
|
||||
<Avatar
|
||||
borderRadius={'0'}
|
||||
src={selectedModel.avatar || HUGGING_FACE_ICON}
|
||||
fallbackSrc={HUGGING_FACE_ICON}
|
||||
w={'18px'}
|
||||
/>
|
||||
}
|
||||
pl={4}
|
||||
onClick={onOpenAIChatSetting}
|
||||
>
|
||||
{selectedModel?.name}
|
||||
</Button>
|
||||
{isOpenAIChatSetting && (
|
||||
<AISettingModal
|
||||
onClose={onCloseAIChatSetting}
|
||||
onSuccess={(e) => {
|
||||
onChange(e);
|
||||
onCloseAIChatSetting();
|
||||
}}
|
||||
defaultData={defaultData}
|
||||
llmModels={modelList}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SettingLLMModel);
|
||||
@@ -50,7 +50,7 @@ const SearchParamsTip = ({
|
||||
</Thead>
|
||||
<Tbody>
|
||||
<Tr color={'myGray.800'}>
|
||||
<Td pt={0} pb={1}>
|
||||
<Td pt={0} pb={2}>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon
|
||||
name={DatasetSearchModeMap[searchMode]?.icon as any}
|
||||
@@ -60,18 +60,18 @@ const SearchParamsTip = ({
|
||||
{t(DatasetSearchModeMap[searchMode]?.title)}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td pt={0} pb={1}>
|
||||
<Td pt={0} pb={2}>
|
||||
{limit}
|
||||
</Td>
|
||||
<Td pt={0} pb={1}>
|
||||
<Td pt={0} pb={2}>
|
||||
{hasSimilarityMode ? similarity : t('core.dataset.search.Nonsupport')}
|
||||
</Td>
|
||||
{hasReRankModel && (
|
||||
<Td pt={0} pb={1}>
|
||||
<Td pt={0} pb={2}>
|
||||
{usingReRank ? '✅' : '❌'}
|
||||
</Td>
|
||||
)}
|
||||
<Td pt={0} pb={1}>
|
||||
<Td pt={0} pb={2}>
|
||||
{usingQueryExtension ? '✅' : '❌'}
|
||||
</Td>
|
||||
{hasEmptyResponseMode && <Th>{responseEmptyText !== '' ? '✅' : '❌'}</Th>}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getDatasets, getDatasetPaths } from '@/web/core/dataset/api';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import React, { Dispatch, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import {
|
||||
Box,
|
||||
BoxProps,
|
||||
Button,
|
||||
Flex,
|
||||
Link,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Switch
|
||||
} from '@chakra-ui/react';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { Prompt_QuotePromptList, Prompt_QuoteTemplateList } from '@/global/core/prompt/AIChat';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MySlider from '@/components/Slider';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
|
||||
import type { AIChatModuleProps } from '@fastgpt/global/core/module/node/type.d';
|
||||
import type { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type.d';
|
||||
import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
||||
|
||||
const PromptTemplate = dynamic(() => import('@/components/PromptTemplate'));
|
||||
|
||||
const AIChatSettingsModal = ({
|
||||
isAdEdit,
|
||||
onClose,
|
||||
onSuccess,
|
||||
defaultData,
|
||||
pickerMenu = []
|
||||
}: {
|
||||
isAdEdit?: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess: (e: AIChatModuleProps) => void;
|
||||
defaultData: AIChatModuleProps;
|
||||
pickerMenu?: EditorVariablePickerType[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const { feConfigs, llmModelList } = useSystemStore();
|
||||
|
||||
const { handleSubmit, getValues, setValue, watch } = useForm({
|
||||
defaultValues: defaultData
|
||||
});
|
||||
const aiChatQuoteTemplate = watch(ModuleInputKeyEnum.aiChatQuoteTemplate);
|
||||
const aiChatQuotePrompt = watch(ModuleInputKeyEnum.aiChatQuotePrompt);
|
||||
|
||||
const [selectTemplateData, setSelectTemplateData] = useState<{
|
||||
title: string;
|
||||
templates: PromptTemplateItem[];
|
||||
}>();
|
||||
|
||||
const tokenLimit = useMemo(() => {
|
||||
return (
|
||||
llmModelList.find((item) => item.model === getValues(ModuleInputKeyEnum.aiModel))
|
||||
?.maxResponse || 4000
|
||||
);
|
||||
}, [getValues, llmModelList]);
|
||||
|
||||
const quoteTemplateVariables = (() => [
|
||||
{
|
||||
key: 'q',
|
||||
label: 'q',
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'a',
|
||||
label: 'a',
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'source',
|
||||
label: t('core.dataset.search.Source name'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'sourceId',
|
||||
label: t('core.dataset.search.Source id'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'index',
|
||||
label: t('core.dataset.search.Quote index'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
...pickerMenu
|
||||
])();
|
||||
const quotePromptVariables = (() => [
|
||||
{
|
||||
key: 'quote',
|
||||
label: t('core.app.Quote templates'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'question',
|
||||
label: t('core.module.input.label.user question'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
...pickerMenu
|
||||
])();
|
||||
|
||||
const LabelStyles: BoxProps = {
|
||||
fontSize: ['sm', 'md']
|
||||
};
|
||||
const selectTemplateBtn: BoxProps = {
|
||||
color: 'primary.500',
|
||||
cursor: 'pointer'
|
||||
};
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
iconSrc="/imgs/module/AI.png"
|
||||
title={
|
||||
<>
|
||||
{t('common.More settings')}
|
||||
{feConfigs?.docUrl && (
|
||||
<Link
|
||||
href={getDocPath('/docs/use-cases/ai_settings/')}
|
||||
target={'_blank'}
|
||||
ml={1}
|
||||
textDecoration={'underline'}
|
||||
fontWeight={'normal'}
|
||||
fontSize={'md'}
|
||||
>
|
||||
{t('common.Read intro')}
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
isCentered
|
||||
w={'700px'}
|
||||
h={['90vh', 'auto']}
|
||||
>
|
||||
<ModalBody flex={['1 0 0', 'auto']} overflowY={'auto'} minH={'40vh'}>
|
||||
{isAdEdit && (
|
||||
<Flex alignItems={'center'}>
|
||||
<Box {...LabelStyles} w={'80px'}>
|
||||
{t('core.app.Ai response')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<Switch
|
||||
isChecked={getValues(ModuleInputKeyEnum.aiChatIsResponseText)}
|
||||
size={'lg'}
|
||||
onChange={(e) => {
|
||||
const value = e.target.checked;
|
||||
setValue(ModuleInputKeyEnum.aiChatIsResponseText, value);
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
<Flex mb={10} mt={isAdEdit ? 8 : 6}>
|
||||
<Box {...LabelStyles} mr={2} w={'80px'}>
|
||||
{t('core.app.Temperature')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: t('core.app.deterministic'), value: 0 },
|
||||
{ label: t('core.app.Random'), value: 10 }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={0}
|
||||
max={10}
|
||||
value={getValues(ModuleInputKeyEnum.aiChatTemperature)}
|
||||
onChange={(e) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatTemperature, e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={5} mb={5}>
|
||||
<Box {...LabelStyles} mr={2} w={'80px'}>
|
||||
{t('core.app.Max tokens')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: `${tokenLimit}`, value: tokenLimit }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={100}
|
||||
max={tokenLimit}
|
||||
step={50}
|
||||
value={getValues(ModuleInputKeyEnum.aiChatMaxToken)}
|
||||
onChange={(val) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatMaxToken, val);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<Box>
|
||||
<Flex {...LabelStyles} mb={1}>
|
||||
{t('core.app.Quote templates')}
|
||||
<MyTooltip
|
||||
label={t('template.Quote Content Tip', {
|
||||
default: Prompt_QuoteTemplateList[0].value
|
||||
})}
|
||||
forceShow
|
||||
>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
<Box flex={1} />
|
||||
<Box
|
||||
{...selectTemplateBtn}
|
||||
onClick={() =>
|
||||
setSelectTemplateData({
|
||||
title: t('core.app.Select quote template'),
|
||||
templates: Prompt_QuoteTemplateList
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('common.Select template')}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<PromptEditor
|
||||
variables={quoteTemplateVariables}
|
||||
h={160}
|
||||
title={t('core.app.Quote templates')}
|
||||
placeholder={t('template.Quote Content Tip', {
|
||||
default: Prompt_QuoteTemplateList[0].value
|
||||
})}
|
||||
value={aiChatQuoteTemplate}
|
||||
onChange={(e) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatQuoteTemplate, e);
|
||||
// setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box mt={4}>
|
||||
<Flex {...LabelStyles} mb={1}>
|
||||
{t('core.app.Quote prompt')}
|
||||
<MyTooltip
|
||||
label={t('template.Quote Prompt Tip', { default: Prompt_QuotePromptList[0].value })}
|
||||
forceShow
|
||||
>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<PromptEditor
|
||||
variables={quotePromptVariables}
|
||||
title={t('core.app.Quote prompt')}
|
||||
h={230}
|
||||
placeholder={t('template.Quote Prompt Tip', {
|
||||
default: Prompt_QuotePromptList[0].value
|
||||
})}
|
||||
value={aiChatQuotePrompt}
|
||||
onChange={(e) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatQuotePrompt, e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button ml={4} onClick={handleSubmit(onSuccess)}>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
{!!selectTemplateData && (
|
||||
<PromptTemplate
|
||||
title={selectTemplateData.title}
|
||||
templates={selectTemplateData.templates}
|
||||
onClose={() => setSelectTemplateData(undefined)}
|
||||
onSuccess={(e) => {
|
||||
const quoteVal = e.value;
|
||||
const promptVal = Prompt_QuotePromptList.find((item) => item.title === e.title)?.value;
|
||||
setValue(ModuleInputKeyEnum.aiChatQuoteTemplate, quoteVal);
|
||||
setValue(ModuleInputKeyEnum.aiChatQuotePrompt, promptVal);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AIChatSettingsModal;
|
||||
@@ -15,7 +15,7 @@ import { useForm } from 'react-hook-form';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import MySlider from '@/components/Slider';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
@@ -28,7 +28,7 @@ import Tabs from '@/components/Tabs';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import SelectAiModel from '@/components/Select/SelectAiModel';
|
||||
import SelectAiModel from '@/components/Select/AIModelSelector';
|
||||
|
||||
export type DatasetParamsProps = {
|
||||
searchMode: `${DatasetSearchModeEnum}`;
|
||||
@@ -40,7 +40,6 @@ export type DatasetParamsProps = {
|
||||
datasetSearchExtensionBg?: string;
|
||||
|
||||
maxTokens?: number; // limit max tokens
|
||||
searchEmptyText?: string;
|
||||
};
|
||||
enum SearchSettingTabEnum {
|
||||
searchMode = 'searchMode',
|
||||
@@ -50,7 +49,6 @@ enum SearchSettingTabEnum {
|
||||
|
||||
const DatasetParamsModal = ({
|
||||
searchMode = DatasetSearchModeEnum.embedding,
|
||||
searchEmptyText,
|
||||
limit,
|
||||
similarity,
|
||||
usingReRank,
|
||||
@@ -71,11 +69,10 @@ const DatasetParamsModal = ({
|
||||
|
||||
const { register, setValue, getValues, handleSubmit, watch } = useForm<DatasetParamsProps>({
|
||||
defaultValues: {
|
||||
searchEmptyText,
|
||||
limit,
|
||||
similarity,
|
||||
searchMode,
|
||||
usingReRank: !!usingReRank && !!teamPlanStatus?.standardConstants?.permissionReRank,
|
||||
usingReRank: !!usingReRank && teamPlanStatus?.standardConstants?.permissionReRank !== false,
|
||||
datasetSearchUsingExtensionQuery,
|
||||
datasetSearchExtensionModel: datasetSearchExtensionModel ?? llmModelList[0]?.model,
|
||||
datasetSearchExtensionBg
|
||||
@@ -272,21 +269,6 @@ const DatasetParamsModal = ({
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
{searchEmptyText !== undefined && (
|
||||
<Box display={['block', 'flex']} pt={3}>
|
||||
<Box flex={'0 0 120px'} mb={[2, 0]}>
|
||||
{t('core.dataset.search.Empty result response')}
|
||||
</Box>
|
||||
<Box flex={1}>
|
||||
<Textarea
|
||||
rows={5}
|
||||
maxLength={500}
|
||||
placeholder={t('core.dataset.search.Empty result response Tips')}
|
||||
{...register('searchEmptyText')}
|
||||
></Textarea>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{currentTabType === SearchSettingTabEnum.queryExtension && (
|
||||
|
||||
@@ -8,10 +8,7 @@ import {
|
||||
Connection,
|
||||
addEdge
|
||||
} from 'reactflow';
|
||||
import type {
|
||||
FlowModuleItemType,
|
||||
FlowModuleTemplateType
|
||||
} from '@fastgpt/global/core/module/type.d';
|
||||
import type { FlowModuleItemType, FlowNodeTemplateType } from '@fastgpt/global/core/module/type.d';
|
||||
import type {
|
||||
FlowNodeChangeProps,
|
||||
FlowNodeInputItemType
|
||||
@@ -64,7 +61,7 @@ export type useFlowProviderStoreType = {
|
||||
onDelNode: (nodeId: string) => void;
|
||||
onChangeNode: (e: FlowNodeChangeProps) => void;
|
||||
onCopyNode: (nodeId: string) => void;
|
||||
onResetNode: (e: { id: string; module: FlowModuleTemplateType }) => void;
|
||||
onResetNode: (e: { id: string; module: FlowNodeTemplateType }) => void;
|
||||
onDelEdge: (e: {
|
||||
moduleId: string;
|
||||
sourceHandle?: string | undefined;
|
||||
@@ -417,7 +414,7 @@ export const FlowProvider = ({
|
||||
|
||||
// reset a node data. delete edge and replace it
|
||||
const onResetNode = useCallback(
|
||||
({ id, module }: { id: string; module: FlowModuleTemplateType }) => {
|
||||
({ id, module }: { id: string; module: FlowNodeTemplateType }) => {
|
||||
setNodes((state) =>
|
||||
state.map((node) => {
|
||||
if (node.id === id) {
|
||||
@@ -549,12 +546,6 @@ export const onResetNode = (e: Parameters<useFlowProviderStoreType['onResetNode'
|
||||
data: e
|
||||
});
|
||||
};
|
||||
export const onDelNode = (nodeId: string) => {
|
||||
eventBus.emit(EventNameEnum.requestFlowEvent, {
|
||||
type: 'onDelNode',
|
||||
data: nodeId
|
||||
});
|
||||
};
|
||||
export const onDelConnect = (e: Parameters<useFlowProviderStoreType['onDelConnect']>[0]) => {
|
||||
eventBus.emit(EventNameEnum.requestFlowEvent, {
|
||||
type: 'onDelConnect',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Textarea, Button, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useFlowProviderStore } from './FlowProvider';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { Box, Flex, IconButton, Input, InputGroup, InputLeftElement } from '@chakra-ui/react';
|
||||
import type {
|
||||
FlowModuleTemplateType,
|
||||
FlowNodeTemplateType,
|
||||
moduleTemplateListType
|
||||
} from '@fastgpt/global/core/module/type.d';
|
||||
import { useViewport, XYPosition } from 'reactflow';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import { getFlowStore, onSetNodes } from './FlowProvider';
|
||||
import { onSetNodes, useFlowProviderStore } from './FlowProvider';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { appModule2FlowNode } from '@/utils/adapt';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
@@ -18,22 +18,83 @@ import { getPreviewPluginModule } from '@/web/core/plugin/api';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { moduleTemplatesList } from '@fastgpt/global/core/module/template/constants';
|
||||
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
|
||||
import { useWorkflowStore } from '@/web/core/workflow/store/workflow';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import ParentPaths from '@/components/common/ParentPaths';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useRouter } from 'next/router';
|
||||
import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
export type ModuleTemplateProps = {
|
||||
templates: FlowModuleTemplateType[];
|
||||
};
|
||||
|
||||
type ModuleTemplateListProps = ModuleTemplateProps & {
|
||||
type ModuleTemplateListProps = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
};
|
||||
type RenderListProps = {
|
||||
templates: FlowModuleTemplateType[];
|
||||
templates: FlowNodeTemplateType[];
|
||||
onClose: () => void;
|
||||
currentParent?: { parentId: string; parentName: string };
|
||||
setCurrentParent: (e: { parentId: string; parentName: string }) => void;
|
||||
};
|
||||
|
||||
const ModuleTemplateList = ({ templates, isOpen, onClose }: ModuleTemplateListProps) => {
|
||||
enum TemplateTypeEnum {
|
||||
'basic' = 'basic',
|
||||
'systemPlugin' = 'systemPlugin',
|
||||
'teamPlugin' = 'teamPlugin'
|
||||
}
|
||||
|
||||
const sliderWidth = 380;
|
||||
|
||||
const ModuleTemplateList = ({ isOpen, onClose }: ModuleTemplateListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const [currentParent, setCurrentParent] = useState<RenderListProps['currentParent']>();
|
||||
const [searchKey, setSearchKey] = useState('');
|
||||
|
||||
const {
|
||||
basicNodeTemplates,
|
||||
systemNodeTemplates,
|
||||
loadSystemNodeTemplates,
|
||||
teamPluginNodeTemplates,
|
||||
loadTeamPluginNodeTemplates
|
||||
} = useWorkflowStore();
|
||||
const [templateType, setTemplateType] = useState(TemplateTypeEnum.basic);
|
||||
|
||||
const templates = useMemo(() => {
|
||||
const map = {
|
||||
[TemplateTypeEnum.basic]: basicNodeTemplates,
|
||||
[TemplateTypeEnum.systemPlugin]: systemNodeTemplates,
|
||||
[TemplateTypeEnum.teamPlugin]: teamPluginNodeTemplates.filter((item) =>
|
||||
searchKey ? item.pluginType !== PluginTypeEnum.folder : true
|
||||
)
|
||||
};
|
||||
return map[templateType];
|
||||
}, [basicNodeTemplates, searchKey, systemNodeTemplates, teamPluginNodeTemplates, templateType]);
|
||||
|
||||
const { mutate: onChangeTab } = useRequest({
|
||||
mutationFn: async (e: any) => {
|
||||
const val = e as TemplateTypeEnum;
|
||||
if (val === TemplateTypeEnum.systemPlugin) {
|
||||
await loadSystemNodeTemplates();
|
||||
} else if (val === TemplateTypeEnum.teamPlugin) {
|
||||
await loadTeamPluginNodeTemplates({
|
||||
parentId: currentParent?.parentId
|
||||
});
|
||||
}
|
||||
setTemplateType(val);
|
||||
},
|
||||
errorToast: t('core.module.templates.Load plugin error')
|
||||
});
|
||||
|
||||
useQuery(['teamNodeTemplate', currentParent?.parentId, searchKey], () =>
|
||||
loadTeamPluginNodeTemplates({
|
||||
parentId: currentParent?.parentId,
|
||||
searchKey,
|
||||
init: true
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -44,27 +105,108 @@ const ModuleTemplateList = ({ templates, isOpen, onClose }: ModuleTemplateListPr
|
||||
top={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
w={'360px'}
|
||||
w={`${sliderWidth}px`}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Flex
|
||||
zIndex={3}
|
||||
flexDirection={'column'}
|
||||
position={'absolute'}
|
||||
top={'65px'}
|
||||
top={'10px'}
|
||||
left={0}
|
||||
pt={2}
|
||||
pt={'20px'}
|
||||
pb={4}
|
||||
h={isOpen ? 'calc(100% - 100px)' : '0'}
|
||||
w={isOpen ? ['100%', '360px'] : '0'}
|
||||
h={isOpen ? 'calc(100% - 20px)' : '0'}
|
||||
w={isOpen ? ['100%', `${sliderWidth}px`] : '0'}
|
||||
bg={'white'}
|
||||
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
|
||||
borderRadius={'20px'}
|
||||
overflow={'hidden'}
|
||||
borderRadius={'0 20px 20px 0'}
|
||||
transition={'.2s ease'}
|
||||
userSelect={'none'}
|
||||
overflow={isOpen ? 'none' : 'hidden'}
|
||||
>
|
||||
<RenderList templates={templates} onClose={onClose} />
|
||||
<Box mb={2} pl={'20px'} pr={'10px'} whiteSpace={'nowrap'} overflow={'hidden'}>
|
||||
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
|
||||
<RowTabs
|
||||
list={[
|
||||
{
|
||||
icon: 'core/modules/basicNode',
|
||||
label: t('core.module.template.Basic Node'),
|
||||
value: TemplateTypeEnum.basic
|
||||
},
|
||||
{
|
||||
icon: 'core/modules/systemPlugin',
|
||||
label: t('core.module.template.System Plugin'),
|
||||
value: TemplateTypeEnum.systemPlugin
|
||||
},
|
||||
{
|
||||
icon: 'core/modules/teamPlugin',
|
||||
label: t('core.module.template.Team Plugin'),
|
||||
value: TemplateTypeEnum.teamPlugin
|
||||
}
|
||||
]}
|
||||
py={'5px'}
|
||||
value={templateType}
|
||||
onChange={onChangeTab}
|
||||
/>
|
||||
{/* close icon */}
|
||||
<IconButton
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'common/backFill'} w={'14px'} color={'myGray.700'} />}
|
||||
w={'26px'}
|
||||
h={'26px'}
|
||||
borderColor={'myGray.300'}
|
||||
variant={'grayBase'}
|
||||
aria-label={''}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</Flex>
|
||||
{templateType === TemplateTypeEnum.teamPlugin && (
|
||||
<Flex mt={2} alignItems={'center'} h={10}>
|
||||
<InputGroup mr={4} h={'full'}>
|
||||
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
|
||||
<MyIcon name={'common/searchLight'} w={'16px'} color={'myGray.500'} ml={3} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
h={'full'}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('plugin.Search plugin')}
|
||||
onChange={debounce((e) => setSearchKey(e.target.value), 200)}
|
||||
/>
|
||||
</InputGroup>
|
||||
<Box flex={1} />
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={() => router.push('/plugin/list')}
|
||||
>
|
||||
<Box>去创建</Box>
|
||||
<MyIcon name={'common/rightArrowLight'} w={'14px'} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
{templateType === TemplateTypeEnum.teamPlugin && !searchKey && currentParent && (
|
||||
<Flex alignItems={'center'} mt={2}>
|
||||
<ParentPaths
|
||||
paths={[currentParent]}
|
||||
FirstPathDom={null}
|
||||
onClick={() => {
|
||||
setCurrentParent(undefined);
|
||||
}}
|
||||
fontSize="md"
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
<RenderList
|
||||
templates={templates}
|
||||
onClose={onClose}
|
||||
currentParent={currentParent}
|
||||
setCurrentParent={setCurrentParent}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
@@ -72,12 +214,18 @@ const ModuleTemplateList = ({ templates, isOpen, onClose }: ModuleTemplateListPr
|
||||
|
||||
export default React.memo(ModuleTemplateList);
|
||||
|
||||
const RenderList = React.memo(function RenderList({ templates, onClose }: RenderListProps) {
|
||||
const RenderList = React.memo(function RenderList({
|
||||
templates,
|
||||
onClose,
|
||||
currentParent,
|
||||
setCurrentParent
|
||||
}: RenderListProps) {
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useSystemStore();
|
||||
const { x, y, zoom } = useViewport();
|
||||
const { setLoading } = useSystemStore();
|
||||
const { toast } = useToast();
|
||||
const { reactFlowWrapper, nodes } = useFlowProviderStore();
|
||||
|
||||
const formatTemplates = useMemo<moduleTemplateListType>(() => {
|
||||
const copy: moduleTemplateListType = JSON.parse(JSON.stringify(moduleTemplatesList));
|
||||
@@ -87,11 +235,11 @@ const RenderList = React.memo(function RenderList({ templates, onClose }: Render
|
||||
copy[index].list.push(item);
|
||||
});
|
||||
return copy.filter((item) => item.list.length > 0);
|
||||
}, [templates]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [templates, currentParent]);
|
||||
|
||||
const onAddNode = useCallback(
|
||||
async ({ template, position }: { template: FlowModuleTemplateType; position: XYPosition }) => {
|
||||
const { reactFlowWrapper, nodes } = await getFlowStore();
|
||||
async ({ template, position }: { template: FlowNodeTemplateType; position: XYPosition }) => {
|
||||
if (!reactFlowWrapper?.current) return;
|
||||
|
||||
const templateModule = await (async () => {
|
||||
@@ -131,34 +279,24 @@ const RenderList = React.memo(function RenderList({ templates, onClose }: Render
|
||||
)
|
||||
);
|
||||
},
|
||||
[setLoading, t, toast, x, y, zoom]
|
||||
[nodes, reactFlowWrapper, setLoading, t, toast, x, y, zoom]
|
||||
);
|
||||
|
||||
return templates.length === 0 ? (
|
||||
<EmptyTip text={t('app.module.No Modules')} />
|
||||
) : (
|
||||
<Box flex={'1 0 0'} overflow={'overlay'}>
|
||||
<Box w={['100%', '330px']} mx={'auto'}>
|
||||
<Box flex={'1 0 0'} overflow={'overlay'} px={'20px'}>
|
||||
<Box mx={'auto'}>
|
||||
{formatTemplates.map((item, i) => (
|
||||
<Box key={item.type}>
|
||||
<Flex>
|
||||
<Box fontWeight={'bold'} flex={1}>
|
||||
{t(item.label)}
|
||||
</Box>
|
||||
{/* {isPlugin && item.type === ModuleTemplateTypeEnum.personalPlugin && (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
cursor={'pointer'}
|
||||
onClick={() => router.push('/plugin/list')}
|
||||
>
|
||||
<Box fontSize={'sm'} transform={'translateY(-1px)'}>
|
||||
{t('plugin.To Edit Plugin')}
|
||||
</Box>
|
||||
<MyIcon name={'common/rightArrowLight'} w={'12px'} />
|
||||
</Flex>
|
||||
)} */}
|
||||
</Flex>
|
||||
{item.label && (
|
||||
<Flex>
|
||||
<Box fontWeight={'bold'} flex={1}>
|
||||
{t(item.label)}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
<>
|
||||
{item.list.map((template) => (
|
||||
<Flex
|
||||
@@ -168,21 +306,32 @@ const RenderList = React.memo(function RenderList({ templates, onClose }: Render
|
||||
cursor={'pointer'}
|
||||
_hover={{ bg: 'myWhite.600' }}
|
||||
borderRadius={'sm'}
|
||||
draggable
|
||||
draggable={template.pluginType !== PluginTypeEnum.folder}
|
||||
onDragEnd={(e) => {
|
||||
if (e.clientX < 360) return;
|
||||
if (e.clientX < sliderWidth) return;
|
||||
onAddNode({
|
||||
template: template,
|
||||
position: { x: e.clientX, y: e.clientY }
|
||||
});
|
||||
}}
|
||||
onClick={(e) => {
|
||||
if (isPc) return;
|
||||
onClose();
|
||||
if (template.pluginType === PluginTypeEnum.folder) {
|
||||
return setCurrentParent({
|
||||
parentId: template.id,
|
||||
parentName: template.name
|
||||
});
|
||||
}
|
||||
if (isPc) {
|
||||
return onAddNode({
|
||||
template,
|
||||
position: { x: sliderWidth * 1.5, y: 200 }
|
||||
});
|
||||
}
|
||||
onAddNode({
|
||||
template: template,
|
||||
position: { x: e.clientX, y: e.clientY }
|
||||
});
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { ModalBody, Flex, Box, useTheme, ModalFooter, Button } from '@chakra-ui/react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { SelectAppItemType } from '@fastgpt/global/core/module/type';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { TTSTypeEnum } from '@/constants/app';
|
||||
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
|
||||
import { useAudioPlay } from '@/web/common/utils/voice';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MySlider from '@/components/Slider';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
|
||||
@@ -67,7 +67,7 @@ const TTSSelect = ({
|
||||
});
|
||||
}
|
||||
},
|
||||
[onChange, value]
|
||||
[audioSpeechModelList, onChange, value]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -79,17 +79,16 @@ const TTSSelect = ({
|
||||
</MyTooltip>
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={t('core.app.Select TTS')}>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
_hover={{ bg: 'myGray.100' }}
|
||||
py={2}
|
||||
px={3}
|
||||
borderRadius={'md'}
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
fontSize={'md'}
|
||||
mr={'-5px'}
|
||||
onClick={onOpen}
|
||||
color={'myGray.600'}
|
||||
>
|
||||
{formLabel}
|
||||
</Box>
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
<MyModal
|
||||
title={
|
||||
|
||||
@@ -32,7 +32,7 @@ import { useForm } from 'react-hook-form';
|
||||
import { useFieldArray } from 'react-hook-form';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { variableTip } from '@fastgpt/global/core/module/template/tip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
@@ -83,16 +83,6 @@ const VariableEdit = ({
|
||||
name: 'variable.enums'
|
||||
});
|
||||
|
||||
const BoxBtnStyles: BoxProps = {
|
||||
cursor: 'pointer',
|
||||
px: 3,
|
||||
py: 1,
|
||||
borderRadius: 'md',
|
||||
_hover: {
|
||||
bg: 'myGray.150'
|
||||
}
|
||||
};
|
||||
|
||||
const formatVariables = useMemo(() => {
|
||||
const results = formatEditorVariablePickerIcon(variables);
|
||||
return results.map((item) => {
|
||||
@@ -114,17 +104,20 @@ const VariableEdit = ({
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Flex
|
||||
{...BoxBtnStyles}
|
||||
alignItems={'center'}
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
mr={'-5px'}
|
||||
fontSize={'md'}
|
||||
onClick={() => {
|
||||
resetEdit({ variable: addVariable() });
|
||||
onOpenEdit();
|
||||
}}
|
||||
>
|
||||
<SmallAddIcon />
|
||||
{t('common.Add New')}
|
||||
</Flex>
|
||||
</Button>
|
||||
</Flex>
|
||||
{formatVariables.length > 0 && (
|
||||
<Box mt={2} borderRadius={'md'} overflow={'hidden'} borderWidth={'1px'} borderBottom="none">
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import type { ContextExtractAgentItemType } from '@fastgpt/global/core/module/type';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { ModalBody, Button, ModalFooter, useDisclosure, Textarea, Box } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { onChangeNode } from '../../../FlowProvider';
|
||||
|
||||
@@ -41,6 +41,47 @@ import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import RenderToolInput from '../../render/RenderToolInput';
|
||||
const OpenApiImportModal = dynamic(() => import('./OpenApiImportModal'));
|
||||
|
||||
export const HttpHeaders = [
|
||||
{ key: 'A-IM', label: 'A-IM' },
|
||||
{ key: 'Accept', label: 'Accept' },
|
||||
{ key: 'Accept-Charset', label: 'Accept-Charset' },
|
||||
{ key: 'Accept-Encoding', label: 'Accept-Encoding' },
|
||||
{ key: 'Accept-Language', label: 'Accept-Language' },
|
||||
{ key: 'Accept-Datetime', label: 'Accept-Datetime' },
|
||||
{ key: 'Access-Control-Request-Method', label: 'Access-Control-Request-Method' },
|
||||
{ key: 'Access-Control-Request-Headers', label: 'Access-Control-Request-Headers' },
|
||||
{ key: 'Authorization', label: 'Authorization' },
|
||||
{ key: 'Cache-Control', label: 'Cache-Control' },
|
||||
{ key: 'Connection', label: 'Connection' },
|
||||
{ key: 'Content-Length', label: 'Content-Length' },
|
||||
{ key: 'Content-Type', label: 'Content-Type' },
|
||||
{ key: 'Cookie', label: 'Cookie' },
|
||||
{ key: 'Date', label: 'Date' },
|
||||
{ key: 'Expect', label: 'Expect' },
|
||||
{ key: 'Forwarded', label: 'Forwarded' },
|
||||
{ key: 'From', label: 'From' },
|
||||
{ key: 'Host', label: 'Host' },
|
||||
{ key: 'If-Match', label: 'If-Match' },
|
||||
{ key: 'If-Modified-Since', label: 'If-Modified-Since' },
|
||||
{ key: 'If-None-Match', label: 'If-None-Match' },
|
||||
{ key: 'If-Range', label: 'If-Range' },
|
||||
{ key: 'If-Unmodified-Since', label: 'If-Unmodified-Since' },
|
||||
{ key: 'Max-Forwards', label: 'Max-Forwards' },
|
||||
{ key: 'Origin', label: 'Origin' },
|
||||
{ key: 'Pragma', label: 'Pragma' },
|
||||
{ key: 'Proxy-Authorization', label: 'Proxy-Authorization' },
|
||||
{ key: 'Range', label: 'Range' },
|
||||
{ key: 'Referer', label: 'Referer' },
|
||||
{ key: 'TE', label: 'TE' },
|
||||
{ key: 'User-Agent', label: 'User-Agent' },
|
||||
{ key: 'Upgrade', label: 'Upgrade' },
|
||||
{ key: 'Via', label: 'Via' },
|
||||
{ key: 'Warning', label: 'Warning' },
|
||||
{ key: 'Dnt', label: 'Dnt' },
|
||||
{ key: 'X-Requested-With', label: 'X-Requested-With' },
|
||||
{ key: 'X-CSRF-Token', label: 'X-CSRF-Token' }
|
||||
];
|
||||
|
||||
enum TabEnum {
|
||||
params = 'params',
|
||||
headers = 'headers',
|
||||
@@ -342,47 +383,6 @@ const RenderForm = ({
|
||||
const [shouldUpdateNode, setShouldUpdateNode] = useState(false);
|
||||
|
||||
const leftVariables = useMemo(() => {
|
||||
const HttpHeaders = [
|
||||
{ key: 'A-IM', label: 'A-IM' },
|
||||
{ key: 'Accept', label: 'Accept' },
|
||||
{ key: 'Accept-Charset', label: 'Accept-Charset' },
|
||||
{ key: 'Accept-Encoding', label: 'Accept-Encoding' },
|
||||
{ key: 'Accept-Language', label: 'Accept-Language' },
|
||||
{ key: 'Accept-Datetime', label: 'Accept-Datetime' },
|
||||
{ key: 'Access-Control-Request-Method', label: 'Access-Control-Request-Method' },
|
||||
{ key: 'Access-Control-Request-Headers', label: 'Access-Control-Request-Headers' },
|
||||
{ key: 'Authorization', label: 'Authorization' },
|
||||
{ key: 'Cache-Control', label: 'Cache-Control' },
|
||||
{ key: 'Connection', label: 'Connection' },
|
||||
{ key: 'Content-Length', label: 'Content-Length' },
|
||||
{ key: 'Content-Type', label: 'Content-Type' },
|
||||
{ key: 'Cookie', label: 'Cookie' },
|
||||
{ key: 'Date', label: 'Date' },
|
||||
{ key: 'Expect', label: 'Expect' },
|
||||
{ key: 'Forwarded', label: 'Forwarded' },
|
||||
{ key: 'From', label: 'From' },
|
||||
{ key: 'Host', label: 'Host' },
|
||||
{ key: 'If-Match', label: 'If-Match' },
|
||||
{ key: 'If-Modified-Since', label: 'If-Modified-Since' },
|
||||
{ key: 'If-None-Match', label: 'If-None-Match' },
|
||||
{ key: 'If-Range', label: 'If-Range' },
|
||||
{ key: 'If-Unmodified-Since', label: 'If-Unmodified-Since' },
|
||||
{ key: 'Max-Forwards', label: 'Max-Forwards' },
|
||||
{ key: 'Origin', label: 'Origin' },
|
||||
{ key: 'Pragma', label: 'Pragma' },
|
||||
{ key: 'Proxy-Authorization', label: 'Proxy-Authorization' },
|
||||
{ key: 'Range', label: 'Range' },
|
||||
{ key: 'Referer', label: 'Referer' },
|
||||
{ key: 'TE', label: 'TE' },
|
||||
{ key: 'User-Agent', label: 'User-Agent' },
|
||||
{ key: 'Upgrade', label: 'Upgrade' },
|
||||
{ key: 'Via', label: 'Via' },
|
||||
{ key: 'Warning', label: 'Warning' },
|
||||
{ key: 'Dnt', label: 'Dnt' },
|
||||
{ key: 'X-Requested-With', label: 'X-Requested-With' },
|
||||
{ key: 'X-CSRF-Token', label: 'X-CSRF-Token' }
|
||||
];
|
||||
|
||||
return (tabType === TabEnum.headers ? HttpHeaders : variables).filter((variable) => {
|
||||
const existVariables = list.map((item) => item.key);
|
||||
return !existVariables.includes(variable.key);
|
||||
@@ -455,7 +455,7 @@ const RenderForm = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContainer>
|
||||
<TableContainer overflowY={'visible'} overflowX={'unset'}>
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
|
||||
@@ -62,44 +62,48 @@ const NodePluginInput = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
|
||||
position={'relative'}
|
||||
mb={7}
|
||||
>
|
||||
<MyIcon
|
||||
name={'common/settingLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
mr={3}
|
||||
_hover={{ color: 'primary.500' }}
|
||||
onClick={() =>
|
||||
setEditField({
|
||||
inputType: item.type,
|
||||
valueType: item.valueType,
|
||||
key: item.key,
|
||||
label: item.label,
|
||||
description: item.description,
|
||||
required: item.required,
|
||||
isToolInput: !!item.toolDescription
|
||||
})
|
||||
}
|
||||
/>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
mr={3}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'delInput',
|
||||
key: item.key
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'delOutput',
|
||||
key: item.key
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{item.edit && (
|
||||
<>
|
||||
<MyIcon
|
||||
name={'common/settingLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
mr={3}
|
||||
_hover={{ color: 'primary.500' }}
|
||||
onClick={() =>
|
||||
setEditField({
|
||||
inputType: item.type,
|
||||
valueType: item.valueType,
|
||||
key: item.key,
|
||||
label: item.label,
|
||||
description: item.description,
|
||||
required: item.required,
|
||||
isToolInput: !!item.toolDescription
|
||||
})
|
||||
}
|
||||
/>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
mr={3}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'delInput',
|
||||
key: item.key
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'delOutput',
|
||||
key: item.key
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{item.description && (
|
||||
<MyTooltip label={t(item.description)} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} mr={1} />
|
||||
|
||||
@@ -10,7 +10,12 @@ import RenderToolInput from '../render/RenderToolInput';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useFlowProviderStore } from '../../FlowProvider';
|
||||
|
||||
const NodeSimple = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
|
||||
const NodeSimple = ({
|
||||
data,
|
||||
selected,
|
||||
minW = '350px',
|
||||
maxW
|
||||
}: NodeProps<FlowModuleItemType> & { minW?: string | number; maxW?: string | number }) => {
|
||||
const { t } = useTranslation();
|
||||
const { splitToolInputs } = useFlowProviderStore();
|
||||
const { moduleId, inputs, outputs } = data;
|
||||
@@ -22,7 +27,7 @@ const NodeSimple = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeCard minW={'350px'} selected={selected} {...data}>
|
||||
<NodeCard minW={minW} maxW={maxW} selected={selected} {...data}>
|
||||
{toolInputs.length > 0 && (
|
||||
<>
|
||||
<Divider text={t('core.module.tool.Tool input')} />
|
||||
|
||||
@@ -10,10 +10,10 @@ import {
|
||||
Textarea
|
||||
} from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { DYNAMIC_INPUT_KEY, ModuleIOValueTypeEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { FlowValueTypeMap } from '@/web/core/modules/constants/dataType';
|
||||
import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum
|
||||
@@ -265,10 +265,6 @@ const FieldEditModal = ({
|
||||
placeholder="appointment/sql"
|
||||
{...register('key', {
|
||||
required: true,
|
||||
pattern: {
|
||||
value: /^[a-zA-Z]+[0-9]*$/,
|
||||
message: '字段key必须是纯英文字母或数字,并且不能以数字开头。'
|
||||
},
|
||||
onChange: (e) => {
|
||||
const value = e.target.value;
|
||||
// auto fill label
|
||||
|
||||
@@ -6,13 +6,7 @@ import type { FlowModuleItemType } from '@fastgpt/global/core/module/type.d';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import {
|
||||
onChangeNode,
|
||||
onCopyNode,
|
||||
onResetNode,
|
||||
onDelNode,
|
||||
useFlowProviderStore
|
||||
} from '../../FlowProvider';
|
||||
import { onChangeNode, onCopyNode, onResetNode, useFlowProviderStore } from '../../FlowProvider';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
@@ -26,6 +20,7 @@ import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea';
|
||||
type Props = FlowModuleItemType & {
|
||||
children?: React.ReactNode | React.ReactNode[] | string;
|
||||
minW?: string | number;
|
||||
maxW?: string | number;
|
||||
forbidMenu?: boolean;
|
||||
selected?: boolean;
|
||||
};
|
||||
@@ -38,6 +33,7 @@ const NodeCard = (props: Props) => {
|
||||
name = t('core.module.template.UnKnow Module'),
|
||||
intro,
|
||||
minW = '300px',
|
||||
maxW = '600px',
|
||||
moduleId,
|
||||
flowType,
|
||||
inputs,
|
||||
@@ -48,21 +44,25 @@ const NodeCard = (props: Props) => {
|
||||
|
||||
const { toast } = useToast();
|
||||
const { setLoading } = useSystemStore();
|
||||
const { nodes, splitToolInputs } = useFlowProviderStore();
|
||||
const { nodes, splitToolInputs, onDelNode } = useFlowProviderStore();
|
||||
// edit intro
|
||||
const { onOpenModal: onOpenIntroModal, EditModal: EditIntroModal } = useEditTextarea({
|
||||
title: t('core.module.Edit intro'),
|
||||
tip: '调整该模块会对工具调用时机有影响。\n你可以通过精确的描述该模块功能,引导模型进行工具调用。',
|
||||
canEmpty: false
|
||||
});
|
||||
|
||||
// custom title edit
|
||||
const { onOpenModal, EditModal: EditTitleModal } = useEditTitle({
|
||||
title: t('common.Custom Title'),
|
||||
placeholder: t('app.module.Custom Title Tip') || ''
|
||||
});
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
const { openConfirm: onOpenConfirmSync, ConfirmModal: ConfirmSyncModal } = useConfirm({
|
||||
content: t('module.Confirm Sync Plugin')
|
||||
});
|
||||
const { openConfirm: onOpenConfirmDeleteNode, ConfirmModal: ConfirmDeleteModal } = useConfirm({
|
||||
content: t('core.module.Confirm Delete Node'),
|
||||
type: 'delete'
|
||||
});
|
||||
|
||||
const showToolHandle = useMemo(
|
||||
() => isTool && !!nodes.find((item) => item.data?.flowType === FlowNodeTypeEnum.tools),
|
||||
@@ -86,7 +86,7 @@ const NodeCard = (props: Props) => {
|
||||
(item) => item.key === ModuleInputKeyEnum.pluginId
|
||||
)?.value;
|
||||
if (!pluginId) return;
|
||||
openConfirm(async () => {
|
||||
onOpenConfirmSync(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const pluginModule = await getPreviewPluginModule(pluginId);
|
||||
@@ -140,7 +140,7 @@ const NodeCard = (props: Props) => {
|
||||
icon: 'delete',
|
||||
label: t('common.Delete'),
|
||||
variant: 'whiteDanger',
|
||||
onClick: () => onDelNode(moduleId)
|
||||
onClick: onOpenConfirmDeleteNode(() => onDelNode(moduleId))
|
||||
}
|
||||
];
|
||||
|
||||
@@ -211,21 +211,23 @@ const NodeCard = (props: Props) => {
|
||||
</Box>
|
||||
);
|
||||
}, [
|
||||
avatar,
|
||||
flowType,
|
||||
forbidMenu,
|
||||
inputs,
|
||||
intro,
|
||||
moduleId,
|
||||
moduleIsTool,
|
||||
name,
|
||||
onOpenIntroModal,
|
||||
onOpenModal,
|
||||
openConfirm,
|
||||
setLoading,
|
||||
showToolHandle,
|
||||
t,
|
||||
toast
|
||||
onOpenConfirmDeleteNode,
|
||||
showToolHandle,
|
||||
moduleId,
|
||||
avatar,
|
||||
name,
|
||||
forbidMenu,
|
||||
intro,
|
||||
moduleIsTool,
|
||||
inputs,
|
||||
onOpenConfirmSync,
|
||||
setLoading,
|
||||
toast,
|
||||
onOpenModal,
|
||||
onDelNode,
|
||||
onOpenIntroModal
|
||||
]);
|
||||
|
||||
const RenderModal = useMemo(() => {
|
||||
@@ -233,15 +235,16 @@ const NodeCard = (props: Props) => {
|
||||
<>
|
||||
<EditTitleModal maxLength={20} />
|
||||
{moduleIsTool && <EditIntroModal maxLength={500} />}
|
||||
<ConfirmModal />
|
||||
<ConfirmSyncModal />
|
||||
<ConfirmDeleteModal />
|
||||
</>
|
||||
);
|
||||
}, [ConfirmModal, EditIntroModal, EditTitleModal, moduleIsTool]);
|
||||
}, [ConfirmDeleteModal, ConfirmSyncModal, EditIntroModal, EditTitleModal, moduleIsTool]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
minW={minW}
|
||||
maxW={'600px'}
|
||||
maxW={maxW}
|
||||
bg={'white'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={selected ? 'primary.600' : 'borderColor.base'}
|
||||
|
||||
@@ -42,12 +42,12 @@ const RenderList: {
|
||||
Component: dynamic(() => import('./templates/SelectApp'))
|
||||
},
|
||||
{
|
||||
types: [FlowNodeInputTypeEnum.aiSettings],
|
||||
Component: dynamic(() => import('./templates/AiSetting'))
|
||||
types: [FlowNodeInputTypeEnum.selectLLMModel],
|
||||
Component: dynamic(() => import('./templates/SelectLLMModel'))
|
||||
},
|
||||
{
|
||||
types: [FlowNodeInputTypeEnum.selectLLMModel],
|
||||
Component: dynamic(() => import('./templates/SelectAiModel'))
|
||||
types: [FlowNodeInputTypeEnum.settingLLMModel],
|
||||
Component: dynamic(() => import('./templates/SettingLLMModel'))
|
||||
},
|
||||
{
|
||||
types: [FlowNodeInputTypeEnum.selectDataset],
|
||||
@@ -64,6 +64,10 @@ const RenderList: {
|
||||
{
|
||||
types: [FlowNodeInputTypeEnum.JSONEditor],
|
||||
Component: dynamic(() => import('./templates/JsonEditor'))
|
||||
},
|
||||
{
|
||||
types: [FlowNodeInputTypeEnum.settingDatasetQuotePrompt],
|
||||
Component: dynamic(() => import('./templates/SettingQuotePrompt'))
|
||||
}
|
||||
];
|
||||
const UserChatInput = dynamic(() => import('./templates/UserChatInput'));
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { onChangeNode } from '../../../../FlowProvider';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Button, useDisclosure } from '@chakra-ui/react';
|
||||
import { AIChatModuleProps } from '@fastgpt/global/core/module/node/type';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import AIChatSettingsModal from '@/components/core/module/AIChatSettingsModal';
|
||||
|
||||
const AiSettingRender = ({ inputs = [], moduleId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const chatModulesData = useMemo(() => {
|
||||
const obj: Record<string, any> = {};
|
||||
inputs.forEach((item) => {
|
||||
obj[item.key] = item.value;
|
||||
});
|
||||
return obj as AIChatModuleProps;
|
||||
}, [inputs]);
|
||||
|
||||
const {
|
||||
isOpen: isOpenAIChatSetting,
|
||||
onOpen: onOpenAIChatSetting,
|
||||
onClose: onCloseAIChatSetting
|
||||
} = useDisclosure();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<MyIcon name={'common/settingLight'} w={'14px'} />}
|
||||
onClick={onOpenAIChatSetting}
|
||||
>
|
||||
{t('app.AI Settings')}
|
||||
</Button>
|
||||
{isOpenAIChatSetting && (
|
||||
<AIChatSettingsModal
|
||||
isAdEdit
|
||||
onClose={onCloseAIChatSetting}
|
||||
onSuccess={(e) => {
|
||||
for (let key in e) {
|
||||
const item = inputs.find((input) => input.key === key);
|
||||
if (!item) continue;
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key,
|
||||
value: {
|
||||
...item,
|
||||
//@ts-ignore
|
||||
value: e[key]
|
||||
}
|
||||
});
|
||||
}
|
||||
onCloseAIChatSetting();
|
||||
}}
|
||||
defaultData={chatModulesData}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(AiSettingRender);
|
||||
@@ -1,83 +0,0 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { onChangeNode } from '../../../../FlowProvider';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import SelectAiModel from '@/components/Select/SelectAiModel';
|
||||
import { llmModelTypeFilterMap } from '@fastgpt/global/core/ai/constants';
|
||||
|
||||
const SelectAiModelRender = ({ item, inputs = [], moduleId }: RenderInputProps) => {
|
||||
const { llmModelList } = useSystemStore();
|
||||
|
||||
const modelList = llmModelList
|
||||
.filter((model) => {
|
||||
if (!item.llmModelType) return true;
|
||||
const filterField = llmModelTypeFilterMap[item.llmModelType];
|
||||
if (!filterField) return true;
|
||||
//@ts-ignore
|
||||
return !!model[filterField];
|
||||
})
|
||||
.map((item) => ({
|
||||
model: item.model,
|
||||
name: item.name,
|
||||
maxResponse: item.maxResponse
|
||||
}));
|
||||
|
||||
const onChangeModel = useCallback(
|
||||
(e: string) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
|
||||
// update max tokens
|
||||
const model = modelList.find((item) => item.model === e) || modelList[0];
|
||||
if (!model) return;
|
||||
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: 'maxToken',
|
||||
value: {
|
||||
...inputs.find((input) => input.key === 'maxToken'),
|
||||
markList: [
|
||||
{ label: '100', value: 100 },
|
||||
{ label: `${model.maxResponse}`, value: model.maxResponse }
|
||||
],
|
||||
max: model.maxResponse,
|
||||
value: model.maxResponse / 2
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputs, item, modelList, moduleId]
|
||||
);
|
||||
|
||||
const list = modelList.map((item) => {
|
||||
return {
|
||||
value: item.model,
|
||||
label: item.name
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!item.value && list.length > 0) {
|
||||
onChangeModel(list[0].value);
|
||||
}
|
||||
}, [item.value, list, onChangeModel]);
|
||||
|
||||
return (
|
||||
<SelectAiModel
|
||||
minW={'350px'}
|
||||
width={'100%'}
|
||||
value={item.value}
|
||||
list={list}
|
||||
onchange={onChangeModel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SelectAiModelRender);
|
||||
@@ -0,0 +1,54 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { onChangeNode } from '../../../../FlowProvider';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { llmModelTypeFilterMap } from '@fastgpt/global/core/ai/constants';
|
||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||
|
||||
const SelectAiModelRender = ({ item, moduleId }: RenderInputProps) => {
|
||||
const { llmModelList } = useSystemStore();
|
||||
|
||||
const modelList = llmModelList.filter((model) => {
|
||||
if (!item.llmModelType) return true;
|
||||
const filterField = llmModelTypeFilterMap[item.llmModelType];
|
||||
if (!filterField) return true;
|
||||
//@ts-ignore
|
||||
return !!model[filterField];
|
||||
});
|
||||
|
||||
const onChangeModel = useCallback(
|
||||
(e: string) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
},
|
||||
[item, moduleId]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!item.value && modelList.length > 0) {
|
||||
onChangeModel(modelList[0].model);
|
||||
}
|
||||
}, [item.value, modelList, onChangeModel]);
|
||||
|
||||
return (
|
||||
<AIModelSelector
|
||||
minW={'350px'}
|
||||
width={'100%'}
|
||||
value={item.value}
|
||||
list={modelList.map((item) => ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
}))}
|
||||
onchange={onChangeModel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SelectAiModelRender);
|
||||
@@ -0,0 +1,49 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { onChangeNode } from '../../../../FlowProvider';
|
||||
import { SettingAIDataType } from '@fastgpt/global/core/module/node/type';
|
||||
import SettingLLMModel from '@/components/core/ai/SettingLLMModel';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
|
||||
const SelectAiModelRender = ({ item, inputs = [], moduleId }: RenderInputProps) => {
|
||||
const onChangeModel = useCallback(
|
||||
(e: SettingAIDataType) => {
|
||||
for (const key in e) {
|
||||
const input = inputs.find((input) => input.key === key);
|
||||
input &&
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key,
|
||||
value: {
|
||||
...input,
|
||||
// @ts-ignore
|
||||
value: e[key]
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
[inputs, moduleId]
|
||||
);
|
||||
|
||||
const llmModelData: SettingAIDataType = {
|
||||
model: inputs.find((input) => input.key === ModuleInputKeyEnum.aiModel)?.value ?? '',
|
||||
maxToken:
|
||||
inputs.find((input) => input.key === ModuleInputKeyEnum.aiChatMaxToken)?.value ?? 2048,
|
||||
temperature:
|
||||
inputs.find((input) => input.key === ModuleInputKeyEnum.aiChatTemperature)?.value ?? 1,
|
||||
isResponseAnswerText: inputs.find(
|
||||
(input) => input.key === ModuleInputKeyEnum.aiChatIsResponseText
|
||||
)?.value
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingLLMModel
|
||||
llmModelType={item.llmModelType}
|
||||
defaultData={llmModelData}
|
||||
onChange={onChangeModel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SelectAiModelRender);
|
||||
@@ -0,0 +1,243 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { Box, BoxProps, Button, Flex, ModalFooter, useDisclosure } from '@chakra-ui/react';
|
||||
import { onChangeNode, useFlowProviderStore } from '../../../../FlowProvider';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
formatEditorVariablePickerIcon,
|
||||
getGuideModule,
|
||||
splitGuideModule
|
||||
} from '@fastgpt/global/core/module/utils';
|
||||
import { ModalBody } from '@chakra-ui/react';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import {
|
||||
Prompt_QuotePromptList,
|
||||
Prompt_QuoteTemplateList
|
||||
} from '@fastgpt/global/core/ai/prompt/AIChat';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import PromptTemplate from '@/components/PromptTemplate';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
|
||||
const SettingQuotePrompt = ({ inputs = [], moduleId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { nodes } = useFlowProviderStore();
|
||||
const { watch, setValue, handleSubmit } = useForm({
|
||||
defaultValues: {
|
||||
quoteTemplate: inputs.find((input) => input.key === 'quoteTemplate')?.value || '',
|
||||
quotePrompt: inputs.find((input) => input.key === 'quotePrompt')?.value || ''
|
||||
}
|
||||
});
|
||||
const aiChatQuoteTemplate = watch('quoteTemplate');
|
||||
const aiChatQuotePrompt = watch('quotePrompt');
|
||||
|
||||
const variables = useMemo(() => {
|
||||
const globalVariables = formatEditorVariablePickerIcon(
|
||||
splitGuideModule(getGuideModule(nodes.map((node) => node.data)))?.variableModules || []
|
||||
);
|
||||
const moduleVariables = formatEditorVariablePickerIcon(
|
||||
inputs
|
||||
.filter((input) => input.edit)
|
||||
.map((item) => ({
|
||||
key: item.key,
|
||||
label: item.label
|
||||
}))
|
||||
);
|
||||
const systemVariables = [
|
||||
{
|
||||
key: 'cTime',
|
||||
label: t('core.module.http.Current time')
|
||||
}
|
||||
];
|
||||
|
||||
return [...globalVariables, ...moduleVariables, ...systemVariables];
|
||||
}, [inputs, t]);
|
||||
const [selectTemplateData, setSelectTemplateData] = useState<{
|
||||
title: string;
|
||||
templates: PromptTemplateItem[];
|
||||
}>();
|
||||
const quoteTemplateVariables = (() => [
|
||||
{
|
||||
key: 'q',
|
||||
label: 'q',
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'a',
|
||||
label: 'a',
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'source',
|
||||
label: t('core.dataset.search.Source name'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'sourceId',
|
||||
label: t('core.dataset.search.Source id'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'index',
|
||||
label: t('core.dataset.search.Quote index'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
...variables
|
||||
])();
|
||||
const quotePromptVariables = (() => [
|
||||
{
|
||||
key: 'quote',
|
||||
label: t('core.app.Quote templates'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'question',
|
||||
label: t('core.module.input.label.user question'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
...variables
|
||||
])();
|
||||
|
||||
const LabelStyles: BoxProps = {
|
||||
fontSize: ['sm', 'md']
|
||||
};
|
||||
const selectTemplateBtn: BoxProps = {
|
||||
color: 'primary.500',
|
||||
cursor: 'pointer'
|
||||
};
|
||||
|
||||
const onSubmit = (data: { quoteTemplate: string; quotePrompt: string }) => {
|
||||
const quoteTemplateInput = inputs.find(
|
||||
(input) => input.key === ModuleInputKeyEnum.aiChatQuoteTemplate
|
||||
);
|
||||
const quotePromptInput = inputs.find(
|
||||
(input) => input.key === ModuleInputKeyEnum.aiChatQuotePrompt
|
||||
);
|
||||
if (quoteTemplateInput) {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: quoteTemplateInput.key,
|
||||
value: {
|
||||
...quoteTemplateInput,
|
||||
value: data.quoteTemplate
|
||||
}
|
||||
});
|
||||
}
|
||||
if (quotePromptInput) {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: quotePromptInput.key,
|
||||
value: {
|
||||
...quotePromptInput,
|
||||
value: data.quotePrompt
|
||||
}
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant={'whitePrimary'} size={'sm'} onClick={onOpen}>
|
||||
{t('core.module.Setting quote prompt')}
|
||||
</Button>
|
||||
<MyModal
|
||||
isOpen={isOpen}
|
||||
iconSrc={'modal/edit'}
|
||||
title={t('core.module.Quote prompt setting')}
|
||||
w={'600px'}
|
||||
>
|
||||
<ModalBody>
|
||||
<Box>
|
||||
<Flex {...LabelStyles} mb={1}>
|
||||
{t('core.app.Quote templates')}
|
||||
<MyTooltip
|
||||
label={t('template.Quote Content Tip', {
|
||||
default: Prompt_QuoteTemplateList[0].value
|
||||
})}
|
||||
forceShow
|
||||
>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
<Box flex={1} />
|
||||
<Box
|
||||
{...selectTemplateBtn}
|
||||
onClick={() =>
|
||||
setSelectTemplateData({
|
||||
title: t('core.app.Select quote template'),
|
||||
templates: Prompt_QuoteTemplateList
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('common.Select template')}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<PromptEditor
|
||||
variables={quoteTemplateVariables}
|
||||
h={160}
|
||||
title={t('core.app.Quote templates')}
|
||||
placeholder={t('template.Quote Content Tip', {
|
||||
default: Prompt_QuoteTemplateList[0].value
|
||||
})}
|
||||
value={aiChatQuoteTemplate}
|
||||
onChange={(e) => {
|
||||
setValue('quoteTemplate', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box mt={4}>
|
||||
<Flex {...LabelStyles} mb={1}>
|
||||
{t('core.app.Quote prompt')}
|
||||
<MyTooltip
|
||||
label={t('template.Quote Prompt Tip', { default: Prompt_QuotePromptList[0].value })}
|
||||
forceShow
|
||||
>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<PromptEditor
|
||||
variables={quotePromptVariables}
|
||||
title={t('core.app.Quote prompt')}
|
||||
h={280}
|
||||
placeholder={t('template.Quote Prompt Tip', {
|
||||
default: Prompt_QuotePromptList[0].value
|
||||
})}
|
||||
value={aiChatQuotePrompt}
|
||||
onChange={(e) => {
|
||||
setValue('quotePrompt', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={2} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button onClick={handleSubmit(onSubmit)}>{t('common.Confirm')}</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
{!!selectTemplateData && (
|
||||
<PromptTemplate
|
||||
title={selectTemplateData.title}
|
||||
templates={selectTemplateData.templates}
|
||||
onClose={() => setSelectTemplateData(undefined)}
|
||||
onSuccess={(e) => {
|
||||
const quoteVal = e.value;
|
||||
const promptVal = Prompt_QuotePromptList.find((item) => item.title === e.title)?.value;
|
||||
setValue('quoteTemplate', quoteVal);
|
||||
setValue('quotePrompt', promptVal);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SettingQuotePrompt);
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import type { EditFieldModalProps } from './type.d';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
import { Handle, Position } from 'reactflow';
|
||||
import { FlowValueTypeMap } from '@/web/core/modules/constants/dataType';
|
||||
import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ModuleIOValueTypeEnum } from '@fastgpt/global/core/module/constants';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
import { Handle, OnConnect, Position } from 'reactflow';
|
||||
import { FlowValueTypeMap } from '@/web/core/modules/constants/dataType';
|
||||
import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ModuleIOValueTypeEnum } from '@fastgpt/global/core/module/constants';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { FlowValueTypeMap } from '@/web/core/modules/constants/dataType';
|
||||
import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
import {
|
||||
ModuleIOValueTypeEnum,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import ReactFlow, { Background, Connection, Controls, ReactFlowProvider } from 'reactflow';
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
Connection,
|
||||
Controls,
|
||||
NodeProps,
|
||||
ReactFlowProvider
|
||||
} from 'reactflow';
|
||||
import { Box, Flex, IconButton, useDisclosure } from '@chakra-ui/react';
|
||||
import { SmallCloseIcon } from '@chakra-ui/icons';
|
||||
import { EDGE_TYPE, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
@@ -7,12 +13,13 @@ import { EDGE_TYPE, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/co
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import ButtonEdge from './components/modules/ButtonEdge';
|
||||
import ModuleTemplateList, { type ModuleTemplateProps } from './ModuleTemplateList';
|
||||
import ModuleTemplateList from './ModuleTemplateList';
|
||||
import { useFlowProviderStore } from './FlowProvider';
|
||||
|
||||
import 'reactflow/dist/style.css';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { FlowModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
|
||||
const NodeSimple = dynamic(() => import('./components/nodes/NodeSimple'));
|
||||
const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = {
|
||||
@@ -34,7 +41,10 @@ const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = {
|
||||
[FlowNodeTypeEnum.pluginOutput]: dynamic(() => import('./components/nodes/NodePluginOutput')),
|
||||
[FlowNodeTypeEnum.pluginModule]: NodeSimple,
|
||||
[FlowNodeTypeEnum.queryExtension]: NodeSimple,
|
||||
[FlowNodeTypeEnum.tools]: dynamic(() => import('./components/nodes/NodeTools'))
|
||||
[FlowNodeTypeEnum.tools]: dynamic(() => import('./components/nodes/NodeTools')),
|
||||
[FlowNodeTypeEnum.stopTool]: (data: NodeProps<FlowModuleItemType>) => (
|
||||
<NodeSimple {...data} minW={'100px'} maxW={'300px'} />
|
||||
)
|
||||
};
|
||||
const edgeTypes = {
|
||||
[EDGE_TYPE]: ButtonEdge
|
||||
@@ -99,11 +109,7 @@ const Container = React.memo(function Container() {
|
||||
);
|
||||
});
|
||||
|
||||
const Flow = ({
|
||||
Header,
|
||||
templates,
|
||||
...data
|
||||
}: ModuleTemplateProps & { Header: React.ReactNode }) => {
|
||||
const Flow = ({ Header, ...data }: { Header: React.ReactNode }) => {
|
||||
const {
|
||||
isOpen: isOpenTemplate,
|
||||
onOpen: onOpenTemplate,
|
||||
@@ -143,14 +149,10 @@ const Flow = ({
|
||||
|
||||
<Container {...data} />
|
||||
|
||||
<ModuleTemplateList
|
||||
templates={templates}
|
||||
isOpen={isOpenTemplate}
|
||||
onClose={onCloseTemplate}
|
||||
/>
|
||||
<ModuleTemplateList isOpen={isOpenTemplate} onClose={onCloseTemplate} />
|
||||
</Box>
|
||||
);
|
||||
}, [data, isOpenTemplate, onCloseTemplate, onOpenTemplate, templates]);
|
||||
}, [data, isOpenTemplate, onCloseTemplate, onOpenTemplate]);
|
||||
|
||||
return (
|
||||
<Box h={'100%'} position={'fixed'} zIndex={999} top={0} left={0} right={0} bottom={0}>
|
||||
|
||||
@@ -35,7 +35,7 @@ import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
|
||||
@@ -12,7 +12,9 @@ const PermissionIconText = ({
|
||||
return PermissionTypeMap[permission] ? (
|
||||
<Flex alignItems={'center'} {...props}>
|
||||
<MyIcon name={PermissionTypeMap[permission]?.iconLight as any} w={'14px'} />
|
||||
<Box ml={'2px'}>{t(PermissionTypeMap[permission]?.label)}</Box>
|
||||
<Box ml={'2px'} lineHeight={1}>
|
||||
{t(PermissionTypeMap[permission]?.label)}
|
||||
</Box>
|
||||
</Flex>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ModalCloseButton, ModalBody, Box, ModalFooter, Button } from '@chakra-ui/react';
|
||||
import TagTextarea from '@/components/common/Textarea/TagTextarea';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { DragHandleIcon } from '@chakra-ui/icons';
|
||||
@@ -194,7 +194,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
bg: 'myGray.100'
|
||||
}
|
||||
})}
|
||||
onClick={() => onSwitchTeam(team.teamId)}
|
||||
>
|
||||
<Avatar src={team.avatar} w={['18px', '22px']} />
|
||||
<Box
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import {
|
||||
Button,
|
||||
ModalFooter,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Button, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
const NotSufficientModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { setIsNotSufficientModal } = useSystemStore();
|
||||
|
||||
const onClose = () => setIsNotSufficientModal(false);
|
||||
|
||||
return (
|
||||
<MyModal isOpen iconSrc="common/confirm/deleteTip" title={t('common.Warning')}>
|
||||
<ModalBody>{t('support.wallet.Not sufficient')}</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={2} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
router.push('/account');
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('support.wallet.To read plan')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotSufficientModal;
|
||||
@@ -1,4 +1,4 @@
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
|
||||
@@ -5,6 +5,10 @@ import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constant
|
||||
import { Box, Flex, Grid } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
|
||||
|
||||
const StandardPlanContentList = ({
|
||||
level,
|
||||
@@ -15,6 +19,7 @@ const StandardPlanContentList = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { subPlans } = useSystemStore();
|
||||
const router = useRouter();
|
||||
|
||||
const planContent = useMemo(() => {
|
||||
const plan = subPlans?.standard?.[level];
|
||||
@@ -87,6 +92,14 @@ const StandardPlanContentList = ({
|
||||
amount: planContent.totalPoints
|
||||
})}
|
||||
</Box>
|
||||
<MyTooltip label={t('support.wallet.subscription.AI points click to read tip')}>
|
||||
<QuestionOutlineIcon
|
||||
ml={'2px'}
|
||||
onClick={() => {
|
||||
router.push(AI_POINT_USAGE_CARD_ROUTE);
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
|
||||
@@ -6,7 +6,6 @@ export const defaultApp: AppDetailType = {
|
||||
userId: 'userId',
|
||||
name: '应用加载中',
|
||||
type: 'simple',
|
||||
simpleTemplateId: 'fastgpt-universal',
|
||||
avatar: '/icon/logo.svg',
|
||||
intro: '',
|
||||
updateTime: Date.now(),
|
||||
|
||||
@@ -7,7 +7,6 @@ import type {
|
||||
} from '@fastgpt/global/core/ai/model.d';
|
||||
|
||||
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';
|
||||
import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type';
|
||||
import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type';
|
||||
|
||||
export type InitDateResponse = {
|
||||
@@ -19,5 +18,4 @@ export type InitDateResponse = {
|
||||
feConfigs: FastGPTFeConfigsType;
|
||||
subPlans?: SubPlanType;
|
||||
systemVersion: string;
|
||||
simpleModeTemplates: AppSimpleEditConfigTemplateType[];
|
||||
};
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type.d';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
export const SimpleModeTemplate_FastGPT_Universal: AppSimpleEditConfigTemplateType = {
|
||||
id: 'fastgpt-universal',
|
||||
name: 'core.app.template.Common template',
|
||||
desc: 'core.app.template.Common template tip',
|
||||
systemForm: {
|
||||
aiSettings: {
|
||||
model: true,
|
||||
systemPrompt: true,
|
||||
temperature: true,
|
||||
maxToken: true,
|
||||
quoteTemplate: true,
|
||||
quotePrompt: true
|
||||
},
|
||||
dataset: {
|
||||
datasets: true,
|
||||
similarity: true,
|
||||
limit: true,
|
||||
searchMode: DatasetSearchModeEnum.embedding,
|
||||
usingReRank: true,
|
||||
searchEmptyText: true
|
||||
},
|
||||
userGuide: {
|
||||
welcomeText: true,
|
||||
variables: true,
|
||||
questionGuide: true,
|
||||
tts: true
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -19,7 +19,6 @@ export type CreateDatasetParams = {
|
||||
avatar: string;
|
||||
vectorModel?: string;
|
||||
agentModel?: string;
|
||||
type: `${DatasetTypeEnum}`;
|
||||
};
|
||||
|
||||
/* ================= collection ===================== */
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
|
||||
|
||||
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
||||
{
|
||||
title: '标准模板',
|
||||
desc: '标准提示词,用于结构不固定的知识库。',
|
||||
value: `{{q}}
|
||||
{{a}}`
|
||||
},
|
||||
{
|
||||
title: '问答模板',
|
||||
desc: '适合 QA 问答结构的知识库,可以让AI较为严格的按预设内容回答',
|
||||
value: `<Question>
|
||||
{{q}}
|
||||
</Question>
|
||||
<Answer>
|
||||
{{a}}
|
||||
</Answer>`
|
||||
},
|
||||
{
|
||||
title: '标准严格模板',
|
||||
desc: '在标准模板基础上,对模型的回答做更严格的要求。',
|
||||
value: `{{q}}
|
||||
{{a}}`
|
||||
},
|
||||
{
|
||||
title: '严格问答模板',
|
||||
desc: '在问答模板基础上,对模型的回答做更严格的要求。',
|
||||
value: `<Question>
|
||||
{{q}}
|
||||
</Question>
|
||||
<Answer>
|
||||
{{a}}
|
||||
</Answer>`
|
||||
}
|
||||
];
|
||||
|
||||
export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
{
|
||||
title: '标准模板',
|
||||
desc: '',
|
||||
value: `使用 <Data></Data> 标记中的内容作为你的知识:
|
||||
|
||||
<Data>
|
||||
{{quote}}
|
||||
</Data>
|
||||
|
||||
回答要求:
|
||||
- 如果你不清楚答案,你需要澄清。
|
||||
- 避免提及你是从 <Data></Data> 获取的知识。
|
||||
- 保持答案与 <Data></Data> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
},
|
||||
{
|
||||
title: '问答模板',
|
||||
desc: '',
|
||||
value: `使用 <QA></QA> 标记中的问答对进行回答。
|
||||
|
||||
<QA>
|
||||
{{quote}}
|
||||
</QA>
|
||||
|
||||
回答要求:
|
||||
- 选择其中一个或多个问答对进行回答。
|
||||
- 回答的内容应尽可能与 <答案></答案> 中的内容一致。
|
||||
- 如果没有相关的问答对,你需要澄清。
|
||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
},
|
||||
{
|
||||
title: '标准严格模板',
|
||||
desc: '',
|
||||
value: `忘记你已有的知识,仅使用 <Data></Data> 标记中的内容作为你的知识:
|
||||
|
||||
<Data>
|
||||
{{quote}}
|
||||
</Data>
|
||||
|
||||
思考流程:
|
||||
1. 判断问题是否与 <Data></Data> 标记中的内容有关。
|
||||
2. 如果有关,你按下面的要求回答。
|
||||
3. 如果无关,你直接拒绝回答本次问题。
|
||||
|
||||
回答要求:
|
||||
- 避免提及你是从 <Data></Data> 获取的知识。
|
||||
- 保持答案与 <Data></Data> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
},
|
||||
{
|
||||
title: '严格问答模板',
|
||||
desc: '',
|
||||
value: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
|
||||
|
||||
<QA>
|
||||
{{quote}}
|
||||
</QA>}
|
||||
|
||||
思考流程:
|
||||
1. 判断问题是否与 <QA></QA> 标记中的内容有关。
|
||||
2. 如果无关,你直接拒绝回答本次问题。
|
||||
3. 判断是否有相近或相同的问题。
|
||||
4. 如果有相同的问题,直接输出对应答案。
|
||||
5. 如果只有相近的问题,请把相近的问题和答案一起输出。
|
||||
|
||||
最后,避免提及你是从 QA 获取的知识,只需要回复答案。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
}
|
||||
];
|
||||
@@ -1,62 +0,0 @@
|
||||
export const Prompt_AgentQA = {
|
||||
description: `<Context></Context> 标记中是一段文本,学习和分析它,并整理学习成果:
|
||||
- 提出问题并给出每个问题的答案。
|
||||
- 答案需详细完整,尽可能保留原文描述。
|
||||
- 答案可以包含普通文字、链接、代码、表格、公示、媒体链接等 Markdown 元素。
|
||||
- 最多提出 30 个问题。
|
||||
`,
|
||||
fixedText: `请按以下格式整理学习成果:
|
||||
<Context>
|
||||
文本
|
||||
</Context>
|
||||
Q1: 问题。
|
||||
A1: 答案。
|
||||
Q2:
|
||||
A2:
|
||||
|
||||
------
|
||||
|
||||
我们开始吧!
|
||||
|
||||
<Context>
|
||||
{{text}}
|
||||
<Context/>
|
||||
`
|
||||
};
|
||||
|
||||
export const Prompt_ExtractJson = `你可以从 <对话记录></对话记录> 中提取指定 JSON 信息,你仅需返回 JSON 字符串,无需回答问题。
|
||||
<提取要求>
|
||||
{{description}}
|
||||
</提取要求>
|
||||
|
||||
<字段说明>
|
||||
1. 下面的 JSON 字符串均按照 JSON Schema 的规则描述。
|
||||
2. key 代表字段名;description 代表字段的描述;enum 是可选值,代表可选的 value。
|
||||
3. 如果没有可提取的内容,忽略该字段。
|
||||
4. 本次需提取的JSON Schema:{{json}}
|
||||
</字段说明>
|
||||
|
||||
<对话记录>
|
||||
{{text}}
|
||||
</对话记录>
|
||||
`;
|
||||
|
||||
export const Prompt_CQJson = `我会给你几个问题类型,请参考背景知识(可能为空)和对话记录,判断我“本次问题”的类型,并返回一个问题“类型ID”:
|
||||
<问题类型>
|
||||
{{typeList}}
|
||||
</问题类型>
|
||||
|
||||
<背景知识>
|
||||
{{systemPrompt}}
|
||||
</背景知识>
|
||||
|
||||
<对话记录>
|
||||
{{history}}
|
||||
</对话记录>
|
||||
|
||||
Human:"{{question}}"
|
||||
|
||||
类型ID=
|
||||
`;
|
||||
|
||||
export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题,引导我继续提问。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
|
||||
@@ -30,7 +30,7 @@ import MyBox from '@/components/common/MyBox';
|
||||
import { useRequest } from '@/web/common/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/CustomModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { usePagination } from '@fastgpt/web/hooks/usePagination';
|
||||
|
||||
const BillTable = () => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import type { UserType } from '@fastgpt/global/support/user/type.d';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
@@ -29,16 +30,24 @@ import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tool
|
||||
import { putUpdateMemberName } from '@/web/support/user/team/api';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import {
|
||||
StandardSubLevelEnum,
|
||||
standardSubLevelMap
|
||||
} from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import { formatTime2YMD } from '@fastgpt/global/common/string/time';
|
||||
import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
|
||||
import {
|
||||
AI_POINT_USAGE_CARD_ROUTE,
|
||||
EXTRA_PLAN_CARD_ROUTE
|
||||
} from '@/web/support/wallet/sub/constants';
|
||||
|
||||
import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList';
|
||||
|
||||
const StandDetailModal = dynamic(() => import('./standardDetailModal'));
|
||||
const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu'));
|
||||
const PayModal = dynamic(() => import('./PayModal'));
|
||||
const UpdatePswModal = dynamic(() => import('./UpdatePswModal'));
|
||||
const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal'));
|
||||
const CommunityModal = dynamic(() => import('@/components/CommunityModal'));
|
||||
|
||||
const Account = () => {
|
||||
const { isPc } = useSystemStore();
|
||||
@@ -113,11 +122,11 @@ const MyInfo = () => {
|
||||
});
|
||||
reset(data);
|
||||
toast({
|
||||
title: '更新数据成功',
|
||||
title: t('dataset.data.Update Success Tip'),
|
||||
status: 'success'
|
||||
});
|
||||
},
|
||||
[reset, toast, updateUserInfo]
|
||||
[reset, t, toast, updateUserInfo]
|
||||
);
|
||||
|
||||
const onSelectFile = useCallback(
|
||||
@@ -184,7 +193,7 @@ const MyInfo = () => {
|
||||
cursor={'pointer'}
|
||||
onClick={onOpenSelectFile}
|
||||
>
|
||||
<MyTooltip label={'更换头像'}>
|
||||
<MyTooltip label={t('common.avatar.Select Avatar')}>
|
||||
<Box
|
||||
w={['44px', '54px']}
|
||||
h={['44px', '54px']}
|
||||
@@ -269,7 +278,6 @@ const MyInfo = () => {
|
||||
);
|
||||
};
|
||||
const PlanUsage = () => {
|
||||
const { isPc } = useSystemStore();
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { userInfo, initUserInfo, teamPlanStatus } = useUserStore();
|
||||
@@ -288,6 +296,21 @@ const PlanUsage = () => {
|
||||
return standardSubLevelMap[teamPlanStatus.standard.currentSubLevel].label;
|
||||
}, [teamPlanStatus?.standard?.currentSubLevel]);
|
||||
const standardPlan = teamPlanStatus?.standard;
|
||||
const isFreeTeam = useMemo(() => {
|
||||
if (!teamPlanStatus || !teamPlanStatus?.standardConstants) return false;
|
||||
const hasExtraDatasetSize =
|
||||
teamPlanStatus.datasetMaxSize > teamPlanStatus.standardConstants.maxDatasetSize;
|
||||
const hasExtraPoints =
|
||||
teamPlanStatus.totalPoints > teamPlanStatus.standardConstants.totalPoints;
|
||||
if (
|
||||
teamPlanStatus?.standard?.currentSubLevel === StandardSubLevelEnum.free &&
|
||||
!hasExtraDatasetSize &&
|
||||
!hasExtraPoints
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, [teamPlanStatus]);
|
||||
|
||||
useQuery(['init'], initUserInfo, {
|
||||
onSuccess(res) {
|
||||
@@ -374,6 +397,11 @@ const PlanUsage = () => {
|
||||
<Box fontWeight={'bold'} fontSize="xl">
|
||||
{t(planName)}
|
||||
</Box>
|
||||
{isFreeTeam && (
|
||||
<Box mt="3" color={'#485264'} fontSize="sm">
|
||||
免费版用户15天无任何使用记录时,系统会自动清理账号知识库。
|
||||
</Box>
|
||||
)}
|
||||
<Flex mt="3" color={'#485264'} fontSize="sm">
|
||||
<Box>{t('common.Expired Time')}:</Box>
|
||||
<Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box>
|
||||
@@ -399,9 +427,29 @@ const PlanUsage = () => {
|
||||
borderColor={'borderColor.low'}
|
||||
borderRadius={'md'}
|
||||
px={[5, 10]}
|
||||
py={[4, 7]}
|
||||
pt={[2, 4]}
|
||||
pb={[4, 7]}
|
||||
>
|
||||
<Box width={'100%'}>
|
||||
<Flex>
|
||||
<Flex flex={'1 0 0'} alignItems={'flex-end'}>
|
||||
<Box fontSize={'xl'}>资源用量</Box>
|
||||
<Box fontSize={'sm'} color={'myGray.500'}>
|
||||
(包含标准套餐与额外资源包)
|
||||
</Box>
|
||||
</Flex>
|
||||
<Link
|
||||
href={EXTRA_PLAN_CARD_ROUTE}
|
||||
transform={'translateX(15px)'}
|
||||
display={'flex'}
|
||||
alignItems={'center'}
|
||||
color={'primary.600'}
|
||||
cursor={'pointer'}
|
||||
>
|
||||
购买额外套餐
|
||||
<MyIcon ml={1} name={'common/rightArrowLight'} w={'12px'} />
|
||||
</Link>
|
||||
</Flex>
|
||||
<Box width={'100%'} mt={5}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box fontWeight={'bold'}>{t('support.user.team.Dataset usage')}</Box>
|
||||
@@ -426,7 +474,10 @@ const PlanUsage = () => {
|
||||
<Box mt="9" width={'100%'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box fontWeight={'bold'}>{t('support.wallet.subscription.AI points')}</Box>
|
||||
<Box fontWeight={'bold'}>{t('support.wallet.subscription.AI points usage')}</Box>
|
||||
<MyTooltip label={t('support.wallet.subscription.AI points usage tip')}>
|
||||
<QuestionOutlineIcon ml={'2px'} />
|
||||
</MyTooltip>
|
||||
<Box color={'myGray.600'} ml={2}>
|
||||
{aiPointsUsageMap.used}/{aiPointsUsageMap.max}
|
||||
</Box>
|
||||
@@ -445,7 +496,6 @@ const PlanUsage = () => {
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Flex></Flex>
|
||||
</Box>
|
||||
{isOpenStandardModal && <StandDetailModal onClose={onCloseStandardModal} />}
|
||||
</Box>
|
||||
@@ -462,6 +512,7 @@ const Other = () => {
|
||||
});
|
||||
|
||||
const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure();
|
||||
const { isOpen: isOpenConcat, onClose: onCloseConcat, onOpen: onOpenConcat } = useDisclosure();
|
||||
|
||||
const onclickSave = useCallback(
|
||||
async (data: UserType) => {
|
||||
@@ -552,6 +603,17 @@ const Other = () => {
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{feConfigs?.concatMd && (
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
justifyContent={'flex-start'}
|
||||
leftIcon={<MyIcon name={'modal/concat'} w={'18px'} color={'myGray.600'} />}
|
||||
onClick={onOpenConcat}
|
||||
h={'48px'}
|
||||
>
|
||||
联系我们
|
||||
</Button>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
{isOpenOpenai && userInfo && (
|
||||
@@ -566,6 +628,7 @@ const Other = () => {
|
||||
onClose={onCloseOpenai}
|
||||
/>
|
||||
)}
|
||||
{isOpenConcat && <CommunityModal onClose={onCloseConcat} />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { ModalFooter, ModalBody, Button, Input, Box, Grid } from '@chakra-ui/react';
|
||||
import { ModalFooter, ModalBody, Button, Input, Box, Grid, Link } from '@chakra-ui/react';
|
||||
import { getWxPayQRCode } from '@/web/support/wallet/bill/api';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
|
||||
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { EXTRA_PLAN_CARD_ROUTE } from '@/web/support/wallet/sub/constants';
|
||||
|
||||
const PayModal = ({
|
||||
onClose,
|
||||
@@ -59,8 +60,12 @@ const PayModal = ({
|
||||
return (
|
||||
<MyModal isOpen={true} onClose={onClose} title={t('user.Pay')} iconSrc="/imgs/modal/pay.svg">
|
||||
<ModalBody px={0} display={'flex'} flexDirection={'column'}>
|
||||
<Box px={6} fontSize={'sm'} color={'myGray.600'} mb={2} maxW={'400px'}>
|
||||
该余额仅用于自动续费标准套餐。如需购买额外套餐,可直接下单,无需充值余额。
|
||||
<Box px={6} fontSize={'sm'} mb={2} py={2} maxW={'400px'}>
|
||||
该余额仅用于自动续费标准套餐。如需购买额外套餐,可
|
||||
<Link href={EXTRA_PLAN_CARD_ROUTE} color={'primary.600'} textDecoration={'underline'}>
|
||||
直接下单
|
||||
</Link>
|
||||
,无需充值余额。
|
||||
</Box>
|
||||
<Grid gridTemplateColumns={'repeat(3,1fr)'} gridGap={5} mb={4} px={6}>
|
||||
{payList.map((item) => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { UsageItemType } from '@fastgpt/global/support/wallet/usage/type.d';
|
||||
import dayjs from 'dayjs';
|
||||
import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { formatNumber } from '@fastgpt/global/common/math/tools';
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
TableContainer,
|
||||
ModalCloseButton
|
||||
} from '@chakra-ui/react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { jiebaSplit } from '@/service/common/string/jieba';
|
||||
|
||||
let success = 0;
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { limit = 50 } = req.body as { limit: number };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
success = 0;
|
||||
|
||||
console.log(
|
||||
'total',
|
||||
await MongoDatasetData.countDocuments({
|
||||
fullTextToken: { $exists: false },
|
||||
updateTime: { $lt: new Date() }
|
||||
})
|
||||
);
|
||||
|
||||
await initFullTextToken(limit, new Date());
|
||||
|
||||
jsonRes(res, {
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
export async function initFullTextToken(limit = 50, endDate: Date): Promise<any> {
|
||||
try {
|
||||
const dataList = await MongoDatasetData.find(
|
||||
{ fullTextToken: { $exists: false }, updateTime: { $lt: endDate } },
|
||||
'_id q a'
|
||||
)
|
||||
.limit(limit)
|
||||
.lean();
|
||||
if (dataList.length === 0) return;
|
||||
|
||||
const result = await Promise.allSettled(
|
||||
dataList.map((item) => {
|
||||
const text = item.q + (item.a || '');
|
||||
const tokens = jiebaSplit({ text });
|
||||
|
||||
return MongoDatasetData.findByIdAndUpdate(item._id, {
|
||||
$set: {
|
||||
fullTextToken: tokens
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
success += result.filter((item) => item.status === 'fulfilled').length;
|
||||
console.log(`success: ${success}`);
|
||||
return initFullTextToken(limit, endDate);
|
||||
} catch (error) {
|
||||
await delay(1000);
|
||||
return initFullTextToken(limit, endDate);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { jiebaSplit } from '@/service/common/string/jieba';
|
||||
|
||||
let success = 0;
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { limit = 50 } = req.body as { limit: number };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
success = 0;
|
||||
|
||||
console.log('total', await MongoDatasetData.countDocuments({ inited: { $exists: false } }));
|
||||
|
||||
await initFullTextToken(limit);
|
||||
|
||||
jsonRes(res, {
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
export async function initFullTextToken(limit = 50): Promise<any> {
|
||||
try {
|
||||
const dataList = await MongoDatasetData.find({ inited: { $exists: false } }, '_id q a')
|
||||
.limit(limit)
|
||||
.lean();
|
||||
if (dataList.length === 0) return;
|
||||
|
||||
const result = await Promise.allSettled(
|
||||
dataList.map((item) => {
|
||||
const text = item.q + (item.a || '');
|
||||
const tokens = jiebaSplit({ text });
|
||||
|
||||
return MongoDatasetData.findByIdAndUpdate(item._id, {
|
||||
$set: {
|
||||
inited: true,
|
||||
fullTextToken: tokens
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
success += result.filter((item) => item.status === 'fulfilled').length;
|
||||
console.log(`success: ${success}`);
|
||||
return initFullTextToken(limit);
|
||||
} catch (error) {
|
||||
await delay(1000);
|
||||
return initFullTextToken(limit);
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { DatasetStatusEnum, TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
|
||||
let success = 0;
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { limit = 50 } = req.body as { limit: number };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
success = 0;
|
||||
|
||||
await MongoDatasetCollection.updateMany({ createTime: { $exists: false } }, [
|
||||
{
|
||||
$set: {
|
||||
createTime: '$updateTime'
|
||||
}
|
||||
}
|
||||
]);
|
||||
await MongoDatasetCollection.updateMany({ trainingType: { $exists: false } }, [
|
||||
{
|
||||
$set: {
|
||||
trainingType: {
|
||||
$cond: {
|
||||
if: { $ifNull: ['$a', false] },
|
||||
then: TrainingModeEnum.qa,
|
||||
else: TrainingModeEnum.chunk
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
await MongoDatasetCollection.updateMany({ chunkSize: { $exists: false } }, [
|
||||
{
|
||||
$set: {
|
||||
chunkSize: 0
|
||||
}
|
||||
}
|
||||
]);
|
||||
await MongoDatasetCollection.updateMany({ fileId: { $exists: false } }, [
|
||||
{
|
||||
$set: {
|
||||
fileId: '$metadata.fileId'
|
||||
}
|
||||
}
|
||||
]);
|
||||
await MongoDatasetCollection.updateMany({ rawLink: { $exists: false } }, [
|
||||
{
|
||||
$set: {
|
||||
rawLink: '$metadata.rawLink'
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
await MongoDatasetData.updateMany(
|
||||
{ chunkIndex: { $exists: false } },
|
||||
{
|
||||
chunkIndex: 0
|
||||
}
|
||||
);
|
||||
await MongoDatasetData.updateMany(
|
||||
{ updateTime: { $exists: false } },
|
||||
{
|
||||
updateTime: new Date()
|
||||
}
|
||||
);
|
||||
|
||||
await MongoDataset.updateMany(
|
||||
{ status: { $exists: false } },
|
||||
{
|
||||
$set: {
|
||||
status: DatasetStatusEnum.active
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// dataset tags to intro
|
||||
await MongoDataset.updateMany({ tags: { $exists: true } }, [
|
||||
{
|
||||
$set: {
|
||||
intro: {
|
||||
$reduce: {
|
||||
input: '$tags',
|
||||
initialValue: '',
|
||||
in: { $concat: ['$$value', ' ', '$$this'] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
jsonRes(res, {
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
49
projects/app/src/pages/api/admin/initv47.ts
Normal file
49
projects/app/src/pages/api/admin/initv47.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoUsage } from '@fastgpt/service/support/wallet/usage/schema';
|
||||
import { connectionMongo } from '@fastgpt/service/common/mongo';
|
||||
import { checkFiles } from '../timerTask/dataset/checkInValidDatasetFiles';
|
||||
import { addHours } from 'date-fns';
|
||||
import { checkInvalid as checkInvalidImg } from '../timerTask/dataset/checkInvalidDatasetImage';
|
||||
import { checkInvalidCollection } from '../timerTask/dataset/checkInvalidMongoCollection';
|
||||
import { checkInvalidVector } from '../timerTask/dataset/checkInvalidVector';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
await authCert({ req, authRoot: true });
|
||||
|
||||
await MongoPlugin.updateMany(
|
||||
{ type: { $exists: false } },
|
||||
{
|
||||
$set: {
|
||||
type: PluginTypeEnum.custom
|
||||
}
|
||||
}
|
||||
);
|
||||
await MongoPlugin.updateMany(
|
||||
{ parentId: { $exists: false } },
|
||||
{
|
||||
$set: {
|
||||
parentId: null
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res, {
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
})) || [],
|
||||
whisperModel: global.whisperModel,
|
||||
audioSpeechModels: global.audioSpeechModels,
|
||||
systemVersion: global.systemVersion || '0.0.0',
|
||||
simpleModeTemplates: global.simpleModeTemplates
|
||||
systemVersion: global.systemVersion || '0.0.0'
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -43,7 +42,7 @@ const defaultFeConfigs: FastGPTFeConfigsType = {
|
||||
openAPIDocUrl: 'https://doc.fastgpt.in/docs/development/openapi',
|
||||
systemTitle: 'FastGPT',
|
||||
concatMd:
|
||||
'* 项目开源地址: [FastGPT GitHub](https://github.com/labring/FastGPT)\n* 交流群: ',
|
||||
'* 项目开源地址: [FastGPT GitHub](https://github.com/labring/FastGPT)\n* 交流群: ',
|
||||
limit: {
|
||||
exportDatasetLimitMinutes: 0,
|
||||
websiteSyncLimitMinuted: 0
|
||||
@@ -68,7 +67,6 @@ export async function getInitConfig() {
|
||||
]);
|
||||
|
||||
console.log({
|
||||
// simpleModeTemplates: global.simpleModeTemplates,
|
||||
communityPlugins: global.communityPlugins
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -141,39 +139,6 @@ export function getSystemVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
// async function getSimpleModeTemplates() {
|
||||
// if (global.simpleModeTemplates && global.simpleModeTemplates.length > 0) return;
|
||||
|
||||
// try {
|
||||
// const basePath =
|
||||
// process.env.NODE_ENV === 'development' ? 'data/simpleTemplates' : '/app/data/simpleTemplates';
|
||||
// // read data/simpleTemplates directory, get all json file
|
||||
// const files = readdirSync(basePath);
|
||||
// // filter json file
|
||||
// const filterFiles = files.filter((item) => item.endsWith('.json'));
|
||||
|
||||
// // read json file
|
||||
// const fileTemplates = filterFiles.map((item) => {
|
||||
// const content = readFileSync(`${basePath}/${item}`, 'utf-8');
|
||||
// return {
|
||||
// id: item.replace('.json', ''),
|
||||
// ...JSON.parse(content)
|
||||
// };
|
||||
// });
|
||||
|
||||
// // fetch templates from plus
|
||||
// const plusTemplates = await getSimpleTemplatesFromPlus();
|
||||
|
||||
// global.simpleModeTemplates = [
|
||||
// SimpleModeTemplate_FastGPT_Universal,
|
||||
// ...plusTemplates,
|
||||
// ...fileTemplates
|
||||
// ];
|
||||
// } catch (error) {
|
||||
// global.simpleModeTemplates = [SimpleModeTemplate_FastGPT_Universal];
|
||||
// }
|
||||
// }
|
||||
|
||||
function getSystemPlugin() {
|
||||
if (global.communityPlugins && global.communityPlugins.length > 0) return;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { CreateAppParams } from '@fastgpt/global/core/app/api.d';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants';
|
||||
import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -35,8 +34,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
teamId,
|
||||
tmbId,
|
||||
modules,
|
||||
type,
|
||||
simpleTemplateId: SimpleModeTemplate_FastGPT_Universal.id
|
||||
type
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
|
||||
@@ -1,620 +0,0 @@
|
||||
/*
|
||||
universal mode.
|
||||
@author: FastGpt Team
|
||||
*/
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
|
||||
import type { ModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
import { FormatForm2ModulesProps } from '@fastgpt/global/core/app/api';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { formData, chatModelMaxToken } = req.body as FormatForm2ModulesProps;
|
||||
|
||||
const modules = [
|
||||
...(formData.dataset.datasets.length > 0
|
||||
? datasetTemplate({ formData, maxToken: chatModelMaxToken })
|
||||
: simpleChatTemplate({ formData, maxToken: chatModelMaxToken }))
|
||||
];
|
||||
|
||||
jsonRes<ModuleItemType[]>(res, {
|
||||
data: modules
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type Props = { formData: AppSimpleEditFormType; maxToken: number };
|
||||
|
||||
function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
return [
|
||||
{
|
||||
moduleId: 'userChatInput',
|
||||
name: 'core.module.template.Chat entrance',
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
flowType: 'questionInput',
|
||||
position: {
|
||||
x: 464.32198615344566,
|
||||
y: 1602.2698463081606
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'systemInput',
|
||||
valueType: 'string',
|
||||
label: 'core.module.input.label.user question',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'source',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
name: 'AI 对话',
|
||||
avatar: '/imgs/module/AI.png',
|
||||
flowType: 'chatNode',
|
||||
showStatus: true,
|
||||
position: {
|
||||
x: 981.9682828103937,
|
||||
y: 890.014595014464
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'switch',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.switch',
|
||||
valueType: 'any',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'model',
|
||||
type: 'selectLLMModel',
|
||||
label: 'core.module.input.label.aiModel',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.model,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'temperature',
|
||||
type: 'hidden',
|
||||
label: '温度',
|
||||
value: 1,
|
||||
valueType: 'number',
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
markList: [
|
||||
{
|
||||
label: '严谨',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '发散',
|
||||
value: 10
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'maxToken',
|
||||
type: 'hidden',
|
||||
label: '回复上限',
|
||||
value: maxToken,
|
||||
valueType: 'number',
|
||||
min: 100,
|
||||
max: 4000,
|
||||
step: 50,
|
||||
markList: [
|
||||
{
|
||||
label: '100',
|
||||
value: 100
|
||||
},
|
||||
{
|
||||
label: '4000',
|
||||
value: 4000
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'isResponseAnswerText',
|
||||
type: 'hidden',
|
||||
label: '返回AI内容',
|
||||
value: true,
|
||||
valueType: 'boolean',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quoteTemplate',
|
||||
type: 'hidden',
|
||||
label: '引用内容模板',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.quoteTemplate,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quotePrompt',
|
||||
type: 'hidden',
|
||||
label: '引用内容提示词',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.quotePrompt,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'aiSettings',
|
||||
type: 'aiSettings',
|
||||
label: '',
|
||||
valueType: 'any',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
type: 'textarea',
|
||||
label: 'core.ai.Prompt',
|
||||
max: 300,
|
||||
valueType: 'string',
|
||||
description:
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}',
|
||||
placeholder:
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
value: formData.aiSettings.systemPrompt,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
type: 'numberInput',
|
||||
label: 'core.module.input.label.chat history',
|
||||
required: true,
|
||||
min: 0,
|
||||
max: 30,
|
||||
valueType: 'chatHistory',
|
||||
value: 8,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quoteQA',
|
||||
type: 'target',
|
||||
label: '引用内容',
|
||||
description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]",
|
||||
valueType: 'datasetQuote',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.user question',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'answerText',
|
||||
label: 'AI回复',
|
||||
description: '将在 stream 回复完毕后触发',
|
||||
valueType: 'string',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'finish',
|
||||
label: 'core.module.output.label.running done',
|
||||
description: 'core.module.output.description.running done',
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
label: '新的上下文',
|
||||
description: '将本次回复内容拼接上历史记录,作为新的上下文返回',
|
||||
valueType: 'chatHistory',
|
||||
type: 'source',
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
const modules: ModuleItemType[] = [
|
||||
{
|
||||
moduleId: 'userChatInput',
|
||||
name: 'core.module.template.Chat entrance',
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
flowType: 'questionInput',
|
||||
position: {
|
||||
x: 324.81436595478294,
|
||||
y: 1527.0012457753612
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'systemInput',
|
||||
valueType: 'string',
|
||||
label: 'core.module.input.label.user question',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'source',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'datasetSearch',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
moduleId: 'datasetSearch',
|
||||
name: 'core.module.template.Dataset search',
|
||||
avatar: '/imgs/module/db.png',
|
||||
flowType: 'datasetSearchNode',
|
||||
showStatus: true,
|
||||
position: {
|
||||
x: 1351.5043753345153,
|
||||
y: 947.0780385418003
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'switch',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.switch',
|
||||
valueType: 'any',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'datasets',
|
||||
type: 'selectDataset',
|
||||
label: '关联的知识库',
|
||||
value: formData.dataset.datasets,
|
||||
valueType: 'selectDataset',
|
||||
list: [],
|
||||
required: true,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'similarity',
|
||||
type: 'hidden',
|
||||
label: '最低相关性',
|
||||
value: 0.15,
|
||||
valueType: 'number',
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
markList: [
|
||||
{
|
||||
label: '0',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '1',
|
||||
value: 1
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'limit',
|
||||
type: 'hidden',
|
||||
label: '引用上限',
|
||||
description: '单次搜索最大的 Tokens 数量,中文约1字=1.7Tokens,英文约1字=1Tokens',
|
||||
value: 2000,
|
||||
valueType: 'number',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'searchMode',
|
||||
type: 'hidden',
|
||||
label: '',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: DatasetSearchModeEnum.mixedRecall,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'usingReRank',
|
||||
type: 'hidden',
|
||||
label: '',
|
||||
valueType: 'boolean',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.user question',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'isEmpty',
|
||||
label: '搜索结果为空',
|
||||
type: 'source',
|
||||
valueType: 'boolean',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'unEmpty',
|
||||
label: '搜索结果不为空',
|
||||
type: 'source',
|
||||
valueType: 'boolean',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'quoteQA',
|
||||
label: '引用内容',
|
||||
description:
|
||||
'始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器',
|
||||
type: 'source',
|
||||
valueType: 'datasetQuote',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'quoteQA'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'finish',
|
||||
label: 'core.module.output.label.running done',
|
||||
description: 'core.module.output.description.running done',
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'hidden',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
name: 'AI 对话',
|
||||
avatar: '/imgs/module/AI.png',
|
||||
flowType: 'chatNode',
|
||||
showStatus: true,
|
||||
position: {
|
||||
x: 2022.7264786978908,
|
||||
y: 1006.3102431257475
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'switch',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.switch',
|
||||
valueType: 'any',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'model',
|
||||
type: 'selectLLMModel',
|
||||
label: 'core.module.input.label.aiModel',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.model,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'temperature',
|
||||
type: 'hidden',
|
||||
label: '温度',
|
||||
value: 0,
|
||||
valueType: 'number',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'maxToken',
|
||||
type: 'hidden',
|
||||
label: '回复上限',
|
||||
value: maxToken,
|
||||
valueType: 'number',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'isResponseAnswerText',
|
||||
type: 'hidden',
|
||||
label: '返回AI内容',
|
||||
value: true,
|
||||
valueType: 'boolean',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quoteTemplate',
|
||||
type: 'hidden',
|
||||
label: '引用内容模板',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: '',
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quotePrompt',
|
||||
type: 'hidden',
|
||||
label: '引用内容提示词',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: '',
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'aiSettings',
|
||||
type: 'aiSettings',
|
||||
label: '',
|
||||
valueType: 'any',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
type: 'textarea',
|
||||
label: 'core.ai.Prompt',
|
||||
max: 300,
|
||||
valueType: 'string',
|
||||
description:
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}',
|
||||
placeholder:
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
value: formData.aiSettings.systemPrompt,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
type: 'numberInput',
|
||||
label: 'core.module.input.label.chat history',
|
||||
required: true,
|
||||
min: 0,
|
||||
max: 30,
|
||||
valueType: 'chatHistory',
|
||||
value: 6,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quoteQA',
|
||||
type: 'target',
|
||||
label: '引用内容',
|
||||
description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]",
|
||||
valueType: 'datasetQuote',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.user question',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'answerText',
|
||||
label: 'AI回复',
|
||||
description: '将在 stream 回复完毕后触发',
|
||||
valueType: 'string',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'finish',
|
||||
label: 'core.module.output.label.running done',
|
||||
description: 'core.module.output.description.running done',
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
label: '新的上下文',
|
||||
description: '将本次回复内容拼接上历史记录,作为新的上下文返回',
|
||||
valueType: 'chatHistory',
|
||||
type: 'source',
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return modules;
|
||||
}
|
||||
@@ -1,721 +0,0 @@
|
||||
/*
|
||||
universal mode.
|
||||
@author: FastGpt Team
|
||||
*/
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
|
||||
import type { ModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
import { FormatForm2ModulesProps } from '@fastgpt/global/core/app/api';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { formData } = req.body as FormatForm2ModulesProps;
|
||||
|
||||
const modules =
|
||||
formData.dataset.datasets.length > 0
|
||||
? datasetTemplate(formData)
|
||||
: simpleChatTemplate(formData);
|
||||
|
||||
jsonRes<ModuleItemType[]>(res, {
|
||||
data: modules
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
return [
|
||||
{
|
||||
moduleId: 'userChatInput',
|
||||
name: 'core.module.template.Chat entrance',
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
flowType: 'questionInput',
|
||||
position: {
|
||||
x: 464.32198615344566,
|
||||
y: 1602.2698463081606
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'systemInput',
|
||||
valueType: 'string',
|
||||
label: 'core.module.input.label.user question',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'source',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
name: 'AI 对话',
|
||||
avatar: '/imgs/module/AI.png',
|
||||
flowType: 'chatNode',
|
||||
showStatus: true,
|
||||
position: {
|
||||
x: 981.9682828103937,
|
||||
y: 890.014595014464
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'switch',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.switch',
|
||||
valueType: 'any',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'model',
|
||||
type: 'selectLLMModel',
|
||||
label: 'core.module.input.label.aiModel',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.model,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'temperature',
|
||||
type: 'hidden',
|
||||
label: '温度',
|
||||
value: formData.aiSettings.temperature,
|
||||
valueType: 'number',
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
markList: [
|
||||
{
|
||||
label: '严谨',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '发散',
|
||||
value: 10
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'maxToken',
|
||||
type: 'hidden',
|
||||
label: '回复上限',
|
||||
value: formData.aiSettings.maxToken,
|
||||
valueType: 'number',
|
||||
min: 100,
|
||||
max: 4000,
|
||||
step: 50,
|
||||
markList: [
|
||||
{
|
||||
label: '100',
|
||||
value: 100
|
||||
},
|
||||
{
|
||||
label: '4000',
|
||||
value: 4000
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'isResponseAnswerText',
|
||||
type: 'hidden',
|
||||
label: '返回AI内容',
|
||||
value: true,
|
||||
valueType: 'boolean',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quoteTemplate',
|
||||
type: 'hidden',
|
||||
label: '引用内容模板',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.quoteTemplate,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quotePrompt',
|
||||
type: 'hidden',
|
||||
label: '引用内容提示词',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.quotePrompt,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'aiSettings',
|
||||
type: 'aiSettings',
|
||||
label: '',
|
||||
valueType: 'any',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
type: 'textarea',
|
||||
label: 'core.ai.Prompt',
|
||||
max: 300,
|
||||
valueType: 'string',
|
||||
description:
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}',
|
||||
placeholder:
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
value: formData.aiSettings.systemPrompt,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
type: 'numberInput',
|
||||
label: 'core.module.input.label.chat history',
|
||||
required: true,
|
||||
min: 0,
|
||||
max: 30,
|
||||
valueType: 'chatHistory',
|
||||
value: 6,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quoteQA',
|
||||
type: 'target',
|
||||
label: '引用内容',
|
||||
description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]",
|
||||
valueType: 'datasetQuote',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.user question',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'answerText',
|
||||
label: 'AI回复',
|
||||
description: '将在 stream 回复完毕后触发',
|
||||
valueType: 'string',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'finish',
|
||||
label: 'core.module.output.label.running done',
|
||||
description: 'core.module.output.description.running done',
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
label: '新的上下文',
|
||||
description: '将本次回复内容拼接上历史记录,作为新的上下文返回',
|
||||
valueType: 'chatHistory',
|
||||
type: 'source',
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
const modules: ModuleItemType[] = [
|
||||
{
|
||||
moduleId: 'userChatInput',
|
||||
name: 'core.module.template.Chat entrance',
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
flowType: 'questionInput',
|
||||
position: {
|
||||
x: 324.81436595478294,
|
||||
y: 1527.0012457753612
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'systemInput',
|
||||
valueType: 'string',
|
||||
label: 'core.module.input.label.user question',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'source',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'datasetSearch',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
moduleId: 'datasetSearch',
|
||||
name: 'core.module.template.Dataset search',
|
||||
avatar: '/imgs/module/db.png',
|
||||
flowType: 'datasetSearchNode',
|
||||
showStatus: true,
|
||||
position: {
|
||||
x: 1351.5043753345153,
|
||||
y: 947.0780385418003
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'switch',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.switch',
|
||||
valueType: 'any',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'datasets',
|
||||
type: 'selectDataset',
|
||||
label: '关联的知识库',
|
||||
value: formData.dataset.datasets,
|
||||
valueType: 'selectDataset',
|
||||
list: [],
|
||||
required: true,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'similarity',
|
||||
type: 'hidden',
|
||||
label: '最低相关性',
|
||||
value: formData.dataset.similarity,
|
||||
valueType: 'number',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'limit',
|
||||
type: 'hidden',
|
||||
label: '引用上限',
|
||||
description: '单次搜索最大的 Tokens 数量,中文约1字=1.7Tokens,英文约1字=1Tokens',
|
||||
value: formData.dataset.limit,
|
||||
valueType: 'number',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'searchMode',
|
||||
type: 'hidden',
|
||||
label: 'core.dataset.search.Mode',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.dataset.searchMode,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'usingReRank',
|
||||
type: 'hidden',
|
||||
label: '',
|
||||
valueType: 'boolean',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.dataset.usingReRank,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'datasetSearchUsingExtensionQuery',
|
||||
type: 'hidden',
|
||||
label: '',
|
||||
valueType: 'boolean',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.dataset.datasetSearchUsingExtensionQuery,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'datasetSearchExtensionBg',
|
||||
type: 'hidden',
|
||||
label: '',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.dataset.datasetSearchExtensionBg,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'datasetSearchExtensionModel',
|
||||
type: 'hidden',
|
||||
label: '',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.dataset.datasetSearchExtensionModel,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.user question',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'isEmpty',
|
||||
label: '搜索结果为空',
|
||||
type: 'source',
|
||||
valueType: 'boolean',
|
||||
targets: formData.dataset.searchEmptyText
|
||||
? [
|
||||
{
|
||||
moduleId: '6dtsvu',
|
||||
key: 'switch'
|
||||
}
|
||||
]
|
||||
: []
|
||||
},
|
||||
{
|
||||
key: 'unEmpty',
|
||||
label: '搜索结果不为空',
|
||||
type: 'source',
|
||||
valueType: 'boolean',
|
||||
targets: formData.dataset.searchEmptyText
|
||||
? [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'switch'
|
||||
}
|
||||
]
|
||||
: []
|
||||
},
|
||||
{
|
||||
key: 'quoteQA',
|
||||
label: '引用内容',
|
||||
description:
|
||||
'始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器',
|
||||
type: 'source',
|
||||
valueType: 'datasetQuote',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'quoteQA'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'finish',
|
||||
label: 'core.module.output.label.running done',
|
||||
description: 'core.module.output.description.running done',
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'hidden',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
name: 'AI 对话',
|
||||
avatar: '/imgs/module/AI.png',
|
||||
flowType: 'chatNode',
|
||||
showStatus: true,
|
||||
position: {
|
||||
x: 2022.7264786978908,
|
||||
y: 1006.3102431257475
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'switch',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.switch',
|
||||
valueType: 'any',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: !!formData.dataset?.searchEmptyText
|
||||
},
|
||||
{
|
||||
key: 'model',
|
||||
type: 'selectLLMModel',
|
||||
label: 'core.module.input.label.aiModel',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.model,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'temperature',
|
||||
type: 'hidden',
|
||||
label: '温度',
|
||||
value: formData.aiSettings.temperature,
|
||||
valueType: 'number',
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
markList: [
|
||||
{
|
||||
label: '严谨',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '发散',
|
||||
value: 10
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'maxToken',
|
||||
type: 'hidden',
|
||||
label: '回复上限',
|
||||
value: formData.aiSettings.maxToken,
|
||||
valueType: 'number',
|
||||
min: 100,
|
||||
max: 4000,
|
||||
step: 50,
|
||||
markList: [
|
||||
{
|
||||
label: '100',
|
||||
value: 100
|
||||
},
|
||||
{
|
||||
label: '4000',
|
||||
value: 4000
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'isResponseAnswerText',
|
||||
type: 'hidden',
|
||||
label: '返回AI内容',
|
||||
value: true,
|
||||
valueType: 'boolean',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quoteTemplate',
|
||||
type: 'hidden',
|
||||
label: '引用内容模板',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.quoteTemplate,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quotePrompt',
|
||||
type: 'hidden',
|
||||
label: '引用内容提示词',
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
value: formData.aiSettings.quotePrompt,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'aiSettings',
|
||||
type: 'aiSettings',
|
||||
label: '',
|
||||
valueType: 'any',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
type: 'textarea',
|
||||
label: 'core.ai.Prompt',
|
||||
max: 300,
|
||||
valueType: 'string',
|
||||
description:
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}',
|
||||
placeholder:
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
value: formData.aiSettings.systemPrompt,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
type: 'numberInput',
|
||||
label: 'core.module.input.label.chat history',
|
||||
required: true,
|
||||
min: 0,
|
||||
max: 30,
|
||||
valueType: 'chatHistory',
|
||||
value: 6,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'quoteQA',
|
||||
type: 'target',
|
||||
label: '引用内容',
|
||||
description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]",
|
||||
valueType: 'datasetQuote',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.user question',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'answerText',
|
||||
label: 'AI回复',
|
||||
description: '将在 stream 回复完毕后触发',
|
||||
valueType: 'string',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'finish',
|
||||
label: 'core.module.output.label.running done',
|
||||
description: 'core.module.output.description.running done',
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
label: '新的上下文',
|
||||
description: '将本次回复内容拼接上历史记录,作为新的上下文返回',
|
||||
valueType: 'chatHistory',
|
||||
type: 'source',
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
if (formData.dataset?.searchEmptyText) {
|
||||
modules.push({
|
||||
moduleId: '6dtsvu',
|
||||
name: '指定回复',
|
||||
avatar: '/imgs/module/reply.png',
|
||||
flowType: 'answerNode',
|
||||
position: {
|
||||
x: 2018.2744321961648,
|
||||
y: 616.1220817209096
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'switch',
|
||||
type: 'target',
|
||||
label: 'core.module.input.label.switch',
|
||||
valueType: 'any',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'text',
|
||||
type: 'textarea',
|
||||
value: formData.dataset.searchEmptyText,
|
||||
valueType: 'any',
|
||||
label: '回复的内容',
|
||||
description:
|
||||
'可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串',
|
||||
placeholder:
|
||||
'可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true,
|
||||
connected: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'finish',
|
||||
label: 'core.module.output.label.running done',
|
||||
description: 'core.module.output.description.running done',
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import { getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { name, avatar, type, simpleTemplateId, intro, modules, permission, teamTags } =
|
||||
const { name, avatar, type, intro, modules, permission, teamTags } =
|
||||
req.body as AppUpdateParams;
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
@@ -29,7 +29,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
let maxTokens = 3000;
|
||||
|
||||
modules.forEach((item) => {
|
||||
if (item.flowType === FlowNodeTypeEnum.chatNode) {
|
||||
if (
|
||||
item.flowType === FlowNodeTypeEnum.chatNode ||
|
||||
item.flowType === FlowNodeTypeEnum.tools
|
||||
) {
|
||||
const model =
|
||||
item.inputs.find((item) => item.key === ModuleInputKeyEnum.aiModel)?.value || '';
|
||||
const chatModel = getLLMModel(model);
|
||||
@@ -61,7 +64,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
{
|
||||
name,
|
||||
type,
|
||||
simpleTemplateId,
|
||||
avatar,
|
||||
intro,
|
||||
permission,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { name, avatar, type, simpleTemplateId, intro, modules, permission, teamTags } =
|
||||
const { name, avatar, type, intro, modules, permission, teamTags } =
|
||||
req.body as AppUpdateParams;
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
@@ -61,7 +61,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
{
|
||||
name,
|
||||
type,
|
||||
simpleTemplateId,
|
||||
avatar,
|
||||
intro,
|
||||
permission,
|
||||
|
||||
@@ -8,10 +8,10 @@ import { pushChatUsage } from '@/service/support/wallet/usage/push';
|
||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
import type { ChatItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { dispatchWorkFlow } from '@/service/moduleDispatch';
|
||||
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
|
||||
import { setEntryEntries } from '@/service/moduleDispatch/utils';
|
||||
import { setEntryEntries } from '@fastgpt/service/core/workflow/dispatch/utils';
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
|
||||
export type Props = {
|
||||
|
||||
@@ -7,8 +7,17 @@ import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { appId, chatId, chatItemId, shareId, outLinkUid, userBadFeedback, userGoodFeedback } =
|
||||
req.body as UpdateChatFeedbackProps;
|
||||
const {
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
shareId,
|
||||
teamId,
|
||||
teamToken,
|
||||
outLinkUid,
|
||||
userBadFeedback,
|
||||
userGoodFeedback
|
||||
} = req.body as UpdateChatFeedbackProps;
|
||||
|
||||
try {
|
||||
await connectToDatabase();
|
||||
@@ -17,6 +26,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
req,
|
||||
authToken: true,
|
||||
appId,
|
||||
teamId,
|
||||
teamToken,
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
|
||||
@@ -9,12 +9,13 @@ import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { appId, chatId, shareId, outLinkUid, customTitle, top } = req.body as UpdateHistoryProps;
|
||||
|
||||
const { appId, chatId, teamId, shareId, outLinkUid, customTitle, top } =
|
||||
req.body as UpdateHistoryProps;
|
||||
await autChatCrud({
|
||||
req,
|
||||
authToken: true,
|
||||
appId,
|
||||
teamId,
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter';
|
||||
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
|
||||
import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller';
|
||||
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller';
|
||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
@@ -83,7 +83,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
]);
|
||||
|
||||
// 4. push chunks to training queue
|
||||
const insertResults = await pushDataToTrainingQueue({
|
||||
const insertResults = await pushDataListToTrainingQueue({
|
||||
teamId,
|
||||
tmbId,
|
||||
collectionId,
|
||||
|
||||
@@ -23,11 +23,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
const { collectionId, q, a, indexes } = req.body as InsertOneDatasetDataProps;
|
||||
|
||||
if (!q) {
|
||||
return Promise.reject('q is required');
|
||||
throw new Error('q is required');
|
||||
}
|
||||
|
||||
if (!collectionId) {
|
||||
return Promise.reject('collectionId is required');
|
||||
throw new Error('collectionId is required');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
|
||||
import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller';
|
||||
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -41,7 +41,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
});
|
||||
|
||||
jsonRes<PushDatasetDataResponse>(res, {
|
||||
data: await pushDataToTrainingQueue({
|
||||
data: await pushDataListToTrainingQueue({
|
||||
...req.body,
|
||||
teamId,
|
||||
tmbId
|
||||
|
||||
@@ -3,7 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authDatasetFile } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { createFileToken } from '@fastgpt/service/support/permission/controller';
|
||||
import { BucketNameEnum, FileBaseUrl } from '@fastgpt/global/common/file/constants';
|
||||
import { BucketNameEnum, ReadFileBaseUrl } from '@fastgpt/global/common/file/constants';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: `${FileBaseUrl}?token=${token}`
|
||||
data: `${ReadFileBaseUrl}?token=${token}`
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { SearchTestProps, SearchTestResponse } from '@/global/core/dataset/
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
|
||||
import { searchDatasetData } from '@/service/core/dataset/data/controller';
|
||||
import { searchDatasetData } 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 { getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
|
||||
@@ -5,6 +5,8 @@ import type { CreateOnePluginParams } from '@fastgpt/global/core/plugin/controll
|
||||
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
import { checkTeamPluginLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { httpApiSchema2Plugins } from '@fastgpt/global/core/plugin/httpPlugin/utils';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -12,17 +14,58 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true });
|
||||
const body = req.body as CreateOnePluginParams;
|
||||
|
||||
await checkTeamPluginLimit(teamId);
|
||||
// await checkTeamPluginLimit(teamId);
|
||||
|
||||
const { _id } = await MongoPlugin.create({
|
||||
...body,
|
||||
teamId,
|
||||
tmbId
|
||||
});
|
||||
// create parent plugin and child plugin
|
||||
if (body.metadata?.apiSchemaStr) {
|
||||
const parentId = await mongoSessionRun(async (session) => {
|
||||
const [{ _id: parentId }] = await MongoPlugin.create(
|
||||
[
|
||||
{
|
||||
...body,
|
||||
parentId: null,
|
||||
teamId,
|
||||
tmbId
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
|
||||
jsonRes(res, {
|
||||
data: _id
|
||||
});
|
||||
const childrenPlugins = httpApiSchema2Plugins({
|
||||
parentId,
|
||||
apiSchemaStr: body.metadata?.apiSchemaStr,
|
||||
customHeader: body.metadata?.customHeaders
|
||||
});
|
||||
|
||||
await MongoPlugin.create(
|
||||
childrenPlugins.map((item) => ({
|
||||
...item,
|
||||
metadata: {
|
||||
pluginUid: item.name
|
||||
},
|
||||
teamId,
|
||||
tmbId
|
||||
})),
|
||||
{
|
||||
session
|
||||
}
|
||||
);
|
||||
return parentId;
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: parentId
|
||||
});
|
||||
} else {
|
||||
const { _id } = await MongoPlugin.create({
|
||||
...body,
|
||||
teamId,
|
||||
tmbId
|
||||
});
|
||||
jsonRes(res, {
|
||||
data: _id
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
|
||||
@@ -3,14 +3,32 @@ import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
import { authPluginCrud } from '@fastgpt/service/support/permission/auth/plugin';
|
||||
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { id } = req.query as { id: string };
|
||||
await connectToDatabase();
|
||||
await authPluginCrud({ req, authToken: true, id, per: 'owner' });
|
||||
const { teamId } = await authUserNotVisitor({ req, authToken: true });
|
||||
const { pluginId } = req.query as { pluginId: string };
|
||||
|
||||
await MongoPlugin.findByIdAndRemove(id);
|
||||
if (!pluginId) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
await authPluginCrud({ req, authToken: true, id: pluginId, per: 'owner' });
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoPlugin.deleteMany(
|
||||
{
|
||||
teamId,
|
||||
parentId: pluginId
|
||||
},
|
||||
{
|
||||
session
|
||||
}
|
||||
);
|
||||
await MongoPlugin.findByIdAndDelete(pluginId, { session });
|
||||
});
|
||||
|
||||
jsonRes(res, {});
|
||||
} catch (err) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getPluginPreviewModule } from '@fastgpt/service/core/plugin/controller';
|
||||
import { authPluginCanUse } from '@fastgpt/service/support/permission/auth/plugin';
|
||||
import { FlowModuleTemplateType } from '@fastgpt/global/core/module/type';
|
||||
import { FlowNodeTemplateType } from '@fastgpt/global/core/module/type';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
const { teamId, tmbId } = await authCert({ req, authToken: true });
|
||||
await authPluginCanUse({ id, teamId, tmbId });
|
||||
|
||||
jsonRes<FlowModuleTemplateType>(res, {
|
||||
jsonRes<FlowNodeTemplateType>(res, {
|
||||
data: await getPluginPreviewModule({ id })
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import * as SwaggerParser from '@apidevtools/swagger-parser';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const apiURL = req.body.url as string;
|
||||
|
||||
const api = await (SwaggerParser as any).validate(apiURL);
|
||||
|
||||
return jsonRes(res, {
|
||||
data: api
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,31 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
import { PluginListItemType } from '@fastgpt/global/core/plugin/controller';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { parentId, type } = req.query as { parentId?: string; type?: `${DatasetTypeEnum}` };
|
||||
|
||||
const { teamId } = await authCert({ req, authToken: true });
|
||||
|
||||
jsonRes(res, {
|
||||
data: await MongoPlugin.find({ teamId })
|
||||
const plugins = await MongoPlugin.find(
|
||||
{
|
||||
teamId,
|
||||
...(parentId !== undefined && { parentId: parentId || null }),
|
||||
...(type && { type })
|
||||
},
|
||||
'_id parentId type name avatar intro metadata'
|
||||
)
|
||||
.sort({ updateTime: -1 })
|
||||
.lean();
|
||||
|
||||
jsonRes<PluginListItemType[]>(res, {
|
||||
data: plugins
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
|
||||
46
projects/app/src/pages/api/core/plugin/paths.ts
Normal file
46
projects/app/src/pages/api/core/plugin/paths.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import type { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type.d';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
const { parentId } = req.query as { parentId: string };
|
||||
|
||||
if (!parentId) {
|
||||
return jsonRes(res, {
|
||||
data: []
|
||||
});
|
||||
}
|
||||
|
||||
await authCert({ req, authToken: true });
|
||||
|
||||
jsonRes<ParentTreePathItemType[]>(res, {
|
||||
data: await getParents(parentId)
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function getParents(parentId?: string): Promise<ParentTreePathItemType[]> {
|
||||
if (!parentId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const parent = await MongoPlugin.findById(parentId, 'name parentId');
|
||||
|
||||
if (!parent) return [];
|
||||
|
||||
const paths = await getParents(parent.parentId);
|
||||
paths.push({ parentId, parentName: parent.name });
|
||||
|
||||
return paths;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { FlowNodeTemplateType } from '@fastgpt/global/core/module/type';
|
||||
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/module/constants';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
await authCert({ req, authToken: true });
|
||||
|
||||
const data: FlowNodeTemplateType[] =
|
||||
global.communityPlugins?.map((plugin) => ({
|
||||
id: plugin.id,
|
||||
templateType: plugin.templateType ?? FlowNodeTemplateTypeEnum.other,
|
||||
flowType: FlowNodeTypeEnum.pluginModule,
|
||||
avatar: plugin.avatar,
|
||||
name: plugin.name,
|
||||
intro: plugin.intro,
|
||||
showStatus: true,
|
||||
isTool: plugin.isTool,
|
||||
inputs: [],
|
||||
outputs: []
|
||||
})) || [];
|
||||
|
||||
jsonRes<FlowNodeTemplateType[]>(res, {
|
||||
data
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { FlowNodeTemplateType } from '@fastgpt/global/core/module/type';
|
||||
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/module/constants';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { parentId, searchKey } = req.query as { parentId?: string; searchKey?: string };
|
||||
const { teamId } = await authCert({ req, authToken: true });
|
||||
|
||||
const userPlugins = await (async () => {
|
||||
if (searchKey) {
|
||||
return MongoPlugin.find({
|
||||
teamId,
|
||||
// search name or intro
|
||||
$or: [
|
||||
{ name: { $regex: searchKey, $options: 'i' } },
|
||||
{ intro: { $regex: searchKey, $options: 'i' } }
|
||||
]
|
||||
})
|
||||
.sort({
|
||||
updateTime: -1
|
||||
})
|
||||
.lean();
|
||||
} else {
|
||||
return MongoPlugin.find({ teamId, parentId: parentId || null })
|
||||
.sort({
|
||||
updateTime: -1
|
||||
})
|
||||
.lean();
|
||||
}
|
||||
})();
|
||||
|
||||
const data: FlowNodeTemplateType[] = userPlugins.map((plugin) => ({
|
||||
id: String(plugin._id),
|
||||
parentId: String(plugin.parentId),
|
||||
pluginType: plugin.type,
|
||||
templateType: FlowNodeTemplateTypeEnum.personalPlugin,
|
||||
flowType: FlowNodeTypeEnum.pluginModule,
|
||||
avatar: plugin.avatar,
|
||||
name: plugin.name,
|
||||
intro: plugin.intro,
|
||||
showStatus: false,
|
||||
inputs: [],
|
||||
outputs: []
|
||||
}));
|
||||
|
||||
jsonRes<FlowNodeTemplateType[]>(res, {
|
||||
data
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { FlowModuleTemplateType } from '@fastgpt/global/core/module/type';
|
||||
import { ModuleTemplateTypeEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { GET } from '@fastgpt/service/common/api/plusRequest';
|
||||
import type { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
|
||||
import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { teamId } = await authCert({ req, authToken: true });
|
||||
|
||||
const [userPlugins, plusPlugins] = await Promise.all([
|
||||
MongoPlugin.find({ teamId }).lean(),
|
||||
FastGPTProUrl ? GET<PluginTemplateType[]>('/core/plugin/getTemplates') : []
|
||||
]);
|
||||
|
||||
const data: FlowModuleTemplateType[] = [
|
||||
...userPlugins.map((plugin) => ({
|
||||
id: String(plugin._id),
|
||||
templateType: ModuleTemplateTypeEnum.personalPlugin,
|
||||
flowType: FlowNodeTypeEnum.pluginModule,
|
||||
avatar: plugin.avatar,
|
||||
name: plugin.name,
|
||||
intro: plugin.intro,
|
||||
showStatus: false,
|
||||
inputs: [],
|
||||
outputs: []
|
||||
})),
|
||||
...(global.communityPlugins?.map((plugin) => ({
|
||||
id: plugin.id,
|
||||
templateType: plugin.templateType ?? ModuleTemplateTypeEnum.other,
|
||||
flowType: FlowNodeTypeEnum.pluginModule,
|
||||
avatar: plugin.avatar,
|
||||
name: plugin.name,
|
||||
intro: plugin.intro,
|
||||
showStatus: true,
|
||||
inputs: [],
|
||||
outputs: []
|
||||
})) || [])
|
||||
];
|
||||
|
||||
jsonRes<FlowModuleTemplateType[]>(res, {
|
||||
data
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,37 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import type { UpdatePluginParams } from '@fastgpt/global/core/plugin/controller';
|
||||
import { authPluginCrud } from '@fastgpt/service/support/permission/auth/plugin';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { ClientSession } from '@fastgpt/service/common/mongo';
|
||||
import { httpApiSchema2Plugins } from '@fastgpt/global/core/plugin/httpPlugin/utils';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { id, ...props } = req.body as UpdatePluginParams;
|
||||
const body = req.body as UpdatePluginParams;
|
||||
|
||||
await authPluginCrud({ req, authToken: true, id, per: 'owner' });
|
||||
const { id, ...props } = body;
|
||||
|
||||
jsonRes(res, {
|
||||
data: await MongoPlugin.findByIdAndUpdate(id, props)
|
||||
});
|
||||
const { teamId, tmbId } = await authPluginCrud({ req, authToken: true, id, per: 'owner' });
|
||||
|
||||
if (props.metadata?.apiSchemaStr) {
|
||||
await mongoSessionRun(async (session) => {
|
||||
// update children
|
||||
await updateHttpChildrenPlugin({
|
||||
teamId,
|
||||
tmbId,
|
||||
parent: body,
|
||||
session
|
||||
});
|
||||
await MongoPlugin.findByIdAndUpdate(id, props, { session });
|
||||
});
|
||||
|
||||
jsonRes(res, {});
|
||||
} else {
|
||||
jsonRes(res, {
|
||||
data: await MongoPlugin.findByIdAndUpdate(id, props)
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
@@ -22,3 +42,64 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const updateHttpChildrenPlugin = async ({
|
||||
teamId,
|
||||
tmbId,
|
||||
parent,
|
||||
session
|
||||
}: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
parent: UpdatePluginParams;
|
||||
session: ClientSession;
|
||||
}) => {
|
||||
if (!parent.metadata?.apiSchemaStr) return;
|
||||
const dbPlugins = await MongoPlugin.find(
|
||||
{
|
||||
parentId: parent.id,
|
||||
teamId
|
||||
},
|
||||
'_id metadata'
|
||||
);
|
||||
|
||||
const schemaPlugins = httpApiSchema2Plugins({
|
||||
parentId: parent.id,
|
||||
apiSchemaStr: parent.metadata?.apiSchemaStr,
|
||||
customHeader: parent.metadata?.customHeaders
|
||||
});
|
||||
|
||||
// 数据库中存在,schema不存在,删除
|
||||
for await (const plugin of dbPlugins) {
|
||||
if (!schemaPlugins.find((p) => p.name === plugin.metadata?.pluginUid)) {
|
||||
await MongoPlugin.deleteOne({ _id: plugin._id }, { session });
|
||||
}
|
||||
}
|
||||
// 数据库中不存在,schema存在,新增
|
||||
for await (const plugin of schemaPlugins) {
|
||||
if (!dbPlugins.find((p) => p.metadata?.pluginUid === plugin.name)) {
|
||||
await MongoPlugin.create(
|
||||
[
|
||||
{
|
||||
...plugin,
|
||||
metadata: {
|
||||
pluginUid: plugin.name
|
||||
},
|
||||
teamId,
|
||||
tmbId
|
||||
}
|
||||
],
|
||||
{
|
||||
session
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
// 数据库中存在,schema存在,更新
|
||||
for await (const plugin of schemaPlugins) {
|
||||
const dbPlugin = dbPlugins.find((p) => plugin.name === p.metadata?.pluginUid);
|
||||
if (dbPlugin) {
|
||||
await MongoPlugin.findByIdAndUpdate(dbPlugin._id, plugin, { session });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user