balibabu commited on
Commit
e3b65ea
·
1 Parent(s): 1157a9f

feat: display the debugging results of each operator in a pop-up window #918 (#1445)

Browse files

### What problem does this PR solve?

feat: display the debugging results of each operator in a pop-up window
#918
### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/package-lock.json CHANGED
@@ -36,6 +36,7 @@
36
  "react-pdf-highlighter": "^6.1.0",
37
  "react-string-replace": "^1.1.1",
38
  "react-syntax-highlighter": "^15.5.0",
 
39
  "reactflow": "^11.11.2",
40
  "recharts": "^2.12.4",
41
  "remark-gfm": "^4.0.0",
@@ -5011,9 +5012,9 @@
5011
  "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
5012
  },
5013
  "node_modules/@types/lodash": {
5014
- "version": "4.14.202",
5015
- "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
5016
- "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
5017
  "dev": true
5018
  },
5019
  "node_modules/@types/mdast": {
@@ -22274,6 +22275,14 @@
22274
  "react-dom": ">=16.6.0"
22275
  }
22276
  },
 
 
 
 
 
 
 
 
22277
  "node_modules/reactcss": {
22278
  "version": "1.2.3",
22279
  "resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",
 
36
  "react-pdf-highlighter": "^6.1.0",
37
  "react-string-replace": "^1.1.1",
38
  "react-syntax-highlighter": "^15.5.0",
39
+ "react18-json-view": "^0.2.8",
40
  "reactflow": "^11.11.2",
41
  "recharts": "^2.12.4",
42
  "remark-gfm": "^4.0.0",
 
5012
  "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
5013
  },
5014
  "node_modules/@types/lodash": {
5015
+ "version": "4.17.6",
5016
+ "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.6.tgz",
5017
+ "integrity": "sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==",
5018
  "dev": true
5019
  },
5020
  "node_modules/@types/mdast": {
 
22275
  "react-dom": ">=16.6.0"
22276
  }
22277
  },
22278
+ "node_modules/react18-json-view": {
22279
+ "version": "0.2.8",
22280
+ "resolved": "https://registry.npmmirror.com/react18-json-view/-/react18-json-view-0.2.8.tgz",
22281
+ "integrity": "sha512-uJlcf5PEDaba6yTqfcDAcMSYECZ15SLcpP94mLFTa/+fa1kZANjERqKzS7YxxsrGP4+jDxt6sIaglR0PbQcKPw==",
22282
+ "peerDependencies": {
22283
+ "react": ">=16.8.0"
22284
+ }
22285
+ },
22286
  "node_modules/reactcss": {
22287
  "version": "1.2.3",
22288
  "resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",
web/package.json CHANGED
@@ -47,6 +47,7 @@
47
  "react-pdf-highlighter": "^6.1.0",
48
  "react-string-replace": "^1.1.1",
49
  "react-syntax-highlighter": "^15.5.0",
 
50
  "reactflow": "^11.11.2",
51
  "recharts": "^2.12.4",
52
  "remark-gfm": "^4.0.0",
 
47
  "react-pdf-highlighter": "^6.1.0",
48
  "react-string-replace": "^1.1.1",
49
  "react-syntax-highlighter": "^15.5.0",
50
+ "react18-json-view": "^0.2.8",
51
  "reactflow": "^11.11.2",
52
  "recharts": "^2.12.4",
53
  "remark-gfm": "^4.0.0",
web/src/pages/flow/canvas/node/categorize-node.tsx CHANGED
@@ -14,65 +14,67 @@ import OperatorIcon from '../../operator-icon';
14
  import CategorizeHandle from './categorize-handle';
15
  import NodeDropdown from './dropdown';
16
  import styles from './index.less';
 
17
 
18
  export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
19
  const categoryData = get(data, 'form.category_description') ?? {};
20
  const style = operatorMap[data.label as Operator];
21
  const { t } = useTranslate('flow');
22
  return (
23
- <section
24
- className={classNames(styles.ragNode, {
25
- [styles.selectedNode]: selected,
26
- })}
27
- style={{
28
- backgroundColor: style.backgroundColor,
29
- color: style.color,
30
- }}
31
- >
32
- <Handle
33
- type="target"
34
- position={Position.Left}
35
- isConnectable
36
- className={styles.handle}
37
- id={'a'}
38
- ></Handle>
39
- <Handle
40
- type="target"
41
- position={Position.Top}
42
- isConnectable
43
- className={styles.handle}
44
- id={'b'}
45
- ></Handle>
46
- <Handle
47
- type="target"
48
- position={Position.Bottom}
49
- isConnectable
50
- className={styles.handle}
51
- id={'c'}
52
- ></Handle>
53
- {Object.keys(categoryData).map((x, idx) => {
54
- console.info(categoryData, id, data);
55
- return (
56
- <CategorizeHandle
57
- top={CategorizeAnchorPointPositions[idx].top}
58
- right={CategorizeAnchorPointPositions[idx].right}
59
- key={idx}
60
- text={x}
61
- idx={idx}
62
- ></CategorizeHandle>
63
- );
64
- })}
65
- <Flex vertical align="center" justify="center" gap={6}>
66
- <OperatorIcon
67
- name={data.label as Operator}
68
- fontSize={24}
69
- ></OperatorIcon>
70
- <span className={styles.type}>{t(lowerFirst(data.label))}</span>
71
- <NodeDropdown id={id}></NodeDropdown>
72
- </Flex>
73
- <section className={styles.bottomBox}>
74
- <div className={styles.nodeName}>{data.name}</div>
 
75
  </section>
76
- </section>
77
  );
78
  }
 
