Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions web/src/constants/agent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ export enum Operator {
Splitter = 'Splitter',
HierarchicalMerger = 'HierarchicalMerger',
Extractor = 'Extractor',
Loop = 'Loop',
LoopStart = 'LoopItem',
}

export enum ComparisonOperator {
Expand Down
3 changes: 3 additions & 0 deletions web/src/pages/agent/canvas/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { InvokeNode } from './node/invoke-node';
import { IterationNode, IterationStartNode } from './node/iteration-node';
import { KeywordNode } from './node/keyword-node';
import { ListOperationsNode } from './node/list-operations-node';
import { LoopNode, LoopStartNode } from './node/loop-node';
import { MessageNode } from './node/message-node';
import NoteNode from './node/note-node';
import ParserNode from './node/parser-node';
Expand Down Expand Up @@ -105,6 +106,8 @@ export const nodeTypes: NodeTypes = {
listOperationsNode: ListOperationsNode,
variableAssignerNode: VariableAssignerNode,
variableAggregatorNode: VariableAggregatorNode,
loopNode: LoopNode,
loopStartNode: LoopStartNode,
};

const edgeTypes = {
Expand Down
58 changes: 0 additions & 58 deletions web/src/pages/agent/canvas/node/dropdown.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export function AccordionOperators({
operators={[
Operator.Switch,
Operator.Iteration,
Operator.Loop,
Operator.Categorize,
]}
isCustomDropdown={isCustomDropdown}
Expand Down
2 changes: 1 addition & 1 deletion web/src/pages/agent/canvas/node/iteration-node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function InnerIterationNode({
);
}

function InnerIterationStartNode({
export function InnerIterationStartNode({
isConnectable = true,
id,
selected,
Expand Down
75 changes: 75 additions & 0 deletions web/src/pages/agent/canvas/node/labeled-group-node.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Panel, type NodeProps, type PanelPosition } from '@xyflow/react';
import { type ComponentProps, type ReactNode } from 'react';

import { BaseNode } from '@/components/xyflow/base-node';
import { cn } from '@/lib/utils';

/* GROUP NODE Label ------------------------------------------------------- */

export type GroupNodeLabelProps = ComponentProps<'div'>;

export function GroupNodeLabel({
children,
className,
...props
}: GroupNodeLabelProps) {
return (
<div className="h-full w-full" {...props}>
<div
className={cn(
'text-card-foreground bg-secondary w-fit p-2 text-xs',
className,
)}
>
{children}
</div>
</div>
);
}

export type GroupNodeProps = Partial<NodeProps> & {
label?: ReactNode;
position?: PanelPosition;
};

/* GROUP NODE -------------------------------------------------------------- */

export function LabeledGroupNode({
label = '',
position,
...props
}: GroupNodeProps) {
const getLabelClassName = (position?: PanelPosition) => {
switch (position) {
case 'top-left':
return 'rounded-br-sm';
case 'top-center':
return 'rounded-b-sm';
case 'top-right':
return 'rounded-bl-sm';
case 'bottom-left':
return 'rounded-tr-sm';
case 'bottom-right':
return 'rounded-tl-sm';
case 'bottom-center':
return 'rounded-t-sm';
default:
return 'rounded-br-sm';
}
};

return (
<BaseNode
className="bg-opacity-50 h-full overflow-hidden rounded-sm"
{...props}
>
<Panel className="m-0 p-0" position={position}>
{label && (
<GroupNodeLabel className={getLabelClassName(position)}>
{label}
</GroupNodeLabel>
)}
</Panel>
</BaseNode>
);
}
16 changes: 16 additions & 0 deletions web/src/pages/agent/canvas/node/loop-node.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BaseNode } from '@/interfaces/database/agent';
import { NodeProps } from '@xyflow/react';
import { memo } from 'react';
import { InnerIterationNode, InnerIterationStartNode } from './iteration-node';

export function InnerLoopNode({ ...props }: NodeProps<BaseNode<any>>) {
return <InnerIterationNode {...props}></InnerIterationNode>;
}

export const LoopNode = memo(InnerLoopNode);

export function InnerLoopStartNode({ ...props }: NodeProps<BaseNode<any>>) {
return <InnerIterationStartNode {...props}></InnerIterationStartNode>;
}

export const LoopStartNode = memo(InnerLoopStartNode);
2 changes: 1 addition & 1 deletion web/src/pages/agent/canvas/node/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function ToolBar({
const deleteNode: MouseEventHandler<HTMLDivElement> = useCallback(
(e) => {
e.stopPropagation();
if (label === Operator.Iteration) {
if ([Operator.Iteration, Operator.Loop].includes(label as Operator)) {
deleteIterationNodeById(id);
} else {
deleteNodeById(id);
Expand Down
6 changes: 6 additions & 0 deletions web/src/pages/agent/constant/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,8 @@ export const initialVariableAssignerValues = {};

export const initialVariableAggregatorValues = { outputs: {}, groups: [] };

export const initialLoopValues = { outputs: {} };

export const CategorizeAnchorPointPositions = [
{ top: 1, right: 34 },
{ top: 8, right: 18 },
Expand Down Expand Up @@ -706,6 +708,8 @@ export const RestrictedUpstreamMap = {
[Operator.Tokenizer]: [Operator.Begin],
[Operator.Extractor]: [Operator.Begin],
[Operator.File]: [Operator.Begin],
[Operator.Loop]: [Operator.Begin],
[Operator.LoopStart]: [Operator.Begin],
};

export const NodeMap = {
Expand Down Expand Up @@ -758,6 +762,8 @@ export const NodeMap = {
[Operator.ListOperations]: 'listOperationsNode',
[Operator.VariableAssigner]: 'variableAssignerNode',
[Operator.VariableAggregator]: 'variableAggregatorNode',
[Operator.Loop]: 'loopNode',
[Operator.LoopStart]: 'loopStartNode',
};

export enum BeginQueryType {
Expand Down
91 changes: 64 additions & 27 deletions web/src/pages/agent/hooks/use-add-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
initialJin10Values,
initialKeywordExtractValues,
initialListOperationsValues,
initialLoopValues,
initialMessageValues,
initialNoteValues,
initialParserValues,
Expand Down Expand Up @@ -68,6 +69,63 @@ function isBottomSubAgent(type: string, position: Position) {
type === Operator.Tool
);
}

const GroupStartNodeMap = {
[Operator.Iteration]: {
id: `${Operator.IterationStart}:${humanId()}`,
type: 'iterationStartNode',
position: { x: 50, y: 100 },
data: {
label: Operator.IterationStart,
name: Operator.IterationStart,
form: initialIterationStartValues,
},
extent: 'parent' as 'parent',
},
[Operator.Loop]: {
id: `${Operator.LoopStart}:${humanId()}`,
type: 'loopStartNode',
position: { x: 50, y: 100 },
data: {
label: Operator.LoopStart,
name: Operator.LoopStart,
form: {},
},
extent: 'parent' as 'parent',
},
};

function useAddGroupNode() {
const { addEdge, addNode } = useGraphStore((state) => state);

const addGroupNode = useCallback(
(operatorType: string, newNode: Node<any>, nodeId?: string) => {
newNode.width = 500;
newNode.height = 250;

const startNode: Node<any> =
GroupStartNodeMap[operatorType as keyof typeof GroupStartNodeMap];

startNode.parentId = newNode.id;

addNode(newNode);
addNode(startNode);

if (nodeId) {
addEdge({
source: nodeId,
target: newNode.id,
sourceHandle: NodeHandleId.Start,
targetHandle: NodeHandleId.End,
});
}
return newNode.id;
},
[addEdge, addNode],
);

return { addGroupNode };
}
export const useInitializeOperatorParams = () => {
const llmId = useFetchModelId();

Expand Down Expand Up @@ -133,6 +191,8 @@ export const useInitializeOperatorParams = () => {
[Operator.ListOperations]: initialListOperationsValues,
[Operator.VariableAssigner]: initialVariableAssignerValues,
[Operator.VariableAggregator]: initialVariableAggregatorValues,
[Operator.Loop]: initialLoopValues,
[Operator.LoopStart]: {},
};
}, [llmId]);

Expand Down Expand Up @@ -311,6 +371,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
const { addChildEdge } = useAddChildEdge();
const { addToolNode } = useAddToolNode();
const { resizeIterationNode } = useResizeIterationNode();
const { addGroupNode } = useAddGroupNode();
// const [reactFlowInstance, setReactFlowInstance] =
// useState<ReactFlowInstance<any, any>>();

Expand Down Expand Up @@ -376,33 +437,8 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
}
}

if (type === Operator.Iteration) {
newNode.width = 500;
newNode.height = 250;
const iterationStartNode: Node<any> = {
id: `${Operator.IterationStart}:${humanId()}`,
type: 'iterationStartNode',
position: { x: 50, y: 100 },
// draggable: false,
data: {
label: Operator.IterationStart,
name: Operator.IterationStart,
form: initialIterationStartValues,
},
parentId: newNode.id,
extent: 'parent',
};
addNode(newNode);
addNode(iterationStartNode);
if (nodeId) {
addEdge({
source: nodeId,
target: newNode.id,
sourceHandle: NodeHandleId.Start,
targetHandle: NodeHandleId.End,
});
}
return newNode.id;
if ([Operator.Iteration, Operator.Loop].includes(type as Operator)) {
return addGroupNode(type, newNode, nodeId);
} else if (
type === Operator.Agent &&
params.position === Position.Bottom
Expand Down Expand Up @@ -456,6 +492,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
[
addChildEdge,
addEdge,
addGroupNode,
addNode,
addToolNode,
calculateNewlyBackChildPosition,
Expand Down
9 changes: 7 additions & 2 deletions web/src/pages/agent/hooks/use-show-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const useShowFormDrawer = () => {
setClickedNodeId,
getNode,
setClickedToolId,
getOperatorTypeFromId,
} = useGraphStore((state) => state);
const {
visible: formDrawerVisible,
Expand All @@ -25,14 +26,18 @@ export const useShowFormDrawer = () => {
(e: React.MouseEvent<Element>, nodeId: string) => {
const tool = get(e.target, 'dataset.tool');
// TODO: Operator type judgment should be used
if (nodeId.startsWith(Operator.Tool) && !tool) {
const operatorType = getOperatorTypeFromId(nodeId);
if (
(operatorType === Operator.Tool && !tool) ||
[Operator.LoopStart].includes(operatorType as Operator)
) {
return;
}
setClickedNodeId(nodeId);
setClickedToolId(tool);
showFormDrawer();
},
[setClickedNodeId, setClickedToolId, showFormDrawer],
[getOperatorTypeFromId, setClickedNodeId, setClickedToolId, showFormDrawer],
);

return {
Expand Down
Loading