feat: after deleting the edge, set the corresponding field in the node's form field to undefined #918 (#1393)

### What problem does this PR solve?

feat: after deleting the edge, set the corresponding field in the node's
form field to undefined #918

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-07-05 19:08:00 +08:00 committed by GitHub
parent d57a68bc2a
commit de610091eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 61 additions and 13 deletions

View File

@ -573,9 +573,8 @@ The above is the content you need to summarize.`,
relevantDescription: `This component is used to judge if the output of upstream is relevent to user's latest question. 'Yes' represents that they're relevant. 'No' represents they're irrelevant.`, relevantDescription: `This component is used to judge if the output of upstream is relevent to user's latest question. 'Yes' represents that they're relevant. 'No' represents they're irrelevant.`,
rewriteQuestionDescription: `This component is used to refine user's quesion. Typically, when a user's original question can't retrieve relevant information from knowledge base, this component help you change the question into a proper one which might be more consistant with the expressions in knowledge base. Only 'Retrieval' can be its downstreams.`, rewriteQuestionDescription: `This component is used to refine user's quesion. Typically, when a user's original question can't retrieve relevant information from knowledge base, this component help you change the question into a proper one which might be more consistant with the expressions in knowledge base. Only 'Retrieval' can be its downstreams.`,
messageDescription: messageDescription:
'This component is used to send user static information.', 'This component is used to send user static information. You can prepare several messages which will be chosen randomly.',
keywordDescription: keywordDescription: `This component is used to extract keywords from user's question. Top N specifies the number of keywords you need to extract.`,
'This component is used to send user static information.',
promptText: `Please summarize the following paragraphs. Be careful with the numbers, do not make things up. Paragraphs as following: promptText: `Please summarize the following paragraphs. Be careful with the numbers, do not make things up. Paragraphs as following:
{input} {input}
The above is the content you need to summarize.`, The above is the content you need to summarize.`,

View File

