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:
Archer
2024-01-19 11:17:28 +08:00
committed by GitHub
parent 8ee7407c4c
commit c031e6dcc9
324 changed files with 8509 additions and 4757 deletions

View File

@@ -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')}

View File

@@ -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>

View File

@@ -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

View File

@@ -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} />

View File

@@ -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>
)}

View File

@@ -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>

View File

@@ -17,6 +17,7 @@ const Loading = ({
position={fixed ? 'fixed' : 'absolute'}
zIndex={zIndex}
bg={bg}
borderRadius={'md'}
top={0}
left={0}
right={0}

View File

@@ -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}

View File

@@ -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>
);
};

View File

@@ -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}

View File

@@ -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>
)}

View File

@@ -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>
);
});

View File

@@ -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>
);
});

View File

@@ -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();

View File

@@ -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'}

View File

@@ -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'}

View File

@@ -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);
}}

View File

@@ -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 && (

View File

@@ -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}>

View File

@@ -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';

View File

@@ -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} />

View File

@@ -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}
</>
);
};

View File

@@ -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} />

View File

@@ -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')}
</>
}

View File

@@ -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;
};

View File

@@ -126,7 +126,7 @@ const NodeCQNode = React.memo(function NodeCQNode({ data }: { data: FlowModuleIt
});
}}
>
{t('core.module.Add question type')}
</Button>
</Box>
);

View File

@@ -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
}
});
});
}}
/>

View File

@@ -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);

View File

@@ -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);

View File

@@ -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]
}
});
}
}}
/>
)} */}
</>
);
};

View File

@@ -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);

View File

@@ -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}
/>
);
};

View File

@@ -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>

View File

@@ -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>

View File

@@ -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: '语音播放',