Feat: Outputs data is directly synchronized to the canvas without going through the form. #10427 (#11406)

### What problem does this PR solve?

Feat: Outputs data is directly synchronized to the canvas without going
through the form. #10427

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-11-20 15:35:28 +08:00
committed by GitHub
parent b846a0f547
commit 0d5589bfda
4 changed files with 43 additions and 29 deletions

View File

@ -22,7 +22,8 @@ import { Switch } from '@/components/ui/switch';
import { LlmModelType } from '@/constants/knowledge'; import { LlmModelType } from '@/constants/knowledge';
import { useFindLlmByUuid } from '@/hooks/use-llm-request'; import { useFindLlmByUuid } from '@/hooks/use-llm-request';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { memo, useCallback, useEffect, useMemo } from 'react'; import { get } from 'lodash';
import { memo, useEffect, useMemo } from 'react';
import { useForm, useWatch } from 'react-hook-form'; import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
@ -45,7 +46,10 @@ import { AgentTools, Agents } from './agent-tools';
import { StructuredOutputDialog } from './structured-output-dialog'; import { StructuredOutputDialog } from './structured-output-dialog';
import { StructuredOutputPanel } from './structured-output-panel'; import { StructuredOutputPanel } from './structured-output-panel';
import { useBuildPromptExtraPromptOptions } from './use-build-prompt-options'; import { useBuildPromptExtraPromptOptions } from './use-build-prompt-options';
import { useShowStructuredOutputDialog } from './use-show-structured-output-dialog'; import {
useHandleShowStructuredOutput,
useShowStructuredOutputDialog,
} from './use-show-structured-output-dialog';
import { useValues } from './use-values'; import { useValues } from './use-values';
import { useWatchFormChange } from './use-watch-change'; import { useWatchFormChange } from './use-watch-change';
@ -121,22 +125,19 @@ function AgentForm({ node }: INextOperatorForm) {
}); });
const { const {
initialStructuredOutput,
showStructuredOutputDialog, showStructuredOutputDialog,
structuredOutputDialogVisible, structuredOutputDialogVisible,
hideStructuredOutputDialog, hideStructuredOutputDialog,
handleStructuredOutputDialogOk, handleStructuredOutputDialogOk,
} = useShowStructuredOutputDialog(node?.id); } = useShowStructuredOutputDialog(node?.id);
const updateNodeForm = useGraphStore((state) => state.updateNodeForm); const structuredOutput = get(
node,
`data.form.outputs.${AgentStructuredOutputField}`,
);
const handleShowStructuredOutput = useCallback( const { handleShowStructuredOutput } = useHandleShowStructuredOutput(
(val: boolean) => { node?.id,
if (node?.id && val) {
updateNodeForm(node?.id, {}, ['outputs', AgentStructuredOutputField]);
}
},
[node?.id, updateNodeForm],
); );
useEffect(() => { useEffect(() => {
@ -327,7 +328,7 @@ function AgentForm({ node }: INextOperatorForm) {
</div> </div>
<StructuredOutputPanel <StructuredOutputPanel
value={initialStructuredOutput} value={structuredOutput}
></StructuredOutputPanel> ></StructuredOutputPanel>
</section> </section>
)} )}
@ -337,7 +338,7 @@ function AgentForm({ node }: INextOperatorForm) {
<StructuredOutputDialog <StructuredOutputDialog
hideModal={hideStructuredOutputDialog} hideModal={hideStructuredOutputDialog}
onOk={handleStructuredOutputDialogOk} onOk={handleStructuredOutputDialogOk}
initialValues={initialStructuredOutput} initialValues={structuredOutput}
></StructuredOutputDialog> ></StructuredOutputDialog>
)} )}
</> </>

View File

