* fix: full text search match query

* perf: mongo schema import, Avoid duplicate import

* feat: mongo log store

* doc

* fix: sandbox outputs

* perf: desc color

* fix: node init

* perf code

* perf: chat header
This commit is contained in:
Archer
2024-07-10 15:52:39 +08:00
committed by GitHub
parent f548e24e7d
commit e2ae571d15
21 changed files with 184 additions and 137 deletions

View File

@@ -13,9 +13,9 @@ weight: 818
### 2. 修改镜像 ### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.6-alpha - fastgpt 镜像 tag 修改成 v4.8.6-alpha2
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha - fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha2
- 商业版镜像 tag 修改成 v4.8.6-alpha - 商业版镜像 tag 修改成 v4.8.6-alpha2
### 3. 执行初始化 ### 3. 执行初始化
@@ -39,6 +39,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv486' \
4. 新增 - 移动文本加工和自定义反馈到基础节点中 4. 新增 - 移动文本加工和自定义反馈到基础节点中
5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力 5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力
6. 优化 - 知识库导入接口,返回值对齐 6. 优化 - 知识库导入接口,返回值对齐
7. 修复 - 工作流中团队插件加载异常 7. 优化 - Mongo model 重复加载
8. 修复 - 知识库集合目录导航失效 8. 修复 - 工作流中团队插件加载异常
9. 修复 - 通过 API 调用 chat 接口,传递 System 异常 9. 修复 - 知识库集合目录导航失效
10. 修复 - 通过 API 调用 chat 接口,传递 System 异常

View File

@@ -8,5 +8,10 @@ export const Output_Template_AddOutput: FlowNodeOutputItemType = {
key: NodeOutputKeyEnum.addOutputParam, key: NodeOutputKeyEnum.addOutputParam,
type: FlowNodeOutputTypeEnum.dynamic, type: FlowNodeOutputTypeEnum.dynamic,
valueType: WorkflowIOValueTypeEnum.dynamic, valueType: WorkflowIOValueTypeEnum.dynamic,
label: '' label: '',
customFieldConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: false
}
}; };

View File

