perf:change plugin version update position (#1493)

* perf:change plugin version update position

* use nodeversion
This commit is contained in:
heheer
2024-05-15 17:06:49 +08:00
committed by GitHub
parent 8386f707cd
commit d5073f98ab
10 changed files with 98 additions and 66 deletions

View File

@@ -23,6 +23,7 @@ export type PluginItemSchema = {
customHeaders?: string; customHeaders?: string;
}; };
version?: 'v1' | 'v2'; version?: 'v1' | 'v2';
nodeVersion?: string;
}; };
/* plugin template */ /* plugin template */
@@ -32,6 +33,7 @@ export type PluginTemplateType = PluginRuntimeType & {
source: `${PluginSourceEnum}`; source: `${PluginSourceEnum}`;
templateType: FlowNodeTemplateType['templateType']; templateType: FlowNodeTemplateType['templateType'];
intro: string; intro: string;
nodeVersion: string;
}; };
export type PluginRuntimeType = { export type PluginRuntimeType = {

View File

@@ -64,6 +64,7 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
// action // action
forbidDelete?: boolean; // forbid delete forbidDelete?: boolean; // forbid delete
unique?: boolean; unique?: boolean;
nodeVersion?: string;
}; };
export type FlowNodeItemType = FlowNodeTemplateType & { export type FlowNodeItemType = FlowNodeTemplateType & {
nodeId: string; nodeId: string;

View File

@@ -52,7 +52,8 @@ const getPluginTemplateById = async (id: string): Promise<PluginTemplateType> =>
nodes: item.modules, nodes: item.modules,
edges: item.edges, edges: item.edges,
templateType: FlowNodeTemplateTypeEnum.personalPlugin, templateType: FlowNodeTemplateTypeEnum.personalPlugin,
isTool: true isTool: true,
nodeVersion: item?.nodeVersion || ''
}; };
} }
return Promise.reject('plugin not found'); return Promise.reject('plugin not found');
@@ -72,6 +73,7 @@ export async function getPluginPreviewNode({ id }: { id: string }): Promise<Flow
intro: plugin.intro, intro: plugin.intro,
showStatus: plugin.showStatus, showStatus: plugin.showStatus,
isTool: plugin.isTool, isTool: plugin.isTool,
nodeVersion: plugin.nodeVersion,
version: '481', version: '481',
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),

View File

@@ -64,6 +64,10 @@ const PluginSchema = new Schema({
version: { version: {
type: String, type: String,
enum: ['v1', 'v2'] enum: ['v1', 'v2']
},
nodeVersion: {
type: String,
default: ''
} }
}); });

View File