@ -1,6 +1,8 @@
import { JSONSchema } from '@/components/jsonjoy-builder'; import { JSONSchema } from '@/components/jsonjoy-builder';
import { AgentStructuredOutputField } from '@/constants/agent';
import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { initialAgentValues } from '../../constant';
import useGraphStore from '../../store'; import useGraphStore from '../../store';
export function useShowStructuredOutputDialog(nodeId?: string) { export function useShowStructuredOutputDialog(nodeId?: string) {
@ -9,15 +11,13 @@ export function useShowStructuredOutputDialog(nodeId?: string) {
showModal: showStructuredOutputDialog, showModal: showStructuredOutputDialog,
hideModal: hideStructuredOutputDialog, hideModal: hideStructuredOutputDialog,
} = useSetModalState(); } = useSetModalState();
const { updateNodeForm, getNode } = useGraphStore((state) => state); const { updateNodeForm } = useGraphStore((state) => state);
const initialStructuredOutput = getNode(nodeId)?.data.form.outputs.structured;
const handleStructuredOutputDialogOk = useCallback( const handleStructuredOutputDialogOk = useCallback(
(values: JSONSchema) => { (values: JSONSchema) => {
// Sync data to canvas // Sync data to canvas
if (nodeId) { if (nodeId) {
updateNodeForm(nodeId, values, ['outputs', 'structured']); updateNodeForm(nodeId, values, ['outputs', AgentStructuredOutputField]);
} }
hideStructuredOutputDialog(); hideStructuredOutputDialog();
}, },
@ -25,10 +25,30 @@ export function useShowStructuredOutputDialog(nodeId?: string) {
); );
return { return {
initialStructuredOutput,
structuredOutputDialogVisible, structuredOutputDialogVisible,
showStructuredOutputDialog, showStructuredOutputDialog,
hideStructuredOutputDialog, hideStructuredOutputDialog,
handleStructuredOutputDialogOk, handleStructuredOutputDialogOk,
}; };
} }
export function useHandleShowStructuredOutput(nodeId?: string) {
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
const handleShowStructuredOutput = useCallback(
(val: boolean) => {
if (nodeId) {
if (val) {
updateNodeForm(nodeId, {}, ['outputs', AgentStructuredOutputField]);
} else {
updateNodeForm(nodeId, initialAgentValues.outputs, ['outputs']);
}
}
},
[nodeId, updateNodeForm],
);
return {
handleShowStructuredOutput,
};
}

View File

@ -6,8 +6,10 @@ import { initialAgentValues } from '../../constant';
// You need to exclude the mcp and tools fields that are not in the form, // You need to exclude the mcp and tools fields that are not in the form,
// otherwise the form data update will reset the tools or mcp data to an array // otherwise the form data update will reset the tools or mcp data to an array
// Exclude data that is not in the form to avoid writing this data to the canvas when using useWatch.
// Outputs, tools, and MCP data are directly synchronized to the canvas without going through the form.
function omitToolsAndMcp(values: Record<string, any>) { function omitToolsAndMcp(values: Record<string, any>) {
return omit(values, ['mcp', 'tools']); return omit(values, ['mcp', 'tools', 'outputs']);
} }
export function useValues(node?: RAGFlowNodeType) { export function useValues(node?: RAGFlowNodeType) {

View File

@ -1,7 +1,6 @@
import { omit } from 'lodash';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { UseFormReturn, useWatch } from 'react-hook-form'; import { UseFormReturn, useWatch } from 'react-hook-form';
import { AgentStructuredOutputField, PromptRole } from '../../constant'; import { PromptRole } from '../../constant';
import useGraphStore from '../../store'; import useGraphStore from '../../store';
export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) { export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
@ -17,14 +16,6 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
prompts: [{ role: PromptRole.User, content: values.prompts }], prompts: [{ role: PromptRole.User, content: values.prompts }],
}; };
if (!values.showStructuredOutput) {
nextValues = {
...nextValues,
outputs: omit(values.outputs, [AgentStructuredOutputField]),
};
} else {
nextValues = omit(nextValues, 'outputs');
}
updateNodeForm(id, nextValues); updateNodeForm(id, nextValues);
} }
}, [form?.formState.isDirty, id, updateNodeForm, values]); }, [form?.formState.isDirty, id, updateNodeForm, values]);