@@ -82,12 +82,7 @@ export const HttpNode468: FlowNodeTemplateType = {
], ],
outputs: [ outputs: [
{ {
...Output_Template_AddOutput, ...Output_Template_AddOutput
customFieldConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
}
}, },
{ {
id: NodeOutputKeyEnum.error, id: NodeOutputKeyEnum.error,

View File

@@ -36,6 +36,32 @@ export const CodeNode: FlowNodeTemplateType = {
showDefaultValue: true showDefaultValue: true
} }
}, },
{
renderTypeList: [FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: 'data1',
label: 'data1',
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
},
required: true
},
{
renderTypeList: [FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: 'data2',
label: 'data2',
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
},
required: true
},
{ {
key: NodeInputKeyEnum.codeType, key: NodeInputKeyEnum.codeType,
renderTypeList: [FlowNodeInputTypeEnum.hidden], renderTypeList: [FlowNodeInputTypeEnum.hidden],
@@ -52,7 +78,7 @@ export const CodeNode: FlowNodeTemplateType = {
outputs: [ outputs: [
{ {
...Output_Template_AddOutput, ...Output_Template_AddOutput,
description: '将代码中 return 的对象作为输出,传递给后续的节点' description: '将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key'
}, },
{ {
id: NodeOutputKeyEnum.rawResponse, id: NodeOutputKeyEnum.rawResponse,

View File

@@ -1,6 +1,9 @@
import { addLog } from '../../common/system/log'; import { addLog } from '../../common/system/log';
import mongoose, { Model } from 'mongoose'; import mongoose, { Model } from 'mongoose';
export default mongoose;
export * from 'mongoose';
export const connectionMongo = (() => { export const connectionMongo = (() => {
if (!global.mongodb) { if (!global.mongodb) {
global.mongodb = mongoose; global.mongodb = mongoose;
@@ -9,9 +12,6 @@ export const connectionMongo = (() => {
return global.mongodb; return global.mongodb;
})(); })();
export default mongoose;
export * from 'mongoose';
const addCommonMiddleware = (schema: mongoose.Schema) => { const addCommonMiddleware = (schema: mongoose.Schema) => {
const operations = [ const operations = [
/^find/, /^find/,

View File

@@ -1,8 +1,8 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import chalk from 'chalk'; import chalk from 'chalk';
import { LogLevelEnum } from './log/constant'; import { LogLevelEnum } from './log/constant';
// import { MongoLog } from './log/schema'; import { connectionMongo } from '../mongo/index';
import connectionMongo from '../mongo/index'; import { getMongoLog } from './log/schema';
const logMap = { const logMap = {
[LogLevelEnum.debug]: { [LogLevelEnum.debug]: {
@@ -52,14 +52,14 @@ export const addLog = {
level === LogLevelEnum.error && console.error(obj); level === LogLevelEnum.error && console.error(obj);
// store // store
// if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) { if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
// // store log // store log
// MongoLog.create({ getMongoLog().create({
// text: msg, text: msg,
// level, level,
// metadata: obj metadata: obj
// }); });
// } }
}, },
debug(msg: string, obj?: Record<string, any>) { debug(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.debug, msg, obj); this.log(LogLevelEnum.debug, msg, obj);

View File

@@ -4,24 +4,26 @@ import { LogLevelEnum } from './constant';
export const LogCollectionName = 'system_logs'; export const LogCollectionName = 'system_logs';
const SystemLogSchema = new Schema({ export const getMongoLog = () => {
text: { const SystemLogSchema = new Schema({
type: String, text: {
required: true type: String,
}, required: true
level: { },
type: String, level: {
required: true, type: String,
enum: Object.values(LogLevelEnum) required: true,
}, enum: Object.values(LogLevelEnum)
time: { },
type: Date, time: {
default: () => new Date() type: Date,
}, default: () => new Date()
metadata: Object },
}); metadata: Object
});
SystemLogSchema.index({ time: 1 }, { expires: '15d' }); SystemLogSchema.index({ time: 1 }, { expires: '15d' });
SystemLogSchema.index({ level: 1 }); SystemLogSchema.index({ level: 1 });
export const MongoLog = getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema); return getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema);
};

View File

@@ -57,7 +57,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.", "Confirm Sync": "The template will be updated to the latest template configuration. Fields that do not exist in the template will be deleted (including all custom fields). You are advised to make a copy of the node and then update the original node version.",
"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 plugins yet~", "No Modules": "No plugins yet~",

View File

@@ -1068,7 +1068,7 @@
"Debug": "Debug", "Debug": "Debug",
"Debug Node": "Debug mode", "Debug Node": "Debug mode",
"Failed": "Execution failed", "Failed": "Execution failed",
"Not intro": "This node has no introduction~\\", "Not intro": "This node has no introduction~",
"Run from here": "Run from here", "Run from here": "Run from here",
"Run result": "Run result", "Run result": "Run result",
"Running": "Running", "Running": "Running",

View File

@@ -16,7 +16,7 @@
"Tool input": "Tool", "Tool input": "Tool",
"code": { "code": {
"Reset template": "Reset template", "Reset template": "Reset template",
"Reset template confirm": "Are you sure to restore the code template? Be careful to save the current code." "Reset template confirm": "Are you sure to restore the code template? All input and output to template values will be reset, please be careful to save the current code."
}, },
"ifelse": { "ifelse": {
"Input value": "Input", "Input value": "Input",

View File

@@ -56,7 +56,7 @@
}, },
"module": { "module": {
"Combine Modules": "组合模块", "Combine Modules": "组合模块",
"Confirm Sync": "将会使用最新模板进行覆盖,可能会丢失一些旧的配置信息,请确认", "Confirm Sync": "将会更新至最新模板配置,不存在模板中的字段将会被删除(包括所有自定义字段),建议您先复制一份节点,再更新原来节点的版本。",
"Custom Title Tip": "该标题名字会展示在对话过程中", "Custom Title Tip": "该标题名字会展示在对话过程中",
"My Modules": "", "My Modules": "",
"No Modules": "没找到插件", "No Modules": "没找到插件",

View File

@@ -1077,7 +1077,7 @@
"Debug": "调试", "Debug": "调试",
"Debug Node": "Debug 模式", "Debug Node": "Debug 模式",
"Failed": "运行失败", "Failed": "运行失败",
"Not intro": "这个节点没有介绍~\\", "Not intro": "这个节点没有介绍~",
"Run from here": "从这里开始运行", "Run from here": "从这里开始运行",
"Run result": "", "Run result": "",
"Running": "运行中", "Running": "运行中",

View File

@@ -16,7 +16,7 @@
"Tool input": "工具参数", "Tool input": "工具参数",
"code": { "code": {
"Reset template": "还原模板", "Reset template": "还原模板",
"Reset template confirm": "确认还原代码模板?请注意保存当前代码。" "Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。"
}, },
"ifelse": { "ifelse": {
"Input value": "输入值", "Input value": "输入值",

View File

@@ -155,7 +155,7 @@ const SelectOneResource = ({
return loading ? ( return loading ? (
<Loading fixed={false} /> <Loading fixed={false} />
) : ( ) : (
<Box maxH={maxH} overflow={'auto'}> <Box maxH={maxH} h={'100%'} overflow={'auto'}>
<Render list={concatRoot} /> <Render list={concatRoot} />
</Box> </Box>
); );

View File

@@ -63,8 +63,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
const avatar = getValues('avatar'); const avatar = getValues('avatar');
// submit config // submit config
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({ const { runAsync: saveSubmitSuccess, loading: btnLoading } = useRequest2(
mutationFn: async (data: AppSchema) => { async (data: AppSchema) => {
await updateAppDetail({ await updateAppDetail({
name: data.name, name: data.name,
avatar: data.avatar, avatar: data.avatar,
@@ -72,16 +72,17 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
defaultPermission: data.defaultPermission defaultPermission: data.defaultPermission
}); });
}, },
onSuccess() { {
onClose(); onSuccess() {
toast({ toast({
title: t('common.Update Success'), title: t('common.Update Success'),
status: 'success' status: 'success'
}); });
reloadApp(); reloadApp();
}, },
errorToast: t('common.Update Failed') errorToast: t('common.Update Failed')
}); }
);
const saveSubmitError = useCallback(() => { const saveSubmitError = useCallback(() => {
// deep search message // deep search message
@@ -101,8 +102,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
}, [errors, t, toast]); }, [errors, t, toast]);
const saveUpdateModel = useCallback( const saveUpdateModel = useCallback(
() => handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)(), () => handleSubmit((data) => saveSubmitSuccess(data).then(onClose), saveSubmitError)(),
[handleSubmit, saveSubmitError, saveSubmitSuccess] [handleSubmit, onClose, saveSubmitError, saveSubmitSuccess]
); );
const onSelectFile = useCallback( const onSelectFile = useCallback(
@@ -210,7 +211,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
isInheritPermission={appDetail.inheritPermission} isInheritPermission={appDetail.inheritPermission}
onChange={(v) => { onChange={(v) => {
setValue('defaultPermission', v); setValue('defaultPermission', v);
handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)(); return handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)();
}} }}
hasParent={!!appDetail.parentId} hasParent={!!appDetail.parentId}
/> />

View File

@@ -396,7 +396,9 @@ const RenderList = React.memo(function RenderList({
{t(template.name)} {t(template.name)}
</Box> </Box>
</Flex> </Flex>
<Box mt={2}>{t(template.intro || 'core.workflow.Not intro')}</Box> <Box mt={2} color={'myGray.500'}>
{t(template.intro) || t('core.workflow.Not intro')}
</Box>
</Box> </Box>
} }
> >

View File

@@ -16,7 +16,8 @@ import CodeEditor from '@fastgpt/web/components/common/Textarea/CodeEditor';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { useI18n } from '@/web/context/I18n'; import { useI18n } from '@/web/context/I18n';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { JS_TEMPLATE } from '@fastgpt/global/core/workflow/template/system/sandbox/constants'; import { getLatestNodeTemplate } from '@/web/core/workflow/utils';
import { CodeNode } from '@fastgpt/global/core/workflow/template/system/sandbox';
const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => { const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -24,6 +25,8 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { nodeId, inputs, outputs } = data; const { nodeId, inputs, outputs } = data;
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs); const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
const { isTool, commonInputs } = splitToolInputs(inputs, nodeId); const { isTool, commonInputs } = splitToolInputs(inputs, nodeId);
const { ConfirmModal, openConfirm } = useConfirm({ const { ConfirmModal, openConfirm } = useConfirm({
content: workflowT('code.Reset template confirm') content: workflowT('code.Reset template confirm')
@@ -41,14 +44,9 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
color={'primary.500'} color={'primary.500'}
fontSize={'xs'} fontSize={'xs'}
onClick={openConfirm(() => { onClick={openConfirm(() => {
onChangeNode({ onResetNode({
nodeId, id: nodeId,
type: 'updateInput', node: getLatestNodeTemplate(data, CodeNode)
key: item.key,
value: {
...item,
value: JS_TEMPLATE
}
}); });
})} })}
> >

View File

@@ -37,6 +37,7 @@ export const defaultInput: FlowNodeInputItemType = {
renderTypeList: [FlowNodeInputTypeEnum.reference], // Can only choose one here renderTypeList: [FlowNodeInputTypeEnum.reference], // Can only choose one here
selectedTypeIndex: 0, selectedTypeIndex: 0,
valueType: WorkflowIOValueTypeEnum.string, valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: '', key: '',
label: '' label: ''
}; };

View File

@@ -16,7 +16,7 @@ import { useDebug } from '../../hooks/useDebug';
import { ResponseBox } from '@/components/ChatBox/components/WholeResponseModal'; import { ResponseBox } from '@/components/ChatBox/components/WholeResponseModal';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { getPreviewPluginNode } from '@/web/core/app/api/plugin'; import { getPreviewPluginNode } from '@/web/core/app/api/plugin';
import { storeNode2FlowNode, updateFlowNodeVersion } from '@/web/core/workflow/utils'; import { storeNode2FlowNode, getLatestNodeTemplate } 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';
import { WorkflowContext } from '../../../context'; import { WorkflowContext } from '../../../context';
@@ -24,8 +24,6 @@ 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 '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useMount } from 'ahooks';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useWorkflowUtils } from '../../hooks/useUtils'; import { useWorkflowUtils } from '../../hooks/useUtils';
@@ -56,13 +54,11 @@ const NodeCard = (props: Props) => {
minW = '300px', minW = '300px',
maxW = '600px', maxW = '600px',
nodeId, nodeId,
flowNodeType,
selected, selected,
menuForbid, menuForbid,
isTool = false, isTool = false,
isError = false, isError = false,
debugResult, debugResult
pluginId
} = props; } = props;
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
@@ -106,11 +102,11 @@ const NodeCard = (props: Props) => {
); );
const hasNewVersion = newNodeVersion && newNodeVersion !== node?.version; const hasNewVersion = newNodeVersion && newNodeVersion !== node?.version;
const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType); const { runAsync: onClickSyncVersion } = useRequest2(
async () => {
const onClickSyncVersion = useCallback(async () => { const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType);
try {
if (!node || !template) return; if (!node || !template) return;
if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) { if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) {
if (!node.pluginId) return; if (!node.pluginId) return;
onResetNode({ onResetNode({
@@ -120,14 +116,15 @@ const NodeCard = (props: Props) => {
} else { } else {
onResetNode({ onResetNode({
id: nodeId, id: nodeId,
node: updateFlowNodeVersion(node, template) node: getLatestNodeTemplate(node, template)
}); });
} }
await getNodeVersion(); await getNodeVersion();
} catch (error) { },
console.error('Error fetching plugin module:', error); {
refreshDeps: [node, nodeId, onResetNode, getNodeVersion]
} }
}, [getNodeVersion, node, nodeId, onResetNode, template]); );
/* Node header */ /* Node header */
const Header = useMemo(() => { const Header = useMemo(() => {
@@ -197,12 +194,7 @@ const NodeCard = (props: Props) => {
</MyTooltip> </MyTooltip>
)} )}
</Flex> </Flex>
<MenuRender <MenuRender nodeId={nodeId} menuForbid={menuForbid} />
nodeId={nodeId}
pluginId={pluginId}
flowNodeType={flowNodeType}
menuForbid={menuForbid}
/>
<NodeIntro nodeId={nodeId} intro={intro} /> <NodeIntro nodeId={nodeId} intro={intro} />
</Box> </Box>
<ConfirmSyncModal /> <ConfirmSyncModal />
@@ -219,8 +211,6 @@ const NodeCard = (props: Props) => {
appT, appT,
onOpenConfirmSync, onOpenConfirmSync,
onClickSyncVersion, onClickSyncVersion,
pluginId,
flowNodeType,
intro, intro,
ConfirmSyncModal, ConfirmSyncModal,
onOpenCustomTitleModal, onOpenCustomTitleModal,
@@ -274,13 +264,9 @@ export default React.memo(NodeCard);
const MenuRender = React.memo(function MenuRender({ const MenuRender = React.memo(function MenuRender({
nodeId, nodeId,
pluginId,
flowNodeType,
menuForbid menuForbid
}: { }: {
nodeId: string; nodeId: string;
pluginId?: string;
flowNodeType: Props['flowNodeType'];
menuForbid?: Props['menuForbid']; menuForbid?: Props['menuForbid'];
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -164,8 +164,12 @@ const ChatHistorySlider = ({
px: 1 px: 1
}} }}
list={[ list={[
{ label: t('core.chat.Recent use'), value: TabEnum.recently }, ...(isTeamChat
...(!isTeamChat ? [{ label: t('App'), value: TabEnum.app }] : []), ? [{ label: t('App'), value: TabEnum.recently }]
: [
{ label: t('core.chat.Recent use'), value: TabEnum.recently },
{ label: t('App'), value: TabEnum.app }
]),
{ label: t('core.chat.History'), value: TabEnum.history } { label: t('core.chat.History'), value: TabEnum.history }
]} ]}
value={currentTab} value={currentTab}
@@ -185,8 +189,8 @@ const ChatHistorySlider = ({
> >
{t('core.chat.New Chat')} {t('core.chat.New Chat')}
</Button> </Button>
{/* Clear */}
{(isPc || !showApps) && ( {isPc && (
<IconButton <IconButton
ml={3} ml={3}
h={'100%'} h={'100%'}

View File

@@ -8,6 +8,7 @@ import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/cons
import { import {
EDGE_TYPE, EDGE_TYPE,
FlowNodeInputTypeEnum, FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum, FlowNodeTypeEnum,
defaultNodeVersion defaultNodeVersion
} from '@fastgpt/global/core/workflow/node/constant'; } from '@fastgpt/global/core/workflow/node/constant';
@@ -34,6 +35,7 @@ import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/syste
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant'; import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
import { AppChatConfigType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import { cloneDeep, isEqual } from 'lodash'; import { cloneDeep, isEqual } from 'lodash';
import { getInputComponentProps } from '@fastgpt/global/core/workflow/node/io/utils';
export const nodeTemplate2FlowNode = ({ export const nodeTemplate2FlowNode = ({
template, template,
@@ -70,13 +72,24 @@ export const storeNode2FlowNode = ({
moduleTemplatesFlat.find((template) => template.flowNodeType === storeNode.flowNodeType) || moduleTemplatesFlat.find((template) => template.flowNodeType === storeNode.flowNodeType) ||
EmptyNode; EmptyNode;
const templateInputs = template.inputs.filter((input) => !input.canEdit);
const templateOutputs = template.outputs.filter(
(output) => output.type !== FlowNodeOutputTypeEnum.dynamic
);
const dynamicInput = template.inputs.find(
(input) => input.renderTypeList[0] === FlowNodeInputTypeEnum.addInputParam
);
// replace item data // replace item data
const nodeItem: FlowNodeItemType = { const nodeItem: FlowNodeItemType = {
...template, ...template,
...storeNode, ...storeNode,
version: storeNode.version ?? template.version ?? defaultNodeVersion, version: storeNode.version ?? template.version ?? defaultNodeVersion,
inputs: template.inputs /*
Inputs and outputs, New fields are added, not reduced
*/
inputs: templateInputs
.map<FlowNodeInputItemType>((templateInput) => { .map<FlowNodeInputItemType>((templateInput) => {
const storeInput = const storeInput =
storeNode.inputs.find((item) => item.key === templateInput.key) || templateInput; storeNode.inputs.find((item) => item.key === templateInput.key) || templateInput;
@@ -91,27 +104,35 @@ export const storeNode2FlowNode = ({
}; };
}) })
.concat( .concat(
/* /* Concat dynamic inputs */
1. Plugin input storeNode.inputs
2. Old version adapt: Dynamic input will be added to the node inputs. .filter((item) => !templateInputs.find((input) => input.key === item.key))
*/ .map((item) => {
storeNode.inputs.filter((item) => !template.inputs.find((input) => input.key === item.key)) if (!dynamicInput) return item;
return {
...item,
...getInputComponentProps(dynamicInput)
};
})
), ),
outputs: template.outputs outputs: templateOutputs
.map<FlowNodeOutputItemType>((templateOutput) => { .map<FlowNodeOutputItemType>((templateOutput) => {
const storeOutput = const storeOutput =
template.outputs.find((item) => item.key === templateOutput.key) || templateOutput; template.outputs.find((item) => item.key === templateOutput.key) || templateOutput;
return { return {
...storeOutput, ...storeOutput,
...templateOutput, ...templateOutput,
id: storeOutput.id ?? templateOutput.id, id: storeOutput.id ?? templateOutput.id,
label: storeOutput.label ?? templateOutput.label,
value: storeOutput.value ?? templateOutput.value value: storeOutput.value ?? templateOutput.value
}; };
}) })
.concat( .concat(
storeNode.outputs.filter( storeNode.outputs.filter(
(item) => !template.outputs.find((output) => output.key === item.key) (item) => !templateOutputs.find((output) => output.key === item.key)
) )
) )
}; };
@@ -365,37 +386,42 @@ export const getWorkflowGlobalVariables = ({
export type CombinedItemType = Partial<FlowNodeInputItemType> & Partial<FlowNodeOutputItemType>; export type CombinedItemType = Partial<FlowNodeInputItemType> & Partial<FlowNodeOutputItemType>;
export const updateFlowNodeVersion = ( /* Reset node to latest version */
export const getLatestNodeTemplate = (
node: FlowNodeItemType, node: FlowNodeItemType,
template: FlowNodeTemplateType template: FlowNodeTemplateType
): FlowNodeItemType => { ): FlowNodeItemType => {
function updateArrayBasedOnTemplate<T extends FlowNodeInputItemType | FlowNodeOutputItemType>(
nodeArray: T[],
templateArray: T[]
): T[] {
return templateArray.map((templateItem) => {
const nodeItem = nodeArray.find((item) => item.key === templateItem.key);
if (nodeItem) {
return { ...templateItem, ...nodeItem } as T;
}
return { ...templateItem };
});
}
const updatedNode: FlowNodeItemType = { const updatedNode: FlowNodeItemType = {
...node, ...node,
...template, ...template,
inputs: template.inputs.map((templateItem) => {
const nodeItem = node.inputs.find((item) => item.key === templateItem.key);
if (nodeItem) {
return {
...templateItem,
value: nodeItem.value,
selectedTypeIndex: nodeItem.selectedTypeIndex,
valueType: nodeItem.valueType
};
}
return { ...templateItem };
}),
outputs: template.outputs.map((templateItem) => {
const nodeItem = node.outputs.find((item) => item.key === templateItem.key);
if (nodeItem) {
return {
...templateItem,
id: nodeItem.id,
value: nodeItem.value,
valueType: nodeItem.valueType
};
}
return { ...templateItem };
}),
name: node.name, name: node.name,
intro: node.intro intro: node.intro
}; };
if (node.inputs && template.inputs) {
updatedNode.inputs = updateArrayBasedOnTemplate(node.inputs, template.inputs);
}
if (node.outputs && template.outputs) {
updatedNode.outputs = updateArrayBasedOnTemplate(node.outputs, template.outputs);
}
return updatedNode; return updatedNode;
}; };