Perf input guide (#1557)
* perf: input guide code * perf: input guide ui * Chat input guide api * Update app chat config store * perf: app chat config field * perf: app context * perf: params * fix: ts * perf: filter private config * perf: filter private config * perf: import workflow * perf: limit max tip amount
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useSpeech } from '@/web/common/hooks/useSpeech';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { Box, Flex, Image, Spinner, Textarea } from '@chakra-ui/react';
|
||||
import React, { useRef, useEffect, useCallback, useTransition } from 'react';
|
||||
import React, { useRef, useEffect, useCallback } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import MyTooltip from '../../MyTooltip';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
@@ -12,18 +12,16 @@ import { ChatFileTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { addDays } from 'date-fns';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import { ChatBoxInputFormType, ChatBoxInputType, UserInputFileItemType } from './type';
|
||||
import { textareaMinH } from './constants';
|
||||
import { ChatBoxInputFormType, ChatBoxInputType, UserInputFileItemType } from '../type';
|
||||
import { textareaMinH } from '../constants';
|
||||
import { UseFormReturn, useFieldArray } from 'react-hook-form';
|
||||
import { useChatProviderStore } from './Provider';
|
||||
import QuestionGuide from './components/QustionGuide';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getMyQuestionGuides } from '@/web/core/app/api';
|
||||
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { useChatProviderStore } from '../Provider';
|
||||
import dynamic from 'next/dynamic';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
|
||||
const MessageInput = ({
|
||||
const InputGuideBox = dynamic(() => import('./InputGuideBox'));
|
||||
|
||||
const ChatInput = ({
|
||||
onSendMessage,
|
||||
onStop,
|
||||
TextareaDom,
|
||||
@@ -38,7 +36,7 @@ const MessageInput = ({
|
||||
TextareaDom: React.MutableRefObject<HTMLTextAreaElement | null>;
|
||||
resetInputVal: (val: ChatBoxInputType) => void;
|
||||
chatForm: UseFormReturn<ChatBoxInputFormType>;
|
||||
appId?: string;
|
||||
appId: string;
|
||||
}) => {
|
||||
const { setValue, watch, control } = chatForm;
|
||||
const inputValue = watch('input');
|
||||
@@ -53,12 +51,19 @@ const MessageInput = ({
|
||||
name: 'files'
|
||||
});
|
||||
|
||||
const { shareId, outLinkUid, teamId, teamToken, isChatting, whisperConfig, autoTTSResponse } =
|
||||
useChatProviderStore();
|
||||
const {
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
isChatting,
|
||||
whisperConfig,
|
||||
autoTTSResponse,
|
||||
chatInputGuide
|
||||
} = useChatProviderStore();
|
||||
const { isPc, whisperModel } = useSystemStore();
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const { t } = useTranslation();
|
||||
const { appDetail } = useAppStore();
|
||||
|
||||
const havInput = !!inputValue || fileList.length > 0;
|
||||
const hasFileUploading = fileList.some((item) => !item.url);
|
||||
@@ -150,9 +155,9 @@ const MessageInput = ({
|
||||
);
|
||||
|
||||
/* on send */
|
||||
const handleSend = async () => {
|
||||
const handleSend = async (val?: string) => {
|
||||
if (!canSendMessage) return;
|
||||
const textareaValue = TextareaDom.current?.value || '';
|
||||
const textareaValue = val || TextareaDom.current?.value || '';
|
||||
|
||||
onSendMessage({
|
||||
text: textareaValue.trim(),
|
||||
@@ -211,23 +216,6 @@ const MessageInput = ({
|
||||
startSpeak(finishWhisperTranscription);
|
||||
}, [finishWhisperTranscription, isSpeaking, startSpeak, stopSpeak]);
|
||||
|
||||
const { data } = useQuery(
|
||||
[appId, inputValue],
|
||||
async () => {
|
||||
if (!appId) return { list: [], total: 0 };
|
||||
return getMyQuestionGuides({
|
||||
appId,
|
||||
customURL: getAppQGuideCustomURL(appDetail),
|
||||
pageSize: 5,
|
||||
current: 1,
|
||||
searchKey: inputValue
|
||||
});
|
||||
},
|
||||
{
|
||||
enabled: !!appId
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<Box m={['0 auto', '10px auto']} w={'100%'} maxW={['auto', 'min(800px, 100%)']} px={[0, 5]}>
|
||||
<Box
|
||||
@@ -248,6 +236,20 @@ const MessageInput = ({
|
||||
borderTopColor: 'rgba(0,0,0,0.15)'
|
||||
})}
|
||||
>
|
||||
{/* Chat input guide box */}
|
||||
{chatInputGuide.open && (
|
||||
<InputGuideBox
|
||||
appId={appId}
|
||||
text={inputValue}
|
||||
onSelect={(e) => {
|
||||
setValue('input', e);
|
||||
}}
|
||||
onSend={(e) => {
|
||||
handleSend(e);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* translate loading */}
|
||||
<Flex
|
||||
position={'absolute'}
|
||||
@@ -266,21 +268,6 @@ const MessageInput = ({
|
||||
{t('core.chat.Converting to text')}
|
||||
</Flex>
|
||||
|
||||
{/* popup */}
|
||||
{havInput && (
|
||||
<QuestionGuide
|
||||
guides={data?.list || []}
|
||||
setDropdownValue={(value) => setValue('input', value)}
|
||||
bottom={'100%'}
|
||||
top={'auto'}
|
||||
left={0}
|
||||
right={0}
|
||||
mb={2}
|
||||
overflowY={'auto'}
|
||||
boxShadow={'sm'}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* file preview */}
|
||||
<Flex wrap={'wrap'} px={[2, 4]} userSelect={'none'}>
|
||||
{fileList.map((item, index) => (
|
||||
@@ -415,12 +402,7 @@ const MessageInput = ({
|
||||
// @ts-ignore
|
||||
e.key === 'a' && e.ctrlKey && e.target?.select();
|
||||
|
||||
if (
|
||||
(isPc || window !== parent) &&
|
||||
e.keyCode === 13 &&
|
||||
!e.shiftKey &&
|
||||
!(havInput && data?.list.length && data?.list.length > 0)
|
||||
) {
|
||||
if ((isPc || window !== parent) && e.keyCode === 13 && !e.shiftKey) {
|
||||
handleSend();
|
||||
e.preventDefault();
|
||||
}
|
||||
@@ -556,4 +538,4 @@ const MessageInput = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(MessageInput);
|
||||
export default React.memo(ChatInput);
|
||||
111
projects/app/src/components/ChatBox/Input/InputGuideBox.tsx
Normal file
111
projects/app/src/components/ChatBox/Input/InputGuideBox.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { queryChatInputGuideList } from '@/web/core/chat/inputGuide/api';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import HighlightText from '@fastgpt/web/components/common/String/HighlightText';
|
||||
import { useChatProviderStore } from '../Provider';
|
||||
|
||||
export default function InputGuideBox({
|
||||
appId,
|
||||
text,
|
||||
onSelect,
|
||||
onSend
|
||||
}: {
|
||||
appId: string;
|
||||
text: string;
|
||||
onSelect: (text: string) => void;
|
||||
onSend: (text: string) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { chatT } = useI18n();
|
||||
const { chatInputGuide } = useChatProviderStore();
|
||||
|
||||
const { data = [] } = useRequest2(
|
||||
async () => {
|
||||
if (!text) return [];
|
||||
return await queryChatInputGuideList(
|
||||
{
|
||||
appId,
|
||||
searchKey: text
|
||||
},
|
||||
chatInputGuide.customUrl ? chatInputGuide.customUrl : undefined
|
||||
);
|
||||
},
|
||||
{
|
||||
refreshDeps: [text],
|
||||
throttleWait: 300
|
||||
}
|
||||
);
|
||||
|
||||
const filterData = data.filter((item) => item !== text).slice(0, 5);
|
||||
|
||||
return filterData.length ? (
|
||||
<Box
|
||||
bg={'white'}
|
||||
boxShadow={'lg'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={'borderColor.base'}
|
||||
p={2}
|
||||
borderRadius={'md'}
|
||||
position={'absolute'}
|
||||
top={-3}
|
||||
w={'100%'}
|
||||
zIndex={150}
|
||||
transform={'translateY(-100%)'}
|
||||
>
|
||||
<Flex alignItems={'center'} fontSize={'sm'} color={'myGray.600'} gap={2} mb={2} px={2}>
|
||||
<MyIcon name={'union'} />
|
||||
<Box>{chatT('Input guide')}</Box>
|
||||
</Flex>
|
||||
{data.map((item, index) => (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
as={'li'}
|
||||
key={item}
|
||||
px={4}
|
||||
py={3}
|
||||
borderRadius={'sm'}
|
||||
cursor={'pointer'}
|
||||
overflow={'auto'}
|
||||
_notLast={{
|
||||
mb: 1
|
||||
}}
|
||||
bg={'myGray.50'}
|
||||
color={'myGray.600'}
|
||||
_hover={{
|
||||
bg: 'primary.50',
|
||||
color: 'primary.600',
|
||||
'.send-icon': {
|
||||
display: 'block'
|
||||
}
|
||||
}}
|
||||
onClick={() => onSelect(item)}
|
||||
>
|
||||
<Box fontSize={'sm'} flex={'1 0 0'}>
|
||||
<HighlightText rawText={item} matchText={text} />
|
||||
</Box>
|
||||
<MyTooltip label={t('core.chat.markdown.Send Question')}>
|
||||
<MyIcon
|
||||
className="send-icon"
|
||||
display={'none'}
|
||||
name={'chatSend'}
|
||||
boxSize={4}
|
||||
color={'myGray.500'}
|
||||
_hover={{
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onSend(item);
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
) : null;
|
||||
}
|
||||
@@ -2,17 +2,23 @@ import React, { useContext, createContext, useState, useMemo, useEffect, useCall
|
||||
import { useAudioPlay } from '@/web/common/utils/voice';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import {
|
||||
AppChatConfigType,
|
||||
AppTTSConfigType,
|
||||
AppWhisperConfigType,
|
||||
ChatInputGuideConfigType,
|
||||
VariableItemType
|
||||
} from '@fastgpt/global/core/app/type';
|
||||
import { ChatSiteItemType } from '@fastgpt/global/core/chat/type';
|
||||
import {
|
||||
defaultChatInputGuideConfig,
|
||||
defaultTTSConfig,
|
||||
defaultWhisperConfig
|
||||
} from '@fastgpt/global/core/app/constants';
|
||||
|
||||
type useChatStoreType = OutLinkChatAuthProps & {
|
||||
welcomeText: string;
|
||||
variableNodes: VariableItemType[];
|
||||
variableList: VariableItemType[];
|
||||
questionGuide: boolean;
|
||||
ttsConfig: AppTTSConfigType;
|
||||
whisperConfig: AppWhisperConfigType;
|
||||
@@ -38,10 +44,11 @@ type useChatStoreType = OutLinkChatAuthProps & {
|
||||
chatHistories: ChatSiteItemType[];
|
||||
setChatHistories: React.Dispatch<React.SetStateAction<ChatSiteItemType[]>>;
|
||||
isChatting: boolean;
|
||||
chatInputGuide: ChatInputGuideConfigType;
|
||||
};
|
||||
const StateContext = createContext<useChatStoreType>({
|
||||
welcomeText: '',
|
||||
variableNodes: [],
|
||||
variableList: [],
|
||||
questionGuide: false,
|
||||
ttsConfig: {
|
||||
type: 'none',
|
||||
@@ -87,11 +94,15 @@ const StateContext = createContext<useChatStoreType>({
|
||||
},
|
||||
finishSegmentedAudio: function (): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
chatInputGuide: {
|
||||
open: false,
|
||||
customUrl: ''
|
||||
}
|
||||
});
|
||||
|
||||
export type ChatProviderProps = OutLinkChatAuthProps & {
|
||||
userGuideModule?: StoreNodeItemType;
|
||||
chatConfig?: AppChatConfigType;
|
||||
|
||||
// not chat test params
|
||||
chatId?: string;
|
||||
@@ -105,15 +116,19 @@ const Provider = ({
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
userGuideModule,
|
||||
chatConfig = {},
|
||||
children
|
||||
}: ChatProviderProps) => {
|
||||
const [chatHistories, setChatHistories] = useState<ChatSiteItemType[]>([]);
|
||||
|
||||
const { welcomeText, variableNodes, questionGuide, ttsConfig, whisperConfig } = useMemo(
|
||||
() => splitGuideModule(userGuideModule),
|
||||
[userGuideModule]
|
||||
);
|
||||
const {
|
||||
welcomeText = '',
|
||||
variables = [],
|
||||
questionGuide = false,
|
||||
ttsConfig = defaultTTSConfig,
|
||||
whisperConfig = defaultWhisperConfig,
|
||||
chatInputGuide = defaultChatInputGuideConfig
|
||||
} = useMemo(() => chatConfig, [chatConfig]);
|
||||
|
||||
// segment audio
|
||||
const [audioPlayingChatId, setAudioPlayingChatId] = useState<string>();
|
||||
@@ -150,7 +165,7 @@ const Provider = ({
|
||||
teamId,
|
||||
teamToken,
|
||||
welcomeText,
|
||||
variableNodes,
|
||||
variableList: variables,
|
||||
questionGuide,
|
||||
ttsConfig,
|
||||
whisperConfig,
|
||||
@@ -167,7 +182,8 @@ const Provider = ({
|
||||
setAudioPlayingChatId,
|
||||
chatHistories,
|
||||
setChatHistories,
|
||||
isChatting
|
||||
isChatting,
|
||||
chatInputGuide
|
||||
};
|
||||
|
||||
return <StateContext.Provider value={value}>{children}</StateContext.Provider>;
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import { Box, BoxProps, Flex } from '@chakra-ui/react';
|
||||
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
|
||||
export default function QuestionGuide({
|
||||
guides,
|
||||
setDropdownValue,
|
||||
...props
|
||||
}: {
|
||||
guides: string[];
|
||||
setDropdownValue?: (value: string) => void;
|
||||
} & BoxProps) {
|
||||
const [highlightedIndex, setHighlightedIndex] = React.useState(0);
|
||||
const { appT } = useI18n();
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(event: any) => {
|
||||
if (event.keyCode === 38) {
|
||||
setHighlightedIndex((prevIndex) => Math.max(prevIndex - 1, 0));
|
||||
} else if (event.keyCode === 40) {
|
||||
setHighlightedIndex((prevIndex) => Math.min(prevIndex + 1, guides.length - 1));
|
||||
} else if (event.keyCode === 13 && guides[highlightedIndex]) {
|
||||
setDropdownValue?.(guides[highlightedIndex]);
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
[highlightedIndex, setDropdownValue, guides]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [handleKeyDown]);
|
||||
|
||||
return guides.length ? (
|
||||
<Box
|
||||
bg={'white'}
|
||||
boxShadow={'lg'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={'borderColor.base'}
|
||||
p={2}
|
||||
borderRadius={'md'}
|
||||
position={'absolute'}
|
||||
top={'100%'}
|
||||
w={'auto'}
|
||||
zIndex={99999}
|
||||
maxH={'300px'}
|
||||
overflow={'auto'}
|
||||
className="nowheel"
|
||||
{...props}
|
||||
>
|
||||
<Flex alignItems={'center'} fontSize={'sm'} color={'myGray.600'} gap={2} mb={2} px={2}>
|
||||
<MyIcon name={'union'} />
|
||||
<Box>{appT('modules.Input Guide')}</Box>
|
||||
</Flex>
|
||||
{guides.map((item, index) => (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
as={'li'}
|
||||
key={item}
|
||||
px={4}
|
||||
py={3}
|
||||
borderRadius={'sm'}
|
||||
cursor={'pointer'}
|
||||
maxH={'300px'}
|
||||
overflow={'auto'}
|
||||
_notLast={{
|
||||
mb: 1
|
||||
}}
|
||||
{...(highlightedIndex === index
|
||||
? {
|
||||
bg: 'primary.50',
|
||||
color: 'primary.600'
|
||||
}
|
||||
: {
|
||||
bg: 'myGray.50',
|
||||
color: 'myGray.600'
|
||||
})}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
setDropdownValue?.(item);
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setHighlightedIndex(index);
|
||||
}}
|
||||
>
|
||||
<Box fontSize={'sm'}>{item}</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
) : null;
|
||||
}
|
||||
@@ -12,12 +12,12 @@ import { ChatBoxInputFormType } from '../type.d';
|
||||
|
||||
const VariableInput = ({
|
||||
appAvatar,
|
||||
variableNodes,
|
||||
variableList,
|
||||
chatForm,
|
||||
onSubmitVariables
|
||||
}: {
|
||||
appAvatar?: string;
|
||||
variableNodes: VariableItemType[];
|
||||
variableList: VariableItemType[];
|
||||
onSubmitVariables: (e: Record<string, any>) => void;
|
||||
chatForm: UseFormReturn<ChatBoxInputFormType>;
|
||||
}) => {
|
||||
@@ -40,7 +40,7 @@ const VariableInput = ({
|
||||
bg={'white'}
|
||||
boxShadow={'0 0 8px rgba(0,0,0,0.15)'}
|
||||
>
|
||||
{variableNodes.map((item) => (
|
||||
{variableList.map((item) => (
|
||||
<Box key={item.id} mb={4}>
|
||||
<Box as={'label'} display={'inline-block'} position={'relative'} mb={1}>
|
||||
{item.label}
|
||||
|
||||
@@ -45,7 +45,7 @@ import type {
|
||||
ChatBoxInputType,
|
||||
ChatBoxInputFormType
|
||||
} from './type.d';
|
||||
import MessageInput from './MessageInput';
|
||||
import ChatInput from './Input/ChatInput';
|
||||
import ChatBoxDivider from '../core/chat/Divider';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
@@ -59,6 +59,7 @@ import ChatItem from './components/ChatItem';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useCreation } from 'ahooks';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
const ResponseTags = dynamic(() => import('./ResponseTags'));
|
||||
const FeedbackModal = dynamic(() => import('./FeedbackModal'));
|
||||
@@ -81,7 +82,7 @@ type Props = OutLinkChatAuthProps & {
|
||||
showEmptyIntro?: boolean;
|
||||
appAvatar?: string;
|
||||
userAvatar?: string;
|
||||
userGuideModule?: StoreNodeItemType;
|
||||
chatConfig?: AppChatConfigType;
|
||||
showFileSelector?: boolean;
|
||||
active?: boolean; // can use
|
||||
appId: string;
|
||||
@@ -149,7 +150,7 @@ const ChatBox = (
|
||||
|
||||
const {
|
||||
welcomeText,
|
||||
variableNodes,
|
||||
variableList,
|
||||
questionGuide,
|
||||
startSegmentedAudio,
|
||||
finishSegmentedAudio,
|
||||
@@ -174,8 +175,8 @@ const ChatBox = (
|
||||
|
||||
/* variable */
|
||||
const filterVariableNodes = useCreation(
|
||||
() => variableNodes.filter((item) => item.type !== VariableInputEnum.custom),
|
||||
[variableNodes]
|
||||
() => variableList.filter((item) => item.type !== VariableInputEnum.custom),
|
||||
[variableList]
|
||||
);
|
||||
|
||||
// 滚动到底部
|
||||
@@ -390,9 +391,9 @@ const ChatBox = (
|
||||
return;
|
||||
}
|
||||
|
||||
// delete invalid variables, 只保留在 variableNodes 中的变量
|
||||
// delete invalid variables, 只保留在 variableList 中的变量
|
||||
const requestVariables: Record<string, any> = {};
|
||||
variableNodes?.forEach((item) => {
|
||||
variableList?.forEach((item) => {
|
||||
requestVariables[item.key] = variables[item.key] || '';
|
||||
});
|
||||
|
||||
@@ -566,7 +567,7 @@ const ChatBox = (
|
||||
startSegmentedAudio,
|
||||
t,
|
||||
toast,
|
||||
variableNodes
|
||||
variableList
|
||||
]
|
||||
);
|
||||
|
||||
@@ -907,7 +908,7 @@ const ChatBox = (
|
||||
{!!filterVariableNodes?.length && (
|
||||
<VariableInput
|
||||
appAvatar={appAvatar}
|
||||
variableNodes={filterVariableNodes}
|
||||
variableList={filterVariableNodes}
|
||||
chatForm={chatForm}
|
||||
onSubmitVariables={(data) => {
|
||||
setValue('chatStarted', true);
|
||||
@@ -1000,7 +1001,7 @@ const ChatBox = (
|
||||
</Box>
|
||||
{/* message input */}
|
||||
{onStartChat && (chatStarted || filterVariableNodes.length === 0) && active && (
|
||||
<MessageInput
|
||||
<ChatInput
|
||||
onSendMessage={sendPrompt}
|
||||
onStop={() => chatController.current?.abort('stop')}
|
||||
TextareaDom={TextareaDom}
|
||||
|
||||
@@ -13,10 +13,19 @@ import Auth from './auth';
|
||||
|
||||
const Navbar = dynamic(() => import('./navbar'));
|
||||
const NavbarPhone = dynamic(() => import('./navbarPhone'));
|
||||
const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal'));
|
||||
const NotSufficientModal = dynamic(() => import('@/components/support/wallet/NotSufficientModal'));
|
||||
const SystemMsgModal = dynamic(() => import('@/components/support/user/inform/SystemMsgModal'));
|
||||
const ImportantInform = dynamic(() => import('@/components/support/user/inform/ImportantInform'));
|
||||
const UpdateInviteModal = dynamic(
|
||||
() => import('@/components/support/user/team/UpdateInviteModal'),
|
||||
{ ssr: false }
|
||||
);
|
||||
const NotSufficientModal = dynamic(() => import('@/components/support/wallet/NotSufficientModal'), {
|
||||
ssr: false
|
||||
});
|
||||
const SystemMsgModal = dynamic(() => import('@/components/support/user/inform/SystemMsgModal'), {
|
||||
ssr: false
|
||||
});
|
||||
const ImportantInform = dynamic(() => import('@/components/support/user/inform/ImportantInform'), {
|
||||
ssr: false
|
||||
});
|
||||
|
||||
const pcUnShowLayoutRoute: Record<string, boolean> = {
|
||||
'/': true,
|
||||
@@ -114,12 +123,17 @@ const Layout = ({ children }: { children: JSX.Element }) => {
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
{!!userInfo && <UpdateInviteModal />}
|
||||
{isNotSufficientModal && !isHideNavbar && <NotSufficientModal />}
|
||||
{!!userInfo && <SystemMsgModal />}
|
||||
{!!userInfo && importantInforms.length > 0 && (
|
||||
<ImportantInform informs={importantInforms} refetch={refetchUnRead} />
|
||||
{feConfigs?.isPlus && (
|
||||
<>
|
||||
{!!userInfo && <UpdateInviteModal />}
|
||||
{isNotSufficientModal && !isHideNavbar && <NotSufficientModal />}
|
||||
{!!userInfo && <SystemMsgModal />}
|
||||
{!!userInfo && importantInforms.length > 0 && (
|
||||
<ImportantInform informs={importantInforms} refetch={refetchUnRead} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Loading loading={loading} zIndex={999999} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -3,24 +3,28 @@ import { Flex, Input, InputProps } from '@chakra-ui/react';
|
||||
|
||||
interface Props extends InputProps {
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
}
|
||||
|
||||
const MyInput = ({ leftIcon, ...props }: Props) => {
|
||||
const MyInput = ({ leftIcon, rightIcon, ...props }: Props) => {
|
||||
return (
|
||||
<Flex position={'relative'} alignItems={'center'}>
|
||||
<Input w={'100%'} pl={leftIcon ? '30px !important' : 3} {...props} />
|
||||
<Flex h={'100%'} position={'relative'} alignItems={'center'}>
|
||||
<Input
|
||||
w={'100%'}
|
||||
pl={leftIcon ? '34px !important' : 3}
|
||||
pr={rightIcon ? '34px !important' : 3}
|
||||
{...props}
|
||||
/>
|
||||
{leftIcon && (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
position={'absolute'}
|
||||
left={3}
|
||||
w={'20px'}
|
||||
zIndex={10}
|
||||
transform={'translateY(1.5px)'}
|
||||
>
|
||||
<Flex alignItems={'center'} position={'absolute'} left={3} w={'20px'} zIndex={10}>
|
||||
{leftIcon}
|
||||
</Flex>
|
||||
)}
|
||||
{rightIcon && (
|
||||
<Flex alignItems={'center'} position={'absolute'} right={3} w={'20px'} zIndex={10}>
|
||||
{rightIcon}
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,479 +0,0 @@
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
ModalBody,
|
||||
useDisclosure,
|
||||
Switch,
|
||||
Input,
|
||||
Textarea,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
Checkbox,
|
||||
useCheckboxGroup,
|
||||
ModalFooter,
|
||||
BoxProps
|
||||
} from '@chakra-ui/react';
|
||||
import React, { ChangeEvent, useEffect, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { AppQuestionGuideTextConfigType } from '@fastgpt/global/core/app/type.d';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import MyInput from '@/components/MyInput';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { fileDownload } from '@/web/common/file/utils';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import { getMyQuestionGuides } from '@/web/core/app/api';
|
||||
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
const csvTemplate = `"第一列内容"
|
||||
"必填列"
|
||||
"只会将第一列内容导入,其余列会被忽略"
|
||||
"AIGC发展分为几个阶段?"
|
||||
`;
|
||||
|
||||
const QGuidesConfig = ({
|
||||
value,
|
||||
onChange
|
||||
}: {
|
||||
value: AppQuestionGuideTextConfigType;
|
||||
onChange: (e: AppQuestionGuideTextConfigType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { appT, commonT } = useI18n();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { isOpen: isOpenTexts, onOpen: onOpenTexts, onClose: onCloseTexts } = useDisclosure();
|
||||
const isOpenQuestionGuide = value.open;
|
||||
const { appDetail } = useAppStore();
|
||||
const [searchKey, setSearchKey] = React.useState<string>('');
|
||||
|
||||
const { data } = useQuery(
|
||||
[appDetail._id, searchKey],
|
||||
async () => {
|
||||
return getMyQuestionGuides({
|
||||
appId: appDetail._id,
|
||||
customURL: getAppQGuideCustomURL(appDetail),
|
||||
pageSize: 30,
|
||||
current: 1,
|
||||
searchKey
|
||||
});
|
||||
},
|
||||
{
|
||||
enabled: !!appDetail._id
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onChange({
|
||||
...value,
|
||||
textList: data?.list || []
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
const formLabel = useMemo(() => {
|
||||
if (!isOpenQuestionGuide) {
|
||||
return t('core.app.whisper.Close');
|
||||
}
|
||||
return t('core.app.whisper.Open');
|
||||
}, [t, isOpenQuestionGuide]);
|
||||
|
||||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/inputGuides'} mr={2} w={'20px'} />
|
||||
<Box fontWeight={'medium'}>{appT('modules.Question Guide')}</Box>
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={appT('modules.Config question guide')}>
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
mr={'-5px'}
|
||||
onClick={onOpen}
|
||||
>
|
||||
{formLabel}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
<MyModal
|
||||
title={appT('modules.Question Guide')}
|
||||
iconSrc="core/app/inputGuides"
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalBody px={[5, 16]} pt={[4, 8]} w={'500px'}>
|
||||
<Flex justifyContent={'space-between'} alignItems={'center'}>
|
||||
{appT('modules.Question Guide Switch')}
|
||||
<Switch
|
||||
isChecked={isOpenQuestionGuide}
|
||||
size={'lg'}
|
||||
onChange={(e) => {
|
||||
onChange({
|
||||
...value,
|
||||
open: e.target.checked
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
{isOpenQuestionGuide && (
|
||||
<>
|
||||
<Flex mt={8} alignItems={'center'}>
|
||||
{appT('modules.Question Guide Texts')}
|
||||
<Box fontSize={'xs'} px={2} bg={'myGray.100'} ml={1} rounded={'full'}>
|
||||
{value.textList.length || 0}
|
||||
</Box>
|
||||
<Box flex={'1 0 0'} />
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon boxSize={'4'} name={'common/settingLight'} />}
|
||||
onClick={() => {
|
||||
onOpenTexts();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{appT('modules.Config Texts')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<>
|
||||
<Flex mt={8} alignItems={'center'}>
|
||||
{appT('modules.Custom question guide URL')}
|
||||
<Flex
|
||||
onClick={() => window.open(getDocPath('/docs/course/custom_link'))}
|
||||
color={'primary.700'}
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
>
|
||||
<MyIcon name={'book'} ml={4} mr={1} />
|
||||
{commonT('common.Documents')}
|
||||
</Flex>
|
||||
<Box flex={'1 0 0'} />
|
||||
</Flex>
|
||||
<Textarea
|
||||
mt={2}
|
||||
bg={'myGray.50'}
|
||||
defaultValue={value.customURL}
|
||||
onBlur={(e) =>
|
||||
onChange({
|
||||
...value,
|
||||
customURL: e.target.value
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
</>
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter px={[5, 16]} pb={[4, 8]}>
|
||||
<Button onClick={() => onClose()}>{commonT('common.Confirm')}</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
||||
{isOpenTexts && (
|
||||
<TextConfigModal
|
||||
onCloseTexts={onCloseTexts}
|
||||
onOpen={onOpen}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
setSearchKey={setSearchKey}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(QGuidesConfig);
|
||||
|
||||
const TextConfigModal = ({
|
||||
onCloseTexts,
|
||||
onOpen,
|
||||
value,
|
||||
onChange,
|
||||
setSearchKey
|
||||
}: {
|
||||
onCloseTexts: () => void;
|
||||
onOpen: () => void;
|
||||
value: AppQuestionGuideTextConfigType;
|
||||
onChange: (e: AppQuestionGuideTextConfigType) => void;
|
||||
setSearchKey: (key: string) => void;
|
||||
}) => {
|
||||
const { appT, commonT } = useI18n();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [checkboxValue, setCheckboxValue] = React.useState<string[]>([]);
|
||||
const [isEditIndex, setIsEditIndex] = React.useState(-1);
|
||||
const [isAdding, setIsAdding] = React.useState(false);
|
||||
const [showIcons, setShowIcons] = React.useState<number | null>(null);
|
||||
|
||||
const { getCheckboxProps } = useCheckboxGroup();
|
||||
|
||||
const handleFileSelected = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e: ProgressEvent<FileReader>) => {
|
||||
const content = e.target?.result as string;
|
||||
const rows = content.split('\n');
|
||||
const texts = rows.map((row) => row.split(',')[0]);
|
||||
const newText = texts.filter((row) => value.textList.indexOf(row) === -1 && !!row);
|
||||
onChange({
|
||||
...value,
|
||||
textList: [...newText, ...value.textList]
|
||||
});
|
||||
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
};
|
||||
|
||||
const allSelected = useMemo(() => {
|
||||
return value.textList.length === checkboxValue.length && value.textList.length !== 0;
|
||||
}, [value.textList, checkboxValue]);
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
title={appT('modules.Config Texts')}
|
||||
iconSrc="core/app/inputGuides"
|
||||
isOpen={true}
|
||||
onClose={() => {
|
||||
setCheckboxValue([]);
|
||||
onCloseTexts();
|
||||
onOpen();
|
||||
}}
|
||||
>
|
||||
<ModalBody w={'500px'} px={0}>
|
||||
<Flex gap={4} px={8} alignItems={'center'} borderBottom={'1px solid #E8EBF0'} pb={4}>
|
||||
<Box flex={1}>
|
||||
<MyInput
|
||||
leftIcon={<MyIcon name={'common/searchLight'} boxSize={4} />}
|
||||
bg={'myGray.50'}
|
||||
w={'full'}
|
||||
h={9}
|
||||
placeholder={commonT('common.Search')}
|
||||
onChange={(e) => setSearchKey(e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
<Input
|
||||
type="file"
|
||||
accept=".csv"
|
||||
style={{ display: 'none' }}
|
||||
ref={fileInputRef}
|
||||
onChange={handleFileSelected}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => {
|
||||
fileInputRef.current?.click();
|
||||
}}
|
||||
variant={'whiteBase'}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon name={'common/importLight'} boxSize={4} />}
|
||||
>
|
||||
{commonT('common.Import')}
|
||||
</Button>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
fileDownload({
|
||||
text: csvTemplate,
|
||||
type: 'text/csv;charset=utf-8',
|
||||
filename: 'questionGuide_template.csv'
|
||||
});
|
||||
}}
|
||||
>
|
||||
<QuestionTip ml={-2} label={appT('modules.Only support CSV')} />
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box mt={4}>
|
||||
<Flex justifyContent={'space-between'} px={8}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Checkbox
|
||||
sx={{
|
||||
'.chakra-checkbox__control': {
|
||||
bg: allSelected ? 'primary.50' : 'none',
|
||||
boxShadow: allSelected && '0 0 0 2px #F0F4FF',
|
||||
_hover: {
|
||||
bg: 'primary.50'
|
||||
},
|
||||
border: allSelected && '1px solid #3370FF',
|
||||
color: 'primary.600'
|
||||
},
|
||||
svg: {
|
||||
strokeWidth: '1px !important'
|
||||
}
|
||||
}}
|
||||
value={'all'}
|
||||
size={'lg'}
|
||||
mr={2}
|
||||
isChecked={allSelected}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setCheckboxValue(value.textList);
|
||||
} else {
|
||||
setCheckboxValue([]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Box fontSize={'sm'} color={'myGray.600'} fontWeight={'medium'}>
|
||||
{commonT('common.Select all')}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<Flex gap={4}>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
display={checkboxValue.length === 0 ? 'none' : 'flex'}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon name={'delete'} boxSize={4} />}
|
||||
onClick={() => {
|
||||
setCheckboxValue([]);
|
||||
onChange({
|
||||
...value,
|
||||
textList: value.textList.filter((_) => !checkboxValue.includes(_))
|
||||
});
|
||||
}}
|
||||
>
|
||||
{commonT('common.Delete')}
|
||||
</Button>
|
||||
<Button
|
||||
display={checkboxValue.length !== 0 ? 'none' : 'flex'}
|
||||
onClick={() => {
|
||||
onChange({
|
||||
...value,
|
||||
textList: ['', ...value.textList]
|
||||
});
|
||||
setIsEditIndex(0);
|
||||
setIsAdding(true);
|
||||
}}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon name={'common/addLight'} boxSize={4} />}
|
||||
>
|
||||
{commonT('common.Add')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Box h={'400px'} pb={4} overflow={'auto'} px={8}>
|
||||
{value.textList.map((text, index) => {
|
||||
const selected = checkboxValue.includes(text);
|
||||
return (
|
||||
<Flex
|
||||
key={index}
|
||||
alignItems={'center'}
|
||||
h={10}
|
||||
mt={2}
|
||||
onMouseEnter={() => setShowIcons(index)}
|
||||
onMouseLeave={() => setShowIcons(null)}
|
||||
>
|
||||
<Checkbox
|
||||
{...getCheckboxProps({ value: text })}
|
||||
sx={{
|
||||
'.chakra-checkbox__control': {
|
||||
bg: selected ? 'primary.50' : 'none',
|
||||
boxShadow: selected ? '0 0 0 2px #F0F4FF' : 'none',
|
||||
_hover: {
|
||||
bg: 'primary.50'
|
||||
},
|
||||
border: selected && '1px solid #3370FF',
|
||||
color: 'primary.600'
|
||||
},
|
||||
svg: {
|
||||
strokeWidth: '1px !important'
|
||||
}
|
||||
}}
|
||||
size={'lg'}
|
||||
mr={2}
|
||||
isChecked={selected}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setCheckboxValue([...checkboxValue, text]);
|
||||
} else {
|
||||
setCheckboxValue(checkboxValue.filter((_) => _ !== text));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{index === isEditIndex ? (
|
||||
<InputGroup alignItems={'center'} h={'full'}>
|
||||
<Input
|
||||
autoFocus
|
||||
h={'full'}
|
||||
defaultValue={text}
|
||||
onBlur={(e) => {
|
||||
setIsEditIndex(-1);
|
||||
if (
|
||||
!e.target.value ||
|
||||
(value.textList.indexOf(e.target.value) !== -1 &&
|
||||
value.textList.indexOf(e.target.value) !== index)
|
||||
) {
|
||||
isAdding &&
|
||||
onChange({
|
||||
...value,
|
||||
textList: value.textList.filter((_, i) => i !== index)
|
||||
});
|
||||
} else {
|
||||
onChange({
|
||||
...value,
|
||||
textList: value.textList?.map((v, i) =>
|
||||
i !== index ? v : e.target.value
|
||||
)
|
||||
});
|
||||
}
|
||||
setIsAdding(false);
|
||||
}}
|
||||
/>
|
||||
<InputRightElement alignItems={'center'} pr={4} display={'flex'}>
|
||||
<MyIcon name={'save'} boxSize={4} cursor={'pointer'} />
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
) : (
|
||||
<Flex
|
||||
h={10}
|
||||
w={'full'}
|
||||
rounded={'md'}
|
||||
px={4}
|
||||
bg={'myGray.50'}
|
||||
alignItems={'center'}
|
||||
border={'1px solid #F0F1F6'}
|
||||
_hover={{ border: '1px solid #94B5FF' }}
|
||||
>
|
||||
{text}
|
||||
<Box flex={1} />
|
||||
{checkboxValue.length === 0 && (
|
||||
<Box display={showIcons === index ? 'flex' : 'none'}>
|
||||
<MyIcon
|
||||
name={'edit'}
|
||||
boxSize={4}
|
||||
mr={2}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => setIsEditIndex(index)}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
boxSize={4}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
const temp = value.textList?.filter((_, i) => i !== index);
|
||||
onChange({
|
||||
...value,
|
||||
textList: temp
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
@@ -92,20 +92,15 @@ const ScheduledTriggerConfig = ({
|
||||
value,
|
||||
onChange
|
||||
}: {
|
||||
value: AppScheduledTriggerConfigType | null;
|
||||
onChange: (e: AppScheduledTriggerConfigType | null) => void;
|
||||
value?: AppScheduledTriggerConfigType;
|
||||
onChange: (e?: AppScheduledTriggerConfigType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { register, setValue, watch } = useForm<AppScheduledTriggerConfigType>({
|
||||
defaultValues: {
|
||||
cronString: value?.cronString || '',
|
||||
timezone: value?.timezone,
|
||||
defaultPrompt: value?.defaultPrompt || ''
|
||||
}
|
||||
});
|
||||
const timezone = watch('timezone');
|
||||
const cronString = watch('cronString');
|
||||
|
||||
const timezone = value?.timezone;
|
||||
const cronString = value?.cronString;
|
||||
const defaultPrompt = value?.defaultPrompt || '';
|
||||
|
||||
const cronSelectList = useRef<MultipleSelectProps['list']>([
|
||||
{
|
||||
@@ -130,15 +125,39 @@ const ScheduledTriggerConfig = ({
|
||||
}
|
||||
]);
|
||||
|
||||
const onUpdate = useCallback(
|
||||
({
|
||||
cronString,
|
||||
timezone,
|
||||
defaultPrompt
|
||||
}: {
|
||||
cronString?: string;
|
||||
timezone?: string;
|
||||
defaultPrompt?: string;
|
||||
}) => {
|
||||
if (!cronString) {
|
||||
onChange(undefined);
|
||||
return;
|
||||
}
|
||||
onChange({
|
||||
...value,
|
||||
cronString,
|
||||
timezone: timezone || Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
defaultPrompt: defaultPrompt || ''
|
||||
});
|
||||
},
|
||||
[onChange, value]
|
||||
);
|
||||
|
||||
/* cron string to config field */
|
||||
const cronConfig = useMemo(() => {
|
||||
if (!cronString) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
const cronField = cronParser2Fields(cronString);
|
||||
|
||||
if (!cronField) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cronField.dayOfMonth.length !== 31) {
|
||||
@@ -169,19 +188,22 @@ const ScheduledTriggerConfig = ({
|
||||
|
||||
const cronConfig2cronString = useCallback(
|
||||
(e: CronFieldType) => {
|
||||
if (e[0] === CronJobTypeEnum.month) {
|
||||
setValue('cronString', `0 ${e[2]} ${e[1]} * *`);
|
||||
} else if (e[0] === CronJobTypeEnum.week) {
|
||||
setValue('cronString', `0 ${e[2]} * * ${e[1]}`);
|
||||
} else if (e[0] === CronJobTypeEnum.day) {
|
||||
setValue('cronString', `0 ${e[1]} * * *`);
|
||||
} else if (e[0] === CronJobTypeEnum.interval) {
|
||||
setValue('cronString', `0 */${e[1]} * * *`);
|
||||
} else {
|
||||
setValue('cronString', '');
|
||||
}
|
||||
const str = (() => {
|
||||
if (e[0] === CronJobTypeEnum.month) {
|
||||
return `0 ${e[2]} ${e[1]} * *`;
|
||||
} else if (e[0] === CronJobTypeEnum.week) {
|
||||
return `0 ${e[2]} * * ${e[1]}`;
|
||||
} else if (e[0] === CronJobTypeEnum.day) {
|
||||
return `0 ${e[1]} * * *`;
|
||||
} else if (e[0] === CronJobTypeEnum.interval) {
|
||||
return `0 */${e[1]} * * *`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
})();
|
||||
onUpdate({ cronString: str });
|
||||
},
|
||||
[setValue]
|
||||
[onUpdate]
|
||||
);
|
||||
|
||||
// cron config to show label
|
||||
@@ -216,22 +238,9 @@ const ScheduledTriggerConfig = ({
|
||||
return t('common.Not open');
|
||||
}, [cronField, isOpenSchedule, t]);
|
||||
|
||||
// update value
|
||||
watch((data) => {
|
||||
if (!data.cronString) {
|
||||
onChange(null);
|
||||
return;
|
||||
}
|
||||
onChange({
|
||||
cronString: data.cronString,
|
||||
timezone: data.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
defaultPrompt: data.defaultPrompt || ''
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!value?.timezone) {
|
||||
setValue('timezone', Intl.DateTimeFormat().resolvedOptions().timeZone);
|
||||
onUpdate({ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone });
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -272,9 +281,9 @@ const ScheduledTriggerConfig = ({
|
||||
isChecked={isOpenSchedule}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setValue('cronString', defaultCronString);
|
||||
onUpdate({ cronString: defaultCronString });
|
||||
} else {
|
||||
setValue('cronString', '');
|
||||
onUpdate({ cronString: '' });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -300,7 +309,7 @@ const ScheduledTriggerConfig = ({
|
||||
<TimezoneSelect
|
||||
value={timezone}
|
||||
onChange={(e) => {
|
||||
setValue('timezone', e);
|
||||
onUpdate({ timezone: e });
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -308,10 +317,13 @@ const ScheduledTriggerConfig = ({
|
||||
<Box mt={5}>
|
||||
<Box>{t('core.app.schedule.Default prompt')}</Box>
|
||||
<Textarea
|
||||
{...register('defaultPrompt')}
|
||||
value={defaultPrompt}
|
||||
rows={8}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('core.app.schedule.Default prompt placeholder')}
|
||||
onChange={(e) => {
|
||||
onUpdate({ defaultPrompt: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
@@ -323,13 +335,13 @@ const ScheduledTriggerConfig = ({
|
||||
}, [
|
||||
cronConfig2cronString,
|
||||
cronField,
|
||||
defaultPrompt,
|
||||
formatLabel,
|
||||
isOpen,
|
||||
isOpenSchedule,
|
||||
onClose,
|
||||
onOpen,
|
||||
register,
|
||||
setValue,
|
||||
onUpdate,
|
||||
t,
|
||||
timezone
|
||||
]);
|
||||
|
||||
@@ -11,12 +11,13 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MySlider from '@/components/Slider';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import { defaultTTSConfig } from '@fastgpt/global/core/app/constants';
|
||||
|
||||
const TTSSelect = ({
|
||||
value,
|
||||
value = defaultTTSConfig,
|
||||
onChange
|
||||
}: {
|
||||
value: AppTTSConfigType;
|
||||
value?: AppTTSConfigType;
|
||||
onChange: (e: AppTTSConfigType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -39,10 +39,10 @@ import MyRadio from '@/components/common/MyRadio';
|
||||
import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/utils';
|
||||
|
||||
const VariableEdit = ({
|
||||
variables,
|
||||
variables = [],
|
||||
onChange
|
||||
}: {
|
||||
variables: VariableItemType[];
|
||||
variables?: VariableItemType[];
|
||||
onChange: (data: VariableItemType[]) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -6,14 +6,15 @@ import { useTranslation } from 'next-i18next';
|
||||
import type { AppWhisperConfigType } from '@fastgpt/global/core/app/type.d';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import { defaultWhisperConfig } from '@fastgpt/global/core/app/constants';
|
||||
|
||||
const WhisperConfig = ({
|
||||
isOpenAudio,
|
||||
value,
|
||||
value = defaultWhisperConfig,
|
||||
onChange
|
||||
}: {
|
||||
isOpenAudio: boolean;
|
||||
value: AppWhisperConfigType;
|
||||
value?: AppWhisperConfigType;
|
||||
onChange: (e: AppWhisperConfigType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -0,0 +1,482 @@
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
ModalBody,
|
||||
useDisclosure,
|
||||
Switch,
|
||||
Textarea,
|
||||
Checkbox,
|
||||
HStack
|
||||
} from '@chakra-ui/react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { ChatInputGuideConfigType } from '@fastgpt/global/core/app/type.d';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MyInput from '@/components/MyInput';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { fileDownload } from '@/web/common/file/utils';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import {
|
||||
delChatInputGuide,
|
||||
getChatInputGuideList,
|
||||
getCountChatInputGuideTotal,
|
||||
postChatInputGuides,
|
||||
putChatInputGuide
|
||||
} from '@/web/core/chat/inputGuide/api';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { readCsvRawText } from '@fastgpt/web/common/file/utils';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useRequest } from 'ahooks';
|
||||
import HighlightText from '@fastgpt/web/components/common/String/HighlightText';
|
||||
import { defaultChatInputGuideConfig } from '@fastgpt/global/core/app/constants';
|
||||
|
||||
const csvTemplate = `"第一列内容"
|
||||
"只会将第一列内容导入,其余列会被忽略"
|
||||
"AIGC发展分为几个阶段?"`;
|
||||
|
||||
const InputGuideConfig = ({
|
||||
appId,
|
||||
value = defaultChatInputGuideConfig,
|
||||
onChange
|
||||
}: {
|
||||
appId: string;
|
||||
value?: ChatInputGuideConfigType;
|
||||
onChange: (e: ChatInputGuideConfigType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { chatT, commonT } = useI18n();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const {
|
||||
isOpen: isOpenLexiconConfig,
|
||||
onOpen: onOpenLexiconConfig,
|
||||
onClose: onCloseLexiconConfig
|
||||
} = useDisclosure();
|
||||
const isOpenQuestionGuide = value.open;
|
||||
|
||||
const { data } = useQuery(
|
||||
[appId, isOpenLexiconConfig],
|
||||
() => {
|
||||
return getCountChatInputGuideTotal({
|
||||
appId
|
||||
});
|
||||
},
|
||||
{
|
||||
enabled: !!appId
|
||||
}
|
||||
);
|
||||
const total = data?.total || 0;
|
||||
|
||||
const formLabel = useMemo(() => {
|
||||
if (!isOpenQuestionGuide) {
|
||||
return t('core.app.whisper.Close');
|
||||
}
|
||||
return t('core.app.whisper.Open');
|
||||
}, [t, isOpenQuestionGuide]);
|
||||
|
||||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/inputGuides'} mr={2} w={'20px'} />
|
||||
<HStack>
|
||||
<Box>{chatT('Input guide')}</Box>
|
||||
<QuestionTip label={chatT('Input guide tip')} />
|
||||
</HStack>
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={chatT('Config input guide')}>
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
mr={'-5px'}
|
||||
onClick={onOpen}
|
||||
>
|
||||
{formLabel}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
<MyModal
|
||||
title={chatT('Input guide')}
|
||||
iconSrc="core/app/inputGuides"
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalBody px={[5, 16]} py={[4, 8]} w={'500px'}>
|
||||
<Flex justifyContent={'space-between'} alignItems={'center'}>
|
||||
{t('Is open')}
|
||||
<Switch
|
||||
isChecked={isOpenQuestionGuide}
|
||||
size={'lg'}
|
||||
onChange={(e) => {
|
||||
onChange({
|
||||
...value,
|
||||
open: e.target.checked
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
{isOpenQuestionGuide && (
|
||||
<>
|
||||
<Flex mt={8} alignItems={'center'}>
|
||||
{chatT('Input guide lexicon')}
|
||||
<Box fontSize={'xs'} px={2} bg={'myGray.100'} ml={1} rounded={'full'}>
|
||||
{total}
|
||||
</Box>
|
||||
<Box flex={'1 0 0'} />
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon boxSize={'4'} name={'common/settingLight'} />}
|
||||
onClick={() => {
|
||||
onOpenLexiconConfig();
|
||||
}}
|
||||
>
|
||||
{chatT('Config input guide lexicon')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<>
|
||||
<Flex mt={8} alignItems={'center'}>
|
||||
{chatT('Custom input guide url')}
|
||||
<Flex
|
||||
onClick={() => window.open(getDocPath('/docs/course/chat_input_guide'))}
|
||||
color={'primary.700'}
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
>
|
||||
<MyIcon name={'book'} ml={4} mr={1} />
|
||||
{commonT('common.Documents')}
|
||||
</Flex>
|
||||
<Box flex={'1 0 0'} />
|
||||
</Flex>
|
||||
<Textarea
|
||||
mt={2}
|
||||
bg={'myGray.50'}
|
||||
defaultValue={value.customUrl}
|
||||
onBlur={(e) =>
|
||||
onChange({
|
||||
...value,
|
||||
customUrl: e.target.value
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
</>
|
||||
)}
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
|
||||
{isOpenLexiconConfig && <LexiconConfigModal appId={appId} onClose={onCloseLexiconConfig} />}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(InputGuideConfig);
|
||||
|
||||
const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () => void }) => {
|
||||
const { chatT, commonT } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
fileType: '.csv'
|
||||
});
|
||||
const [newData, setNewData] = useState<string>();
|
||||
|
||||
const [selectedRows, setSelectedRows] = useState<string[]>([]);
|
||||
const [editDataId, setEditDataId] = useState<string>();
|
||||
|
||||
const [searchKey, setSearchKey] = useState('');
|
||||
|
||||
const {
|
||||
list,
|
||||
setData,
|
||||
ScrollList,
|
||||
isLoading: isRequesting,
|
||||
fetchData,
|
||||
scroll2Top
|
||||
} = useScrollPagination(getChatInputGuideList, {
|
||||
refreshDeps: [searchKey],
|
||||
debounceWait: 300,
|
||||
|
||||
itemHeight: 46,
|
||||
overscan: 20,
|
||||
|
||||
pageSize: 20,
|
||||
defaultParams: {
|
||||
appId,
|
||||
searchKey
|
||||
}
|
||||
});
|
||||
|
||||
const { run: createNewData, loading: isCreating } = useRequest2(
|
||||
(textList: string[]) => {
|
||||
if (textList.filter(Boolean).length === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
scroll2Top();
|
||||
return postChatInputGuides({
|
||||
appId,
|
||||
textList
|
||||
}).then((res) => {
|
||||
if (res.insertLength < textList.length) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: chatT('Insert input guide, Some data already exists', { len: res.insertLength })
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common.Add Success')
|
||||
});
|
||||
}
|
||||
fetchData(1);
|
||||
});
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
onSuccess() {
|
||||
setNewData(undefined);
|
||||
},
|
||||
errorToast: t('error.Create failed')
|
||||
}
|
||||
);
|
||||
|
||||
const onUpdateData = ({ text, dataId }: { text: string; dataId: string }) => {
|
||||
setData((state) =>
|
||||
state.map((item) => {
|
||||
if (item._id === dataId) {
|
||||
return {
|
||||
...item,
|
||||
text
|
||||
};
|
||||
}
|
||||
return item;
|
||||
})
|
||||
);
|
||||
|
||||
if (text) {
|
||||
putChatInputGuide({
|
||||
appId,
|
||||
text,
|
||||
dataId
|
||||
});
|
||||
}
|
||||
|
||||
setEditDataId(undefined);
|
||||
};
|
||||
const onDeleteData = (dataIdList: string[]) => {
|
||||
setData((state) => state.filter((item) => !dataIdList.includes(item._id)));
|
||||
delChatInputGuide({
|
||||
appId,
|
||||
dataIdList
|
||||
});
|
||||
};
|
||||
|
||||
const onSelectFile = async (files: File[]) => {
|
||||
const file = files?.[0];
|
||||
if (file) {
|
||||
const list = await readCsvRawText({ file });
|
||||
const textList = list.map((item) => item[0]?.trim() || '').filter(Boolean);
|
||||
createNewData(textList);
|
||||
}
|
||||
};
|
||||
|
||||
const isLoading = isRequesting || isCreating;
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
title={chatT('Config input guide lexicon title')}
|
||||
iconSrc="core/app/inputGuides"
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
isLoading={isLoading}
|
||||
h={'600px'}
|
||||
w={'500px'}
|
||||
>
|
||||
<Flex gap={4} px={8} py={4} mb={4} alignItems={'center'} borderBottom={'base'}>
|
||||
<Box flex={1}>
|
||||
<MyInput
|
||||
leftIcon={<MyIcon name={'common/searchLight'} boxSize={4} color={'myGray.500'} />}
|
||||
bg={'myGray.50'}
|
||||
w={'full'}
|
||||
h={9}
|
||||
placeholder={commonT('common.Search')}
|
||||
onChange={(e) => setSearchKey(e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
<Button
|
||||
onClick={onOpenSelectFile}
|
||||
variant={'whiteBase'}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon name={'common/importLight'} boxSize={4} />}
|
||||
>
|
||||
{commonT('common.Import')}
|
||||
</Button>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
fileDownload({
|
||||
text: csvTemplate,
|
||||
type: 'text/csv;charset=utf-8',
|
||||
filename: 'questionGuide_template.csv'
|
||||
});
|
||||
}}
|
||||
>
|
||||
<QuestionTip ml={-2} label={chatT('Csv input lexicon tip')} />
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box px={8}>
|
||||
{/* button */}
|
||||
<Flex mb={1} justifyContent={'space-between'}>
|
||||
<Box flex={1} />
|
||||
<Flex gap={4}>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
display={selectedRows.length === 0 ? 'none' : 'flex'}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon name={'delete'} boxSize={4} />}
|
||||
onClick={() => {
|
||||
onDeleteData(selectedRows);
|
||||
setSelectedRows([]);
|
||||
}}
|
||||
>
|
||||
{commonT('common.Delete')}
|
||||
</Button>
|
||||
<Button
|
||||
display={selectedRows.length !== 0 ? 'none' : 'flex'}
|
||||
onClick={() => {
|
||||
setNewData('');
|
||||
}}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon name={'common/addLight'} boxSize={4} />}
|
||||
>
|
||||
{commonT('common.Add')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{/* new data input */}
|
||||
{newData !== undefined && (
|
||||
<Box mt={5} ml={list.length > 0 ? 7 : 0}>
|
||||
<MyInput
|
||||
autoFocus
|
||||
rightIcon={<MyIcon name={'save'} w={'14px'} cursor={'pointer'} />}
|
||||
placeholder={chatT('New input guide lexicon')}
|
||||
onBlur={(e) => {
|
||||
createNewData([e.target.value.trim()]);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
createNewData([e.currentTarget.value.trim()]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<ScrollList
|
||||
px={8}
|
||||
flex={'1 0 0'}
|
||||
EmptyChildren={<EmptyTip text={chatT('Chat input guide lexicon is empty')} />}
|
||||
>
|
||||
{list.map((data, index) => {
|
||||
const item = data.data;
|
||||
|
||||
const selected = selectedRows.includes(item._id);
|
||||
const edited = editDataId === item._id;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
key={index}
|
||||
alignItems={'center'}
|
||||
h={10}
|
||||
mt={3}
|
||||
_hover={{
|
||||
'& .icon-list': {
|
||||
display: 'flex'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
size={'lg'}
|
||||
mr={2}
|
||||
isChecked={selected}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedRows([...selectedRows, item._id]);
|
||||
} else {
|
||||
setSelectedRows(selectedRows.filter((id) => id !== item._id));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{edited ? (
|
||||
<Box h={'full'} flex={'1 0 0'}>
|
||||
<MyInput
|
||||
autoFocus
|
||||
defaultValue={item.text}
|
||||
rightIcon={<MyIcon name={'save'} boxSize={4} cursor={'pointer'} />}
|
||||
onBlur={(e) => {
|
||||
onUpdateData({
|
||||
text: e.target.value.trim(),
|
||||
dataId: item._id
|
||||
});
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
onUpdateData({
|
||||
text: e.currentTarget.value.trim(),
|
||||
dataId: item._id
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Flex
|
||||
h={'40px'}
|
||||
w={0}
|
||||
flex={'1 0 0'}
|
||||
rounded={'md'}
|
||||
px={4}
|
||||
bg={'myGray.50'}
|
||||
alignItems={'center'}
|
||||
border={'base'}
|
||||
_hover={{ borderColor: 'primary.300' }}
|
||||
>
|
||||
<Box className="textEllipsis" w={0} flex={'1 0 0'}>
|
||||
<HighlightText rawText={item.text} matchText={searchKey} />
|
||||
</Box>
|
||||
{selectedRows.length === 0 && (
|
||||
<Box className="icon-list" display={'none'}>
|
||||
<MyIcon
|
||||
name={'edit'}
|
||||
boxSize={4}
|
||||
mr={2}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => setEditDataId(item._id)}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
boxSize={4}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={() => onDeleteData([item._id])}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</ScrollList>
|
||||
|
||||
<File onSelect={onSelectFile} />
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
@@ -29,7 +29,8 @@ import {
|
||||
initWorkflowEdgeStatus,
|
||||
storeNodes2RuntimeNodes
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { getGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
export type ChatTestComponentRef = {
|
||||
resetChatTest: () => void;
|
||||
@@ -37,13 +38,11 @@ export type ChatTestComponentRef = {
|
||||
|
||||
const ChatTest = (
|
||||
{
|
||||
app,
|
||||
isOpen,
|
||||
nodes = [],
|
||||
edges = [],
|
||||
onClose
|
||||
}: {
|
||||
app: AppSchema;
|
||||
isOpen: boolean;
|
||||
nodes?: StoreNodeItemType[];
|
||||
edges?: StoreEdgeItemType[];
|
||||
@@ -54,6 +53,7 @@ const ChatTest = (
|
||||
const { t } = useTranslation();
|
||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||
const { userInfo } = useUserStore();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const startChat = useCallback(
|
||||
async ({ chatList, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||
@@ -70,8 +70,8 @@ const ChatTest = (
|
||||
nodes: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)),
|
||||
edges: initWorkflowEdgeStatus(edges),
|
||||
variables,
|
||||
appId: app._id,
|
||||
appName: `调试-${app.name}`,
|
||||
appId: appDetail._id,
|
||||
appName: `调试-${appDetail.name}`,
|
||||
mode: 'test'
|
||||
},
|
||||
onMessage: generatingMessage,
|
||||
@@ -80,7 +80,7 @@ const ChatTest = (
|
||||
|
||||
return { responseText, responseData, newVariables };
|
||||
},
|
||||
[app._id, app.name, edges, nodes]
|
||||
[appDetail._id, appDetail.name, edges, nodes]
|
||||
);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
@@ -139,11 +139,11 @@ const ChatTest = (
|
||||
<Box flex={1}>
|
||||
<ChatBox
|
||||
ref={ChatBoxRef}
|
||||
appId={app._id}
|
||||
appAvatar={app.avatar}
|
||||
appId={appDetail._id}
|
||||
appAvatar={appDetail.avatar}
|
||||
userAvatar={userInfo?.avatar}
|
||||
showMarkIcon
|
||||
userGuideModule={getGuideModule(nodes)}
|
||||
chatConfig={appDetail.chatConfig}
|
||||
showFileSelector={checkChatSupportSelectFileByModules(nodes)}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={() => {}}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useCallback, useState } from 'react';
|
||||
import { checkWorkflowNodeAndConnection } from '@/web/core/workflow/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { flowNode2StoreNodes } from '../../utils';
|
||||
import { uiWorkflow2StoreWorkflow } from '../../utils';
|
||||
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
@@ -52,7 +52,7 @@ export const useDebug = () => {
|
||||
|
||||
const checkResults = checkWorkflowNodeAndConnection({ nodes, edges });
|
||||
if (!checkResults) {
|
||||
const storeNodes = flowNode2StoreNodes({ nodes, edges });
|
||||
const storeNodes = uiWorkflow2StoreWorkflow({ nodes, edges });
|
||||
|
||||
return JSON.stringify(storeNodes);
|
||||
} else {
|
||||
|
||||
@@ -39,6 +39,7 @@ import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
const CurlImportModal = dynamic(() => import('./CurlImportModal'));
|
||||
|
||||
export const HttpHeaders = [
|
||||
@@ -251,6 +252,7 @@ export function RenderHttpProps({
|
||||
const { t } = useTranslation();
|
||||
const [selectedTab, setSelectedTab] = useState(TabEnum.params);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const requestMethods = inputs.find((item) => item.key === NodeInputKeyEnum.httpMethod)?.value;
|
||||
const params = inputs.find((item) => item.key === NodeInputKeyEnum.httpParams);
|
||||
@@ -262,7 +264,11 @@ export function RenderHttpProps({
|
||||
|
||||
// get variable
|
||||
const variables = useMemo(() => {
|
||||
const globalVariables = getWorkflowGlobalVariables(nodeList, t);
|
||||
const globalVariables = getWorkflowGlobalVariables({
|
||||
nodes: nodeList,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
t
|
||||
});
|
||||
|
||||
const moduleVariables = formatEditorVariablePickerIcon(
|
||||
inputs
|
||||
|
||||
@@ -30,6 +30,7 @@ import { SourceHandle } from '../render/Handle';
|
||||
import { Position, useReactFlow } from 'reactflow';
|
||||
import { getReferenceDataValueType } from '@/web/core/workflow/utils';
|
||||
import DragIcon from '@fastgpt/web/components/common/DndDrag/DragIcon';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const ListItem = ({
|
||||
provided,
|
||||
@@ -342,15 +343,17 @@ const ConditionSelect = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||
|
||||
// get condition type
|
||||
const valueType = useMemo(() => {
|
||||
return getReferenceDataValueType({
|
||||
variable,
|
||||
nodeList,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
t
|
||||
});
|
||||
}, [nodeList, t, variable]);
|
||||
}, [appDetail.chatConfig, nodeList, t, variable]);
|
||||
|
||||
const conditionList = useMemo(() => {
|
||||
if (valueType === WorkflowIOValueTypeEnum.string) return stringConditionList;
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import React, { useMemo, useTransition } from 'react';
|
||||
import React, { Dispatch, useMemo, useTransition } from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import { Box, Flex, Textarea, useTheme } from '@chakra-ui/react';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { FlowNodeItemType, StoreNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { welcomeTextTip } from '@fastgpt/global/core/workflow/template/tip';
|
||||
|
||||
import QGSwitch from '@/components/core/app/QGSwitch';
|
||||
import TTSSelect from '@/components/core/app/TTSSelect';
|
||||
import WhisperConfig from '@/components/core/app/WhisperConfig';
|
||||
import QGuidesConfig from '@/components/core/app/QGuidesConfig';
|
||||
import { splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import InputGuideConfig from '@/components/core/chat/appConfig/InputGuideConfig';
|
||||
import { getAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { TTSTypeEnum } from '@/web/core/app/constants';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
@@ -19,12 +18,35 @@ import NodeCard from './render/NodeCard';
|
||||
import ScheduledTriggerConfig from '@/components/core/app/ScheduledTriggerConfig';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../context';
|
||||
import { VariableItemType } from '@fastgpt/global/core/app/type';
|
||||
import { AppChatConfigType, AppDetailType, VariableItemType } from '@fastgpt/global/core/app/type';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import VariableEdit from '@/components/core/app/VariableEdit';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
type ComponentProps = {
|
||||
chatConfig: AppChatConfigType;
|
||||
setAppDetail: Dispatch<React.SetStateAction<AppDetailType>>;
|
||||
};
|
||||
|
||||
const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const theme = useTheme();
|
||||
const { appDetail, setAppDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const chatConfig = useMemo<AppChatConfigType>(() => {
|
||||
return getAppChatConfig({
|
||||
chatConfig: appDetail.chatConfig,
|
||||
systemConfigNode: data,
|
||||
isPublicFetch: true
|
||||
});
|
||||
}, [data, appDetail]);
|
||||
|
||||
const componentsProps = useMemo(
|
||||
() => ({
|
||||
chatConfig,
|
||||
setAppDetail
|
||||
}),
|
||||
[chatConfig, setAppDetail]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -40,24 +62,24 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
{...data}
|
||||
>
|
||||
<Box px={4} py={'10px'} position={'relative'} borderRadius={'md'} className="nodrag">
|
||||
<WelcomeText data={data} />
|
||||
<WelcomeText {...componentsProps} />
|
||||
<Box pt={4}>
|
||||
<ChatStartVariable data={data} />
|
||||
<ChatStartVariable {...componentsProps} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={theme.borders.base}>
|
||||
<TTSGuide data={data} />
|
||||
<TTSGuide {...componentsProps} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={theme.borders.base}>
|
||||
<WhisperGuide data={data} />
|
||||
<WhisperGuide {...componentsProps} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={theme.borders.base}>
|
||||
<QuestionGuide data={data} />
|
||||
<QuestionGuide {...componentsProps} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={theme.borders.base}>
|
||||
<ScheduledTrigger data={data} />
|
||||
<ScheduledTrigger {...componentsProps} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={theme.borders.base}>
|
||||
<QuestionInputGuide data={data} />
|
||||
<QuestionInputGuide {...componentsProps} />
|
||||
</Box>
|
||||
</Box>
|
||||
</NodeCard>
|
||||
@@ -67,13 +89,9 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
|
||||
export default React.memo(NodeUserGuide);
|
||||
|
||||
function WelcomeText({ data }: { data: FlowNodeItemType }) {
|
||||
function WelcomeText({ chatConfig: { welcomeText }, setAppDetail }: ComponentProps) {
|
||||
const { t } = useTranslation();
|
||||
const { inputs, nodeId } = data;
|
||||
const [, startTst] = useTransition();
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const welcomeText = inputs.find((item) => item.key === NodeInputKeyEnum.welcomeText);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -84,181 +102,136 @@ function WelcomeText({ data }: { data: FlowNodeItemType }) {
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
{welcomeText && (
|
||||
<Textarea
|
||||
className="nodrag"
|
||||
rows={6}
|
||||
fontSize={'12px'}
|
||||
resize={'both'}
|
||||
defaultValue={welcomeText.value}
|
||||
bg={'myWhite.500'}
|
||||
placeholder={t(welcomeTextTip)}
|
||||
onChange={(e) => {
|
||||
startTst(() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
key: NodeInputKeyEnum.welcomeText,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...welcomeText,
|
||||
value: e.target.value
|
||||
}
|
||||
});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Textarea
|
||||
className="nodrag"
|
||||
rows={6}
|
||||
fontSize={'12px'}
|
||||
resize={'both'}
|
||||
defaultValue={welcomeText}
|
||||
bg={'myWhite.500'}
|
||||
placeholder={t(welcomeTextTip)}
|
||||
onChange={(e) => {
|
||||
startTst(() => {
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
welcomeText: e.target.value
|
||||
}
|
||||
}));
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ChatStartVariable({ data }: { data: FlowNodeItemType }) {
|
||||
const { inputs, nodeId } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const variables = useMemo(
|
||||
() =>
|
||||
(inputs.find((item) => item.key === NodeInputKeyEnum.variables)
|
||||
?.value as VariableItemType[]) || [],
|
||||
[inputs]
|
||||
);
|
||||
|
||||
function ChatStartVariable({ chatConfig: { variables = [] }, setAppDetail }: ComponentProps) {
|
||||
const updateVariables = useMemoizedFn((value: VariableItemType[]) => {
|
||||
// update system config node
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
key: NodeInputKeyEnum.variables,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...inputs.find((item) => item.key === NodeInputKeyEnum.variables),
|
||||
value
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
variables: value
|
||||
}
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
return <VariableEdit variables={variables} onChange={(e) => updateVariables(e)} />;
|
||||
}
|
||||
|
||||
function QuestionGuide({ data }: { data: FlowNodeItemType }) {
|
||||
const { inputs, nodeId } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const questionGuide = useMemo(
|
||||
() =>
|
||||
(inputs.find((item) => item.key === NodeInputKeyEnum.questionGuide)?.value as boolean) ||
|
||||
false,
|
||||
[inputs]
|
||||
);
|
||||
|
||||
function QuestionGuide({ chatConfig: { questionGuide = false }, setAppDetail }: ComponentProps) {
|
||||
return (
|
||||
<QGSwitch
|
||||
isChecked={questionGuide}
|
||||
size={'md'}
|
||||
onChange={(e) => {
|
||||
const value = e.target.checked;
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
key: NodeInputKeyEnum.questionGuide,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...inputs.find((item) => item.key === NodeInputKeyEnum.questionGuide),
|
||||
value
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
questionGuide: value
|
||||
}
|
||||
});
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function TTSGuide({ data }: { data: FlowNodeItemType }) {
|
||||
const { inputs, nodeId } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const { ttsConfig } = splitGuideModule({ inputs } as StoreNodeItemType);
|
||||
|
||||
function TTSGuide({ chatConfig: { ttsConfig }, setAppDetail }: ComponentProps) {
|
||||
return (
|
||||
<TTSSelect
|
||||
value={ttsConfig}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
key: NodeInputKeyEnum.tts,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...inputs.find((item) => item.key === NodeInputKeyEnum.tts),
|
||||
value: e
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
ttsConfig: e
|
||||
}
|
||||
});
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function WhisperGuide({ data }: { data: FlowNodeItemType }) {
|
||||
const { inputs, nodeId } = data;
|
||||
function WhisperGuide({ chatConfig: { whisperConfig, ttsConfig }, setAppDetail }: ComponentProps) {
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const { ttsConfig, whisperConfig } = splitGuideModule({ inputs } as StoreNodeItemType);
|
||||
|
||||
return (
|
||||
<WhisperConfig
|
||||
isOpenAudio={ttsConfig.type !== TTSTypeEnum.none}
|
||||
isOpenAudio={ttsConfig?.type !== TTSTypeEnum.none}
|
||||
value={whisperConfig}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
key: NodeInputKeyEnum.whisper,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...inputs.find((item) => item.key === NodeInputKeyEnum.whisper),
|
||||
value: e
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
whisperConfig: e
|
||||
}
|
||||
});
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ScheduledTrigger({ data }: { data: FlowNodeItemType }) {
|
||||
const { inputs, nodeId } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const { scheduledTriggerConfig } = splitGuideModule({ inputs } as StoreNodeItemType);
|
||||
|
||||
function ScheduledTrigger({
|
||||
chatConfig: { scheduledTriggerConfig },
|
||||
setAppDetail
|
||||
}: ComponentProps) {
|
||||
return (
|
||||
<ScheduledTriggerConfig
|
||||
value={scheduledTriggerConfig}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
key: NodeInputKeyEnum.scheduleTrigger,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...inputs.find((item) => item.key === NodeInputKeyEnum.scheduleTrigger),
|
||||
value: e
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
scheduledTriggerConfig: e
|
||||
}
|
||||
});
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionInputGuide({ data }: { data: FlowNodeItemType }) {
|
||||
const { inputs, nodeId } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const { questionGuideText } = splitGuideModule({ inputs } as StoreNodeItemType);
|
||||
function QuestionInputGuide({ chatConfig: { chatInputGuide }, setAppDetail }: ComponentProps) {
|
||||
const appId = useContextSelector(WorkflowContext, (v) => v.appId);
|
||||
|
||||
return (
|
||||
<QGuidesConfig
|
||||
value={questionGuideText}
|
||||
return appId ? (
|
||||
<InputGuideConfig
|
||||
appId={appId}
|
||||
value={chatInputGuide}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
key: NodeInputKeyEnum.questionGuideText,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...inputs.find((item) => item.key === NodeInputKeyEnum.questionGuideText),
|
||||
value: e
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
chatInputGuide: e
|
||||
}
|
||||
});
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
) : null;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { ReferSelector, useReference } from './render/RenderInput/templates/Reference';
|
||||
import { getReferenceDataValueType } from '@/web/core/workflow/utils';
|
||||
import { isReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { inputs = [], nodeId } = data;
|
||||
@@ -39,6 +40,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||
|
||||
const updateList = useMemo(
|
||||
() =>
|
||||
@@ -85,6 +87,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
const valueType = getReferenceDataValueType({
|
||||
variable: updateItem.variable,
|
||||
nodeList,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
t
|
||||
});
|
||||
|
||||
|
||||
@@ -13,14 +13,20 @@ import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||
import { FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const { nodeId, outputs } = data;
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const variablesOutputs = useCreation(() => {
|
||||
const variables = getWorkflowGlobalVariables(nodeList, t);
|
||||
const variables = getWorkflowGlobalVariables({
|
||||
nodes: nodeList,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
t
|
||||
});
|
||||
|
||||
return variables.map<FlowNodeOutputItemType>((item) => ({
|
||||
id: item.key,
|
||||
|
||||
@@ -7,15 +7,21 @@ import { WorkflowContext } from '@/components/core/workflow/context';
|
||||
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||
import { useCreation } from 'ahooks';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const JsonEditor = ({ inputs = [], item, nodeId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
// get variable
|
||||
const variables = useCreation(() => {
|
||||
const globalVariables = getWorkflowGlobalVariables(nodeList, t);
|
||||
const globalVariables = getWorkflowGlobalVariables({
|
||||
nodes: nodeList,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
t
|
||||
});
|
||||
|
||||
const moduleVariables = formatEditorVariablePickerIcon(
|
||||
inputs
|
||||
|
||||
@@ -14,6 +14,7 @@ import dynamic from 'next/dynamic';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '@/components/core/workflow/context';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const MultipleRowSelect = dynamic(
|
||||
() => import('@fastgpt/web/components/common/MySelect/MultipleRowSelect')
|
||||
@@ -98,6 +99,7 @@ export const useReference = ({
|
||||
value?: any;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
|
||||
|
||||
@@ -106,6 +108,7 @@ export const useReference = ({
|
||||
nodeId,
|
||||
nodes: nodeList,
|
||||
edges: edges,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
t
|
||||
});
|
||||
|
||||
|
||||
@@ -5,11 +5,6 @@ import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
formatEditorVariablePickerIcon,
|
||||
getGuideModule,
|
||||
splitGuideModule
|
||||
} from '@fastgpt/global/core/workflow/utils';
|
||||
import { ModalBody } from '@chakra-ui/react';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import {
|
||||
@@ -22,12 +17,12 @@ import PromptTemplate from '@/components/PromptTemplate';
|
||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Reference from './Reference';
|
||||
import { getSystemVariables } from '@/web/core/app/utils';
|
||||
import ValueTypeLabel from '../../ValueTypeLabel';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '@/components/core/workflow/context';
|
||||
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||
import { useCreation } from 'ahooks';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const LabelStyles: BoxProps = {
|
||||
fontSize: ['sm', 'md']
|
||||
@@ -52,9 +47,14 @@ const SettingQuotePrompt = (props: RenderInputProps) => {
|
||||
});
|
||||
const aiChatQuoteTemplate = watch('quoteTemplate');
|
||||
const aiChatQuotePrompt = watch('quotePrompt');
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const variables = useCreation(() => {
|
||||
const globalVariables = getWorkflowGlobalVariables(nodeList, t);
|
||||
const globalVariables = getWorkflowGlobalVariables({
|
||||
nodes: nodeList,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
t
|
||||
});
|
||||
|
||||
return globalVariables;
|
||||
}, [nodeList, t]);
|
||||
|
||||
@@ -7,15 +7,21 @@ import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '@/components/core/workflow/context';
|
||||
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||
import { useCreation } from 'ahooks';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
// get variable
|
||||
const variables = useCreation(() => {
|
||||
const globalVariables = getWorkflowGlobalVariables(nodeList, t);
|
||||
const globalVariables = getWorkflowGlobalVariables({
|
||||
nodes: nodeList,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
t
|
||||
});
|
||||
|
||||
const moduleVariables = formatEditorVariablePickerIcon(
|
||||
inputs
|
||||
|
||||
@@ -8,7 +8,6 @@ import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../context';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
@@ -16,6 +15,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const PublishHistoriesSlider = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -23,7 +23,7 @@ const PublishHistoriesSlider = () => {
|
||||
content: t('core.workflow.publish.OnRevert version confirm')
|
||||
});
|
||||
|
||||
const { appDetail, setAppDetail } = useAppStore();
|
||||
const { appDetail, setAppDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const appId = useContextSelector(WorkflowContext, (e) => e.appId);
|
||||
const setIsShowVersionHistories = useContextSelector(
|
||||
WorkflowContext,
|
||||
@@ -73,11 +73,11 @@ const PublishHistoriesSlider = () => {
|
||||
editEdges: appDetail.edges
|
||||
});
|
||||
|
||||
setAppDetail({
|
||||
...appDetail,
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
modules: data.nodes,
|
||||
edges: data.edges
|
||||
});
|
||||
}));
|
||||
|
||||
onCloseSlider(data);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { RuntimeEdgeItemType, StoreEdgeItemType } from '@fastgpt/global/core/wor
|
||||
import { FlowNodeChangeProps } from '@fastgpt/global/core/workflow/type/fe';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useCreation, useMemoizedFn } from 'ahooks';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import React, {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
@@ -32,11 +32,13 @@ import {
|
||||
useEdgesState,
|
||||
useNodesState
|
||||
} from 'reactflow';
|
||||
import { createContext } from 'use-context-selector';
|
||||
import { createContext, useContextSelector } from 'use-context-selector';
|
||||
import { defaultRunningStatus } from './constants';
|
||||
import { checkNodeRunStatus } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
||||
|
||||
@@ -83,7 +85,11 @@ type WorkflowContextType = {
|
||||
toolInputs: FlowNodeInputItemType[];
|
||||
commonInputs: FlowNodeInputItemType[];
|
||||
};
|
||||
initData: (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => Promise<void>;
|
||||
initData: (e: {
|
||||
nodes: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
chatConfig?: AppChatConfigType;
|
||||
}) => Promise<void>;
|
||||
|
||||
// debug
|
||||
workflowDebugData:
|
||||
@@ -223,6 +229,7 @@ const WorkflowContextProvider = ({
|
||||
const { appId, pluginId } = value;
|
||||
const { toast } = useToast();
|
||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||
const setAppDetail = useContextSelector(AppContext, (v) => v.setAppDetail);
|
||||
|
||||
/* edge */
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||
@@ -426,12 +433,18 @@ const WorkflowContextProvider = ({
|
||||
};
|
||||
};
|
||||
|
||||
const initData = useMemoizedFn(
|
||||
async (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => {
|
||||
setNodes(e.nodes?.map((item) => storeNode2FlowNode({ item })) || []);
|
||||
setEdges(e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || []);
|
||||
const initData = useMemoizedFn(async (e: Parameters<WorkflowContextType['initData']>[0]) => {
|
||||
setNodes(e.nodes?.map((item) => storeNode2FlowNode({ item })) || []);
|
||||
setEdges(e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || []);
|
||||
|
||||
const chatConfig = e.chatConfig;
|
||||
if (chatConfig) {
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig
|
||||
}));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/* debug */
|
||||
const [workflowDebugData, setWorkflowDebugData] = useState<DebugDataType>();
|
||||
|
||||
@@ -4,7 +4,7 @@ import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { FlowNodeItemType, StoreNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { type Node, type Edge } from 'reactflow';
|
||||
|
||||
export const flowNode2StoreNodes = ({
|
||||
export const uiWorkflow2StoreWorkflow = ({
|
||||
nodes,
|
||||
edges
|
||||
}: {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { TeamContext } from '.';
|
||||
import {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
TeamMemberStatusMap
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { TeamContext } from '.';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
|
||||
2
projects/app/src/global/core/app/api.d.ts
vendored
2
projects/app/src/global/core/app/api.d.ts
vendored
@@ -16,6 +16,7 @@ export type AppUpdateParams = {
|
||||
intro?: string;
|
||||
nodes?: AppSchema['modules'];
|
||||
edges?: AppSchema['edges'];
|
||||
chatConfig?: AppSchema['chatConfig'];
|
||||
permission?: AppSchema['permission'];
|
||||
teamTags?: AppSchema['teamTags'];
|
||||
};
|
||||
@@ -24,6 +25,7 @@ export type PostPublishAppProps = {
|
||||
type: `${AppTypeEnum}`;
|
||||
nodes: AppSchema['modules'];
|
||||
edges: AppSchema['edges'];
|
||||
chatConfig: AppSchema['chatConfig'];
|
||||
};
|
||||
|
||||
export type PostRevertAppProps = {
|
||||
|
||||
4
projects/app/src/global/core/chat/api.d.ts
vendored
4
projects/app/src/global/core/chat/api.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
import type { AppTTSConfigType } from '@fastgpt/global/core/app/type.d';
|
||||
import type { AppChatConfigType, AppTTSConfigType } from '@fastgpt/global/core/app/type.d';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { AdminFbkType, ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat.d';
|
||||
@@ -34,7 +34,7 @@ export type InitChatResponse = {
|
||||
variables: Record<string, any>;
|
||||
history: ChatItemType[];
|
||||
app: {
|
||||
userGuideModule?: StoreNodeItemType;
|
||||
chatConfig?: AppChatConfigType;
|
||||
chatModels?: string[];
|
||||
name: string;
|
||||
avatar: string;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
|
||||
@@ -5,9 +5,9 @@ import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { MongoAppQGuide } from '@fastgpt/service/core/app/qGuideSchema';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { appId } = req.query as { appId: string };
|
||||
@@ -47,7 +47,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
await MongoAppQGuide.deleteMany(
|
||||
await MongoChatInputGuide.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { MongoAppQGuide } from '@fastgpt/service/core/app/qGuideSchema';
|
||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||
import axios from 'axios';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { textList = [], appId, customURL } = req.body;
|
||||
const { textList = [], appId, customUrl } = req.body;
|
||||
|
||||
if (!customURL) {
|
||||
if (!customUrl) {
|
||||
const { teamId } = await authUserNotVisitor({ req, authToken: true });
|
||||
|
||||
const currentQGuide = await MongoAppQGuide.find({ appId, teamId });
|
||||
const currentQGuide = await MongoChatInputGuide.find({ appId, teamId });
|
||||
const currentTexts = currentQGuide.map((item) => item.text);
|
||||
const textsToDelete = currentTexts.filter((text) => !textList.includes(text));
|
||||
|
||||
await MongoAppQGuide.deleteMany({ text: { $in: textsToDelete }, appId, teamId });
|
||||
await MongoChatInputGuide.deleteMany({ text: { $in: textsToDelete }, appId, teamId });
|
||||
|
||||
const newTexts = textList.filter((text: string) => !currentTexts.includes(text));
|
||||
|
||||
@@ -24,10 +24,10 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
teamId: teamId
|
||||
}));
|
||||
|
||||
await MongoAppQGuide.insertMany(newDocuments);
|
||||
await MongoChatInputGuide.insertMany(newDocuments);
|
||||
} else {
|
||||
try {
|
||||
const response = await axios.post(customURL, {
|
||||
const response = await axios.post(customUrl, {
|
||||
textList,
|
||||
appId
|
||||
});
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { MongoAppQGuide } from '@fastgpt/service/core/app/qGuideSchema';
|
||||
import axios from 'axios';
|
||||
import { PaginationProps } from '@fastgpt/web/common/fetch/type';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
|
||||
type Props = PaginationProps<{
|
||||
appId: string;
|
||||
customURL: string;
|
||||
searchKey: string;
|
||||
}>;
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { appId, customURL, current, pageSize, searchKey } = req.query as unknown as Props;
|
||||
|
||||
if (!customURL) {
|
||||
const [result, total] = await Promise.all([
|
||||
MongoAppQGuide.find({
|
||||
appId,
|
||||
...(searchKey && { text: { $regex: new RegExp(searchKey, 'i') } })
|
||||
})
|
||||
.sort({
|
||||
time: -1
|
||||
})
|
||||
.skip((current - 1) * pageSize)
|
||||
.limit(pageSize),
|
||||
MongoAppQGuide.countDocuments({ appId })
|
||||
]);
|
||||
|
||||
return {
|
||||
list: result.map((item) => item.text) || [],
|
||||
total
|
||||
};
|
||||
} else {
|
||||
try {
|
||||
const response = await axios.get(customURL as string, {
|
||||
params: {
|
||||
appid: appId
|
||||
}
|
||||
});
|
||||
res.status(200).json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
@@ -7,7 +7,7 @@ import { NextAPI } from '@/service/middleware/entry';
|
||||
|
||||
/* 获取我的模型 */
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { name, avatar, type, intro, nodes, edges, permission, teamTags } =
|
||||
const { name, avatar, type, intro, nodes, edges, chatConfig, permission, teamTags } =
|
||||
req.body as AppUpdateParams;
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
@@ -39,7 +39,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
}),
|
||||
...(edges && {
|
||||
edges
|
||||
})
|
||||
}),
|
||||
...(chatConfig && { chatConfig })
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
|
||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { beforeUpdateAppFormat } from '@fastgpt/service/core/app/controller';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
|
||||
import { PostPublishAppProps } from '@/global/core/app/api';
|
||||
|
||||
@@ -13,14 +12,12 @@ type Response = {};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<{}> {
|
||||
const { appId } = req.query as { appId: string };
|
||||
const { nodes = [], edges = [], type } = req.body as PostPublishAppProps;
|
||||
const { nodes = [], edges = [], chatConfig, type } = req.body as PostPublishAppProps;
|
||||
|
||||
await authApp({ appId, req, per: 'w', authToken: true });
|
||||
|
||||
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
|
||||
|
||||
const { scheduledTriggerConfig } = splitGuideModule(getGuideModule(formatNodes || []));
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
// create version histories
|
||||
await MongoAppVersion.create(
|
||||
@@ -28,7 +25,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<
|
||||
{
|
||||
appId,
|
||||
nodes: formatNodes,
|
||||
edges
|
||||
edges,
|
||||
chatConfig
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
@@ -38,12 +36,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<
|
||||
await MongoApp.findByIdAndUpdate(appId, {
|
||||
modules: formatNodes,
|
||||
edges,
|
||||
chatConfig,
|
||||
updateTime: new Date(),
|
||||
version: 'v2',
|
||||
type,
|
||||
scheduledTriggerConfig,
|
||||
scheduledTriggerNextTime: scheduledTriggerConfig
|
||||
? getNextTimeByCronStringAndTimezone(scheduledTriggerConfig)
|
||||
scheduledTriggerConfig: chatConfig?.scheduledTriggerConfig,
|
||||
scheduledTriggerNextTime: chatConfig?.scheduledTriggerConfig
|
||||
? getNextTimeByCronStringAndTimezone(chatConfig.scheduledTriggerConfig)
|
||||
: null
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { beforeUpdateAppFormat } from '@fastgpt/service/core/app/controller';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
|
||||
import { PostRevertAppProps } from '@/global/core/app/api';
|
||||
|
||||
@@ -28,7 +27,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<
|
||||
|
||||
const { nodes: formatEditNodes } = beforeUpdateAppFormat({ nodes: editNodes });
|
||||
|
||||
const { scheduledTriggerConfig } = splitGuideModule(getGuideModule(version.nodes));
|
||||
const scheduledTriggerConfig = version.chatConfig.scheduledTriggerConfig;
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
// 为编辑中的数据创建一个版本
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { getGuideModule, replaceAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getGuideModule, getAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/workflow';
|
||||
import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
@@ -61,10 +61,12 @@ async function handler(
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: replaceAppChatConfig({
|
||||
node: getGuideModule(nodes),
|
||||
variableList: chat?.variableList,
|
||||
welcomeText: chat?.welcomeText
|
||||
chatConfig: getAppChatConfig({
|
||||
chatConfig: app.chatConfig,
|
||||
systemConfigNode: getGuideModule(nodes),
|
||||
storeVariables: chat?.variableList,
|
||||
storeWelcomeText: chat?.welcomeText,
|
||||
isPublicFetch: false
|
||||
}),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
|
||||
export type countChatInputGuideTotalQuery = { appId: string };
|
||||
|
||||
export type countChatInputGuideTotalBody = {};
|
||||
|
||||
export type countChatInputGuideTotalResponse = { total: number };
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<countChatInputGuideTotalBody, countChatInputGuideTotalQuery>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<countChatInputGuideTotalResponse> {
|
||||
await authCert({ req, authToken: true });
|
||||
|
||||
const appId = req.query.appId;
|
||||
if (!appId) {
|
||||
return {
|
||||
total: 0
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
total: await MongoChatInputGuide.countDocuments({ appId })
|
||||
};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
45
projects/app/src/pages/api/core/chat/inputGuide/create.ts
Normal file
45
projects/app/src/pages/api/core/chat/inputGuide/create.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||
|
||||
export type createChatInputGuideQuery = {};
|
||||
|
||||
export type createInputGuideBody = {
|
||||
appId: string;
|
||||
textList: string[];
|
||||
};
|
||||
|
||||
export type createInputGuideResponse = {
|
||||
insertLength: number;
|
||||
};
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<createInputGuideBody, createChatInputGuideQuery>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<createInputGuideResponse> {
|
||||
const { appId, textList } = req.body;
|
||||
await authApp({ req, appId, authToken: true, per: 'r' });
|
||||
|
||||
try {
|
||||
const result = await MongoChatInputGuide.insertMany(
|
||||
textList.map((text) => ({
|
||||
appId,
|
||||
text
|
||||
})),
|
||||
{
|
||||
ordered: false
|
||||
}
|
||||
);
|
||||
return {
|
||||
insertLength: result.length
|
||||
};
|
||||
} catch (error: any) {
|
||||
const errLength = error.writeErrors?.length ?? textList.length;
|
||||
return {
|
||||
insertLength: textList.length - errLength
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
27
projects/app/src/pages/api/core/chat/inputGuide/delete.ts
Normal file
27
projects/app/src/pages/api/core/chat/inputGuide/delete.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||
|
||||
export type deleteChatInputGuideQuery = {};
|
||||
|
||||
export type deleteInputGuideBody = { appId: string; dataIdList: string[] };
|
||||
|
||||
export type deleteInputGuideResponse = {};
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<deleteInputGuideBody, deleteChatInputGuideQuery>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<deleteInputGuideResponse> {
|
||||
const { appId, dataIdList } = req.body;
|
||||
await authApp({ req, appId, authToken: true, per: 'r' });
|
||||
console.log(dataIdList);
|
||||
await MongoChatInputGuide.deleteMany({
|
||||
_id: { $in: dataIdList },
|
||||
appId
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
42
projects/app/src/pages/api/core/chat/inputGuide/list.ts
Normal file
42
projects/app/src/pages/api/core/chat/inputGuide/list.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { NextApiResponse } from 'next';
|
||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
|
||||
export type ChatInputGuideProps = PaginationProps<{
|
||||
appId: string;
|
||||
searchKey: string;
|
||||
}>;
|
||||
export type ChatInputGuideResponse = PaginationResponse<ChatInputGuideSchemaType>;
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<{}, ChatInputGuideProps>,
|
||||
res: NextApiResponse<any>
|
||||
): Promise<ChatInputGuideResponse> {
|
||||
const { appId, pageSize, current, searchKey } = req.query;
|
||||
|
||||
await authApp({ req, appId, authToken: true, per: 'r' });
|
||||
|
||||
const params = {
|
||||
appId,
|
||||
...(searchKey && { text: { $regex: new RegExp(searchKey, 'i') } })
|
||||
};
|
||||
|
||||
const [result, total] = await Promise.all([
|
||||
MongoChatInputGuide.find(params)
|
||||
.sort({ _id: -1 })
|
||||
.skip(pageSize * (current - 1))
|
||||
.limit(pageSize),
|
||||
MongoChatInputGuide.countDocuments(params)
|
||||
]);
|
||||
|
||||
return {
|
||||
list: result,
|
||||
total
|
||||
};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
34
projects/app/src/pages/api/core/chat/inputGuide/query.ts
Normal file
34
projects/app/src/pages/api/core/chat/inputGuide/query.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { NextApiResponse } from 'next';
|
||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
|
||||
export type QueryChatInputGuideProps = {
|
||||
appId: string;
|
||||
searchKey: string;
|
||||
};
|
||||
export type QueryChatInputGuideResponse = string[];
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<{}, QueryChatInputGuideProps>,
|
||||
res: NextApiResponse<any>
|
||||
): Promise<QueryChatInputGuideResponse> {
|
||||
const { appId, searchKey } = req.query;
|
||||
|
||||
await authApp({ req, appId, authToken: true, authApiKey: true, per: 'r' });
|
||||
|
||||
const params = {
|
||||
appId,
|
||||
...(searchKey && { text: { $regex: new RegExp(searchKey, 'i') } })
|
||||
};
|
||||
|
||||
const result = await MongoChatInputGuide.find(params).sort({ _id: -1 }).limit(6);
|
||||
|
||||
return result
|
||||
.map((item) => item.text)
|
||||
.filter(Boolean)
|
||||
.filter((item) => item !== searchKey);
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
36
projects/app/src/pages/api/core/chat/inputGuide/update.ts
Normal file
36
projects/app/src/pages/api/core/chat/inputGuide/update.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||
|
||||
export type updateChatInputGuideQuery = {};
|
||||
|
||||
export type updateInputGuideBody = {
|
||||
appId: string;
|
||||
dataId: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type updateInputGuideResponse = {};
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<updateInputGuideBody, updateChatInputGuideQuery>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<updateInputGuideResponse> {
|
||||
const { appId, dataId, text } = req.body;
|
||||
await authApp({ req, appId, authToken: true, per: 'r' });
|
||||
|
||||
await MongoChatInputGuide.findOneAndUpdate(
|
||||
{
|
||||
_id: dataId,
|
||||
appId
|
||||
},
|
||||
{
|
||||
text
|
||||
}
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import type { InitChatResponse, InitOutLinkChatProps } from '@/global/core/chat/api.d';
|
||||
import { getGuideModule, replaceAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getGuideModule, getAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/workflow';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
@@ -72,10 +72,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: replaceAppChatConfig({
|
||||
node: getGuideModule(nodes),
|
||||
variableList: chat?.variableList,
|
||||
welcomeText: chat?.welcomeText
|
||||
chatConfig: getAppChatConfig({
|
||||
chatConfig: app.chatConfig,
|
||||
systemConfigNode: getGuideModule(nodes),
|
||||
storeVariables: chat?.variableList,
|
||||
storeWelcomeText: chat?.welcomeText,
|
||||
isPublicFetch: false
|
||||
}),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getGuideModule, replaceAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getGuideModule, getAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/workflow';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import type { InitChatResponse, InitTeamChatProps } from '@/global/core/chat/api.d';
|
||||
@@ -73,10 +73,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: replaceAppChatConfig({
|
||||
node: getGuideModule(nodes),
|
||||
variableList: chat?.variableList,
|
||||
welcomeText: chat?.welcomeText
|
||||
chatConfig: getAppChatConfig({
|
||||
chatConfig: app.chatConfig,
|
||||
systemConfigNode: getGuideModule(nodes),
|
||||
storeVariables: chat?.variableList,
|
||||
storeWelcomeText: chat?.welcomeText,
|
||||
isPublicFetch: false
|
||||
}),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { getGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/workflow';
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
let { appId, chatId, loadCustomFeedbacks } = req.query as InitChatProps;
|
||||
|
||||
if (!appId) {
|
||||
return jsonRes(res, {
|
||||
code: 501,
|
||||
message: "You don't have an app yet"
|
||||
});
|
||||
}
|
||||
|
||||
// auth app permission
|
||||
const [{ app, tmbId }, chat] = await Promise.all([
|
||||
authApp({
|
||||
req,
|
||||
authToken: true,
|
||||
appId,
|
||||
per: 'r'
|
||||
}),
|
||||
chatId ? MongoChat.findOne({ appId, chatId }) : undefined
|
||||
]);
|
||||
|
||||
// // auth chat permission
|
||||
// if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) {
|
||||
// throw new Error(ChatErrEnum.unAuthChat);
|
||||
// }
|
||||
|
||||
// get app and history
|
||||
const { history } = await getChatItems({
|
||||
appId,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${
|
||||
DispatchNodeResponseKeyEnum.nodeResponse
|
||||
} ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`
|
||||
});
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
data: {
|
||||
chatId,
|
||||
appId,
|
||||
title: chat?.title || '新对话',
|
||||
userAvatar: undefined,
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(app.modules),
|
||||
chatModels: getChatModelNameListByModules(app.modules),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
responseLimit: '10mb'
|
||||
}
|
||||
};
|
||||
@@ -26,7 +26,7 @@ async function handler(
|
||||
});
|
||||
|
||||
const [rebuildingCount, trainingCount] = await Promise.all([
|
||||
MongoDatasetData.countDocuments({ teamId, datasetId, rebuilding: true }),
|
||||
MongoDatasetData.countDocuments({ rebuilding: true, teamId, datasetId }),
|
||||
MongoDatasetTraining.countDocuments({ teamId, datasetId })
|
||||
]);
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import { getAIApi } from '@fastgpt/service/core/ai/config';
|
||||
import { pushWhisperUsage } from '@/service/support/wallet/usage/push';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
|
||||
@@ -47,14 +46,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
// auth role
|
||||
const { teamId, tmbId } = await authChatCert({ req, authToken: true });
|
||||
// auth app
|
||||
const app = await MongoApp.findById(appId, 'modules').lean();
|
||||
if (!app) {
|
||||
throw new Error('app not found');
|
||||
}
|
||||
const { whisperConfig } = splitGuideModule(getGuideModule(app?.modules));
|
||||
if (!whisperConfig?.open) {
|
||||
throw new Error('Whisper is not open in the app');
|
||||
}
|
||||
// const app = await MongoApp.findById(appId, 'modules').lean();
|
||||
// if (!app) {
|
||||
// throw new Error('app not found');
|
||||
// }
|
||||
// if (!whisperConfig?.open) {
|
||||
// throw new Error('Whisper is not open in the app');
|
||||
// }
|
||||
|
||||
const ai = getAIApi();
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
// 1. get and concat history; 2. get app workflow
|
||||
const limit = getMaxHistoryLimitFromNodes(app.modules);
|
||||
const [{ history }, { nodes, edges }] = await Promise.all([
|
||||
const [{ history }, { nodes, edges, chatConfig }] = await Promise.all([
|
||||
getChatItems({
|
||||
appId: app._id,
|
||||
chatId,
|
||||
@@ -249,6 +249,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
teamId,
|
||||
tmbId: tmbId,
|
||||
nodes,
|
||||
appChatConfig: chatConfig,
|
||||
variables: newVariables,
|
||||
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
|
||||
shareId,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure, Button } from '@chakra-ui/react';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { AppSchema } from '@fastgpt/global/core/app/type.d';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
@@ -9,8 +8,7 @@ import dynamic from 'next/dynamic';
|
||||
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import ChatTest, { type ChatTestComponentRef } from '@/components/core/workflow/Flow/ChatTest';
|
||||
import { flowNode2StoreNodes } from '@/components/core/workflow/utils';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { uiWorkflow2StoreWorkflow } from '@/components/core/workflow/utils';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
@@ -27,19 +25,16 @@ import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext, getWorkflowStore } from '@/components/core/workflow/context';
|
||||
import { useInterval, useUpdateEffect } from 'ahooks';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { importQuestionGuides } from '@/web/core/app/api';
|
||||
import { getAppQGuideCustomURL, getNodesWithNoQGuide } from '@/web/core/app/utils';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const ImportSettings = dynamic(() => import('@/components/core/workflow/Flow/ImportSettings'));
|
||||
const PublishHistories = dynamic(
|
||||
() => import('@/components/core/workflow/components/PublishHistoriesSlider')
|
||||
);
|
||||
|
||||
type Props = { app: AppSchema; onClose: () => void };
|
||||
type Props = { onClose: () => void };
|
||||
|
||||
const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
app,
|
||||
ChatTestRef,
|
||||
setWorkflowTestData,
|
||||
onClose
|
||||
@@ -55,7 +50,9 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
>
|
||||
>;
|
||||
}) {
|
||||
const isV2Workflow = app?.version === 'v2';
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const isV2Workflow = appDetail?.version === 'v2';
|
||||
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
@@ -66,7 +63,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
const { openConfirm: openConfigPublish, ConfirmModal } = useConfirm({
|
||||
content: t('core.app.Publish Confirm')
|
||||
});
|
||||
const { publishApp, updateAppDetail } = useAppStore();
|
||||
const { publishApp, updateAppDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
@@ -90,7 +87,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
const checkResults = checkWorkflowNodeAndConnection({ nodes, edges });
|
||||
|
||||
if (!checkResults) {
|
||||
const storeNodes = flowNode2StoreNodes({ nodes, edges });
|
||||
const storeNodes = uiWorkflow2StoreWorkflow({ nodes, edges });
|
||||
|
||||
return storeNodes;
|
||||
} else {
|
||||
@@ -112,12 +109,13 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
if (nodes.length === 0) return null;
|
||||
setIsSaving(true);
|
||||
|
||||
const storeWorkflow = flowNode2StoreNodes({ nodes, edges });
|
||||
const storeWorkflow = uiWorkflow2StoreWorkflow({ nodes, edges });
|
||||
|
||||
try {
|
||||
await updateAppDetail(app._id, {
|
||||
await updateAppDetail({
|
||||
...storeWorkflow,
|
||||
type: AppTypeEnum.advanced,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
//@ts-ignore
|
||||
version: 'v2'
|
||||
});
|
||||
@@ -134,7 +132,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
|
||||
return null;
|
||||
},
|
||||
[isV2Workflow, isShowVersionHistories, edges, updateAppDetail, app._id, t]
|
||||
[isV2Workflow, isShowVersionHistories, edges, updateAppDetail, appDetail.chatConfig, t]
|
||||
);
|
||||
|
||||
const onclickPublish = useCallback(async () => {
|
||||
@@ -142,19 +140,10 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
const data = await flowData2StoreDataAndCheck();
|
||||
if (data) {
|
||||
try {
|
||||
const { questionGuideText } = splitGuideModule(getGuideModule(data.nodes));
|
||||
await importQuestionGuides({
|
||||
appId: app._id,
|
||||
textList: questionGuideText.textList,
|
||||
customURL: getAppQGuideCustomURL(app)
|
||||
});
|
||||
|
||||
const newNodes = getNodesWithNoQGuide(data.nodes, questionGuideText);
|
||||
|
||||
await publishApp(app._id, {
|
||||
await publishApp({
|
||||
...data,
|
||||
nodes: newNodes,
|
||||
type: AppTypeEnum.advanced,
|
||||
chatConfig: appDetail.chatConfig,
|
||||
//@ts-ignore
|
||||
version: 'v2'
|
||||
});
|
||||
@@ -172,7 +161,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
}
|
||||
|
||||
setIsSaving(false);
|
||||
}, [flowData2StoreDataAndCheck, publishApp, app._id, toast, t, ChatTestRef]);
|
||||
}, [flowData2StoreDataAndCheck, publishApp, appDetail.chatConfig, toast, t, ChatTestRef]);
|
||||
|
||||
const saveAndBack = useCallback(async () => {
|
||||
try {
|
||||
@@ -188,7 +177,8 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
JSON.stringify(
|
||||
{
|
||||
nodes: filterSensitiveNodesData(data.nodes),
|
||||
edges: data.edges
|
||||
edges: data.edges,
|
||||
chatConfig: appDetail.chatConfig
|
||||
},
|
||||
null,
|
||||
2
|
||||
@@ -196,7 +186,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
appT('Export Config Successful')
|
||||
);
|
||||
}
|
||||
}, [appT, copyData, flowData2StoreDataAndCheck]);
|
||||
}, [appDetail.chatConfig, appT, copyData, flowData2StoreDataAndCheck]);
|
||||
|
||||
// effect
|
||||
useBeforeunload({
|
||||
@@ -205,7 +195,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
});
|
||||
|
||||
useInterval(() => {
|
||||
if (!app._id) return;
|
||||
if (!appDetail._id) return;
|
||||
onclickSave(!!workflowDebugData);
|
||||
}, 20000);
|
||||
|
||||
@@ -235,7 +225,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
/>
|
||||
<Box ml={[2, 4]}>
|
||||
<Box fontSize={['md', 'lg']} fontWeight={'bold'}>
|
||||
{app.name}
|
||||
{appDetail.name}
|
||||
</Box>
|
||||
{!isShowVersionHistories && isV2Workflow && (
|
||||
<MyTooltip label={t('core.app.Onclick to save')}>
|
||||
@@ -327,7 +317,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
theme.borders.base,
|
||||
isSaving,
|
||||
saveAndBack,
|
||||
app.name,
|
||||
appDetail.name,
|
||||
isShowVersionHistories,
|
||||
isV2Workflow,
|
||||
t,
|
||||
@@ -354,7 +344,6 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
});
|
||||
|
||||
const Header = (props: Props) => {
|
||||
const { app } = props;
|
||||
const ChatTestRef = useRef<ChatTestComponentRef>(null);
|
||||
|
||||
const [workflowTestData, setWorkflowTestData] = useState<{
|
||||
@@ -374,13 +363,7 @@ const Header = (props: Props) => {
|
||||
ChatTestRef={ChatTestRef}
|
||||
setWorkflowTestData={setWorkflowTestData}
|
||||
/>
|
||||
<ChatTest
|
||||
ref={ChatTestRef}
|
||||
isOpen={isOpenTest}
|
||||
{...workflowTestData}
|
||||
app={app}
|
||||
onClose={onCloseTest}
|
||||
/>
|
||||
<ChatTest ref={ChatTestRef} isOpen={isOpenTest} {...workflowTestData} onClose={onCloseTest} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,11 +7,15 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
|
||||
import WorkflowContextProvider, { WorkflowContext } from '@/components/core/workflow/context';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
import { useMount } from 'ahooks';
|
||||
|
||||
type Props = { app: AppSchema; onClose: () => void };
|
||||
type Props = { onClose: () => void };
|
||||
|
||||
const Render = ({ app, onClose }: Props) => {
|
||||
const isV2Workflow = app?.version === 'v2';
|
||||
const Render = ({ onClose }: Props) => {
|
||||
const appDetail = useContextSelector(AppContext, (e) => e.appDetail);
|
||||
|
||||
const isV2Workflow = appDetail?.version === 'v2';
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
showCancel: false,
|
||||
content:
|
||||
@@ -21,26 +25,23 @@ const Render = ({ app, onClose }: Props) => {
|
||||
const initData = useContextSelector(WorkflowContext, (v) => v.initData);
|
||||
|
||||
const workflowStringData = JSON.stringify({
|
||||
nodes: app.modules || [],
|
||||
edges: app.edges || []
|
||||
nodes: appDetail.modules || [],
|
||||
edges: appDetail.edges || []
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isV2Workflow) return;
|
||||
initData(JSON.parse(workflowStringData));
|
||||
}, [isV2Workflow, initData, app._id, workflowStringData]);
|
||||
|
||||
useEffect(() => {
|
||||
useMount(() => {
|
||||
if (!isV2Workflow) {
|
||||
openConfirm(() => {
|
||||
initData(JSON.parse(JSON.stringify(v1Workflow2V2((app.modules || []) as any))));
|
||||
initData(JSON.parse(JSON.stringify(v1Workflow2V2((appDetail.modules || []) as any))));
|
||||
})();
|
||||
} else {
|
||||
initData(JSON.parse(workflowStringData));
|
||||
}
|
||||
}, [app.modules, initData, isV2Workflow, openConfirm]);
|
||||
});
|
||||
|
||||
const memoRender = useMemo(() => {
|
||||
return <Flow Header={<Header app={app} onClose={onClose} />} />;
|
||||
}, [app, onClose]);
|
||||
return <Flow Header={<Header onClose={onClose} />} />;
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -51,12 +52,13 @@ const Render = ({ app, onClose }: Props) => {
|
||||
};
|
||||
|
||||
export default React.memo(function FlowEdit(props: Props) {
|
||||
const filterAppIds = useMemo(() => [props.app._id], [props.app._id]);
|
||||
const appDetail = useContextSelector(AppContext, (e) => e.appDetail);
|
||||
const filterAppIds = useMemo(() => [appDetail._id], [appDetail._id]);
|
||||
|
||||
return (
|
||||
<WorkflowContextProvider
|
||||
value={{
|
||||
appId: props.app._id,
|
||||
appId: appDetail._id,
|
||||
mode: 'app',
|
||||
filterAppIds,
|
||||
basicNodeTemplates: appSystemModuleTemplates
|
||||
|
||||
@@ -19,10 +19,11 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import PermissionRadio from '@/components/support/permission/Radio';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
|
||||
const InfoModal = ({
|
||||
defaultApp,
|
||||
@@ -35,7 +36,7 @@ const InfoModal = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { updateAppDetail } = useAppStore();
|
||||
const { updateAppDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
@@ -55,7 +56,7 @@ const InfoModal = ({
|
||||
// submit config
|
||||
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
|
||||
mutationFn: async (data: AppSchema) => {
|
||||
await updateAppDetail(data._id, {
|
||||
await updateAppDetail({
|
||||
name: data.name,
|
||||
avatar: data.avatar,
|
||||
intro: data.intro,
|
||||
|
||||
@@ -335,7 +335,7 @@ const DetailLogsModal = ({
|
||||
feedbackType={'admin'}
|
||||
showMarkIcon
|
||||
showVoiceIcon={false}
|
||||
userGuideModule={chat?.app?.userGuideModule}
|
||||
chatConfig={chat?.app?.chatConfig}
|
||||
appId={appId}
|
||||
chatId={chatId}
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { AppSchema } from '@fastgpt/global/core/app/type.d';
|
||||
import { delModelById } from '@/web/core/app/api';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import PermissionIconText from '@/components/support/permission/IconText';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Avatar from '@/components/Avatar';
|
||||
@@ -16,6 +15,8 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import TagsEditModal from './TagsEditModal';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
const InfoModal = dynamic(() => import('../InfoModal'));
|
||||
|
||||
const AppCard = ({ appId }: { appId: string }) => {
|
||||
@@ -24,7 +25,7 @@ const AppCard = ({ appId }: { appId: string }) => {
|
||||
const { appT } = useI18n();
|
||||
|
||||
const { toast } = useToast();
|
||||
const { appDetail } = useAppStore();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const { feConfigs } = useSystemStore();
|
||||
const [settingAppInfo, setSettingAppInfo] = useState<AppSchema>();
|
||||
const [TeamTagsSet, setTeamTagsSet] = useState<AppSchema>();
|
||||
|
||||
@@ -16,12 +16,13 @@ import {
|
||||
initWorkflowEdgeStatus,
|
||||
storeNodes2RuntimeNodes
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { useCreation, useMemoizedFn, useSafeState } from 'ahooks';
|
||||
import { useMemoizedFn, useSafeState } from 'ahooks';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { form2AppWorkflow } from '@/web/core/app/utils';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const ChatTest = ({
|
||||
editForm,
|
||||
@@ -35,18 +36,15 @@ const ChatTest = ({
|
||||
|
||||
const { userInfo } = useUserStore();
|
||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||
const { appDetail } = useAppStore();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const { watch } = editForm;
|
||||
const chatConfig = watch('chatConfig');
|
||||
|
||||
const [workflowData, setWorkflowData] = useSafeState({
|
||||
nodes: appDetail.modules || [],
|
||||
edges: appDetail.edges || []
|
||||
});
|
||||
const userGuideModule = useCreation(
|
||||
() => getGuideModule(workflowData.nodes),
|
||||
[workflowData.nodes]
|
||||
);
|
||||
|
||||
const startChat = useMemoizedFn(
|
||||
async ({ chatList, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||
@@ -131,7 +129,7 @@ const ChatTest = ({
|
||||
appAvatar={appDetail.avatar}
|
||||
userAvatar={userInfo?.avatar}
|
||||
showMarkIcon
|
||||
userGuideModule={userGuideModule}
|
||||
chatConfig={chatConfig}
|
||||
showFileSelector={checkChatSupportSelectFileByModules(workflowData.nodes)}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={() => {}}
|
||||
|
||||
@@ -11,12 +11,7 @@ import { useRouter } from 'next/router';
|
||||
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 {
|
||||
form2AppWorkflow,
|
||||
getAppQGuideCustomURL,
|
||||
getNodesWithNoQGuide
|
||||
} from '@/web/core/app/utils';
|
||||
import { form2AppWorkflow } from '@/web/core/app/utils';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
@@ -34,15 +29,27 @@ import { TTSTypeEnum } from '@/web/core/app/constants';
|
||||
import { getSystemVariables } from '@/web/core/app/utils';
|
||||
import { useUpdate } from 'ahooks';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { importQuestionGuides } from '@/web/core/app/api';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
|
||||
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
|
||||
const ToolSelectModal = dynamic(() => import('./ToolSelectModal'));
|
||||
const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect'));
|
||||
const QGSwitch = dynamic(() => import('@/components/core/app/QGSwitch'));
|
||||
const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig'));
|
||||
const QGuidesConfigModal = dynamic(() => import('@/components/core/app/QGuidesConfig'));
|
||||
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'), {
|
||||
ssr: false
|
||||
});
|
||||
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'), {
|
||||
ssr: false
|
||||
});
|
||||
const ToolSelectModal = dynamic(() => import('./ToolSelectModal'), { ssr: false });
|
||||
const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect'), { ssr: false });
|
||||
const QGSwitch = dynamic(() => import('@/components/core/app/QGSwitch'), { ssr: false });
|
||||
const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig'), { ssr: false });
|
||||
const InputGuideConfig = dynamic(
|
||||
() => import('@/components/core/chat/appConfig/InputGuideConfig'),
|
||||
{ ssr: false }
|
||||
);
|
||||
const ScheduledTriggerConfig = dynamic(
|
||||
() => import('@/components/core/app/ScheduledTriggerConfig'),
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
const BoxStyles: BoxProps = {
|
||||
px: 5,
|
||||
@@ -70,7 +77,7 @@ const EditForm = ({
|
||||
const { t } = useTranslation();
|
||||
const { appT } = useI18n();
|
||||
|
||||
const { appDetail, publishApp } = useAppStore();
|
||||
const { appDetail, publishApp } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const { allDatasets } = useDatasetStore();
|
||||
const { llmModelList } = useSystemStore();
|
||||
@@ -107,18 +114,19 @@ const EditForm = ({
|
||||
const aiSystemPrompt = watch('aiSettings.systemPrompt');
|
||||
const selectLLMModel = watch('aiSettings.model');
|
||||
const datasetSearchSetting = watch('dataset');
|
||||
const variables = watch('userGuide.variables');
|
||||
const variables = watch('chatConfig.variables');
|
||||
|
||||
const formatVariables: any = useMemo(
|
||||
() => formatEditorVariablePickerIcon([...getSystemVariables(t), ...variables]),
|
||||
() => formatEditorVariablePickerIcon([...getSystemVariables(t), ...(variables || [])]),
|
||||
[t, variables]
|
||||
);
|
||||
const searchMode = watch('dataset.searchMode');
|
||||
const tts = getValues('userGuide.tts');
|
||||
const whisperConfig = getValues('userGuide.whisper');
|
||||
const postQuestionGuide = getValues('userGuide.questionGuide');
|
||||
const tts = getValues('chatConfig.ttsConfig');
|
||||
const whisperConfig = getValues('chatConfig.whisperConfig');
|
||||
const postQuestionGuide = getValues('chatConfig.questionGuide');
|
||||
const selectedTools = watch('selectedTools');
|
||||
const QGuidesConfig = watch('userGuide.questionGuideText');
|
||||
const inputGuideConfig = watch('chatConfig.chatInputGuide');
|
||||
const scheduledTriggerConfig = watch('chatConfig.scheduledTriggerConfig');
|
||||
const searchMode = watch('dataset.searchMode');
|
||||
|
||||
const selectDatasets = useMemo(
|
||||
() => allDatasets.filter((item) => datasets.find((dataset) => dataset.datasetId === item._id)),
|
||||
@@ -132,20 +140,11 @@ const EditForm = ({
|
||||
/* on save app */
|
||||
const { mutate: onSubmitPublish, isLoading: isSaving } = useRequest({
|
||||
mutationFn: async (data: AppSimpleEditFormType) => {
|
||||
const questionGuideText = data.userGuide.questionGuideText;
|
||||
await importQuestionGuides({
|
||||
appId: appDetail._id,
|
||||
textList: questionGuideText.textList,
|
||||
customURL: getAppQGuideCustomURL(appDetail)
|
||||
});
|
||||
|
||||
const { nodes, edges } = form2AppWorkflow(data);
|
||||
|
||||
const newNodes = getNodesWithNoQGuide(nodes, questionGuideText);
|
||||
|
||||
await publishApp(appDetail._id, {
|
||||
nodes: newNodes,
|
||||
await publishApp({
|
||||
nodes,
|
||||
edges,
|
||||
chatConfig: data.chatConfig,
|
||||
type: AppTypeEnum.simple
|
||||
});
|
||||
},
|
||||
@@ -403,7 +402,7 @@ const EditForm = ({
|
||||
<VariableEdit
|
||||
variables={variables}
|
||||
onChange={(e) => {
|
||||
setValue('userGuide.variables', e);
|
||||
setValue('chatConfig.variables', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -422,9 +421,9 @@ const EditForm = ({
|
||||
bg={'myWhite.400'}
|
||||
rows={5}
|
||||
placeholder={t(welcomeTextTip)}
|
||||
defaultValue={getValues('userGuide.welcomeText')}
|
||||
defaultValue={getValues('chatConfig.welcomeText')}
|
||||
onBlur={(e) => {
|
||||
setValue('userGuide.welcomeText', e.target.value || '');
|
||||
setValue('chatConfig.welcomeText', e.target.value || '');
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -434,7 +433,7 @@ const EditForm = ({
|
||||
<TTSSelect
|
||||
value={tts}
|
||||
onChange={(e) => {
|
||||
setValue('userGuide.tts', e);
|
||||
setValue('chatConfig.ttsConfig', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -442,10 +441,10 @@ const EditForm = ({
|
||||
{/* whisper */}
|
||||
<Box {...BoxStyles}>
|
||||
<WhisperConfig
|
||||
isOpenAudio={tts.type !== TTSTypeEnum.none}
|
||||
isOpenAudio={tts?.type !== TTSTypeEnum.none}
|
||||
value={whisperConfig}
|
||||
onChange={(e) => {
|
||||
setValue('userGuide.whisper', e);
|
||||
setValue('chatConfig.whisperConfig', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -456,17 +455,28 @@ const EditForm = ({
|
||||
isChecked={postQuestionGuide}
|
||||
size={'lg'}
|
||||
onChange={(e) => {
|
||||
setValue('userGuide.questionGuide', e.target.checked);
|
||||
setValue('chatConfig.questionGuide', e.target.checked);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* question tips */}
|
||||
<Box {...BoxStyles} borderBottom={'none'}>
|
||||
<QGuidesConfigModal
|
||||
value={QGuidesConfig}
|
||||
<Box {...BoxStyles}>
|
||||
<InputGuideConfig
|
||||
appId={appDetail._id}
|
||||
value={inputGuideConfig}
|
||||
onChange={(e) => {
|
||||
setValue('userGuide.questionGuideText', e);
|
||||
setValue('chatConfig.chatInputGuide', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* timer trigger */}
|
||||
<Box {...BoxStyles} borderBottom={'none'}>
|
||||
<ScheduledTriggerConfig
|
||||
value={scheduledTriggerConfig}
|
||||
onChange={(e) => {
|
||||
setValue('chatConfig.scheduledTriggerConfig', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -19,22 +19,22 @@ import {
|
||||
TagLabel
|
||||
} from '@chakra-ui/react';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { getTeamsTags } from '@/web/support/user/team/api';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
|
||||
const TagsEditModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { appDetail } = useAppStore();
|
||||
const { toast } = useToast();
|
||||
const { updateAppDetail } = useAppStore();
|
||||
const { appDetail, updateAppDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>(appDetail?.teamTags || []);
|
||||
|
||||
// submit config
|
||||
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
await updateAppDetail(appDetail._id, {
|
||||
await updateAppDetail({
|
||||
teamTags: selectedTags
|
||||
});
|
||||
},
|
||||
|
||||
@@ -4,36 +4,42 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useSticky } from '@/web/common/hooks/useSticky';
|
||||
import { useMount } from 'ahooks';
|
||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { appWorkflow2Form } from '@fastgpt/global/core/app/utils';
|
||||
import { appWorkflow2Form, getDefaultAppForm } from '@fastgpt/global/core/app/utils';
|
||||
|
||||
import ChatTest from './ChatTest';
|
||||
import AppCard from './AppCard';
|
||||
import EditForm from './EditForm';
|
||||
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
|
||||
import { AppContext } from '@/web/core/app/context/appContext';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
|
||||
const SimpleEdit = ({ appId }: { appId: string }) => {
|
||||
const { isPc } = useSystemStore();
|
||||
const { parentRef, divRef, isSticky } = useSticky();
|
||||
const { loadAllDatasets } = useDatasetStore();
|
||||
const { appDetail } = useAppStore();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const editForm = useForm<AppSimpleEditFormType>({
|
||||
defaultValues: appWorkflow2Form({
|
||||
nodes: appDetail.modules
|
||||
})
|
||||
defaultValues: getDefaultAppForm()
|
||||
});
|
||||
|
||||
// show selected dataset
|
||||
useMount(() => {
|
||||
loadAllDatasets();
|
||||
editForm.reset(
|
||||
appWorkflow2Form({
|
||||
nodes: appDetail.modules,
|
||||
chatConfig: appDetail.chatConfig
|
||||
})
|
||||
);
|
||||
|
||||
if (appDetail.version !== 'v2') {
|
||||
editForm.reset(
|
||||
appWorkflow2Form({
|
||||
nodes: v1Workflow2V2((appDetail.modules || []) as any)?.nodes
|
||||
nodes: v1Workflow2V2((appDetail.modules || []) as any)?.nodes,
|
||||
chatConfig: appDetail.chatConfig
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import React, { useEffect, useMemo, useCallback } from 'react';
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Box, Flex, IconButton, useTheme } from '@chakra-ui/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
import Tabs from '@/components/Tabs';
|
||||
@@ -14,11 +12,11 @@ import PageContainer from '@/components/PageContainer';
|
||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||
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';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
|
||||
import { AppContext, AppContextProvider } from '@/web/core/app/context/appContext';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
|
||||
const FlowEdit = dynamic(() => import('./components/FlowEdit'), {
|
||||
loading: () => <Loading />
|
||||
@@ -34,19 +32,16 @@ enum TabEnum {
|
||||
'startChat' = 'startChat'
|
||||
}
|
||||
|
||||
const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
const AppDetail = ({ appId, currentTab }: { appId: string; currentTab: TabEnum }) => {
|
||||
const { t } = useTranslation();
|
||||
const { appT } = useI18n();
|
||||
|
||||
const router = useRouter();
|
||||
const theme = useTheme();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { toast } = useToast();
|
||||
const { appId } = router.query as { appId: string };
|
||||
const { appDetail, loadAppDetail } = useAppStore();
|
||||
const { appDetail, loadingApp } = useContextSelector(AppContext, (e) => e);
|
||||
|
||||
const setCurrentTab = useCallback(
|
||||
(tab: `${TabEnum}`) => {
|
||||
(tab: TabEnum) => {
|
||||
router.push({
|
||||
query: {
|
||||
...router.query,
|
||||
@@ -86,26 +81,13 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
|
||||
const onCloseFlowEdit = useCallback(() => setCurrentTab(TabEnum.simpleEdit), [setCurrentTab]);
|
||||
|
||||
const { isSuccess, isLoading } = useQuery([appId], () => loadAppDetail(appId, true), {
|
||||
onError(err: any) {
|
||||
toast({
|
||||
title: err?.message || t('core.app.error.Get app failed'),
|
||||
status: 'error'
|
||||
});
|
||||
router.replace('/app/list');
|
||||
},
|
||||
onSettled() {
|
||||
router.prefetch(`/chat?appId=${appId}`);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{appDetail.name}</title>
|
||||
</Head>
|
||||
<PageContainer isLoading={isLoading}>
|
||||
{isSuccess && (
|
||||
<PageContainer isLoading={loadingApp}>
|
||||
{!loadingApp && (
|
||||
<Flex flexDirection={['column', 'row']} h={'100%'}>
|
||||
{/* pc tab */}
|
||||
<Box
|
||||
@@ -180,9 +162,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
</Box>
|
||||
<Box flex={'1 0 0'} h={[0, '100%']} overflow={['overlay', '']}>
|
||||
{currentTab === TabEnum.simpleEdit && <SimpleEdit appId={appId} />}
|
||||
{currentTab === TabEnum.adEdit && appDetail && (
|
||||
<FlowEdit app={appDetail} onClose={onCloseFlowEdit} />
|
||||
)}
|
||||
{currentTab === TabEnum.adEdit && appDetail && <FlowEdit onClose={onCloseFlowEdit} />}
|
||||
{currentTab === TabEnum.logs && <Logs appId={appId} />}
|
||||
{currentTab === TabEnum.publish && <Publish appId={appId} />}
|
||||
</Box>
|
||||
@@ -193,15 +173,25 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Provider = ({ appId, currentTab }: { appId: string; currentTab: TabEnum }) => {
|
||||
return (
|
||||
<AppContextProvider appId={appId}>
|
||||
<AppDetail appId={appId} currentTab={currentTab} />
|
||||
</AppContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
const currentTab = context?.query?.currentTab || TabEnum.simpleEdit;
|
||||
const appId = context?.query?.appId || '';
|
||||
|
||||
return {
|
||||
props: {
|
||||
currentTab,
|
||||
...(await serviceSideProps(context, ['app', 'file', 'publish', 'workflow']))
|
||||
appId,
|
||||
...(await serviceSideProps(context, ['app', 'chat', 'file', 'publish', 'workflow']))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default AppDetail;
|
||||
export default Provider;
|
||||
|
||||
@@ -45,7 +45,7 @@ const MyApps = () => {
|
||||
title: '删除成功',
|
||||
status: 'success'
|
||||
});
|
||||
loadMyApps(true);
|
||||
loadMyApps();
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: err?.message || t('common.Delete Failed'),
|
||||
@@ -57,7 +57,7 @@ const MyApps = () => {
|
||||
);
|
||||
|
||||
/* 加载模型 */
|
||||
const { isFetching } = useQuery(['loadApps'], () => loadMyApps(true), {
|
||||
const { isFetching } = useQuery(['loadApps'], () => loadMyApps(), {
|
||||
refetchOnMount: true
|
||||
});
|
||||
|
||||
@@ -182,7 +182,7 @@ const MyApps = () => {
|
||||
)}
|
||||
<ConfirmModal />
|
||||
{isOpenCreateModal && (
|
||||
<CreateModal onClose={onCloseCreateModal} onSuccess={() => loadMyApps(true)} />
|
||||
<CreateModal onClose={onCloseCreateModal} onSuccess={() => loadMyApps()} />
|
||||
)}
|
||||
</PageContainer>
|
||||
);
|
||||
|
||||
@@ -33,15 +33,10 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import {
|
||||
checkChatSupportSelectFileByChatModels,
|
||||
getAppQuestionGuidesByUserGuideModule
|
||||
} from '@/web/core/chat/utils';
|
||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
|
||||
|
||||
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
const router = useRouter();
|
||||
@@ -133,7 +128,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
||||
);
|
||||
|
||||
useQuery(['loadModels'], () => loadMyApps(false));
|
||||
useQuery(['loadModels'], () => loadMyApps());
|
||||
|
||||
// get chat app info
|
||||
const loadChatInfo = useCallback(
|
||||
@@ -354,7 +349,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
showEmptyIntro
|
||||
appAvatar={chatData.app.avatar}
|
||||
userAvatar={userInfo?.avatar}
|
||||
userGuideModule={chatData.app?.userGuideModule}
|
||||
chatConfig={chatData.app?.chatConfig}
|
||||
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
|
||||
feedbackType={'user'}
|
||||
onStartChat={startChat}
|
||||
|
||||
@@ -20,10 +20,7 @@ import PageContainer from '@/components/PageContainer';
|
||||
import ChatHeader from './components/ChatHeader';
|
||||
import ChatHistorySlider from './components/ChatHistorySlider';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import {
|
||||
checkChatSupportSelectFileByChatModels,
|
||||
getAppQuestionGuidesByUserGuideModule
|
||||
} from '@/web/core/chat/utils';
|
||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||
@@ -34,9 +31,6 @@ import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
|
||||
|
||||
const OutLink = ({
|
||||
appName,
|
||||
@@ -393,7 +387,7 @@ const OutLink = ({
|
||||
ref={ChatBoxRef}
|
||||
appAvatar={chatData.app.avatar}
|
||||
userAvatar={chatData.userAvatar}
|
||||
userGuideModule={chatData.app?.userGuideModule}
|
||||
chatConfig={chatData.app?.chatConfig}
|
||||
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
|
||||
feedbackType={'user'}
|
||||
onUpdateVariable={(e) => {}}
|
||||
|
||||
@@ -39,7 +39,6 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import SliderApps from './components/SliderApps';
|
||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
|
||||
|
||||
const OutLink = () => {
|
||||
@@ -364,7 +363,7 @@ const OutLink = () => {
|
||||
ref={ChatBoxRef}
|
||||
appAvatar={chatData.app.avatar}
|
||||
userAvatar={chatData.userAvatar}
|
||||
userGuideModule={chatData.app?.userGuideModule}
|
||||
chatConfig={chatData.app?.chatConfig}
|
||||
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
|
||||
feedbackType={'user'}
|
||||
onUpdateVariable={(e) => {}}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { flowNode2StoreNodes } from '@/components/core/workflow/utils';
|
||||
import { uiWorkflow2StoreWorkflow } from '@/components/core/workflow/utils';
|
||||
import { putUpdatePlugin } from '@/web/core/plugin/api';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||
@@ -39,7 +39,7 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
|
||||
const checkResults = checkWorkflowNodeAndConnection({ nodes, edges });
|
||||
if (!checkResults) {
|
||||
const storeNodes = flowNode2StoreNodes({ nodes, edges });
|
||||
const storeNodes = uiWorkflow2StoreWorkflow({ nodes, edges });
|
||||
|
||||
return storeNodes;
|
||||
} else {
|
||||
@@ -68,7 +68,7 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
}
|
||||
});
|
||||
|
||||
const onCopy = useCallback(async () => {
|
||||
const onExportWorkflow = useCallback(async () => {
|
||||
const data = await flowData2StoreDataAndCheck();
|
||||
if (data) {
|
||||
copyData(
|
||||
@@ -125,7 +125,7 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
{
|
||||
label: appT('Export Configs'),
|
||||
icon: 'export',
|
||||
onClick: onCopy
|
||||
onClick: onExportWorkflow
|
||||
}
|
||||
]}
|
||||
/>
|
||||
@@ -147,7 +147,7 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
isOpenImport,
|
||||
onClose,
|
||||
onCloseImport,
|
||||
onCopy,
|
||||
onExportWorkflow,
|
||||
onOpenImport,
|
||||
onclickSave,
|
||||
plugin.name,
|
||||
|
||||
@@ -15,6 +15,7 @@ import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
|
||||
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
|
||||
import WorkflowContextProvider, { WorkflowContext } from '@/components/core/workflow/context';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContextProvider } from '@/web/core/app/context/appContext';
|
||||
|
||||
type Props = { pluginId: string };
|
||||
|
||||
@@ -77,13 +78,15 @@ const Render = ({ pluginId }: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default function FlowEdit(props: any) {
|
||||
function Provider(props: Props) {
|
||||
return (
|
||||
<WorkflowContextProvider
|
||||
value={{ mode: 'plugin', basicNodeTemplates: pluginSystemModuleTemplates }}
|
||||
>
|
||||
<Render {...props} />
|
||||
</WorkflowContextProvider>
|
||||
<AppContextProvider appId={''}>
|
||||
<WorkflowContextProvider
|
||||
value={{ mode: 'plugin', basicNodeTemplates: pluginSystemModuleTemplates }}
|
||||
>
|
||||
<Render {...props} />
|
||||
</WorkflowContextProvider>
|
||||
</AppContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -95,3 +98,5 @@ export async function getServerSideProps(context: any) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default Provider;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
|
||||
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import {
|
||||
@@ -28,6 +29,7 @@ export const getScheduleTriggerApp = async () => {
|
||||
|
||||
try {
|
||||
await dispatchWorkFlow({
|
||||
chatId: getNanoid(),
|
||||
user,
|
||||
mode: 'chat',
|
||||
teamId: String(app.teamId),
|
||||
|
||||
@@ -7,7 +7,12 @@ import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import {
|
||||
getAppChatConfig,
|
||||
getGuideModule,
|
||||
splitGuideModule
|
||||
} from '@fastgpt/global/core/workflow/utils';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
type Props = {
|
||||
chatId: string;
|
||||
@@ -15,6 +20,7 @@ type Props = {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
nodes: StoreNodeItemType[];
|
||||
appChatConfig?: AppChatConfigType;
|
||||
variables?: Record<string, any>;
|
||||
isUpdateUseTime: boolean;
|
||||
source: `${ChatSourceEnum}`;
|
||||
@@ -30,6 +36,7 @@ export async function saveChat({
|
||||
teamId,
|
||||
tmbId,
|
||||
nodes,
|
||||
appChatConfig,
|
||||
variables,
|
||||
isUpdateUseTime,
|
||||
source,
|
||||
@@ -72,7 +79,11 @@ export async function saveChat({
|
||||
chat.variables = variables || {};
|
||||
await chat.save({ session });
|
||||
} else {
|
||||
const { welcomeText, variableNodes } = splitGuideModule(getGuideModule(nodes));
|
||||
const { welcomeText, variables: variableList } = getAppChatConfig({
|
||||
chatConfig: appChatConfig,
|
||||
systemConfigNode: getGuideModule(nodes),
|
||||
isPublicFetch: false
|
||||
});
|
||||
|
||||
await MongoChat.create(
|
||||
[
|
||||
@@ -81,7 +92,7 @@ export async function saveChat({
|
||||
teamId,
|
||||
tmbId,
|
||||
appId,
|
||||
variableList: variableNodes,
|
||||
variableList,
|
||||
welcomeText,
|
||||
variables,
|
||||
title,
|
||||
|
||||
2
projects/app/src/types/i18n.d.ts
vendored
2
projects/app/src/types/i18n.d.ts
vendored
@@ -6,6 +6,7 @@ import file from '../../i18n/zh/file.json';
|
||||
import publish from '../../i18n/zh/publish.json';
|
||||
import workflow from '../../i18n/zh/workflow.json';
|
||||
import user from '../../i18n/zh/user.json';
|
||||
import chat from '../../i18n/zh/chat.json';
|
||||
|
||||
export interface I18nNamespaces {
|
||||
common: typeof common;
|
||||
@@ -15,6 +16,7 @@ export interface I18nNamespaces {
|
||||
publish: typeof publish;
|
||||
workflow: typeof workflow;
|
||||
user: typeof user;
|
||||
chat: typeof chat;
|
||||
}
|
||||
|
||||
export type I18nNsType = (keyof I18nNamespaces)[];
|
||||
|
||||
@@ -10,6 +10,7 @@ type I18nContextType = {
|
||||
publishT: TFunction<['publish'], undefined>;
|
||||
workflowT: TFunction<['workflow'], undefined>;
|
||||
userT: TFunction<['user'], undefined>;
|
||||
chatT: TFunction<['chat'], undefined>;
|
||||
};
|
||||
|
||||
export const I18nContext = createContext<I18nContextType>({
|
||||
@@ -25,6 +26,7 @@ const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const { t: publishT } = useTranslation('publish');
|
||||
const { t: workflowT } = useTranslation('workflow');
|
||||
const { t: userT } = useTranslation('user');
|
||||
const { t: chatT } = useTranslation('chat');
|
||||
|
||||
return (
|
||||
<I18nContext.Provider
|
||||
@@ -35,7 +37,8 @@ const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
fileT,
|
||||
publishT,
|
||||
workflowT,
|
||||
userT
|
||||
userT,
|
||||
chatT
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
||||
import type {
|
||||
AppDetailType,
|
||||
AppListItemType,
|
||||
AppQuestionGuideTextConfigType
|
||||
ChatInputGuideConfigType
|
||||
} from '@fastgpt/global/core/app/type.d';
|
||||
import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
|
||||
import { AppUpdateParams, CreateAppParams } from '@/global/core/app/api';
|
||||
@@ -27,7 +27,7 @@ export const delModelById = (id: string) => DELETE(`/core/app/del?appId=${id}`);
|
||||
/**
|
||||
* 根据 ID 获取模型
|
||||
*/
|
||||
export const getModelById = (id: string) => GET<AppDetailType>(`/core/app/detail?appId=${id}`);
|
||||
export const getAppDetailById = (id: string) => GET<AppDetailType>(`/core/app/detail?appId=${id}`);
|
||||
|
||||
/**
|
||||
* 根据 ID 更新模型
|
||||
@@ -37,19 +37,3 @@ export const putAppById = (id: string, data: AppUpdateParams) =>
|
||||
|
||||
// =================== chat logs
|
||||
export const getAppChatLogs = (data: GetAppChatLogsParams) => POST(`/core/app/getChatLogs`, data);
|
||||
|
||||
/**
|
||||
* 导入提示词库
|
||||
*/
|
||||
export const importQuestionGuides = (data: {
|
||||
appId: string;
|
||||
textList: string[];
|
||||
customURL: string;
|
||||
}) => POST(`/core/app/questionGuides/import`, data);
|
||||
|
||||
/**
|
||||
* 获取提示词库
|
||||
*/
|
||||
export const getMyQuestionGuides = (
|
||||
data: PaginationProps<{ appId: string; customURL: string; searchKey: string }>
|
||||
) => GET<PaginationResponse<string>>(`/core/app/questionGuides/list`, data);
|
||||
|
||||
@@ -9,6 +9,7 @@ export const defaultApp: AppDetailType = {
|
||||
intro: '',
|
||||
updateTime: Date.now(),
|
||||
modules: [],
|
||||
chatConfig: {},
|
||||
teamId: '',
|
||||
tmbId: '',
|
||||
permission: 'private',
|
||||
|
||||
114
projects/app/src/web/core/app/context/appContext.tsx
Normal file
114
projects/app/src/web/core/app/context/appContext.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Dispatch, ReactNode, SetStateAction, useCallback, useState } from 'react';
|
||||
import { createContext } from 'use-context-selector';
|
||||
import { defaultApp } from '../constants';
|
||||
import { getAppDetailById, putAppById } from '../api';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { AppDetailType } from '@fastgpt/global/core/app/type';
|
||||
import { AppUpdateParams, PostPublishAppProps } from '@/global/core/app/api';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { postPublishApp } from '../versionApi';
|
||||
|
||||
type AppContextType = {
|
||||
appDetail: AppDetailType;
|
||||
setAppDetail: Dispatch<SetStateAction<AppDetailType>>;
|
||||
loadingApp: boolean;
|
||||
updateAppDetail: (data: AppUpdateParams) => Promise<void>;
|
||||
publishApp: (data: PostPublishAppProps) => Promise<void>;
|
||||
};
|
||||
|
||||
export const AppContext = createContext<AppContextType>({
|
||||
appDetail: defaultApp,
|
||||
setAppDetail: function (value: SetStateAction<AppDetailType>): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
loadingApp: false,
|
||||
updateAppDetail: function (data: AppUpdateParams): Promise<void> {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
publishApp: function (data: PostPublishAppProps): Promise<void> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
});
|
||||
|
||||
export const AppContextProvider = ({ children, appId }: { children: ReactNode; appId: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const [appDetail, setAppDetail] = useState(defaultApp);
|
||||
|
||||
const { loading } = useRequest(
|
||||
() => {
|
||||
if (appId) {
|
||||
return getAppDetailById(appId);
|
||||
}
|
||||
return Promise.resolve(defaultApp);
|
||||
},
|
||||
{
|
||||
refreshDeps: [appId],
|
||||
onSuccess(res) {
|
||||
setAppDetail(res);
|
||||
},
|
||||
onError(err: any) {
|
||||
toast({
|
||||
title: err?.message || t('core.app.error.Get app failed'),
|
||||
status: 'error'
|
||||
});
|
||||
router.replace('/app/list');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const updateAppDetail = useCallback(
|
||||
async (data: AppUpdateParams) => {
|
||||
try {
|
||||
await putAppById(appId, data);
|
||||
setAppDetail((state) => {
|
||||
return {
|
||||
...state,
|
||||
...data,
|
||||
modules: data?.nodes || state.modules
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error)
|
||||
});
|
||||
}
|
||||
},
|
||||
[appId, toast]
|
||||
);
|
||||
const publishApp = useCallback(
|
||||
async (data: PostPublishAppProps) => {
|
||||
try {
|
||||
await postPublishApp(appId, data);
|
||||
setAppDetail((state) => {
|
||||
return {
|
||||
...state,
|
||||
...data,
|
||||
modules: data?.nodes || state.modules
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error)
|
||||
});
|
||||
}
|
||||
},
|
||||
[appId, toast]
|
||||
);
|
||||
|
||||
const contextValue = {
|
||||
appDetail,
|
||||
setAppDetail,
|
||||
loadingApp: loading,
|
||||
updateAppDetail,
|
||||
publishApp
|
||||
};
|
||||
|
||||
return <AppContext.Provider value={contextValue}>{children}</AppContext.Provider>;
|
||||
};
|
||||
@@ -1,22 +1,12 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import { getMyApps, getModelById, putAppById, getMyQuestionGuides } from '@/web/core/app/api';
|
||||
import type { AppUpdateParams } from '@/global/core/app/api.d';
|
||||
import { AppDetailType, AppListItemType } from '@fastgpt/global/core/app/type.d';
|
||||
import { PostPublishAppProps } from '@/global/core/app/api';
|
||||
import { postPublishApp } from '../versionApi';
|
||||
import { defaultApp } from '../constants';
|
||||
import { getMyApps } from '@/web/core/app/api';
|
||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
type State = {
|
||||
export type State = {
|
||||
myApps: AppListItemType[];
|
||||
loadMyApps: (init?: boolean) => Promise<AppListItemType[]>;
|
||||
appDetail: AppDetailType;
|
||||
loadAppDetail: (id: string, init?: boolean) => Promise<AppDetailType>;
|
||||
updateAppDetail(appId: string, data: AppUpdateParams): Promise<void>;
|
||||
publishApp(appId: string, data: PostPublishAppProps): Promise<void>;
|
||||
clearAppModules(): void;
|
||||
setAppDetail(data: AppDetailType): void;
|
||||
loadMyApps: () => Promise<AppListItemType[]>;
|
||||
};
|
||||
|
||||
export const useAppStore = create<State>()(
|
||||
@@ -24,57 +14,12 @@ export const useAppStore = create<State>()(
|
||||
persist(
|
||||
immer((set, get) => ({
|
||||
myApps: [],
|
||||
async loadMyApps(init = true) {
|
||||
if (get().myApps.length > 0 && !init) return [];
|
||||
async loadMyApps() {
|
||||
const res = await getMyApps();
|
||||
set((state) => {
|
||||
state.myApps = res;
|
||||
});
|
||||
return res;
|
||||
},
|
||||
appDetail: defaultApp,
|
||||
async loadAppDetail(id: string, init = false) {
|
||||
if (id === get().appDetail._id && !init) return get().appDetail;
|
||||
|
||||
const res = await getModelById(id);
|
||||
set((state) => {
|
||||
state.appDetail = res;
|
||||
});
|
||||
return res;
|
||||
},
|
||||
async updateAppDetail(appId: string, data: AppUpdateParams) {
|
||||
await putAppById(appId, data);
|
||||
set((state) => {
|
||||
state.appDetail = {
|
||||
...state.appDetail,
|
||||
...data,
|
||||
modules: data?.nodes || state.appDetail.modules
|
||||
};
|
||||
});
|
||||
},
|
||||
async publishApp(appId: string, data: PostPublishAppProps) {
|
||||
await postPublishApp(appId, data);
|
||||
set((state) => {
|
||||
state.appDetail = {
|
||||
...state.appDetail,
|
||||
...data,
|
||||
modules: data?.nodes || state.appDetail.modules
|
||||
};
|
||||
});
|
||||
},
|
||||
setAppDetail(data: AppDetailType) {
|
||||
set((state) => {
|
||||
state.appDetail = data;
|
||||
});
|
||||
},
|
||||
|
||||
clearAppModules() {
|
||||
set((state) => {
|
||||
state.appDetail = {
|
||||
...state.appDetail,
|
||||
modules: []
|
||||
};
|
||||
});
|
||||
}
|
||||
})),
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
AppDetailType,
|
||||
AppQuestionGuideTextConfigType,
|
||||
ChatInputGuideConfigType,
|
||||
AppSchema,
|
||||
AppSimpleEditFormType
|
||||
} from '@fastgpt/global/core/app/type';
|
||||
@@ -33,50 +33,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType {
|
||||
y: -486.7611729549753
|
||||
},
|
||||
version: '481',
|
||||
inputs: [
|
||||
{
|
||||
key: NodeInputKeyEnum.welcomeText,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: 'core.app.Welcome Text',
|
||||
value: formData.userGuide.welcomeText
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.variables,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: 'core.app.Chat Variable',
|
||||
value: formData.userGuide.variables
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.questionGuide,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: 'core.app.Question Guide',
|
||||
value: formData.userGuide.questionGuide
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.tts,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: '',
|
||||
value: formData.userGuide.tts
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.whisper,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: '',
|
||||
value: formData.userGuide.whisper
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.scheduleTrigger,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: '',
|
||||
value: formData.userGuide.scheduleTrigger
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.questionGuideText,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: '',
|
||||
value: formData.userGuide.questionGuideText
|
||||
}
|
||||
],
|
||||
inputs: [],
|
||||
outputs: []
|
||||
};
|
||||
}
|
||||
@@ -773,29 +730,6 @@ export const getAppQGuideCustomURL = (appDetail: AppDetailType | AppSchema): str
|
||||
return (
|
||||
appDetail?.modules
|
||||
.find((m) => m.flowNodeType === FlowNodeTypeEnum.systemConfig)
|
||||
?.inputs.find((i) => i.key === NodeInputKeyEnum.questionGuideText)?.value.customURL || ''
|
||||
?.inputs.find((i) => i.key === NodeInputKeyEnum.chatInputGuide)?.value.customUrl || ''
|
||||
);
|
||||
};
|
||||
|
||||
export const getNodesWithNoQGuide = (
|
||||
nodes: StoreNodeItemType[],
|
||||
questionGuideText: AppQuestionGuideTextConfigType
|
||||
): StoreNodeItemType[] => {
|
||||
return nodes.map((node) => {
|
||||
if (node.flowNodeType === FlowNodeTypeEnum.systemConfig) {
|
||||
return {
|
||||
...node,
|
||||
inputs: node.inputs.map((input) => {
|
||||
if (input.key === NodeInputKeyEnum.questionGuideText) {
|
||||
return {
|
||||
...input,
|
||||
value: { ...questionGuideText, textList: [] }
|
||||
};
|
||||
}
|
||||
return input;
|
||||
})
|
||||
};
|
||||
}
|
||||
return node;
|
||||
});
|
||||
};
|
||||
|
||||
39
projects/app/src/web/core/chat/inputGuide/api.ts
Normal file
39
projects/app/src/web/core/chat/inputGuide/api.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
||||
import type {
|
||||
ChatInputGuideProps,
|
||||
ChatInputGuideResponse
|
||||
} from '@/pages/api/core/chat/inputGuide/list';
|
||||
import type {
|
||||
countChatInputGuideTotalQuery,
|
||||
countChatInputGuideTotalResponse
|
||||
} from '@/pages/api/core/chat/inputGuide/countTotal';
|
||||
import type {
|
||||
createInputGuideBody,
|
||||
createInputGuideResponse
|
||||
} from '@/pages/api/core/chat/inputGuide/create';
|
||||
import type { updateInputGuideBody } from '@/pages/api/core/chat/inputGuide/update';
|
||||
import type { deleteChatInputGuideQuery } from '@/pages/api/core/chat/inputGuide/delete';
|
||||
import type {
|
||||
QueryChatInputGuideProps,
|
||||
QueryChatInputGuideResponse
|
||||
} from '@/pages/api/core/chat/inputGuide/query';
|
||||
|
||||
export const getCountChatInputGuideTotal = (data: countChatInputGuideTotalQuery) =>
|
||||
GET<countChatInputGuideTotalResponse>(`/core/chat/inputGuide/countTotal`, data);
|
||||
/**
|
||||
* Get chat input guide list
|
||||
*/
|
||||
export const getChatInputGuideList = (data: ChatInputGuideProps) =>
|
||||
GET<ChatInputGuideResponse>(`/core/chat/inputGuide/list`, data);
|
||||
|
||||
export const queryChatInputGuideList = (
|
||||
data: QueryChatInputGuideProps,
|
||||
url = `/core/chat/inputGuide/query`
|
||||
) => GET<QueryChatInputGuideResponse>(url, data);
|
||||
|
||||
export const postChatInputGuides = (data: createInputGuideBody) =>
|
||||
POST<createInputGuideResponse>(`/core/chat/inputGuide/create`, data);
|
||||
export const putChatInputGuide = (data: updateInputGuideBody) =>
|
||||
PUT(`/core/chat/inputGuide/update`, data);
|
||||
export const delChatInputGuide = (data: deleteChatInputGuideQuery) =>
|
||||
POST(`/core/chat/inputGuide/delete`, data);
|
||||
@@ -29,20 +29,20 @@ export function checkChatSupportSelectFileByModules(modules: StoreNodeItemType[]
|
||||
|
||||
export function getAppQuestionGuidesByModules(modules: StoreNodeItemType[] = []) {
|
||||
const systemModule = modules.find((item) => item.flowNodeType === FlowNodeTypeEnum.systemConfig);
|
||||
const questionGuideText = systemModule?.inputs.find(
|
||||
(item) => item.key === NodeInputKeyEnum.questionGuideText
|
||||
const chatInputGuide = systemModule?.inputs.find(
|
||||
(item) => item.key === NodeInputKeyEnum.chatInputGuide
|
||||
)?.value;
|
||||
|
||||
return questionGuideText?.open ? questionGuideText?.textList : [];
|
||||
return chatInputGuide?.open ? chatInputGuide?.textList : [];
|
||||
}
|
||||
|
||||
export function getAppQuestionGuidesByUserGuideModule(
|
||||
module: StoreNodeItemType,
|
||||
qGuideText: string[] = []
|
||||
) {
|
||||
const questionGuideText = module?.inputs.find(
|
||||
(item) => item.key === NodeInputKeyEnum.questionGuideText
|
||||
const chatInputGuide = module?.inputs.find(
|
||||
(item) => item.key === NodeInputKeyEnum.chatInputGuide
|
||||
)?.value;
|
||||
|
||||
return questionGuideText?.open ? qGuideText : [];
|
||||
return chatInputGuide?.open ? qGuideText : [];
|
||||
}
|
||||
|
||||
@@ -26,8 +26,17 @@ import {
|
||||
import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { getWorkflowGlobalVariables } from './utils';
|
||||
import { TFunction } from 'next-i18next';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
export const getGlobalVariableNode = (nodes: FlowNodeItemType[], t: TFunction) => {
|
||||
export const getGlobalVariableNode = ({
|
||||
nodes,
|
||||
chatConfig,
|
||||
t
|
||||
}: {
|
||||
nodes: FlowNodeItemType[];
|
||||
chatConfig: AppChatConfigType;
|
||||
t: TFunction;
|
||||
}) => {
|
||||
const template: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.globalVariable,
|
||||
templateType: FlowNodeTemplateTypeEnum.other,
|
||||
@@ -44,7 +53,7 @@ export const getGlobalVariableNode = (nodes: FlowNodeItemType[], t: TFunction) =
|
||||
outputs: []
|
||||
};
|
||||
|
||||
const globalVariables = getWorkflowGlobalVariables(nodes, t);
|
||||
const globalVariables = getWorkflowGlobalVariables({ nodes, chatConfig, t });
|
||||
|
||||
const variableNode: FlowNodeItemType = {
|
||||
nodeId: VARIABLE_NODE_ID,
|
||||
|
||||
@@ -19,8 +19,8 @@ import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workfl
|
||||
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
||||
import {
|
||||
formatEditorVariablePickerIcon,
|
||||
getGuideModule,
|
||||
splitGuideModule
|
||||
getAppChatConfig,
|
||||
getGuideModule
|
||||
} from '@fastgpt/global/core/workflow/utils';
|
||||
import { getSystemVariables } from '../app/utils';
|
||||
import { TFunction } from 'next-i18next';
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
} from '@fastgpt/global/core/workflow/type/io';
|
||||
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
export const nodeTemplate2FlowNode = ({
|
||||
template,
|
||||
@@ -114,11 +115,13 @@ export const computedNodeInputReference = ({
|
||||
nodeId,
|
||||
nodes,
|
||||
edges,
|
||||
chatConfig,
|
||||
t
|
||||
}: {
|
||||
nodeId: string;
|
||||
nodes: FlowNodeItemType[];
|
||||
edges: Edge[];
|
||||
chatConfig: AppChatConfigType;
|
||||
t: TFunction;
|
||||
}) => {
|
||||
// get current node
|
||||
@@ -144,23 +147,31 @@ export const computedNodeInputReference = ({
|
||||
};
|
||||
findSourceNode(nodeId);
|
||||
|
||||
sourceNodes.unshift(getGlobalVariableNode(nodes, t));
|
||||
sourceNodes.unshift(
|
||||
getGlobalVariableNode({
|
||||
nodes,
|
||||
t,
|
||||
chatConfig
|
||||
})
|
||||
);
|
||||
|
||||
return sourceNodes;
|
||||
};
|
||||
export const getReferenceDataValueType = ({
|
||||
variable,
|
||||
nodeList,
|
||||
chatConfig,
|
||||
t
|
||||
}: {
|
||||
variable?: ReferenceValueProps;
|
||||
nodeList: FlowNodeItemType[];
|
||||
chatConfig: AppChatConfigType;
|
||||
t: TFunction;
|
||||
}) => {
|
||||
if (!variable) return WorkflowIOValueTypeEnum.any;
|
||||
|
||||
const node = nodeList.find((node) => node.nodeId === variable[0]);
|
||||
const systemVariables = getWorkflowGlobalVariables(nodeList, t);
|
||||
const systemVariables = getWorkflowGlobalVariables({ nodes: nodeList, chatConfig, t });
|
||||
|
||||
if (!node) return systemVariables.find((item) => item.key === variable?.[1])?.valueType;
|
||||
|
||||
@@ -288,12 +299,21 @@ export const filterSensitiveNodesData = (nodes: StoreNodeItemType[]) => {
|
||||
};
|
||||
|
||||
/* get workflowStart output to global variables */
|
||||
export const getWorkflowGlobalVariables = (
|
||||
nodes: FlowNodeItemType[],
|
||||
t: TFunction
|
||||
): EditorVariablePickerType[] => {
|
||||
export const getWorkflowGlobalVariables = ({
|
||||
nodes,
|
||||
chatConfig,
|
||||
t
|
||||
}: {
|
||||
nodes: FlowNodeItemType[];
|
||||
chatConfig: AppChatConfigType;
|
||||
t: TFunction;
|
||||
}): EditorVariablePickerType[] => {
|
||||
const globalVariables = formatEditorVariablePickerIcon(
|
||||
splitGuideModule(getGuideModule(nodes))?.variableNodes || []
|
||||
getAppChatConfig({
|
||||
chatConfig,
|
||||
systemConfigNode: getGuideModule(nodes),
|
||||
isPublicFetch: true
|
||||
})?.variables || []
|
||||
).map((item) => ({
|
||||
...item,
|
||||
valueType: WorkflowIOValueTypeEnum.any
|
||||
|
||||
Reference in New Issue
Block a user