14
  import CategorizeHandle from './categorize-handle';
15
  import NodeDropdown from './dropdown';
16
  import styles from './index.less';
17
+ import NodePopover from './popover';
18
 
19
  export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
20
  const categoryData = get(data, 'form.category_description') ?? {};
21
  const style = operatorMap[data.label as Operator];
22
  const { t } = useTranslate('flow');
23
  return (
24
+ <NodePopover nodeId={id}>
25
+ <section
26
+ className={classNames(styles.ragNode, {
27
+ [styles.selectedNode]: selected,
28
+ })}
29
+ style={{
30
+ backgroundColor: style.backgroundColor,
31
+ color: style.color,
32
+ }}
33
+ >
34
+ <Handle
35
+ type="target"
36
+ position={Position.Left}
37
+ isConnectable
38
+ className={styles.handle}
39
+ id={'a'}
40
+ ></Handle>
41
+ <Handle
42
+ type="target"
43
+ position={Position.Top}
44
+ isConnectable
45
+ className={styles.handle}
46
+ id={'b'}
47
+ ></Handle>
48
+ <Handle
49
+ type="target"
50
+ position={Position.Bottom}
51
+ isConnectable
52
+ className={styles.handle}
53
+ id={'c'}
54
+ ></Handle>
55
+ {Object.keys(categoryData).map((x, idx) => {
56
+ return (
57
+ <CategorizeHandle
58
+ top={CategorizeAnchorPointPositions[idx].top}
59
+ right={CategorizeAnchorPointPositions[idx].right}
60
+ key={idx}
61
+ text={x}
62
+ idx={idx}
63
+ ></CategorizeHandle>
64
+ );
65
+ })}
66
+ <Flex vertical align="center" justify="center" gap={6}>
67
+ <OperatorIcon
68
+ name={data.label as Operator}
69
+ fontSize={24}
70
+ ></OperatorIcon>
71
+ <span className={styles.type}>{t(lowerFirst(data.label))}</span>
72
+ <NodeDropdown id={id}></NodeDropdown>
73
+ </Flex>
74
+ <section className={styles.bottomBox}>
75
+ <div className={styles.nodeName}>{data.name}</div>
76
+ </section>
77
  </section>
78
+ </NodePopover>
79
  );
80
  }
web/src/pages/flow/canvas/node/index.tsx CHANGED
@@ -9,6 +9,7 @@ import { NodeData } from '../../interface';
9
  import OperatorIcon from '../../operator-icon';
10
  import NodeDropdown from './dropdown';
11
  import styles from './index.less';
 
12
 
