mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-18 03:26:42 +08:00
### What problem does this PR solve? Feat: Remove the copy icon from the toolbar for the Splitter and Parser nodes #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -11,7 +11,7 @@ import useGraphStore from '../../store';
|
|||||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
import { useFetchAgent } from '@/hooks/use-agent-request';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { NodeHandleId, Operator } from '../../constant';
|
import { Operator } from '../../constant';
|
||||||
|
|
||||||
function InnerButtonEdge({
|
function InnerButtonEdge({
|
||||||
id,
|
id,
|
||||||
@ -27,7 +27,6 @@ function InnerButtonEdge({
|
|||||||
markerEnd,
|
markerEnd,
|
||||||
selected,
|
selected,
|
||||||
data,
|
data,
|
||||||
sourceHandleId,
|
|
||||||
}: EdgeProps<Edge<{ isHovered: boolean }>>) {
|
}: EdgeProps<Edge<{ isHovered: boolean }>>) {
|
||||||
const deleteEdgeById = useGraphStore((state) => state.deleteEdgeById);
|
const deleteEdgeById = useGraphStore((state) => state.deleteEdgeById);
|
||||||
const [edgePath, labelX, labelY] = getBezierPath({
|
const [edgePath, labelX, labelY] = getBezierPath({
|
||||||
@ -49,47 +48,32 @@ function InnerButtonEdge({
|
|||||||
// highlight the nodes that the workflow passes through
|
// highlight the nodes that the workflow passes through
|
||||||
const { data: flowDetail } = useFetchAgent();
|
const { data: flowDetail } = useFetchAgent();
|
||||||
|
|
||||||
const graphPath = useMemo(() => {
|
const showHighlight = useMemo(() => {
|
||||||
// TODO: this will be called multiple times
|
|
||||||
const path = flowDetail?.dsl?.path ?? [];
|
const path = flowDetail?.dsl?.path ?? [];
|
||||||
// The second to last
|
const idx = path.findIndex((x) => x === target);
|
||||||
const previousGraphPath: string[] = path.at(-2) ?? [];
|
|
||||||
let graphPath: string[] = path.at(-1) ?? [];
|
|
||||||
// The last of the second to last article
|
|
||||||
const previousLatestElement = previousGraphPath.at(-1);
|
|
||||||
if (previousGraphPath.length > 0 && previousLatestElement) {
|
|
||||||
graphPath = [previousLatestElement, ...graphPath];
|
|
||||||
}
|
|
||||||
return Array.isArray(graphPath) ? graphPath : [];
|
|
||||||
}, [flowDetail.dsl?.path]);
|
|
||||||
|
|
||||||
const highlightStyle = useMemo(() => {
|
|
||||||
const idx = graphPath.findIndex((x) => x === source);
|
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
// The set of elements following source
|
let index = idx - 1;
|
||||||
const slicedGraphPath = graphPath.slice(idx + 1);
|
while (index >= 0) {
|
||||||
if (slicedGraphPath.some((x) => x === target)) {
|
if (path[index] === source) {
|
||||||
return { strokeWidth: 1, stroke: 'red' };
|
return { strokeWidth: 1, stroke: 'var(--accent-primary)' };
|
||||||
}
|
}
|
||||||
|
index--;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}, [source, target, graphPath]);
|
}
|
||||||
|
return {};
|
||||||
|
}, [flowDetail?.dsl?.path, source, target]);
|
||||||
|
|
||||||
const visible = useMemo(() => {
|
const visible = useMemo(() => {
|
||||||
return (
|
return data?.isHovered && source !== Operator.Begin;
|
||||||
data?.isHovered &&
|
}, [data?.isHovered, source]);
|
||||||
sourceHandleId !== NodeHandleId.Tool &&
|
|
||||||
sourceHandleId !== NodeHandleId.AgentBottom && // The connection between the agent node and the tool node does not need to display the delete button
|
|
||||||
!target.startsWith(Operator.Tool)
|
|
||||||
);
|
|
||||||
}, [data?.isHovered, sourceHandleId, target]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BaseEdge
|
<BaseEdge
|
||||||
path={edgePath}
|
path={edgePath}
|
||||||
markerEnd={markerEnd}
|
markerEnd={markerEnd}
|
||||||
style={{ ...style, ...selectedStyle, ...highlightStyle }}
|
style={{ ...style, ...selectedStyle, ...showHighlight }}
|
||||||
className="text-text-secondary"
|
className="text-text-secondary"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@ -42,8 +42,8 @@ import { ButtonEdge } from './edge';
|
|||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
import { RagNode } from './node';
|
import { RagNode } from './node';
|
||||||
import { BeginNode } from './node/begin-node';
|
import { BeginNode } from './node/begin-node';
|
||||||
import { ContextNode } from './node/context-node';
|
|
||||||
import { NextStepDropdown } from './node/dropdown/next-step-dropdown';
|
import { NextStepDropdown } from './node/dropdown/next-step-dropdown';
|
||||||
|
import { ExtractorNode } from './node/extractor-node';
|
||||||
import { HierarchicalMergerNode } from './node/hierarchical-merger-node';
|
import { HierarchicalMergerNode } from './node/hierarchical-merger-node';
|
||||||
import NoteNode from './node/note-node';
|
import NoteNode from './node/note-node';
|
||||||
import ParserNode from './node/parser-node';
|
import ParserNode from './node/parser-node';
|
||||||
@ -58,7 +58,7 @@ export const nodeTypes: NodeTypes = {
|
|||||||
tokenizerNode: TokenizerNode,
|
tokenizerNode: TokenizerNode,
|
||||||
splitterNode: SplitterNode,
|
splitterNode: SplitterNode,
|
||||||
hierarchicalMergerNode: HierarchicalMergerNode,
|
hierarchicalMergerNode: HierarchicalMergerNode,
|
||||||
contextNode: ContextNode,
|
contextNode: ExtractorNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const edgeTypes = {
|
const edgeTypes = {
|
||||||
@ -316,7 +316,6 @@ function DataFlowCanvas({ drawerVisible, hideDrawer, showLogSheet }: IProps) {
|
|||||||
loading={running}
|
loading={running}
|
||||||
></RunSheet>
|
></RunSheet>
|
||||||
)}
|
)}
|
||||||
{/* {logSheetVisible && <LogSheet hideModal={hideLogSheet}></LogSheet>} */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export { RagNode as ContextNode } from './index';
|
|
||||||
@ -24,7 +24,7 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Operator } from '../../../constant';
|
import { Operator, SingleOperators } from '../../../constant';
|
||||||
import { AgentInstanceContext, HandleContext } from '../../../context';
|
import { AgentInstanceContext, HandleContext } from '../../../context';
|
||||||
import OperatorIcon from '../../../operator-icon';
|
import OperatorIcon from '../../../operator-icon';
|
||||||
|
|
||||||
@ -112,18 +112,12 @@ function OperatorItemList({
|
|||||||
return <ul className="space-y-2">{operators.map(renderOperatorItem)}</ul>;
|
return <ul className="space-y-2">{operators.map(renderOperatorItem)}</ul>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const singleOperators = [
|
|
||||||
Operator.Tokenizer,
|
|
||||||
Operator.Splitter,
|
|
||||||
Operator.HierarchicalMerger,
|
|
||||||
Operator.Parser,
|
|
||||||
];
|
|
||||||
// Limit the number of operators of a certain type on the canvas to only one
|
// Limit the number of operators of a certain type on the canvas to only one
|
||||||
function useRestrictSingleOperatorOnCanvas() {
|
function useRestrictSingleOperatorOnCanvas() {
|
||||||
const list: Operator[] = [];
|
const list: Operator[] = [];
|
||||||
const { findNodeByName } = useGraphStore((state) => state);
|
const { findNodeByName } = useGraphStore((state) => state);
|
||||||
|
|
||||||
singleOperators.forEach((operator) => {
|
SingleOperators.forEach((operator) => {
|
||||||
if (!findNodeByName(operator)) {
|
if (!findNodeByName(operator)) {
|
||||||
list.push(operator);
|
list.push(operator);
|
||||||
}
|
}
|
||||||
|
|||||||
1
web/src/pages/data-flow/canvas/node/extractor-node.tsx
Normal file
1
web/src/pages/data-flow/canvas/node/extractor-node.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { RagNode as ExtractorNode } from './index';
|
||||||
@ -1,7 +1,8 @@
|
|||||||
import { IRagNode } from '@/interfaces/database/flow';
|
import { IRagNode } from '@/interfaces/database/flow';
|
||||||
import { NodeProps, Position } from '@xyflow/react';
|
import { NodeProps, Position } from '@xyflow/react';
|
||||||
import { memo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { NodeHandleId } from '../../constant';
|
import { NodeHandleId, SingleOperators } from '../../constant';
|
||||||
|
import useGraphStore from '../../store';
|
||||||
import { CommonHandle } from './handle';
|
import { CommonHandle } from './handle';
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
||||||
import NodeHeader from './node-header';
|
import NodeHeader from './node-header';
|
||||||
@ -14,8 +15,17 @@ function InnerRagNode({
|
|||||||
isConnectable = true,
|
isConnectable = true,
|
||||||
selected,
|
selected,
|
||||||
}: NodeProps<IRagNode>) {
|
}: NodeProps<IRagNode>) {
|
||||||
|
const getOperatorTypeFromId = useGraphStore(
|
||||||
|
(state) => state.getOperatorTypeFromId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const showCopy = useMemo(() => {
|
||||||
|
const operatorName = getOperatorTypeFromId(id);
|
||||||
|
return SingleOperators.every((x) => x !== operatorName);
|
||||||
|
}, [getOperatorTypeFromId, id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolBar selected={selected} id={id} label={data.label}>
|
<ToolBar selected={selected} id={id} label={data.label} showCopy={showCopy}>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected}>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
id={NodeHandleId.End}
|
id={NodeHandleId.End}
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
import { ILogicNode } from '@/interfaces/database/flow';
|
|
||||||
import { NodeProps, Position } from '@xyflow/react';
|
|
||||||
import { memo } from 'react';
|
|
||||||
import { CommonHandle } from './handle';
|
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
||||||
import NodeHeader from './node-header';
|
|
||||||
import { NodeWrapper } from './node-wrapper';
|
|
||||||
import { ToolBar } from './toolbar';
|
|
||||||
|
|
||||||
export function InnerLogicNode({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<ILogicNode>) {
|
|
||||||
return (
|
|
||||||
<ToolBar selected={selected} id={id} label={data.label}>
|
|
||||||
<NodeWrapper selected={selected}>
|
|
||||||
<CommonHandle
|
|
||||||
id="c"
|
|
||||||
type="source"
|
|
||||||
position={Position.Left}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
style={LeftHandleStyle}
|
|
||||||
nodeId={id}
|
|
||||||
></CommonHandle>
|
|
||||||
<CommonHandle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
style={RightHandleStyle}
|
|
||||||
id="b"
|
|
||||||
nodeId={id}
|
|
||||||
></CommonHandle>
|
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
|
||||||
</NodeWrapper>
|
|
||||||
</ToolBar>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LogicNode = memo(InnerLogicNode);
|
|
||||||
@ -2,12 +2,10 @@ import { IRagNode } from '@/interfaces/database/flow';
|
|||||||
import { NodeProps, Position } from '@xyflow/react';
|
import { NodeProps, Position } from '@xyflow/react';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { NodeHandleId } from '../../constant';
|
import { NodeHandleId } from '../../constant';
|
||||||
import { needsSingleStepDebugging } from '../../utils';
|
|
||||||
import { CommonHandle } from './handle';
|
import { CommonHandle } from './handle';
|
||||||
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
||||||
import NodeHeader from './node-header';
|
import NodeHeader from './node-header';
|
||||||
import { NodeWrapper } from './node-wrapper';
|
import { NodeWrapper } from './node-wrapper';
|
||||||
import { ToolBar } from './toolbar';
|
|
||||||
|
|
||||||
function ParserNode({
|
function ParserNode({
|
||||||
id,
|
id,
|
||||||
@ -16,12 +14,6 @@ function ParserNode({
|
|||||||
selected,
|
selected,
|
||||||
}: NodeProps<IRagNode>) {
|
}: NodeProps<IRagNode>) {
|
||||||
return (
|
return (
|
||||||
<ToolBar
|
|
||||||
selected={selected}
|
|
||||||
id={id}
|
|
||||||
label={data.label}
|
|
||||||
showRun={needsSingleStepDebugging(data.label)}
|
|
||||||
>
|
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected}>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
id={NodeHandleId.End}
|
id={NodeHandleId.End}
|
||||||
@ -42,7 +34,6 @@ function ParserNode({
|
|||||||
></CommonHandle>
|
></CommonHandle>
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
||||||
</NodeWrapper>
|
</NodeWrapper>
|
||||||
</ToolBar>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { IRagNode } from '@/interfaces/database/flow';
|
|||||||
import { NodeProps, Position } from '@xyflow/react';
|
import { NodeProps, Position } from '@xyflow/react';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { NodeHandleId } from '../../constant';
|
import { NodeHandleId } from '../../constant';
|
||||||
import { needsSingleStepDebugging } from '../../utils';
|
|
||||||
import { CommonHandle } from './handle';
|
import { CommonHandle } from './handle';
|
||||||
import { LeftHandleStyle } from './handle-icon';
|
import { LeftHandleStyle } from './handle-icon';
|
||||||
import NodeHeader from './node-header';
|
import NodeHeader from './node-header';
|
||||||
@ -20,7 +19,8 @@ function TokenizerNode({
|
|||||||
selected={selected}
|
selected={selected}
|
||||||
id={id}
|
id={id}
|
||||||
label={data.label}
|
label={data.label}
|
||||||
showRun={needsSingleStepDebugging(data.label)}
|
showRun={false}
|
||||||
|
showCopy={false}
|
||||||
>
|
>
|
||||||
<NodeWrapper selected={selected}>
|
<NodeWrapper selected={selected}>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
|
|||||||
@ -27,6 +27,7 @@ type ToolBarProps = {
|
|||||||
label: string;
|
label: string;
|
||||||
id: string;
|
id: string;
|
||||||
showRun?: boolean;
|
showRun?: boolean;
|
||||||
|
showCopy?: boolean;
|
||||||
} & PropsWithChildren;
|
} & PropsWithChildren;
|
||||||
|
|
||||||
export function ToolBar({
|
export function ToolBar({
|
||||||
@ -35,6 +36,7 @@ export function ToolBar({
|
|||||||
label,
|
label,
|
||||||
id,
|
id,
|
||||||
showRun = false,
|
showRun = false,
|
||||||
|
showCopy = true,
|
||||||
}: ToolBarProps) {
|
}: ToolBarProps) {
|
||||||
const deleteNodeById = useGraphStore((store) => store.deleteNodeById);
|
const deleteNodeById = useGraphStore((store) => store.deleteNodeById);
|
||||||
|
|
||||||
@ -66,10 +68,13 @@ export function ToolBar({
|
|||||||
<IconWrapper>
|
<IconWrapper>
|
||||||
<Play className="size-3.5" data-play />
|
<Play className="size-3.5" data-play />
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
)}{' '}
|
)}
|
||||||
|
{showCopy && (
|
||||||
<IconWrapper onClick={handleDuplicate}>
|
<IconWrapper onClick={handleDuplicate}>
|
||||||
<Copy className="size-3.5" />
|
<Copy className="size-3.5" />
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
|
)}
|
||||||
|
|
||||||
<IconWrapper onClick={deleteNode}>
|
<IconWrapper onClick={deleteNode}>
|
||||||
<Trash2 className="size-3.5" />
|
<Trash2 className="size-3.5" />
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
|
|||||||
@ -325,13 +325,14 @@ export const CategorizeAnchorPointPositions = [
|
|||||||
|
|
||||||
// key is the source of the edge, value is the target of the edge
|
// key is the source of the edge, value is the target of the edge
|
||||||
// no connection lines are allowed between key and value
|
// no connection lines are allowed between key and value
|
||||||
export const RestrictedUpstreamMap = {
|
export const RestrictedUpstreamMap: Record<Operator, Operator[]> = {
|
||||||
[Operator.Begin]: [],
|
[Operator.Begin]: [] as Operator[],
|
||||||
[Operator.Parser]: [Operator.Begin],
|
[Operator.Parser]: [Operator.Begin],
|
||||||
[Operator.Splitter]: [Operator.Begin],
|
[Operator.Splitter]: [Operator.Begin],
|
||||||
[Operator.HierarchicalMerger]: [Operator.Begin],
|
[Operator.HierarchicalMerger]: [Operator.Begin],
|
||||||
[Operator.Tokenizer]: [Operator.Begin],
|
[Operator.Tokenizer]: [Operator.Begin],
|
||||||
[Operator.Extractor]: [Operator.Begin],
|
[Operator.Extractor]: [Operator.Begin],
|
||||||
|
[Operator.Note]: [Operator.Begin],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NodeMap = {
|
export const NodeMap = {
|
||||||
@ -411,3 +412,10 @@ export const FileTypeSuffixMap = {
|
|||||||
'ape',
|
'ape',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SingleOperators = [
|
||||||
|
Operator.Tokenizer,
|
||||||
|
Operator.Splitter,
|
||||||
|
Operator.HierarchicalMerger,
|
||||||
|
Operator.Parser,
|
||||||
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user