mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-28 00:56:26 +00:00
4.8.6 fix (#1970)
* 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:
@@ -155,7 +155,7 @@ const SelectOneResource = ({
|
||||
return loading ? (
|
||||
<Loading fixed={false} />
|
||||
) : (
|
||||
<Box maxH={maxH} overflow={'auto'}>
|
||||
<Box maxH={maxH} h={'100%'} overflow={'auto'}>
|
||||
<Render list={concatRoot} />
|
||||
</Box>
|
||||
);
|
||||
|
@@ -63,8 +63,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const avatar = getValues('avatar');
|
||||
|
||||
// submit config
|
||||
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
|
||||
mutationFn: async (data: AppSchema) => {
|
||||
const { runAsync: saveSubmitSuccess, loading: btnLoading } = useRequest2(
|
||||
async (data: AppSchema) => {
|
||||
await updateAppDetail({
|
||||
name: data.name,
|
||||
avatar: data.avatar,
|
||||
@@ -72,16 +72,17 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||
defaultPermission: data.defaultPermission
|
||||
});
|
||||
},
|
||||
onSuccess() {
|
||||
onClose();
|
||||
toast({
|
||||
title: t('common.Update Success'),
|
||||
status: 'success'
|
||||
});
|
||||
reloadApp();
|
||||
},
|
||||
errorToast: t('common.Update Failed')
|
||||
});
|
||||
{
|
||||
onSuccess() {
|
||||
toast({
|
||||
title: t('common.Update Success'),
|
||||
status: 'success'
|
||||
});
|
||||
reloadApp();
|
||||
},
|
||||
errorToast: t('common.Update Failed')
|
||||
}
|
||||
);
|
||||
|
||||
const saveSubmitError = useCallback(() => {
|
||||
// deep search message
|
||||
@@ -101,8 +102,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||
}, [errors, t, toast]);
|
||||
|
||||
const saveUpdateModel = useCallback(
|
||||
() => handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)(),
|
||||
[handleSubmit, saveSubmitError, saveSubmitSuccess]
|
||||
() => handleSubmit((data) => saveSubmitSuccess(data).then(onClose), saveSubmitError)(),
|
||||
[handleSubmit, onClose, saveSubmitError, saveSubmitSuccess]
|
||||
);
|
||||
|
||||
const onSelectFile = useCallback(
|
||||
@@ -210,7 +211,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||
isInheritPermission={appDetail.inheritPermission}
|
||||
onChange={(v) => {
|
||||
setValue('defaultPermission', v);
|
||||
handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)();
|
||||
return handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)();
|
||||
}}
|
||||
hasParent={!!appDetail.parentId}
|
||||
/>
|
||||
|
@@ -396,7 +396,9 @@ const RenderList = React.memo(function RenderList({
|
||||
{t(template.name)}
|
||||
</Box>
|
||||
</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>
|
||||
}
|
||||
>
|
||||
|
@@ -16,7 +16,8 @@ import CodeEditor from '@fastgpt/web/components/common/Textarea/CodeEditor';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
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 { t } = useTranslation();
|
||||
@@ -24,6 +25,8 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { nodeId, inputs, outputs } = data;
|
||||
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
|
||||
|
||||
const { isTool, commonInputs } = splitToolInputs(inputs, nodeId);
|
||||
const { ConfirmModal, openConfirm } = useConfirm({
|
||||
content: workflowT('code.Reset template confirm')
|
||||
@@ -41,14 +44,9 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
color={'primary.500'}
|
||||
fontSize={'xs'}
|
||||
onClick={openConfirm(() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: JS_TEMPLATE
|
||||
}
|
||||
onResetNode({
|
||||
id: nodeId,
|
||||
node: getLatestNodeTemplate(data, CodeNode)
|
||||
});
|
||||
})}
|
||||
>
|
||||
|
@@ -37,6 +37,7 @@ export const defaultInput: FlowNodeInputItemType = {
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference], // Can only choose one here
|
||||
selectedTypeIndex: 0,
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
canEdit: true,
|
||||
key: '',
|
||||
label: ''
|
||||
};
|
||||
|
@@ -16,7 +16,7 @@ import { useDebug } from '../../hooks/useDebug';
|
||||
import { ResponseBox } from '@/components/ChatBox/components/WholeResponseModal';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
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 { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
@@ -24,8 +24,6 @@ import { useI18n } from '@/web/context/I18n';
|
||||
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
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 { useWorkflowUtils } from '../../hooks/useUtils';
|
||||
|
||||
@@ -56,13 +54,11 @@ const NodeCard = (props: Props) => {
|
||||
minW = '300px',
|
||||
maxW = '600px',
|
||||
nodeId,
|
||||
flowNodeType,
|
||||
selected,
|
||||
menuForbid,
|
||||
isTool = false,
|
||||
isError = false,
|
||||
debugResult,
|
||||
pluginId
|
||||
debugResult
|
||||
} = props;
|
||||
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
@@ -106,11 +102,11 @@ const NodeCard = (props: Props) => {
|
||||
);
|
||||
const hasNewVersion = newNodeVersion && newNodeVersion !== node?.version;
|
||||
|
||||
const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType);
|
||||
|
||||
const onClickSyncVersion = useCallback(async () => {
|
||||
try {
|
||||
const { runAsync: onClickSyncVersion } = useRequest2(
|
||||
async () => {
|
||||
const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType);
|
||||
if (!node || !template) return;
|
||||
|
||||
if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) {
|
||||
if (!node.pluginId) return;
|
||||
onResetNode({
|
||||
@@ -120,14 +116,15 @@ const NodeCard = (props: Props) => {
|
||||
} else {
|
||||
onResetNode({
|
||||
id: nodeId,
|
||||
node: updateFlowNodeVersion(node, template)
|
||||
node: getLatestNodeTemplate(node, template)
|
||||
});
|
||||
}
|
||||
await getNodeVersion();
|
||||
} catch (error) {
|
||||
console.error('Error fetching plugin module:', error);
|
||||
},
|
||||
{
|
||||
refreshDeps: [node, nodeId, onResetNode, getNodeVersion]
|
||||
}
|
||||
}, [getNodeVersion, node, nodeId, onResetNode, template]);
|
||||
);
|
||||
|
||||
/* Node header */
|
||||
const Header = useMemo(() => {
|
||||
@@ -197,12 +194,7 @@ const NodeCard = (props: Props) => {
|
||||
</MyTooltip>
|
||||
)}
|
||||
</Flex>
|
||||
<MenuRender
|
||||
nodeId={nodeId}
|
||||
pluginId={pluginId}
|
||||
flowNodeType={flowNodeType}
|
||||
menuForbid={menuForbid}
|
||||
/>
|
||||
<MenuRender nodeId={nodeId} menuForbid={menuForbid} />
|
||||
<NodeIntro nodeId={nodeId} intro={intro} />
|
||||
</Box>
|
||||
<ConfirmSyncModal />
|
||||
@@ -219,8 +211,6 @@ const NodeCard = (props: Props) => {
|
||||
appT,
|
||||
onOpenConfirmSync,
|
||||
onClickSyncVersion,
|
||||
pluginId,
|
||||
flowNodeType,
|
||||
intro,
|
||||
ConfirmSyncModal,
|
||||
onOpenCustomTitleModal,
|
||||
@@ -274,13 +264,9 @@ export default React.memo(NodeCard);
|
||||
|
||||
const MenuRender = React.memo(function MenuRender({
|
||||
nodeId,
|
||||
pluginId,
|
||||
flowNodeType,
|
||||
menuForbid
|
||||
}: {
|
||||
nodeId: string;
|
||||
pluginId?: string;
|
||||
flowNodeType: Props['flowNodeType'];
|
||||
menuForbid?: Props['menuForbid'];
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
@@ -164,8 +164,12 @@ const ChatHistorySlider = ({
|
||||
px: 1
|
||||
}}
|
||||
list={[
|
||||
{ label: t('core.chat.Recent use'), value: TabEnum.recently },
|
||||
...(!isTeamChat ? [{ label: t('App'), value: TabEnum.app }] : []),
|
||||
...(isTeamChat
|
||||
? [{ 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 }
|
||||
]}
|
||||
value={currentTab}
|
||||
@@ -185,8 +189,8 @@ const ChatHistorySlider = ({
|
||||
>
|
||||
{t('core.chat.New Chat')}
|
||||
</Button>
|
||||
|
||||
{(isPc || !showApps) && (
|
||||
{/* Clear */}
|
||||
{isPc && (
|
||||
<IconButton
|
||||
ml={3}
|
||||
h={'100%'}
|
||||
|
@@ -8,6 +8,7 @@ import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/cons
|
||||
import {
|
||||
EDGE_TYPE,
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum,
|
||||
defaultNodeVersion
|
||||
} 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 { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
import { cloneDeep, isEqual } from 'lodash';
|
||||
import { getInputComponentProps } from '@fastgpt/global/core/workflow/node/io/utils';
|
||||
|
||||
export const nodeTemplate2FlowNode = ({
|
||||
template,
|
||||
@@ -70,13 +72,24 @@ export const storeNode2FlowNode = ({
|
||||
moduleTemplatesFlat.find((template) => template.flowNodeType === storeNode.flowNodeType) ||
|
||||
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
|
||||
const nodeItem: FlowNodeItemType = {
|
||||
...template,
|
||||
...storeNode,
|
||||
version: storeNode.version ?? template.version ?? defaultNodeVersion,
|
||||
|
||||
inputs: template.inputs
|
||||
/*
|
||||
Inputs and outputs, New fields are added, not reduced
|
||||
*/
|
||||
inputs: templateInputs
|
||||
.map<FlowNodeInputItemType>((templateInput) => {
|
||||
const storeInput =
|
||||
storeNode.inputs.find((item) => item.key === templateInput.key) || templateInput;
|
||||
@@ -91,27 +104,35 @@ export const storeNode2FlowNode = ({
|
||||
};
|
||||
})
|
||||
.concat(
|
||||
/*
|
||||
1. Plugin input
|
||||
2. Old version adapt: Dynamic input will be added to the node inputs.
|
||||
*/
|
||||
storeNode.inputs.filter((item) => !template.inputs.find((input) => input.key === item.key))
|
||||
/* Concat dynamic inputs */
|
||||
storeNode.inputs
|
||||
.filter((item) => !templateInputs.find((input) => input.key === item.key))
|
||||
.map((item) => {
|
||||
if (!dynamicInput) return item;
|
||||
|
||||
return {
|
||||
...item,
|
||||
...getInputComponentProps(dynamicInput)
|
||||
};
|
||||
})
|
||||
),
|
||||
outputs: template.outputs
|
||||
outputs: templateOutputs
|
||||
.map<FlowNodeOutputItemType>((templateOutput) => {
|
||||
const storeOutput =
|
||||
template.outputs.find((item) => item.key === templateOutput.key) || templateOutput;
|
||||
|
||||
return {
|
||||
...storeOutput,
|
||||
...templateOutput,
|
||||
|
||||
id: storeOutput.id ?? templateOutput.id,
|
||||
label: storeOutput.label ?? templateOutput.label,
|
||||
value: storeOutput.value ?? templateOutput.value
|
||||
};
|
||||
})
|
||||
.concat(
|
||||
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 const updateFlowNodeVersion = (
|
||||
/* Reset node to latest version */
|
||||
export const getLatestNodeTemplate = (
|
||||
node: FlowNodeItemType,
|
||||
template: FlowNodeTemplateType
|
||||
): 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 = {
|
||||
...node,
|
||||
...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,
|
||||
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;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user