瀏覽代碼

Feat: Save the agent tool data to the node #3221 (#8364)

### What problem does this PR solve?

Feat: Save the agent tool data to the node #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.19.1
balibabu 4 月之前
父節點
當前提交
403efe81a1
沒有連結到貢獻者的電子郵件帳戶。

+ 24
- 1
web/src/interfaces/database/agent.ts 查看文件

script?: string; script?: string;
} }


export interface IAgentForm {
sys_prompt: string;
prompts: Array<{
role: string;
content: string;
}>;
max_retries: number;
delay_after_error: number;
visual_files_var: string;
max_rounds: number;
exception_method: Nullable<'comment' | 'go'>;
exception_comment: any;
exception_goto: any;
tools: Array<{
component_name: string;
params: Record<string, any>;
}>;
outputs: {
structured_output: Record<string, Record<string, any>>;
content: Record<string, any>;
};
}

export type BaseNodeData<TForm extends any> = { export type BaseNodeData<TForm extends any> = {
label: string; // operator type label: string; // operator type
name: string; // operator name name: string; // operator name
export type IKeywordNode = BaseNode; export type IKeywordNode = BaseNode;
export type ICodeNode = BaseNode<ICodeForm>; export type ICodeNode = BaseNode<ICodeForm>;
export type IAgentNode = BaseNode; export type IAgentNode = BaseNode;
export type IToolNode = BaseNode;
export type IToolNode = BaseNode<IAgentForm>;


export type RAGFlowNodeType = export type RAGFlowNodeType =
| IBeginNode | IBeginNode

+ 18
- 1
web/src/pages/agent/canvas/node/tool-node.tsx 查看文件

import { IToolNode } from '@/interfaces/database/agent';
import { IAgentForm, IToolNode } from '@/interfaces/database/agent';
import { Handle, NodeProps, Position } from '@xyflow/react'; import { Handle, NodeProps, Position } from '@xyflow/react';
import { get } from 'lodash';
import { memo } from 'react'; import { memo } from 'react';
import { NodeHandleId } from '../../constant'; import { NodeHandleId } from '../../constant';
import useGraphStore from '../../store';
import { NodeWrapper } from './node-wrapper'; import { NodeWrapper } from './node-wrapper';


function InnerToolNode({ function InnerToolNode({
isConnectable = true, isConnectable = true,
selected, selected,
}: NodeProps<IToolNode>) { }: NodeProps<IToolNode>) {
const { edges, getNode } = useGraphStore((state) => state);
const upstreamAgentNodeId = edges.find((x) => x.target === id)?.source;
const upstreamAgentNode = getNode(upstreamAgentNodeId);

const tools: IAgentForm['tools'] = get(
upstreamAgentNode,
'data.form.tools',
[],
);

return ( return (
<NodeWrapper> <NodeWrapper>
<Handle <Handle
position={Position.Top} position={Position.Top}
isConnectable={isConnectable} isConnectable={isConnectable}
></Handle> ></Handle>
<ul className="space-y-1">
{tools.map((x) => (
<li key={x.component_name}>{x.component_name}</li>
))}
</ul>
</NodeWrapper> </NodeWrapper>
); );
} }

+ 6
- 0
web/src/pages/agent/form-sheet/use-form-config-map.tsx 查看文件

import RewriteQuestionForm from '../form/rewrite-question-form'; import RewriteQuestionForm from '../form/rewrite-question-form';
import SwitchForm from '../form/switch-form'; import SwitchForm from '../form/switch-form';
import TemplateForm from '../form/template-form'; import TemplateForm from '../form/template-form';
import ToolForm from '../form/tool-form';
import TuShareForm from '../form/tushare-form'; import TuShareForm from '../form/tushare-form';
import WenCaiForm from '../form/wencai-form'; import WenCaiForm from '../form/wencai-form';
import WikipediaForm from '../form/wikipedia-form'; import WikipediaForm from '../form/wikipedia-form';
defaultValues: {}, defaultValues: {},
schema: z.object({}), schema: z.object({}),
}, },
[Operator.Tool]: {
component: ToolForm,
defaultValues: {},
schema: z.object({}),
},
}; };


