Browse Source

Feat: Click the edit tool button of the agent form to open the corresponding form #3221 (#9071)

### What problem does this PR solve?

Feat: Click the edit tool button of the agent form to open the
corresponding form #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.20.0
balibabu 3 months ago
parent
commit
35b1a5b7e0
No account linked to committer's email address

+ 5
- 2
web/src/pages/agent/canvas/index.tsx View File

runVisible, runVisible,
hideRunOrChatDrawer, hideRunOrChatDrawer,
showChatModal, showChatModal,
showFormDrawer,
} = useShowDrawer({ } = useShowDrawer({
drawerVisible, drawerVisible,
hideDrawer, hideDrawer,
</marker> </marker>
</defs> </defs>
</svg> </svg>
<AgentInstanceContext.Provider value={{ addCanvasNode }}>
<AgentInstanceContext.Provider value={{ addCanvasNode, showFormDrawer }}>
<ReactFlow <ReactFlow
connectionMode={ConnectionMode.Loose} connectionMode={ConnectionMode.Loose}
nodes={nodes} nodes={nodes}
ref={ref} ref={ref}
></NotebookPen> ></NotebookPen>
{formDrawerVisible && ( {formDrawerVisible && (
<AgentInstanceContext.Provider value={{ addCanvasNode }}>
<AgentInstanceContext.Provider
value={{ addCanvasNode, showFormDrawer }}
>
<FormSheet <FormSheet
node={clickedNode} node={clickedNode}
visible={formDrawerVisible} visible={formDrawerVisible}

+ 3
- 2
web/src/pages/agent/context.ts View File

import { createContext } from 'react'; import { createContext } from 'react';
import { useAddNode } from './hooks/use-add-node'; import { useAddNode } from './hooks/use-add-node';
import { useCacheChatLog } from './hooks/use-cache-chat-log'; import { useCacheChatLog } from './hooks/use-cache-chat-log';
import { useShowLogSheet } from './hooks/use-show-drawer';
import { useShowFormDrawer, useShowLogSheet } from './hooks/use-show-drawer';


export const AgentFormContext = createContext<RAGFlowNodeType | undefined>( export const AgentFormContext = createContext<RAGFlowNodeType | undefined>(
undefined, undefined,
type AgentInstanceContextType = Pick< type AgentInstanceContextType = Pick<
ReturnType<typeof useAddNode>, ReturnType<typeof useAddNode>,
'addCanvasNode' 'addCanvasNode'
>;
> &
Pick<ReturnType<typeof useShowFormDrawer>, 'showFormDrawer'>;


export const AgentInstanceContext = createContext<AgentInstanceContextType>( export const AgentInstanceContext = createContext<AgentInstanceContextType>(
{} as AgentInstanceContextType, {} as AgentInstanceContextType,

+ 39
- 12
web/src/pages/agent/form/agent-form/agent-tools.tsx View File

import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Position } from '@xyflow/react'; import { Position } from '@xyflow/react';
import { PencilLine, X } from 'lucide-react'; import { PencilLine, X } from 'lucide-react';
import { PropsWithChildren, useCallback, useContext, useMemo } from 'react';
import {
MouseEventHandler,
PropsWithChildren,
useCallback,
useContext,
useMemo,
} from 'react';
import { Operator } from '../../constant'; import { Operator } from '../../constant';
import { AgentInstanceContext } from '../../context'; import { AgentInstanceContext } from '../../context';
import { useFindMcpById } from '../../hooks/use-find-mcp-by-id'; import { useFindMcpById } from '../../hooks/use-find-mcp-by-id';
type ActionButtonProps<T> = { type ActionButtonProps<T> = {
record: T; record: T;
deleteRecord(record: T): void; deleteRecord(record: T): void;
edit(record: T): void;
edit: MouseEventHandler<HTMLOrSVGElement>;
}; };


function ActionButton<T>({ edit, deleteRecord, record }: ActionButtonProps<T>) {
function ActionButton<T>({ deleteRecord, record, edit }: ActionButtonProps<T>) {
const handleDelete = useCallback(() => { const handleDelete = useCallback(() => {
deleteRecord(record); deleteRecord(record);
}, [deleteRecord, record]); }, [deleteRecord, record]);
const handleEdit = useCallback(() => {
edit(record);
}, [edit, record]);


return ( return (
<div className="flex items-center gap-2 text-text-sub-title"> <div className="flex items-center gap-2 text-text-sub-title">
<PencilLine <PencilLine
className="size-4 cursor-pointer" className="size-4 cursor-pointer"
data-tool={record} data-tool={record}
onClick={handleEdit}
onClick={edit}
/> />
<X className="size-4 cursor-pointer" onClick={handleDelete} /> <X className="size-4 cursor-pointer" onClick={handleDelete} />
</div> </div>
const { mcpIds } = useGetAgentMCPIds(); const { mcpIds } = useGetAgentMCPIds();
const { findMcpById } = useFindMcpById(); const { findMcpById } = useFindMcpById();
const { deleteNodeMCP } = useDeleteAgentNodeMCP(); const { deleteNodeMCP } = useDeleteAgentNodeMCP();
const { showFormDrawer } = useContext(AgentInstanceContext);
const { clickedNodeId, findAgentToolNodeById, selectNodeIds } = useGraphStore(
(state) => state,
);

const handleEdit: MouseEventHandler<SVGSVGElement> = useCallback(
(e) => {
const toolNodeId = findAgentToolNodeById(clickedNodeId);
if (toolNodeId) {
selectNodeIds([toolNodeId]);
showFormDrawer(e, toolNodeId);
}
},
[clickedNodeId, findAgentToolNodeById, selectNodeIds, showFormDrawer],
);


return ( return (
<section className="space-y-2.5"> <section className="space-y-2.5">
{x} {x}
<ActionButton <ActionButton
record={x} record={x}
edit={() => {}}
deleteRecord={deleteNodeTool(x)} deleteRecord={deleteNodeTool(x)}
edit={handleEdit}
></ActionButton> ></ActionButton>
</ToolCard> </ToolCard>
))} ))}
{findMcpById(id)?.name} {findMcpById(id)?.name}
<ActionButton <ActionButton
record={id} record={id}
edit={() => {}}
deleteRecord={deleteNodeMCP(id)} deleteRecord={deleteNodeMCP(id)}
edit={handleEdit}
></ActionButton> ></ActionButton>
</ToolCard> </ToolCard>
))} ))}


export function Agents({ node }: INextOperatorForm) { export function Agents({ node }: INextOperatorForm) {
const { addCanvasNode } = useContext(AgentInstanceContext); const { addCanvasNode } = useContext(AgentInstanceContext);
const { deleteAgentDownstreamNodesById, edges, getNode } = useGraphStore(
(state) => state,
const { deleteAgentDownstreamNodesById, edges, getNode, selectNodeIds } =
useGraphStore((state) => state);
const { showFormDrawer } = useContext(AgentInstanceContext);

const handleEdit = useCallback(
(nodeId: string): MouseEventHandler<SVGSVGElement> =>
(e) => {
selectNodeIds([nodeId]);
showFormDrawer(e, nodeId);
},
[selectNodeIds, showFormDrawer],
); );


const subBottomAgentNodeIds = useMemo(() => { const subBottomAgentNodeIds = useMemo(() => {
{currentNode?.data.name} {currentNode?.data.name}
<ActionButton <ActionButton
record={id} record={id}
edit={() => {}}
deleteRecord={deleteAgentDownstreamNodesById} deleteRecord={deleteAgentDownstreamNodesById}
edit={handleEdit(id)}
></ActionButton> ></ActionButton>
</ToolCard> </ToolCard>
); );

+ 1
- 1
web/src/pages/agent/form/agent-form/tool-popover/use-update-mcp.ts View File

} else if (tools) { } else if (tools) {
pre.push({ pre.push({
mcp_id: cur, mcp_id: cur,
tools,
tools: {},
}); });
} }
return pre; return pre;

+ 6
- 6
web/src/pages/agent/hooks/use-show-drawer.tsx View File

import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { Node, NodeMouseHandler } from '@xyflow/react';
import { NodeMouseHandler } from '@xyflow/react';
import get from 'lodash/get'; import get from 'lodash/get';
import { useCallback, useEffect } from 'react';
import React, { useCallback, useEffect } from 'react';
import { Operator } from '../constant'; import { Operator } from '../constant';
import useGraphStore from '../store'; import useGraphStore from '../store';
import { useCacheChatLog } from './use-cache-chat-log'; import { useCacheChatLog } from './use-cache-chat-log';
showModal: showFormDrawer, showModal: showFormDrawer,
} = useSetModalState(); } = useSetModalState();


const handleShow: NodeMouseHandler = useCallback(
(e, node: Node) => {
setClickedNodeId(node.id);
const handleShow = useCallback(
(e: React.MouseEvent<Element>, nodeId: string) => {
setClickedNodeId(nodeId);
setClickedToolId(get(e.target, 'dataset.tool')); setClickedToolId(get(e.target, 'dataset.tool'));
showFormDrawer(); showFormDrawer();
}, },
if (!ExcludedNodes.some((x) => x === node.data.label)) { if (!ExcludedNodes.some((x) => x === node.data.label)) {
hideSingleDebugDrawer(); hideSingleDebugDrawer();
// hideRunOrChatDrawer(); // hideRunOrChatDrawer();
showFormDrawer(e, node);
showFormDrawer(e, node.id);
} }
// handle single debug icon click // handle single debug icon click
if ( if (

+ 18
- 0
web/src/pages/agent/store.ts View File

setClickedToolId: (id?: string) => void; setClickedToolId: (id?: string) => void;
findUpstreamNodeById: (id?: string | null) => RAGFlowNodeType | undefined; findUpstreamNodeById: (id?: string | null) => RAGFlowNodeType | undefined;
deleteCategorizeCaseEdges: (source: string, sourceHandle: string) => void; // Deleting a condition of a classification operator will delete the related edge deleteCategorizeCaseEdges: (source: string, sourceHandle: string) => void; // Deleting a condition of a classification operator will delete the related edge
findAgentToolNodeById: (id: string | null) => string | undefined;
selectNodeIds: (nodeIds: string[]) => void;
}; };


// this is our useStore hook that we can use in our components to get parts of the store and call actions // this is our useStore hook that we can use in our components to get parts of the store and call actions
), ),
); );
}, },
findAgentToolNodeById: (id) => {
const { edges } = get();
return edges.find(
(edge) =>
edge.source === id && edge.sourceHandle === NodeHandleId.Tool,
)?.target;
},
selectNodeIds: (nodeIds) => {
const { nodes, setNodes } = get();
setNodes(
nodes.map((node) => ({
...node,
selected: nodeIds.includes(node.id),
})),
);
},
})), })),
{ name: 'graph', trace: true }, { name: 'graph', trace: true },
), ),

Loading…
Cancel
Save