### 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)tags/v0.8.0
| @@ -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.`, | |||
| 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.', | |||
| 'This component is used to send user static information. You can prepare several messages which will be chosen randomly.', | |||
| keywordDescription: `This component is used to extract keywords from user's question. Top N specifies the number of keywords you need to extract.`, | |||
| 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.`, | |||
| @@ -537,6 +537,27 @@ export default { | |||
| messageMsg: '请输入消息或删除此字段。', | |||
| addField: '新增字段', | |||
| 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: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -2,6 +2,7 @@ import { useTranslate } from '@/hooks/commonHooks'; | |||
| import { Card, Divider, Flex, Layout, Tooltip } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import lowerFirst from 'lodash/lowerFirst'; | |||
| import React from 'react'; | |||
| import { Operator, componentMenuList } from '../constant'; | |||
| import { useHandleDrag } from '../hooks'; | |||
| import OperatorIcon from '../operator-icon'; | |||
| @@ -29,7 +30,7 @@ const FlowSide = ({ setCollapsed, collapsed }: IProps) => { | |||
| <Flex vertical gap={10} className={styles.siderContent}> | |||
| {componentMenuList.map((x) => { | |||
| return ( | |||
| <> | |||
| <React.Fragment key={x.name}> | |||
| {x.name === Operator.DuckDuckGo && ( | |||
| <Divider | |||
| style={{ | |||
| @@ -57,7 +58,7 @@ const FlowSide = ({ setCollapsed, collapsed }: IProps) => { | |||
| </section> | |||
| </Flex> | |||
| </Card> | |||
| </> | |||
| </React.Fragment> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| @@ -1,3 +1,4 @@ | |||
| import pick from 'lodash/pick'; | |||
| import { useCallback, useEffect } from 'react'; | |||
| import { Edge } from 'reactflow'; | |||
| import { IOperatorForm } from '../interface'; | |||
| @@ -30,6 +31,14 @@ const getTargetOfEdge = (edges: Edge[], sourceHandle: string) => | |||
| */ | |||
| export const useWatchConnectionChanges = ({ nodeId, form }: IOperatorForm) => { | |||
| 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 edgeList = edges.filter((x) => x.source === nodeId); | |||
| @@ -39,6 +48,6 @@ export const useWatchConnectionChanges = ({ nodeId, form }: IOperatorForm) => { | |||
| }, [edges, nodeId, form]); | |||
| useEffect(() => { | |||
| watchConnectionChanges(); | |||
| }, [watchConnectionChanges]); | |||
| watchFormChanges(); | |||
| }, [watchFormChanges]); | |||
| }; | |||
| @@ -38,6 +38,7 @@ export type RFState = { | |||
| getNode: (id?: string | null) => Node<NodeData> | undefined; | |||
| addEdge: (connection: Connection) => void; | |||
| getEdge: (id: string) => Edge | undefined; | |||
| updateFormDataOnConnect: (connection: Connection) => void; | |||
| deletePreviousEdgeOfClassificationNode: (connection: Connection) => void; | |||
| duplicateNode: (id: string) => void; | |||
| deleteEdge: () => void; | |||
| @@ -71,10 +72,15 @@ const useGraphStore = create<RFState>()( | |||
| }); | |||
| }, | |||
| onConnect: (connection: Connection) => { | |||
| const { | |||
| deletePreviousEdgeOfClassificationNode, | |||
| updateFormDataOnConnect, | |||
| } = get(); | |||
| set({ | |||
| edges: addEdge(connection, get().edges), | |||
| }); | |||
| get().deletePreviousEdgeOfClassificationNode(connection); | |||
| deletePreviousEdgeOfClassificationNode(connection); | |||
| updateFormDataOnConnect(connection); | |||
| }, | |||
| onSelectionChange: ({ nodes, edges }: OnSelectionChangeParams) => { | |||
| set({ | |||
| @@ -106,9 +112,16 @@ const useGraphStore = create<RFState>()( | |||
| getEdge: (id: string) => { | |||
| 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) => { | |||
| // 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 | |||
| const anchoredNodes = [Operator.Categorize, Operator.Relevant]; | |||
| if ( | |||
| @@ -123,9 +136,7 @@ const useGraphStore = create<RFState>()( | |||
| x.target !== connection.target, | |||
| ); | |||
| if (previousEdge) { | |||
| set({ | |||
| edges: edges.filter((edge) => edge !== previousEdge), | |||
| }); | |||
| deleteEdgeById(previousEdge.id); | |||
| } | |||
| } | |||
| }, | |||
| @@ -155,7 +166,14 @@ const useGraphStore = create<RFState>()( | |||
| }); | |||
| }, | |||
| 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({ | |||
| edges: edges.filter((edge) => edge.id !== id), | |||
| }); | |||