13
  export function RagNode({
14
  id,
@@ -18,53 +19,54 @@ export function RagNode({
18
  }: NodeProps<NodeData>) {
19
  const style = operatorMap[data.label as Operator];
20
  const { t } = useTranslate('flow');
 
21
  return (
22
- <section
23
- className={classNames(styles.ragNode, {
24
- [styles.selectedNode]: selected,
25
- })}
26
- style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
27
- >
28
- <Handle
29
- id="c"
30
- type="source"
31
- position={Position.Left}
32
- isConnectable={isConnectable}
33
- className={styles.handle}
34
- ></Handle>
35
- <Handle type="source" position={Position.Top} id="d" isConnectable />
36
- <Handle
37
- type="source"
38
- position={Position.Right}
39
- isConnectable={isConnectable}
40
- className={styles.handle}
41
- id="b"
42
- ></Handle>
43
- <Handle type="source" position={Position.Bottom} id="a" isConnectable />
44
- <Flex
45
- vertical
46
- align="center"
47
- justify={'center'}
48
- gap={data.label === Operator.RewriteQuestion ? 0 : 6}
49
  >
50
- <OperatorIcon
51
- name={data.label as Operator}
52
- fontSize={style['iconFontSize'] ?? 24}
53
- ></OperatorIcon>
54
- <span
55
- className={styles.type}
56
- style={{ fontSize: style.fontSize ?? 14 }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  >
58
- {data.label === Operator.RewriteQuestion
59
- ? t(lowerFirst('Rewrite'))
60
- : t(lowerFirst(data.label))}
61
- </span>
62
- <NodeDropdown id={id}></NodeDropdown>
63
- </Flex>
 
 
 
 
 
 
64
 
65
- <section className={styles.bottomBox}>
66
- <div className={styles.nodeName}>{data.name}</div>
 
67
  </section>
68
- </section>
69
  );
70
  }
 
9
  import OperatorIcon from '../../operator-icon';
10
  import NodeDropdown from './dropdown';
11
  import styles from './index.less';
12
+ import NodePopover from './popover';
13
 
14
  export function RagNode({
15
  id,
 
19
  }: NodeProps<NodeData>) {
20
  const style = operatorMap[data.label as Operator];
21
  const { t } = useTranslate('flow');
22
+
23
  return (
24
+ <NodePopover nodeId={id}>
25
+ <section
26
+ className={classNames(styles.ragNode, {
27
+ [styles.selectedNode]: selected,
28
+ })}
29
+ style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  >
31
+ <Handle
32
+ id="c"
33
+ type="source"
34
+ position={Position.Left}
35
+ isConnectable={isConnectable}
36
+ className={styles.handle}
37
+ ></Handle>
38
+ <Handle type="source" position={Position.Top} id="d" isConnectable />
39
+ <Handle
40
+ type="source"
41
+ position={Position.Right}
42
+ isConnectable={isConnectable}
43
+ className={styles.handle}
44
+ id="b"
45
+ ></Handle>
46
+ <Handle type="source" position={Position.Bottom} id="a" isConnectable />
47
+ <Flex
48
+ vertical
49
+ align="center"
50
+ justify={'center'}
51
+ gap={data.label === Operator.RewriteQuestion ? 0 : 6}
52
  >
53
+ <OperatorIcon
54
+ name={data.label as Operator}
55
+ fontSize={style['iconFontSize'] ?? 24}
56
+ ></OperatorIcon>
57
+ <span
58
+ className={styles.type}
59
+ style={{ fontSize: style.fontSize ?? 14 }}
60
+ >
61
+ {t(lowerFirst(data.label))}
62
+ </span>
63
+ <NodeDropdown id={id}></NodeDropdown>
64
+ </Flex>
65
 
66
+ <section className={styles.bottomBox}>
67
+ <div className={styles.nodeName}>{data.name}</div>
68
+ </section>
69
  </section>
70
+ </NodePopover>
71
  );
72
  }
web/src/pages/flow/canvas/node/popover.tsx ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useFetchFlow } from '@/hooks/flow-hooks';
2
+ import { Popover } from 'antd';
3
+ import get from 'lodash/get';
4
+ import React, { useMemo } from 'react';
5
+ import JsonView from 'react18-json-view';
6
+ import 'react18-json-view/src/style.css';
7
+ import { Operator } from '../../constant';
8
+ import { useReplaceIdWithText } from '../../hooks';
9
+
10
+ interface IProps extends React.PropsWithChildren {
11
+ nodeId: string;
12
+ }
13
+
14
+ const NodePopover = ({ children, nodeId }: IProps) => {
15
+ const { data } = useFetchFlow();
16
+ const component = useMemo(() => {
17
+ return get(data, ['dsl', 'components', nodeId], {});
18
+ }, [nodeId, data]);
19
+
20
+ const output = get(component, ['obj', 'params', 'output'], {});
21
+ const componentName = get(component, ['obj', 'component_name'], '');
22
+ const replacedOutput = useReplaceIdWithText(output);
23
+
24
+ const content =
25
+ componentName !== Operator.Answer ? (
26
+ <div
27
+ onClick={(e) => {
28
+ e.preventDefault();
29
+ e.stopPropagation();
30
+ }}
31
+ >
32
+ <JsonView
33
+ src={replacedOutput}
34
+ displaySize={30}
35
+ style={{ maxWidth: 300, maxHeight: 500 }}
36
+ />
37
+ </div>
38
+ ) : undefined;
39
+
40
+ return (
41
+ <Popover content={content} placement="right" destroyTooltipOnHide>
42
+ {children}
43
+ </Popover>
44
+ );
45
+ };
46
+
47
+ export default NodePopover;
web/src/pages/flow/canvas/node/relevant-node.tsx CHANGED
@@ -11,56 +11,59 @@ import NodeDropdown from './dropdown';
11
 
12
  import CategorizeHandle from './categorize-handle';
13
  import styles from './index.less';
 
14
 
15
  export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
16
  const style = operatorMap[data.label as Operator];
17
  const { t } = useTranslate('flow');
18
  return (
19
- <section
20
- className={classNames(styles.ragNode, {
21
- [styles.selectedNode]: selected,
22
- })}
23
- style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
24
- >
25
- <Handle
26
- type="target"
27
- position={Position.Left}
28
- isConnectable
29
- className={styles.handle}
30
- id={'a'}
31
- ></Handle>
32
- <Handle
33
- type="target"
34
- position={Position.Top}
35
- isConnectable
36
- className={styles.handle}
37
- id={'b'}
38
- ></Handle>
39
- <Handle
40
- type="target"
41
- position={Position.Bottom}
42
- isConnectable
43
- className={styles.handle}
44
- id={'c'}
45
- ></Handle>
46
- <CategorizeHandle top={20} right={6} text={'yes'}></CategorizeHandle>
47
- <CategorizeHandle top={80} right={6} text={'no'}></CategorizeHandle>
48
- <Flex vertical align="center" justify="center">
49
- <OperatorIcon
50
- name={data.label as Operator}
51
- fontSize={style.iconFontSize}
52
- ></OperatorIcon>
53
- <span
54
- className={styles.type}
55
- style={{ fontSize: style.fontSize ?? 14 }}
56
- >
57
- {t(lowerFirst(data.label))}
58
- </span>
59
- <NodeDropdown id={id}></NodeDropdown>
60
- </Flex>
61
- <section className={styles.bottomBox}>
62
- <div className={styles.nodeName}>{data.name}</div>
 
 
63
  </section>
64
- </section>
65
  );
66
  }
 
11
 
12
  import CategorizeHandle from './categorize-handle';
13
  import styles from './index.less';
14
+ import NodePopover from './popover';
15
 
16
  export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
17
  const style = operatorMap[data.label as Operator];
18
  const { t } = useTranslate('flow');
19
  return (
20
+ <NodePopover nodeId={id}>
21
+ <section
22
+ className={classNames(styles.ragNode, {
23
+ [styles.selectedNode]: selected,
24
+ })}
25
+ style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
26
+ >
27
+ <Handle
28
+ type="target"
29
+ position={Position.Left}
30
+ isConnectable
31
+ className={styles.handle}
32
+ id={'a'}
33
+ ></Handle>
34
+ <Handle
35
+ type="target"
36
+ position={Position.Top}
37
+ isConnectable
38
+ className={styles.handle}
39
+ id={'b'}
40
+ ></Handle>
41
+ <Handle
42
+ type="target"
43
+ position={Position.Bottom}
44
+ isConnectable
45
+ className={styles.handle}
46
+ id={'c'}
47
+ ></Handle>
48
+ <CategorizeHandle top={20} right={6} text={'yes'}></CategorizeHandle>
49
+ <CategorizeHandle top={80} right={6} text={'no'}></CategorizeHandle>
50
+ <Flex vertical align="center" justify="center">
51
+ <OperatorIcon
52
+ name={data.label as Operator}
53
+ fontSize={style.iconFontSize}
54
+ ></OperatorIcon>
55
+ <span
56
+ className={styles.type}
57
+ style={{ fontSize: style.fontSize ?? 14 }}
58
+ >
59
+ {t(lowerFirst(data.label))}
60
+ </span>
61
+ <NodeDropdown id={id}></NodeDropdown>
62
+ </Flex>
63
+ <section className={styles.bottomBox}>
64
+ <div className={styles.nodeName}>{data.name}</div>
65
+ </section>
66
  </section>
67
+ </NodePopover>
68
  );
69
  }
