fix: global variable during debug & variable update textarea rerender (#2553)

* fix: global variable during debug & variable update textarea rerender

* update var node use prompt editor

* fix
This commit is contained in:
heheer
2024-08-29 14:09:20 +08:00
committed by GitHub
parent 0632dfed80
commit 034108c218
8 changed files with 128 additions and 65 deletions

View File

@@ -8,6 +8,7 @@ import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
import { removeSystemVariable, valueTypeFormat } from '../utils';
import { replaceVariableLabel } from '@fastgpt/global/core/workflow/utils';
type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.updateList]: TUpdateListItem[];
@@ -15,7 +16,7 @@ type Props = ModuleDispatchProps<{
type Response = DispatchNodeResultType<{}>;
export const dispatchUpdateVariable = async (props: Props): Promise<Response> => {
const { params, variables, runtimeNodes, workflowStreamResponse } = props;
const { params, variables, runtimeNodes, workflowStreamResponse, node } = props;
const { updateList } = params;
updateList.forEach((item) => {
@@ -28,7 +29,16 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
const value = (() => {
if (!item.value?.[0]) {
return valueTypeFormat(item.value?.[1], item.valueType);
const formatValue = valueTypeFormat(item.value?.[1], item.valueType);
return typeof formatValue === 'string'
? replaceVariableLabel({
text: formatValue,
nodes: runtimeNodes,
variables,
runningNode: node
})
: formatValue;
} else {
return getReferenceVariableValue({
value: item.value,

View File

@@ -40,14 +40,20 @@ const PromptEditor = ({
const { isOpen, onOpen, onClose } = useDisclosure();
const { t } = useTranslation();
const onChangeInput = useCallback((editorState: EditorState, editor: LexicalEditor) => {
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
onChange?.(text);
}, []);
const onBlurInput = useCallback((editor: LexicalEditor) => {
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
onBlur?.(text);
}, []);
const onChangeInput = useCallback(
(editorState: EditorState, editor: LexicalEditor) => {
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
onChange?.(text);
},
[onChange]
);
const onBlurInput = useCallback(
(editor: LexicalEditor) => {
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
onBlur?.(text);
},
[onBlur]
);
return (
<>

View File

@@ -16,4 +16,5 @@ export type PostWorkflowDebugResponse = {
finishedEdges: RuntimeEdgeItemType[];
nextStepRunNodes: RuntimeNodeItemType[];
flowResponses: ChatHistoryItemResType[];
newVariables: Record<string, any>;
};

View File

@@ -39,7 +39,7 @@ async function handler(
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
/* start process */
const { flowUsages, flowResponses, debugResponse } = await dispatchWorkFlow({
const { flowUsages, flowResponses, debugResponse, newVariables } = await dispatchWorkFlow({
res,
requestOrigin: req.headers.origin,
mode: 'debug',
@@ -68,6 +68,7 @@ async function handler(
return {
...debugResponse,
newVariables,
flowResponses
};
}

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import NodeCard from './render/NodeCard';
import { NodeProps } from 'reactflow';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
@@ -12,8 +12,7 @@ import {
NumberInput,
NumberInputField,
NumberInputStepper,
Switch,
Textarea
Switch
} from '@chakra-ui/react';
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
@@ -33,6 +32,9 @@ import { ReferSelector, useReference } from './render/RenderInput/templates/Refe
import { getRefData } from '@/web/core/workflow/utils';
import { isReferenceValue } from '@fastgpt/global/core/workflow/utils';
import { AppContext } from '@/pages/app/detail/components/context';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
import { useCreation } from 'ahooks';
import { getEditorVariables } from '../../utils';
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { inputs = [], nodeId } = data;
@@ -41,6 +43,17 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
const variables = useCreation(() => {
return getEditorVariables({
nodeId,
nodeList,
edges,
appDetail,
t
});
}, [nodeList, edges, inputs, t]);
const updateList = useMemo(
() =>
@@ -90,7 +103,6 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
chatConfig: appDetail.chatConfig,
t
});
const renderTypeData = menuList.find((item) => item.renderType === updateItem.renderType);
const handleUpdate = (newValue: ReferenceValueProps | string) => {
if (isReferenceValue(newValue)) {
@@ -199,12 +211,15 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
}
if (valueType === WorkflowIOValueTypeEnum.string) {
return (
<Textarea
bg="white"
value={updateItem.value?.[1] || ''}
w="300px"
onChange={(e) => handleUpdate(e.target.value)}
/>
<Box w={'300px'}>
<PromptEditor
value={updateItem.value?.[1] || ''}
onChange={handleUpdate}
showOpenModal={false}
variableLabels={variables}
h={100}
/>
</Box>
);
}
if (valueType === WorkflowIOValueTypeEnum.number) {
@@ -248,7 +263,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
})}
</>
);
}, [nodeId, nodeList, onUpdateList, t, updateList]);
}, [appDetail.chatConfig, nodeId, nodeList, onUpdateList, t, updateList, variables]);
return (
<NodeCard selected={selected} maxW={'1000px'} {...data}>

View File

@@ -4,61 +4,27 @@ import { useTranslation } from 'next-i18next';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
import { computedNodeInputReference } from '@/web/core/workflow/utils';
import { useCreation } from 'ahooks';
import { AppContext } from '@/pages/app/detail/components/context';
import { getEditorVariables } from '../../../../../utils';
const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
const { t } = useTranslation();
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const getNodeDynamicInputs = useContextSelector(WorkflowContext, (v) => v.getNodeDynamicInputs);
const { appDetail } = useContextSelector(AppContext, (v) => v);
// get variable
const variables = useCreation(() => {
const currentNode = nodeList.find((node) => node.nodeId === nodeId)!;
const nodeVariables = getNodeDynamicInputs(nodeId).map((item) => ({
key: item.key,
label: item.label,
parent: {
id: currentNode.nodeId,
label: currentNode.name,
avatar: currentNode.avatar
}
}));
const sourceNodes = computedNodeInputReference({
return getEditorVariables({
nodeId,
nodes: nodeList,
edges: edges,
chatConfig: appDetail.chatConfig,
nodeList,
edges,
appDetail,
t
});
const sourceNodeVariables = !sourceNodes
? []
: sourceNodes
.map((node) => {
return node.outputs
.filter((output) => !!output.label)
.map((output) => {
return {
label: t((output.label as any) || ''),
key: output.id,
parent: {
id: node.nodeId,
label: node.name,
avatar: node.avatar
}
};
});
})
.flat();
return [...nodeVariables, ...sourceNodeVariables];
}, [nodeList, edges, inputs, t]);
const onChange = useCallback(

View File

@@ -188,6 +188,7 @@ type DebugDataType = {
runtimeNodes: RuntimeNodeItemType[];
runtimeEdges: RuntimeEdgeItemType[];
nextRunNodes: RuntimeNodeItemType[];
variables: Record<string, any>;
};
export const WorkflowContext = createContext<WorkflowContextType>({
@@ -676,13 +677,14 @@ const WorkflowContextProvider = ({
try {
// 4. Run one step
const { finishedEdges, finishedNodes, nextStepRunNodes, flowResponses } =
const { finishedEdges, finishedNodes, nextStepRunNodes, flowResponses, newVariables } =
await postWorkflowDebug({
nodes: runtimeNodes,
edges: debugData.runtimeEdges,
variables: {
appId,
cTime: formatTime2YMDHMW()
cTime: formatTime2YMDHMW(),
...debugData.variables
},
appId
});
@@ -703,7 +705,8 @@ const WorkflowContextProvider = ({
status
};
}),
nextRunNodes: nextStepRunNodes
nextRunNodes: nextStepRunNodes,
variables: newVariables
};
setWorkflowDebugData(newStoreDebugData);
@@ -794,7 +797,8 @@ const WorkflowContextProvider = ({
const data = {
runtimeNodes,
runtimeEdges,
nextRunNodes: runtimeNodes.filter((node) => node.nodeId === entryNodeId)
nextRunNodes: runtimeNodes.filter((node) => node.nodeId === entryNodeId),
variables: {}
};
onStopNodeDebug();
setWorkflowDebugData(data);

View File

@@ -1,7 +1,10 @@
import { computedNodeInputReference } from '@/web/core/workflow/utils';
import { AppDetailType } from '@fastgpt/global/core/app/type';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { FlowNodeItemType, StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
import { TFunction } from 'i18next';
import { type Node, type Edge } from 'reactflow';
export const uiWorkflow2StoreWorkflow = ({
@@ -70,3 +73,60 @@ export const filterExportModules = (modules: StoreNodeItemType[]) => {
export default function Dom() {
return <></>;
}
export const getEditorVariables = ({
nodeId,
nodeList,
edges,
appDetail,
t
}: {
nodeId: string;
nodeList: FlowNodeItemType[];
edges: Edge<any>[];
appDetail: AppDetailType;
t: TFunction;
}) => {
const currentNode = nodeList.find((node) => node.nodeId === nodeId)!;
const nodeVariables = currentNode.inputs
.filter((input) => input.canEdit)
.map((item) => ({
key: item.key,
label: item.label,
parent: {
id: currentNode.nodeId,
label: currentNode.name,
avatar: currentNode.avatar
}
}));
const sourceNodes = computedNodeInputReference({
nodeId,
nodes: nodeList,
edges: edges,
chatConfig: appDetail.chatConfig,
t
});
const sourceNodeVariables = !sourceNodes
? []
: sourceNodes
.map((node) => {
return node.outputs
.filter((output) => !!output.label)
.map((output) => {
return {
label: t((output.label as any) || ''),
key: output.id,
parent: {
id: node.nodeId,
label: node.name,
avatar: node.avatar
}
};
});
})
.flat();
return [...nodeVariables, ...sourceNodeVariables];
};