4.6.7-alpha commit (#743)
Co-authored-by: Archer <545436317@qq.com> Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
@@ -56,7 +56,7 @@ const FeedbackModal = ({
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={2} onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button isLoading={isLoading} onClick={mutate}>
|
||||
{t('core.chat.Feedback Submit')}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
||||
import ChatBoxDivider from '@/components/core/chat/Divider';
|
||||
import { strIsLink } from '@fastgpt/global/common/string/tools';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false });
|
||||
const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false });
|
||||
@@ -123,7 +124,7 @@ const ResponseTags = ({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Image src={item.icon} alt={''} mr={1} flexShrink={0} w={'12px'} />
|
||||
<MyIcon name={item.icon as any} mr={1} flexShrink={0} w={'12px'} />
|
||||
<Box className="textEllipsis3" wordBreak={'break-all'} flex={'1 0 0'}>
|
||||
{item.sourceName}
|
||||
</Box>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ModalBody, useTheme, ModalFooter, Button, Box, Card, Flex, Grid } from
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Avatar from '../Avatar';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import DatasetSelectModal, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { AdminFbkType } from '@fastgpt/global/core/chat/type.d';
|
||||
@@ -46,7 +46,7 @@ const SelectMarkCollection = ({
|
||||
paths={paths}
|
||||
onClose={onClose}
|
||||
setParentId={setParentId}
|
||||
tips={t('core.chat.Select Mark Kb Desc')}
|
||||
tips={t('core.chat.Select dataset Desc')}
|
||||
>
|
||||
<ModalBody flex={'1 0 0'} overflowY={'auto'}>
|
||||
<Grid
|
||||
|
||||
@@ -11,7 +11,7 @@ import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
|
||||
import Markdown from '../Markdown';
|
||||
import { QuoteList } from './QuoteModal';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
function Row({
|
||||
label,
|
||||
@@ -141,6 +141,7 @@ const ResponseBox = React.memo(function ResponseBox({
|
||||
value={`${activeModule?.runningTime || 0}s`}
|
||||
/>
|
||||
<Row label={t('core.chat.response.module model')} value={activeModule?.model} />
|
||||
<Row label={t('wallet.bill.Chars length')} value={`${activeModule?.charsLength}`} />
|
||||
<Row label={t('wallet.bill.Input Token Length')} value={`${activeModule?.inputTokens}`} />
|
||||
<Row label={t('wallet.bill.Output Token Length')} value={`${activeModule?.outputTokens}`} />
|
||||
<Row label={t('core.chat.response.module query')} value={activeModule?.query} />
|
||||
|
||||
@@ -174,7 +174,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
mb={0}
|
||||
color={'myGray.500'}
|
||||
>
|
||||
<MyIcon name={'common/courseLight'} width={'26px'} height={'26px'} />
|
||||
<MyIcon name={'common/courseLight'} width={'24px'} height={'24px'} />
|
||||
</Link>
|
||||
</MyTooltip>
|
||||
)}
|
||||
@@ -189,7 +189,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
||||
mt={0}
|
||||
color={'myGray.500'}
|
||||
>
|
||||
<MyIcon name={'common/gitLight'} width={'22px'} height={'22px'} />
|
||||
<MyIcon name={'common/gitInlight'} width={'26px'} height={'26px'} />
|
||||
</Link>
|
||||
</MyTooltip>
|
||||
)}
|
||||
|
||||
@@ -15,6 +15,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
|
||||
{
|
||||
label: t('navbar.Chat'),
|
||||
icon: 'core/chat/chatLight',
|
||||
activeIcon: 'core/chat/chatFill',
|
||||
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
|
||||
activeLink: ['/chat'],
|
||||
unread: 0
|
||||
@@ -22,20 +23,23 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
|
||||
{
|
||||
label: t('navbar.Apps'),
|
||||
icon: 'core/app/aiLight',
|
||||
activeIcon: 'core/app/aiFill',
|
||||
link: `/app/list`,
|
||||
activeLink: ['/app/list', '/app/detail'],
|
||||
unread: 0
|
||||
},
|
||||
{
|
||||
label: t('navbar.Tools'),
|
||||
icon: 'phoneTabbar/more',
|
||||
icon: 'phoneTabbar/tool',
|
||||
activeIcon: 'phoneTabbar/toolFill',
|
||||
link: '/tools',
|
||||
activeLink: ['/tools'],
|
||||
unread: 0
|
||||
},
|
||||
{
|
||||
label: t('navbar.Account'),
|
||||
icon: 'phoneTabbar/me',
|
||||
icon: 'support/user/userLight',
|
||||
activeIcon: 'support/user/userFill',
|
||||
link: '/account',
|
||||
activeLink: ['/account'],
|
||||
unread
|
||||
@@ -68,35 +72,24 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
|
||||
transform={'scale(0.9)'}
|
||||
{...(item.activeLink.includes(router.pathname)
|
||||
? {
|
||||
color: '#7089f1'
|
||||
color: 'primary.600'
|
||||
}
|
||||
: {
|
||||
color: 'myGray.500'
|
||||
})}
|
||||
_after={
|
||||
item.activeLink.includes(router.pathname)
|
||||
? {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%,-50%)',
|
||||
borderRadius: '50%',
|
||||
w: '18px',
|
||||
h: '18px',
|
||||
bg: ' #6782f1',
|
||||
filter: 'blur(10px)',
|
||||
boxShadow: '0px 2px 4px 0px rgba(0, 0, 0, 0.25)'
|
||||
}
|
||||
: {}
|
||||
}
|
||||
onClick={() => {
|
||||
if (item.link === router.asPath) return;
|
||||
router.push(item.link);
|
||||
}}
|
||||
>
|
||||
<Badge isDot count={item.unread}>
|
||||
<MyIcon name={item.icon as any} width={'20px'} height={'20px'} />
|
||||
<MyIcon
|
||||
name={
|
||||
(item.activeLink.includes(router.pathname) ? item.activeIcon : item.icon) as any
|
||||
}
|
||||
width={'20px'}
|
||||
height={'20px'}
|
||||
/>
|
||||
<Box fontSize={'12px'}>{item.label}</Box>
|
||||
</Badge>
|
||||
</Flex>
|
||||
|
||||
@@ -17,6 +17,7 @@ const Loading = ({
|
||||
position={fixed ? 'fixed' : 'absolute'}
|
||||
zIndex={zIndex}
|
||||
bg={bg}
|
||||
borderRadius={'md'}
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
|
||||
@@ -19,8 +19,8 @@ const MyMenu = ({ width, offset = [0, 10], Button, menuList }: Props) => {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
_hover: {
|
||||
backgroundColor: 'myWhite.600',
|
||||
color: 'hover.blue'
|
||||
backgroundColor: 'myGray.05',
|
||||
color: 'primary.600'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ const MyMenu = ({ width, offset = [0, 10], Button, menuList }: Props) => {
|
||||
e.stopPropagation();
|
||||
item.onClick && item.onClick();
|
||||
}}
|
||||
color={item.isActive ? 'primary.500' : ''}
|
||||
color={item.isActive ? 'primary.700' : 'myGray.600'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
>
|
||||
{item.child}
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalCloseButton,
|
||||
ModalContentProps,
|
||||
Box,
|
||||
Image
|
||||
} from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { ModalContentProps } from '@chakra-ui/react';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import CustomModal from '@fastgpt/web/components/common/MyModal';
|
||||
|
||||
export interface MyModalProps extends ModalContentProps {
|
||||
iconSrc?: string;
|
||||
@@ -33,59 +24,18 @@ const MyModal = ({
|
||||
}: MyModalProps) => {
|
||||
const { isPc } = useSystemStore();
|
||||
return (
|
||||
<Modal
|
||||
<CustomModal
|
||||
isOpen={isOpen}
|
||||
onClose={() => onClose && onClose()}
|
||||
autoFocus={false}
|
||||
onClose={onClose}
|
||||
iconSrc={iconSrc}
|
||||
title={title}
|
||||
isCentered={isPc ? isCentered : true}
|
||||
w={w}
|
||||
maxW={maxW}
|
||||
{...props}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent
|
||||
w={w}
|
||||
minW={['90vw', '400px']}
|
||||
maxW={maxW}
|
||||
position={'relative'}
|
||||
maxH={'85vh'}
|
||||
{...props}
|
||||
>
|
||||
{!title && onClose && <ModalCloseButton zIndex={1} />}
|
||||
{!!title && (
|
||||
<ModalHeader
|
||||
display={'flex'}
|
||||
alignItems={'center'}
|
||||
fontWeight={500}
|
||||
background={'#FBFBFC'}
|
||||
borderBottom={'1px solid #F4F6F8'}
|
||||
roundedTop={'lg'}
|
||||
py={'10px'}
|
||||
>
|
||||
{iconSrc && (
|
||||
<>
|
||||
{iconSrc.startsWith('/') ? (
|
||||
<Image mr={3} objectFit={'contain'} alt="" src={iconSrc} w={'20px'} />
|
||||
) : (
|
||||
<MyIcon mr={3} name={iconSrc as any} w={'20px'} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{title}
|
||||
<Box flex={1} />
|
||||
{onClose && (
|
||||
<ModalCloseButton position={'relative'} fontSize={'sm'} top={0} right={0} />
|
||||
)}
|
||||
</ModalHeader>
|
||||
)}
|
||||
|
||||
<Box
|
||||
overflow={props.overflow || 'overlay'}
|
||||
h={'100%'}
|
||||
display={'flex'}
|
||||
flexDirection={'column'}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
{children}
|
||||
</CustomModal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ const Tag = ({ children, colorSchema = 'blue', ...props }: Props) => {
|
||||
color: '#A558C9'
|
||||
},
|
||||
gray: {
|
||||
borderColor: '#979797',
|
||||
bg: '#F7F7F7',
|
||||
color: '#979797'
|
||||
borderColor: 'borderColor.base',
|
||||
bg: 'myGray.50',
|
||||
color: 'myGray.700'
|
||||
}
|
||||
};
|
||||
return map[colorSchema];
|
||||
@@ -35,14 +35,14 @@ const Tag = ({ children, colorSchema = 'blue', ...props }: Props) => {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
border={'1px solid'}
|
||||
{...theme}
|
||||
borderWidth={'1px'}
|
||||
px={2}
|
||||
lineHeight={1}
|
||||
py={1}
|
||||
borderRadius={'sm'}
|
||||
fontSize={'xs'}
|
||||
alignItems={'center'}
|
||||
{...theme}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -87,7 +87,7 @@ const MyRadio = ({
|
||||
<Box pr={hiddenCircle ? 0 : 2} color={'myGray.800'}>
|
||||
<Box>{typeof item.title === 'string' ? t(item.title) : item.title}</Box>
|
||||
{!!item.desc && (
|
||||
<Box fontSize={['xs', 'sm']} color={'myGray.500'}>
|
||||
<Box fontSize={'xs'} color={'myGray.500'} lineHeight={1.2}>
|
||||
{t(item.desc)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Textarea,
|
||||
TextareaProps,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyModal from '@/components/MyModal';
|
||||
|
||||
type Props = TextareaProps & {
|
||||
title?: string;
|
||||
// variables: string[];
|
||||
};
|
||||
|
||||
const MyTextarea = React.forwardRef<HTMLTextAreaElement, Props>(function MyTextarea(props, ref) {
|
||||
const ModalTextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const TextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { title = t('core.app.edit.Prompt Editor'), ...childProps } = props;
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Editor textareaRef={TextareaRef} {...childProps} onOpenModal={onOpen} />
|
||||
{isOpen && (
|
||||
<MyModal iconSrc="/imgs/modal/edit.svg" title={title} isOpen onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Editor
|
||||
textareaRef={ModalTextareaRef}
|
||||
{...childProps}
|
||||
minH={'300px'}
|
||||
maxH={'auto'}
|
||||
minW={['100%', '512px']}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (ModalTextareaRef.current && TextareaRef.current) {
|
||||
TextareaRef.current.value = ModalTextareaRef.current.value;
|
||||
}
|
||||
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default React.memo(MyTextarea);
|
||||
|
||||
const Editor = React.memo(function Editor({
|
||||
onOpenModal,
|
||||
textareaRef,
|
||||
...props
|
||||
}: Props & {
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement>;
|
||||
onOpenModal?: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Box h={'100%'} w={'100%'} position={'relative'}>
|
||||
<Textarea ref={textareaRef} maxW={'100%'} {...props} />
|
||||
{onOpenModal && (
|
||||
<Box
|
||||
zIndex={1}
|
||||
position={'absolute'}
|
||||
bottom={1}
|
||||
right={2}
|
||||
cursor={'pointer'}
|
||||
onClick={onOpenModal}
|
||||
>
|
||||
<MyTooltip label={t('common.ui.textarea.Magnifying')}>
|
||||
<MyIcon name={'common/fullScreenLight'} w={'14px'} color={'myGray.600'} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
@@ -1,97 +0,0 @@
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Textarea,
|
||||
TextareaProps,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyModal from '@/components/MyModal';
|
||||
|
||||
type Props = TextareaProps & {
|
||||
title?: string;
|
||||
// variables: string[];
|
||||
};
|
||||
|
||||
const PromptTextarea = React.forwardRef<HTMLTextAreaElement, Props>(
|
||||
function PromptTextarea(props, ref) {
|
||||
const ModalTextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const TextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { title = t('core.app.edit.Prompt Editor'), ...childProps } = props;
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Editor textareaRef={TextareaRef} {...childProps} onOpenModal={onOpen} />
|
||||
{isOpen && (
|
||||
<MyModal iconSrc="/imgs/modal/edit.svg" title={title} isOpen onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Editor
|
||||
textareaRef={ModalTextareaRef}
|
||||
{...childProps}
|
||||
minH={'300px'}
|
||||
maxH={'auto'}
|
||||
minW={['100%', '512px']}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (ModalTextareaRef.current && TextareaRef.current) {
|
||||
TextareaRef.current.value = ModalTextareaRef.current.value;
|
||||
}
|
||||
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default React.memo(PromptTextarea);
|
||||
|
||||
const Editor = React.memo(function Editor({
|
||||
onOpenModal,
|
||||
textareaRef,
|
||||
...props
|
||||
}: Props & {
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement>;
|
||||
onOpenModal?: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Box h={'100%'} w={'100%'} position={'relative'}>
|
||||
<Textarea ref={textareaRef} maxW={'100%'} {...props} />
|
||||
{onOpenModal && (
|
||||
<Box
|
||||
zIndex={1}
|
||||
position={'absolute'}
|
||||
bottom={1}
|
||||
right={2}
|
||||
cursor={'pointer'}
|
||||
onClick={onOpenModal}
|
||||
>
|
||||
<MyTooltip label={t('common.ui.textarea.Magnifying')}>
|
||||
<MyIcon name={'common/fullScreenLight'} w={'14px'} color={'myGray.600'} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Box, Flex, FlexProps } from '@chakra-ui/react';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import React from 'react';
|
||||
import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
const DatasetTypeTag = ({ type, ...props }: { type: `${DatasetTypeEnum}` } & FlexProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useTranslation } from 'next-i18next';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyBox from '@/components/common/MyBox';
|
||||
import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal'));
|
||||
|
||||
@@ -102,6 +102,7 @@ const QuoteItem = ({
|
||||
overflow={'hidden'}
|
||||
fontSize={'sm'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-all'}
|
||||
_hover={{ '& .hover-data': { display: 'flex' } }}
|
||||
h={'100%'}
|
||||
display={'flex'}
|
||||
@@ -220,7 +221,6 @@ const QuoteItem = ({
|
||||
display={['flex', 'none']}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
boxShadow={'-10px 0 10px rgba(255,255,255,1)'}
|
||||
>
|
||||
<MyIcon
|
||||
name={'edit'}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useTranslation } from 'next-i18next';
|
||||
import { getFileAndOpen } from '@/web/core/dataset/utils';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
type Props = BoxProps & {
|
||||
sourceName?: string;
|
||||
@@ -52,7 +53,7 @@ const RawSourceBox = ({ sourceId, sourceName = '', canView = true, ...props }: P
|
||||
: {})}
|
||||
{...props}
|
||||
>
|
||||
<Image src={icon} alt="" w={['14px', '16px']} mr={2} />
|
||||
<MyIcon name={icon as any} w={['14px', '16px']} mr={2} />
|
||||
<Box
|
||||
maxW={['200px', '300px']}
|
||||
className={props.className ?? 'textEllipsis'}
|
||||
|
||||
@@ -38,7 +38,7 @@ const DatasetSelectContainer = ({
|
||||
parentId: path.parentId,
|
||||
parentName: path.parentName
|
||||
}))}
|
||||
FirstPathDom={t('core.chat.Select Mark Kb')}
|
||||
FirstPathDom={t('core.chat.Select dataset')}
|
||||
onClick={(e) => {
|
||||
setParentId(e);
|
||||
}}
|
||||
|
||||
@@ -26,7 +26,8 @@ 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 PromptTextarea from '@/components/common/Textarea/PromptTextarea';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import { PickerMenuItemType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
||||
|
||||
const PromptTemplate = dynamic(() => import('@/components/PromptTemplate'));
|
||||
|
||||
@@ -35,13 +36,15 @@ const AIChatSettingsModal = ({
|
||||
onClose,
|
||||
onSuccess,
|
||||
defaultData,
|
||||
simpleModeTemplate = SimpleModeTemplate_FastGPT_Universal
|
||||
simpleModeTemplate = SimpleModeTemplate_FastGPT_Universal,
|
||||
pickerMenu = []
|
||||
}: {
|
||||
isAdEdit?: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess: (e: AIChatModuleProps) => void;
|
||||
defaultData: AIChatModuleProps;
|
||||
simpleModeTemplate?: AppSimpleEditConfigTemplateType;
|
||||
pickerMenu?: PickerMenuItemType[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
@@ -60,7 +63,44 @@ const AIChatSettingsModal = ({
|
||||
chatModelList.find((item) => item.model === getValues(ModuleInputKeyEnum.aiModel))
|
||||
?.maxResponse || 4000
|
||||
);
|
||||
}, [getValues, refresh]);
|
||||
}, [getValues]);
|
||||
|
||||
const quoteTemplateVariables = (() => [
|
||||
...pickerMenu,
|
||||
{
|
||||
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'
|
||||
}
|
||||
])();
|
||||
const quotePromptVariables = (() => [
|
||||
...pickerMenu,
|
||||
{
|
||||
key: 'quote',
|
||||
label: t('core.app.Quote templates'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
}
|
||||
])();
|
||||
|
||||
const LabelStyles: BoxProps = {
|
||||
fontSize: ['sm', 'md']
|
||||
@@ -76,7 +116,7 @@ const AIChatSettingsModal = ({
|
||||
iconSrc="/imgs/module/AI.png"
|
||||
title={
|
||||
<>
|
||||
{t('app.AI Advanced Settings')}
|
||||
{t('common.More settings')}
|
||||
{feConfigs?.docUrl && (
|
||||
<Link
|
||||
href={getDocPath('/docs/use-cases/ai_settings/')}
|
||||
@@ -86,7 +126,7 @@ const AIChatSettingsModal = ({
|
||||
fontWeight={'normal'}
|
||||
fontSize={'md'}
|
||||
>
|
||||
查看说明
|
||||
{t('common.Read intro')}
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
@@ -99,7 +139,7 @@ const AIChatSettingsModal = ({
|
||||
{isAdEdit && (
|
||||
<Flex alignItems={'center'}>
|
||||
<Box {...LabelStyles} w={'80px'}>
|
||||
返回AI内容
|
||||
{t('core.app.Ai response')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<Switch
|
||||
@@ -117,13 +157,13 @@ const AIChatSettingsModal = ({
|
||||
{simpleModeTemplate?.systemForm?.aiSettings?.temperature && (
|
||||
<Flex alignItems={'center'} mb={10} mt={isAdEdit ? 8 : 5}>
|
||||
<Box {...LabelStyles} mr={2} w={'80px'}>
|
||||
温度
|
||||
{t('core.app.Temperature')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '严谨', value: 0 },
|
||||
{ label: '发散', value: 10 }
|
||||
{ label: t('core.app.deterministic'), value: 0 },
|
||||
{ label: t('core.app.Random'), value: 10 }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={0}
|
||||
@@ -140,7 +180,7 @@ const AIChatSettingsModal = ({
|
||||
{simpleModeTemplate?.systemForm?.aiSettings?.maxToken && (
|
||||
<Flex alignItems={'center'} mt={12} mb={10}>
|
||||
<Box {...LabelStyles} mr={2} w={'80px'}>
|
||||
回复上限
|
||||
{t('core.app.Max tokens')}
|
||||
</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
@@ -165,7 +205,7 @@ const AIChatSettingsModal = ({
|
||||
{simpleModeTemplate?.systemForm?.aiSettings?.quoteTemplate && (
|
||||
<Box>
|
||||
<Flex {...LabelStyles} mb={1}>
|
||||
引用内容模板
|
||||
{t('core.app.Quote templates')}
|
||||
<MyTooltip
|
||||
label={t('template.Quote Content Tip', {
|
||||
default: Prompt_QuoteTemplateList[0].value
|
||||
@@ -179,24 +219,24 @@ const AIChatSettingsModal = ({
|
||||
{...selectTemplateBtn}
|
||||
onClick={() =>
|
||||
setSelectTemplateData({
|
||||
title: '选择知识库提示词模板',
|
||||
title: t('core.app.Select quote template'),
|
||||
templates: Prompt_QuoteTemplateList
|
||||
})
|
||||
}
|
||||
>
|
||||
选择模板
|
||||
{t('common.Select template')}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<PromptTextarea
|
||||
bg={'myWhite.400'}
|
||||
rows={8}
|
||||
<PromptEditor
|
||||
variables={quoteTemplateVariables}
|
||||
title={t('core.app.Quote templates')}
|
||||
placeholder={t('template.Quote Content Tip', {
|
||||
default: Prompt_QuoteTemplateList[0].value
|
||||
})}
|
||||
defaultValue={getValues(ModuleInputKeyEnum.aiChatQuoteTemplate)}
|
||||
onBlur={(e) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatQuoteTemplate, e.target.value);
|
||||
onChange={(e) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatQuoteTemplate, e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
@@ -205,7 +245,7 @@ const AIChatSettingsModal = ({
|
||||
{simpleModeTemplate?.systemForm?.aiSettings?.quotePrompt && (
|
||||
<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
|
||||
@@ -213,16 +253,16 @@ const AIChatSettingsModal = ({
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<PromptTextarea
|
||||
bg={'myWhite.400'}
|
||||
rows={11}
|
||||
<PromptEditor
|
||||
variables={quotePromptVariables}
|
||||
title={t('core.app.Quote prompt')}
|
||||
h={220}
|
||||
placeholder={t('template.Quote Prompt Tip', {
|
||||
default: Prompt_QuotePromptList[0].value
|
||||
})}
|
||||
defaultValue={getValues(ModuleInputKeyEnum.aiChatQuotePrompt)}
|
||||
onBlur={(e) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatQuotePrompt, e.target.value);
|
||||
setRefresh(!refresh);
|
||||
onChange={(e) => {
|
||||
setValue(ModuleInputKeyEnum.aiChatQuotePrompt, e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -230,10 +270,10 @@ const AIChatSettingsModal = ({
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button ml={4} onClick={handleSubmit(onSuccess)}>
|
||||
{t('Confirm')}
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
{!!selectTemplateData && (
|
||||
|
||||
@@ -15,12 +15,12 @@ import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import MySlider from '@/components/Slider';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { reRankModelList } from '@/web/common/system/staticData';
|
||||
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
import MyRadio from '@/components/common/MyRadio';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
@@ -136,7 +136,7 @@ const DatasetParamsModal = ({
|
||||
|
||||
{limit !== undefined && (
|
||||
<Box display={['block', 'flex']} py={8} mt={3}>
|
||||
<Box flex={'0 0 100px'} mb={[8, 0]}>
|
||||
<Box flex={'0 0 120px'} mb={[8, 0]}>
|
||||
{t('core.dataset.search.Max Tokens')}
|
||||
<MyTooltip label={t('core.dataset.search.Max Tokens Tips')} forceShow>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
@@ -162,7 +162,7 @@ const DatasetParamsModal = ({
|
||||
)}
|
||||
{showSimilarity && (
|
||||
<Box display={['block', 'flex']} py={8}>
|
||||
<Box flex={'0 0 100px'} mb={[8, 0]}>
|
||||
<Box flex={'0 0 120px'} mb={[8, 0]}>
|
||||
{t('core.dataset.search.Min Similarity')}
|
||||
<MyTooltip label={t('core.dataset.search.Min Similarity Tips')} forceShow>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
@@ -189,7 +189,7 @@ const DatasetParamsModal = ({
|
||||
|
||||
{searchEmptyText !== undefined && (
|
||||
<Box display={['block', 'flex']} pt={3}>
|
||||
<Box flex={'0 0 100px'} mb={[2, 0]}>
|
||||
<Box flex={'0 0 120px'} mb={[2, 0]}>
|
||||
{t('core.dataset.search.Empty result response')}
|
||||
</Box>
|
||||
<Box flex={1}>
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||
import DatasetSelectContainer, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { SelectAppItemType } from '@fastgpt/global/core/module/type';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useLoading } from '@/web/common/hooks/useLoading';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
|
||||
const SelectAppModal = ({
|
||||
@@ -84,7 +83,7 @@ const SelectAppModal = ({
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
ml={2}
|
||||
@@ -101,7 +100,7 @@ const SelectAppModal = ({
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('Confirm')}
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
<Loading loading={isLoading} fixed={false} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { BezierEdge, getBezierPath, EdgeLabelRenderer, EdgeProps } from 'reactflow';
|
||||
import { onDelConnect } from '../../FlowProvider';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
@@ -17,7 +17,7 @@ const ButtonEdge = (props: EdgeProps) => {
|
||||
style = {}
|
||||
} = props;
|
||||
|
||||
const [edgePath, labelX, labelY] = getBezierPath({
|
||||
const [labelX, labelY] = getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition,
|
||||
@@ -26,19 +26,8 @@ const ButtonEdge = (props: EdgeProps) => {
|
||||
targetPosition
|
||||
});
|
||||
|
||||
const edgeStyle: React.CSSProperties = {
|
||||
...style,
|
||||
...(selected
|
||||
? {
|
||||
strokeWidth: 4,
|
||||
stroke: '#3370ff'
|
||||
}
|
||||
: { strokeWidth: 2, stroke: '#BDC1C5' })
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<BezierEdge {...props} style={edgeStyle} />
|
||||
const memoEdgeLabel = useMemo(() => {
|
||||
return (
|
||||
<EdgeLabelRenderer>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
@@ -66,6 +55,27 @@ const ButtonEdge = (props: EdgeProps) => {
|
||||
></MyIcon>
|
||||
</Flex>
|
||||
</EdgeLabelRenderer>
|
||||
);
|
||||
}, [id, labelX, labelY, selected]);
|
||||
|
||||
const memoBezierEdge = useMemo(() => {
|
||||
const edgeStyle: React.CSSProperties = {
|
||||
...style,
|
||||
...(selected
|
||||
? {
|
||||
strokeWidth: 4,
|
||||
stroke: '#3370ff'
|
||||
}
|
||||
: { strokeWidth: 2, stroke: '#BDC1C5' })
|
||||
};
|
||||
|
||||
return <BezierEdge {...props} style={edgeStyle} />;
|
||||
}, [props, selected, style]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{memoBezierEdge}
|
||||
{memoEdgeLabel}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ const QGSwitch = (props: SwitchProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/questionGuide'} mr={2} w={'16px'} />
|
||||
<MyIcon name={'core/app/questionGuide'} mr={2} w={'20px'} />
|
||||
<Box>{t('core.app.Next Step Guide')}</Box>
|
||||
<MyTooltip label={t('core.app.Question Guide Tip')} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
|
||||
@@ -71,7 +71,7 @@ const TTSSelect = ({
|
||||
|
||||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/tts'} mr={2} w={'16px'} />
|
||||
<MyIcon name={'core/app/simpleMode/tts'} mr={2} w={'20px'} />
|
||||
<Box>{t('core.app.TTS')}</Box>
|
||||
<MyTooltip label={t('core.app.TTS Tip')} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
@@ -93,7 +93,7 @@ const TTSSelect = ({
|
||||
<MyModal
|
||||
title={
|
||||
<>
|
||||
<MyIcon name={'core/app/tts'} mr={2} w={'20px'} />
|
||||
<MyIcon name={'core/app/simpleMode/tts'} mr={2} w={'20px'} />
|
||||
{t('core.app.TTS')}
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import { variableTip } from '@fastgpt/global/core/module/template/tip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import MyRadio from '@/components/common/MyRadio';
|
||||
import { formatVariablesIcon } from '@fastgpt/global/core/module/utils';
|
||||
|
||||
const VariableEdit = ({
|
||||
variables,
|
||||
@@ -101,16 +102,13 @@ const VariableEdit = ({
|
||||
};
|
||||
|
||||
const formatVariables = useMemo(() => {
|
||||
return variables.map((item) => ({
|
||||
...item,
|
||||
icon: VariableTypeList.find((type) => type.value === item.type)?.icon
|
||||
}));
|
||||
}, [VariableTypeList, variables]);
|
||||
return formatVariablesIcon(variables);
|
||||
}, [variables]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Flex alignItems={'center'}>
|
||||
<Image alt={''} src={'/imgs/module/variable.png'} objectFit={'contain'} w={'18px'} />
|
||||
<MyIcon name={'core/app/simpleMode/variable'} w={'20px'} />
|
||||
<Box ml={2} flex={1}>
|
||||
{t('core.module.Variable')}
|
||||
<MyTooltip label={variableTip} forceShow>
|
||||
@@ -179,7 +177,7 @@ const VariableEdit = ({
|
||||
</Box>
|
||||
)}
|
||||
<MyModal
|
||||
iconSrc="/imgs/module/variable.png"
|
||||
iconSrc="core/app/simpleMode/variable"
|
||||
title={t('core.module.Variable Setting')}
|
||||
isOpen={isOpenEdit}
|
||||
onClose={onCloseEdit}
|
||||
@@ -342,6 +340,6 @@ export const defaultVariable: VariableItemType = {
|
||||
enums: [{ value: '' }]
|
||||
};
|
||||
export const addVariable = () => {
|
||||
const newVariable = { ...defaultVariable, key: nanoid(), id: '' };
|
||||
const newVariable = { ...defaultVariable, key: '', id: '' };
|
||||
return newVariable;
|
||||
};
|
||||
|
||||
@@ -126,7 +126,7 @@ const NodeCQNode = React.memo(function NodeCQNode({ data }: { data: FlowModuleIt
|
||||
});
|
||||
}}
|
||||
>
|
||||
添加问题类型
|
||||
{t('core.module.Add question type')}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useTransition } from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Textarea,
|
||||
useTheme,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
TableContainer,
|
||||
Switch
|
||||
} from '@chakra-ui/react';
|
||||
import { Box, Flex, Textarea, useTheme } from '@chakra-ui/react';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { FlowModuleItemType, ModuleItemType } from '@fastgpt/global/core/module/type.d';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { welcomeTextTip, variableTip } from '@fastgpt/global/core/module/template/tip';
|
||||
import { welcomeTextTip } from '@fastgpt/global/core/module/template/tip';
|
||||
import { onChangeNode } from '../../FlowProvider';
|
||||
|
||||
import VariableEdit from '../modules/VariableEdit';
|
||||
@@ -29,6 +16,7 @@ import type { VariableItemType } from '@fastgpt/global/core/module/type.d';
|
||||
import QGSwitch from '@/components/core/module/Flow/components/modules/QGSwitch';
|
||||
import TTSSelect from '@/components/core/module/Flow/components/modules/TTSSelect';
|
||||
import { splitGuideModule } from '@fastgpt/global/core/module/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const NodeUserGuide = React.memo(function NodeUserGuide({ data }: { data: FlowModuleItemType }) {
|
||||
const theme = useTheme();
|
||||
@@ -56,19 +44,18 @@ export default function Node({ data }: NodeProps<FlowModuleItemType>) {
|
||||
return <NodeUserGuide data={data} />;
|
||||
}
|
||||
export function WelcomeText({ data }: { data: FlowModuleItemType }) {
|
||||
const { t } = useTranslation();
|
||||
const { inputs, moduleId } = data;
|
||||
const [, startTst] = useTransition();
|
||||
|
||||
const welcomeText = useMemo(
|
||||
() => inputs.find((item) => item.key === ModuleInputKeyEnum.welcomeText),
|
||||
[inputs]
|
||||
);
|
||||
const welcomeText = inputs.find((item) => item.key === ModuleInputKeyEnum.welcomeText);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex mb={1} alignItems={'center'}>
|
||||
<MyIcon name={'core/modules/welcomeText'} mr={2} w={'16px'} color={'#E74694'} />
|
||||
<Box>开场白</Box>
|
||||
<MyTooltip label={welcomeTextTip} forceShow>
|
||||
<Box>{t('core.app.Welcome Text')}</Box>
|
||||
<MyTooltip label={t(welcomeTextTip)} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
@@ -79,16 +66,18 @@ export function WelcomeText({ data }: { data: FlowModuleItemType }) {
|
||||
resize={'both'}
|
||||
defaultValue={welcomeText.value}
|
||||
bg={'myWhite.500'}
|
||||
placeholder={welcomeTextTip}
|
||||
placeholder={t(welcomeTextTip)}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
key: ModuleInputKeyEnum.welcomeText,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...welcomeText,
|
||||
value: e.target.value
|
||||
}
|
||||
startTst(() => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
key: ModuleInputKeyEnum.welcomeText,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...welcomeText,
|
||||
value: e.target.value
|
||||
}
|
||||
});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/* Abandon */
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import { Box, Button, Table, Thead, Tbody, Tr, Th, Td, TableContainer } from '@chakra-ui/react';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import NodeCard from '../../render/NodeCard';
|
||||
import { FlowModuleItemType } from '@fastgpt/global/core/module/type.d';
|
||||
import Container from '../../modules/Container';
|
||||
import { VariableInputEnum, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import type { VariableItemType } from '@fastgpt/global/core/module/type.d';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
import VariableEditModal, { addVariable } from '../../modules/VariableEdit';
|
||||
import { onChangeNode } from '../../../FlowProvider';
|
||||
|
||||
export const defaultVariable: VariableItemType = {
|
||||
id: nanoid(),
|
||||
key: 'key',
|
||||
label: 'label',
|
||||
type: VariableInputEnum.input,
|
||||
required: true,
|
||||
maxLen: 50,
|
||||
enums: [{ value: '' }]
|
||||
};
|
||||
|
||||
const NodeUserGuide = ({ data }: NodeProps<FlowModuleItemType>) => {
|
||||
const { inputs, moduleId } = data;
|
||||
|
||||
const variables = useMemo(
|
||||
() =>
|
||||
(inputs.find((item) => item.key === ModuleInputKeyEnum.variables)
|
||||
?.value as VariableItemType[]) || [],
|
||||
[inputs]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeCard minW={'300px'} {...data}>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
|
||||
<VariableEditModal
|
||||
variables={variables}
|
||||
onChange={(e) =>
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
key: ModuleInputKeyEnum.variables,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...inputs.find((item) => item.key === ModuleInputKeyEnum.variables),
|
||||
value: e
|
||||
}
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Container>
|
||||
</NodeCard>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default React.memo(NodeUserGuide);
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import type { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
@@ -6,7 +6,7 @@ import dynamic from 'next/dynamic';
|
||||
|
||||
import InputLabel from './Label';
|
||||
import type { RenderInputProps } from './type.d';
|
||||
import { getFlowStore, type useFlowProviderStoreType } from '../../../FlowProvider';
|
||||
import { useFlowProviderStore } from '../../../FlowProvider';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
|
||||
const RenderList: {
|
||||
@@ -77,8 +77,8 @@ type Props = {
|
||||
moduleId: string;
|
||||
CustomComponent?: Record<string, (e: FlowNodeInputItemType) => React.ReactNode>;
|
||||
};
|
||||
const RenderInput = ({ flowInputList, moduleId, CustomComponent = {} }: Props) => {
|
||||
const [mode, setMode] = useState<useFlowProviderStoreType['mode']>('app');
|
||||
const RenderInput = ({ flowInputList, moduleId, CustomComponent }: Props) => {
|
||||
const { mode } = useFlowProviderStore();
|
||||
|
||||
const sortInputs = useMemo(
|
||||
() =>
|
||||
@@ -109,48 +109,43 @@ const RenderInput = ({ flowInputList, moduleId, CustomComponent = {} }: Props) =
|
||||
[mode, sortInputs]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
async () => {
|
||||
const { mode } = await getFlowStore();
|
||||
setMode(mode);
|
||||
};
|
||||
}, []);
|
||||
const memoCustomComponent = useMemo(() => CustomComponent || {}, [CustomComponent]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{filterInputs.map((input) => {
|
||||
const RenderComponent = (() => {
|
||||
if (input.type === FlowNodeInputTypeEnum.custom && CustomComponent[input.key]) {
|
||||
return <>{CustomComponent[input.key]({ ...input })}</>;
|
||||
}
|
||||
const Component = RenderList.find((item) => item.types.includes(input.type))?.Component;
|
||||
const Render = useMemo(() => {
|
||||
return filterInputs.map((input) => {
|
||||
const RenderComponent = (() => {
|
||||
if (input.type === FlowNodeInputTypeEnum.custom && memoCustomComponent[input.key]) {
|
||||
return <>{memoCustomComponent[input.key]({ ...input })}</>;
|
||||
}
|
||||
const Component = RenderList.find((item) => item.types.includes(input.type))?.Component;
|
||||
|
||||
if (!Component) return null;
|
||||
return <Component inputs={filterInputs} item={input} moduleId={moduleId} />;
|
||||
})();
|
||||
if (!Component) return null;
|
||||
return <Component inputs={filterInputs} item={input} moduleId={moduleId} />;
|
||||
})();
|
||||
|
||||
return (
|
||||
<Box key={input.key} _notLast={{ mb: 7 }} position={'relative'}>
|
||||
{input.key === ModuleInputKeyEnum.userChatInput && (
|
||||
<UserChatInput inputs={filterInputs} item={input} moduleId={moduleId} />
|
||||
)}
|
||||
{input.type !== FlowNodeInputTypeEnum.hidden && (
|
||||
<>
|
||||
{!!input.label && (
|
||||
<InputLabel moduleId={moduleId} inputKey={input.key} mode={mode} {...input} />
|
||||
)}
|
||||
{!!RenderComponent && (
|
||||
<Box mt={2} className={'nodrag'}>
|
||||
{RenderComponent}
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<Box key={input.key} _notLast={{ mb: 7 }} position={'relative'}>
|
||||
{input.key === ModuleInputKeyEnum.userChatInput && (
|
||||
<UserChatInput inputs={filterInputs} item={input} moduleId={moduleId} />
|
||||
)}
|
||||
{input.type !== FlowNodeInputTypeEnum.hidden && (
|
||||
<>
|
||||
{!!input.label && (
|
||||
<InputLabel moduleId={moduleId} inputKey={input.key} mode={mode} {...input} />
|
||||
)}
|
||||
{!!RenderComponent && (
|
||||
<Box mt={2} className={'nodrag'}>
|
||||
{RenderComponent}
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
}, [memoCustomComponent, filterInputs, mode, moduleId]);
|
||||
|
||||
return <>{Render}</>;
|
||||
};
|
||||
|
||||
export default React.memo(RenderInput);
|
||||
|
||||
@@ -1,20 +1,39 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { onChangeNode } from '../../../../FlowProvider';
|
||||
import { getFlowStore, onChangeNode, useFlowProviderStoreType } from '../../../../FlowProvider';
|
||||
import { Box, Button, Flex, Grid, useDisclosure, useTheme } from '@chakra-ui/react';
|
||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||
import { SelectedDatasetType } from '@fastgpt/global/core/module/api';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import DatasetSelectModal from '@/components/core/module/DatasetSelectModal';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { chatModelList } from '@/web/common/system/staticData';
|
||||
|
||||
const SelectDatasetRender = ({ item, moduleId }: RenderInputProps) => {
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
const DatasetSelectModal = dynamic(() => import('@/components/core/module/DatasetSelectModal'));
|
||||
const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal'));
|
||||
|
||||
const SelectDatasetRender = ({ inputs = [], item, moduleId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const [nodes, setNodes] = useState<useFlowProviderStoreType['nodes']>([]);
|
||||
const [data, setData] = useState({
|
||||
searchMode: DatasetSearchModeEnum.embedding,
|
||||
limit: 5,
|
||||
similarity: 0.5,
|
||||
usingReRank: false
|
||||
});
|
||||
|
||||
const { allDatasets, loadAllDatasets } = useDatasetStore();
|
||||
const {
|
||||
isOpen: isOpenKbSelect,
|
||||
onOpen: onOpenKbSelect,
|
||||
onClose: onCloseKbSelect
|
||||
isOpen: isOpenDatasetSelect,
|
||||
onOpen: onOpenDatasetSelect,
|
||||
onClose: onCloseDatasetSelect
|
||||
} = useDisclosure();
|
||||
|
||||
const selectedDatasets = useMemo(() => {
|
||||
@@ -22,14 +41,68 @@ const SelectDatasetRender = ({ item, moduleId }: RenderInputProps) => {
|
||||
return allDatasets.filter((dataset) => value?.find((item) => item.datasetId === dataset._id));
|
||||
}, [allDatasets, item.value]);
|
||||
|
||||
const tokenLimit = useMemo(() => {
|
||||
let maxTokens = 3000;
|
||||
|
||||
nodes.forEach((item) => {
|
||||
if (item.type === FlowNodeTypeEnum.chatNode) {
|
||||
const model =
|
||||
item.data.inputs.find((item) => item.key === ModuleInputKeyEnum.aiModel)?.value || '';
|
||||
const quoteMaxToken =
|
||||
chatModelList.find((item) => item.model === model)?.quoteMaxToken || 3000;
|
||||
|
||||
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
||||
}
|
||||
});
|
||||
|
||||
return maxTokens;
|
||||
}, [nodes]);
|
||||
|
||||
const {
|
||||
isOpen: isOpenDatasetPrams,
|
||||
onOpen: onOpenDatasetParams,
|
||||
onClose: onCloseDatasetParams
|
||||
} = useDisclosure();
|
||||
|
||||
useQuery(['loadAllDatasets'], loadAllDatasets);
|
||||
|
||||
useEffect(() => {
|
||||
inputs.forEach((input) => {
|
||||
// @ts-ignore
|
||||
if (data[input.key] !== undefined) {
|
||||
setData((state) => ({
|
||||
...state,
|
||||
[input.key]: input.value
|
||||
}));
|
||||
}
|
||||
});
|
||||
}, [inputs]);
|
||||
|
||||
useEffect(() => {
|
||||
async () => {
|
||||
const { nodes } = await getFlowStore();
|
||||
setNodes(nodes);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid gridTemplateColumns={'repeat(2, minmax(0, 1fr))'} gridGap={4} minW={'350px'} w={'100%'}>
|
||||
<Button h={'36px'} onClick={onOpenKbSelect}>
|
||||
选择知识库
|
||||
<Button
|
||||
h={'36px'}
|
||||
leftIcon={<MyIcon name={'common/selectLight'} w={'14px'} />}
|
||||
onClick={onOpenDatasetSelect}
|
||||
>
|
||||
{t('common.Choose')}
|
||||
</Button>
|
||||
{/* <Button
|
||||
h={'36px'}
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<MyIcon name={'common/settingLight'} w={'14px'} />}
|
||||
onClick={onOpenDatasetParams}
|
||||
>
|
||||
{t('core.dataset.search.Params Setting')}
|
||||
</Button> */}
|
||||
{selectedDatasets.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
@@ -53,9 +126,9 @@ const SelectDatasetRender = ({ item, moduleId }: RenderInputProps) => {
|
||||
</Flex>
|
||||
))}
|
||||
</Grid>
|
||||
{isOpenKbSelect && (
|
||||
{isOpenDatasetSelect && (
|
||||
<DatasetSelectModal
|
||||
isOpen={isOpenKbSelect}
|
||||
isOpen={isOpenDatasetSelect}
|
||||
defaultSelectedDatasets={item.value}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
@@ -68,9 +141,32 @@ const SelectDatasetRender = ({ item, moduleId }: RenderInputProps) => {
|
||||
}
|
||||
});
|
||||
}}
|
||||
onClose={onCloseKbSelect}
|
||||
onClose={onCloseDatasetSelect}
|
||||
/>
|
||||
)}
|
||||
{/* {isOpenDatasetPrams && (
|
||||
<DatasetParamsModal
|
||||
{...data}
|
||||
maxTokens={tokenLimit}
|
||||
onClose={onCloseDatasetParams}
|
||||
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]
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)} */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { getFlowStore, onChangeNode, useFlowProviderStoreType } from '../../../../FlowProvider';
|
||||
import { onChangeNode, useFlowProviderStore } from '../../../../FlowProvider';
|
||||
import { Button, useDisclosure } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { chatModelList } from '@/web/common/system/staticData';
|
||||
@@ -11,7 +11,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import DatasetParamsModal from '@/components/core/module/DatasetParamsModal';
|
||||
|
||||
const SelectDatasetParam = ({ inputs = [], moduleId }: RenderInputProps) => {
|
||||
const [nodes, setNodes] = useState<useFlowProviderStoreType['nodes']>([]);
|
||||
const { nodes } = useFlowProviderStore();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [data, setData] = useState({
|
||||
@@ -52,47 +52,44 @@ const SelectDatasetParam = ({ inputs = [], moduleId }: RenderInputProps) => {
|
||||
});
|
||||
}, [inputs]);
|
||||
|
||||
useEffect(() => {
|
||||
async () => {
|
||||
const { nodes } = await getFlowStore();
|
||||
setNodes(nodes);
|
||||
};
|
||||
}, []);
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<MyIcon name={'common/settingLight'} w={'14px'} />}
|
||||
onClick={onOpen}
|
||||
>
|
||||
{t('core.dataset.search.Params Setting')}
|
||||
</Button>
|
||||
{isOpen && (
|
||||
<DatasetParamsModal
|
||||
{...data}
|
||||
maxTokens={tokenLimit}
|
||||
onClose={onClose}
|
||||
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]
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}, [data, inputs, isOpen, moduleId, onClose, onOpen, t, tokenLimit]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<MyIcon name={'common/settingLight'} w={'14px'} />}
|
||||
onClick={onOpen}
|
||||
>
|
||||
{t('core.dataset.search.Params Setting')}
|
||||
</Button>
|
||||
{isOpen && (
|
||||
<DatasetParamsModal
|
||||
{...data}
|
||||
maxTokens={tokenLimit}
|
||||
onClose={onClose}
|
||||
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]
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return Render;
|
||||
};
|
||||
|
||||
export default React.memo(SelectDatasetParam);
|
||||
|
||||
@@ -1,38 +1,53 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useMemo, useTransition } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { onChangeNode } from '../../../../FlowProvider';
|
||||
import { useFlowProviderStore, onChangeNode } from '../../../../FlowProvider';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import PromptTextarea from '@/components/common/Textarea/PromptTextarea';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import {
|
||||
formatVariablesIcon,
|
||||
getGuideModule,
|
||||
splitGuideModule
|
||||
} from '@fastgpt/global/core/module/utils';
|
||||
|
||||
const TextareaRender = ({ item, moduleId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [, startTst] = useTransition();
|
||||
const { nodes } = useFlowProviderStore();
|
||||
|
||||
const update = useCallback(
|
||||
(value: string) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value
|
||||
}
|
||||
// get variable
|
||||
const variables = useMemo(
|
||||
() =>
|
||||
formatVariablesIcon(
|
||||
splitGuideModule(getGuideModule(nodes.map((node) => node.data)))?.variableModules || []
|
||||
),
|
||||
[nodes]
|
||||
);
|
||||
|
||||
const onChange = useCallback(
|
||||
(e: string) => {
|
||||
startTst(() => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
[item, moduleId]
|
||||
);
|
||||
|
||||
return (
|
||||
<PromptTextarea
|
||||
<PromptEditor
|
||||
variables={variables}
|
||||
title={t(item.label)}
|
||||
rows={5}
|
||||
bg={'myWhite.400'}
|
||||
h={150}
|
||||
placeholder={t(item.placeholder || '')}
|
||||
resize={'both'}
|
||||
defaultValue={item.value}
|
||||
onBlur={(e) => {
|
||||
update(e.target.value);
|
||||
}}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import ReactFlow, { Background, Controls, ReactFlowProvider } from 'reactflow';
|
||||
import { Box, Flex, IconButton, useDisclosure } from '@chakra-ui/react';
|
||||
import { SmallCloseIcon } from '@chakra-ui/icons';
|
||||
@@ -15,7 +15,6 @@ import 'reactflow/dist/style.css';
|
||||
const NodeSimple = dynamic(() => import('./components/nodes/NodeSimple'));
|
||||
const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = {
|
||||
[FlowNodeTypeEnum.userGuide]: dynamic(() => import('./components/nodes/NodeUserGuide')),
|
||||
[FlowNodeTypeEnum.variable]: dynamic(() => import('./components/nodes/abandon/NodeVariable')),
|
||||
[FlowNodeTypeEnum.questionInput]: dynamic(() => import('./components/nodes/NodeQuestionInput')),
|
||||
[FlowNodeTypeEnum.historyNode]: NodeSimple,
|
||||
[FlowNodeTypeEnum.chatNode]: NodeSimple,
|
||||
@@ -38,6 +37,16 @@ const Container = React.memo(function Container() {
|
||||
const { reactFlowWrapper, nodes, onNodesChange, edges, onEdgesChange, onConnect } =
|
||||
useFlowProviderStore();
|
||||
|
||||
const memoRenderTools = useMemo(
|
||||
() => (
|
||||
<>
|
||||
<Background />
|
||||
<Controls position={'bottom-right'} style={{ display: 'flex' }} showInteractive={false} />
|
||||
</>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
ref={reactFlowWrapper}
|
||||
@@ -64,8 +73,7 @@ const Container = React.memo(function Container() {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Background />
|
||||
<Controls position={'bottom-right'} style={{ display: 'flex' }} showInteractive={false} />
|
||||
{memoRenderTools}
|
||||
</ReactFlow>
|
||||
);
|
||||
});
|
||||
@@ -81,48 +89,54 @@ const Flow = ({
|
||||
onClose: onCloseTemplate
|
||||
} = useDisclosure();
|
||||
|
||||
const memoRenderContainer = useMemo(() => {
|
||||
return (
|
||||
<Box
|
||||
minH={'400px'}
|
||||
flex={'1 0 0'}
|
||||
w={'100%'}
|
||||
h={0}
|
||||
position={'relative'}
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
{/* open module template */}
|
||||
<IconButton
|
||||
position={'absolute'}
|
||||
top={5}
|
||||
left={5}
|
||||
size={'mdSquare'}
|
||||
borderRadius={'50%'}
|
||||
icon={<SmallCloseIcon fontSize={'26px'} />}
|
||||
transform={isOpenTemplate ? '' : 'rotate(135deg)'}
|
||||
transition={'0.2s ease'}
|
||||
aria-label={''}
|
||||
zIndex={1}
|
||||
boxShadow={'2px 2px 6px #85b1ff'}
|
||||
onClick={() => {
|
||||
isOpenTemplate ? onCloseTemplate() : onOpenTemplate();
|
||||
}}
|
||||
/>
|
||||
|
||||
<Container {...data} />
|
||||
|
||||
<ModuleTemplateList
|
||||
templates={templates}
|
||||
isOpen={isOpenTemplate}
|
||||
onClose={onCloseTemplate}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}, [data, isOpenTemplate, onCloseTemplate, onOpenTemplate, templates]);
|
||||
|
||||
return (
|
||||
<Box h={'100%'} position={'fixed'} zIndex={999} top={0} left={0} right={0} bottom={0}>
|
||||
<ReactFlowProvider>
|
||||
<Flex h={'100%'} flexDirection={'column'} bg={'#fff'}>
|
||||
{Header}
|
||||
<Box
|
||||
minH={'400px'}
|
||||
flex={'1 0 0'}
|
||||
w={'100%'}
|
||||
h={0}
|
||||
position={'relative'}
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
{/* open module template */}
|
||||
<IconButton
|
||||
position={'absolute'}
|
||||
top={5}
|
||||
left={5}
|
||||
size={'mdSquare'}
|
||||
borderRadius={'50%'}
|
||||
icon={<SmallCloseIcon fontSize={'26px'} />}
|
||||
transform={isOpenTemplate ? '' : 'rotate(135deg)'}
|
||||
transition={'0.2s ease'}
|
||||
aria-label={''}
|
||||
zIndex={1}
|
||||
boxShadow={'2px 2px 6px #85b1ff'}
|
||||
onClick={() => {
|
||||
isOpenTemplate ? onCloseTemplate() : onOpenTemplate();
|
||||
}}
|
||||
/>
|
||||
|
||||
<Container {...data} />
|
||||
|
||||
<ModuleTemplateList
|
||||
templates={templates}
|
||||
isOpen={isOpenTemplate}
|
||||
onClose={onCloseTemplate}
|
||||
/>
|
||||
</Box>
|
||||
{memoRenderContainer}
|
||||
</Flex>
|
||||
</ReactFlowProvider>
|
||||
</Box>
|
||||
|
||||
@@ -81,7 +81,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
<Box flex={1}>
|
||||
<Flex alignItems={'flex-end'}>
|
||||
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
||||
API 秘钥管理
|
||||
{t('support.openapi.Api manager')}
|
||||
</Box>
|
||||
{feConfigs?.docUrl && (
|
||||
<Link
|
||||
@@ -90,7 +90,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
ml={1}
|
||||
color={'primary.500'}
|
||||
>
|
||||
查看文档
|
||||
{t('common.Read document')}
|
||||
</Link>
|
||||
)}
|
||||
</Flex>
|
||||
@@ -106,10 +106,10 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
borderRadius={'md'}
|
||||
cursor={'pointer'}
|
||||
userSelect={'none'}
|
||||
onClick={() => copyData(baseUrl, '已复制 API 地址')}
|
||||
onClick={() => copyData(baseUrl, t('support.openapi.Copy success'))}
|
||||
>
|
||||
<Box border={theme.borders.md} px={2} borderRadius={'md'} fontSize={'sm'}>
|
||||
API根地址
|
||||
{t('support.openapi.Api baseurl')}
|
||||
</Box>
|
||||
<Box ml={2} color={'myGray.900'} fontSize={['sm', 'md']}>
|
||||
{baseUrl}
|
||||
@@ -127,7 +127,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
})
|
||||
}
|
||||
>
|
||||
新建
|
||||
{t('common.New Create')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -137,16 +137,16 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
<Tr>
|
||||
<Th>{t('Name')}</Th>
|
||||
<Th>Api Key</Th>
|
||||
<Th>已用额度(¥)</Th>
|
||||
<Th>{t('support.openapi.Usage')}</Th>
|
||||
{feConfigs?.isPlus && (
|
||||
<>
|
||||
<Th>最大额度(¥)</Th>
|
||||
<Th>过期时间</Th>
|
||||
<Th>{t('support.openapi.Max usage')}</Th>
|
||||
<Th>{t('common.Expired Time')}</Th>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Th>创建时间</Th>
|
||||
<Th>最后一次使用时间</Th>
|
||||
<Th>{t('common.Create Time')}</Th>
|
||||
<Th>{t('common.Last use time')}</Th>
|
||||
<Th />
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -158,7 +158,11 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
<Td>{usage}</Td>
|
||||
{feConfigs?.isPlus && (
|
||||
<>
|
||||
<Td>{limit?.credit && limit?.credit > -1 ? `${limit?.credit}` : '无限制'}</Td>
|
||||
<Td>
|
||||
{limit?.credit && limit?.credit > -1
|
||||
? `${limit?.credit}`
|
||||
: t('common.Unlimited')}
|
||||
</Td>
|
||||
<Td whiteSpace={'pre-wrap'}>
|
||||
{limit?.expiredTime
|
||||
? dayjs(limit?.expiredTime).format('YYYY/MM/DD\nHH:mm')
|
||||
@@ -168,7 +172,9 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
)}
|
||||
<Td whiteSpace={'pre-wrap'}>{dayjs(createTime).format('YYYY/MM/DD\nHH:mm:ss')}</Td>
|
||||
<Td whiteSpace={'pre-wrap'}>
|
||||
{lastUsedTime ? dayjs(lastUsedTime).format('YYYY/MM/DD\nHH:mm:ss') : '没有使用过'}
|
||||
{lastUsedTime
|
||||
? dayjs(lastUsedTime).format('YYYY/MM/DD\nHH:mm:ss')
|
||||
: t('common.Un used')}
|
||||
</Td>
|
||||
<Td>
|
||||
<Menu autoSelect={false} isLazy>
|
||||
@@ -229,10 +235,10 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
title={
|
||||
<Box>
|
||||
<Box fontWeight={'bold'} fontSize={'xl'}>
|
||||
新的 API 秘钥
|
||||
{t('support.openapi.New api key')}
|
||||
</Box>
|
||||
<Box fontSize={'sm'} color={'myGray.600'}>
|
||||
请保管好你的秘钥,秘钥不会再次展示~
|
||||
{t('support.openapi.New api key tip')}
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
@@ -359,14 +365,14 @@ function EditKeyModal({
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
isLoading={creating || updating}
|
||||
onClick={submitShareChat((data) => (isEdit ? onclickUpdate(data) : onclickCreate(data)))}
|
||||
>
|
||||
{t('Confirm')}
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
||||
@@ -33,20 +33,16 @@ ${chatModelList
|
||||
md: `
|
||||
| 模型 | 价格(¥) |
|
||||
| --- | --- |
|
||||
${vectorModelList?.map((item) => `| ${item.name} | ${item.inputPrice}/1k tokens |`).join('\n')}
|
||||
${vectorModelList?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 |`).join('\n')}
|
||||
`
|
||||
},
|
||||
{
|
||||
title: '文件预处理模型(QA 拆分)',
|
||||
describe: '',
|
||||
md: `
|
||||
| 模型 | 输入价格(¥) | 输出价格(¥) |
|
||||
| --- | --- | --- |
|
||||
${qaModelList
|
||||
?.map(
|
||||
(item) => `| ${item.name} | ${item.inputPrice}/1k tokens | ${item.outputPrice}/1k tokens |`
|
||||
)
|
||||
.join('\n')}
|
||||
| 模型 | 价格(¥) |
|
||||
| --- | --- |
|
||||
${qaModelList?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 |`).join('\n')}
|
||||
`
|
||||
},
|
||||
{
|
||||
@@ -84,14 +80,6 @@ ${qgModelList
|
||||
(item) => `| ${item.name} | ${item.inputPrice}/1k tokens | ${item.outputPrice}/1k tokens |`
|
||||
)
|
||||
.join('\n')}`
|
||||
},
|
||||
{
|
||||
title: '重排模型(增强检索 & 混合检索)',
|
||||
describe: '',
|
||||
md: `
|
||||
| 模型 | 价格(¥) |
|
||||
| --- | --- |
|
||||
${reRankModelList?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 |`).join('\n')}`
|
||||
},
|
||||
{
|
||||
title: '语音播放',
|
||||
|
||||
@@ -2,9 +2,9 @@ import {
|
||||
TrainingModeEnum,
|
||||
DatasetCollectionTypeEnum,
|
||||
DatasetTypeEnum
|
||||
} from '@fastgpt/global/core/dataset/constant';
|
||||
} from '@fastgpt/global/core/dataset/constants';
|
||||
import type { RequestPaging } from '@/types';
|
||||
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import type { SearchTestItemType } from '@/types/core/dataset';
|
||||
import { UploadChunkItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type.d';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
export const SimpleModeTemplate_FastGPT_Universal: AppSimpleEditConfigTemplateType = {
|
||||
id: 'fastgpt-universal',
|
||||
name: '通用模板',
|
||||
desc: '通用模板\n可完全自行配置AI属性和知识库',
|
||||
name: 'core.app.template.Common template',
|
||||
desc: 'core.app.template.Common template tip',
|
||||
systemForm: {
|
||||
aiSettings: {
|
||||
model: true,
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
DatasetSearchModeEnum,
|
||||
DatasetTypeEnum,
|
||||
TrainingModeEnum
|
||||
} from '@fastgpt/global/core/dataset/constant';
|
||||
} from '@fastgpt/global/core/dataset/constants';
|
||||
import {
|
||||
DatasetDataIndexItemType,
|
||||
SearchDataResponseItemType
|
||||
|
||||
@@ -53,8 +53,8 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
|
||||
回答要求:
|
||||
- 如果你不清楚答案,你需要澄清。
|
||||
- 避免提及你是从 data 获取的知识。
|
||||
- 保持答案与 data 中描述的一致。
|
||||
- 避免提及你是从 <data></data> 获取的知识。
|
||||
- 保持答案与 <data></data> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
|
||||
@@ -88,8 +88,8 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
3. 如果无关,你直接拒绝回答本次问题。
|
||||
|
||||
回答要求:
|
||||
- 避免提及你是从 data 获取的知识。
|
||||
- 保持答案与 data 中描述的一致。
|
||||
- 避免提及你是从 <data></data> 获取的知识。
|
||||
- 保持答案与 <data></data> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { AppProps } from 'next/app';
|
||||
import Script from 'next/script';
|
||||
import Head from 'next/head';
|
||||
|
||||
@@ -30,7 +30,7 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
|
||||
hasTokens,
|
||||
hasInputTokens,
|
||||
hasOutputTokens,
|
||||
hasTextLen,
|
||||
hasCharsLen,
|
||||
hasDuration,
|
||||
hasDataLen
|
||||
} = useMemo(() => {
|
||||
@@ -38,7 +38,7 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
|
||||
let hasTokens = false;
|
||||
let hasInputTokens = false;
|
||||
let hasOutputTokens = false;
|
||||
let hasTextLen = false;
|
||||
let hasCharsLen = false;
|
||||
let hasDuration = false;
|
||||
let hasDataLen = false;
|
||||
|
||||
@@ -46,24 +46,21 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
|
||||
if (item.model !== undefined) {
|
||||
hasModel = true;
|
||||
}
|
||||
if (item.tokenLen !== undefined) {
|
||||
if (typeof item.tokenLen === 'number') {
|
||||
hasTokens = true;
|
||||
}
|
||||
if (item.inputTokens !== undefined) {
|
||||
if (typeof item.inputTokens === 'number') {
|
||||
hasInputTokens = true;
|
||||
}
|
||||
if (item.outputTokens !== undefined) {
|
||||
if (typeof item.outputTokens === 'number') {
|
||||
hasOutputTokens = true;
|
||||
}
|
||||
if (item.textLen !== undefined) {
|
||||
hasTextLen = true;
|
||||
if (typeof item.charsLength === 'number') {
|
||||
hasCharsLen = true;
|
||||
}
|
||||
if (item.duration !== undefined) {
|
||||
if (typeof item.duration === 'number') {
|
||||
hasDuration = true;
|
||||
}
|
||||
if (item.dataLen !== undefined) {
|
||||
hasDataLen = true;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -71,7 +68,7 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
|
||||
hasTokens,
|
||||
hasInputTokens,
|
||||
hasOutputTokens,
|
||||
hasTextLen,
|
||||
hasCharsLen,
|
||||
hasDuration,
|
||||
hasDataLen
|
||||
};
|
||||
@@ -123,9 +120,8 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
|
||||
{hasTokens && <Th>{t('wallet.bill.Token Length')}</Th>}
|
||||
{hasInputTokens && <Th>{t('wallet.bill.Input Token Length')}</Th>}
|
||||
{hasOutputTokens && <Th>{t('wallet.bill.Output Token Length')}</Th>}
|
||||
{hasTextLen && <Th>{t('wallet.bill.Text Length')}</Th>}
|
||||
{hasCharsLen && <Th>{t('wallet.bill.Text Length')}</Th>}
|
||||
{hasDuration && <Th>{t('wallet.bill.Duration')}</Th>}
|
||||
{hasDataLen && <Th>{t('wallet.bill.Data Length')}</Th>}
|
||||
<Th>费用(¥)</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -137,10 +133,8 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
|
||||
{hasTokens && <Td>{item.tokenLen ?? '-'}</Td>}
|
||||
{hasInputTokens && <Td>{item.inputTokens ?? '-'}</Td>}
|
||||
{hasOutputTokens && <Td>{item.outputTokens ?? '-'}</Td>}
|
||||
{hasTextLen && <Td>{item.textLen ?? '-'}</Td>}
|
||||
{hasCharsLen && <Td>{item.charsLength ?? '-'}</Td>}
|
||||
{hasDuration && <Td>{item.duration ?? '-'}</Td>}
|
||||
{hasDataLen && <Td>{item.dataLen ?? '-'}</Td>}
|
||||
|
||||
<Td>{formatStorePrice2Read(item.amount)}</Td>
|
||||
</Tr>
|
||||
))}
|
||||
|
||||
@@ -108,12 +108,12 @@ const UserInfo = () => {
|
||||
});
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: typeof err === 'string' ? err : '头像选择异常',
|
||||
title: typeof err === 'string' ? err : t('common.error.Select avatar failed'),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
},
|
||||
[onclickSave, toast, userInfo]
|
||||
[onclickSave, t, toast, userInfo]
|
||||
);
|
||||
|
||||
useQuery(['init'], initUserInfo, {
|
||||
|
||||
@@ -1,184 +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 { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants';
|
||||
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { getUserDefaultTeam } from '@fastgpt/service/support/user/team/controller';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
import { defaultQAModels } from '@fastgpt/global/core/ai/model';
|
||||
|
||||
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;
|
||||
|
||||
try {
|
||||
await Promise.allSettled([
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ADD COLUMN data_id VARCHAR(50);`),
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN q DROP NOT NULL;`), // q can null
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN a DROP NOT NULL;`), // a can null
|
||||
PgClient.query(
|
||||
`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN team_id TYPE VARCHAR(50) USING team_id::VARCHAR(50);`
|
||||
), // team_id varchar
|
||||
PgClient.query(
|
||||
`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN tmb_id TYPE VARCHAR(50) USING tmb_id::VARCHAR(50);`
|
||||
), // tmb_id varchar
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN team_id SET NOT NULL;`), // team_id not null
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN tmb_id SET NOT NULL;`), // tmb_id not null
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN dataset_id SET NOT NULL;`), // dataset_id not null
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN collection_id SET NOT NULL;`) // collection_id not null
|
||||
]);
|
||||
} catch (error) {}
|
||||
|
||||
try {
|
||||
await initPgData();
|
||||
} catch (error) {}
|
||||
|
||||
await MongoDataset.updateMany(
|
||||
{},
|
||||
{
|
||||
agentModel: defaultQAModels[0].model
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res, {
|
||||
data: await init(limit),
|
||||
message:
|
||||
'初始化任务已开始,请注意日志进度。可通过 select count(id) from modeldata where data_id is null; 检查是否完全初始化,如果结果为 0 ,则完全初始化。'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type PgItemType = {
|
||||
id: string;
|
||||
q: string;
|
||||
a: string;
|
||||
dataset_id: string;
|
||||
collection_id: string;
|
||||
team_id: string;
|
||||
tmb_id: string;
|
||||
};
|
||||
|
||||
async function initPgData() {
|
||||
const limit = 10;
|
||||
const { rows } = await PgClient.query<{ user_id: string }>(`
|
||||
SELECT DISTINCT user_id FROM ${PgDatasetTableName} WHERE team_id='null';
|
||||
`);
|
||||
console.log('init pg', rows.length);
|
||||
let success = 0;
|
||||
for (let i = 0; i < limit; i++) {
|
||||
init(i);
|
||||
}
|
||||
|
||||
async function init(index: number): Promise<any> {
|
||||
const userId = rows[index]?.user_id;
|
||||
if (!userId) return;
|
||||
try {
|
||||
const tmb = await getUserDefaultTeam({ userId });
|
||||
console.log(tmb);
|
||||
|
||||
// update pg
|
||||
await PgClient.query(
|
||||
`Update ${PgDatasetTableName} set team_id = '${String(tmb.teamId)}', tmb_id = '${String(
|
||||
tmb.tmbId
|
||||
)}' where user_id = '${userId}' AND team_id='null';`
|
||||
);
|
||||
console.log(++success);
|
||||
init(index + limit);
|
||||
} catch (error) {
|
||||
if (error === 'default team not exist') {
|
||||
return;
|
||||
}
|
||||
console.log(error);
|
||||
await delay(1000);
|
||||
return init(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function init(limit: number): Promise<any> {
|
||||
const { rows: idList } = await PgClient.query<{ id: string }>(
|
||||
`SELECT id FROM ${PgDatasetTableName} WHERE data_id IS NULL`
|
||||
);
|
||||
|
||||
console.log('totalCount', idList.length);
|
||||
if (idList.length === 0) return;
|
||||
|
||||
for (let i = 0; i < limit; i++) {
|
||||
initData(i);
|
||||
}
|
||||
|
||||
async function initData(index: number): Promise<any> {
|
||||
const dataId = idList[index]?.id;
|
||||
if (!dataId) {
|
||||
console.log('done');
|
||||
return;
|
||||
}
|
||||
// get limit data where data_id is null
|
||||
const { rows } = await PgClient.query<PgItemType>(
|
||||
`SELECT id,q,a,dataset_id,collection_id,team_id,tmb_id FROM ${PgDatasetTableName} WHERE id=${dataId};`
|
||||
);
|
||||
const data = rows[0];
|
||||
if (!data) {
|
||||
console.log('done');
|
||||
return;
|
||||
}
|
||||
|
||||
let id = '';
|
||||
try {
|
||||
// create mongo data and update data_id
|
||||
const { _id } = await MongoDatasetData.create({
|
||||
teamId: data.team_id.trim(),
|
||||
tmbId: data.tmb_id.trim(),
|
||||
datasetId: data.dataset_id,
|
||||
collectionId: data.collection_id,
|
||||
q: data.q,
|
||||
a: data.a,
|
||||
fullTextToken: '',
|
||||
indexes: [
|
||||
{
|
||||
defaultIndex: !data.a,
|
||||
type: data.a ? DatasetDataIndexTypeEnum.qa : DatasetDataIndexTypeEnum.chunk,
|
||||
dataId: data.id,
|
||||
text: data.q
|
||||
}
|
||||
]
|
||||
});
|
||||
id = _id;
|
||||
// update pg data_id
|
||||
await PgClient.query(
|
||||
`UPDATE ${PgDatasetTableName} SET data_id='${String(_id)}' WHERE id=${dataId};`
|
||||
);
|
||||
|
||||
console.log(++success);
|
||||
return initData(index + limit);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log(data);
|
||||
|
||||
try {
|
||||
if (id) {
|
||||
await MongoDatasetData.findByIdAndDelete(id);
|
||||
}
|
||||
} catch (error) {}
|
||||
await delay(500);
|
||||
return initData(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,173 +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 { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants';
|
||||
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { Types, connectionMongo } from '@fastgpt/service/common/mongo';
|
||||
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||
import { getUserDefaultTeam } from '@fastgpt/service/support/user/team/controller';
|
||||
import { getGFSCollection } from '@fastgpt/service/common/file/gridfs/controller';
|
||||
|
||||
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 init(limit);
|
||||
await initCollectionFileTeam(limit);
|
||||
|
||||
jsonRes(res, {});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type PgItemType = {
|
||||
id: string;
|
||||
q: string;
|
||||
a: string;
|
||||
dataset_id: string;
|
||||
collection_id: string;
|
||||
data_id: string;
|
||||
};
|
||||
|
||||
async function init(limit: number): Promise<any> {
|
||||
const { rows } = await PgClient.query<{ id: string; data_id: string }>(
|
||||
`SELECT id,data_id FROM ${PgDatasetTableName} WHERE team_id = tmb_id`
|
||||
);
|
||||
|
||||
console.log('totalCount', rows.length);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
if (rows.length === 0) return;
|
||||
|
||||
for (let i = 0; i < limit; i++) {
|
||||
initData(i);
|
||||
}
|
||||
|
||||
async function initData(index: number): Promise<any> {
|
||||
const item = rows[index];
|
||||
if (!item) {
|
||||
console.log('done');
|
||||
return;
|
||||
}
|
||||
// get mongo
|
||||
const mongoData = await MongoDatasetData.findById(item.data_id, '_id teamId tmbId');
|
||||
if (!mongoData) {
|
||||
return initData(index + limit);
|
||||
}
|
||||
|
||||
try {
|
||||
// find team owner
|
||||
const db = connectionMongo?.connection?.db;
|
||||
const TeamMember = db.collection(TeamMemberCollectionName);
|
||||
|
||||
const tmb = await TeamMember.findOne({
|
||||
teamId: new Types.ObjectId(mongoData.teamId),
|
||||
role: 'owner'
|
||||
});
|
||||
|
||||
if (!tmb) {
|
||||
return initData(index + limit);
|
||||
}
|
||||
|
||||
// update mongo and pg tmb_id
|
||||
await MongoDatasetData.findByIdAndUpdate(item.data_id, {
|
||||
tmbId: tmb._id
|
||||
});
|
||||
await PgClient.query(
|
||||
`UPDATE ${PgDatasetTableName} SET tmb_id = '${String(tmb._id)}' WHERE id = '${item.id}'`
|
||||
);
|
||||
|
||||
console.log(++success);
|
||||
|
||||
return initData(index + limit);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
await delay(500);
|
||||
return initData(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initCollectionFileTeam(limit: number) {
|
||||
/* init user default Team */
|
||||
const DatasetFile = getGFSCollection('dataset');
|
||||
const matchWhere = {
|
||||
$or: [{ 'metadata.teamId': { $exists: false } }, { 'metadata.teamId': null }]
|
||||
};
|
||||
const uniqueUsersWithNoTeamId = await DatasetFile.aggregate([
|
||||
{
|
||||
$match: matchWhere
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$metadata.userId', // 按 metadata.userId 分组以去重
|
||||
userId: { $first: '$metadata.userId' } // 保留第一个出现的 userId
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0, // 不显示 _id 字段
|
||||
userId: 1 // 只显示 userId 字段
|
||||
}
|
||||
}
|
||||
]).toArray();
|
||||
const users = uniqueUsersWithNoTeamId;
|
||||
|
||||
console.log('un init total', users.length);
|
||||
// limit 组一次
|
||||
const userArr: any[][] = [];
|
||||
for (let i = 0; i < users.length; i += limit) {
|
||||
userArr.push(users.slice(i, i + limit));
|
||||
}
|
||||
|
||||
let success = 0;
|
||||
for await (const item of userArr) {
|
||||
await Promise.all(item.map((item) => init(item.userId)));
|
||||
success += limit;
|
||||
console.log(success);
|
||||
}
|
||||
|
||||
async function init(userId: string): Promise<any> {
|
||||
try {
|
||||
const tmb = await getUserDefaultTeam({
|
||||
userId
|
||||
});
|
||||
|
||||
await DatasetFile.updateMany(
|
||||
{
|
||||
'metadata.userId': String(userId),
|
||||
...matchWhere
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'metadata.teamId': String(tmb.teamId),
|
||||
'metadata.tmbId': String(tmb.tmbId)
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (error === 'team not exist' || error === 'tmbId or userId is required') {
|
||||
return;
|
||||
}
|
||||
console.log(error);
|
||||
await delay(1000);
|
||||
return init(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoBill } from '@fastgpt/service/support/wallet/bill/schema';
|
||||
import {
|
||||
createDefaultTeam,
|
||||
getUserDefaultTeam
|
||||
} from '@fastgpt/service/support/user/team/controller';
|
||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||
import { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
|
||||
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { getGFSCollection } from '@fastgpt/service/common/file/gridfs/controller';
|
||||
import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { limit = 50, maxSize = 3 } = req.body as { limit: number; maxSize: number };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
await initDefaultTeam(limit, maxSize);
|
||||
await initMongoTeamId(limit);
|
||||
await initDatasetAndApp();
|
||||
await initCollectionFileTeam(limit);
|
||||
|
||||
if (FastGPTProUrl) {
|
||||
POST('/admin/init46');
|
||||
}
|
||||
|
||||
await initPgData();
|
||||
|
||||
jsonRes(res, {
|
||||
data: {}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function initDefaultTeam(limit: number, maxSize: number) {
|
||||
/* init user default Team */
|
||||
const users = await MongoUser.find({}, '_id balance');
|
||||
console.log('init user default team', users.length);
|
||||
// 100 组一次
|
||||
const userArr: UserModelSchema[][] = [];
|
||||
for (let i = 0; i < users.length; i += limit) {
|
||||
userArr.push(users.slice(i, i + limit));
|
||||
}
|
||||
let success = 0;
|
||||
for await (const users of userArr) {
|
||||
await Promise.all(users.map(init));
|
||||
success += limit;
|
||||
console.log(success);
|
||||
}
|
||||
|
||||
async function init(user: UserModelSchema): Promise<any> {
|
||||
try {
|
||||
await createDefaultTeam({
|
||||
userId: user._id,
|
||||
balance: user.balance,
|
||||
maxSize
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
await delay(1000);
|
||||
return init(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function initMongoTeamId(limit: number) {
|
||||
const mongoSchema = [
|
||||
{
|
||||
label: 'MongoPlugin',
|
||||
schema: MongoPlugin
|
||||
},
|
||||
{
|
||||
label: 'MongoChat',
|
||||
schema: MongoChat
|
||||
},
|
||||
{
|
||||
label: 'MongoChatItem',
|
||||
schema: MongoChatItem
|
||||
},
|
||||
{
|
||||
label: 'MongoApp',
|
||||
schema: MongoApp
|
||||
},
|
||||
{
|
||||
label: 'MongoDataset',
|
||||
schema: MongoDataset
|
||||
},
|
||||
{
|
||||
label: 'MongoDatasetCollection',
|
||||
schema: MongoDatasetCollection
|
||||
},
|
||||
{
|
||||
label: 'MongoDatasetTraining',
|
||||
schema: MongoDatasetTraining
|
||||
},
|
||||
{
|
||||
label: 'MongoBill',
|
||||
schema: MongoBill
|
||||
},
|
||||
{
|
||||
label: 'MongoOutLink',
|
||||
schema: MongoOutLink
|
||||
},
|
||||
{
|
||||
label: 'MongoOpenApi',
|
||||
schema: MongoOpenApi
|
||||
}
|
||||
];
|
||||
/* init user default Team */
|
||||
|
||||
for await (const item of mongoSchema) {
|
||||
console.log('start init', item.label);
|
||||
await initTeamTmbId(item.schema);
|
||||
console.log('finish init', item.label);
|
||||
}
|
||||
|
||||
async function initTeamTmbId(schema: any) {
|
||||
const emptyWhere = {
|
||||
$or: [{ teamId: { $exists: false } }, { teamId: null }]
|
||||
};
|
||||
const uniqueUsersWithNoTeamId = await schema.aggregate([
|
||||
{
|
||||
$match: emptyWhere
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$userId', // 按 userId 分组以去重
|
||||
userId: { $first: '$userId' } // 保留第一个出现的 userId
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0, // 不显示 _id 字段
|
||||
userId: 1 // 只显示 userId 字段
|
||||
}
|
||||
}
|
||||
]);
|
||||
const users = uniqueUsersWithNoTeamId;
|
||||
|
||||
console.log('un init total', users.length);
|
||||
// limit 组一次
|
||||
const userArr: any[][] = [];
|
||||
for (let i = 0; i < users.length; i += limit) {
|
||||
userArr.push(users.slice(i, i + limit));
|
||||
}
|
||||
|
||||
let success = 0;
|
||||
for await (const users of userArr) {
|
||||
await Promise.all(users.map((item) => init(item.userId)));
|
||||
success += limit;
|
||||
console.log(success);
|
||||
}
|
||||
|
||||
async function init(userId: string): Promise<any> {
|
||||
try {
|
||||
const tmb = await getUserDefaultTeam({ userId });
|
||||
|
||||
await schema.updateMany(
|
||||
{
|
||||
userId,
|
||||
...emptyWhere
|
||||
},
|
||||
{
|
||||
teamId: tmb.teamId,
|
||||
tmbId: tmb.tmbId
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (error === 'team not exist' || error === 'tmbId or userId is required') {
|
||||
return;
|
||||
}
|
||||
console.log(error);
|
||||
await delay(1000);
|
||||
return init(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async function initDatasetAndApp() {
|
||||
await MongoDataset.updateMany(
|
||||
{},
|
||||
{
|
||||
$set: {
|
||||
permission: PermissionTypeEnum.private
|
||||
}
|
||||
}
|
||||
);
|
||||
await MongoApp.updateMany(
|
||||
{},
|
||||
{
|
||||
$set: {
|
||||
permission: PermissionTypeEnum.private
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
async function initCollectionFileTeam(limit: number) {
|
||||
/* init user default Team */
|
||||
const DatasetFile = getGFSCollection('dataset');
|
||||
const matchWhere = {
|
||||
$or: [{ 'metadata.teamId': { $exists: false } }, { 'metadata.teamId': null }]
|
||||
};
|
||||
const uniqueUsersWithNoTeamId = await DatasetFile.aggregate([
|
||||
{
|
||||
$match: matchWhere
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$metadata.userId', // 按 metadata.userId 分组以去重
|
||||
userId: { $first: '$metadata.userId' } // 保留第一个出现的 userId
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0, // 不显示 _id 字段
|
||||
userId: 1 // 只显示 userId 字段
|
||||
}
|
||||
}
|
||||
]).toArray();
|
||||
const users = uniqueUsersWithNoTeamId;
|
||||
|
||||
console.log('un init total', users.length);
|
||||
// limit 组一次
|
||||
const userArr: any[][] = [];
|
||||
for (let i = 0; i < users.length; i += limit) {
|
||||
userArr.push(users.slice(i, i + limit));
|
||||
}
|
||||
|
||||
let success = 0;
|
||||
for await (const item of userArr) {
|
||||
await Promise.all(item.map((item) => init(item.userId)));
|
||||
success += limit;
|
||||
console.log(success);
|
||||
}
|
||||
|
||||
async function init(userId: string): Promise<any> {
|
||||
try {
|
||||
const tmb = await getUserDefaultTeam({
|
||||
userId
|
||||
});
|
||||
|
||||
await DatasetFile.updateMany(
|
||||
{
|
||||
'metadata.userId': String(userId),
|
||||
...matchWhere
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'metadata.teamId': String(tmb.teamId),
|
||||
'metadata.tmbId': String(tmb.tmbId)
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (error === 'team not exist' || error === 'tmbId or userId is required') {
|
||||
return;
|
||||
}
|
||||
console.log(error);
|
||||
await delay(1000);
|
||||
return init(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function initPgData() {
|
||||
const limit = 10;
|
||||
// add column
|
||||
try {
|
||||
await Promise.allSettled([
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ADD COLUMN team_id VARCHAR(50);`),
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ADD COLUMN tmb_id VARCHAR(50);`),
|
||||
PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN user_id DROP NOT NULL;`)
|
||||
]);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log('column exists');
|
||||
}
|
||||
|
||||
const { rows } = await PgClient.query<{ user_id: string }>(`
|
||||
SELECT DISTINCT user_id FROM ${PgDatasetTableName} WHERE team_id IS NULL;
|
||||
`);
|
||||
console.log('init pg', rows.length);
|
||||
let success = 0;
|
||||
for (let i = 0; i < limit; i++) {
|
||||
init(i);
|
||||
}
|
||||
async function init(index: number): Promise<any> {
|
||||
const userId = rows[index]?.user_id;
|
||||
if (!userId) return;
|
||||
try {
|
||||
const tmb = await getUserDefaultTeam({ userId });
|
||||
// update pg
|
||||
await PgClient.query(
|
||||
`Update ${PgDatasetTableName} set team_id = '${tmb.teamId}', tmb_id = '${tmb.tmbId}' where user_id = '${userId}' AND team_id IS NULL;`
|
||||
);
|
||||
console.log(++success);
|
||||
init(index + limit);
|
||||
} catch (error) {
|
||||
if (error === 'default team not exist') {
|
||||
return;
|
||||
}
|
||||
console.log(error);
|
||||
await delay(1000);
|
||||
return init(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ 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/constant';
|
||||
import { DatasetStatusEnum, TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
|
||||
let success = 0;
|
||||
|
||||
106
projects/app/src/pages/api/admin/initv467.ts
Normal file
106
projects/app/src/pages/api/admin/initv467.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants';
|
||||
import { MongoImage } from '@fastgpt/service/common/file/image/schema';
|
||||
import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
let success = 0;
|
||||
let deleteImg = 0;
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { test = false } = req.body as { test: boolean };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
success = 0;
|
||||
deleteImg = 0;
|
||||
|
||||
// 取消 pg tmb_id 和 data_id 的null
|
||||
await PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN tmb_id DROP NOT NULL;`);
|
||||
await PgClient.query(`ALTER TABLE ${PgDatasetTableName} ALTER COLUMN data_id DROP NOT NULL;`);
|
||||
|
||||
// 重新绑定 images 和 collections
|
||||
const images = await MongoImage.find(
|
||||
{ 'metadata.fileId': { $exists: true } },
|
||||
'_id metadata'
|
||||
).lean();
|
||||
|
||||
// 去除 fileId 相同的数据
|
||||
const fileIdMap = new Map<string, MongoImageSchemaType>();
|
||||
images.forEach((image) => {
|
||||
// @ts-ignore
|
||||
const fileId = image.metadata?.fileId;
|
||||
if (!fileIdMap.has(fileId) && fileId) {
|
||||
fileIdMap.set(fileId, image);
|
||||
}
|
||||
});
|
||||
const images2 = Array.from(fileIdMap.values());
|
||||
|
||||
console.log('total image list', images2.length);
|
||||
|
||||
for await (const image of images2) {
|
||||
await initImages(image, test);
|
||||
}
|
||||
|
||||
jsonRes(res, {
|
||||
data: success,
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
export const initImages = async (image: MongoImageSchemaType, test: boolean): Promise<any> => {
|
||||
try {
|
||||
//@ts-ignore
|
||||
const fileId = image.metadata.fileId as string;
|
||||
if (!fileId) return;
|
||||
|
||||
// 找到集合
|
||||
const collection = await MongoDatasetCollection.findOne({ fileId }, '_id metadata').lean();
|
||||
|
||||
if (!collection) {
|
||||
deleteImg++;
|
||||
console.log('deleteImg', deleteImg);
|
||||
|
||||
if (test) return;
|
||||
return MongoImage.deleteOne({ _id: image._id });
|
||||
}
|
||||
|
||||
const relatedImageId = getNanoid(24);
|
||||
|
||||
// update image
|
||||
if (!test) {
|
||||
await Promise.all([
|
||||
MongoImage.updateMany(
|
||||
{ 'metadata.fileId': fileId },
|
||||
{ $set: { 'metadata.relatedId': relatedImageId } }
|
||||
),
|
||||
MongoDatasetCollection.findByIdAndUpdate(collection._id, {
|
||||
$set: {
|
||||
'metadata.relatedImgId': relatedImageId
|
||||
}
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
success++;
|
||||
console.log('success', success);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
await delay(1000);
|
||||
return initImages(image, test);
|
||||
}
|
||||
};
|
||||
@@ -1,95 +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 {
|
||||
delFileByFileIdList,
|
||||
getGFSCollection
|
||||
} from '@fastgpt/service/common/file/gridfs/controller';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
|
||||
/*
|
||||
check dataset.files data. If there is no match in dataset.collections, delete it
|
||||
*/
|
||||
let deleteFileAmount = 0;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const {
|
||||
startDay = 10,
|
||||
endDay = 3,
|
||||
limit = 30
|
||||
} = req.body as { startDay?: number; endDay?: number; limit?: number };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
// start: now - maxDay, end: now - 3 day
|
||||
const start = new Date(Date.now() - startDay * 24 * 60 * 60 * 1000);
|
||||
const end = new Date(Date.now() - endDay * 24 * 60 * 60 * 1000);
|
||||
deleteFileAmount = 0;
|
||||
|
||||
checkFiles(start, end, limit);
|
||||
|
||||
jsonRes(res, {
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
addLog.error(`check valid dataset files error`, error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkFiles(start: Date, end: Date, limit: number) {
|
||||
const collection = getGFSCollection('dataset');
|
||||
const where = {
|
||||
uploadDate: { $gte: start, $lte: end }
|
||||
};
|
||||
|
||||
// 1. get all _id
|
||||
const ids = await collection
|
||||
.find(where, {
|
||||
projection: {
|
||||
_id: 1
|
||||
}
|
||||
})
|
||||
.toArray();
|
||||
console.log('total files', ids.length);
|
||||
|
||||
for (let i = 0; i < limit; i++) {
|
||||
check(i);
|
||||
}
|
||||
|
||||
async function check(index: number): Promise<any> {
|
||||
const id = ids[index];
|
||||
if (!id) {
|
||||
console.log(`检测完成,共删除 ${deleteFileAmount} 个无效文件`);
|
||||
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { _id } = id;
|
||||
|
||||
// 2. find fileId in dataset.collections
|
||||
const hasCollection = await MongoDatasetCollection.countDocuments({ fileId: _id });
|
||||
|
||||
// 3. if not found, delete file
|
||||
if (hasCollection === 0) {
|
||||
await delFileByFileIdList({ bucketName: 'dataset', fileIdList: [String(_id)] });
|
||||
console.log('delete file', _id);
|
||||
deleteFileAmount++;
|
||||
}
|
||||
index % 100 === 0 && console.log(index);
|
||||
return check(index + limit);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
await delay(2000);
|
||||
return check(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,32 +18,24 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
try {
|
||||
const { userId, teamId, tmbId } = await authCert({ req, authToken: true });
|
||||
|
||||
const { files, bucketName, metadata } = await upload.doUpload(req, res);
|
||||
|
||||
filePaths = files.map((file) => file.path);
|
||||
const { file, bucketName, metadata } = await upload.doUpload(req, res);
|
||||
|
||||
filePaths = [file.path];
|
||||
await connectToDatabase();
|
||||
|
||||
if (!bucketName) {
|
||||
throw new Error('bucketName is empty');
|
||||
}
|
||||
|
||||
const upLoadResults = await Promise.all(
|
||||
files.map((file) =>
|
||||
uploadFile({
|
||||
teamId,
|
||||
tmbId,
|
||||
bucketName,
|
||||
path: file.path,
|
||||
filename: file.originalname,
|
||||
metadata: {
|
||||
...metadata,
|
||||
contentType: file.mimetype,
|
||||
userId
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
const upLoadResults = await uploadFile({
|
||||
teamId,
|
||||
tmbId,
|
||||
bucketName,
|
||||
path: file.path,
|
||||
filename: file.originalname,
|
||||
contentType: file.mimetype,
|
||||
metadata: metadata
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: upLoadResults
|
||||
|
||||
@@ -55,7 +55,8 @@ const defaultFeConfigs: FastGPTFeConfigsType = {
|
||||
websiteSyncLimitMinuted: 0
|
||||
},
|
||||
scripts: [],
|
||||
favicon: '/favicon.ico'
|
||||
favicon: '/favicon.ico',
|
||||
uploadFileMaxSize: 500
|
||||
};
|
||||
|
||||
export async function getInitConfig() {
|
||||
|
||||
@@ -7,7 +7,7 @@ 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/constant';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { getExtractModel } from '@/service/core/ai/model';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -37,7 +37,7 @@ function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
return [
|
||||
{
|
||||
moduleId: 'userChatInput',
|
||||
name: '用户问题(对话入口)',
|
||||
name: 'core.module.template.Chat entrance',
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
flowType: 'questionInput',
|
||||
position: {
|
||||
@@ -49,7 +49,7 @@ function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
key: 'userChatInput',
|
||||
type: 'systemInput',
|
||||
valueType: 'string',
|
||||
label: '用户问题',
|
||||
label: 'core.module.input.label.user question',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
@@ -58,7 +58,7 @@ function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: '用户问题',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'source',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
@@ -93,7 +93,7 @@ function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
{
|
||||
key: 'model',
|
||||
type: 'selectChatModel',
|
||||
label: '对话模型',
|
||||
label: 'core.module.input.label.aiModel',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
@@ -189,7 +189,7 @@ function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
type: 'textarea',
|
||||
label: '系统提示词',
|
||||
label: 'core.ai.Prompt',
|
||||
max: 300,
|
||||
valueType: 'string',
|
||||
description:
|
||||
@@ -268,7 +268,7 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
const modules: ModuleItemType[] = [
|
||||
{
|
||||
moduleId: 'userChatInput',
|
||||
name: '用户问题(对话入口)',
|
||||
name: 'core.module.template.Chat entrance',
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
flowType: 'questionInput',
|
||||
position: {
|
||||
@@ -280,7 +280,7 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
key: 'userChatInput',
|
||||
type: 'systemInput',
|
||||
valueType: 'string',
|
||||
label: '用户问题',
|
||||
label: 'core.module.input.label.user question',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
@@ -289,17 +289,13 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: '用户问题',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'source',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'vuc92c',
|
||||
key: 'userChatInput'
|
||||
},
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -307,7 +303,7 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
},
|
||||
{
|
||||
moduleId: 'datasetSearch',
|
||||
name: '知识库搜索',
|
||||
name: 'core.module.template.Dataset search',
|
||||
avatar: '/imgs/module/db.png',
|
||||
flowType: 'datasetSearchNode',
|
||||
showStatus: true,
|
||||
@@ -447,6 +443,18 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'hidden',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -473,7 +481,7 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
{
|
||||
key: 'model',
|
||||
type: 'selectChatModel',
|
||||
label: '对话模型',
|
||||
label: 'core.module.input.label.aiModel',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
@@ -569,7 +577,7 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] {
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
type: 'textarea',
|
||||
label: '系统提示词',
|
||||
label: 'core.ai.Prompt',
|
||||
max: 300,
|
||||
valueType: 'string',
|
||||
description:
|
||||
|
||||
@@ -33,7 +33,7 @@ function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
return [
|
||||
{
|
||||
moduleId: 'userChatInput',
|
||||
name: '用户问题(对话入口)',
|
||||
name: 'core.module.template.Chat entrance',
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
flowType: 'questionInput',
|
||||
position: {
|
||||
@@ -45,7 +45,7 @@ function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
key: 'userChatInput',
|
||||
type: 'systemInput',
|
||||
valueType: 'string',
|
||||
label: '用户问题',
|
||||
label: 'core.module.input.label.user question',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
@@ -54,7 +54,7 @@ function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: '用户问题',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'source',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
@@ -89,7 +89,7 @@ function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
{
|
||||
key: 'model',
|
||||
type: 'selectChatModel',
|
||||
label: '对话模型',
|
||||
label: 'core.module.input.label.aiModel',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
@@ -185,7 +185,7 @@ function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
type: 'textarea',
|
||||
label: '系统提示词',
|
||||
label: 'core.ai.Prompt',
|
||||
max: 300,
|
||||
valueType: 'string',
|
||||
description:
|
||||
@@ -264,7 +264,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
const modules: ModuleItemType[] = [
|
||||
{
|
||||
moduleId: 'userChatInput',
|
||||
name: '用户问题(对话入口)',
|
||||
name: 'core.module.template.Chat entrance',
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
flowType: 'questionInput',
|
||||
position: {
|
||||
@@ -276,7 +276,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
key: 'userChatInput',
|
||||
type: 'systemInput',
|
||||
valueType: 'string',
|
||||
label: '用户问题',
|
||||
label: 'core.module.input.label.user question',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false,
|
||||
connected: false
|
||||
@@ -285,17 +285,13 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: '用户问题',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'source',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'vuc92c',
|
||||
key: 'userChatInput'
|
||||
},
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -303,7 +299,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
},
|
||||
{
|
||||
moduleId: 'datasetSearch',
|
||||
name: '知识库搜索',
|
||||
name: 'core.module.template.Dataset search',
|
||||
avatar: '/imgs/module/db.png',
|
||||
flowType: 'datasetSearchNode',
|
||||
showStatus: true,
|
||||
@@ -457,6 +453,18 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
valueType: 'boolean',
|
||||
type: 'source',
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
label: 'core.module.input.label.user question',
|
||||
type: 'hidden',
|
||||
valueType: 'string',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -483,7 +491,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
{
|
||||
key: 'model',
|
||||
type: 'selectChatModel',
|
||||
label: '对话模型',
|
||||
label: 'core.module.input.label.aiModel',
|
||||
required: true,
|
||||
valueType: 'string',
|
||||
showTargetInApp: false,
|
||||
@@ -579,7 +587,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
type: 'textarea',
|
||||
label: '系统提示词',
|
||||
label: 'core.ai.Prompt',
|
||||
max: 300,
|
||||
valueType: 'string',
|
||||
description:
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Types } from '@fastgpt/service/common/mongo';
|
||||
import { addDays } from 'date-fns';
|
||||
import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
@@ -28,8 +29,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const { teamId } = await authApp({ req, authToken: true, appId, per: 'w' });
|
||||
|
||||
const where = {
|
||||
appId: new Types.ObjectId(appId),
|
||||
teamId: new Types.ObjectId(teamId),
|
||||
appId: new Types.ObjectId(appId),
|
||||
updateTime: {
|
||||
$gte: new Date(dateStart),
|
||||
$lte: new Date(dateEnd)
|
||||
@@ -41,18 +42,26 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
{ $match: where },
|
||||
{
|
||||
$lookup: {
|
||||
from: 'chatitems',
|
||||
let: { chat_id: '$chatId' },
|
||||
from: ChatItemCollectionName,
|
||||
let: { chatId: '$chatId' },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [
|
||||
{ $eq: ['$chatId', '$$chat_id'] },
|
||||
{ $eq: ['$appId', new Types.ObjectId(appId)] }
|
||||
{ $eq: ['$appId', new Types.ObjectId(appId)] },
|
||||
{ $eq: ['$chatId', '$$chatId'] }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
userGoodFeedback: 1,
|
||||
userBadFeedback: 1,
|
||||
customFeedbacks: 1,
|
||||
adminFeedback: 1
|
||||
}
|
||||
}
|
||||
],
|
||||
as: 'chatitems'
|
||||
|
||||
@@ -35,16 +35,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
return Promise.reject('Param are error');
|
||||
})();
|
||||
console.log(match);
|
||||
|
||||
// find chatIds
|
||||
const list = await MongoChat.find(match, 'chatId').lean();
|
||||
const idList = list.map((item) => item.chatId);
|
||||
|
||||
await MongoChatItem.deleteMany({
|
||||
appId,
|
||||
chatId: { $in: idList }
|
||||
});
|
||||
await MongoChat.deleteMany({
|
||||
appId,
|
||||
chatId: { $in: idList }
|
||||
});
|
||||
|
||||
|
||||
@@ -23,9 +23,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
|
||||
await MongoChatItem.deleteMany({
|
||||
appId,
|
||||
chatId
|
||||
});
|
||||
await MongoChat.findOneAndRemove({
|
||||
appId,
|
||||
chatId
|
||||
});
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
await MongoChatItem.findOneAndUpdate(
|
||||
{
|
||||
appId,
|
||||
chatId,
|
||||
dataId: chatItemId
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,14 +2,11 @@ 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 type {
|
||||
AdminUpdateFeedbackParams,
|
||||
CloseCustomFeedbackParams
|
||||
} from '@/global/core/chat/api.d';
|
||||
import type { CloseCustomFeedbackParams } from '@/global/core/chat/api.d';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
/* remove custom feedback */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
@@ -29,13 +26,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
await authCert({ req, authToken: true });
|
||||
|
||||
await MongoChatItem.findOneAndUpdate(
|
||||
{ dataId: chatItemId },
|
||||
{ appId, chatId, dataId: chatItemId },
|
||||
{ $unset: { [`customFeedbacks.${index}`]: 1 } }
|
||||
);
|
||||
await MongoChatItem.findOneAndUpdate(
|
||||
{ dataId: chatItemId },
|
||||
{ $pull: { customFeedbacks: null } }
|
||||
);
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
|
||||
@@ -29,6 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
await MongoChatItem.findOneAndUpdate(
|
||||
{
|
||||
appId,
|
||||
chatId,
|
||||
dataId: chatItemId
|
||||
},
|
||||
|
||||
@@ -31,8 +31,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
if (appId) {
|
||||
const { tmbId } = await authCert({ req, authToken: true });
|
||||
return {
|
||||
appId,
|
||||
tmbId,
|
||||
appId,
|
||||
source: ChatSourceEnum.online
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
appId,
|
||||
per: 'r'
|
||||
}),
|
||||
chatId ? MongoChat.findOne({ chatId }) : undefined
|
||||
chatId ? MongoChat.findOne({ appId, chatId }) : undefined
|
||||
]);
|
||||
|
||||
// auth chat permission
|
||||
@@ -41,6 +41,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
// get app and history
|
||||
const { history } = await getChatItems({
|
||||
appId,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${
|
||||
|
||||
@@ -25,8 +25,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
|
||||
await MongoChatItem.deleteOne({
|
||||
dataId: contentId,
|
||||
chatId
|
||||
appId,
|
||||
chatId,
|
||||
dataId: contentId
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
|
||||
@@ -56,7 +56,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
try {
|
||||
pushAudioSpeechBill({
|
||||
model: model,
|
||||
textLen: input.length,
|
||||
charsLength: input.length,
|
||||
tmbId,
|
||||
teamId,
|
||||
source: authType2BillSource({ authType })
|
||||
|
||||
@@ -26,7 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
// auth app permission
|
||||
const [tmb, chat, app] = await Promise.all([
|
||||
MongoTeamMember.findById(shareChat.tmbId, '_id userId').populate('userId', 'avatar').lean(),
|
||||
MongoChat.findOne({ chatId, shareId }).lean(),
|
||||
MongoChat.findOne({ appId, chatId, shareId }).lean(),
|
||||
MongoApp.findById(appId).lean()
|
||||
]);
|
||||
|
||||
@@ -40,6 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
}
|
||||
|
||||
const { history } = await getChatItems({
|
||||
appId: app._id,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value userGoodFeedback userBadFeedback ${
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
|
||||
await MongoChat.findOneAndUpdate(
|
||||
{ chatId },
|
||||
{ appId, chatId },
|
||||
{
|
||||
...(customTitle !== undefined && { customTitle }),
|
||||
...(top !== undefined && { top })
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getVectorModel } from '@/service/core/ai/model';
|
||||
import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d';
|
||||
import { mongoRPermission } from '@fastgpt/global/support/permission/utils';
|
||||
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
/* get all dataset by teamId or tmbId */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { uploadFile } from '@fastgpt/service/common/file/gridfs/controller';
|
||||
import { getUploadModel } from '@fastgpt/service/common/file/multer';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { FileCreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api';
|
||||
import { removeFilesByPaths } from '@fastgpt/service/common/file/utils';
|
||||
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
|
||||
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
/**
|
||||
* Creates the multer uploader
|
||||
*/
|
||||
const upload = getUploadModel({
|
||||
maxSize: 500 * 1024 * 1024
|
||||
});
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
let filePaths: string[] = [];
|
||||
|
||||
const { datasetId } = req.query as { datasetId: string };
|
||||
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
const { teamId, tmbId } = await authDataset({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
per: 'w',
|
||||
datasetId
|
||||
});
|
||||
|
||||
const { file, bucketName, data } = await upload.doUpload<FileCreateDatasetCollectionParams>(
|
||||
req,
|
||||
res
|
||||
);
|
||||
filePaths = [file.path];
|
||||
|
||||
if (!file || !bucketName) {
|
||||
throw new Error('file is empty');
|
||||
}
|
||||
|
||||
const { fileMetadata, collectionMetadata, ...collectionData } = data;
|
||||
|
||||
// upload file and create collection
|
||||
const fileId = await uploadFile({
|
||||
teamId,
|
||||
tmbId,
|
||||
bucketName,
|
||||
path: file.path,
|
||||
filename: file.originalname,
|
||||
contentType: file.mimetype,
|
||||
metadata: fileMetadata
|
||||
});
|
||||
|
||||
// create collection
|
||||
const collectionId = await createOneCollection({
|
||||
...collectionData,
|
||||
metadata: collectionMetadata,
|
||||
teamId,
|
||||
tmbId,
|
||||
type: DatasetCollectionTypeEnum.file,
|
||||
fileId
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: collectionId
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
|
||||
removeFilesByPaths(filePaths);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false
|
||||
}
|
||||
};
|
||||
@@ -7,7 +7,10 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import type { LinkCreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
|
||||
import { TrainingModeEnum, DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import {
|
||||
TrainingModeEnum,
|
||||
DatasetCollectionTypeEnum
|
||||
} from '@fastgpt/global/core/dataset/constants';
|
||||
import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
|
||||
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
|
||||
import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller';
|
||||
@@ -7,11 +7,14 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import type { TextCreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
|
||||
import { TrainingModeEnum, DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import {
|
||||
TrainingModeEnum,
|
||||
DatasetCollectionTypeEnum
|
||||
} from '@fastgpt/global/core/dataset/constants';
|
||||
import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter';
|
||||
import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
|
||||
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
|
||||
import { pushDataToDatasetCollection } from '@/service/core/dataset/data/controller';
|
||||
import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller';
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -39,8 +42,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
text,
|
||||
chunkLen: chunkSize,
|
||||
overlapRatio: trainingType === TrainingModeEnum.chunk ? 0.2 : 0,
|
||||
customReg: chunkSplitter ? [chunkSplitter] : [],
|
||||
countTokens: false
|
||||
customReg: chunkSplitter ? [chunkSplitter] : []
|
||||
});
|
||||
|
||||
// 2. check dataset limit
|
||||
@@ -67,7 +69,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
|
||||
// 4. push chunks to training queue
|
||||
const insertResults = await pushDataToDatasetCollection({
|
||||
const insertResults = await pushDataToTrainingQueue({
|
||||
teamId,
|
||||
tmbId,
|
||||
collectionId,
|
||||
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { findCollectionAndChild } from '@fastgpt/service/core/dataset/collection/utils';
|
||||
import { delCollectionRelevantData } from '@fastgpt/service/core/dataset/data/controller';
|
||||
import { delCollectionAndRelatedSources } from '@fastgpt/service/core/dataset/collection/controller';
|
||||
import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -15,7 +15,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
throw new Error('CollectionIdId is required');
|
||||
}
|
||||
|
||||
await authDatasetCollection({
|
||||
const { teamId, collection } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
@@ -24,13 +24,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
|
||||
// find all delete id
|
||||
const collections = await findCollectionAndChild(collectionId, '_id fileId');
|
||||
const delIdList = collections.map((item) => item._id);
|
||||
const collections = await findCollectionAndChild({
|
||||
teamId,
|
||||
datasetId: collection.datasetId._id,
|
||||
collectionId,
|
||||
fields: '_id teamId fileId metadata'
|
||||
});
|
||||
|
||||
// delete
|
||||
await delCollectionRelevantData({
|
||||
collectionIds: delIdList,
|
||||
fileIds: collections.map((item) => item?.fileId || '').filter(Boolean)
|
||||
await delCollectionAndRelatedSources({
|
||||
collections
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.
|
||||
import type { GetDatasetCollectionsProps } from '@/global/core/api/datasetReq';
|
||||
import { PagingData } from '@/types';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { startQueue } from '@/service/utils/tools';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { DatasetDataCollectionName } from '@fastgpt/service/core/dataset/data/schema';
|
||||
@@ -87,7 +87,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$eq: ['$collectionId', '$$id']
|
||||
$and: [{ $eq: ['$teamId', match.teamId] }, { $eq: ['$collectionId', '$$id'] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -105,7 +105,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$eq: ['$collectionId', '$$id']
|
||||
$and: [
|
||||
{ $eq: ['$teamId', match.teamId] },
|
||||
{ $eq: ['$datasetId', match.datasetId] },
|
||||
{ $eq: ['$collectionId', '$$id'] }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,11 +6,11 @@ import {
|
||||
getCollectionAndRawText,
|
||||
reloadCollectionChunks
|
||||
} from '@fastgpt/service/core/dataset/collection/utils';
|
||||
import { delCollectionRelevantData } from '@fastgpt/service/core/dataset/data/controller';
|
||||
import { delCollectionAndRelatedSources } from '@fastgpt/service/core/dataset/collection/controller';
|
||||
import {
|
||||
DatasetCollectionSyncResultEnum,
|
||||
DatasetCollectionTypeEnum
|
||||
} from '@fastgpt/global/core/dataset/constant';
|
||||
} from '@fastgpt/global/core/dataset/constants';
|
||||
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||
import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller';
|
||||
import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
@@ -27,7 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
throw new Error('CollectionIdId is required');
|
||||
}
|
||||
|
||||
const { collection, tmbId } = await authDatasetCollection({
|
||||
const { collection, teamId, tmbId } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
collectionId,
|
||||
@@ -87,9 +87,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
|
||||
// delete old collection
|
||||
await delCollectionRelevantData({
|
||||
collectionIds: [collection._id],
|
||||
fileIds: collection.fileId ? [collection.fileId] : []
|
||||
await delCollectionAndRelatedSources({
|
||||
collections: [collection]
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
import type { CreateDatasetParams } from '@/global/core/dataset/api.d';
|
||||
import { createDefaultCollection } from '@fastgpt/service/core/dataset/collection/controller';
|
||||
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
|
||||
@@ -3,7 +3,8 @@ import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authDatasetData } from '@/service/support/permission/auth/dataset';
|
||||
import { delDatasetDataByDataId } from '@fastgpt/service/core/dataset/data/controller';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -17,7 +18,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { datasetData } = await authDatasetData({
|
||||
const { teamId, datasetData } = await authDatasetData({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
@@ -25,11 +26,20 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
await delDatasetDataByDataId({
|
||||
collectionId: datasetData.collectionId,
|
||||
mongoDataId: dataId
|
||||
// update mongo data update time
|
||||
await MongoDatasetData.findByIdAndUpdate(dataId, {
|
||||
updateTime: new Date()
|
||||
});
|
||||
|
||||
// delete vector data
|
||||
await deleteDatasetDataVector({
|
||||
teamId,
|
||||
idList: datasetData.indexes.map((item) => item.dataId)
|
||||
});
|
||||
|
||||
// delete mongo data
|
||||
await MongoDatasetData.findByIdAndDelete(dataId);
|
||||
|
||||
jsonRes(res, {
|
||||
data: 'success'
|
||||
});
|
||||
|
||||
@@ -71,12 +71,13 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
|
||||
// Duplicate data check
|
||||
await hasSameValue({
|
||||
teamId,
|
||||
collectionId,
|
||||
q: formatQ,
|
||||
a: formatA
|
||||
});
|
||||
|
||||
const { insertId, tokens } = await insertData2Dataset({
|
||||
const { insertId, charsLength } = await insertData2Dataset({
|
||||
teamId,
|
||||
tmbId,
|
||||
datasetId,
|
||||
@@ -91,7 +92,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
pushGenerateVectorBill({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
charsLength,
|
||||
model: vectorModelData.model
|
||||
});
|
||||
|
||||
|
||||
@@ -20,11 +20,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
pageSize = Math.min(pageSize, 30);
|
||||
|
||||
// 凭证校验
|
||||
await authDatasetCollection({ req, authToken: true, authApiKey: true, collectionId, per: 'r' });
|
||||
const { teamId, collection } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: 'r'
|
||||
});
|
||||
|
||||
searchText = searchText.replace(/'/g, '');
|
||||
|
||||
const match = {
|
||||
teamId,
|
||||
datasetId: collection.datasetId._id,
|
||||
collectionId,
|
||||
...(searchText
|
||||
? {
|
||||
|
||||
@@ -3,13 +3,12 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { TrainingModeEnum, TrainingTypeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
import type { PushDataResponse } from '@/global/core/api/datasetRes.d';
|
||||
import type { PushDatasetDataProps } from '@/global/core/dataset/api.d';
|
||||
import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
|
||||
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
|
||||
import { pushDataToDatasetCollection } from '@/service/core/dataset/data/controller';
|
||||
import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -41,7 +40,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
});
|
||||
|
||||
jsonRes<PushDataResponse>(res, {
|
||||
data: await pushDataToDatasetCollection({
|
||||
data: await pushDataToTrainingQueue({
|
||||
...req.body,
|
||||
teamId,
|
||||
tmbId
|
||||
|
||||
@@ -31,7 +31,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
// auth team balance
|
||||
await authTeamBalance(teamId);
|
||||
|
||||
const { tokens } = await updateData2Dataset({
|
||||
const { charsLength } = await updateData2Dataset({
|
||||
dataId: id,
|
||||
q,
|
||||
a,
|
||||
@@ -42,7 +42,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
pushGenerateVectorBill({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
charsLength,
|
||||
model: vectorModel
|
||||
});
|
||||
|
||||
|
||||
@@ -2,32 +2,35 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { delDatasetRelevantData } from '@fastgpt/service/core/dataset/data/controller';
|
||||
import { findDatasetIdTreeByTopDatasetId } from '@fastgpt/service/core/dataset/controller';
|
||||
import { delDatasetRelevantData } from '@fastgpt/service/core/dataset/controller';
|
||||
import { findDatasetAndAllChildren } from '@fastgpt/service/core/dataset/controller';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { id } = req.query as {
|
||||
const { id: datasetId } = req.query as {
|
||||
id: string;
|
||||
};
|
||||
|
||||
if (!id) {
|
||||
if (!datasetId) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
// auth owner
|
||||
await authDataset({ req, authToken: true, datasetId: id, per: 'owner' });
|
||||
const { teamId } = await authDataset({ req, authToken: true, datasetId, per: 'owner' });
|
||||
|
||||
const deletedIds = await findDatasetIdTreeByTopDatasetId(id);
|
||||
const datasets = await findDatasetAndAllChildren({
|
||||
teamId,
|
||||
datasetId
|
||||
});
|
||||
|
||||
// delete all dataset.data and pg data
|
||||
await delDatasetRelevantData({ datasetIds: deletedIds });
|
||||
await delDatasetRelevantData({ datasets });
|
||||
|
||||
// delete dataset data
|
||||
await MongoDataset.deleteMany({
|
||||
_id: { $in: deletedIds }
|
||||
_id: { $in: datasets.map((d) => d._id) }
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { findDatasetIdTreeByTopDatasetId } from '@fastgpt/service/core/dataset/controller';
|
||||
import { findDatasetAndAllChildren } from '@fastgpt/service/core/dataset/controller';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import {
|
||||
checkExportDatasetLimit,
|
||||
@@ -30,7 +30,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
limitMinutes: global.feConfigs?.limit?.exportDatasetLimitMinutes
|
||||
});
|
||||
|
||||
const exportIds = await findDatasetIdTreeByTopDatasetId(datasetId);
|
||||
const datasets = await findDatasetAndAllChildren({
|
||||
teamId,
|
||||
datasetId,
|
||||
fields: '_id'
|
||||
});
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv; charset=utf-8;');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=dataset.csv; ');
|
||||
@@ -42,7 +46,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
a: string;
|
||||
}>(
|
||||
{
|
||||
datasetId: { $in: exportIds }
|
||||
teamId,
|
||||
datasetId: { $in: datasets.map((d) => d._id) }
|
||||
},
|
||||
'q a'
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
import { mongoRPermission } from '@fastgpt/global/support/permission/utils';
|
||||
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
|
||||
|
||||
@@ -47,7 +47,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
// model: global.chatModels[0].model
|
||||
// });
|
||||
|
||||
const { searchRes, tokens, ...result } = await searchDatasetData({
|
||||
const { searchRes, charsLength, ...result } = await searchDatasetData({
|
||||
teamId,
|
||||
rawQuery: text,
|
||||
queries: [text],
|
||||
model: dataset.vectorModel,
|
||||
@@ -62,7 +63,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
const { total } = pushGenerateVectorBill({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
charsLength,
|
||||
model: dataset.vectorModel,
|
||||
source: apikey ? BillSourceEnum.api : BillSourceEnum.fastgpt
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ type Props = HttpBodyType<{
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const {
|
||||
appId,
|
||||
chatId,
|
||||
responseChatItemId: chatItemId,
|
||||
data: { defaultFeedback, customFeedback }
|
||||
@@ -30,6 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
// wait the chat finish
|
||||
setTimeout(() => {
|
||||
addCustomFeedbacks({
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
feedbacks: [feedback]
|
||||
|
||||
@@ -10,7 +10,7 @@ import { UserStatusEnum } from '@fastgpt/global/support/user/constant';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { username, password, tmbId = '' } = req.body as PostLoginProps;
|
||||
const { username, password } = req.body as PostLoginProps;
|
||||
|
||||
if (!username || !password) {
|
||||
throw new Error('缺少参数');
|
||||
@@ -40,7 +40,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
throw new Error('密码错误');
|
||||
}
|
||||
|
||||
const userDetail = await getUserDetail({ tmbId, userId: user._id });
|
||||
const userDetail = await getUserDetail({
|
||||
tmbId: user?.lastLoginTmbId,
|
||||
userId: user._id
|
||||
});
|
||||
|
||||
MongoUser.findByIdAndUpdate(user._id, {
|
||||
lastLoginTmbId: userDetail.team.tmbId
|
||||
});
|
||||
|
||||
const token = createJWT(userDetail);
|
||||
setCookie(res, token);
|
||||
|
||||
@@ -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 { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { size } = req.query as {
|
||||
size: string;
|
||||
};
|
||||
|
||||
// 凭证校验
|
||||
const { teamId } = await authCert({ req, authToken: true });
|
||||
|
||||
if (!size) {
|
||||
return jsonRes(res);
|
||||
}
|
||||
|
||||
const numberSize = Number(size);
|
||||
|
||||
await checkDatasetLimit({
|
||||
teamId,
|
||||
freeSize: global.feConfigs?.subscription?.datasetStoreFreeSize,
|
||||
insertLen: numberSize
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,32 @@
|
||||
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 { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { CreateTrainingBillProps } from '@fastgpt/global/support/wallet/bill/api.d';
|
||||
import { getQAModel, getVectorModel } from '@/service/core/ai/model';
|
||||
import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { name, vectorModel, agentModel } = req.body as CreateTrainingBillProps;
|
||||
const { name, datasetId } = req.body as CreateTrainingBillProps;
|
||||
|
||||
const { teamId, tmbId } = await authCert({ req, authToken: true, authApiKey: true });
|
||||
|
||||
const vectorModelData = getVectorModel(vectorModel);
|
||||
const agentModelData = getQAModel(agentModel);
|
||||
const { teamId, tmbId, dataset } = await authDataset({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
datasetId,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
const { billId } = await createTrainingBill({
|
||||
teamId,
|
||||
tmbId,
|
||||
appName: name,
|
||||
billSource: BillSourceEnum.training,
|
||||
vectorModel: vectorModelData.name,
|
||||
agentModel: agentModelData.name
|
||||
vectorModel: getVectorModel(dataset.vectorModel).name,
|
||||
agentModel: getQAModel(dataset.agentModel).name
|
||||
});
|
||||
|
||||
jsonRes<string>(res, {
|
||||
|
||||
@@ -17,11 +17,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
|
||||
try {
|
||||
const {
|
||||
files,
|
||||
metadata: { duration, shareId }
|
||||
file,
|
||||
data: { duration }
|
||||
} = await upload.doUpload<{ duration: number; shareId?: string }>(req, res);
|
||||
|
||||
filePaths = files.map((file) => file.path);
|
||||
filePaths = [file.path];
|
||||
|
||||
const { teamId, tmbId } = await authCert({ req, authToken: true });
|
||||
|
||||
@@ -29,8 +29,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
throw new Error('whisper model not found');
|
||||
}
|
||||
|
||||
const file = files[0];
|
||||
|
||||
if (!file) {
|
||||
throw new Error('file not found');
|
||||
}
|
||||
|
||||
@@ -195,7 +195,12 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
});
|
||||
|
||||
// get and concat history
|
||||
const { history } = await getChatItems({ chatId, limit: 30, field: `dataId obj value` });
|
||||
const { history } = await getChatItems({
|
||||
appId: app._id,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value`
|
||||
});
|
||||
const concatHistories = history.concat(chatMessages);
|
||||
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
|
||||
await authTeamBalance(teamId);
|
||||
|
||||
const { tokens, vectors } = await getVectorsByText({ input: query, model });
|
||||
const { charsLength, vectors } = await getVectorsByText({ input: query, model });
|
||||
|
||||
res.json({
|
||||
object: 'list',
|
||||
@@ -44,15 +44,15 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
})),
|
||||
model,
|
||||
usage: {
|
||||
prompt_tokens: tokens,
|
||||
total_tokens: tokens
|
||||
prompt_tokens: charsLength,
|
||||
total_tokens: charsLength
|
||||
}
|
||||
});
|
||||
|
||||
const { total } = pushGenerateVectorBill({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
charsLength,
|
||||
model,
|
||||
billId,
|
||||
source: getBillSourceByAuthType({ authType })
|
||||
|
||||
@@ -62,7 +62,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
});
|
||||
|
||||
if (unconnected) {
|
||||
const msg = `【${t(item.name)}】存在未填或未连接参数`;
|
||||
const msg = t('core.module.Unlink tip', { name: t(item.name) });
|
||||
|
||||
toast({
|
||||
status: 'warning',
|
||||
@@ -82,8 +82,8 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
permission: undefined
|
||||
});
|
||||
},
|
||||
successToast: '保存配置成功',
|
||||
errorToast: '保存配置异常',
|
||||
successToast: t('common.Save Success'),
|
||||
errorToast: t('common.Save Failed'),
|
||||
onSuccess() {
|
||||
ChatTestRef.current?.resetChatTest();
|
||||
}
|
||||
@@ -98,22 +98,23 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
alignItems={'center'}
|
||||
userSelect={'none'}
|
||||
>
|
||||
<MyTooltip label={t('common.Back')} offset={[10, 10]}>
|
||||
<IconButton
|
||||
size={'smSquare'}
|
||||
icon={<MyIcon name={'common/backLight'} w={'14px'} />}
|
||||
borderColor={'myGray.300'}
|
||||
variant={'whiteBase'}
|
||||
aria-label={''}
|
||||
onClick={openConfirmOut(async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
await onclickSave(modules);
|
||||
}
|
||||
onClose();
|
||||
}, onClose)}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<IconButton
|
||||
size={'smSquare'}
|
||||
icon={<MyIcon name={'common/backFill'} w={'14px'} />}
|
||||
borderRadius={'50%'}
|
||||
w={'26px'}
|
||||
h={'26px'}
|
||||
borderColor={'myGray.300'}
|
||||
variant={'whiteBase'}
|
||||
aria-label={''}
|
||||
onClick={openConfirmOut(async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
await onclickSave(modules);
|
||||
}
|
||||
onClose();
|
||||
}, onClose)}
|
||||
/>
|
||||
<Box ml={[3, 6]} fontSize={['md', '2xl']} flex={1}>
|
||||
{app.name}
|
||||
</Box>
|
||||
@@ -154,7 +155,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
onClick={() => setTestModules(undefined)}
|
||||
/>
|
||||
) : (
|
||||
<MyTooltip label={'测试对话'}>
|
||||
<MyTooltip label={t('core.Chat test')}>
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
icon={<MyIcon name={'core/chat/chatLight'} w={['14px', '16px']} />}
|
||||
@@ -171,9 +172,9 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
</MyTooltip>
|
||||
)}
|
||||
|
||||
<MyTooltip label={'保存配置'}>
|
||||
<MyTooltip label={t('common.Save')}>
|
||||
<IconButton
|
||||
icon={<MyIcon name={'save'} w={['14px', '16px']} />}
|
||||
icon={<MyIcon name={'common/saveFill'} w={['14px', '16px']} />}
|
||||
size={'smSquare'}
|
||||
isLoading={isLoading}
|
||||
aria-label={'save'}
|
||||
|
||||
@@ -44,12 +44,18 @@ const Render = ({ app, onClose }: Props) => {
|
||||
initData(JSON.parse(JSON.stringify(app.modules)));
|
||||
}, [app.modules]);
|
||||
|
||||
return <Flow templates={moduleTemplates} Header={<Header app={app} onClose={onClose} />} />;
|
||||
const memoRender = useMemo(() => {
|
||||
return <Flow templates={moduleTemplates} Header={<Header app={app} onClose={onClose} />} />;
|
||||
}, [app, moduleTemplates.length, onClose]);
|
||||
|
||||
return memoRender;
|
||||
};
|
||||
|
||||
export default React.memo(function FlowEdit(props: Props) {
|
||||
const filterAppIds = useMemo(() => [props.app._id], [props.app._id]);
|
||||
|
||||
return (
|
||||
<FlowProvider mode={'app'} filterAppIds={[props.app._id]}>
|
||||
<FlowProvider mode={'app'} filterAppIds={filterAppIds}>
|
||||
<Render {...props} />
|
||||
</FlowProvider>
|
||||
);
|
||||
|
||||
@@ -52,7 +52,7 @@ const InfoModal = ({
|
||||
});
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
// 提交保存模型修改
|
||||
// submit config
|
||||
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
|
||||
mutationFn: async (data: AppSchema) => {
|
||||
await updateAppDetail(data._id, {
|
||||
@@ -66,18 +66,17 @@ const InfoModal = ({
|
||||
onSuccess && onSuccess();
|
||||
onClose();
|
||||
toast({
|
||||
title: '更新成功',
|
||||
title: t('common.Update Success'),
|
||||
status: 'success'
|
||||
});
|
||||
},
|
||||
errorToast: '更新失败'
|
||||
errorToast: t('common.Update Failed')
|
||||
});
|
||||
|
||||
// 提交保存表单失败
|
||||
const saveSubmitError = useCallback(() => {
|
||||
// deep search message
|
||||
const deepSearch = (obj: any): string => {
|
||||
if (!obj) return '提交表单错误';
|
||||
if (!obj) return t('common.Submit failed');
|
||||
if (!!obj.message) {
|
||||
return obj.message;
|
||||
}
|
||||
@@ -89,7 +88,7 @@ const InfoModal = ({
|
||||
duration: 4000,
|
||||
isClosable: true
|
||||
});
|
||||
}, [errors, toast]);
|
||||
}, [errors, t, toast]);
|
||||
|
||||
const saveUpdateModel = useCallback(
|
||||
() => handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)(),
|
||||
@@ -111,12 +110,12 @@ const InfoModal = ({
|
||||
setRefresh((state) => !state);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: getErrText(err, '头像选择异常'),
|
||||
title: getErrText(err, t('common.error.Select avatar failed')),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
},
|
||||
[setValue, toast]
|
||||
[setValue, t, toast]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -127,7 +126,7 @@ const InfoModal = ({
|
||||
title={t('core.app.setting')}
|
||||
>
|
||||
<ModalBody>
|
||||
<Box>头像 & 名称</Box>
|
||||
<Box>{t('core.app.Name and avatar')}</Box>
|
||||
<Flex mt={2} alignItems={'center'}>
|
||||
<Avatar
|
||||
src={getValues('avatar')}
|
||||
@@ -136,21 +135,21 @@ const InfoModal = ({
|
||||
cursor={'pointer'}
|
||||
borderRadius={'md'}
|
||||
mr={4}
|
||||
title={'点击切换头像'}
|
||||
title={t('common.Set Avatar')}
|
||||
onClick={() => onOpenSelectFile()}
|
||||
/>
|
||||
<FormControl>
|
||||
<Input
|
||||
bg={'myWhite.600'}
|
||||
placeholder={'给应用设置一个名称'}
|
||||
placeholder={t('core.app.Set a name for your app')}
|
||||
{...register('name', {
|
||||
required: '展示名称不能为空'
|
||||
required: true
|
||||
})}
|
||||
></Input>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
<Box mt={4} mb={1}>
|
||||
应用介绍
|
||||
{t('core.app.App intro')}
|
||||
</Box>
|
||||
{/* <Box color={'myGray.500'} mb={2} fontSize={'sm'}>
|
||||
该介绍主要用于记忆和在应用市场展示
|
||||
@@ -158,7 +157,7 @@ const InfoModal = ({
|
||||
<Textarea
|
||||
rows={4}
|
||||
maxLength={500}
|
||||
placeholder={'给你的 AI 应用一个介绍'}
|
||||
placeholder={t('core.app.Make a brief introduction of your app')}
|
||||
bg={'myWhite.600'}
|
||||
{...register('intro')}
|
||||
/>
|
||||
@@ -176,10 +175,10 @@ const InfoModal = ({
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
取消
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button isLoading={btnLoading} onClick={saveUpdateModel}>
|
||||
保存
|
||||
{t('common.Save')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
|
||||
@@ -67,11 +67,8 @@ const Share = ({ appId }: { appId: string }) => {
|
||||
<Box position={'relative'} pt={3} px={5} minH={'50vh'}>
|
||||
<Flex justifyContent={'space-between'}>
|
||||
<Box fontWeight={'bold'} fontSize={['md', 'xl']}>
|
||||
免登录窗口
|
||||
<MyTooltip
|
||||
forceShow
|
||||
label="可以直接分享该模型给其他用户去进行对话,对方无需登录即可直接进行对话。注意,这个功能会消耗你账号的余额,请保管好链接!"
|
||||
>
|
||||
{t('core.app.Share link')}
|
||||
<MyTooltip forceShow label={t('core.app.Share link desc detail')}>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
@@ -82,29 +79,29 @@ const Share = ({ appId }: { appId: string }) => {
|
||||
{...(shareChatList.length >= 10
|
||||
? {
|
||||
isDisabled: true,
|
||||
title: '最多创建10组'
|
||||
title: t('core.app.share.Amount limit tip')
|
||||
}
|
||||
: {})}
|
||||
onClick={() => setEditLinkData(defaultOutLinkForm)}
|
||||
>
|
||||
创建新链接
|
||||
{t('core.app.share.Create link')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<TableContainer mt={3}>
|
||||
<Table variant={'simple'} w={'100%'} overflowX={'auto'} fontSize={'sm'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>名称</Th>
|
||||
<Th>金额消耗</Th>
|
||||
<Th>返回引用</Th>
|
||||
<Th>{t('common.Name')}</Th>
|
||||
<Th>{t('common.Price used')}</Th>
|
||||
<Th>{t('core.app.share.Is response quote')}</Th>
|
||||
{feConfigs?.isPlus && (
|
||||
<>
|
||||
<Th>IP限流(人/分钟)</Th>
|
||||
<Th>过期时间</Th>
|
||||
<Th>身份校验</Th>
|
||||
<Th>{t('core.app.share.Ip limit title')}</Th>
|
||||
<Th>{t('common.Expired Time')}</Th>
|
||||
<Th>{t('core.app.share.Role check')}</Th>
|
||||
</>
|
||||
)}
|
||||
<Th>最后使用时间</Th>
|
||||
<Th>{t('common.Last use time')}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -117,8 +114,8 @@ const Share = ({ appId }: { appId: string }) => {
|
||||
{feConfigs?.isPlus
|
||||
? `${
|
||||
item.limit && item.limit.credit > -1
|
||||
? ` / ${item.limit.credit}元`
|
||||
: ' / 无限制'
|
||||
? ` / ¥${item.limit.credit}`
|
||||
: ` / ${t('common.Unlimited')}`
|
||||
}`
|
||||
: ''}
|
||||
</Td>
|
||||
@@ -134,7 +131,9 @@ const Share = ({ appId }: { appId: string }) => {
|
||||
<Th>{item?.limit?.hookUrl ? '✔' : '✖'}</Th>
|
||||
</>
|
||||
)}
|
||||
<Td>{item.lastTime ? formatTimeToChatTime(item.lastTime) : '未使用'}</Td>
|
||||
<Td>
|
||||
{item.lastTime ? t(formatTimeToChatTime(item.lastTime)) : t('common.Un used')}
|
||||
</Td>
|
||||
<Td display={'flex'} alignItems={'center'}>
|
||||
<Menu autoSelect={false} isLazy>
|
||||
<MenuButton
|
||||
@@ -197,7 +196,7 @@ const Share = ({ appId }: { appId: string }) => {
|
||||
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} pt={'10vh'}>
|
||||
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
||||
<Box mt={2} color={'myGray.500'}>
|
||||
没有创建分享链接
|
||||
{t('core.app.share.Not share link')}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
@@ -208,7 +207,7 @@ const Share = ({ appId }: { appId: string }) => {
|
||||
defaultData={editLinkData}
|
||||
onCreate={(id) => {
|
||||
const url = `${location.origin}/chat/share?shareId=${id}`;
|
||||
copyData(url, '创建成功。已复制分享地址,可直接分享使用');
|
||||
copyData(url, t('core.app.share.Create link tip'));
|
||||
refetchShareChatList();
|
||||
setEditLinkData(undefined);
|
||||
}}
|
||||
@@ -268,14 +267,14 @@ function EditLinkModal({
|
||||
appId,
|
||||
type
|
||||
}),
|
||||
errorToast: '创建链接异常',
|
||||
errorToast: t('common.Create Failed'),
|
||||
onSuccess: onCreate
|
||||
});
|
||||
const { mutate: onclickUpdate, isLoading: updating } = useRequest({
|
||||
mutationFn: (e: OutLinkEditType) => {
|
||||
return putShareChat(e);
|
||||
},
|
||||
errorToast: '更新链接异常',
|
||||
errorToast: t('common.Update Failed'),
|
||||
onSuccess: onEdit
|
||||
});
|
||||
|
||||
@@ -384,14 +383,13 @@ function EditLinkModal({
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
取消
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
isLoading={creating || updating}
|
||||
onClick={submitShareChat((data) => (isEdit ? onclickUpdate(data) : onclickCreate(data)))}
|
||||
>
|
||||
确认
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
||||
@@ -6,9 +6,11 @@ import dynamic from 'next/dynamic';
|
||||
|
||||
import MyRadio from '@/components/common/MyRadio';
|
||||
import Share from './Share';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
const API = dynamic(() => import('./API'));
|
||||
|
||||
const OutLink = ({ appId }: { appId: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
const [linkType, setLinkType] = useState<`${OutLinkTypeEnum}`>(OutLinkTypeEnum.share);
|
||||
@@ -16,7 +18,7 @@ const OutLink = ({ appId }: { appId: string }) => {
|
||||
return (
|
||||
<Box pt={[1, 5]}>
|
||||
<Box fontWeight={'bold'} fontSize={['md', 'xl']} mb={2} px={[4, 8]}>
|
||||
外部使用途径
|
||||
{t('core.app.External using')}
|
||||
</Box>
|
||||
<Box pb={[5, 7]} px={[4, 8]} borderBottom={theme.borders.base}>
|
||||
<MyRadio
|
||||
@@ -25,14 +27,14 @@ const OutLink = ({ appId }: { appId: string }) => {
|
||||
list={[
|
||||
{
|
||||
icon: '/imgs/modal/shareFill.svg',
|
||||
title: '免登录窗口',
|
||||
desc: '分享链接给其他用户,无需登录即可直接进行使用',
|
||||
title: t('core.app.Share link'),
|
||||
desc: t('core.app.Share link desc'),
|
||||
value: OutLinkTypeEnum.share
|
||||
},
|
||||
{
|
||||
icon: 'support/outlink/apikeyFill',
|
||||
title: 'API 访问',
|
||||
desc: '通过 API 接入到已有系统中,或企微、飞书等',
|
||||
title: t('core.app.Api request'),
|
||||
desc: t('core.app.Api request desc'),
|
||||
value: OutLinkTypeEnum.apikey
|
||||
}
|
||||
// {
|
||||
|
||||
@@ -96,7 +96,7 @@ const AppCard = ({ appId }: { appId: string }) => {
|
||||
wordBreak={'break-all'}
|
||||
color={'myGray.600'}
|
||||
>
|
||||
{appDetail.intro || '快来给应用一个介绍~'}
|
||||
{appDetail.intro || t('core.app.tip.Add a intro to app')}
|
||||
</Box>
|
||||
<Flex>
|
||||
<Button
|
||||
@@ -105,7 +105,7 @@ const AppCard = ({ appId }: { appId: string }) => {
|
||||
leftIcon={<MyIcon name={'core/chat/chatLight'} w={'16px'} />}
|
||||
onClick={() => router.push(`/chat?appId=${appId}`)}
|
||||
>
|
||||
对话
|
||||
{t('core.Chat')}
|
||||
</Button>
|
||||
<Button
|
||||
mx={3}
|
||||
@@ -121,7 +121,7 @@ const AppCard = ({ appId }: { appId: string }) => {
|
||||
});
|
||||
}}
|
||||
>
|
||||
外接
|
||||
{t('core.app.navbar.External')}
|
||||
</Button>
|
||||
{appDetail.isOwner && (
|
||||
<Button
|
||||
@@ -130,7 +130,7 @@ const AppCard = ({ appId }: { appId: string }) => {
|
||||
leftIcon={<MyIcon name={'common/settingLight'} w={'16px'} />}
|
||||
onClick={() => setSettingAppInfo(appDetail)}
|
||||
>
|
||||
设置
|
||||
{t('common.Setting')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useCallback, useState, useTransition } from 'react';
|
||||
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Button, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
import PromptTextarea from '@/components/common/Textarea/PromptTextarea';
|
||||
import { Box, Button, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
|
||||
@@ -18,6 +18,7 @@ const CfrEditModal = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const [, startTst] = useTransition();
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
@@ -32,17 +33,19 @@ const CfrEditModal = ({
|
||||
<MyTooltip label={t('core.app.edit.cfr background tip')} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
<PromptTextarea
|
||||
mt={1}
|
||||
flex={1}
|
||||
bg={'myWhite.400'}
|
||||
rows={5}
|
||||
placeholder={t('core.module.input.placeholder.cfr background')}
|
||||
defaultValue={value}
|
||||
onBlur={(e) => {
|
||||
setValue(e.target.value || '');
|
||||
}}
|
||||
/>
|
||||
<Box mt={1} flex={1}>
|
||||
<PromptEditor
|
||||
h={200}
|
||||
showOpenModal={false}
|
||||
placeholder={t('core.module.input.placeholder.cfr background')}
|
||||
defaultValue={value}
|
||||
onChange={useCallback((e: string) => {
|
||||
startTst(() => {
|
||||
setValue(e);
|
||||
});
|
||||
}, [])}
|
||||
/>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
|
||||
@@ -114,7 +114,7 @@ const ChatTest = ({ appId }: { appId: string }) => {
|
||||
right={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
bg={'rgba(255,255,255,0.6)'}
|
||||
bg={'rgba(255,255,255,0.7)'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
flexDirection={'column'}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useMemo, useState, useTransition } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
@@ -24,7 +24,6 @@ import { useTranslation } from 'next-i18next';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
|
||||
import { postForm2Modules } from '@/web/core/app/utils';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
@@ -34,9 +33,11 @@ import Avatar from '@/components/Avatar';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants';
|
||||
import VariableEdit from '@/components/core/module/Flow/components/modules/VariableEdit';
|
||||
import PromptTextarea from '@/components/common/Textarea/PromptTextarea/index';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
import MyTextarea from '@/components/common/Textarea/MyTextarea/index';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
import SelectAiModel from '@/components/Select/SelectAiModel';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import { formatVariablesIcon } from '@fastgpt/global/core/module/utils';
|
||||
|
||||
const DatasetSelectModal = dynamic(() => import('@/components/core/module/DatasetSelectModal'));
|
||||
const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal'));
|
||||
@@ -61,8 +62,9 @@ const EditForm = ({
|
||||
const { loadAllDatasets, allDatasets } = useDatasetStore();
|
||||
const { isPc } = useSystemStore();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [, startTst] = useTransition();
|
||||
|
||||
const { register, setValue, getValues, reset, handleSubmit, control } =
|
||||
const { setValue, getValues, reset, handleSubmit, control, watch } =
|
||||
useForm<AppSimpleEditFormType>({
|
||||
defaultValues: getDefaultAppForm()
|
||||
});
|
||||
@@ -97,24 +99,25 @@ const EditForm = ({
|
||||
content: t('core.app.edit.Confirm Save App Tip')
|
||||
});
|
||||
|
||||
const chatModelSelectList = useMemo(() => {
|
||||
return chatModelList.map((item) => ({
|
||||
const variables = watch('userGuide.variables');
|
||||
const formatVariables = useMemo(() => formatVariablesIcon(variables), [variables]);
|
||||
const aiSystemPrompt = watch('aiSettings.systemPrompt');
|
||||
const searchMode = watch('dataset.searchMode');
|
||||
|
||||
const chatModelSelectList = (() =>
|
||||
chatModelList.map((item) => ({
|
||||
value: item.model,
|
||||
label: item.name
|
||||
}));
|
||||
}, [refresh]);
|
||||
})))();
|
||||
|
||||
const selectDatasets = useMemo(
|
||||
() => allDatasets.filter((item) => datasets.find((dataset) => dataset.datasetId === item._id)),
|
||||
[allDatasets, datasets]
|
||||
);
|
||||
|
||||
const selectSimpleTemplate = useMemo(
|
||||
() =>
|
||||
simpleModeTemplates?.find((item) => item.id === getValues('templateId')) ||
|
||||
SimpleModeTemplate_FastGPT_Universal,
|
||||
[getValues, refresh]
|
||||
);
|
||||
const selectSimpleTemplate = (() =>
|
||||
simpleModeTemplates?.find((item) => item.id === getValues('templateId')) ||
|
||||
SimpleModeTemplate_FastGPT_Universal)();
|
||||
|
||||
const tokenLimit = useMemo(() => {
|
||||
return (
|
||||
@@ -124,10 +127,9 @@ const EditForm = ({
|
||||
}, [getValues, refresh]);
|
||||
|
||||
const datasetSearchMode = useMemo(() => {
|
||||
const mode = getValues('dataset.searchMode');
|
||||
if (!mode) return '';
|
||||
return t(DatasetSearchModeMap[mode]?.title);
|
||||
}, [getValues, t, refresh]);
|
||||
if (!searchMode) return '';
|
||||
return t(DatasetSearchModeMap[searchMode]?.title);
|
||||
}, [searchMode, t]);
|
||||
|
||||
const { mutate: onSubmitSave, isLoading: isSaving } = useRequest({
|
||||
mutationFn: async (data: AppSimpleEditFormType) => {
|
||||
@@ -144,21 +146,21 @@ const EditForm = ({
|
||||
errorToast: t('common.Save Failed')
|
||||
});
|
||||
|
||||
const appModule2Form = useCallback(() => {
|
||||
const formVal = appModules2Form({
|
||||
templateId: appDetail.simpleTemplateId,
|
||||
modules: appDetail.modules
|
||||
});
|
||||
|
||||
reset(formVal);
|
||||
setTimeout(() => {
|
||||
setRefresh((state) => !state);
|
||||
}, 100);
|
||||
}, [appDetail.modules, appDetail.simpleTemplateId, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
appModule2Form();
|
||||
}, [appModule2Form]);
|
||||
const { isSuccess: isInitd } = useQuery(
|
||||
['init', appDetail],
|
||||
() => {
|
||||
const formatVal = appModules2Form({
|
||||
templateId: appDetail.simpleTemplateId,
|
||||
modules: appDetail.modules
|
||||
});
|
||||
reset(formatVal);
|
||||
setRefresh(!refresh);
|
||||
return formatVal;
|
||||
},
|
||||
{
|
||||
enabled: !!appDetail._id
|
||||
}
|
||||
);
|
||||
useQuery(['loadAllDatasets'], loadAllDatasets);
|
||||
|
||||
const BoxStyles: BoxProps = {
|
||||
@@ -229,15 +231,15 @@ const EditForm = ({
|
||||
{/* simple mode select */}
|
||||
<Flex {...BoxStyles}>
|
||||
<Flex alignItems={'center'} flex={'1 0 0'}>
|
||||
<Image alt={''} src={'/imgs/module/templates.png'} w={'18px'} />
|
||||
<MyIcon name={'core/app/simpleMode/template'} w={'20px'} />
|
||||
<Box mx={2}>{t('core.app.simple.mode template select')}</Box>
|
||||
</Flex>
|
||||
<MySelect
|
||||
w={['200px', '250px']}
|
||||
list={
|
||||
simpleModeTemplates?.map((item) => ({
|
||||
alias: item.name,
|
||||
label: item.desc,
|
||||
alias: t(item.name),
|
||||
label: t(item.desc),
|
||||
value: item.id
|
||||
})) || []
|
||||
}
|
||||
@@ -253,7 +255,7 @@ const EditForm = ({
|
||||
{selectSimpleTemplate?.systemForm?.aiSettings && (
|
||||
<Box {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Image alt={''} src={'/imgs/module/AI.png'} w={'18px'} />
|
||||
<MyIcon name={'core/app/simpleMode/ai'} w={'20px'} />
|
||||
<Box ml={2} flex={1}>
|
||||
{t('app.AI Settings')}
|
||||
</Box>
|
||||
@@ -263,7 +265,7 @@ const EditForm = ({
|
||||
selectSimpleTemplate.systemForm.aiSettings.quotePrompt) && (
|
||||
<Flex {...BoxBtnStyles} onClick={onOpenAIChatSetting}>
|
||||
<MyIcon mr={1} name={'common/settingLight'} w={'14px'} />
|
||||
{t('app.Open AI Advanced Settings')}
|
||||
{t('common.More settings')}
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
@@ -293,20 +295,23 @@ const EditForm = ({
|
||||
<Flex mt={10} alignItems={'flex-start'}>
|
||||
<Box {...LabelStyles}>
|
||||
{t('core.ai.Prompt')}
|
||||
<MyTooltip label={chatNodeSystemPromptTip} forceShow>
|
||||
<MyTooltip label={t(chatNodeSystemPromptTip)} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<PromptTextarea
|
||||
flex={1}
|
||||
bg={'myWhite.400'}
|
||||
rows={5}
|
||||
placeholder={chatNodeSystemPromptTip}
|
||||
defaultValue={getValues('aiSettings.systemPrompt')}
|
||||
onBlur={(e) => {
|
||||
setValue('aiSettings.systemPrompt', e.target.value || '');
|
||||
}}
|
||||
/>
|
||||
{isInitd && (
|
||||
<PromptEditor
|
||||
defaultValue={aiSystemPrompt}
|
||||
onChange={(text) => {
|
||||
startTst(() => {
|
||||
setValue('aiSettings.systemPrompt', text);
|
||||
});
|
||||
}}
|
||||
variables={formatVariables}
|
||||
placeholder={t('core.app.tip.chatNodeSystemPromptTip')}
|
||||
title={t('core.ai.Prompt')}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
@@ -317,7 +322,7 @@ const EditForm = ({
|
||||
<Box {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<Image alt={''} src={'/imgs/module/db.png'} w={'18px'} />
|
||||
<MyIcon name={'core/app/simpleMode/dataset'} w={'20px'} />
|
||||
<Box ml={2}>{t('core.dataset.Choose Dataset')}</Box>
|
||||
</Flex>
|
||||
{selectSimpleTemplate.systemForm.dataset.datasets && (
|
||||
@@ -409,7 +414,7 @@ const EditForm = ({
|
||||
{selectSimpleTemplate?.systemForm?.userGuide?.variables && (
|
||||
<Box {...BoxStyles}>
|
||||
<VariableEdit
|
||||
variables={getValues('userGuide.variables')}
|
||||
variables={variables}
|
||||
onChange={(e) => {
|
||||
setValue('userGuide.variables', e);
|
||||
setRefresh(!refresh);
|
||||
@@ -422,17 +427,17 @@ const EditForm = ({
|
||||
{selectSimpleTemplate?.systemForm?.userGuide?.welcomeText && (
|
||||
<Box {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Image alt={''} src={'/imgs/module/userGuide.png'} w={'18px'} />
|
||||
<MyIcon name={'core/app/simpleMode/chat'} w={'20px'} />
|
||||
<Box mx={2}>{t('core.app.Welcome Text')}</Box>
|
||||
<MyTooltip label={welcomeTextTip} forceShow>
|
||||
<MyTooltip label={t(welcomeTextTip)} forceShow>
|
||||
<QuestionOutlineIcon />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<PromptTextarea
|
||||
<MyTextarea
|
||||
mt={2}
|
||||
bg={'myWhite.400'}
|
||||
rows={5}
|
||||
placeholder={welcomeTextTip}
|
||||
placeholder={t(welcomeTextTip)}
|
||||
defaultValue={getValues('userGuide.welcomeText')}
|
||||
onBlur={(e) => {
|
||||
setValue('userGuide.welcomeText', e.target.value || '');
|
||||
@@ -481,6 +486,7 @@ const EditForm = ({
|
||||
}}
|
||||
defaultData={getValues('aiSettings')}
|
||||
simpleModeTemplate={selectSimpleTemplate}
|
||||
pickerMenu={formatVariables}
|
||||
/>
|
||||
)}
|
||||
{isOpenDatasetSelect && (
|
||||
|
||||
@@ -16,6 +16,7 @@ import SimpleEdit from './components/SimpleEdit';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import Head from 'next/head';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const FlowEdit = dynamic(() => import('./components/FlowEdit'), {
|
||||
loading: () => <Loading />
|
||||
@@ -32,6 +33,7 @@ enum TabEnum {
|
||||
}
|
||||
|
||||
const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
@@ -52,23 +54,39 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
|
||||
const tabList = useMemo(
|
||||
() => [
|
||||
{ label: '简易配置', id: TabEnum.simpleEdit, icon: 'common/overviewLight' },
|
||||
{
|
||||
label: t('core.app.navbar.Simple mode'),
|
||||
id: TabEnum.simpleEdit,
|
||||
icon: 'common/overviewLight'
|
||||
},
|
||||
...(feConfigs?.hide_app_flow
|
||||
? []
|
||||
: [{ label: '高级编排', id: TabEnum.adEdit, icon: 'common/settingLight' }]),
|
||||
{ label: '外部使用', id: TabEnum.outLink, icon: 'support/outlink/shareLight' },
|
||||
{ label: '对话日志', id: TabEnum.logs, icon: 'core/app/logsLight' },
|
||||
{ label: '立即对话', id: TabEnum.startChat, icon: 'core/chat/chatLight' }
|
||||
: [
|
||||
{
|
||||
label: t('core.app.navbar.Flow mode'),
|
||||
id: TabEnum.adEdit,
|
||||
icon: 'core/modules/flowLight'
|
||||
}
|
||||
]),
|
||||
{
|
||||
label: t('core.app.navbar.External'),
|
||||
id: TabEnum.outLink,
|
||||
icon: 'support/outlink/shareLight'
|
||||
},
|
||||
{ label: t('app.Chat logs'), id: TabEnum.logs, icon: 'core/app/logsLight' },
|
||||
{ label: t('core.Start chat'), id: TabEnum.startChat, icon: 'core/chat/chatLight' }
|
||||
],
|
||||
[]
|
||||
[t]
|
||||
);
|
||||
|
||||
const onCloseFlowEdit = useCallback(() => setCurrentTab(TabEnum.simpleEdit), [setCurrentTab]);
|
||||
|
||||
useEffect(() => {
|
||||
const listen =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? (e: any) => {
|
||||
e.preventDefault();
|
||||
e.returnValue = '内容已修改,确认离开页面吗?';
|
||||
e.returnValue = t('core.common.tip.leave page');
|
||||
}
|
||||
: () => {};
|
||||
window.addEventListener('beforeunload', listen);
|
||||
@@ -82,7 +100,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
useQuery([appId], () => loadAppDetail(appId, true), {
|
||||
onError(err: any) {
|
||||
toast({
|
||||
title: err?.message || '获取应用异常',
|
||||
title: err?.message || t('core.app.error.Get app failed'),
|
||||
status: 'error'
|
||||
});
|
||||
router.replace('/app/list');
|
||||
@@ -146,7 +164,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
borderRadius={'50%'}
|
||||
aria-label={''}
|
||||
/>
|
||||
我的应用
|
||||
{t('app.My Apps')}
|
||||
</Flex>
|
||||
</Box>
|
||||
{/* phone tab */}
|
||||
@@ -173,7 +191,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
<Box flex={'1 0 0'} h={[0, '100%']} overflow={['overlay', '']}>
|
||||
{currentTab === TabEnum.simpleEdit && <SimpleEdit appId={appId} />}
|
||||
{currentTab === TabEnum.adEdit && appDetail && (
|
||||
<FlowEdit app={appDetail} onClose={() => setCurrentTab(TabEnum.simpleEdit)} />
|
||||
<FlowEdit app={appDetail} onClose={onCloseFlowEdit} />
|
||||
)}
|
||||
{currentTab === TabEnum.logs && <Logs appId={appId} />}
|
||||
{currentTab === TabEnum.outLink && <OutLink appId={appId} />}
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
Box,
|
||||
Flex,
|
||||
Button,
|
||||
ModalHeader,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
Input,
|
||||
@@ -69,19 +68,19 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
|
||||
setRefresh((state) => !state);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: getErrText(err, '头像选择异常'),
|
||||
title: getErrText(err, t('common.error.Select avatar failed')),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
},
|
||||
[setValue, toast]
|
||||
[setValue, t, toast]
|
||||
);
|
||||
|
||||
const { mutate: onclickCreate, isLoading: creating } = useRequest({
|
||||
mutationFn: async (data: FormType) => {
|
||||
const template = appTemplates.find((item) => item.id === data.templateId);
|
||||
if (!template) {
|
||||
return Promise.reject('模板不存在');
|
||||
return Promise.reject(t('core.dataset.error.Template does not exist'));
|
||||
}
|
||||
return postCreateApp({
|
||||
avatar: data.avatar,
|
||||
@@ -95,8 +94,8 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
|
||||
onSuccess();
|
||||
onClose();
|
||||
},
|
||||
successToast: '创建成功',
|
||||
errorToast: '创建应用异常'
|
||||
successToast: t('common.Create Success'),
|
||||
errorToast: t('common.Create Failed')
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -109,10 +108,10 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
|
||||
>
|
||||
<ModalBody>
|
||||
<Box color={'myGray.800'} fontWeight={'bold'}>
|
||||
取个响亮的名字
|
||||
{t('common.Set Name')}
|
||||
</Box>
|
||||
<Flex mt={3} alignItems={'center'}>
|
||||
<MyTooltip label={'点击设置头像'}>
|
||||
<MyTooltip label={t('common.Set Avatar')}>
|
||||
<Avatar
|
||||
flexShrink={0}
|
||||
src={getValues('avatar')}
|
||||
@@ -129,14 +128,14 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
|
||||
autoFocus
|
||||
bg={'myWhite.600'}
|
||||
{...register('name', {
|
||||
required: '应用名不能为空~'
|
||||
required: t('core.app.error.App name can not be empty')
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
{!feConfigs?.hide_app_flow && (
|
||||
<>
|
||||
<Box mt={[4, 7]} mb={[0, 3]} color={'myGray.800'} fontWeight={'bold'}>
|
||||
从模板中选择
|
||||
{t('core.app.Select app from template')}
|
||||
</Box>
|
||||
<Grid
|
||||
userSelect={'none'}
|
||||
@@ -168,11 +167,11 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={item.avatar} borderRadius={'md'} w={'20px'} />
|
||||
<Box ml={3} fontWeight={'bold'}>
|
||||
{item.name}
|
||||
{t(item.name)}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box fontSize={'sm'} mt={4}>
|
||||
{item.intro}
|
||||
{t(item.intro)}
|
||||
</Box>
|
||||
</Card>
|
||||
))}
|
||||
@@ -183,10 +182,10 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
取消
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button isLoading={creating} onClick={handleSubmit((data) => onclickCreate(data))}>
|
||||
确认创建
|
||||
{t('common.Confirm Create')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
|
||||
@@ -348,7 +348,6 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
userGuideModule={chatData.app?.userGuideModule}
|
||||
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
|
||||
feedbackType={'user'}
|
||||
onUpdateVariable={(e) => {}}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={(e) => delOneHistoryItem({ ...e, appId, chatId })}
|
||||
appId={appId}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user