web/src/pages/flow/hooks.ts CHANGED
@@ -38,7 +38,11 @@ import {
38
  initialRewriteQuestionValues,
39
  } from './constant';
40
  import useGraphStore, { RFState } from './store';
41
- import { buildDslComponentsByGraph, receiveMessageError } from './utils';
 
 
 
 
42
 
43
  const selector = (state: RFState) => ({
44
  nodes: state.nodes,
@@ -376,3 +380,13 @@ export const useSaveGraphBeforeOpeningDebugDrawer = (show: () => void) => {
376
 
377
  return handleRun;
378
  };
 
 
 
 
 
 
 
 
 
 
 
38
  initialRewriteQuestionValues,
39
  } from './constant';
40
  import useGraphStore, { RFState } from './store';
41
+ import {
42
+ buildDslComponentsByGraph,
43
+ receiveMessageError,
44
+ replaceIdWithText,
45
+ } from './utils';
46
 
47
  const selector = (state: RFState) => ({
48
  nodes: state.nodes,
 
380
 
381
  return handleRun;
382
  };
383
+
384
+ export const useReplaceIdWithText = (output: unknown) => {
385
+ const getNode = useGraphStore((state) => state.getNode);
386
+
387
+ const getNameById = (id?: string) => {
388
+ return getNode(id)?.data.name;
389
+ };
390
+
391
+ return replaceIdWithText(output, getNameById);
392
+ };
web/src/pages/flow/utils.ts CHANGED
@@ -4,6 +4,7 @@ import dagre from 'dagre';
4
  import { humanId } from 'human-id';
5
  import { curry } from 'lodash';
6
  import pipe from 'lodash/fp/pipe';
 
7
  import { Edge, Node, Position } from 'reactflow';
8
  import { v4 as uuidv4 } from 'uuid';
9
  import { NodeMap, Operator } from './constant';
@@ -184,3 +185,26 @@ export const buildDslComponentsByGraph = (
184
 
185
  export const receiveMessageError = (res: any) =>
186
  res && (res?.response.status !== 200 || res?.data?.retcode !== 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import { humanId } from 'human-id';
5
  import { curry } from 'lodash';
6
  import pipe from 'lodash/fp/pipe';
7
+ import isObject from 'lodash/isObject';
8
  import { Edge, Node, Position } from 'reactflow';
9
  import { v4 as uuidv4 } from 'uuid';
10
  import { NodeMap, Operator } from './constant';
 
185
 
186
  export const receiveMessageError = (res: any) =>
187
  res && (res?.response.status !== 200 || res?.data?.retcode !== 0);
188
+
189
+ // Replace the id in the object with text
190
+ export const replaceIdWithText = (
191
+ obj: Record<string, unknown> | unknown[] | unknown,
192
+ getNameById: (id?: string) => string | undefined,
193
+ ) => {
194
+ if (isObject(obj)) {
195
+ const ret: Record<string, unknown> | unknown[] = Array.isArray(obj)
196
+ ? []
197
+ : {};
198
+ Object.keys(obj).forEach((key) => {
199
+ const val = (obj as Record<string, unknown>)[key];
200
+ const text = typeof val === 'string' ? getNameById(val) : undefined;
201
+ (ret as Record<string, unknown>)[key] = text
202
+ ? text
203
+ : replaceIdWithText(val, getNameById);
204
+ });
205
+
206
+ return ret;
207
+ }
208
+
209
+ return obj;
210
+ };