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

### 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)
This commit is contained in:
balibabu 2024-07-09 16:34:59 +08:00 committed by GitHub
parent 198a8b6592
commit fb66b1e726
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 250 additions and 148 deletions

15
web/package-lock.json generated
View File

@ -36,6 +36,7 @@
"react-pdf-highlighter": "^6.1.0",
"react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0",
"react18-json-view": "^0.2.8",
"reactflow": "^11.11.2",
"recharts": "^2.12.4",
"remark-gfm": "^4.0.0",
@ -5011,9 +5012,9 @@
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
},
"node_modules/@types/lodash": {
"version": "4.14.202",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
"version": "4.17.6",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.6.tgz",
"integrity": "sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==",
"dev": true
},
"node_modules/@types/mdast": {
@ -22274,6 +22275,14 @@
"react-dom": ">=16.6.0"
}
},
"node_modules/react18-json-view": {
"version": "0.2.8",
"resolved": "https://registry.npmmirror.com/react18-json-view/-/react18-json-view-0.2.8.tgz",
"integrity": "sha512-uJlcf5PEDaba6yTqfcDAcMSYECZ15SLcpP94mLFTa/+fa1kZANjERqKzS7YxxsrGP4+jDxt6sIaglR0PbQcKPw==",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",

View File

@ -47,6 +47,7 @@
"react-pdf-highlighter": "^6.1.0",
"react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0",
"react18-json-view": "^0.2.8",
"reactflow": "^11.11.2",
"recharts": "^2.12.4",
"remark-gfm": "^4.0.0",

View File

@ -14,12 +14,14 @@ import OperatorIcon from '../../operator-icon';
import CategorizeHandle from './categorize-handle';
import NodeDropdown from './dropdown';
import styles from './index.less';
import NodePopover from './popover';
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
const categoryData = get(data, 'form.category_description') ?? {};
const style = operatorMap[data.label as Operator];
const { t } = useTranslate('flow');
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
@ -51,7 +53,6 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
id={'c'}
></Handle>
{Object.keys(categoryData).map((x, idx) => {
console.info(categoryData, id, data);
return (
<CategorizeHandle
top={CategorizeAnchorPointPositions[idx].top}
@ -74,5 +75,6 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
<div className={styles.nodeName}>{data.name}</div>
</section>
</section>
</NodePopover>
);
}

View File

@ -9,6 +9,7 @@ import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon';
import NodeDropdown from './dropdown';
import styles from './index.less';
import NodePopover from './popover';
export function RagNode({
id,
@ -18,7 +19,9 @@ export function RagNode({
}: NodeProps<NodeData>) {
const style = operatorMap[data.label as Operator];
const { t } = useTranslate('flow');
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
@ -55,9 +58,7 @@ export function RagNode({
className={styles.type}
style={{ fontSize: style.fontSize ?? 14 }}
>
{data.label === Operator.RewriteQuestion
? t(lowerFirst('Rewrite'))
: t(lowerFirst(data.label))}
{t(lowerFirst(data.label))}
</span>
<NodeDropdown id={id}></NodeDropdown>
</Flex>
@ -66,5 +67,6 @@ export function RagNode({
<div className={styles.nodeName}>{data.name}</div>
</section>
</section>
</NodePopover>
);
}

View File

@ -0,0 +1,47 @@
import { useFetchFlow } from '@/hooks/flow-hooks';
import { Popover } from 'antd';
import get from 'lodash/get';
import React, { useMemo } from 'react';
import JsonView from 'react18-json-view';
import 'react18-json-view/src/style.css';
import { Operator } from '../../constant';
import { useReplaceIdWithText } from '../../hooks';
interface IProps extends React.PropsWithChildren {
nodeId: string;
}
const NodePopover = ({ children, nodeId }: IProps) => {
const { data } = useFetchFlow();
const component = useMemo(() => {
return get(data, ['dsl', 'components', nodeId], {});
}, [nodeId, data]);
const output = get(component, ['obj', 'params', 'output'], {});
const componentName = get(component, ['obj', 'component_name'], '');
const replacedOutput = useReplaceIdWithText(output);
const content =
componentName !== Operator.Answer ? (
<div
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<JsonView
src={replacedOutput}
displaySize={30}
style={{ maxWidth: 300, maxHeight: 500 }}
/>
</div>
) : undefined;
return (
<Popover content={content} placement="right" destroyTooltipOnHide>
{children}
</Popover>
);
};
export default NodePopover;

View File

@ -11,11 +11,13 @@ import NodeDropdown from './dropdown';
import CategorizeHandle from './categorize-handle';
import styles from './index.less';
import NodePopover from './popover';
export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
const style = operatorMap[data.label as Operator];
const { t } = useTranslate('flow');
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
@ -62,5 +64,6 @@ export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
<div className={styles.nodeName}>{data.name}</div>
</section>
</section>
</NodePopover>
);
}

View File

@ -38,7 +38,11 @@ import {
initialRewriteQuestionValues,
} from './constant';
import useGraphStore, { RFState } from './store';
import { buildDslComponentsByGraph, receiveMessageError } from './utils';
import {
buildDslComponentsByGraph,
receiveMessageError,
replaceIdWithText,
} from './utils';
const selector = (state: RFState) => ({
nodes: state.nodes,
@ -376,3 +380,13 @@ export const useSaveGraphBeforeOpeningDebugDrawer = (show: () => void) => {
return handleRun;
};
export const useReplaceIdWithText = (output: unknown) => {
const getNode = useGraphStore((state) => state.getNode);
const getNameById = (id?: string) => {
return getNode(id)?.data.name;
};
return replaceIdWithText(output, getNameById);
};

View File

@ -4,6 +4,7 @@ import dagre from 'dagre';
import { humanId } from 'human-id';
import { curry } from 'lodash';
import pipe from 'lodash/fp/pipe';
import isObject from 'lodash/isObject';
import { Edge, Node, Position } from 'reactflow';
import { v4 as uuidv4 } from 'uuid';
import { NodeMap, Operator } from './constant';
@ -184,3 +185,26 @@ export const buildDslComponentsByGraph = (
export const receiveMessageError = (res: any) =>
res && (res?.response.status !== 200 || res?.data?.retcode !== 0);
// Replace the id in the object with text
export const replaceIdWithText = (
obj: Record<string, unknown> | unknown[] | unknown,
getNameById: (id?: string) => string | undefined,
) => {
if (isObject(obj)) {
const ret: Record<string, unknown> | unknown[] = Array.isArray(obj)
? []
: {};
Object.keys(obj).forEach((key) => {
const val = (obj as Record<string, unknown>)[key];
const text = typeof val === 'string' ? getNameById(val) : undefined;
(ret as Record<string, unknown>)[key] = text
? text
: replaceIdWithText(val, getNameById);
});
return ret;
}
return obj;
};