@@ -62,7 +62,7 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
return JSON.stringify(value); return JSON.stringify(value);
} }
if (type === 'number') return Number(value); if (type === 'number') return Number(value);
if (type === 'boolean') return Boolean(value); if (type === 'boolean') return value === 'true' ? true : false;
try { try {
if (type === WorkflowIOValueTypeEnum.datasetQuote && !Array.isArray(value)) { if (type === WorkflowIOValueTypeEnum.datasetQuote && !Array.isArray(value)) {
return JSON.parse(value); return JSON.parse(value);

View File

@@ -39,6 +39,7 @@
}, },
"module": { "module": {
"Combine Modules": "Combine Modules", "Combine Modules": "Combine Modules",
"Confirm Sync": "Using the latest template will overwrite the existing one and may result in the loss of some previous configuration information. Please confirm.",
"Custom Title Tip": "This title will be displayed during the conversation", "Custom Title Tip": "This title will be displayed during the conversation",
"My Modules": "My Modules", "My Modules": "My Modules",
"No Modules": "No modules yet~", "No Modules": "No modules yet~",

View File

@@ -38,6 +38,7 @@
}, },
"module": { "module": {
"Combine Modules": "组合模块", "Combine Modules": "组合模块",
"Confirm Sync": "将会使用最新模板进行覆盖,可能会丢失一些旧的配置信息,请确认",
"Custom Title Tip": "该标题名字会展示在对话过程中", "Custom Title Tip": "该标题名字会展示在对话过程中",
"My Modules": "", "My Modules": "",
"No Modules": "还没有模块~", "No Modules": "还没有模块~",

View File

@@ -1,13 +1,15 @@
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Box, Button, Card, Flex } from '@chakra-ui/react'; import { Box, Button, Card, Flex } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/index.d'; import type {
FlowNodeItemType,
FlowNodeTemplateType
} from '@fastgpt/global/core/workflow/type/index.d';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useEditTitle } from '@/web/common/hooks/useEditTitle'; import { useEditTitle } from '@/web/common/hooks/useEditTitle';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants'; import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import { ToolTargetHandle } from './Handle/ToolHandle'; import { ToolTargetHandle } from './Handle/ToolHandle';
@@ -17,7 +19,6 @@ import { useDebug } from '../../hooks/useDebug';
import { ResponseBox } from '@/components/ChatBox/WholeResponseModal'; import { ResponseBox } from '@/components/ChatBox/WholeResponseModal';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { getPreviewPluginModule } from '@/web/core/plugin/api'; import { getPreviewPluginModule } from '@/web/core/plugin/api';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { storeNode2FlowNode, updateFlowNodeVersion } from '@/web/core/workflow/utils'; import { storeNode2FlowNode, updateFlowNodeVersion } from '@/web/core/workflow/utils';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
@@ -26,6 +27,8 @@ import { useI18n } from '@/web/context/I18n';
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants'; import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
import { QuestionOutlineIcon } from '@chakra-ui/icons'; import { QuestionOutlineIcon } from '@chakra-ui/icons';
import MyTooltip from '@/components/MyTooltip'; import MyTooltip from '@/components/MyTooltip';
import { isEqual } from 'lodash';
import { useSystemStore } from '@/web/common/system/useSystemStore';
type Props = FlowNodeItemType & { type Props = FlowNodeItemType & {
children?: React.ReactNode | React.ReactNode[] | string; children?: React.ReactNode | React.ReactNode[] | string;
@@ -69,6 +72,9 @@ const NodeCard = (props: Props) => {
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode); const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
const [hasNewVersion, setHasNewVersion] = useState(false);
const { setLoading } = useSystemStore();
// custom title edit // custom title edit
const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({ const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({
title: t('common.Custom Title'), title: t('common.Custom Title'),
@@ -81,13 +87,50 @@ const NodeCard = (props: Props) => {
); );
const node = nodeList.find((node) => node.nodeId === nodeId); const node = nodeList.find((node) => node.nodeId === nodeId);
const { openConfirm: onOpenConfirmSync, ConfirmModal: ConfirmSyncModal } = useConfirm({
content: appT('module.Confirm Sync')
});
useEffect(() => {
const fetchPluginModule = async () => {
if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) {
if (!node?.pluginId) return;
const template = await getPreviewPluginModule(node.pluginId);
setHasNewVersion(!!template.nodeVersion && node.nodeVersion !== template.nodeVersion);
} else {
const template = moduleTemplatesFlat.find(
(item) => item.flowNodeType === node?.flowNodeType
);
setHasNewVersion(node?.version !== template?.version);
}
};
fetchPluginModule();
}, [node]);
const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType); const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType);
const hasNewVersion = useMemo(() => {
return ( const onClickSyncVersion = useCallback(async () => {
template?.flowNodeType !== FlowNodeTypeEnum.pluginModule && try {
node?.version !== template?.version setLoading(true);
); if (!node || !template) return;
}, [node?.version, template?.flowNodeType, template?.version]); if (node?.flowNodeType === 'pluginModule') {
if (!node.pluginId) return;
onResetNode({
id: nodeId,
node: await getPreviewPluginModule(node.pluginId)
});
} else {
onResetNode({
id: nodeId,
node: updateFlowNodeVersion(node, template)
});
}
} catch (error) {
console.error('Error fetching plugin module:', error);
}
setLoading(false);
}, [node, nodeId, onResetNode, setLoading, template]);
/* Node header */ /* Node header */
const Header = useMemo(() => { const Header = useMemo(() => {
@@ -149,13 +192,7 @@ const NodeCard = (props: Props) => {
fontWeight={'medium'} fontWeight={'medium'}
cursor={'pointer'} cursor={'pointer'}
_hover={{ bg: 'yellow.100' }} _hover={{ bg: 'yellow.100' }}
onClick={() => { onClick={onOpenConfirmSync(onClickSyncVersion)}
if (!node || !template) return;
onResetNode({
id: nodeId,
node: updateFlowNodeVersion(node, template)
});
}}
> >
<Box>{appT('app.modules.has new version')}</Box> <Box>{appT('app.modules.has new version')}</Box>
<QuestionOutlineIcon ml={1} /> <QuestionOutlineIcon ml={1} />
@@ -171,6 +208,7 @@ const NodeCard = (props: Props) => {
/> />
<NodeIntro nodeId={nodeId} intro={intro} /> <NodeIntro nodeId={nodeId} intro={intro} />
</Box> </Box>
<ConfirmSyncModal />
</Box> </Box>
); );
}, [ }, [
@@ -181,16 +219,16 @@ const NodeCard = (props: Props) => {
name, name,
menuForbid, menuForbid,
hasNewVersion, hasNewVersion,
appT,
onOpenConfirmSync,
onClickSyncVersion,
pluginId, pluginId,
flowNodeType, flowNodeType,
intro, intro,
ConfirmSyncModal,
onOpenCustomTitleModal, onOpenCustomTitleModal,
onChangeNode, onChangeNode,
toast, toast
appT,
node,
template,
onResetNode
]); ]);
return ( return (
@@ -249,21 +287,14 @@ const MenuRender = React.memo(function MenuRender({
menuForbid?: Props['menuForbid']; menuForbid?: Props['menuForbid'];
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { toast } = useToast();
const { setLoading } = useSystemStore();
const { openDebugNode, DebugInputModal } = useDebug(); const { openDebugNode, DebugInputModal } = useDebug();
const { openConfirm: onOpenConfirmSync, ConfirmModal: ConfirmSyncModal } = useConfirm({
content: t('module.Confirm Sync Plugin')
});
const { openConfirm: onOpenConfirmDeleteNode, ConfirmModal: ConfirmDeleteModal } = useConfirm({ const { openConfirm: onOpenConfirmDeleteNode, ConfirmModal: ConfirmDeleteModal } = useConfirm({
content: t('core.module.Confirm Delete Node'), content: t('core.module.Confirm Delete Node'),
type: 'delete' type: 'delete'
}); });
const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes); const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes);
const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
const setEdges = useContextSelector(WorkflowContext, (v) => v.setEdges); const setEdges = useContextSelector(WorkflowContext, (v) => v.setEdges);
const onCopyNode = useCallback( const onCopyNode = useCallback(
@@ -310,22 +341,6 @@ const MenuRender = React.memo(function MenuRender({
}, },
[setEdges, setNodes] [setEdges, setNodes]
); );
const onClickSyncVersion = useCallback(async () => {
if (!pluginId) return;
try {
setLoading(true);
onResetNode({
id: nodeId,
node: await getPreviewPluginModule(pluginId)
});
} catch (e) {
return toast({
status: 'error',
title: getErrText(e, t('plugin.Get Plugin Module Detail Failed'))
});
}
setLoading(false);
}, [nodeId, onResetNode, pluginId, setLoading, t, toast]);
const Render = useMemo(() => { const Render = useMemo(() => {
const menuList = [ const menuList = [
@@ -349,17 +364,6 @@ const MenuRender = React.memo(function MenuRender({
onClick: () => onCopyNode(nodeId) onClick: () => onCopyNode(nodeId)
} }
]), ]),
...(flowNodeType === FlowNodeTypeEnum.pluginModule
? [
{
icon: 'common/refreshLight',
label: t('plugin.Synchronous version'),
variant: 'whiteBase',
onClick: onOpenConfirmSync(onClickSyncVersion)
}
]
: []),
...(menuForbid?.delete ...(menuForbid?.delete
? [] ? []
: [ : [
@@ -401,7 +405,6 @@ const MenuRender = React.memo(function MenuRender({
</Box> </Box>
))} ))}
</Box> </Box>
<ConfirmSyncModal />
<ConfirmDeleteModal /> <ConfirmDeleteModal />
<DebugInputModal /> <DebugInputModal />
</> </>
@@ -411,11 +414,7 @@ const MenuRender = React.memo(function MenuRender({
menuForbid?.copy, menuForbid?.copy,
menuForbid?.delete, menuForbid?.delete,
t, t,
flowNodeType,
onOpenConfirmSync,
onClickSyncVersion,
onOpenConfirmDeleteNode, onOpenConfirmDeleteNode,
ConfirmSyncModal,
ConfirmDeleteModal, ConfirmDeleteModal,
DebugInputModal, DebugInputModal,
openDebugNode, openDebugNode,

View File

@@ -22,7 +22,8 @@ export const flowNode2StoreNodes = ({
version: item.data.version, version: item.data.version,
inputs: item.data.inputs, inputs: item.data.inputs,
outputs: item.data.outputs, outputs: item.data.outputs,
pluginId: item.data.pluginId pluginId: item.data.pluginId,
nodeVersion: item.data.nodeVersion
})); }));
// get all handle // get all handle

View File

@@ -7,6 +7,8 @@ import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { ClientSession } from '@fastgpt/service/common/mongo'; import { ClientSession } from '@fastgpt/service/common/mongo';
import { httpApiSchema2Plugins } from '@fastgpt/global/core/plugin/httpPlugin/utils'; import { httpApiSchema2Plugins } from '@fastgpt/global/core/plugin/httpPlugin/utils';
import { isEqual } from 'lodash';
import { nanoid } from 'nanoid';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
@@ -22,7 +24,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
per: 'owner' per: 'owner'
}); });
const updateData = { const originPlugin = await MongoPlugin.findById(id);
let updateData = {
name: props.name, name: props.name,
intro: props.intro, intro: props.intro,
avatar: props.avatar, avatar: props.avatar,
@@ -32,9 +36,26 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
modules: modules modules: modules
}), }),
...(edges?.length && { edges }), ...(edges?.length && { edges }),
metadata: props.metadata metadata: props.metadata,
nodeVersion: originPlugin?.nodeVersion
}; };
const isNodeVersionEqual =
isEqual(
originPlugin?.modules.map((module) => {
return { ...module, position: undefined };
}),
updateData.modules?.map((module) => {
return { ...module, position: undefined };
})
) && isEqual(originPlugin?.edges, updateData.edges);
if (!isNodeVersionEqual) {
updateData = {
...updateData,
nodeVersion: nanoid(6)
};
}
if (props.metadata?.apiSchemaStr) { if (props.metadata?.apiSchemaStr) {
await mongoSessionRun(async (session) => { await mongoSessionRun(async (session) => {
// update children // update children