@ -537,6 +537,27 @@ export default {
messageMsg: '请输入消息或删除此字段。', messageMsg: '请输入消息或删除此字段。',
addField: '新增字段', addField: '新增字段',
loop: '环', loop: '环',
createFlow: 'Create a workflow',
yes: 'Yes',
no: 'No',
key: 'key',
componentId: 'component id',
add: 'Add',
operation: 'operation',
beginDescription: 'This is where the flow begin',
answerDescription: `This component is used as an interface between bot and human. It receives input of user and display the result of the computation of the bot.`,
retrievalDescription: `This component is for the process of retrieving relevent information from knowledge base. So, knowledgebases should be selected. If there's nothing retrieved, the 'Empty response' will be returned.`,
generateDescription: `This component is used to call LLM to generate text. Be careful about the prompt setting.`,
categorizeDescription: `This component is used to categorize text. Please specify the name, description and examples of the category. Every single category leads to different downstream components.`,
relevantDescription: `This component is used to judge if the output of upstream is relevent to user's latest question. 'Yes' represents that they're relevant. 'No' represents they're irrelevant.`,
rewriteQuestionDescription: `This component is used to refine user's quesion. Typically, when a user's original question can't retrieve relevant information from knowledge base, this component help you change the question into a proper one which might be more consistant with the expressions in knowledge base. Only 'Retrieval' can be its downstreams.`,
messageDescription:
'This component is used to send user static information.',
keywordDescription:
'This component is used to send user static information.',
promptText: `Please summarize the following paragraphs. Be careful with the numbers, do not make things up. Paragraphs as following:
{input}
The above is the content you need to summarize.`,
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

View File

@ -2,6 +2,7 @@ import { useTranslate } from '@/hooks/commonHooks';
import { Card, Divider, Flex, Layout, Tooltip } from 'antd'; import { Card, Divider, Flex, Layout, Tooltip } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import lowerFirst from 'lodash/lowerFirst'; import lowerFirst from 'lodash/lowerFirst';
import React from 'react';
import { Operator, componentMenuList } from '../constant'; import { Operator, componentMenuList } from '../constant';
import { useHandleDrag } from '../hooks'; import { useHandleDrag } from '../hooks';
import OperatorIcon from '../operator-icon'; import OperatorIcon from '../operator-icon';
@ -29,7 +30,7 @@ const FlowSide = ({ setCollapsed, collapsed }: IProps) => {
<Flex vertical gap={10} className={styles.siderContent}> <Flex vertical gap={10} className={styles.siderContent}>
{componentMenuList.map((x) => { {componentMenuList.map((x) => {
return ( return (
<> <React.Fragment key={x.name}>
{x.name === Operator.DuckDuckGo && ( {x.name === Operator.DuckDuckGo && (
<Divider <Divider
style={{ style={{
@ -57,7 +58,7 @@ const FlowSide = ({ setCollapsed, collapsed }: IProps) => {
</section> </section>
</Flex> </Flex>
</Card> </Card>
</> </React.Fragment>
); );
})} })}
</Flex> </Flex>

View File

@ -1,3 +1,4 @@
import pick from 'lodash/pick';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect } from 'react';
import { Edge } from 'reactflow'; import { Edge } from 'reactflow';
import { IOperatorForm } from '../interface'; import { IOperatorForm } from '../interface';
@ -30,6 +31,14 @@ const getTargetOfEdge = (edges: Edge[], sourceHandle: string) =>
*/ */
export const useWatchConnectionChanges = ({ nodeId, form }: IOperatorForm) => { export const useWatchConnectionChanges = ({ nodeId, form }: IOperatorForm) => {
const edges = useGraphStore((state) => state.edges); const edges = useGraphStore((state) => state.edges);
const getNode = useGraphStore((state) => state.getNode);
const node = getNode(nodeId);
const watchFormChanges = useCallback(() => {
if (node) {
form?.setFieldsValue(pick(node, ['yes', 'no']));
}
}, [node, form]);
const watchConnectionChanges = useCallback(() => { const watchConnectionChanges = useCallback(() => {
const edgeList = edges.filter((x) => x.source === nodeId); const edgeList = edges.filter((x) => x.source === nodeId);
@ -39,6 +48,6 @@ export const useWatchConnectionChanges = ({ nodeId, form }: IOperatorForm) => {
}, [edges, nodeId, form]); }, [edges, nodeId, form]);
useEffect(() => { useEffect(() => {
watchConnectionChanges(); watchFormChanges();
}, [watchConnectionChanges]); }, [watchFormChanges]);
}; };

View File

@ -38,6 +38,7 @@ export type RFState = {
getNode: (id?: string | null) => Node<NodeData> | undefined; getNode: (id?: string | null) => Node<NodeData> | undefined;
addEdge: (connection: Connection) => void; addEdge: (connection: Connection) => void;
getEdge: (id: string) => Edge | undefined; getEdge: (id: string) => Edge | undefined;
updateFormDataOnConnect: (connection: Connection) => void;
deletePreviousEdgeOfClassificationNode: (connection: Connection) => void; deletePreviousEdgeOfClassificationNode: (connection: Connection) => void;
duplicateNode: (id: string) => void; duplicateNode: (id: string) => void;
deleteEdge: () => void; deleteEdge: () => void;
@ -71,10 +72,15 @@ const useGraphStore = create<RFState>()(
}); });
}, },
onConnect: (connection: Connection) => { onConnect: (connection: Connection) => {
const {
deletePreviousEdgeOfClassificationNode,
updateFormDataOnConnect,
} = get();
set({ set({
edges: addEdge(connection, get().edges), edges: addEdge(connection, get().edges),
}); });
get().deletePreviousEdgeOfClassificationNode(connection); deletePreviousEdgeOfClassificationNode(connection);
updateFormDataOnConnect(connection);
}, },
onSelectionChange: ({ nodes, edges }: OnSelectionChangeParams) => { onSelectionChange: ({ nodes, edges }: OnSelectionChangeParams) => {
set({ set({
@ -106,9 +112,16 @@ const useGraphStore = create<RFState>()(
getEdge: (id: string) => { getEdge: (id: string) => {
return get().edges.find((x) => x.id === id); return get().edges.find((x) => x.id === id);
}, },
updateFormDataOnConnect: (connection: Connection) => {
const { getOperatorTypeFromId, updateNodeForm } = get();
const { source, target, sourceHandle } = connection;
if (source && getOperatorTypeFromId(source) === Operator.Relevant) {
updateNodeForm(source, { [sourceHandle as string]: target });
}
},
deletePreviousEdgeOfClassificationNode: (connection: Connection) => { deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
// Delete the edge on the classification node or relevant node anchor when the anchor is connected to other nodes // Delete the edge on the classification node or relevant node anchor when the anchor is connected to other nodes
const { edges, getOperatorTypeFromId } = get(); const { edges, getOperatorTypeFromId, deleteEdgeById } = get();
// the node containing the anchor // the node containing the anchor
const anchoredNodes = [Operator.Categorize, Operator.Relevant]; const anchoredNodes = [Operator.Categorize, Operator.Relevant];
if ( if (
@ -123,9 +136,7 @@ const useGraphStore = create<RFState>()(
x.target !== connection.target, x.target !== connection.target,
); );
if (previousEdge) { if (previousEdge) {
set({ deleteEdgeById(previousEdge.id);
edges: edges.filter((edge) => edge !== previousEdge),
});
} }
} }
}, },
@ -155,7 +166,14 @@ const useGraphStore = create<RFState>()(
}); });
}, },
deleteEdgeById: (id: string) => { deleteEdgeById: (id: string) => {
const { edges } = get(); const { edges, updateNodeForm } = get();
const currentEdge = edges.find((x) => x.id === id);
if (currentEdge) {
// After deleting the edge, set the corresponding field in the node's form field to undefined
updateNodeForm(currentEdge.source, {
[currentEdge.sourceHandle as string]: undefined,
});
}
set({ set({
edges: edges.filter((edge) => edge.id !== id), edges: edges.filter((edge) => edge.id !== id),
}); });