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

View File

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

View File

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

View File

@@ -39,7 +39,7 @@ async function handler(
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId); const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
/* start process */ /* start process */
const { flowUsages, flowResponses, debugResponse } = await dispatchWorkFlow({ const { flowUsages, flowResponses, debugResponse, newVariables } = await dispatchWorkFlow({
res, res,
requestOrigin: req.headers.origin, requestOrigin: req.headers.origin,
mode: 'debug', mode: 'debug',
@@ -68,6 +68,7 @@ async function handler(
return { return {
...debugResponse, ...debugResponse,
newVariables,
flowResponses 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 NodeCard from './render/NodeCard';
import { NodeProps } from 'reactflow'; import { NodeProps } from 'reactflow';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
@@ -12,8 +12,7 @@ import {
NumberInput, NumberInput,
NumberInputField, NumberInputField,
NumberInputStepper, NumberInputStepper,
Switch, Switch
Textarea
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type'; import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; 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 { getRefData } from '@/web/core/workflow/utils';
import { isReferenceValue } from '@fastgpt/global/core/workflow/utils'; import { isReferenceValue } from '@fastgpt/global/core/workflow/utils';
import { AppContext } from '@/pages/app/detail/components/context'; 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 NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { inputs = [], nodeId } = data; const { inputs = [], nodeId } = data;
@@ -41,6 +43,17 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const appDetail = useContextSelector(AppContext, (v) => v.appDetail); 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( const updateList = useMemo(
() => () =>
@@ -90,7 +103,6 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
chatConfig: appDetail.chatConfig, chatConfig: appDetail.chatConfig,
t t
}); });
const renderTypeData = menuList.find((item) => item.renderType === updateItem.renderType); const renderTypeData = menuList.find((item) => item.renderType === updateItem.renderType);
const handleUpdate = (newValue: ReferenceValueProps | string) => { const handleUpdate = (newValue: ReferenceValueProps | string) => {
if (isReferenceValue(newValue)) { if (isReferenceValue(newValue)) {
@@ -199,12 +211,15 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
} }
if (valueType === WorkflowIOValueTypeEnum.string) { if (valueType === WorkflowIOValueTypeEnum.string) {
return ( return (
<Textarea <Box w={'300px'}>
bg="white" <PromptEditor
value={updateItem.value?.[1] || ''} value={updateItem.value?.[1] || ''}
w="300px" onChange={handleUpdate}
onChange={(e) => handleUpdate(e.target.value)} showOpenModal={false}
/> variableLabels={variables}
h={100}
/>
</Box>
); );
} }
if (valueType === WorkflowIOValueTypeEnum.number) { 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 ( return (
<NodeCard selected={selected} maxW={'1000px'} {...data}> <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 PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context'; import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
import { computedNodeInputReference } from '@/web/core/workflow/utils';
import { useCreation } from 'ahooks'; import { useCreation } from 'ahooks';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from '@/pages/app/detail/components/context';
import { getEditorVariables } from '../../../../../utils';
const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => { const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const edges = useContextSelector(WorkflowContext, (v) => v.edges); const edges = useContextSelector(WorkflowContext, (v) => v.edges);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const getNodeDynamicInputs = useContextSelector(WorkflowContext, (v) => v.getNodeDynamicInputs);
const { appDetail } = useContextSelector(AppContext, (v) => v); const { appDetail } = useContextSelector(AppContext, (v) => v);
// get variable // get variable
const variables = useCreation(() => { const variables = useCreation(() => {
const currentNode = nodeList.find((node) => node.nodeId === nodeId)!; return getEditorVariables({
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({
nodeId, nodeId,
nodes: nodeList, nodeList,
edges: edges, edges,
chatConfig: appDetail.chatConfig, appDetail,
t 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]); }, [nodeList, edges, inputs, t]);
const onChange = useCallback( const onChange = useCallback(

View File

@@ -188,6 +188,7 @@ type DebugDataType = {
runtimeNodes: RuntimeNodeItemType[]; runtimeNodes: RuntimeNodeItemType[];
runtimeEdges: RuntimeEdgeItemType[]; runtimeEdges: RuntimeEdgeItemType[];
nextRunNodes: RuntimeNodeItemType[]; nextRunNodes: RuntimeNodeItemType[];
variables: Record<string, any>;
}; };
export const WorkflowContext = createContext<WorkflowContextType>({ export const WorkflowContext = createContext<WorkflowContextType>({
@@ -676,13 +677,14 @@ const WorkflowContextProvider = ({
try { try {
// 4. Run one step // 4. Run one step
const { finishedEdges, finishedNodes, nextStepRunNodes, flowResponses } = const { finishedEdges, finishedNodes, nextStepRunNodes, flowResponses, newVariables } =
await postWorkflowDebug({ await postWorkflowDebug({
nodes: runtimeNodes, nodes: runtimeNodes,
edges: debugData.runtimeEdges, edges: debugData.runtimeEdges,
variables: { variables: {
appId, appId,
cTime: formatTime2YMDHMW() cTime: formatTime2YMDHMW(),
...debugData.variables
}, },
appId appId
}); });
@@ -703,7 +705,8 @@ const WorkflowContextProvider = ({
status status
}; };
}), }),
nextRunNodes: nextStepRunNodes nextRunNodes: nextStepRunNodes,
variables: newVariables
}; };
setWorkflowDebugData(newStoreDebugData); setWorkflowDebugData(newStoreDebugData);
@@ -794,7 +797,8 @@ const WorkflowContextProvider = ({
const data = { const data = {
runtimeNodes, runtimeNodes,
runtimeEdges, runtimeEdges,
nextRunNodes: runtimeNodes.filter((node) => node.nodeId === entryNodeId) nextRunNodes: runtimeNodes.filter((node) => node.nodeId === entryNodeId),
variables: {}
}; };
onStopNodeDebug(); onStopNodeDebug();
setWorkflowDebugData(data); 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 { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { FlowNodeItemType, StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; import { FlowNodeItemType, StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
import { TFunction } from 'i18next';
import { type Node, type Edge } from 'reactflow'; import { type Node, type Edge } from 'reactflow';
export const uiWorkflow2StoreWorkflow = ({ export const uiWorkflow2StoreWorkflow = ({
@@ -70,3 +73,60 @@ export const filterExportModules = (modules: StoreNodeItemType[]) => {
export default function Dom() { export default function Dom() {
return <></>; 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];
};