mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Render the mcp list on the agent page #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -159,6 +159,10 @@ export interface IAgentForm {
|
|||||||
component_name: string;
|
component_name: string;
|
||||||
params: Record<string, any>;
|
params: Record<string, any>;
|
||||||
}>;
|
}>;
|
||||||
|
mcp: Array<{
|
||||||
|
mcp_id: string;
|
||||||
|
tools: Record<string, Record<string, any>>;
|
||||||
|
}>;
|
||||||
outputs: {
|
outputs: {
|
||||||
structured_output: Record<string, Record<string, any>>;
|
structured_output: Record<string, Record<string, any>>;
|
||||||
content: Record<string, any>;
|
content: Record<string, any>;
|
||||||
|
|||||||
@ -4,18 +4,15 @@ import { get } from 'lodash';
|
|||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { NodeHandleId } from '../../constant';
|
import { NodeHandleId } from '../../constant';
|
||||||
import { ToolCard } from '../../form/agent-form/agent-tools';
|
import { ToolCard } from '../../form/agent-form/agent-tools';
|
||||||
|
import { useFindMcpById } from '../../hooks/use-find-mcp-by-id';
|
||||||
import useGraphStore from '../../store';
|
import useGraphStore from '../../store';
|
||||||
import { NodeWrapper } from './node-wrapper';
|
import { NodeWrapper } from './node-wrapper';
|
||||||
|
|
||||||
function InnerToolNode({
|
function InnerToolNode({ id, isConnectable = true }: NodeProps<IToolNode>) {
|
||||||
id,
|
|
||||||
data,
|
|
||||||
isConnectable = true,
|
|
||||||
selected,
|
|
||||||
}: NodeProps<IToolNode>) {
|
|
||||||
const { edges, getNode } = useGraphStore((state) => state);
|
const { edges, getNode } = useGraphStore((state) => state);
|
||||||
const upstreamAgentNodeId = edges.find((x) => x.target === id)?.source;
|
const upstreamAgentNodeId = edges.find((x) => x.target === id)?.source;
|
||||||
const upstreamAgentNode = getNode(upstreamAgentNodeId);
|
const upstreamAgentNode = getNode(upstreamAgentNodeId);
|
||||||
|
const { findMcpById } = useFindMcpById();
|
||||||
|
|
||||||
const handleClick = useCallback(() => {}, []);
|
const handleClick = useCallback(() => {}, []);
|
||||||
|
|
||||||
@ -25,6 +22,12 @@ function InnerToolNode({
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const mcpList: IAgentForm['mcp'] = get(
|
||||||
|
upstreamAgentNode,
|
||||||
|
'data.form.mcp',
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeWrapper>
|
<NodeWrapper>
|
||||||
<Handle
|
<Handle
|
||||||
@ -44,6 +47,16 @@ function InnerToolNode({
|
|||||||
{x.component_name}
|
{x.component_name}
|
||||||
</ToolCard>
|
</ToolCard>
|
||||||
))}
|
))}
|
||||||
|
{mcpList.map((x) => (
|
||||||
|
<ToolCard
|
||||||
|
key={x.mcp_id}
|
||||||
|
onClick={handleClick}
|
||||||
|
className="cursor-pointer"
|
||||||
|
data-tool={x.mcp_id}
|
||||||
|
>
|
||||||
|
{findMcpById(x.mcp_id)?.name}
|
||||||
|
</ToolCard>
|
||||||
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</NodeWrapper>
|
</NodeWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -697,6 +697,7 @@ export const initialAgentValues = {
|
|||||||
exception_comment: '',
|
exception_comment: '',
|
||||||
exception_goto: '',
|
exception_goto: '',
|
||||||
tools: [],
|
tools: [],
|
||||||
|
mcp: [],
|
||||||
outputs: {
|
outputs: {
|
||||||
structured_output: {
|
structured_output: {
|
||||||
// topic: {
|
// topic: {
|
||||||
|
|||||||
@ -5,12 +5,14 @@ import { PencilLine, X } from 'lucide-react';
|
|||||||
import { PropsWithChildren, useCallback, useContext, useMemo } from 'react';
|
import { 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 { INextOperatorForm } from '../../interface';
|
import { INextOperatorForm } from '../../interface';
|
||||||
import useGraphStore from '../../store';
|
import useGraphStore from '../../store';
|
||||||
import { filterDownstreamAgentNodeIds } from '../../utils/filter-downstream-nodes';
|
import { filterDownstreamAgentNodeIds } from '../../utils/filter-downstream-nodes';
|
||||||
import { ToolPopover } from './tool-popover';
|
import { ToolPopover } from './tool-popover';
|
||||||
|
import { useDeleteAgentNodeMCP } from './tool-popover/use-update-mcp';
|
||||||
import { useDeleteAgentNodeTools } from './tool-popover/use-update-tools';
|
import { useDeleteAgentNodeTools } from './tool-popover/use-update-tools';
|
||||||
import { useGetAgentToolNames } from './use-get-tools';
|
import { useGetAgentMCPIds, useGetAgentToolNames } from './use-get-tools';
|
||||||
|
|
||||||
export function ToolCard({
|
export function ToolCard({
|
||||||
children,
|
children,
|
||||||
@ -59,6 +61,9 @@ function ActionButton<T>({ edit, deleteRecord, record }: ActionButtonProps<T>) {
|
|||||||
export function AgentTools() {
|
export function AgentTools() {
|
||||||
const { toolNames } = useGetAgentToolNames();
|
const { toolNames } = useGetAgentToolNames();
|
||||||
const { deleteNodeTool } = useDeleteAgentNodeTools();
|
const { deleteNodeTool } = useDeleteAgentNodeTools();
|
||||||
|
const { mcpIds } = useGetAgentMCPIds();
|
||||||
|
const { findMcpById } = useFindMcpById();
|
||||||
|
const { deleteNodeMCP } = useDeleteAgentNodeMCP();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="space-y-2.5">
|
<section className="space-y-2.5">
|
||||||
@ -74,6 +79,16 @@ export function AgentTools() {
|
|||||||
></ActionButton>
|
></ActionButton>
|
||||||
</ToolCard>
|
</ToolCard>
|
||||||
))}
|
))}
|
||||||
|
{mcpIds.map((id) => (
|
||||||
|
<ToolCard key={id}>
|
||||||
|
{findMcpById(id)?.name}
|
||||||
|
<ActionButton
|
||||||
|
record={id}
|
||||||
|
edit={() => {}}
|
||||||
|
deleteRecord={deleteNodeMCP(id)}
|
||||||
|
></ActionButton>
|
||||||
|
</ToolCard>
|
||||||
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<ToolPopover>
|
<ToolPopover>
|
||||||
<BlockButton>Add Tool</BlockButton>
|
<BlockButton>Add Tool</BlockButton>
|
||||||
|
|||||||
@ -3,15 +3,22 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/ui/popover';
|
} from '@/components/ui/popover';
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Operator } from '@/pages/agent/constant';
|
import { Operator } from '@/pages/agent/constant';
|
||||||
import { AgentFormContext, AgentInstanceContext } from '@/pages/agent/context';
|
import { AgentFormContext, AgentInstanceContext } from '@/pages/agent/context';
|
||||||
import useGraphStore from '@/pages/agent/store';
|
import useGraphStore from '@/pages/agent/store';
|
||||||
import { Position } from '@xyflow/react';
|
import { Position } from '@xyflow/react';
|
||||||
import { PropsWithChildren, useCallback, useContext } from 'react';
|
import { PropsWithChildren, useCallback, useContext, useEffect } from 'react';
|
||||||
import { useGetAgentToolNames } from '../use-get-tools';
|
import { useGetAgentMCPIds, useGetAgentToolNames } from '../use-get-tools';
|
||||||
import { ToolCommand } from './tool-command';
|
import { MCPCommand, ToolCommand } from './tool-command';
|
||||||
|
import { useUpdateAgentNodeMCP } from './use-update-mcp';
|
||||||
import { useUpdateAgentNodeTools } from './use-update-tools';
|
import { useUpdateAgentNodeTools } from './use-update-tools';
|
||||||
|
|
||||||
|
enum ToolType {
|
||||||
|
Common = 'common',
|
||||||
|
MCP = 'mcp',
|
||||||
|
}
|
||||||
|
|
||||||
export function ToolPopover({ children }: PropsWithChildren) {
|
export function ToolPopover({ children }: PropsWithChildren) {
|
||||||
const { addCanvasNode } = useContext(AgentInstanceContext);
|
const { addCanvasNode } = useContext(AgentInstanceContext);
|
||||||
const node = useContext(AgentFormContext);
|
const node = useContext(AgentFormContext);
|
||||||
@ -20,29 +27,57 @@ export function ToolPopover({ children }: PropsWithChildren) {
|
|||||||
const deleteAgentToolNodeById = useGraphStore(
|
const deleteAgentToolNodeById = useGraphStore(
|
||||||
(state) => state.deleteAgentToolNodeById,
|
(state) => state.deleteAgentToolNodeById,
|
||||||
);
|
);
|
||||||
|
const { mcpIds } = useGetAgentMCPIds();
|
||||||
|
const { updateNodeMCP } = useUpdateAgentNodeMCP();
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(value: string[]) => {
|
(value: string[]) => {
|
||||||
if (Array.isArray(value) && node?.id) {
|
if (Array.isArray(value) && node?.id) {
|
||||||
updateNodeTools(value);
|
updateNodeTools(value);
|
||||||
if (value.length > 0) {
|
}
|
||||||
|
},
|
||||||
|
[node?.id, updateNodeTools],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const total = toolNames.length + mcpIds.length;
|
||||||
|
if (node?.id) {
|
||||||
|
if (total > 0) {
|
||||||
addCanvasNode(Operator.Tool, {
|
addCanvasNode(Operator.Tool, {
|
||||||
position: Position.Bottom,
|
position: Position.Bottom,
|
||||||
nodeId: node?.id,
|
nodeId: node?.id,
|
||||||
})();
|
})();
|
||||||
} else {
|
} else {
|
||||||
deleteAgentToolNodeById(node.id); // TODO: The tool node should be derived from the agent tools data
|
deleteAgentToolNodeById(node.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}, [
|
||||||
[addCanvasNode, deleteAgentToolNodeById, node?.id, updateNodeTools],
|
addCanvasNode,
|
||||||
);
|
deleteAgentToolNodeById,
|
||||||
|
mcpIds.length,
|
||||||
|
node?.id,
|
||||||
|
toolNames.length,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
||||||
<PopoverContent className="w-80 p-0">
|
<PopoverContent className="w-80 p-4">
|
||||||
<ToolCommand onChange={handleChange} value={toolNames}></ToolCommand>
|
<Tabs defaultValue={ToolType.Common}>
|
||||||
|
<TabsList>
|
||||||
|
<TabsTrigger value={ToolType.Common}>Built-in</TabsTrigger>
|
||||||
|
<TabsTrigger value={ToolType.MCP}>MCP</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value={ToolType.Common}>
|
||||||
|
<ToolCommand
|
||||||
|
onChange={handleChange}
|
||||||
|
value={toolNames}
|
||||||
|
></ToolCommand>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value={ToolType.MCP}>
|
||||||
|
<MCPCommand value={mcpIds} onChange={updateNodeMCP}></MCPCommand>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,9 +8,10 @@ import {
|
|||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
} from '@/components/ui/command';
|
} from '@/components/ui/command';
|
||||||
|
import { useListMcpServer } from '@/hooks/use-mcp-request';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Operator } from '@/pages/agent/constant';
|
import { Operator } from '@/pages/agent/constant';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { PropsWithChildren, useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
const Menus = [
|
const Menus = [
|
||||||
{
|
{
|
||||||
@ -52,7 +53,36 @@ type ToolCommandProps = {
|
|||||||
onChange?(values: string[]): void;
|
onChange?(values: string[]): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ToolCommand({ value, onChange }: ToolCommandProps) {
|
type ToolCommandItemProps = {
|
||||||
|
toggleOption(id: string): void;
|
||||||
|
id: string;
|
||||||
|
isSelected: boolean;
|
||||||
|
} & ToolCommandProps;
|
||||||
|
|
||||||
|
function ToolCommandItem({
|
||||||
|
toggleOption,
|
||||||
|
id,
|
||||||
|
isSelected,
|
||||||
|
children,
|
||||||
|
}: ToolCommandItemProps & PropsWithChildren) {
|
||||||
|
return (
|
||||||
|
<CommandItem className="cursor-pointer" onSelect={() => toggleOption(id)}>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
|
||||||
|
isSelected
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: 'opacity-50 [&_svg]:invisible',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CheckIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</CommandItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useHandleSelectChange({ onChange, value }: ToolCommandProps) {
|
||||||
const [currentValue, setCurrentValue] = useState<string[]>([]);
|
const [currentValue, setCurrentValue] = useState<string[]>([]);
|
||||||
|
|
||||||
const toggleOption = useCallback(
|
const toggleOption = useCallback(
|
||||||
@ -72,8 +102,20 @@ export function ToolCommand({ value, onChange }: ToolCommandProps) {
|
|||||||
}
|
}
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
toggleOption,
|
||||||
|
currentValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ToolCommand({ value, onChange }: ToolCommandProps) {
|
||||||
|
const { toggleOption, currentValue } = useHandleSelectChange({
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Command className="rounded-lg border shadow-md md:min-w-[450px]">
|
<Command>
|
||||||
<CommandInput placeholder="Type a command or search..." />
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>No results found.</CommandEmpty>
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
@ -82,28 +124,17 @@ export function ToolCommand({ value, onChange }: ToolCommandProps) {
|
|||||||
{x.list.map((y) => {
|
{x.list.map((y) => {
|
||||||
const isSelected = currentValue.includes(y);
|
const isSelected = currentValue.includes(y);
|
||||||
return (
|
return (
|
||||||
<CommandItem
|
<ToolCommandItem
|
||||||
key={y}
|
key={y}
|
||||||
className="cursor-pointer"
|
id={y}
|
||||||
onSelect={() => toggleOption(y)}
|
toggleOption={toggleOption}
|
||||||
|
isSelected={isSelected}
|
||||||
>
|
>
|
||||||
<div
|
<>
|
||||||
className={cn(
|
|
||||||
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
|
|
||||||
isSelected
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'opacity-50 [&_svg]:invisible',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<CheckIcon className="h-4 w-4" />
|
|
||||||
</div>
|
|
||||||
{/* {option.icon && (
|
|
||||||
<option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
|
|
||||||
)} */}
|
|
||||||
{/* <span>{option.label}</span> */}
|
|
||||||
<Calendar />
|
<Calendar />
|
||||||
<span>{y}</span>
|
<span>{y}</span>
|
||||||
</CommandItem>
|
</>
|
||||||
|
</ToolCommandItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
@ -112,3 +143,34 @@ export function ToolCommand({ value, onChange }: ToolCommandProps) {
|
|||||||
</Command>
|
</Command>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MCPCommand({ onChange, value }: ToolCommandProps) {
|
||||||
|
const { data } = useListMcpServer();
|
||||||
|
const { toggleOption, currentValue } = useHandleSelectChange({
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
{data.mcp_servers.map((item) => {
|
||||||
|
const isSelected = currentValue.includes(item.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolCommandItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
isSelected={isSelected}
|
||||||
|
toggleOption={toggleOption}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</ToolCommandItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,74 @@
|
|||||||
|
import { useListMcpServer } from '@/hooks/use-mcp-request';
|
||||||
|
import { IAgentForm } from '@/interfaces/database/agent';
|
||||||
|
import { AgentFormContext } from '@/pages/agent/context';
|
||||||
|
import useGraphStore from '@/pages/agent/store';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
import { useCallback, useContext, useMemo } from 'react';
|
||||||
|
|
||||||
|
export function useGetNodeMCP() {
|
||||||
|
const node = useContext(AgentFormContext);
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
const mcp: IAgentForm['mcp'] = get(node, 'data.form.mcp');
|
||||||
|
return mcp;
|
||||||
|
}, [node]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUpdateAgentNodeMCP() {
|
||||||
|
const { updateNodeForm } = useGraphStore((state) => state);
|
||||||
|
const node = useContext(AgentFormContext);
|
||||||
|
const mcpList = useGetNodeMCP();
|
||||||
|
const { data } = useListMcpServer();
|
||||||
|
const mcpServers = data.mcp_servers;
|
||||||
|
|
||||||
|
const findMcpTools = useCallback(
|
||||||
|
(mcpId: string) => {
|
||||||
|
const mcp = mcpServers.find((x) => x.id === mcpId);
|
||||||
|
return mcp?.variables.tools;
|
||||||
|
},
|
||||||
|
[mcpServers],
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateNodeMCP = useCallback(
|
||||||
|
(value: string[]) => {
|
||||||
|
if (node?.id) {
|
||||||
|
const nextValue = value.reduce<IAgentForm['mcp']>((pre, cur) => {
|
||||||
|
const mcp = mcpList.find((x) => x.mcp_id === cur);
|
||||||
|
const tools = findMcpTools(cur);
|
||||||
|
if (mcp) {
|
||||||
|
pre.push(mcp);
|
||||||
|
} else if (tools) {
|
||||||
|
pre.push({
|
||||||
|
mcp_id: cur,
|
||||||
|
tools,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return pre;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
updateNodeForm(node?.id, nextValue, ['mcp']);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[node?.id, updateNodeForm, mcpList, findMcpTools],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { updateNodeMCP };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDeleteAgentNodeMCP() {
|
||||||
|
const { updateNodeForm } = useGraphStore((state) => state);
|
||||||
|
const mcpList = useGetNodeMCP();
|
||||||
|
const node = useContext(AgentFormContext);
|
||||||
|
|
||||||
|
const deleteNodeMCP = useCallback(
|
||||||
|
(value: string) => () => {
|
||||||
|
const nextMCP = mcpList.filter((x) => x.mcp_id !== value);
|
||||||
|
if (node?.id) {
|
||||||
|
updateNodeForm(node?.id, nextMCP, ['mcp']);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[node?.id, mcpList, updateNodeForm],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { deleteNodeMCP };
|
||||||
|
}
|
||||||
@ -45,35 +45,22 @@ export function useUpdateAgentNodeTools() {
|
|||||||
[node?.id, tools, updateNodeForm],
|
[node?.id, tools, updateNodeForm],
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteNodeTool = useCallback(
|
return { updateNodeTools };
|
||||||
(value: string) => {
|
|
||||||
updateNodeTools([value]);
|
|
||||||
},
|
|
||||||
[updateNodeTools],
|
|
||||||
);
|
|
||||||
|
|
||||||
return { updateNodeTools, deleteNodeTool };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDeleteAgentNodeTools() {
|
export function useDeleteAgentNodeTools() {
|
||||||
const { updateNodeForm } = useGraphStore((state) => state);
|
const { updateNodeForm } = useGraphStore((state) => state);
|
||||||
const tools = useGetNodeTools();
|
const tools = useGetNodeTools();
|
||||||
const node = useContext(AgentFormContext);
|
const node = useContext(AgentFormContext);
|
||||||
const deleteAgentToolNodeById = useGraphStore(
|
|
||||||
(state) => state.deleteAgentToolNodeById,
|
|
||||||
);
|
|
||||||
|
|
||||||
const deleteNodeTool = useCallback(
|
const deleteNodeTool = useCallback(
|
||||||
(value: string) => () => {
|
(value: string) => () => {
|
||||||
const nextTools = tools.filter((x) => x.component_name !== value);
|
const nextTools = tools.filter((x) => x.component_name !== value);
|
||||||
if (node?.id) {
|
if (node?.id) {
|
||||||
updateNodeForm(node?.id, nextTools, ['tools']);
|
updateNodeForm(node?.id, nextTools, ['tools']);
|
||||||
if (nextTools.length === 0) {
|
|
||||||
deleteAgentToolNodeById(node?.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[deleteAgentToolNodeById, node?.id, tools, updateNodeForm],
|
[node?.id, tools, updateNodeForm],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { deleteNodeTool };
|
return { deleteNodeTool };
|
||||||
|
|||||||
@ -13,3 +13,14 @@ export function useGetAgentToolNames() {
|
|||||||
|
|
||||||
return { toolNames };
|
return { toolNames };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useGetAgentMCPIds() {
|
||||||
|
const node = useContext(AgentFormContext);
|
||||||
|
|
||||||
|
const mcpIds = useMemo(() => {
|
||||||
|
const ids: IAgentForm['mcp'] = get(node, 'data.form.mcp', []);
|
||||||
|
return ids.map((x) => x.mcp_id);
|
||||||
|
}, [node]);
|
||||||
|
|
||||||
|
return { mcpIds };
|
||||||
|
}
|
||||||
|
|||||||
12
web/src/pages/agent/hooks/use-find-mcp-by-id.ts
Normal file
12
web/src/pages/agent/hooks/use-find-mcp-by-id.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { useListMcpServer } from '@/hooks/use-mcp-request';
|
||||||
|
|
||||||
|
export function useFindMcpById() {
|
||||||
|
const { data } = useListMcpServer();
|
||||||
|
|
||||||
|
const findMcpById = (id: string) =>
|
||||||
|
data.mcp_servers.find((item) => item.id === id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
findMcpById,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -90,7 +90,7 @@ export default function Dataset() {
|
|||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem onClick={showCreateModal}>
|
<DropdownMenuItem onClick={showCreateModal}>
|
||||||
{t('fileManager.newFolder')}
|
{t('knowledgeDetails.emptyFiles')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@ -15,9 +15,12 @@ import { Input } from '@/components/ui/input';
|
|||||||
import { RAGFlowSelect } from '@/components/ui/select';
|
import { RAGFlowSelect } from '@/components/ui/select';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
|
import { Editor, loader } from '@monaco-editor/react';
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
loader.config({ paths: { vs: '/vs' } });
|
||||||
|
|
||||||
export const FormId = 'EditMcpForm';
|
export const FormId = 'EditMcpForm';
|
||||||
|
|
||||||
export enum ServerType {
|
export enum ServerType {
|
||||||
@ -50,7 +53,7 @@ export function useBuildFormSchema() {
|
|||||||
message: t('common.namePlaceholder'),
|
message: t('common.namePlaceholder'),
|
||||||
})
|
})
|
||||||
.trim(),
|
.trim(),
|
||||||
// variables: z.object({}).optional(),
|
headers: z.record(z.string(), z.any()).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return FormSchema;
|
return FormSchema;
|
||||||
@ -137,6 +140,28 @@ export function EditMcpForm({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="headers"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Headers</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Editor
|
||||||
|
height={200}
|
||||||
|
defaultLanguage="json"
|
||||||
|
theme="vs-dark"
|
||||||
|
{...field}
|
||||||
|
onChange={(value) => {
|
||||||
|
field.onChange(value);
|
||||||
|
setFieldChanged(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user