mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-26 04:32:50 +00:00
perf: agent form editor
This commit is contained in:
parent
b0f9dd0f69
commit
3e0eea7cfb
|
|
@ -97,7 +97,7 @@ export type AppDatasetSearchParamsType = {
|
|||
datasetSearchExtensionBg?: string;
|
||||
};
|
||||
|
||||
export type AppSimpleEditFormType = {
|
||||
export type AppFormEditFormType = {
|
||||
// templateId: string;
|
||||
aiSettings: {
|
||||
[NodeInputKeyEnum.aiModel]: string;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<boolean>;
|
||||
appForm: AppSimpleEditFormType;
|
||||
setAppForm: (form: AppSimpleEditFormType) => void;
|
||||
past: SimpleAppSnapshotType[];
|
||||
setPast: (value: React.SetStateAction<SimpleAppSnapshotType[]>) => 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 (
|
||||
<Box h={14}>
|
||||
{!isPc && (
|
||||
<Flex justifyContent={'center'}>
|
||||
<RouteTab />
|
||||
</Flex>
|
||||
)}
|
||||
<Flex w={'full'} alignItems={'center'} position={'relative'} h={'full'}>
|
||||
<Box flex={'1'}>
|
||||
<FolderPath
|
||||
rootName={t('app:all_apps')}
|
||||
paths={paths}
|
||||
hoverStyle={{ color: 'primary.600' }}
|
||||
onClick={onClickRoute}
|
||||
fontSize={'14px'}
|
||||
/>
|
||||
</Box>
|
||||
{isPc && (
|
||||
<Box position={'absolute'} left={'50%'} transform={'translateX(-50%)'}>
|
||||
<RouteTab />
|
||||
</Box>
|
||||
)}
|
||||
{currentTab === TabEnum.appEdit && (
|
||||
<Flex alignItems={'center'}>
|
||||
{!isShowHistories && (
|
||||
<>
|
||||
{isPc && (
|
||||
<MyTag
|
||||
mr={3}
|
||||
type={'borderFill'}
|
||||
showDot
|
||||
colorSchema={
|
||||
isSaved
|
||||
? publishStatusStyle.published.colorSchema
|
||||
: publishStatusStyle.unPublish.colorSchema
|
||||
}
|
||||
>
|
||||
{t(
|
||||
isSaved
|
||||
? publishStatusStyle.published.text
|
||||
: publishStatusStyle.unPublish.text
|
||||
)}
|
||||
</MyTag>
|
||||
)}
|
||||
|
||||
<IconButton
|
||||
mr={[2, 4]}
|
||||
icon={<MyIcon name={'history'} w={'18px'} />}
|
||||
aria-label={''}
|
||||
size={'sm'}
|
||||
w={'30px'}
|
||||
variant={'whitePrimary'}
|
||||
onClick={setIsShowHistories}
|
||||
/>
|
||||
<SaveButton
|
||||
isLoading={loading}
|
||||
onClickSave={onClickSave}
|
||||
checkData={() => {
|
||||
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;
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{isShowHistories && currentTab === TabEnum.appEdit && (
|
||||
<PublishHistories<SimpleAppSnapshotType>
|
||||
onClose={closeHistories}
|
||||
past={past}
|
||||
onSwitchTmpVersion={onSwitchTmpVersion}
|
||||
onSwitchCloudVersion={onSwitchCloudVersion}
|
||||
positionStyles={{
|
||||
top: 14,
|
||||
bottom: 3
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
|
|
@ -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<string, any>
|
||||
)
|
||||
: {}
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
isCentered
|
||||
title={t('common:core.app.ToolCall.Parameter setting')}
|
||||
iconSrc="core/app/toolCall"
|
||||
overflow={'auto'}
|
||||
>
|
||||
<ModalBody>
|
||||
<HStack mb={4} spacing={1} fontSize={'sm'}>
|
||||
<MyIcon name={'common/info'} color={'primary.600'} w={'1.25rem'} />
|
||||
<Box flex={1}>{t('app:tool_input_param_tip')}</Box>
|
||||
{!!(configTool?.courseUrl || configTool?.userGuide) && (
|
||||
<UseGuideModal
|
||||
title={configTool?.name}
|
||||
iconSrc={configTool?.avatar}
|
||||
text={configTool?.userGuide}
|
||||
link={configTool?.courseUrl}
|
||||
>
|
||||
{({ onClick }) => (
|
||||
<Box cursor={'pointer'} color={'primary.500'} onClick={onClick}>
|
||||
{t('app:workflow.Input guide')}
|
||||
</Box>
|
||||
)}
|
||||
</UseGuideModal>
|
||||
)}
|
||||
</HStack>
|
||||
{configTool.inputs
|
||||
.filter(
|
||||
(input) =>
|
||||
!input.toolDescription &&
|
||||
!childAppSystemKey.includes(input.key) &&
|
||||
!input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) &&
|
||||
!input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
|
||||
)
|
||||
.map((input) => {
|
||||
return (
|
||||
<Box key={input.key} _notLast={{ mb: 4 }}>
|
||||
<Flex alignItems={'center'} mb={1}>
|
||||
{input.required && <Box color={'red.500'}>*</Box>}
|
||||
<FormLabel>{input.label}</FormLabel>
|
||||
{input.description && <QuestionTip ml={1} label={input.description} />}
|
||||
</Flex>
|
||||
|
||||
{input.key === NodeInputKeyEnum.systemInputConfig && input.inputList ? (
|
||||
<Controller
|
||||
control={control}
|
||||
name={input.key}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Box>
|
||||
<FormLabel mb={1}>{t('common:secret_key')}</FormLabel>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
border={'base'}
|
||||
borderRadius={'md'}
|
||||
leftIcon={
|
||||
<Box w={'6px'} h={'6px'} bg={'primary.600'} borderRadius={'md'} />
|
||||
}
|
||||
onClick={setTrueSecretModal}
|
||||
>
|
||||
{(() => {
|
||||
const val = value as ToolParamsFormType;
|
||||
if (!val) {
|
||||
return t('workflow:tool_active_config');
|
||||
}
|
||||
|
||||
return t('workflow:tool_active_config_type', {
|
||||
type: t(SystemToolInputTypeMap[val.type]?.text as any)
|
||||
});
|
||||
})()}
|
||||
</Button>
|
||||
|
||||
{isOpenSecretModal && (
|
||||
<SecretInputModal
|
||||
isFolder={configTool?.isFolder}
|
||||
inputConfig={{
|
||||
...input,
|
||||
value: value as ToolParamsFormType
|
||||
}}
|
||||
hasSystemSecret={configTool?.hasSystemSecret}
|
||||
secretCost={configTool?.systemKeyCost}
|
||||
courseUrl={configTool?.courseUrl}
|
||||
parentId={configTool?.pluginId}
|
||||
onClose={setFalseSecretModal}
|
||||
onSubmit={(data) => {
|
||||
onChange(data);
|
||||
setFalseSecretModal();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Controller
|
||||
control={control}
|
||||
name={input.key}
|
||||
rules={{
|
||||
validate: (value) => {
|
||||
if (input.valueType === WorkflowIOValueTypeEnum.boolean) {
|
||||
return value !== undefined;
|
||||
}
|
||||
if (input.required) {
|
||||
return !!value;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
||||
return (
|
||||
<InputRender
|
||||
{...input}
|
||||
isInvalid={!!error}
|
||||
inputType={nodeInputTypeToInputType(input.renderTypeList)}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</ModalBody>
|
||||
<ModalFooter gap={3}>
|
||||
<Button onClick={onCloseConfigTool} variant={'whiteBase'}>
|
||||
{t('common:Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={'primary'}
|
||||
onClick={handleSubmit((data) => {
|
||||
onAddTool({
|
||||
...configTool,
|
||||
inputs: configTool.inputs.map((input) => ({
|
||||
...input,
|
||||
value: data[input.key] ?? input.value
|
||||
}))
|
||||
});
|
||||
onCloseConfigTool();
|
||||
})}
|
||||
>
|
||||
{t('common:Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ConfigToolModal);
|
||||
|
|
@ -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<React.SetStateAction<AppSimpleEditFormType>>;
|
||||
}) => {
|
||||
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 (
|
||||
<>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<MyIcon name={'core/app/toolCall'} w={'20px'} />
|
||||
<FormLabel ml={2}>{t('common:core.app.Tool call')}</FormLabel>
|
||||
<QuestionTip ml={1} label={t('app:plugin_dispatch_tip')} />
|
||||
</Flex>
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
mr={'-5px'}
|
||||
size={'sm'}
|
||||
fontSize={'sm'}
|
||||
onClick={onOpenToolsSelect}
|
||||
>
|
||||
{t('common:Choose')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Grid
|
||||
mt={appForm.selectedTools.length > 0 ? 2 : 0}
|
||||
gridTemplateColumns={'repeat(2, minmax(0, 1fr))'}
|
||||
gridGap={[2, 4]}
|
||||
>
|
||||
{appForm.selectedTools.map((item) => {
|
||||
const toolError = formatToolError(item.pluginData?.error);
|
||||
|
||||
return (
|
||||
<MyTooltip key={item.id} label={item.intro}>
|
||||
<Flex
|
||||
overflow={'hidden'}
|
||||
alignItems={'center'}
|
||||
p={2.5}
|
||||
bg={'white'}
|
||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||
borderRadius={'md'}
|
||||
border={theme.borders.base}
|
||||
borderColor={toolError ? 'red.600' : ''}
|
||||
_hover={{
|
||||
...hoverDeleteStyles,
|
||||
borderColor: toolError ? 'red.600' : 'primary.300'
|
||||
}}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
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);
|
||||
}}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'1.5rem'} h={'1.5rem'} borderRadius={'sm'} />
|
||||
<Box
|
||||
flex={'1 0 0'}
|
||||
ml={2}
|
||||
gap={2}
|
||||
className={'textEllipsis'}
|
||||
fontSize={'sm'}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
{toolError && (
|
||||
<Flex
|
||||
bg={'red.50'}
|
||||
alignItems={'center'}
|
||||
h={6}
|
||||
px={2}
|
||||
rounded={'6px'}
|
||||
fontSize={'xs'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'}>{t(toolError as any)}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
<DeleteIcon
|
||||
ml={2}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setAppForm((state: AppSimpleEditFormType) => ({
|
||||
...state,
|
||||
selectedTools: state.selectedTools.filter((tool) => tool.id !== item.id)
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
|
||||
{isOpenToolsSelect && (
|
||||
<ToolSelectModal
|
||||
selectedTools={appForm.selectedTools}
|
||||
chatConfig={appForm.chatConfig}
|
||||
selectedModel={selectedModel}
|
||||
onAddTool={(e) => {
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
selectedTools: [...state.selectedTools, e]
|
||||
}));
|
||||
}}
|
||||
onRemoveTool={(e) => {
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
selectedTools: state.selectedTools.filter((item) => item.pluginId !== e.id)
|
||||
}));
|
||||
}}
|
||||
onClose={onCloseToolsSelect}
|
||||
/>
|
||||
)}
|
||||
{configTool && (
|
||||
<ConfigToolModal
|
||||
configTool={configTool}
|
||||
onCloseConfigTool={() => setConfigTool(null)}
|
||||
onAddTool={(e) => {
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
selectedTools: state.selectedTools.map((item) =>
|
||||
item.pluginId === configTool.pluginId ? e : item
|
||||
)
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ToolSelect);
|
||||
|
|
@ -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<string, any>;
|
||||
};
|
||||
export type onSaveSnapshotFnType = (props: {
|
||||
appForm: AppSimpleEditFormType; // Current edited app form data
|
||||
title?: string;
|
||||
isSaved?: boolean;
|
||||
}) => Promise<boolean>;
|
||||
|
||||
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<SimpleAppSnapshotType[]>([]);
|
||||
|
||||
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 <></>;
|
||||
}
|
||||
|
|
@ -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<React.SetStateAction<AppSimpleEditFormType>>;
|
||||
appForm: AppFormEditFormType;
|
||||
setAppForm: React.Dispatch<React.SetStateAction<AppFormEditFormType>>;
|
||||
setPast: (value: React.SetStateAction<SimpleAppSnapshotType[]>) => void;
|
||||
}) => {
|
||||
const { isPc } = useSystem();
|
||||
|
|
@ -42,7 +43,7 @@ const Edit = ({
|
|||
flex={'1'}
|
||||
>
|
||||
<Box {...cardStyles} boxShadow={'2'}>
|
||||
<AppCard appForm={appForm} setPast={setPast} />
|
||||
<AppCard appForm={appForm} setPast={setPast} form2WorkflowFn={agentForm2AppWorkflow} />
|
||||
</Box>
|
||||
|
||||
<Box mt={4} {...cardStyles} boxShadow={'3.5'}>
|
||||
|
|
@ -52,7 +53,11 @@ const Edit = ({
|
|||
)}
|
||||
{isPc && (
|
||||
<Box flex={'2 0 0'} w={0} mb={3}>
|
||||
<ChatTest appForm={appForm} setRenderEdit={setRenderEdit} />
|
||||
<ChatTest
|
||||
appForm={appForm}
|
||||
setRenderEdit={setRenderEdit}
|
||||
form2WorkflowFn={agentForm2AppWorkflow}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
|
@ -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<React.SetStateAction<AppSimpleEditFormType>>;
|
||||
appForm: AppFormEditFormType;
|
||||
setAppForm: React.Dispatch<React.SetStateAction<AppFormEditFormType>>;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
|
|
@ -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 ? (
|
||||
<Edit appForm={appForm} setAppForm={setAppForm} setPast={setPast} />
|
||||
|
|
@ -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;
|
||||
|
|
@ -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<SimpleAppSnapshotType[]>) => void;
|
||||
form2WorkflowFn: Form2WorkflowFnType;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -49,7 +51,7 @@ const AppCard = ({
|
|||
const [transitionCreateNew, setTransitionCreateNew] = useState<boolean>();
|
||||
const { runAsync: onTransition, loading: transiting } = useRequest2(
|
||||
async () => {
|
||||
const { nodes, edges } = form2AppWorkflow(appForm, t);
|
||||
const { nodes, edges } = form2WorkflowFn(appForm, t);
|
||||
await onSaveApp({
|
||||
nodes,
|
||||
edges,
|
||||
|
|
@ -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<React.SetStateAction<boolean>>;
|
||||
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
|
||||
>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<ChatTest appForm={appForm} setRenderEdit={setRenderEdit} />
|
||||
<ChatTest
|
||||
appForm={appForm}
|
||||
setRenderEdit={setRenderEdit}
|
||||
form2WorkflowFn={form2WorkflowFn}
|
||||
/>
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
);
|
||||
|
|
@ -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<boolean>;
|
||||
appForm: AppSimpleEditFormType;
|
||||
setAppForm: (form: AppSimpleEditFormType) => void;
|
||||
appForm: AppFormEditFormType;
|
||||
setAppForm: (form: AppFormEditFormType) => void;
|
||||
past: SimpleAppSnapshotType[];
|
||||
setPast: (value: React.SetStateAction<SimpleAppSnapshotType[]>) => 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 }));
|
||||
|
|
@ -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<React.SetStateAction<AppSimpleEditFormType>>;
|
||||
appForm: AppFormEditFormType;
|
||||
setAppForm: React.Dispatch<React.SetStateAction<AppFormEditFormType>>;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [configTool, setConfigTool] = useState<
|
||||
AppSimpleEditFormType['selectedTools'][number] | null
|
||||
>(null);
|
||||
const [configTool, setConfigTool] = useState<AppFormEditFormType['selectedTools'][number] | null>(
|
||||
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)
|
||||
}));
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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<string, any>;
|
||||
};
|
||||
export type onSaveSnapshotFnType = (props: {
|
||||
appForm: AppSimpleEditFormType; // Current edited app form data
|
||||
appForm: AppFormEditFormType; // Current edited app form data
|
||||
title?: string;
|
||||
isSaved?: boolean;
|
||||
}) => Promise<boolean>;
|
||||
|
||||
export const compareSimpleAppSnapshot = (
|
||||
appForm1?: AppSimpleEditFormType,
|
||||
appForm2?: AppSimpleEditFormType
|
||||
appForm1?: AppFormEditFormType,
|
||||
appForm2?: AppFormEditFormType
|
||||
) => {
|
||||
if (
|
||||
appForm1?.chatConfig &&
|
||||
|
|
@ -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();
|
||||
|
|
@ -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 = ({
|
||||
|
|
@ -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';
|
||||
|
|
@ -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';
|
||||
|
|
@ -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';
|
||||
|
|
@ -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';
|
||||
|
|
@ -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<React.SetStateAction<AppSimpleEditFormType>>;
|
||||
appForm: AppFormEditFormType;
|
||||
setAppForm: React.Dispatch<React.SetStateAction<AppFormEditFormType>>;
|
||||
setPast: (value: React.SetStateAction<SimpleAppSnapshotType[]>) => void;
|
||||
}) => {
|
||||
const { isPc } = useSystem();
|
||||
|
|
@ -42,7 +43,7 @@ const Edit = ({
|
|||
flex={'1'}
|
||||
>
|
||||
<Box {...cardStyles} boxShadow={'2'}>
|
||||
<AppCard appForm={appForm} setPast={setPast} />
|
||||
<AppCard appForm={appForm} setPast={setPast} form2WorkflowFn={form2AppWorkflow} />
|
||||
</Box>
|
||||
|
||||
<Box mt={4} {...cardStyles} boxShadow={'3.5'}>
|
||||
|
|
@ -52,7 +53,11 @@ const Edit = ({
|
|||
)}
|
||||
{isPc && (
|
||||
<Box flex={'2 0 0'} w={0} mb={3}>
|
||||
<ChatTest appForm={appForm} setRenderEdit={setRenderEdit} />
|
||||
<ChatTest
|
||||
appForm={appForm}
|
||||
setRenderEdit={setRenderEdit}
|
||||
form2WorkflowFn={form2AppWorkflow}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
|
@ -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<React.SetStateAction<AppSimpleEditFormType>>;
|
||||
appForm: AppFormEditFormType;
|
||||
setAppForm: React.Dispatch<React.SetStateAction<AppFormEditFormType>>;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 ? (
|
||||
<Edit appForm={appForm} setAppForm={setAppForm} setPast={setPast} />
|
||||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
@ -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 }] =
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<SimpleAppSnapshotType[]>) => 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<AppSchema>();
|
||||
|
||||
// transition to workflow
|
||||
const [transitionCreateNew, setTransitionCreateNew] = useState<boolean>();
|
||||
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 */}
|
||||
<Box px={[4, 6]} py={4} position={'relative'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} />
|
||||
<Box ml={3} fontWeight={'bold'} fontSize={'md'} flex={'1 0 0'} color={'myGray.900'}>
|
||||
{appDetail.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box
|
||||
flex={1}
|
||||
mt={3}
|
||||
mb={4}
|
||||
className={'textEllipsis3'}
|
||||
wordBreak={'break-all'}
|
||||
color={'myGray.600'}
|
||||
fontSize={'xs'}
|
||||
minH={'46px'}
|
||||
>
|
||||
{appDetail.intro || t('common:core.app.tip.Add a intro to app')}
|
||||
</Box>
|
||||
<HStack alignItems={'center'}>
|
||||
<Button
|
||||
size={['sm', 'md']}
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<MyIcon name={'core/chat/chatLight'} w={'16px'} />}
|
||||
onClick={() =>
|
||||
router.push(`/chat?appId=${appId}&pane=${ChatSidebarPaneEnum.RECENTLY_USED_APPS}`)
|
||||
}
|
||||
>
|
||||
{t('common:core.Chat')}
|
||||
</Button>
|
||||
{appDetail.permission.hasManagePer && (
|
||||
<Button
|
||||
size={['sm', 'md']}
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<MyIcon name={'common/settingLight'} w={'16px'} />}
|
||||
onClick={onOpenInfoEdit}
|
||||
>
|
||||
{t('common:Setting')}
|
||||
</Button>
|
||||
)}
|
||||
{appDetail.permission.isOwner && (
|
||||
<MyMenu
|
||||
size={'xs'}
|
||||
Button={
|
||||
<IconButton
|
||||
variant={'whitePrimary'}
|
||||
size={['smSquare', 'mdSquare']}
|
||||
icon={<MyIcon name={'more'} w={'1rem'} />}
|
||||
aria-label={''}
|
||||
/>
|
||||
}
|
||||
menuList={[
|
||||
{
|
||||
children: [
|
||||
{
|
||||
label: (
|
||||
<Flex>
|
||||
<ExportConfigPopover
|
||||
appName={appDetail.name}
|
||||
appForm={appForm}
|
||||
chatConfig={appDetail.chatConfig}
|
||||
/>
|
||||
</Flex>
|
||||
)
|
||||
},
|
||||
{
|
||||
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
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<Box flex={1} />
|
||||
{/* {isPc && ( */}
|
||||
{/* <MyTag */}
|
||||
{/* type="borderFill" */}
|
||||
{/* colorSchema="gray" */}
|
||||
{/* onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)} */}
|
||||
{/* > */}
|
||||
{/* <PermissionIconText defaultPermission={appDetail.defaultPermission} /> */}
|
||||
{/* </MyTag> */}
|
||||
{/* )} */}
|
||||
</HStack>
|
||||
</Box>
|
||||
{TeamTagsSet && <TagsEditModal onClose={() => setTeamTagsSet(undefined)} />}
|
||||
{transitionCreateNew !== undefined && (
|
||||
<MyModal isOpen title={t('app:transition_to_workflow')} iconSrc="core/app/type/workflow">
|
||||
<ModalBody>
|
||||
<Box mb={3}>{t('app:transition_to_workflow_create_new_tip')}</Box>
|
||||
<HStack cursor={'pointer'} onClick={() => setTransitionCreateNew((state) => !state)}>
|
||||
<Checkbox
|
||||
isChecked={transitionCreateNew}
|
||||
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
||||
/>
|
||||
<Box>{t('app:transition_to_workflow_create_new_placeholder')}</Box>
|
||||
</HStack>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} onClick={() => setTransitionCreateNew(undefined)} mr={3}>
|
||||
{t('common:Close')}
|
||||
</Button>
|
||||
<Button variant={'dangerFill'} isLoading={transiting} onClick={() => onTransition()}>
|
||||
{t('common:Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(AppCard);
|
||||
|
|
@ -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<React.SetStateAction<boolean>>;
|
||||
};
|
||||
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 (
|
||||
<Flex h={'full'} gap={2}>
|
||||
<MyBox
|
||||
flex={'1 0 0'}
|
||||
w={0}
|
||||
display={'flex'}
|
||||
position={'relative'}
|
||||
flexDirection={'column'}
|
||||
h={'full'}
|
||||
py={4}
|
||||
{...cardStyles}
|
||||
boxShadow={'3'}
|
||||
>
|
||||
<Flex px={[2, 5]} pb={2}>
|
||||
<Box fontSize={['md', 'lg']} fontWeight={'bold'} color={'myGray.900'} mr={3}>
|
||||
{t('app:chat_debug')}
|
||||
</Box>
|
||||
{!isVariableVisible && <VariablePopover chatType={ChatTypeEnum.test} />}
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={t('common:core.chat.Restart')}>
|
||||
<IconButton
|
||||
className="chat"
|
||||
size={'smSquare'}
|
||||
icon={<MyIcon name={'common/clearLight'} w={'14px'} />}
|
||||
variant={'whiteDanger'}
|
||||
borderRadius={'md'}
|
||||
aria-label={'delete'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
restartChat();
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<Box flex={1}>
|
||||
<ChatContainer />
|
||||
</Box>
|
||||
</MyBox>
|
||||
{datasetCiteData && (
|
||||
<Box flex={'1 0 0'} w={0} maxW={'560px'} {...cardStyles} boxShadow={'3'}>
|
||||
<ChatQuoteList
|
||||
rawSearch={datasetCiteData.rawSearch}
|
||||
metadata={datasetCiteData.metadata}
|
||||
onClose={() => setCiteModalData(undefined)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<ChatItemContextProvider
|
||||
showRouteToDatasetDetail={true}
|
||||
isShowReadRawSource={true}
|
||||
isResponseDetail={true}
|
||||
// isShowFullText={true}
|
||||
showNodeStatus
|
||||
>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<ChatTest appForm={appForm} setRenderEdit={setRenderEdit} />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Render);
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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: () => <Loading fixed={false} />
|
||||
});
|
||||
const AgentEdit = dynamic(() => import('@/pageComponents/app/detail/Agent'), {
|
||||
const AgentEdit = dynamic(() => import('@/pageComponents/app/detail/Edit/Agent'), {
|
||||
ssr: false,
|
||||
loading: () => <Loading fixed={false} />
|
||||
});
|
||||
|
|
@ -27,7 +27,7 @@ const Plugin = dynamic(() => import('@/pageComponents/app/detail/Plugin'), {
|
|||
ssr: false,
|
||||
loading: () => <Loading fixed={false} />
|
||||
});
|
||||
const MCPTools = dynamic(() => import('@/pageComponents/app/detail/MCPTools'), {
|
||||
const MCPTools = dynamic(() => import('@/pageComponents/app/detail/Edit/MCPTools'), {
|
||||
ssr: false,
|
||||
loading: () => <Loading fixed={false} />
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue