feat: 重构交互式调试逻辑,创建共用 Hook 以简化用户选择和输入处理

This commit is contained in:
sd0ric4
2025-03-27 12:01:14 +08:00
parent 8c086979da
commit 00d8188c77

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Box, Button, Flex, Textarea, FormLabel as ChakraFormLabel } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { Controller, useForm } from 'react-hook-form';
@@ -18,65 +18,30 @@ import { WorkflowContext } from '@/pageComponents/app/detail/WorkflowComponents/
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { StoreEdgeItemType, RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { initWorkflowEdgeStatus } from '@fastgpt/global/core/workflow/runtime/utils';
export const RenderUserSelectInteractive = React.memo(function RenderInteractive({
interactive,
nodeId
}: {
interactive: UserSelectInteractive;
nodeId?: string;
}) {
const { t } = useTranslation();
// 在两个组件中更新上下文选择器,添加 onStartNodeDebug
// 创建共用的交互式调试 Hook
const useInteractiveDebug = (
interactive: UserSelectInteractive | UserInputInteractive,
nodeId?: string
) => {
const { onStartNodeDebug, workflowDebugData } = useContextSelector(WorkflowContext, (v) => ({
onStartNodeDebug: v.onStartNodeDebug,
// onNextNodeDebug: v.onNextNodeDebug, // 不再使用
workflowDebugData: v.workflowDebugData
}));
/**
* 创建交互数据结构
* @param nodeId 交互节点ID
* @param interactive 交互组件数据
* @param edges 当前的边数组
* @returns 交互数据结构
*/
const createInteractiveData = (
nodeId: string,
interactive: UserSelectInteractive | UserInputInteractive,
edges: StoreEdgeItemType[]
) => {
// 创建 memoryEdges - 指向交互节点的边设为 active
const memoryEdges: RuntimeEdgeItemType[] = edges.map((edge) => ({
...edge,
status: edge.target === nodeId ? ('active' as const) : ('waiting' as const)
}));
// 处理交互数据结构
const interactiveData = useMemo(() => {
return {
...interactive,
entryNodeIds: [nodeId],
memoryEdges,
nodeOutputs: [] // 如果有需要可以填充节点输出数据
memoryEdges: interactive?.memoryEdges || [],
entryNodeIds: interactive?.entryNodeIds || [],
nodeOutputs: interactive?.nodeOutputs || []
};
};
/**
* 创建模拟的历史记录
* @param nodeId 交互节点ID
* @param interactive 交互组件数据
* @param edges 当前的边数组
* @returns 模拟的历史记录
*/
const createMockHistory = (
nodeId: string,
interactive: UserSelectInteractive | UserInputInteractive,
edges: StoreEdgeItemType[]
): ChatItemType[] => {
const interactiveData = createInteractiveData(nodeId, interactive, edges);
}, [interactive]);
// 创建模拟的历史记录
const createMockHistory = useCallback((): ChatItemType[] => {
return [
{
obj: ChatRoleEnum.AI,
@@ -88,57 +53,31 @@ export const RenderUserSelectInteractive = React.memo(function RenderInteractive
]
}
];
};
}, [interactiveData]);
// 合并选择和下一步操作
const handleSelectAndNext = useCallback(
(value: string) => {
// 启动调试的通用函数
const startDebug = useCallback(
(userContent: string, nodeUpdater: (node: any) => any) => {
if (!nodeId || !workflowDebugData) return;
// 创建包含用户选择的查询数据
// 创建包含用户交互的查询数据
const updatedQuery: UserChatItemValueItemType[] = [
...(workflowDebugData.query || []),
{
type: ChatItemValueTypeEnum.text,
text: {
content: value || ''
}
} as UserChatItemValueItemType
text: { content: userContent }
}
];
// 创建模拟的历史记录
const mockHistory = createMockHistory(nodeId, interactive, workflowDebugData.runtimeEdges);
// 使用模拟的历史记录初始化边状态
const mockHistory = createMockHistory();
const updatedRuntimeEdges = initWorkflowEdgeStatus(
workflowDebugData.runtimeEdges,
mockHistory
);
const updatedRuntimeNodes = workflowDebugData.runtimeNodes.map((node) =>
node.nodeId === nodeId ? nodeUpdater(node) : node
);
// 更新 runtimeNodes 以反映用户的选择
const updatedRuntimeNodes = workflowDebugData.runtimeNodes.map((node) => {
if (node.nodeId === nodeId) {
// 找到我们需要的输入字段并更新它
return {
...node,
inputs: node.inputs.map((input) => {
// 根据您的实际字段结构,这里可能需要调整
if (input.key === 'userSelect' || input.key === 'selectedOption') {
return {
...input,
value: value
};
}
return input;
}),
// 添加或更新任何需要的节点属性
userSelectedVal: value
};
}
return node;
});
// 使用 onStartNodeDebug 替代 onNextNodeDebug带上更新后的 nodes 和 edges
onStartNodeDebug({
entryNodeId: nodeId,
runtimeNodes: updatedRuntimeNodes,
@@ -148,7 +87,35 @@ export const RenderUserSelectInteractive = React.memo(function RenderInteractive
history: mockHistory
});
},
[nodeId, workflowDebugData, onStartNodeDebug, interactive]
[nodeId, workflowDebugData, onStartNodeDebug, createMockHistory]
);
return { workflowDebugData, interactiveData, startDebug };
};
export const RenderUserSelectInteractive = React.memo(function RenderInteractive({
interactive,
nodeId
}: {
interactive: UserSelectInteractive;
nodeId?: string;
}) {
const { startDebug } = useInteractiveDebug(interactive, nodeId);
const handleSelectAndNext = useCallback(
(value: string) => {
startDebug(value || '', (node) => ({
...node,
inputs: node.inputs.map((input: { key: string }) => {
if (input.key === 'userSelect' || input.key === 'selectedOption') {
return { ...input, value };
}
return input;
}),
userSelectedVal: value
}));
},
[startDebug]
);
return (
@@ -208,6 +175,7 @@ export const RenderUserSelectInteractive = React.memo(function RenderInteractive
</Box>
);
});
export const RenderUserFormInteractive = React.memo(function RenderFormInput({
interactive,
nodeId
@@ -225,145 +193,29 @@ export const RenderUserFormInteractive = React.memo(function RenderFormInput({
getValues
} = useForm();
const [isSubmitted, setIsSubmitted] = useState(false);
// 在两个组件中更新上下文选择器,添加 onStartNodeDebug
const { onStartNodeDebug, workflowDebugData } = useContextSelector(WorkflowContext, (v) => ({
onStartNodeDebug: v.onStartNodeDebug,
workflowDebugData: v.workflowDebugData
}));
/**
* 创建交互数据结构
* @param nodeId 交互节点ID
* @param interactive 交互组件数据
* @param edges 当前的边数组
* @returns 交互数据结构
*/
const createInteractiveData = (
nodeId: string,
interactive: UserInputInteractive,
edges: StoreEdgeItemType[]
) => {
// 创建 memoryEdges - 指向交互节点的边设为 active
const memoryEdges: RuntimeEdgeItemType[] = edges.map((edge) => ({
...edge,
status: edge.target === nodeId ? ('active' as const) : ('waiting' as const)
}));
return {
...interactive,
entryNodeIds: [nodeId],
memoryEdges,
nodeOutputs: [] // 如果有需要可以填充节点输出数据
};
};
/**
* 创建模拟的历史记录
* @param nodeId 交互节点ID
* @param interactive 交互组件数据
* @param edges 当前的边数组
* @returns 模拟的历史记录
*/
const createMockHistory = (
nodeId: string,
interactive: UserInputInteractive,
edges: StoreEdgeItemType[]
): ChatItemType[] => {
// 创建一个新的 interactive 对象,确保 submitted 为 false
const adjustedInteractive = {
...interactive,
params: {
...interactive.params,
submitted: false // 关键修改点:确保 submitted 为 false
}
};
const interactiveData = createInteractiveData(nodeId, adjustedInteractive, edges);
return [
{
obj: ChatRoleEnum.AI,
value: [
{
type: ChatItemValueTypeEnum.interactive,
interactive: interactiveData
}
]
}
];
};
const { startDebug } = useInteractiveDebug(interactive, nodeId);
const onSubmit = useCallback(
(data: any) => {
if (!nodeId || !workflowDebugData) return;
if (!nodeId) return;
setIsSubmitted(true);
// 直接调用 handleStartDebug合并提交和下一步操作
const formData = getValues();
const updatedQuery: UserChatItemValueItemType[] = [
...(workflowDebugData.query || []),
{
type: ChatItemValueTypeEnum.text,
text: {
content: JSON.stringify(formData)
startDebug(JSON.stringify(formData), (node) => ({
...node,
inputs: node.inputs.map((input: { key: string }) => {
const formField = interactive.params.inputForm?.find(
(field) => field.label === input.key || field.key === input.key
);
if (formField) {
return { ...input, value: formData[formField.label] };
}
} as UserChatItemValueItemType
];
const updatedInteractive = {
...interactive,
params: {
...interactive.params,
submitted: true
}
};
const mockHistory = createMockHistory(
nodeId,
updatedInteractive,
workflowDebugData.runtimeEdges
);
const updatedRuntimeEdges = initWorkflowEdgeStatus(
workflowDebugData.runtimeEdges,
mockHistory
);
const updatedRuntimeNodes = workflowDebugData.runtimeNodes.map((node) => {
if (node.nodeId === nodeId) {
return {
...node,
inputs: node.inputs.map((input) => {
const formField = interactive.params.inputForm?.find(
(field) => field.label === input.key || field.key === input.key
);
if (formField) {
return {
...input,
value: formData[formField.label]
};
}
return input;
}),
formSubmitted: true
};
}
return node;
});
onStartNodeDebug({
entryNodeId: nodeId,
runtimeNodes: updatedRuntimeNodes,
runtimeEdges: updatedRuntimeEdges,
variables: workflowDebugData.variables,
query: updatedQuery,
history: mockHistory
});
return input;
}),
formSubmitted: true
}));
},
[nodeId, workflowDebugData, onStartNodeDebug, getValues, interactive]
[nodeId, getValues, startDebug, interactive.params.inputForm]
);
useEffect(() => {
@@ -382,7 +234,7 @@ export const RenderUserFormInteractive = React.memo(function RenderFormInput({
if (interactive.params.submitted) {
setIsSubmitted(true);
}
}, [interactive, reset, nodeId]);
}, [interactive, reset]);
return (
<Box px={4} py={4} bg="white" borderRadius="md">