diff --git a/projects/app/src/components/core/chat/components/InteractiveComponents.tsx b/projects/app/src/components/core/chat/components/InteractiveComponents.tsx index 49b0d9483..ea0f538b8 100644 --- a/projects/app/src/components/core/chat/components/InteractiveComponents.tsx +++ b/projects/app/src/components/core/chat/components/InteractiveComponents.tsx @@ -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 ); }); + 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 (