diff --git a/web/src/pages/agent/form/agent-form/agent-tools.tsx b/web/src/pages/agent/form/agent-form/agent-tools.tsx index 5961c8e5a..65666de3e 100644 --- a/web/src/pages/agent/form/agent-form/agent-tools.tsx +++ b/web/src/pages/agent/form/agent-form/agent-tools.tsx @@ -1,7 +1,13 @@ import { BlockButton } from '@/components/ui/button'; import { cn } from '@/lib/utils'; +import { Position } from '@xyflow/react'; import { PencilLine, X } from 'lucide-react'; -import { PropsWithChildren } from 'react'; +import { PropsWithChildren, useCallback, useContext, useMemo } from 'react'; +import { Operator } from '../../constant'; +import { AgentInstanceContext } from '../../context'; +import { INextOperatorForm } from '../../interface'; +import useGraphStore from '../../store'; +import { filterDownstreamAgentNodeIds } from '../../utils/filter-downstream-nodes'; import { ToolPopover } from './tool-popover'; import { useDeleteAgentNodeTools } from './tool-popover/use-update-tools'; import { useGetAgentToolNames } from './use-get-tools'; @@ -24,6 +30,32 @@ export function ToolCard({ ); } +type ActionButtonProps = { + record: T; + deleteRecord(record: T): void; + edit(record: T): void; +}; + +function ActionButton({ edit, deleteRecord, record }: ActionButtonProps) { + const handleDelete = useCallback(() => { + deleteRecord(record); + }, [deleteRecord, record]); + const handleEdit = useCallback(() => { + edit(record); + }, [edit, record]); + + return ( +
+ + +
+ ); +} + export function AgentTools() { const { toolNames } = useGetAgentToolNames(); const { deleteNodeTool } = useDeleteAgentNodeTools(); @@ -35,13 +67,11 @@ export function AgentTools() { {toolNames.map((x) => ( {x} -
- - -
+ {}} + deleteRecord={deleteNodeTool(x)} + >
))} @@ -51,3 +81,44 @@ export function AgentTools() { ); } + +export function Agents({ node }: INextOperatorForm) { + const { addCanvasNode } = useContext(AgentInstanceContext); + const { deleteAgentDownstreamNodesById, edges, getNode } = useGraphStore( + (state) => state, + ); + + const subBottomAgentNodeIds = useMemo(() => { + return filterDownstreamAgentNodeIds(edges, node?.id); + }, [edges, node?.id]); + + return ( +
+ Agents +
    + {subBottomAgentNodeIds.map((id) => { + const currentNode = getNode(id); + + return ( + + {currentNode?.data.name} + {}} + deleteRecord={deleteAgentDownstreamNodesById} + > + + ); + })} +
+ + Add Agent + +
+ ); +} diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index b89057654..59b6ec892 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -2,7 +2,6 @@ import { FormContainer } from '@/components/form-container'; import { LargeModelFormField } from '@/components/large-model-form-field'; import { LlmSettingSchema } from '@/components/llm-setting-items/next'; import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; -import { BlockButton } from '@/components/ui/button'; import { Form, FormControl, @@ -11,20 +10,18 @@ import { FormLabel, } from '@/components/ui/form'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Position } from '@xyflow/react'; -import { useContext, useMemo } from 'react'; +import { useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { Operator, initialAgentValues } from '../../constant'; -import { AgentInstanceContext } from '../../context'; +import { initialAgentValues } from '../../constant'; import { INextOperatorForm } from '../../interface'; import useGraphStore from '../../store'; import { isBottomSubAgent } from '../../utils'; import { DescriptionField } from '../components/description-field'; import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; -import { AgentTools } from './agent-tools'; +import { AgentTools, Agents } from './agent-tools'; import { useValues } from './use-values'; import { useWatchFormChange } from './use-watch-change'; @@ -74,8 +71,6 @@ const AgentForm = ({ node }: INextOperatorForm) => { useWatchFormChange(node?.id, form); - const { addCanvasNode } = useContext(AgentInstanceContext); - return (
{ )} - - Add Agent - +
diff --git a/web/src/pages/agent/utils/filter-downstream-nodes.ts b/web/src/pages/agent/utils/filter-downstream-nodes.ts index be9b350a9..f376caff5 100644 --- a/web/src/pages/agent/utils/filter-downstream-nodes.ts +++ b/web/src/pages/agent/utils/filter-downstream-nodes.ts @@ -1,23 +1,21 @@ import { Edge } from '@xyflow/react'; import { NodeHandleId } from '../constant'; -// Get all downstream agent operators of the current agent operator -export function filterAllDownstreamAgentAndToolNodeIds( +// Get all downstream node ids +export function filterAllDownstreamNodeIds( edges: Edge[], nodeIds: string[], + predicate: (edge: Edge) => boolean, ) { return nodeIds.reduce((pre, nodeId) => { const currentEdges = edges.filter( - (x) => - x.source === nodeId && - (x.sourceHandle === NodeHandleId.AgentBottom || - x.sourceHandle === NodeHandleId.Tool), + (x) => x.source === nodeId && predicate(x), ); const downstreamNodeIds: string[] = currentEdges.map((x) => x.target); const ids = downstreamNodeIds.concat( - filterAllDownstreamAgentAndToolNodeIds(edges, downstreamNodeIds), + filterAllDownstreamNodeIds(edges, downstreamNodeIds, predicate), ); ids.forEach((x) => { @@ -29,3 +27,37 @@ export function filterAllDownstreamAgentAndToolNodeIds( return pre; }, []); } + +// Get all downstream agent and tool operators of the current agent operator +export function filterAllDownstreamAgentAndToolNodeIds( + edges: Edge[], + nodeIds: string[], +) { + return filterAllDownstreamNodeIds( + edges, + nodeIds, + (edge: Edge) => + edge.sourceHandle === NodeHandleId.AgentBottom || + edge.sourceHandle === NodeHandleId.Tool, + ); +} + +// Get all downstream agent operators of the current agent operator +export function filterAllDownstreamAgentNodeIds( + edges: Edge[], + nodeIds: string[], +) { + return filterAllDownstreamNodeIds( + edges, + nodeIds, + (edge: Edge) => edge.sourceHandle === NodeHandleId.AgentBottom, + ); +} +// The direct child agent node of the current node +export function filterDownstreamAgentNodeIds(edges: Edge[], nodeId?: string) { + return edges + .filter( + (x) => x.source === nodeId && x.sourceHandle === NodeHandleId.AgentBottom, + ) + .map((x) => x.target); +}