feat: 重构交互式调试逻辑,创建共用 Hook 以简化用户选择和输入处理
This commit is contained in:
@@ -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 { Box, Button, Flex, Textarea, FormLabel as ChakraFormLabel } from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
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 { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
import { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
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';
|
import { initWorkflowEdgeStatus } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
|
|
||||||
export const RenderUserSelectInteractive = React.memo(function RenderInteractive({
|
// 创建共用的交互式调试 Hook
|
||||||
interactive,
|
const useInteractiveDebug = (
|
||||||
nodeId
|
interactive: UserSelectInteractive | UserInputInteractive,
|
||||||
}: {
|
nodeId?: string
|
||||||
interactive: UserSelectInteractive;
|
) => {
|
||||||
nodeId?: string;
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
// 在两个组件中更新上下文选择器,添加 onStartNodeDebug
|
|
||||||
const { onStartNodeDebug, workflowDebugData } = useContextSelector(WorkflowContext, (v) => ({
|
const { onStartNodeDebug, workflowDebugData } = useContextSelector(WorkflowContext, (v) => ({
|
||||||
onStartNodeDebug: v.onStartNodeDebug,
|
onStartNodeDebug: v.onStartNodeDebug,
|
||||||
// onNextNodeDebug: v.onNextNodeDebug, // 不再使用
|
|
||||||
workflowDebugData: v.workflowDebugData
|
workflowDebugData: v.workflowDebugData
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
// 处理交互数据结构
|
||||||
* 创建交互数据结构
|
const interactiveData = useMemo(() => {
|
||||||
* @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)
|
|
||||||
}));
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...interactive,
|
...interactive,
|
||||||
entryNodeIds: [nodeId],
|
memoryEdges: interactive?.memoryEdges || [],
|
||||||
memoryEdges,
|
entryNodeIds: interactive?.entryNodeIds || [],
|
||||||
nodeOutputs: [] // 如果有需要可以填充节点输出数据
|
nodeOutputs: interactive?.nodeOutputs || []
|
||||||
};
|
};
|
||||||
};
|
}, [interactive]);
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建模拟的历史记录
|
|
||||||
* @param nodeId 交互节点ID
|
|
||||||
* @param interactive 交互组件数据
|
|
||||||
* @param edges 当前的边数组
|
|
||||||
* @returns 模拟的历史记录
|
|
||||||
*/
|
|
||||||
const createMockHistory = (
|
|
||||||
nodeId: string,
|
|
||||||
interactive: UserSelectInteractive | UserInputInteractive,
|
|
||||||
edges: StoreEdgeItemType[]
|
|
||||||
): ChatItemType[] => {
|
|
||||||
const interactiveData = createInteractiveData(nodeId, interactive, edges);
|
|
||||||
|
|
||||||
|
// 创建模拟的历史记录
|
||||||
|
const createMockHistory = useCallback((): ChatItemType[] => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
obj: ChatRoleEnum.AI,
|
obj: ChatRoleEnum.AI,
|
||||||
@@ -88,57 +53,31 @@ export const RenderUserSelectInteractive = React.memo(function RenderInteractive
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
}, [interactiveData]);
|
||||||
|
|
||||||
// 合并选择和下一步操作
|
// 启动调试的通用函数
|
||||||
const handleSelectAndNext = useCallback(
|
const startDebug = useCallback(
|
||||||
(value: string) => {
|
(userContent: string, nodeUpdater: (node: any) => any) => {
|
||||||
if (!nodeId || !workflowDebugData) return;
|
if (!nodeId || !workflowDebugData) return;
|
||||||
|
|
||||||
// 创建包含用户选择的查询数据
|
// 创建包含用户交互的查询数据
|
||||||
const updatedQuery: UserChatItemValueItemType[] = [
|
const updatedQuery: UserChatItemValueItemType[] = [
|
||||||
...(workflowDebugData.query || []),
|
...(workflowDebugData.query || []),
|
||||||
{
|
{
|
||||||
type: ChatItemValueTypeEnum.text,
|
type: ChatItemValueTypeEnum.text,
|
||||||
text: {
|
text: { content: userContent }
|
||||||
content: value || ''
|
}
|
||||||
}
|
|
||||||
} as UserChatItemValueItemType
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// 创建模拟的历史记录
|
const mockHistory = createMockHistory();
|
||||||
const mockHistory = createMockHistory(nodeId, interactive, workflowDebugData.runtimeEdges);
|
|
||||||
|
|
||||||
// 使用模拟的历史记录初始化边状态
|
|
||||||
const updatedRuntimeEdges = initWorkflowEdgeStatus(
|
const updatedRuntimeEdges = initWorkflowEdgeStatus(
|
||||||
workflowDebugData.runtimeEdges,
|
workflowDebugData.runtimeEdges,
|
||||||
mockHistory
|
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({
|
onStartNodeDebug({
|
||||||
entryNodeId: nodeId,
|
entryNodeId: nodeId,
|
||||||
runtimeNodes: updatedRuntimeNodes,
|
runtimeNodes: updatedRuntimeNodes,
|
||||||
@@ -148,7 +87,35 @@ export const RenderUserSelectInteractive = React.memo(function RenderInteractive
|
|||||||
history: mockHistory
|
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 (
|
return (
|
||||||
@@ -208,6 +175,7 @@ export const RenderUserSelectInteractive = React.memo(function RenderInteractive
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
export const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
||||||
interactive,
|
interactive,
|
||||||
nodeId
|
nodeId
|
||||||
@@ -225,145 +193,29 @@ export const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
|||||||
getValues
|
getValues
|
||||||
} = useForm();
|
} = useForm();
|
||||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||||
// 在两个组件中更新上下文选择器,添加 onStartNodeDebug
|
const { startDebug } = useInteractiveDebug(interactive, nodeId);
|
||||||
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 onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
(data: any) => {
|
(data: any) => {
|
||||||
if (!nodeId || !workflowDebugData) return;
|
if (!nodeId) return;
|
||||||
|
|
||||||
setIsSubmitted(true);
|
setIsSubmitted(true);
|
||||||
|
|
||||||
// 直接调用 handleStartDebug,合并提交和下一步操作
|
|
||||||
const formData = getValues();
|
const formData = getValues();
|
||||||
|
startDebug(JSON.stringify(formData), (node) => ({
|
||||||
const updatedQuery: UserChatItemValueItemType[] = [
|
...node,
|
||||||
...(workflowDebugData.query || []),
|
inputs: node.inputs.map((input: { key: string }) => {
|
||||||
{
|
const formField = interactive.params.inputForm?.find(
|
||||||
type: ChatItemValueTypeEnum.text,
|
(field) => field.label === input.key || field.key === input.key
|
||||||
text: {
|
);
|
||||||
content: JSON.stringify(formData)
|
if (formField) {
|
||||||
|
return { ...input, value: formData[formField.label] };
|
||||||
}
|
}
|
||||||
} as UserChatItemValueItemType
|
return input;
|
||||||
];
|
}),
|
||||||
|
formSubmitted: true
|
||||||
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
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[nodeId, workflowDebugData, onStartNodeDebug, getValues, interactive]
|
[nodeId, getValues, startDebug, interactive.params.inputForm]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -382,7 +234,7 @@ export const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
|||||||
if (interactive.params.submitted) {
|
if (interactive.params.submitted) {
|
||||||
setIsSubmitted(true);
|
setIsSubmitted(true);
|
||||||
}
|
}
|
||||||
}, [interactive, reset, nodeId]);
|
}, [interactive, reset]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={4} py={4} bg="white" borderRadius="md">
|
<Box px={4} py={4} bg="white" borderRadius="md">
|
||||||
|
|||||||
Reference in New Issue
Block a user