return FormConfigMap; return FormConfigMap;

+ 14
- 4
web/src/pages/agent/form/agent-form/tool-popover/index.tsx 查看文件

PopoverContent, PopoverContent,
PopoverTrigger, PopoverTrigger,
} from '@/components/ui/popover'; } from '@/components/ui/popover';
import { IAgentForm } from '@/interfaces/database/agent';
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 { Position } from '@xyflow/react'; import { Position } from '@xyflow/react';
import { PropsWithChildren, useCallback, useContext } from 'react';
import { get } from 'lodash';
import { PropsWithChildren, useCallback, useContext, useMemo } from 'react';
import { ToolCommand } from './tool-command'; import { ToolCommand } from './tool-command';
import { useUpdateAgentNodeTools } from './use-update-tools';


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);
const { updateNodeTools } = useUpdateAgentNodeTools();

const toolNames = useMemo(() => {
const tools: IAgentForm['tools'] = get(node, 'data.form.tools', []);
return tools.map((x) => x.component_name);
}, [node]);


const handleChange = useCallback( const handleChange = useCallback(
(value: string[]) => { (value: string[]) => {
if (Array.isArray(value) && value.length > 0) {
if (Array.isArray(value) && value.length > 0 && node?.id) {
updateNodeTools(value);
addCanvasNode(Operator.Tool, { addCanvasNode(Operator.Tool, {
position: Position.Bottom, position: Position.Bottom,
nodeId: node?.id, nodeId: node?.id,
})(); })();
} }
}, },
[addCanvasNode, node?.id],
[addCanvasNode, node?.id, updateNodeTools],
); );


return ( return (
<Popover> <Popover>
<PopoverTrigger asChild>{children}</PopoverTrigger> <PopoverTrigger asChild>{children}</PopoverTrigger>
<PopoverContent className="w-80 p-0"> <PopoverContent className="w-80 p-0">
<ToolCommand onChange={handleChange}></ToolCommand>
<ToolCommand onChange={handleChange} value={toolNames}></ToolCommand>
</PopoverContent> </PopoverContent>
</Popover> </Popover>
); );

+ 0
- 6
web/src/pages/agent/form/agent-form/tool-popover/tool-command.tsx 查看文件

}, },
]; ];


const Options = Menus.reduce<string[]>((pre, cur) => {
pre.push(...cur.list);
return pre;
}, []);

type ToolCommandProps = { type ToolCommandProps = {
value?: string[]; value?: string[];
onChange?(values: string[]): void; onChange?(values: string[]): void;


export function ToolCommand({ value, onChange }: ToolCommandProps) { export function ToolCommand({ value, onChange }: ToolCommandProps) {
const [currentValue, setCurrentValue] = useState<string[]>([]); const [currentValue, setCurrentValue] = useState<string[]>([]);
console.log('🚀 ~ ToolCommand ~ currentValue:', currentValue);


const toggleOption = useCallback( const toggleOption = useCallback(
(option: string) => { (option: string) => {

+ 29
- 0
web/src/pages/agent/form/agent-form/tool-popover/use-update-tools.ts 查看文件

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 } from 'react';

export function useUpdateAgentNodeTools() {
const { updateNodeForm } = useGraphStore((state) => state);
const node = useContext(AgentFormContext);

const updateNodeTools = useCallback(
(value: string[]) => {
if (node?.id) {
const tools: IAgentForm['tools'] = get(node, 'data.form.tools');

const nextValue = value.reduce<IAgentForm['tools']>((pre, cur) => {
const tool = tools.find((x) => x.component_name === cur);
pre.push(tool ? tool : { component_name: cur, params: {} });
return pre;
}, []);

updateNodeForm(node?.id, nextValue, ['tools']);
}
},
[node, updateNodeForm],
);

return { updateNodeTools };
}

Loading…
取消
儲存