diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index 3360c2c8a..5ea36a001 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -97,7 +97,7 @@ export type AppDatasetSearchParamsType = { datasetSearchExtensionBg?: string; }; -export type AppSimpleEditFormType = { +export type AppFormEditFormType = { // templateId: string; aiSettings: { [NodeInputKeyEnum.aiModel]: string; diff --git a/packages/global/core/app/utils.ts b/packages/global/core/app/utils.ts index 85d24d1e1..c9a796a29 100644 --- a/packages/global/core/app/utils.ts +++ b/packages/global/core/app/utils.ts @@ -1,16 +1,11 @@ -import type { AppChatConfigType, AppSimpleEditFormType } from '../app/type'; -import { FlowNodeTypeEnum } from '../workflow/node/constant'; -import { FlowNodeTemplateTypeEnum, NodeInputKeyEnum } from '../workflow/constants'; -import type { FlowNodeInputItemType } from '../workflow/type/io.d'; -import { getAppChatConfig } from '../workflow/utils'; -import { type StoreNodeItemType } from '../workflow/type/node'; +import type { AppFormEditFormType } from '../app/type'; import { DatasetSearchModeEnum } from '../dataset/constants'; import { type WorkflowTemplateBasicType } from '../workflow/type'; import { AppTypeEnum } from './constants'; import appErrList from '../../common/error/code/app'; import pluginErrList from '../../common/error/code/plugin'; -export const getDefaultAppForm = (): AppSimpleEditFormType => { +export const getDefaultAppForm = (): AppFormEditFormType => { return { aiSettings: { model: '', @@ -37,143 +32,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => { }; }; -/* format app nodes to edit form */ -export const appWorkflow2Form = ({ - nodes, - chatConfig -}: { - nodes: StoreNodeItemType[]; - chatConfig: AppChatConfigType; -}) => { - const defaultAppForm = getDefaultAppForm(); - const findInputValueByKey = (inputs: FlowNodeInputItemType[], key: string) => { - return inputs.find((item) => item.key === key)?.value; - }; - - nodes.forEach((node) => { - if ( - node.flowNodeType === FlowNodeTypeEnum.chatNode || - node.flowNodeType === FlowNodeTypeEnum.toolCall - ) { - defaultAppForm.aiSettings.model = findInputValueByKey(node.inputs, NodeInputKeyEnum.aiModel); - defaultAppForm.aiSettings.systemPrompt = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiSystemPrompt - ); - defaultAppForm.aiSettings.temperature = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatTemperature - ); - defaultAppForm.aiSettings.maxToken = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatMaxToken - ); - defaultAppForm.aiSettings.maxHistories = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.history - ); - defaultAppForm.aiSettings.aiChatReasoning = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatReasoning - ); - defaultAppForm.aiSettings.aiChatTopP = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatTopP - ); - defaultAppForm.aiSettings.aiChatStopSign = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatStopSign - ); - defaultAppForm.aiSettings.aiChatResponseFormat = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatResponseFormat - ); - defaultAppForm.aiSettings.aiChatJsonSchema = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatJsonSchema - ); - } else if (node.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) { - defaultAppForm.dataset.datasets = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSelectList - ); - defaultAppForm.dataset.similarity = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSimilarity - ); - defaultAppForm.dataset.limit = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetMaxTokens - ); - defaultAppForm.dataset.searchMode = - findInputValueByKey(node.inputs, NodeInputKeyEnum.datasetSearchMode) || - DatasetSearchModeEnum.embedding; - defaultAppForm.dataset.embeddingWeight = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchEmbeddingWeight - ); - // Rerank - defaultAppForm.dataset.usingReRank = !!findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchUsingReRank - ); - defaultAppForm.dataset.rerankModel = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchRerankModel - ); - defaultAppForm.dataset.rerankWeight = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchRerankWeight - ); - // Query extension - defaultAppForm.dataset.datasetSearchUsingExtensionQuery = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchUsingExtensionQuery - ); - defaultAppForm.dataset.datasetSearchExtensionModel = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchExtensionModel - ); - defaultAppForm.dataset.datasetSearchExtensionBg = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchExtensionBg - ); - } else if ( - node.flowNodeType === FlowNodeTypeEnum.pluginModule || - node.flowNodeType === FlowNodeTypeEnum.appModule || - node.flowNodeType === FlowNodeTypeEnum.tool || - node.flowNodeType === FlowNodeTypeEnum.toolSet - ) { - if (!node.pluginId) return; - - defaultAppForm.selectedTools.push({ - id: node.nodeId, - pluginId: node.pluginId, - name: node.name, - avatar: node.avatar, - intro: node.intro || '', - flowNodeType: node.flowNodeType, - showStatus: node.showStatus, - version: node.version, - inputs: node.inputs, - outputs: node.outputs, - templateType: FlowNodeTemplateTypeEnum.other, - pluginData: node.pluginData, - toolConfig: node.toolConfig - }); - } else if (node.flowNodeType === FlowNodeTypeEnum.systemConfig) { - defaultAppForm.chatConfig = getAppChatConfig({ - chatConfig, - systemConfigNode: node, - isPublicFetch: true - }); - } - }); - - return defaultAppForm; -}; - -export const getAppType = (config?: WorkflowTemplateBasicType | AppSimpleEditFormType) => { +export const getAppType = (config?: WorkflowTemplateBasicType | AppFormEditFormType) => { if (!config) return ''; if ('aiSettings' in config) { diff --git a/packages/global/core/workflow/template/system/agent/index.ts b/packages/global/core/workflow/template/system/agent/index.ts index 55512278b..6d125a51e 100644 --- a/packages/global/core/workflow/template/system/agent/index.ts +++ b/packages/global/core/workflow/template/system/agent/index.ts @@ -43,12 +43,6 @@ export const AgentNode: FlowNodeTemplateType = { }, Input_Template_System_Prompt, Input_Template_History, - { - key: NodeInputKeyEnum.modelConfig, - renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window - label: '', - valueType: WorkflowIOValueTypeEnum.object - }, { key: NodeInputKeyEnum.subApps, renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window diff --git a/projects/app/src/pageComponents/app/detail/Agent/Header.tsx b/projects/app/src/pageComponents/app/detail/Agent/Header.tsx deleted file mode 100644 index 26d0c436f..000000000 --- a/projects/app/src/pageComponents/app/detail/Agent/Header.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../context'; -import FolderPath from '@/components/common/folder/Path'; -import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { getAppFolderPath } from '@/web/core/app/api/app'; -import { Box, Flex, IconButton } from '@chakra-ui/react'; -import { useRouter } from 'next/router'; -import RouteTab from '../RouteTab'; -import { useTranslation } from 'next-i18next'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { form2AppWorkflow } from '@/web/core/app/utils'; -import { TabEnum } from '../context'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import MyTag from '@fastgpt/web/components/common/Tag/index'; -import { publishStatusStyle } from '../constants'; -import { useSystem } from '@fastgpt/web/hooks/useSystem'; -import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import SaveButton from '../Workflow/components/SaveButton'; -import { useBoolean, useDebounceEffect, useLockFn } from 'ahooks'; -import { appWorkflow2Form } from '@fastgpt/global/core/app/utils'; -import { - compareSimpleAppSnapshot, - type onSaveSnapshotFnType, - type SimpleAppSnapshotType -} from './useSnapshots'; -import PublishHistories from '../PublishHistoriesSlider'; -import { type AppVersionSchemaType } from '@fastgpt/global/core/app/version'; -import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload'; -import { isProduction } from '@fastgpt/global/common/system/constants'; -import { useToast } from '@fastgpt/web/hooks/useToast'; -import { - checkWorkflowNodeAndConnection, - storeEdge2RenderEdge, - storeNode2FlowNode -} from '@/web/core/workflow/utils'; - -const Header = ({ - forbiddenSaveSnapshot, - appForm, - setAppForm, - past, - setPast, - saveSnapshot -}: { - forbiddenSaveSnapshot: React.MutableRefObject; - appForm: AppSimpleEditFormType; - setAppForm: (form: AppSimpleEditFormType) => void; - past: SimpleAppSnapshotType[]; - setPast: (value: React.SetStateAction) => void; - saveSnapshot: onSaveSnapshotFnType; -}) => { - const { t } = useTranslation(); - const { isPc } = useSystem(); - const { toast } = useToast(); - const router = useRouter(); - const appId = useContextSelector(AppContext, (v) => v.appId); - const onSaveApp = useContextSelector(AppContext, (v) => v.onSaveApp); - const currentTab = useContextSelector(AppContext, (v) => v.currentTab); - - const { lastAppListRouteType } = useSystemStore(); - - const { data: paths = [] } = useRequest2( - () => getAppFolderPath({ sourceId: appId, type: 'parent' }), - { - manual: false, - refreshDeps: [appId] - } - ); - const onClickRoute = useCallback( - (parentId: string) => { - router.push({ - pathname: '/dashboard/apps', - query: { - parentId, - type: lastAppListRouteType - } - }); - }, - [router, lastAppListRouteType] - ); - - const { runAsync: onClickSave, loading } = useRequest2( - async ({ - isPublish, - versionName = formatTime2YMDHMS(new Date()), - autoSave - }: { - isPublish?: boolean; - versionName?: string; - autoSave?: boolean; - }) => { - const { nodes, edges } = form2AppWorkflow(appForm, t); - await onSaveApp({ - nodes, - edges, - chatConfig: appForm.chatConfig, - isPublish, - versionName, - autoSave - }); - setPast((prevPast) => - prevPast.map((item, index) => - index === 0 - ? { - ...item, - isSaved: true - } - : item - ) - ); - } - ); - - const [isShowHistories, { setTrue: setIsShowHistories, setFalse: closeHistories }] = - useBoolean(false); - - const onSwitchTmpVersion = useCallback( - (data: SimpleAppSnapshotType, customTitle: string) => { - setAppForm(data.appForm); - - // Remove multiple "copy-" - const copyText = t('app:version_copy'); - const regex = new RegExp(`(${copyText}-)\\1+`, 'g'); - const title = customTitle.replace(regex, `$1`); - - return saveSnapshot({ - appForm: data.appForm, - title - }); - }, - [saveSnapshot, setAppForm, t] - ); - const onSwitchCloudVersion = useCallback( - (appVersion: AppVersionSchemaType) => { - const appForm = appWorkflow2Form({ - nodes: appVersion.nodes, - chatConfig: appVersion.chatConfig - }); - - const res = saveSnapshot({ - appForm, - title: `${t('app:version_copy')}-${appVersion.versionName}` - }); - forbiddenSaveSnapshot.current = true; - - setAppForm(appForm); - - return res; - }, - [forbiddenSaveSnapshot, saveSnapshot, setAppForm, t] - ); - - // Check if the workflow is published - const [isSaved, setIsSaved] = useState(false); - useDebounceEffect( - () => { - const savedSnapshot = past.find((snapshot) => snapshot.isSaved); - const val = compareSimpleAppSnapshot(savedSnapshot?.appForm, appForm); - setIsSaved(val); - }, - [past], - { wait: 500 } - ); - - const onLeaveAutoSave = useLockFn(async () => { - if (isSaved) return; - try { - console.log('Leave auto save'); - return onClickSave({ isPublish: false, autoSave: true }); - } catch (error) { - console.error(error); - } - }); - useEffect(() => { - return () => { - if (isProduction) { - onLeaveAutoSave(); - } - }; - }, []); - useBeforeunload({ - tip: t('common:core.tip.leave page'), - callback: onLeaveAutoSave - }); - - return ( - - {!isPc && ( - - - - )} - - - - - {isPc && ( - - - - )} - {currentTab === TabEnum.appEdit && ( - - {!isShowHistories && ( - <> - {isPc && ( - - {t( - isSaved - ? publishStatusStyle.published.text - : publishStatusStyle.unPublish.text - )} - - )} - - } - aria-label={''} - size={'sm'} - w={'30px'} - variant={'whitePrimary'} - onClick={setIsShowHistories} - /> - { - const { nodes: storeNodes, edges: storeEdges } = form2AppWorkflow(appForm, t); - - const nodes = storeNodes.map((item) => storeNode2FlowNode({ item, t })); - const edges = storeEdges.map((item) => storeEdge2RenderEdge({ edge: item })); - - const checkResults = checkWorkflowNodeAndConnection({ nodes, edges }); - - if (checkResults) { - toast({ - title: t('app:app.error.publish_unExist_app'), - status: 'warning' - }); - } - return !checkResults; - }} - /> - - )} - - )} - - - {isShowHistories && currentTab === TabEnum.appEdit && ( - - onClose={closeHistories} - past={past} - onSwitchTmpVersion={onSwitchTmpVersion} - onSwitchCloudVersion={onSwitchCloudVersion} - positionStyles={{ - top: 14, - bottom: 3 - }} - /> - )} - - ); -}; - -export default Header; diff --git a/projects/app/src/pageComponents/app/detail/Agent/components/ConfigToolModal.tsx b/projects/app/src/pageComponents/app/detail/Agent/components/ConfigToolModal.tsx deleted file mode 100644 index 8cc73d1ba..000000000 --- a/projects/app/src/pageComponents/app/detail/Agent/components/ConfigToolModal.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import { Button, Flex, HStack, ModalBody, ModalFooter } from '@chakra-ui/react'; -import MyModal from '@fastgpt/web/components/common/MyModal'; -import React from 'react'; -import { useTranslation } from 'next-i18next'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import { Box } from '@chakra-ui/react'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { childAppSystemKey } from './ToolSelectModal'; -import { Controller, useForm } from 'react-hook-form'; -import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; -import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; -import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants'; -import UseGuideModal from '@/components/common/Modal/UseGuideModal'; -import InputRender from '@/components/core/app/formRender'; -import { nodeInputTypeToInputType } from '@/components/core/app/formRender/utils'; -import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; -import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; -import SecretInputModal, { - type ToolParamsFormType -} from '@/pageComponents/app/plugin/SecretInputModal'; -import { SystemToolInputTypeMap } from '@fastgpt/global/core/app/systemTool/constants'; -import { useBoolean } from 'ahooks'; - -const ConfigToolModal = ({ - configTool, - onCloseConfigTool, - onAddTool -}: { - configTool: AppSimpleEditFormType['selectedTools'][number]; - onCloseConfigTool: () => void; - onAddTool: (tool: AppSimpleEditFormType['selectedTools'][number]) => void; -}) => { - const { t } = useTranslation(); - const [isOpenSecretModal, { setTrue: setTrueSecretModal, setFalse: setFalseSecretModal }] = - useBoolean(false); - - const { handleSubmit, control } = useForm({ - defaultValues: configTool - ? configTool.inputs.reduce( - (acc, input) => { - acc[input.key] = input.value || input.defaultValue; - return acc; - }, - {} as Record - ) - : {} - }); - - return ( - - - - - {t('app:tool_input_param_tip')} - {!!(configTool?.courseUrl || configTool?.userGuide) && ( - - {({ onClick }) => ( - - {t('app:workflow.Input guide')} - - )} - - )} - - {configTool.inputs - .filter( - (input) => - !input.toolDescription && - !childAppSystemKey.includes(input.key) && - !input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) && - !input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) - ) - .map((input) => { - return ( - - - {input.required && *} - {input.label} - {input.description && } - - - {input.key === NodeInputKeyEnum.systemInputConfig && input.inputList ? ( - ( - - {t('common:secret_key')} - - - {isOpenSecretModal && ( - { - onChange(data); - setFalseSecretModal(); - }} - /> - )} - - )} - /> - ) : ( - { - if (input.valueType === WorkflowIOValueTypeEnum.boolean) { - return value !== undefined; - } - if (input.required) { - return !!value; - } - return true; - } - }} - render={({ field: { onChange, value }, fieldState: { error } }) => { - return ( - - ); - }} - /> - )} - - ); - })} - - - - - - - ); -}; - -export default React.memo(ConfigToolModal); diff --git a/projects/app/src/pageComponents/app/detail/Agent/components/ToolSelect.tsx b/projects/app/src/pageComponents/app/detail/Agent/components/ToolSelect.tsx deleted file mode 100644 index 61c1b7432..000000000 --- a/projects/app/src/pageComponents/app/detail/Agent/components/ToolSelect.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { Box, Button, Flex, Grid, useDisclosure } from '@chakra-ui/react'; -import React, { useState } from 'react'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import { useTranslation } from 'next-i18next'; -import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; -import { SmallAddIcon } from '@chakra-ui/icons'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; -import { theme } from '@fastgpt/web/styles/theme'; -import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete'; -import ToolSelectModal, { childAppSystemKey } from './ToolSelectModal'; -import { - FlowNodeInputTypeEnum, - FlowNodeTypeEnum -} from '@fastgpt/global/core/workflow/node/constant'; -import Avatar from '@fastgpt/web/components/common/Avatar'; -import ConfigToolModal from './ConfigToolModal'; -import { getWebLLMModel } from '@/web/common/system/utils'; -import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; -import { formatToolError } from '@fastgpt/global/core/app/utils'; - -const ToolSelect = ({ - appForm, - setAppForm -}: { - appForm: AppSimpleEditFormType; - setAppForm: React.Dispatch>; -}) => { - const { t } = useTranslation(); - - const [configTool, setConfigTool] = useState< - AppSimpleEditFormType['selectedTools'][number] | null - >(null); - - const { - isOpen: isOpenToolsSelect, - onOpen: onOpenToolsSelect, - onClose: onCloseToolsSelect - } = useDisclosure(); - const selectedModel = getWebLLMModel(appForm.aiSettings.model); - - return ( - <> - - - - {t('common:core.app.Tool call')} - - - - - 0 ? 2 : 0} - gridTemplateColumns={'repeat(2, minmax(0, 1fr))'} - gridGap={[2, 4]} - > - {appForm.selectedTools.map((item) => { - const toolError = formatToolError(item.pluginData?.error); - - return ( - - { - if ( - item.inputs - .filter((input) => !childAppSystemKey.includes(input.key)) - .every( - (input) => - input.toolDescription || - input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) || - input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) - ) || - toolError || - item.flowNodeType === FlowNodeTypeEnum.tool || - item.flowNodeType === FlowNodeTypeEnum.toolSet - ) { - return; - } - setConfigTool(item); - }} - > - - - {item.name} - - {toolError && ( - - - {t(toolError as any)} - - )} - { - e.stopPropagation(); - setAppForm((state: AppSimpleEditFormType) => ({ - ...state, - selectedTools: state.selectedTools.filter((tool) => tool.id !== item.id) - })); - }} - /> - - - ); - })} - - - {isOpenToolsSelect && ( - { - setAppForm((state) => ({ - ...state, - selectedTools: [...state.selectedTools, e] - })); - }} - onRemoveTool={(e) => { - setAppForm((state) => ({ - ...state, - selectedTools: state.selectedTools.filter((item) => item.pluginId !== e.id) - })); - }} - onClose={onCloseToolsSelect} - /> - )} - {configTool && ( - setConfigTool(null)} - onAddTool={(e) => { - setAppForm((state) => ({ - ...state, - selectedTools: state.selectedTools.map((item) => - item.pluginId === configTool.pluginId ? e : item - ) - })); - }} - /> - )} - - ); -}; - -export default React.memo(ToolSelect); diff --git a/projects/app/src/pageComponents/app/detail/Agent/useSnapshots.tsx b/projects/app/src/pageComponents/app/detail/Agent/useSnapshots.tsx deleted file mode 100644 index a2f322961..000000000 --- a/projects/app/src/pageComponents/app/detail/Agent/useSnapshots.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { useMemoizedFn } from 'ahooks'; -import { useRef, useState } from 'react'; -import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { isEqual } from 'lodash'; - -export type SimpleAppSnapshotType = { - title: string; - isSaved?: boolean; - appForm: AppSimpleEditFormType; - - // abandon - state?: AppSimpleEditFormType; - diff?: Record; -}; -export type onSaveSnapshotFnType = (props: { - appForm: AppSimpleEditFormType; // Current edited app form data - title?: string; - isSaved?: boolean; -}) => Promise; - -export const compareSimpleAppSnapshot = ( - appForm1?: AppSimpleEditFormType, - appForm2?: AppSimpleEditFormType -) => { - if ( - appForm1?.chatConfig && - appForm2?.chatConfig && - !isEqual( - { - welcomeText: appForm1.chatConfig?.welcomeText || '', - variables: appForm1.chatConfig?.variables || [], - questionGuide: appForm1.chatConfig?.questionGuide || undefined, - ttsConfig: appForm1.chatConfig?.ttsConfig || undefined, - whisperConfig: appForm1.chatConfig?.whisperConfig || undefined, - chatInputGuide: appForm1.chatConfig?.chatInputGuide || undefined, - fileSelectConfig: appForm1.chatConfig?.fileSelectConfig || undefined - }, - { - welcomeText: appForm2.chatConfig?.welcomeText || '', - variables: appForm2.chatConfig?.variables || [], - questionGuide: appForm2.chatConfig?.questionGuide || undefined, - ttsConfig: appForm2.chatConfig?.ttsConfig || undefined, - whisperConfig: appForm2.chatConfig?.whisperConfig || undefined, - chatInputGuide: appForm2.chatConfig?.chatInputGuide || undefined, - fileSelectConfig: appForm2.chatConfig?.fileSelectConfig || undefined - } - ) - ) { - console.log('chatConfig not equal'); - return false; - } - - return isEqual({ ...appForm1, chatConfig: undefined }, { ...appForm2, chatConfig: undefined }); -}; - -export const useSimpleAppSnapshots = (appId: string) => { - const forbiddenSaveSnapshot = useRef(false); - const [past, setPast] = useState([]); - - const saveSnapshot: onSaveSnapshotFnType = useMemoizedFn(async ({ appForm, title, isSaved }) => { - if (forbiddenSaveSnapshot.current) { - forbiddenSaveSnapshot.current = false; - return false; - } - - if (past.length === 0) { - setPast([ - { - title: title || formatTime2YMDHMS(new Date()), - isSaved, - appForm - } - ]); - return true; - } - - const pastState = past[0]; - const isPastEqual = compareSimpleAppSnapshot(pastState?.appForm, appForm); - if (isPastEqual) return false; - - setPast((past) => [ - { - appForm, - title: title || formatTime2YMDHMS(new Date()), - isSaved - }, - ...past.slice(0, 99) - ]); - - return true; - }); - - return { forbiddenSaveSnapshot, past, setPast, saveSnapshot }; -}; - -export default function Snapshots() { - return <>; -} diff --git a/projects/app/src/pageComponents/app/detail/Agent/Edit.tsx b/projects/app/src/pageComponents/app/detail/Edit/Agent/Edit.tsx similarity index 60% rename from projects/app/src/pageComponents/app/detail/Agent/Edit.tsx rename to projects/app/src/pageComponents/app/detail/Edit/Agent/Edit.tsx index c17fd2434..0333a358f 100644 --- a/projects/app/src/pageComponents/app/detail/Agent/Edit.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/Agent/Edit.tsx @@ -1,23 +1,24 @@ import React, { useState } from 'react'; import { Box } from '@chakra-ui/react'; -import ChatTest from './ChatTest'; -import AppCard from './AppCard'; +import ChatTest from '../FormComponent/ChatTest'; +import AppCard from '../FormComponent/AppCard'; import EditForm from './EditForm'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { cardStyles } from '../constants'; +import { type AppFormEditFormType } from '@fastgpt/global/core/app/type'; +import { cardStyles } from '../../constants'; -import styles from './styles.module.scss'; +import styles from '../FormComponent/styles.module.scss'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; -import { type SimpleAppSnapshotType } from './useSnapshots'; +import { type SimpleAppSnapshotType } from '../FormComponent/useSnapshots'; +import { agentForm2AppWorkflow } from './utils'; const Edit = ({ appForm, setAppForm, setPast }: { - appForm: AppSimpleEditFormType; - setAppForm: React.Dispatch>; + appForm: AppFormEditFormType; + setAppForm: React.Dispatch>; setPast: (value: React.SetStateAction) => void; }) => { const { isPc } = useSystem(); @@ -42,7 +43,7 @@ const Edit = ({ flex={'1'} > - + @@ -52,7 +53,11 @@ const Edit = ({ )} {isPc && ( - + )} diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/EditForm.tsx b/projects/app/src/pageComponents/app/detail/Edit/Agent/EditForm.tsx similarity index 98% rename from projects/app/src/pageComponents/app/detail/SimpleApp/EditForm.tsx rename to projects/app/src/pageComponents/app/detail/Edit/Agent/EditForm.tsx index 7737c118d..5c11e233c 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/EditForm.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/Agent/EditForm.tsx @@ -9,7 +9,7 @@ import { Button, HStack } from '@chakra-ui/react'; -import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d'; +import type { AppFormEditFormType } from '@fastgpt/global/core/app/type.d'; import { useRouter } from 'next/router'; import { useTranslation } from 'next-i18next'; @@ -30,7 +30,7 @@ import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip'; import { getWebLLMModel } from '@/web/common/system/utils'; -import ToolSelect from './components/ToolSelect'; +import ToolSelect from '../FormComponent/ToolSelector/ToolSelect'; import OptimizerPopover from '@/components/common/PromptEditor/OptimizerPopover'; const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal')); @@ -60,8 +60,8 @@ const EditForm = ({ appForm, setAppForm }: { - appForm: AppSimpleEditFormType; - setAppForm: React.Dispatch>; + appForm: AppFormEditFormType; + setAppForm: React.Dispatch>; }) => { const theme = useTheme(); const router = useRouter(); diff --git a/projects/app/src/pageComponents/app/detail/Agent/index.tsx b/projects/app/src/pageComponents/app/detail/Edit/Agent/index.tsx similarity index 82% rename from projects/app/src/pageComponents/app/detail/Agent/index.tsx rename to projects/app/src/pageComponents/app/detail/Edit/Agent/index.tsx index aeb85108e..096d9da8b 100644 --- a/projects/app/src/pageComponents/app/detail/Agent/index.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/Agent/index.tsx @@ -1,21 +1,20 @@ import React, { useState } from 'react'; import { getDefaultAppForm } from '@fastgpt/global/core/app/utils'; -import { appWorkflow2AgentForm } from './utils'; +import { agentForm2AppWorkflow, appWorkflow2AgentForm } from './utils'; -import Header from './Header'; +import Header from '../FormComponent/Header'; import { useContextSelector } from 'use-context-selector'; -import { AppContext, TabEnum } from '../context'; +import { AppContext, TabEnum } from '../../context'; import dynamic from 'next/dynamic'; import { Box, Flex } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; -import { useSimpleAppSnapshots } from './useSnapshots'; +import { useSimpleAppSnapshots } from '../FormComponent/useSnapshots'; import { useDebounceEffect, useMount } from 'ahooks'; -import { v1Workflow2V2 } from '@/web/core/workflow/adapt'; import { defaultAppSelectFileConfig } from '@fastgpt/global/core/app/constants'; const Edit = dynamic(() => import('./Edit')); -const Logs = dynamic(() => import('../Logs/index')); -const PublishChannel = dynamic(() => import('../Publish')); +const Logs = dynamic(() => import('../../Logs/index')); +const PublishChannel = dynamic(() => import('../../Publish')); const AgentEdit = () => { const { t } = useTranslation(); @@ -71,6 +70,8 @@ const AgentEdit = () => { past={past} setPast={setPast} saveSnapshot={saveSnapshot} + form2WorkflowFn={agentForm2AppWorkflow} + form2AppWorkflowFn={appWorkflow2AgentForm} /> {currentTab === TabEnum.appEdit ? ( diff --git a/projects/app/src/pageComponents/app/detail/Agent/utils.ts b/projects/app/src/pageComponents/app/detail/Edit/Agent/utils.ts similarity index 61% rename from projects/app/src/pageComponents/app/detail/Agent/utils.ts rename to projects/app/src/pageComponents/app/detail/Edit/Agent/utils.ts index 52c0b501b..e7c6dfaa9 100644 --- a/projects/app/src/pageComponents/app/detail/Agent/utils.ts +++ b/projects/app/src/pageComponents/app/detail/Edit/Agent/utils.ts @@ -1,14 +1,13 @@ -import { type AppChatConfigType, type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { type StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; +import type { AppChatConfigType, AppFormEditFormType } from '@fastgpt/global/core/app/type'; +import type { + FlowNodeTemplateType, + StoreNodeItemType +} from '@fastgpt/global/core/workflow/type/node.d'; import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; -import { - FlowNodeTemplateTypeEnum, - NodeInputKeyEnum, - WorkflowIOValueTypeEnum -} from '@fastgpt/global/core/workflow/constants'; +import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; import { type StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; import { @@ -21,7 +20,6 @@ import { workflowStartNodeId } from '@/web/core/app/constants'; import { AgentNode } from '@fastgpt/global/core/workflow/template/system/agent/index'; import { getDefaultAppForm } from '@fastgpt/global/core/app/utils'; import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; -import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants'; import { getAppChatConfig } from '@fastgpt/global/core/workflow/utils'; /* format app nodes to edit form */ @@ -38,116 +36,21 @@ export const appWorkflow2AgentForm = ({ }; nodes.forEach((node) => { - if ( - node.flowNodeType === FlowNodeTypeEnum.chatNode || - node.flowNodeType === FlowNodeTypeEnum.toolCall - ) { + const inputMap = new Map(node.inputs.map((input) => [input.key, input.value])); + if (node.flowNodeType === FlowNodeTypeEnum.agent) { defaultAppForm.aiSettings.model = findInputValueByKey(node.inputs, NodeInputKeyEnum.aiModel); - defaultAppForm.aiSettings.systemPrompt = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiSystemPrompt - ); - defaultAppForm.aiSettings.temperature = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatTemperature - ); - defaultAppForm.aiSettings.maxToken = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatMaxToken - ); - defaultAppForm.aiSettings.maxHistories = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.history - ); - defaultAppForm.aiSettings.aiChatReasoning = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatReasoning - ); - defaultAppForm.aiSettings.aiChatTopP = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatTopP - ); - defaultAppForm.aiSettings.aiChatStopSign = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatStopSign - ); - defaultAppForm.aiSettings.aiChatResponseFormat = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatResponseFormat - ); - defaultAppForm.aiSettings.aiChatJsonSchema = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.aiChatJsonSchema - ); - } else if (node.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) { - defaultAppForm.dataset.datasets = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSelectList - ); - defaultAppForm.dataset.similarity = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSimilarity - ); - defaultAppForm.dataset.limit = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetMaxTokens - ); - defaultAppForm.dataset.searchMode = - findInputValueByKey(node.inputs, NodeInputKeyEnum.datasetSearchMode) || - DatasetSearchModeEnum.embedding; - defaultAppForm.dataset.embeddingWeight = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchEmbeddingWeight - ); - // Rerank - defaultAppForm.dataset.usingReRank = !!findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchUsingReRank - ); - defaultAppForm.dataset.rerankModel = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchRerankModel - ); - defaultAppForm.dataset.rerankWeight = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchRerankWeight - ); - // Query extension - defaultAppForm.dataset.datasetSearchUsingExtensionQuery = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchUsingExtensionQuery - ); - defaultAppForm.dataset.datasetSearchExtensionModel = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchExtensionModel - ); - defaultAppForm.dataset.datasetSearchExtensionBg = findInputValueByKey( - node.inputs, - NodeInputKeyEnum.datasetSearchExtensionBg - ); - } else if ( - node.flowNodeType === FlowNodeTypeEnum.pluginModule || - node.flowNodeType === FlowNodeTypeEnum.appModule || - node.flowNodeType === FlowNodeTypeEnum.tool || - node.flowNodeType === FlowNodeTypeEnum.toolSet - ) { - if (!node.pluginId) return; + defaultAppForm.aiSettings.systemPrompt = inputMap.get(NodeInputKeyEnum.aiSystemPrompt); + defaultAppForm.aiSettings.temperature = inputMap.get(NodeInputKeyEnum.aiChatTemperature); + defaultAppForm.aiSettings.maxHistories = inputMap.get(NodeInputKeyEnum.history); + defaultAppForm.aiSettings.aiChatTopP = inputMap.get(NodeInputKeyEnum.aiChatTopP); - defaultAppForm.selectedTools.push({ - id: node.nodeId, - pluginId: node.pluginId, - name: node.name, - avatar: node.avatar, - intro: node.intro || '', - flowNodeType: node.flowNodeType, - showStatus: node.showStatus, - version: node.version, - inputs: node.inputs, - outputs: node.outputs, - templateType: FlowNodeTemplateTypeEnum.other, - pluginData: node.pluginData, - toolConfig: node.toolConfig - }); + const subApps = inputMap.get(NodeInputKeyEnum.subApps) as FlowNodeTemplateType[]; + console.log(subApps); + if (subApps) { + subApps.forEach((subApp) => { + defaultAppForm.selectedTools.push(subApp); + }); + } } else if (node.flowNodeType === FlowNodeTypeEnum.systemConfig) { defaultAppForm.chatConfig = getAppChatConfig({ chatConfig, @@ -160,12 +63,12 @@ export const appWorkflow2AgentForm = ({ return defaultAppForm; }; -type WorkflowType = { +export type WorkflowType = { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[]; }; export function agentForm2AppWorkflow( - data: AppSimpleEditFormType, + data: AppFormEditFormType, t: any // i18nT ): WorkflowType & { chatConfig: AppChatConfigType; diff --git a/projects/app/src/pageComponents/app/detail/Agent/AppCard.tsx b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/AppCard.tsx similarity index 94% rename from projects/app/src/pageComponents/app/detail/Agent/AppCard.tsx rename to projects/app/src/pageComponents/app/detail/Edit/FormComponent/AppCard.tsx index cbcdf4588..1e3436d2f 100644 --- a/projects/app/src/pageComponents/app/detail/Agent/AppCard.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/AppCard.tsx @@ -10,11 +10,11 @@ import { ModalFooter } from '@chakra-ui/react'; import { useRouter } from 'next/router'; -import { type AppSchema, type AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d'; +import { type AppSchema, type AppFormEditFormType } from '@fastgpt/global/core/app/type.d'; import { useTranslation } from 'next-i18next'; import Avatar from '@fastgpt/web/components/common/Avatar'; import MyIcon from '@fastgpt/web/components/common/Icon'; -import TagsEditModal from '../TagsEditModal'; +import TagsEditModal from '../../TagsEditModal'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { AppContext } from '@/pageComponents/app/detail/context'; import { useContextSelector } from 'use-context-selector'; @@ -22,17 +22,19 @@ import MyMenu from '@fastgpt/web/components/common/MyMenu'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { postTransition2Workflow } from '@/web/core/app/api/app'; -import { form2AppWorkflow } from '@/web/core/app/utils'; -import { type SimpleAppSnapshotType } from './useSnapshots'; +import type { SimpleAppSnapshotType } from './useSnapshots'; import ExportConfigPopover from '@/pageComponents/app/detail/ExportConfigPopover'; import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants'; +import type { Form2WorkflowFnType } from './type'; const AppCard = ({ appForm, - setPast + setPast, + form2WorkflowFn }: { - appForm: AppSimpleEditFormType; + appForm: AppFormEditFormType; setPast: (value: React.SetStateAction) => void; + form2WorkflowFn: Form2WorkflowFnType; }) => { const router = useRouter(); const { t } = useTranslation(); @@ -49,7 +51,7 @@ const AppCard = ({ const [transitionCreateNew, setTransitionCreateNew] = useState(); const { runAsync: onTransition, loading: transiting } = useRequest2( async () => { - const { nodes, edges } = form2AppWorkflow(appForm, t); + const { nodes, edges } = form2WorkflowFn(appForm, t); await onSaveApp({ nodes, edges, diff --git a/projects/app/src/pageComponents/app/detail/Agent/ChatTest.tsx b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/ChatTest.tsx similarity index 85% rename from projects/app/src/pageComponents/app/detail/Agent/ChatTest.tsx rename to projects/app/src/pageComponents/app/detail/Edit/FormComponent/ChatTest.tsx index 07a9b9eb4..be9e3e2af 100644 --- a/projects/app/src/pageComponents/app/detail/Agent/ChatTest.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/ChatTest.tsx @@ -5,25 +5,26 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useSafeState } from 'ahooks'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { agentForm2AppWorkflow } from './utils'; +import type { AppFormEditFormType } from '@fastgpt/global/core/app/type'; import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../context'; -import { useChatTest } from '../useChatTest'; +import { AppContext } from '../../context'; +import { useChatTest } from '../../useChatTest'; import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import ChatRecordContextProvider from '@/web/core/chat/context/chatRecordContext'; import { useChatStore } from '@/web/core/chat/context/useChatStore'; import MyBox from '@fastgpt/web/components/common/MyBox'; -import { cardStyles } from '../constants'; +import { cardStyles } from '../../constants'; import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList'; import VariablePopover from '@/components/core/chat/ChatContainer/components/VariablePopover'; import { ChatTypeEnum } from '@/components/core/chat/ChatContainer/ChatBox/constants'; +import type { Form2WorkflowFnType } from './type'; type Props = { - appForm: AppSimpleEditFormType; + appForm: AppFormEditFormType; setRenderEdit: React.Dispatch>; + form2WorkflowFn: Form2WorkflowFnType; }; -const ChatTest = ({ appForm, setRenderEdit }: Props) => { +const ChatTest = ({ appForm, setRenderEdit, form2WorkflowFn }: Props) => { const { t } = useTranslation(); const { appDetail } = useContextSelector(AppContext, (v) => v); @@ -38,7 +39,7 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => { }); useEffect(() => { - const { nodes, edges } = agentForm2AppWorkflow(appForm, t); + const { nodes, edges } = form2WorkflowFn(appForm, t); setWorkflowData({ nodes, edges }); }, [appForm, setWorkflowData, t]); @@ -103,7 +104,7 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => { ); }; -const Render = ({ appForm, setRenderEdit }: Props) => { +const Render = ({ appForm, setRenderEdit, form2WorkflowFn }: Props) => { const { chatId } = useChatStore(); const { appDetail } = useContextSelector(AppContext, (v) => v); @@ -124,7 +125,11 @@ const Render = ({ appForm, setRenderEdit }: Props) => { showNodeStatus > - + ); diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/Header.tsx b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/Header.tsx similarity index 90% rename from projects/app/src/pageComponents/app/detail/SimpleApp/Header.tsx rename to projects/app/src/pageComponents/app/detail/Edit/FormComponent/Header.tsx index 0d7ea7bec..1b625d3ec 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/Header.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/Header.tsx @@ -1,32 +1,30 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../context'; +import { AppContext } from '../../context'; import FolderPath from '@/components/common/folder/Path'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { getAppFolderPath } from '@/web/core/app/api/app'; import { Box, Flex, IconButton } from '@chakra-ui/react'; import { useRouter } from 'next/router'; -import RouteTab from '../RouteTab'; +import RouteTab from '../../RouteTab'; import { useTranslation } from 'next-i18next'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { form2AppWorkflow } from '@/web/core/app/utils'; -import { TabEnum } from '../context'; +import type { AppFormEditFormType } from '@fastgpt/global/core/app/type'; +import { TabEnum } from '../../context'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyTag from '@fastgpt/web/components/common/Tag/index'; -import { publishStatusStyle } from '../constants'; +import { publishStatusStyle } from '../../constants'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time'; import { useSystemStore } from '@/web/common/system/useSystemStore'; -import SaveButton from '../Workflow/components/SaveButton'; +import SaveButton from '../../Workflow/components/SaveButton'; import { useBoolean, useDebounceEffect, useLockFn } from 'ahooks'; -import { appWorkflow2Form } from '@fastgpt/global/core/app/utils'; import { compareSimpleAppSnapshot, type onSaveSnapshotFnType, type SimpleAppSnapshotType } from './useSnapshots'; -import PublishHistories from '../PublishHistoriesSlider'; -import { type AppVersionSchemaType } from '@fastgpt/global/core/app/version'; +import PublishHistories from '../../PublishHistoriesSlider'; +import type { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload'; import { isProduction } from '@fastgpt/global/common/system/constants'; import { useToast } from '@fastgpt/web/hooks/useToast'; @@ -35,6 +33,7 @@ import { storeEdge2RenderEdge, storeNode2FlowNode } from '@/web/core/workflow/utils'; +import type { AppForm2WorkflowFnType, Form2WorkflowFnType } from './type.d'; const Header = ({ forbiddenSaveSnapshot, @@ -42,14 +41,18 @@ const Header = ({ setAppForm, past, setPast, - saveSnapshot + saveSnapshot, + form2WorkflowFn, + form2AppWorkflowFn }: { forbiddenSaveSnapshot: React.MutableRefObject; - appForm: AppSimpleEditFormType; - setAppForm: (form: AppSimpleEditFormType) => void; + appForm: AppFormEditFormType; + setAppForm: (form: AppFormEditFormType) => void; past: SimpleAppSnapshotType[]; setPast: (value: React.SetStateAction) => void; saveSnapshot: onSaveSnapshotFnType; + form2AppWorkflowFn: AppForm2WorkflowFnType; + form2WorkflowFn: Form2WorkflowFnType; }) => { const { t } = useTranslation(); const { isPc } = useSystem(); @@ -91,7 +94,7 @@ const Header = ({ versionName?: string; autoSave?: boolean; }) => { - const { nodes, edges } = form2AppWorkflow(appForm, t); + const { nodes, edges } = form2WorkflowFn(appForm, t); await onSaveApp({ nodes, edges, @@ -134,7 +137,7 @@ const Header = ({ ); const onSwitchCloudVersion = useCallback( (appVersion: AppVersionSchemaType) => { - const appForm = appWorkflow2Form({ + const appForm = form2AppWorkflowFn({ nodes: appVersion.nodes, chatConfig: appVersion.chatConfig }); @@ -243,7 +246,7 @@ const Header = ({ isLoading={loading} onClickSave={onClickSave} checkData={() => { - const { nodes: storeNodes, edges: storeEdges } = form2AppWorkflow(appForm, t); + const { nodes: storeNodes, edges: storeEdges } = form2WorkflowFn(appForm, t); const nodes = storeNodes.map((item) => storeNode2FlowNode({ item, t })); const edges = storeEdges.map((item) => storeEdge2RenderEdge({ edge: item })); diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/ToolSelector/ToolSelect.tsx similarity index 94% rename from projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx rename to projects/app/src/pageComponents/app/detail/Edit/FormComponent/ToolSelector/ToolSelect.tsx index 8460472ff..a485416b9 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/ToolSelector/ToolSelect.tsx @@ -4,7 +4,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon'; import { useTranslation } from 'next-i18next'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import { SmallAddIcon } from '@chakra-ui/icons'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; +import { type AppFormEditFormType } from '@fastgpt/global/core/app/type'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { theme } from '@fastgpt/web/styles/theme'; import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete'; @@ -14,7 +14,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import Avatar from '@fastgpt/web/components/common/Avatar'; -import ConfigToolModal from './ConfigToolModal'; +import ConfigToolModal from '../../component/ConfigToolModal'; import { getWebLLMModel } from '@/web/common/system/utils'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import { formatToolError } from '@fastgpt/global/core/app/utils'; @@ -25,14 +25,14 @@ const ToolSelect = ({ appForm, setAppForm }: { - appForm: AppSimpleEditFormType; - setAppForm: React.Dispatch>; + appForm: AppFormEditFormType; + setAppForm: React.Dispatch>; }) => { const { t } = useTranslation(); - const [configTool, setConfigTool] = useState< - AppSimpleEditFormType['selectedTools'][number] | null - >(null); + const [configTool, setConfigTool] = useState( + null + ); const { isOpen: isOpenToolsSelect, @@ -141,7 +141,7 @@ const ToolSelect = ({ ml={2} onClick={(e) => { e.stopPropagation(); - setAppForm((state: AppSimpleEditFormType) => ({ + setAppForm((state: AppFormEditFormType) => ({ ...state, selectedTools: state.selectedTools.filter((tool) => tool.id !== item.id) })); diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelectModal.tsx b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/ToolSelector/ToolSelectModal.tsx similarity index 98% rename from projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelectModal.tsx rename to projects/app/src/pageComponents/app/detail/Edit/FormComponent/ToolSelector/ToolSelectModal.tsx index 35d044ebe..67366c2f9 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelectModal.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/ToolSelector/ToolSelectModal.tsx @@ -21,16 +21,16 @@ import FolderPath from '@/components/common/folder/Path'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../../context'; +import { AppContext } from '../../../context'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import { useMemoizedFn } from 'ahooks'; import MyAvatar from '@fastgpt/web/components/common/Avatar'; import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; +import { type AppFormEditFormType } from '@fastgpt/global/core/app/type'; import { useToast } from '@fastgpt/web/hooks/useToast'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import { workflowStartNodeId } from '@/web/core/app/constants'; -import ConfigToolModal from './ConfigToolModal'; +import ConfigToolModal from '../../component/ConfigToolModal'; import CostTooltip from '@/components/core/app/tool/CostTooltip'; import { useSafeTranslation } from '@fastgpt/web/hooks/useSafeTranslation'; import { useSystemStore } from '@/web/common/system/useSystemStore'; @@ -42,7 +42,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; type Props = { selectedTools: FlowNodeTemplateType[]; - chatConfig: AppSimpleEditFormType['chatConfig']; + chatConfig: AppFormEditFormType['chatConfig']; selectedModel: LLMModelItemType; onAddTool: (tool: FlowNodeTemplateType) => void; onRemoveTool: (tool: NodeTemplateListItemType) => void; diff --git a/projects/app/src/pageComponents/app/detail/Agent/styles.module.scss b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/styles.module.scss similarity index 100% rename from projects/app/src/pageComponents/app/detail/Agent/styles.module.scss rename to projects/app/src/pageComponents/app/detail/Edit/FormComponent/styles.module.scss diff --git a/projects/app/src/pageComponents/app/detail/Edit/FormComponent/type.d.ts b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/type.d.ts new file mode 100644 index 000000000..27ef4089a --- /dev/null +++ b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/type.d.ts @@ -0,0 +1,18 @@ +import type { AppChatConfigType, AppFormEditFormType } from '@fastgpt/global/core/app/type'; +import type { WorkflowType } from '../Agent/utils'; +import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node'; + +export type AppForm2WorkflowFnType = ({ + nodes, + chatConfig +}: { + nodes: StoreNodeItemType[]; + chatConfig: AppChatConfigType; +}) => AppFormEditFormType; + +export type Form2WorkflowFnType = ( + data: AppFormEditFormType, + t: any +) => WorkflowType & { + chatConfig: AppChatConfigType; +}; diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/useSnapshots.tsx b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/useSnapshots.tsx similarity index 90% rename from projects/app/src/pageComponents/app/detail/SimpleApp/useSnapshots.tsx rename to projects/app/src/pageComponents/app/detail/Edit/FormComponent/useSnapshots.tsx index a2f322961..3af5684bc 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/useSnapshots.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/FormComponent/useSnapshots.tsx @@ -1,27 +1,27 @@ import { useMemoizedFn } from 'ahooks'; import { useRef, useState } from 'react'; import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; +import { type AppFormEditFormType } from '@fastgpt/global/core/app/type'; import { isEqual } from 'lodash'; export type SimpleAppSnapshotType = { title: string; isSaved?: boolean; - appForm: AppSimpleEditFormType; + appForm: AppFormEditFormType; // abandon - state?: AppSimpleEditFormType; + state?: AppFormEditFormType; diff?: Record; }; export type onSaveSnapshotFnType = (props: { - appForm: AppSimpleEditFormType; // Current edited app form data + appForm: AppFormEditFormType; // Current edited app form data title?: string; isSaved?: boolean; }) => Promise; export const compareSimpleAppSnapshot = ( - appForm1?: AppSimpleEditFormType, - appForm2?: AppSimpleEditFormType + appForm1?: AppFormEditFormType, + appForm2?: AppFormEditFormType ) => { if ( appForm1?.chatConfig && diff --git a/projects/app/src/pageComponents/app/detail/MCPTools/AppCard.tsx b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/AppCard.tsx similarity index 96% rename from projects/app/src/pageComponents/app/detail/MCPTools/AppCard.tsx rename to projects/app/src/pageComponents/app/detail/Edit/MCPTools/AppCard.tsx index bba85b73b..1b7e3eb8b 100644 --- a/projects/app/src/pageComponents/app/detail/MCPTools/AppCard.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/AppCard.tsx @@ -1,13 +1,13 @@ import { Box, Button, Flex, HStack, IconButton } from '@chakra-ui/react'; import React, { useState } from 'react'; -import { AppContext } from '../context'; +import { AppContext } from '../../context'; import { useContextSelector } from 'use-context-selector'; import Avatar from '@fastgpt/web/components/common/Avatar'; import { useTranslation } from 'next-i18next'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyMenu from '@fastgpt/web/components/common/MyMenu'; import { type AppSchema } from '@fastgpt/global/core/app/type'; -import TagsEditModal from '../TagsEditModal'; +import TagsEditModal from '../../TagsEditModal'; const AppCard = () => { const { t } = useTranslation(); diff --git a/projects/app/src/pageComponents/app/detail/MCPTools/ChatTest.tsx b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/ChatTest.tsx similarity index 97% rename from projects/app/src/pageComponents/app/detail/MCPTools/ChatTest.tsx rename to projects/app/src/pageComponents/app/detail/Edit/MCPTools/ChatTest.tsx index 2064d0fbd..0dd7b4645 100644 --- a/projects/app/src/pageComponents/app/detail/MCPTools/ChatTest.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/ChatTest.tsx @@ -1,11 +1,11 @@ import { useChatStore } from '@/web/core/chat/context/useChatStore'; import React, { useEffect, useMemo, useState } from 'react'; import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../context'; +import { AppContext } from '../../context'; import ChatItemContextProvider from '@/web/core/chat/context/chatItemContext'; import ChatRecordContextProvider from '@/web/core/chat/context/chatRecordContext'; import { Box, Button, Flex, HStack } from '@chakra-ui/react'; -import { cardStyles } from '../constants'; +import { cardStyles } from '../../constants'; import { useTranslation } from 'next-i18next'; import { type McpToolConfigType } from '@fastgpt/global/core/app/tool/mcpTool/type'; import { useForm } from 'react-hook-form'; @@ -17,7 +17,7 @@ import { valueTypeToInputType } from '@/components/core/app/formRender/utils'; import { getNodeInputTypeFromSchemaInputType } from '@fastgpt/global/core/app/jsonschema'; import LabelAndFormRender from '@/components/core/app/formRender/LabelAndForm'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; -import ValueTypeLabel from '../WorkflowComponents/Flow/nodes/render/ValueTypeLabel'; +import ValueTypeLabel from '../../WorkflowComponents/Flow/nodes/render/ValueTypeLabel'; import { valueTypeFormat } from '@fastgpt/global/core/workflow/runtime/utils'; const ChatTest = ({ diff --git a/projects/app/src/pageComponents/app/detail/MCPTools/Edit.tsx b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/Edit.tsx similarity index 95% rename from projects/app/src/pageComponents/app/detail/MCPTools/Edit.tsx rename to projects/app/src/pageComponents/app/detail/Edit/MCPTools/Edit.tsx index 5d2f90a57..02d6b633c 100644 --- a/projects/app/src/pageComponents/app/detail/MCPTools/Edit.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/Edit.tsx @@ -1,8 +1,8 @@ import { Box, Flex } from '@chakra-ui/react'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import React from 'react'; -import styles from '../SimpleApp/styles.module.scss'; -import { cardStyles } from '../constants'; +import styles from '../FormComponent/styles.module.scss'; +import { cardStyles } from '../../constants'; import AppCard from './AppCard'; import ChatTest from './ChatTest'; import MyBox from '@fastgpt/web/components/common/MyBox'; diff --git a/projects/app/src/pageComponents/app/detail/MCPTools/EditForm.tsx b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/EditForm.tsx similarity index 99% rename from projects/app/src/pageComponents/app/detail/MCPTools/EditForm.tsx rename to projects/app/src/pageComponents/app/detail/Edit/MCPTools/EditForm.tsx index 2bf63f695..c59eef5f4 100644 --- a/projects/app/src/pageComponents/app/detail/MCPTools/EditForm.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/EditForm.tsx @@ -4,7 +4,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import { useTranslation } from 'next-i18next'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { AppContext } from '../context'; +import { AppContext } from '../../context'; import { useContextSelector } from 'use-context-selector'; import MyIconButton from '@fastgpt/web/components/common/Icon/button'; import { type McpToolConfigType } from '@fastgpt/global/core/app/tool/mcpTool/type'; diff --git a/projects/app/src/pageComponents/app/detail/MCPTools/Header.tsx b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/Header.tsx similarity index 98% rename from projects/app/src/pageComponents/app/detail/MCPTools/Header.tsx rename to projects/app/src/pageComponents/app/detail/Edit/MCPTools/Header.tsx index 3c412b69e..5b4167765 100644 --- a/projects/app/src/pageComponents/app/detail/MCPTools/Header.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/Header.tsx @@ -3,7 +3,7 @@ import FolderPath from '@/components/common/folder/Path'; import { useTranslation } from 'next-i18next'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../context'; +import { AppContext } from '../../context'; import { getAppFolderPath } from '@/web/core/app/api/app'; import { useCallback } from 'react'; import { useRouter } from 'next/router'; diff --git a/projects/app/src/pageComponents/app/detail/MCPTools/index.tsx b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/index.tsx similarity index 97% rename from projects/app/src/pageComponents/app/detail/MCPTools/index.tsx rename to projects/app/src/pageComponents/app/detail/Edit/MCPTools/index.tsx index e647f9edd..05cb4930c 100644 --- a/projects/app/src/pageComponents/app/detail/MCPTools/index.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/MCPTools/index.tsx @@ -3,7 +3,7 @@ import React, { useMemo, useState } from 'react'; import Header from './Header'; import Edit from './Edit'; import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../context'; +import { AppContext } from '../../context'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { type McpToolConfigType } from '@fastgpt/global/core/app/tool/mcpTool/type'; import { type StoreSecretValueType } from '@fastgpt/global/common/secret/type'; diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/Edit.tsx b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/Edit.tsx similarity index 60% rename from projects/app/src/pageComponents/app/detail/SimpleApp/Edit.tsx rename to projects/app/src/pageComponents/app/detail/Edit/SimpleApp/Edit.tsx index c17fd2434..e6f787f77 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/Edit.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/Edit.tsx @@ -1,23 +1,24 @@ import React, { useState } from 'react'; import { Box } from '@chakra-ui/react'; -import ChatTest from './ChatTest'; -import AppCard from './AppCard'; +import AppCard from '../FormComponent/AppCard'; import EditForm from './EditForm'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { cardStyles } from '../constants'; +import { type AppFormEditFormType } from '@fastgpt/global/core/app/type'; +import { cardStyles } from '../../constants'; -import styles from './styles.module.scss'; +import styles from '../FormComponent/styles.module.scss'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; -import { type SimpleAppSnapshotType } from './useSnapshots'; +import { type SimpleAppSnapshotType } from '../FormComponent/useSnapshots'; +import ChatTest from '../FormComponent/ChatTest'; +import { form2AppWorkflow } from './utils'; const Edit = ({ appForm, setAppForm, setPast }: { - appForm: AppSimpleEditFormType; - setAppForm: React.Dispatch>; + appForm: AppFormEditFormType; + setAppForm: React.Dispatch>; setPast: (value: React.SetStateAction) => void; }) => { const { isPc } = useSystem(); @@ -42,7 +43,7 @@ const Edit = ({ flex={'1'} > - + @@ -52,7 +53,11 @@ const Edit = ({ )} {isPc && ( - + )} diff --git a/projects/app/src/pageComponents/app/detail/Agent/EditForm.tsx b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/EditForm.tsx similarity index 98% rename from projects/app/src/pageComponents/app/detail/Agent/EditForm.tsx rename to projects/app/src/pageComponents/app/detail/Edit/SimpleApp/EditForm.tsx index 7737c118d..5c11e233c 100644 --- a/projects/app/src/pageComponents/app/detail/Agent/EditForm.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/EditForm.tsx @@ -9,7 +9,7 @@ import { Button, HStack } from '@chakra-ui/react'; -import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d'; +import type { AppFormEditFormType } from '@fastgpt/global/core/app/type.d'; import { useRouter } from 'next/router'; import { useTranslation } from 'next-i18next'; @@ -30,7 +30,7 @@ import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip'; import { getWebLLMModel } from '@/web/common/system/utils'; -import ToolSelect from './components/ToolSelect'; +import ToolSelect from '../FormComponent/ToolSelector/ToolSelect'; import OptimizerPopover from '@/components/common/PromptEditor/OptimizerPopover'; const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal')); @@ -60,8 +60,8 @@ const EditForm = ({ appForm, setAppForm }: { - appForm: AppSimpleEditFormType; - setAppForm: React.Dispatch>; + appForm: AppFormEditFormType; + setAppForm: React.Dispatch>; }) => { const theme = useTheme(); const router = useRouter(); diff --git a/projects/app/src/pageComponents/app/detail/Agent/components/ToolSelectModal.tsx b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/components/ToolSelectModal.tsx similarity index 98% rename from projects/app/src/pageComponents/app/detail/Agent/components/ToolSelectModal.tsx rename to projects/app/src/pageComponents/app/detail/Edit/SimpleApp/components/ToolSelectModal.tsx index 6d05845a9..baaa63535 100644 --- a/projects/app/src/pageComponents/app/detail/Agent/components/ToolSelectModal.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/components/ToolSelectModal.tsx @@ -39,21 +39,21 @@ import FolderPath from '@/components/common/folder/Path'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../../context'; +import { AppContext } from '../../../context'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import { useMemoizedFn } from 'ahooks'; import MyAvatar from '@fastgpt/web/components/common/Avatar'; import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; +import { type AppFormEditFormType } from '@fastgpt/global/core/app/type'; import { useToast } from '@fastgpt/web/hooks/useToast'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import { workflowStartNodeId } from '@/web/core/app/constants'; -import ConfigToolModal from './ConfigToolModal'; +import ConfigToolModal from '../../component/ConfigToolModal'; import CostTooltip from '@/components/core/app/plugin/CostTooltip'; type Props = { selectedTools: FlowNodeTemplateType[]; - chatConfig: AppSimpleEditFormType['chatConfig']; + chatConfig: AppFormEditFormType['chatConfig']; selectedModel: LLMModelItemType; onAddTool: (tool: FlowNodeTemplateType) => void; onRemoveTool: (tool: NodeTemplateListItemType) => void; diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/index.tsx b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/index.tsx similarity index 82% rename from projects/app/src/pageComponents/app/detail/SimpleApp/index.tsx rename to projects/app/src/pageComponents/app/detail/Edit/SimpleApp/index.tsx index d4becc302..9ea632f87 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/index.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/index.tsx @@ -1,21 +1,21 @@ import React, { useState } from 'react'; -import { appWorkflow2Form, getDefaultAppForm } from '@fastgpt/global/core/app/utils'; +import { getDefaultAppForm } from '@fastgpt/global/core/app/utils'; -import Header from './Header'; +import Header from '../FormComponent/Header'; import { useContextSelector } from 'use-context-selector'; -import { AppContext, TabEnum } from '../context'; +import { AppContext, TabEnum } from '../../context'; import dynamic from 'next/dynamic'; import { Box, Flex } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; -import { type SimpleAppSnapshotType, useSimpleAppSnapshots } from './useSnapshots'; +import { useSimpleAppSnapshots } from '../FormComponent/useSnapshots'; import { useDebounceEffect, useMount } from 'ahooks'; import { v1Workflow2V2 } from '@/web/core/workflow/adapt'; -import { getAppConfigByDiff } from '@/web/core/app/diff'; import { defaultAppSelectFileConfig } from '@fastgpt/global/core/app/constants'; +import { form2AppWorkflow, appWorkflow2Form } from './utils'; const Edit = dynamic(() => import('./Edit')); -const Logs = dynamic(() => import('../Logs/index')); -const PublishChannel = dynamic(() => import('../Publish')); +const Logs = dynamic(() => import('../../Logs/index')); +const PublishChannel = dynamic(() => import('../../Publish')); const SimpleEdit = () => { const { t } = useTranslation(); @@ -81,6 +81,8 @@ const SimpleEdit = () => { past={past} setPast={setPast} saveSnapshot={saveSnapshot} + form2WorkflowFn={form2AppWorkflow} + form2AppWorkflowFn={appWorkflow2Form} /> {currentTab === TabEnum.appEdit ? ( diff --git a/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/utils.ts b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/utils.ts new file mode 100644 index 000000000..ddc664c9f --- /dev/null +++ b/projects/app/src/pageComponents/app/detail/Edit/SimpleApp/utils.ts @@ -0,0 +1,708 @@ +import { type AppChatConfigType, type AppFormEditFormType } from '@fastgpt/global/core/app/type'; +import { type StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; +import { + FlowNodeInputTypeEnum, + FlowNodeTypeEnum +} from '@fastgpt/global/core/workflow/node/constant'; +import { + FlowNodeTemplateTypeEnum, + NodeInputKeyEnum, + NodeOutputKeyEnum, + WorkflowIOValueTypeEnum +} from '@fastgpt/global/core/workflow/constants'; + +import { getNanoid } from '@fastgpt/global/common/string/tools'; +import { type StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; +import { ToolCallNode } from '@fastgpt/global/core/workflow/template/system/toolCall'; +import { + WorkflowStart, + userFilesInput +} from '@fastgpt/global/core/workflow/template/system/workflowStart'; +import { SystemConfigNode } from '@fastgpt/global/core/workflow/template/system/systemConfig'; +import { + AiChatModule, + AiChatQuotePrompt, + AiChatQuoteRole, + AiChatQuoteTemplate +} from '@fastgpt/global/core/workflow/template/system/aiChat/index'; +import { DatasetSearchModule } from '@fastgpt/global/core/workflow/template/system/datasetSearch'; +import { i18nT } from '@fastgpt/web/i18n/utils'; +import { + Input_Template_File_Link, + Input_Template_UserChatInput +} from '@fastgpt/global/core/workflow/template/input'; +import { workflowStartNodeId } from '@/web/core/app/constants'; +import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants'; +import { getAppChatConfig } from '@fastgpt/global/core/workflow/utils'; +import { getDefaultAppForm } from '@fastgpt/global/core/app/utils'; +import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; + +/* format app nodes to edit form */ +export const appWorkflow2Form = ({ + nodes, + chatConfig +}: { + nodes: StoreNodeItemType[]; + chatConfig: AppChatConfigType; +}) => { + const defaultAppForm = getDefaultAppForm(); + const findInputValueByKey = (inputs: FlowNodeInputItemType[], key: string) => { + return inputs.find((item) => item.key === key)?.value; + }; + + nodes.forEach((node) => { + if ( + node.flowNodeType === FlowNodeTypeEnum.chatNode || + node.flowNodeType === FlowNodeTypeEnum.toolCall + ) { + defaultAppForm.aiSettings.model = findInputValueByKey(node.inputs, NodeInputKeyEnum.aiModel); + defaultAppForm.aiSettings.systemPrompt = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.aiSystemPrompt + ); + defaultAppForm.aiSettings.temperature = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.aiChatTemperature + ); + defaultAppForm.aiSettings.maxToken = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.aiChatMaxToken + ); + defaultAppForm.aiSettings.maxHistories = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.history + ); + defaultAppForm.aiSettings.aiChatReasoning = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.aiChatReasoning + ); + defaultAppForm.aiSettings.aiChatTopP = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.aiChatTopP + ); + defaultAppForm.aiSettings.aiChatStopSign = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.aiChatStopSign + ); + defaultAppForm.aiSettings.aiChatResponseFormat = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.aiChatResponseFormat + ); + defaultAppForm.aiSettings.aiChatJsonSchema = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.aiChatJsonSchema + ); + } else if (node.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) { + defaultAppForm.dataset.datasets = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSelectList + ); + defaultAppForm.dataset.similarity = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSimilarity + ); + defaultAppForm.dataset.limit = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetMaxTokens + ); + defaultAppForm.dataset.searchMode = + findInputValueByKey(node.inputs, NodeInputKeyEnum.datasetSearchMode) || + DatasetSearchModeEnum.embedding; + defaultAppForm.dataset.embeddingWeight = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSearchEmbeddingWeight + ); + // Rerank + defaultAppForm.dataset.usingReRank = !!findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSearchUsingReRank + ); + defaultAppForm.dataset.rerankModel = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSearchRerankModel + ); + defaultAppForm.dataset.rerankWeight = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSearchRerankWeight + ); + // Query extension + defaultAppForm.dataset.datasetSearchUsingExtensionQuery = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSearchUsingExtensionQuery + ); + defaultAppForm.dataset.datasetSearchExtensionModel = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSearchExtensionModel + ); + defaultAppForm.dataset.datasetSearchExtensionBg = findInputValueByKey( + node.inputs, + NodeInputKeyEnum.datasetSearchExtensionBg + ); + } else if ( + node.flowNodeType === FlowNodeTypeEnum.pluginModule || + node.flowNodeType === FlowNodeTypeEnum.appModule || + node.flowNodeType === FlowNodeTypeEnum.tool || + node.flowNodeType === FlowNodeTypeEnum.toolSet + ) { + if (!node.pluginId) return; + + defaultAppForm.selectedTools.push({ + id: node.nodeId, + pluginId: node.pluginId, + name: node.name, + avatar: node.avatar, + intro: node.intro || '', + flowNodeType: node.flowNodeType, + showStatus: node.showStatus, + version: node.version, + inputs: node.inputs, + outputs: node.outputs, + templateType: FlowNodeTemplateTypeEnum.other, + pluginData: node.pluginData, + toolConfig: node.toolConfig + }); + } else if (node.flowNodeType === FlowNodeTypeEnum.systemConfig) { + defaultAppForm.chatConfig = getAppChatConfig({ + chatConfig, + systemConfigNode: node, + isPublicFetch: true + }); + } + }); + + return defaultAppForm; +}; + +export type WorkflowType = { + nodes: StoreNodeItemType[]; + edges: StoreEdgeItemType[]; +}; +export function form2AppWorkflow( + data: AppFormEditFormType, + t: any // i18nT +): WorkflowType & { + chatConfig: AppChatConfigType; +} { + const datasetNodeId = 'iKBoX2vIzETU'; + const aiChatNodeId = '7BdojPlukIQw'; + const selectedDatasets = data.dataset.datasets; + function systemConfigTemplate(): StoreNodeItemType { + return { + nodeId: SystemConfigNode.id, + name: t(SystemConfigNode.name), + intro: '', + flowNodeType: SystemConfigNode.flowNodeType, + position: { + x: 531.2422736065552, + y: -486.7611729549753 + }, + version: SystemConfigNode.version, + inputs: [], + outputs: [] + }; + } + function workflowStartTemplate(): StoreNodeItemType { + return { + nodeId: workflowStartNodeId, + name: t(WorkflowStart.name), + intro: '', + avatar: WorkflowStart.avatar, + flowNodeType: WorkflowStart.flowNodeType, + position: { + x: 558.4082376415505, + y: 123.72387429194112 + }, + version: WorkflowStart.version, + inputs: WorkflowStart.inputs, + outputs: [...WorkflowStart.outputs, userFilesInput] + }; + } + function aiChatTemplate(formData: AppFormEditFormType): StoreNodeItemType { + return { + nodeId: aiChatNodeId, + name: t(AiChatModule.name), + intro: t(AiChatModule.intro), + avatar: AiChatModule.avatar, + flowNodeType: AiChatModule.flowNodeType, + showStatus: true, + position: { + x: 1106.3238387960757, + y: -350.6030674683474 + }, + version: AiChatModule.version, + inputs: [ + { + key: NodeInputKeyEnum.aiModel, + renderTypeList: [FlowNodeInputTypeEnum.settingLLMModel, FlowNodeInputTypeEnum.reference], + label: '', + valueType: WorkflowIOValueTypeEnum.string, + value: formData.aiSettings.model + }, + { + key: NodeInputKeyEnum.aiChatTemperature, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + value: formData.aiSettings.temperature, + valueType: WorkflowIOValueTypeEnum.number, + min: 0, + max: 10, + step: 1 + }, + { + key: NodeInputKeyEnum.aiChatMaxToken, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + value: formData.aiSettings.maxToken, + valueType: WorkflowIOValueTypeEnum.number, + min: 100, + max: 4000, + step: 50 + }, + { + key: NodeInputKeyEnum.aiChatIsResponseText, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + value: true, + valueType: WorkflowIOValueTypeEnum.boolean + }, + AiChatQuoteRole, + AiChatQuoteTemplate, + AiChatQuotePrompt, + { + key: NodeInputKeyEnum.aiSystemPrompt, + renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference], + max: 3000, + valueType: WorkflowIOValueTypeEnum.string, + label: 'core.ai.Prompt', + description: 'core.app.tip.systemPromptTip', + placeholder: 'core.app.tip.chatNodeSystemPromptTip', + value: formData.aiSettings.systemPrompt + }, + { + key: NodeInputKeyEnum.history, + renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference], + valueType: WorkflowIOValueTypeEnum.chatHistory, + label: 'core.module.input.label.chat history', + required: true, + min: 0, + max: 30, + value: formData.aiSettings.maxHistories + }, + { + key: NodeInputKeyEnum.userChatInput, + renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea], + valueType: WorkflowIOValueTypeEnum.string, + label: i18nT('common:core.module.input.label.user question'), + required: true, + toolDescription: i18nT('common:core.module.input.label.user question'), + value: [workflowStartNodeId, NodeInputKeyEnum.userChatInput] + }, + { + key: NodeInputKeyEnum.aiChatDatasetQuote, + renderTypeList: [FlowNodeInputTypeEnum.settingDatasetQuotePrompt], + label: '', + debugLabel: i18nT('common:core.module.Dataset quote.label'), + description: '', + valueType: WorkflowIOValueTypeEnum.datasetQuote, + value: selectedDatasets?.length > 0 ? [datasetNodeId, 'quoteQA'] : undefined + }, + { + ...Input_Template_File_Link, + value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]] + }, + { + key: NodeInputKeyEnum.aiChatVision, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.boolean, + value: true + }, + { + key: NodeInputKeyEnum.aiChatReasoning, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.boolean, + value: formData.aiSettings.aiChatReasoning + }, + { + key: NodeInputKeyEnum.aiChatTopP, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.number, + value: formData.aiSettings.aiChatTopP + }, + { + key: NodeInputKeyEnum.aiChatStopSign, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.string, + value: formData.aiSettings.aiChatStopSign + }, + { + key: NodeInputKeyEnum.aiChatResponseFormat, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.string, + value: formData.aiSettings.aiChatResponseFormat + }, + { + key: NodeInputKeyEnum.aiChatJsonSchema, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.string, + value: formData.aiSettings.aiChatJsonSchema + } + ], + outputs: AiChatModule.outputs + }; + } + function datasetNodeTemplate(formData: AppFormEditFormType, question: any): StoreNodeItemType { + return { + nodeId: datasetNodeId, + name: t(DatasetSearchModule.name), + intro: t('app:dataset_search_tool_description'), + avatar: DatasetSearchModule.avatar, + flowNodeType: DatasetSearchModule.flowNodeType, + showStatus: true, + position: { + x: 918.5901682164496, + y: -227.11542247619582 + }, + version: DatasetSearchModule.version, + inputs: [ + { + key: NodeInputKeyEnum.datasetSelectList, + renderTypeList: [FlowNodeInputTypeEnum.selectDataset, FlowNodeInputTypeEnum.reference], + label: i18nT('common:core.module.input.label.Select dataset'), + value: selectedDatasets, + valueType: WorkflowIOValueTypeEnum.selectDataset, + list: [], + required: true + }, + { + key: NodeInputKeyEnum.datasetSimilarity, + renderTypeList: [FlowNodeInputTypeEnum.selectDatasetParamsModal], + label: '', + value: formData.dataset.similarity, + valueType: WorkflowIOValueTypeEnum.number + }, + { + key: NodeInputKeyEnum.datasetMaxTokens, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + value: formData.dataset.limit, + valueType: WorkflowIOValueTypeEnum.number + }, + { + key: NodeInputKeyEnum.datasetSearchMode, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.string, + value: formData.dataset.searchMode + }, + { + key: NodeInputKeyEnum.datasetSearchEmbeddingWeight, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.number, + value: formData.dataset.embeddingWeight + }, + { + key: NodeInputKeyEnum.datasetSearchUsingReRank, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.boolean, + value: formData.dataset.usingReRank + }, + { + key: NodeInputKeyEnum.datasetSearchRerankModel, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.string, + value: formData.dataset.rerankModel + }, + { + key: NodeInputKeyEnum.datasetSearchRerankWeight, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.number, + value: formData.dataset.rerankWeight + }, + { + key: NodeInputKeyEnum.datasetSearchUsingExtensionQuery, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.boolean, + value: formData.dataset.datasetSearchUsingExtensionQuery + }, + { + key: NodeInputKeyEnum.datasetSearchExtensionModel, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.string, + value: formData.dataset.datasetSearchExtensionModel + }, + { + key: NodeInputKeyEnum.datasetSearchExtensionBg, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.string, + value: formData.dataset.datasetSearchExtensionBg + }, + { + ...Input_Template_UserChatInput, + toolDescription: i18nT('workflow:content_to_search'), + value: question + } + ], + outputs: DatasetSearchModule.outputs + }; + } + + // Start, AiChat + function simpleChatTemplate(formData: AppFormEditFormType): WorkflowType { + return { + nodes: [aiChatTemplate(formData)], + edges: [ + { + source: workflowStartNodeId, + target: aiChatNodeId, + sourceHandle: `${workflowStartNodeId}-source-right`, + targetHandle: `${aiChatNodeId}-target-left` + } + ] + }; + } + // Start, Dataset search, AiChat + function datasetTemplate(formData: AppFormEditFormType): WorkflowType { + return { + nodes: [ + aiChatTemplate(formData), + datasetNodeTemplate(formData, [workflowStartNodeId, 'userChatInput']) + ], + edges: [ + { + source: workflowStartNodeId, + target: datasetNodeId, + sourceHandle: `${workflowStartNodeId}-source-right`, + targetHandle: `${datasetNodeId}-target-left` + }, + { + source: datasetNodeId, + target: aiChatNodeId, + sourceHandle: `${datasetNodeId}-source-right`, + targetHandle: `${aiChatNodeId}-target-left` + } + ] + }; + } + function toolTemplates(formData: AppFormEditFormType): WorkflowType { + const toolNodeId = getNanoid(6); + + // Dataset tool config + const datasetTool: WorkflowType | null = + selectedDatasets.length > 0 + ? { + nodes: [datasetNodeTemplate(formData, '')], + edges: [ + { + source: toolNodeId, + target: datasetNodeId, + sourceHandle: 'selectedTools', + targetHandle: 'selectedTools' + } + ] + } + : null; + + // Computed tools config + const pluginTool: WorkflowType[] = formData.selectedTools.map((tool, i) => { + const nodeId = getNanoid(6); + return { + nodes: [ + { + nodeId, + id: tool.id, + pluginId: tool.pluginId, + name: tool.name, + intro: tool.intro, + toolDescription: tool.toolDescription, + avatar: tool.avatar, + flowNodeType: tool.flowNodeType, + showStatus: tool.showStatus, + position: { + x: 500 + 500 * (i + 1), + y: 545 + }, + toolConfig: tool.toolConfig, + pluginData: tool.pluginData, + inputs: tool.inputs.map((input) => { + // Special key value + if (input.key === NodeInputKeyEnum.forbidStream) { + input.value = true; + } + // Special tool + if ( + tool.flowNodeType === FlowNodeTypeEnum.appModule && + input.key === NodeInputKeyEnum.history + ) { + return { + ...input, + value: formData.aiSettings.maxHistories + }; + } + return input; + }), + outputs: tool.outputs + } + ], + edges: [ + { + source: toolNodeId, + target: nodeId, + sourceHandle: 'selectedTools', + targetHandle: 'selectedTools' + } + ] + }; + }); + + const config: WorkflowType = { + nodes: [ + { + nodeId: toolNodeId, + name: ToolCallNode.name, + intro: ToolCallNode.intro, + avatar: ToolCallNode.avatar, + flowNodeType: ToolCallNode.flowNodeType, + showStatus: true, + position: { + x: 1062.1738942532802, + y: -223.65033022650476 + }, + version: ToolCallNode.version, + inputs: [ + { + key: NodeInputKeyEnum.aiModel, + renderTypeList: [ + FlowNodeInputTypeEnum.settingLLMModel, + FlowNodeInputTypeEnum.reference + ], + label: 'core.module.input.label.aiModel', + valueType: WorkflowIOValueTypeEnum.string, + llmModelType: 'all', + value: formData.aiSettings.model + }, + { + key: 'temperature', + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + value: formData.aiSettings.temperature, + valueType: WorkflowIOValueTypeEnum.number, + min: 0, + max: 10, + step: 1 + }, + { + key: 'maxToken', + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + value: formData.aiSettings.maxToken, + valueType: WorkflowIOValueTypeEnum.number, + min: 100, + max: 4000, + step: 50 + }, + { + key: 'systemPrompt', + renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference], + max: 3000, + valueType: WorkflowIOValueTypeEnum.string, + label: 'core.ai.Prompt', + description: 'core.app.tip.systemPromptTip', + placeholder: 'core.app.tip.chatNodeSystemPromptTip', + value: formData.aiSettings.systemPrompt + }, + { + key: 'history', + renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference], + valueType: WorkflowIOValueTypeEnum.chatHistory, + label: 'core.module.input.label.chat history', + required: true, + min: 0, + max: 30, + value: formData.aiSettings.maxHistories + }, + { + ...Input_Template_File_Link, + value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]] + }, + { + key: 'userChatInput', + renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea], + valueType: WorkflowIOValueTypeEnum.string, + label: i18nT('common:core.module.input.label.user question'), + required: true, + value: [workflowStartNodeId, 'userChatInput'] + }, + { + key: NodeInputKeyEnum.aiChatVision, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.boolean, + value: true + }, + { + key: NodeInputKeyEnum.aiChatReasoning, + renderTypeList: [FlowNodeInputTypeEnum.hidden], + label: '', + valueType: WorkflowIOValueTypeEnum.boolean, + value: formData.aiSettings.aiChatReasoning + } + ], + outputs: ToolCallNode.outputs + }, + // tool nodes + ...(datasetTool ? datasetTool.nodes : []), + ...pluginTool.map((tool) => tool.nodes).flat() + ], + edges: [ + { + source: workflowStartNodeId, + target: toolNodeId, + sourceHandle: `${workflowStartNodeId}-source-right`, + targetHandle: `${toolNodeId}-target-left` + }, + // tool edges + ...(datasetTool ? datasetTool.edges : []), + ...pluginTool.map((tool) => tool.edges).flat() + ] + }; + + // Add t + config.nodes.forEach((node) => { + node.name = t(node.name); + node.intro = t(node.intro); + + node.inputs.forEach((input) => { + input.label = t(input.label); + input.description = t(input.description); + input.toolDescription = t(input.toolDescription); + }); + }); + + return config; + } + + const workflow = (() => { + if (data.selectedTools.length > 0) return toolTemplates(data); + if (selectedDatasets.length > 0) return datasetTemplate(data); + return simpleChatTemplate(data); + })(); + + return { + nodes: [systemConfigTemplate(), workflowStartTemplate(), ...workflow.nodes], + edges: workflow.edges, + chatConfig: data.chatConfig + }; +} diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ConfigToolModal.tsx b/projects/app/src/pageComponents/app/detail/Edit/component/ConfigToolModal.tsx similarity index 96% rename from projects/app/src/pageComponents/app/detail/SimpleApp/components/ConfigToolModal.tsx rename to projects/app/src/pageComponents/app/detail/Edit/component/ConfigToolModal.tsx index 407df20de..37e0ec944 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ConfigToolModal.tsx +++ b/projects/app/src/pageComponents/app/detail/Edit/component/ConfigToolModal.tsx @@ -4,8 +4,8 @@ import React from 'react'; import { useTranslation } from 'next-i18next'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { Box } from '@chakra-ui/react'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { childAppSystemKey } from './ToolSelectModal'; +import { type AppFormEditFormType } from '@fastgpt/global/core/app/type'; +import { childAppSystemKey } from '../FormComponent/ToolSelector/ToolSelectModal'; import { Controller, useForm } from 'react-hook-form'; import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; @@ -26,9 +26,9 @@ const ConfigToolModal = ({ onCloseConfigTool, onAddTool }: { - configTool: AppSimpleEditFormType['selectedTools'][number]; + configTool: AppFormEditFormType['selectedTools'][number]; onCloseConfigTool: () => void; - onAddTool: (tool: AppSimpleEditFormType['selectedTools'][number]) => void; + onAddTool: (tool: AppFormEditFormType['selectedTools'][number]) => void; }) => { const { t } = useTranslation(); const [isOpenSecretModal, { setTrue: setTrueSecretModal, setFalse: setFalseSecretModal }] = diff --git a/projects/app/src/pageComponents/app/detail/ExportConfigPopover.tsx b/projects/app/src/pageComponents/app/detail/ExportConfigPopover.tsx index b430c38bd..ead4ed108 100644 --- a/projects/app/src/pageComponents/app/detail/ExportConfigPopover.tsx +++ b/projects/app/src/pageComponents/app/detail/ExportConfigPopover.tsx @@ -6,7 +6,7 @@ import { filterSensitiveNodesData } from '@/web/core/workflow/utils'; import { useCopyData } from '@fastgpt/web/hooks/useCopyData'; import MyPopover from '@fastgpt/web/components/common/MyPopover'; import { fileDownload } from '@/web/common/file/utils'; -import { type AppChatConfigType, type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; +import { type AppChatConfigType, type AppFormEditFormType } from '@fastgpt/global/core/app/type'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { filterSensitiveFormData } from '@/web/core/app/utils'; import { type RequireOnlyOne } from '@fastgpt/global/common/type/utils'; @@ -29,7 +29,7 @@ const ExportConfigPopover = ({ edges: StoreEdgeItemType[]; } | undefined; - appForm: AppSimpleEditFormType; + appForm: AppFormEditFormType; }>) => { const { t } = useTranslation(); const { copyData } = useCopyData(); diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/AppCard.tsx b/projects/app/src/pageComponents/app/detail/SimpleApp/AppCard.tsx deleted file mode 100644 index cbcdf4588..000000000 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/AppCard.tsx +++ /dev/null @@ -1,216 +0,0 @@ -import React, { useState } from 'react'; -import { - Box, - Flex, - Button, - IconButton, - HStack, - ModalBody, - Checkbox, - ModalFooter -} from '@chakra-ui/react'; -import { useRouter } from 'next/router'; -import { type AppSchema, type AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d'; -import { useTranslation } from 'next-i18next'; -import Avatar from '@fastgpt/web/components/common/Avatar'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import TagsEditModal from '../TagsEditModal'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { AppContext } from '@/pageComponents/app/detail/context'; -import { useContextSelector } from 'use-context-selector'; -import MyMenu from '@fastgpt/web/components/common/MyMenu'; -import MyModal from '@fastgpt/web/components/common/MyModal'; -import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { postTransition2Workflow } from '@/web/core/app/api/app'; -import { form2AppWorkflow } from '@/web/core/app/utils'; -import { type SimpleAppSnapshotType } from './useSnapshots'; -import ExportConfigPopover from '@/pageComponents/app/detail/ExportConfigPopover'; -import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants'; - -const AppCard = ({ - appForm, - setPast -}: { - appForm: AppSimpleEditFormType; - setPast: (value: React.SetStateAction) => void; -}) => { - const router = useRouter(); - const { t } = useTranslation(); - const onSaveApp = useContextSelector(AppContext, (v) => v.onSaveApp); - const appDetail = useContextSelector(AppContext, (v) => v.appDetail); - const onOpenInfoEdit = useContextSelector(AppContext, (v) => v.onOpenInfoEdit); - const onDelApp = useContextSelector(AppContext, (v) => v.onDelApp); - - const appId = appDetail._id; - const { feConfigs } = useSystemStore(); - const [TeamTagsSet, setTeamTagsSet] = useState(); - - // transition to workflow - const [transitionCreateNew, setTransitionCreateNew] = useState(); - const { runAsync: onTransition, loading: transiting } = useRequest2( - async () => { - const { nodes, edges } = form2AppWorkflow(appForm, t); - await onSaveApp({ - nodes, - edges, - chatConfig: appForm.chatConfig, - isPublish: false, - versionName: t('app:transition_to_workflow') - }); - - return postTransition2Workflow({ appId, createNew: transitionCreateNew }); - }, - { - onSuccess: ({ id }) => { - if (id) { - router.replace({ - query: { - appId: id - } - }); - } else { - setPast([]); - router.reload(); - } - }, - successToast: t('common:Success') - } - ); - - return ( - <> - {/* basic info */} - - - - - {appDetail.name} - - - - {appDetail.intro || t('common:core.app.tip.Add a intro to app')} - - - - {appDetail.permission.hasManagePer && ( - - )} - {appDetail.permission.isOwner && ( - } - aria-label={''} - /> - } - menuList={[ - { - children: [ - { - label: ( - - - - ) - }, - { - icon: 'core/app/type/workflow', - label: t('app:transition_to_workflow'), - onClick: () => setTransitionCreateNew(true) - }, - ...(appDetail.permission.hasWritePer && feConfigs?.show_team_chat - ? [ - { - icon: 'core/chat/fileSelect', - label: t('app:team_tags_set'), - onClick: () => setTeamTagsSet(appDetail) - } - ] - : []) - ] - }, - { - children: [ - { - icon: 'delete', - type: 'danger', - label: t('common:Delete'), - onClick: onDelApp - } - ] - } - ]} - /> - )} - - {/* {isPc && ( */} - {/* (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)} */} - {/* > */} - {/* */} - {/* */} - {/* )} */} - - - {TeamTagsSet && setTeamTagsSet(undefined)} />} - {transitionCreateNew !== undefined && ( - - - {t('app:transition_to_workflow_create_new_tip')} - setTransitionCreateNew((state) => !state)}> - } - /> - {t('app:transition_to_workflow_create_new_placeholder')} - - - - - - - - )} - - ); -}; - -export default React.memo(AppCard); diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx b/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx deleted file mode 100644 index 4a7bdbf79..000000000 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { Box, Flex, IconButton } from '@chakra-ui/react'; -import { useTranslation } from 'next-i18next'; -import React, { useEffect, useMemo } from 'react'; -import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; -import MyIcon from '@fastgpt/web/components/common/Icon'; - -import { useSafeState } from 'ahooks'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { form2AppWorkflow } from '@/web/core/app/utils'; -import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '../context'; -import { useChatTest } from '../useChatTest'; -import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; -import ChatRecordContextProvider from '@/web/core/chat/context/chatRecordContext'; -import { useChatStore } from '@/web/core/chat/context/useChatStore'; -import MyBox from '@fastgpt/web/components/common/MyBox'; -import { cardStyles } from '../constants'; -import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList'; -import VariablePopover from '@/components/core/chat/ChatContainer/components/VariablePopover'; -import { ChatTypeEnum } from '@/components/core/chat/ChatContainer/ChatBox/constants'; - -type Props = { - appForm: AppSimpleEditFormType; - setRenderEdit: React.Dispatch>; -}; -const ChatTest = ({ appForm, setRenderEdit }: Props) => { - const { t } = useTranslation(); - - const { appDetail } = useContextSelector(AppContext, (v) => v); - const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData); - const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData); - // form2AppWorkflow dependent allDatasets - const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible); - - const [workflowData, setWorkflowData] = useSafeState({ - nodes: appDetail.modules || [], - edges: appDetail.edges || [] - }); - - useEffect(() => { - const { nodes, edges } = form2AppWorkflow(appForm, t); - setWorkflowData({ nodes, edges }); - }, [appForm, setWorkflowData, t]); - - useEffect(() => { - setRenderEdit(!datasetCiteData); - }, [datasetCiteData, setRenderEdit]); - - const { ChatContainer, restartChat } = useChatTest({ - ...workflowData, - chatConfig: appForm.chatConfig, - isReady: true - }); - - return ( - - - - - {t('app:chat_debug')} - - {!isVariableVisible && } - - - } - variant={'whiteDanger'} - borderRadius={'md'} - aria-label={'delete'} - onClick={(e) => { - e.stopPropagation(); - restartChat(); - }} - /> - - - - - - - {datasetCiteData && ( - - setCiteModalData(undefined)} - /> - - )} - - ); -}; - -const Render = ({ appForm, setRenderEdit }: Props) => { - const { chatId } = useChatStore(); - const { appDetail } = useContextSelector(AppContext, (v) => v); - - const chatRecordProviderParams = useMemo( - () => ({ - chatId: chatId, - appId: appDetail._id - }), - [appDetail._id, chatId] - ); - - return ( - - - - - - ); -}; - -export default React.memo(Render); diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/styles.module.scss b/projects/app/src/pageComponents/app/detail/SimpleApp/styles.module.scss deleted file mode 100644 index f07d856e4..000000000 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/styles.module.scss +++ /dev/null @@ -1,10 +0,0 @@ -.EditAppBox { - &::-webkit-scrollbar-thumb { - background: #dfe2ea !important; - transition: background 1s; - } - - &::-webkit-scrollbar-thumb:hover { - background: var(--chakra-colors-gray-300) !important; - } -} diff --git a/projects/app/src/pageComponents/chat/ChatSetting/ToolSelectModal.tsx b/projects/app/src/pageComponents/chat/ChatSetting/ToolSelectModal.tsx index a0d4dcc4b..49b775077 100644 --- a/projects/app/src/pageComponents/chat/ChatSetting/ToolSelectModal.tsx +++ b/projects/app/src/pageComponents/chat/ChatSetting/ToolSelectModal.tsx @@ -21,19 +21,19 @@ import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import { useMemoizedFn } from 'ahooks'; import MyAvatar from '@fastgpt/web/components/common/Avatar'; import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; -import { type AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; +import { type AppFormEditFormType } from '@fastgpt/global/core/app/type'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { workflowStartNodeId } from '@/web/core/app/constants'; -import ConfigToolModal from '@/pageComponents/app/detail/SimpleApp/components/ConfigToolModal'; import type { ChatSettingType } from '@fastgpt/global/core/chat/setting/type'; import CostTooltip from '@/components/core/app/tool/CostTooltip'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import ToolTagFilterBox from '@fastgpt/web/components/core/plugin/tool/TagFilterBox'; import { getPluginToolTags } from '@/web/core/plugin/toolTag/api'; +import ConfigToolModal from '@/pageComponents/app/detail/Edit/component/ConfigToolModal'; type Props = { selectedTools: ChatSettingType['selectedTools']; - chatConfig?: AppSimpleEditFormType['chatConfig']; + chatConfig?: AppFormEditFormType['chatConfig']; onAddTool: (tool: FlowNodeTemplateType) => void; onRemoveTool: (tool: NodeTemplateListItemType) => void; }; diff --git a/projects/app/src/pages/app/detail/index.tsx b/projects/app/src/pages/app/detail/index.tsx index 864fd2a9f..c403a0886 100644 --- a/projects/app/src/pages/app/detail/index.tsx +++ b/projects/app/src/pages/app/detail/index.tsx @@ -11,11 +11,11 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { useChatStore } from '@/web/core/chat/context/useChatStore'; import { TabEnum } from '@/pageComponents/app/detail/context'; -const SimpleEdit = dynamic(() => import('@/pageComponents/app/detail/SimpleApp'), { +const SimpleEdit = dynamic(() => import('@/pageComponents/app/detail/Edit/SimpleApp'), { ssr: false, loading: () => }); -const AgentEdit = dynamic(() => import('@/pageComponents/app/detail/Agent'), { +const AgentEdit = dynamic(() => import('@/pageComponents/app/detail/Edit/Agent'), { ssr: false, loading: () => }); @@ -27,7 +27,7 @@ const Plugin = dynamic(() => import('@/pageComponents/app/detail/Plugin'), { ssr: false, loading: () => }); -const MCPTools = dynamic(() => import('@/pageComponents/app/detail/MCPTools'), { +const MCPTools = dynamic(() => import('@/pageComponents/app/detail/Edit/MCPTools'), { ssr: false, loading: () => }); diff --git a/projects/app/src/web/core/app/utils.ts b/projects/app/src/web/core/app/utils.ts index 7ff078b24..ea254feb7 100644 --- a/projects/app/src/web/core/app/utils.ts +++ b/projects/app/src/web/core/app/utils.ts @@ -1,579 +1,19 @@ import { - type AppChatConfigType, type AppDetailType, type AppSchema, - type AppSimpleEditFormType + type AppFormEditFormType } from '@fastgpt/global/core/app/type'; -import { type StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; import { chatHistoryValueDesc, - FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; -import { - NodeInputKeyEnum, - NodeOutputKeyEnum, - WorkflowIOValueTypeEnum -} from '@fastgpt/global/core/workflow/constants'; +import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; -import { getNanoid } from '@fastgpt/global/common/string/tools'; -import { type StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; import { type EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type'; -import { ToolCallNode } from '@fastgpt/global/core/workflow/template/system/toolCall'; -import { - WorkflowStart, - userFilesInput -} from '@fastgpt/global/core/workflow/template/system/workflowStart'; -import { SystemConfigNode } from '@fastgpt/global/core/workflow/template/system/systemConfig'; -import { - AiChatModule, - AiChatQuotePrompt, - AiChatQuoteRole, - AiChatQuoteTemplate -} from '@fastgpt/global/core/workflow/template/system/aiChat/index'; -import { DatasetSearchModule } from '@fastgpt/global/core/workflow/template/system/datasetSearch'; import { i18nT } from '@fastgpt/web/i18n/utils'; -import { - Input_Template_File_Link, - Input_Template_UserChatInput -} from '@fastgpt/global/core/workflow/template/input'; -import { workflowStartNodeId } from './constants'; import { getDefaultAppForm } from '@fastgpt/global/core/app/utils'; -type WorkflowType = { - nodes: StoreNodeItemType[]; - edges: StoreEdgeItemType[]; -}; -export function form2AppWorkflow( - data: AppSimpleEditFormType, - t: any // i18nT -): WorkflowType & { - chatConfig: AppChatConfigType; -} { - const datasetNodeId = 'iKBoX2vIzETU'; - const aiChatNodeId = '7BdojPlukIQw'; - const selectedDatasets = data.dataset.datasets; - function systemConfigTemplate(): StoreNodeItemType { - return { - nodeId: SystemConfigNode.id, - name: t(SystemConfigNode.name), - intro: '', - flowNodeType: SystemConfigNode.flowNodeType, - position: { - x: 531.2422736065552, - y: -486.7611729549753 - }, - version: SystemConfigNode.version, - inputs: [], - outputs: [] - }; - } - function workflowStartTemplate(): StoreNodeItemType { - return { - nodeId: workflowStartNodeId, - name: t(WorkflowStart.name), - intro: '', - avatar: WorkflowStart.avatar, - flowNodeType: WorkflowStart.flowNodeType, - position: { - x: 558.4082376415505, - y: 123.72387429194112 - }, - version: WorkflowStart.version, - inputs: WorkflowStart.inputs, - outputs: [...WorkflowStart.outputs, userFilesInput] - }; - } - function aiChatTemplate(formData: AppSimpleEditFormType): StoreNodeItemType { - return { - nodeId: aiChatNodeId, - name: t(AiChatModule.name), - intro: t(AiChatModule.intro), - avatar: AiChatModule.avatar, - flowNodeType: AiChatModule.flowNodeType, - showStatus: true, - position: { - x: 1106.3238387960757, - y: -350.6030674683474 - }, - version: AiChatModule.version, - inputs: [ - { - key: NodeInputKeyEnum.aiModel, - renderTypeList: [FlowNodeInputTypeEnum.settingLLMModel, FlowNodeInputTypeEnum.reference], - label: '', - valueType: WorkflowIOValueTypeEnum.string, - value: formData.aiSettings.model - }, - { - key: NodeInputKeyEnum.aiChatTemperature, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - value: formData.aiSettings.temperature, - valueType: WorkflowIOValueTypeEnum.number, - min: 0, - max: 10, - step: 1 - }, - { - key: NodeInputKeyEnum.aiChatMaxToken, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - value: formData.aiSettings.maxToken, - valueType: WorkflowIOValueTypeEnum.number, - min: 100, - max: 4000, - step: 50 - }, - { - key: NodeInputKeyEnum.aiChatIsResponseText, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - value: true, - valueType: WorkflowIOValueTypeEnum.boolean - }, - AiChatQuoteRole, - AiChatQuoteTemplate, - AiChatQuotePrompt, - { - key: NodeInputKeyEnum.aiSystemPrompt, - renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference], - max: 3000, - valueType: WorkflowIOValueTypeEnum.string, - label: 'core.ai.Prompt', - description: 'core.app.tip.systemPromptTip', - placeholder: 'core.app.tip.chatNodeSystemPromptTip', - value: formData.aiSettings.systemPrompt - }, - { - key: NodeInputKeyEnum.history, - renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference], - valueType: WorkflowIOValueTypeEnum.chatHistory, - label: 'core.module.input.label.chat history', - required: true, - min: 0, - max: 30, - value: formData.aiSettings.maxHistories - }, - { - key: NodeInputKeyEnum.userChatInput, - renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea], - valueType: WorkflowIOValueTypeEnum.string, - label: i18nT('common:core.module.input.label.user question'), - required: true, - toolDescription: i18nT('common:core.module.input.label.user question'), - value: [workflowStartNodeId, NodeInputKeyEnum.userChatInput] - }, - { - key: NodeInputKeyEnum.aiChatDatasetQuote, - renderTypeList: [FlowNodeInputTypeEnum.settingDatasetQuotePrompt], - label: '', - debugLabel: i18nT('common:core.module.Dataset quote.label'), - description: '', - valueType: WorkflowIOValueTypeEnum.datasetQuote, - value: selectedDatasets?.length > 0 ? [datasetNodeId, 'quoteQA'] : undefined - }, - { - ...Input_Template_File_Link, - value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]] - }, - { - key: NodeInputKeyEnum.aiChatVision, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.boolean, - value: true - }, - { - key: NodeInputKeyEnum.aiChatReasoning, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.boolean, - value: formData.aiSettings.aiChatReasoning - }, - { - key: NodeInputKeyEnum.aiChatTopP, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.number, - value: formData.aiSettings.aiChatTopP - }, - { - key: NodeInputKeyEnum.aiChatStopSign, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.string, - value: formData.aiSettings.aiChatStopSign - }, - { - key: NodeInputKeyEnum.aiChatResponseFormat, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.string, - value: formData.aiSettings.aiChatResponseFormat - }, - { - key: NodeInputKeyEnum.aiChatJsonSchema, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.string, - value: formData.aiSettings.aiChatJsonSchema - } - ], - outputs: AiChatModule.outputs - }; - } - function datasetNodeTemplate(formData: AppSimpleEditFormType, question: any): StoreNodeItemType { - return { - nodeId: datasetNodeId, - name: t(DatasetSearchModule.name), - intro: t('app:dataset_search_tool_description'), - avatar: DatasetSearchModule.avatar, - flowNodeType: DatasetSearchModule.flowNodeType, - showStatus: true, - position: { - x: 918.5901682164496, - y: -227.11542247619582 - }, - version: DatasetSearchModule.version, - inputs: [ - { - key: NodeInputKeyEnum.datasetSelectList, - renderTypeList: [FlowNodeInputTypeEnum.selectDataset, FlowNodeInputTypeEnum.reference], - label: i18nT('common:core.module.input.label.Select dataset'), - value: selectedDatasets, - valueType: WorkflowIOValueTypeEnum.selectDataset, - list: [], - required: true - }, - { - key: NodeInputKeyEnum.datasetSimilarity, - renderTypeList: [FlowNodeInputTypeEnum.selectDatasetParamsModal], - label: '', - value: formData.dataset.similarity, - valueType: WorkflowIOValueTypeEnum.number - }, - { - key: NodeInputKeyEnum.datasetMaxTokens, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - value: formData.dataset.limit, - valueType: WorkflowIOValueTypeEnum.number - }, - { - key: NodeInputKeyEnum.datasetSearchMode, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.string, - value: formData.dataset.searchMode - }, - { - key: NodeInputKeyEnum.datasetSearchEmbeddingWeight, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.number, - value: formData.dataset.embeddingWeight - }, - { - key: NodeInputKeyEnum.datasetSearchUsingReRank, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.boolean, - value: formData.dataset.usingReRank - }, - { - key: NodeInputKeyEnum.datasetSearchRerankModel, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.string, - value: formData.dataset.rerankModel - }, - { - key: NodeInputKeyEnum.datasetSearchRerankWeight, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.number, - value: formData.dataset.rerankWeight - }, - { - key: NodeInputKeyEnum.datasetSearchUsingExtensionQuery, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.boolean, - value: formData.dataset.datasetSearchUsingExtensionQuery - }, - { - key: NodeInputKeyEnum.datasetSearchExtensionModel, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.string, - value: formData.dataset.datasetSearchExtensionModel - }, - { - key: NodeInputKeyEnum.datasetSearchExtensionBg, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.string, - value: formData.dataset.datasetSearchExtensionBg - }, - { - ...Input_Template_UserChatInput, - toolDescription: i18nT('workflow:content_to_search'), - value: question - } - ], - outputs: DatasetSearchModule.outputs - }; - } - - // Start, AiChat - function simpleChatTemplate(formData: AppSimpleEditFormType): WorkflowType { - return { - nodes: [aiChatTemplate(formData)], - edges: [ - { - source: workflowStartNodeId, - target: aiChatNodeId, - sourceHandle: `${workflowStartNodeId}-source-right`, - targetHandle: `${aiChatNodeId}-target-left` - } - ] - }; - } - // Start, Dataset search, AiChat - function datasetTemplate(formData: AppSimpleEditFormType): WorkflowType { - return { - nodes: [ - aiChatTemplate(formData), - datasetNodeTemplate(formData, [workflowStartNodeId, 'userChatInput']) - ], - edges: [ - { - source: workflowStartNodeId, - target: datasetNodeId, - sourceHandle: `${workflowStartNodeId}-source-right`, - targetHandle: `${datasetNodeId}-target-left` - }, - { - source: datasetNodeId, - target: aiChatNodeId, - sourceHandle: `${datasetNodeId}-source-right`, - targetHandle: `${aiChatNodeId}-target-left` - } - ] - }; - } - function toolTemplates(formData: AppSimpleEditFormType): WorkflowType { - const toolNodeId = getNanoid(6); - - // Dataset tool config - const datasetTool: WorkflowType | null = - selectedDatasets.length > 0 - ? { - nodes: [datasetNodeTemplate(formData, '')], - edges: [ - { - source: toolNodeId, - target: datasetNodeId, - sourceHandle: 'selectedTools', - targetHandle: 'selectedTools' - } - ] - } - : null; - - // Computed tools config - const pluginTool: WorkflowType[] = formData.selectedTools.map((tool, i) => { - const nodeId = getNanoid(6); - return { - nodes: [ - { - nodeId, - id: tool.id, - pluginId: tool.pluginId, - name: tool.name, - intro: tool.intro, - toolDescription: tool.toolDescription, - avatar: tool.avatar, - flowNodeType: tool.flowNodeType, - showStatus: tool.showStatus, - position: { - x: 500 + 500 * (i + 1), - y: 545 - }, - toolConfig: tool.toolConfig, - pluginData: tool.pluginData, - inputs: tool.inputs.map((input) => { - // Special key value - if (input.key === NodeInputKeyEnum.forbidStream) { - input.value = true; - } - // Special tool - if ( - tool.flowNodeType === FlowNodeTypeEnum.appModule && - input.key === NodeInputKeyEnum.history - ) { - return { - ...input, - value: formData.aiSettings.maxHistories - }; - } - return input; - }), - outputs: tool.outputs - } - ], - edges: [ - { - source: toolNodeId, - target: nodeId, - sourceHandle: 'selectedTools', - targetHandle: 'selectedTools' - } - ] - }; - }); - - const config: WorkflowType = { - nodes: [ - { - nodeId: toolNodeId, - name: ToolCallNode.name, - intro: ToolCallNode.intro, - avatar: ToolCallNode.avatar, - flowNodeType: ToolCallNode.flowNodeType, - showStatus: true, - position: { - x: 1062.1738942532802, - y: -223.65033022650476 - }, - version: ToolCallNode.version, - inputs: [ - { - key: NodeInputKeyEnum.aiModel, - renderTypeList: [ - FlowNodeInputTypeEnum.settingLLMModel, - FlowNodeInputTypeEnum.reference - ], - label: 'core.module.input.label.aiModel', - valueType: WorkflowIOValueTypeEnum.string, - llmModelType: 'all', - value: formData.aiSettings.model - }, - { - key: 'temperature', - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - value: formData.aiSettings.temperature, - valueType: WorkflowIOValueTypeEnum.number, - min: 0, - max: 10, - step: 1 - }, - { - key: 'maxToken', - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - value: formData.aiSettings.maxToken, - valueType: WorkflowIOValueTypeEnum.number, - min: 100, - max: 4000, - step: 50 - }, - { - key: 'systemPrompt', - renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference], - max: 3000, - valueType: WorkflowIOValueTypeEnum.string, - label: 'core.ai.Prompt', - description: 'core.app.tip.systemPromptTip', - placeholder: 'core.app.tip.chatNodeSystemPromptTip', - value: formData.aiSettings.systemPrompt - }, - { - key: 'history', - renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference], - valueType: WorkflowIOValueTypeEnum.chatHistory, - label: 'core.module.input.label.chat history', - required: true, - min: 0, - max: 30, - value: formData.aiSettings.maxHistories - }, - { - ...Input_Template_File_Link, - value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]] - }, - { - key: 'userChatInput', - renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea], - valueType: WorkflowIOValueTypeEnum.string, - label: i18nT('common:core.module.input.label.user question'), - required: true, - value: [workflowStartNodeId, 'userChatInput'] - }, - { - key: NodeInputKeyEnum.aiChatVision, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.boolean, - value: true - }, - { - key: NodeInputKeyEnum.aiChatReasoning, - renderTypeList: [FlowNodeInputTypeEnum.hidden], - label: '', - valueType: WorkflowIOValueTypeEnum.boolean, - value: formData.aiSettings.aiChatReasoning - } - ], - outputs: ToolCallNode.outputs - }, - // tool nodes - ...(datasetTool ? datasetTool.nodes : []), - ...pluginTool.map((tool) => tool.nodes).flat() - ], - edges: [ - { - source: workflowStartNodeId, - target: toolNodeId, - sourceHandle: `${workflowStartNodeId}-source-right`, - targetHandle: `${toolNodeId}-target-left` - }, - // tool edges - ...(datasetTool ? datasetTool.edges : []), - ...pluginTool.map((tool) => tool.edges).flat() - ] - }; - - // Add t - config.nodes.forEach((node) => { - node.name = t(node.name); - node.intro = t(node.intro); - - node.inputs.forEach((input) => { - input.label = t(input.label); - input.description = t(input.description); - input.toolDescription = t(input.toolDescription); - }); - }); - - return config; - } - - const workflow = (() => { - if (data.selectedTools.length > 0) return toolTemplates(data); - if (selectedDatasets.length > 0) return datasetTemplate(data); - return simpleChatTemplate(data); - })(); - - return { - nodes: [systemConfigTemplate(), workflowStartTemplate(), ...workflow.nodes], - edges: workflow.edges, - chatConfig: data.chatConfig - }; -} -export function filterSensitiveFormData(appForm: AppSimpleEditFormType) { +export function filterSensitiveFormData(appForm: AppFormEditFormType) { const defaultAppForm = getDefaultAppForm(); return { ...appForm,