From 1b8ec6710aeadc93f636ee027f9f64f7296be719 Mon Sep 17 00:00:00 2001 From: AkaraChen Date: Thu, 2 Jan 2025 11:29:10 +0800 Subject: [PATCH] feat: agent node checklist --- .../workflow/hooks/use-checklist.ts | 15 +++++++++- .../components/workflow/hooks/use-workflow.ts | 20 ++++++++++++- .../components/agent-strategy-selector.tsx | 11 ++++--- .../nodes/_base/components/agent-strategy.tsx | 9 ++++-- .../nodes/_base/components/setting-item.tsx | 8 +++-- .../nodes/agent/components/tool-icon.tsx | 8 +++-- .../workflow/nodes/agent/default.ts | 30 ++++++++++++++----- .../components/workflow/nodes/agent/node.tsx | 6 ++-- .../components/workflow/nodes/agent/panel.tsx | 8 +++-- .../components/workflow/nodes/agent/types.ts | 1 + .../components/workflow/nodes/agent/utils.ts | 5 ---- web/app/components/workflow/store.ts | 5 ++++ web/service/strategy.ts | 10 +++++++ web/service/use-strategy.ts | 6 ++-- 14 files changed, 107 insertions(+), 35 deletions(-) delete mode 100644 web/app/components/workflow/nodes/agent/utils.ts create mode 100644 web/service/strategy.ts diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts index 36201ddfef..a018ce7cfe 100644 --- a/web/app/components/workflow/hooks/use-checklist.ts +++ b/web/app/components/workflow/hooks/use-checklist.ts @@ -24,6 +24,7 @@ import { useNodesExtraData } from './use-nodes-data' import { useToastContext } from '@/app/components/base/toast' import { CollectionType } from '@/app/components/tools/types' import { useGetLanguage } from '@/context/i18n' +import type { AgentNodeType } from '../nodes/agent/types' export const useChecklist = (nodes: Node[], edges: Edge[]) => { const { t } = useTranslation() @@ -33,6 +34,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { const buildInTools = useStore(s => s.buildInTools) const customTools = useStore(s => s.customTools) const workflowTools = useStore(s => s.workflowTools) + const agentStrategies = useStore(s => s.agentStrategies) const needWarningNodes = useMemo(() => { const list = [] @@ -57,6 +59,17 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { toolIcon = workflowTools.find(tool => tool.id === node.data.provider_id)?.icon } + if (node.data.type === BlockEnum.Agent) { + const data = node.data as AgentNodeType + const provider = agentStrategies.find(s => s.plugin_unique_identifier === data.plugin_unique_identifier) + const strategy = provider?.declaration.strategies.find(s => s.identity.name === data.agent_strategy_name) + // debugger + moreDataForCheckValid = { + provider, + strategy, + } + } + if (node.type === CUSTOM_NODE) { const { errorMessage } = nodesExtraData[node.data.type].checkValid(node.data, t, moreDataForCheckValid) @@ -92,7 +105,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { } return list - }, [t, nodes, edges, nodesExtraData, buildInTools, customTools, workflowTools, language, isChatMode]) + }, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, agentStrategies]) return needWarningNodes } diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index 0f6ae59b6e..8ce31b8acf 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -58,6 +58,7 @@ import I18n from '@/context/i18n' import { CollectionType } from '@/app/components/tools/types' import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' import { useWorkflowConfig } from '@/service/use-workflow' +import { fetchStrategyList } from '@/service/strategy' export const useIsChatMode = () => { const appDetail = useAppStore(s => s.appDetail) @@ -459,6 +460,21 @@ export const useFetchToolsData = () => { } } +export const useFetchAgentStrategy = () => { + const workflowStore = useWorkflowStore() + const handleFetchAllAgentStrategies = useCallback(async () => { + const agentStrategies = await fetchStrategyList() + + workflowStore.setState({ + agentStrategies: agentStrategies || [], + }) + }, [workflowStore]) + + return { + handleFetchAllAgentStrategies, + } +} + export const useWorkflowInit = () => { const workflowStore = useWorkflowStore() const { @@ -466,6 +482,7 @@ export const useWorkflowInit = () => { edges: edgesTemplate, } = useWorkflowTemplate() const { handleFetchAllTools } = useFetchToolsData() + const { handleFetchAllAgentStrategies } = useFetchAgentStrategy() const appDetail = useAppStore(state => state.appDetail)! const setSyncWorkflowDraftHash = useStore(s => s.setSyncWorkflowDraftHash) const [data, setData] = useState() @@ -545,7 +562,8 @@ export const useWorkflowInit = () => { handleFetchAllTools('builtin') handleFetchAllTools('custom') handleFetchAllTools('workflow') - }, [handleFetchPreloadData, handleFetchAllTools]) + handleFetchAllAgentStrategies() + }, [handleFetchPreloadData, handleFetchAllTools, handleFetchAllAgentStrategies]) useEffect(() => { if (data) { diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index 9a4944080b..1cf9fc23ef 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -1,5 +1,5 @@ import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' -import { useMemo, useState } from 'react' +import { memo, useMemo, useState } from 'react' import type { Strategy } from './agent-strategy' import classNames from '@/utils/classnames' import { RiArrowDownSLine, RiArrowRightUpLine, RiErrorWarningFill } from '@remixicon/react' @@ -38,7 +38,7 @@ const ExternalNotInstallWarn = () => { function formatStrategy(input: StrategyPluginDetail[], getIcon: (i: string) => string): ToolWithProvider[] { return input.map((item) => { const res: ToolWithProvider = { - id: item.provider, + id: item.plugin_unique_identifier, author: item.declaration.identity.author, name: item.declaration.identity.name, description: item.declaration.identity.description as any, @@ -69,7 +69,7 @@ export type AgentStrategySelectorProps = { onChange: (value?: Strategy) => void, } -export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { +export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => { const { value, onChange } = props const [open, setOpen] = useState(false) const [viewType, setViewType] = useState(ViewType.flat) @@ -126,6 +126,7 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { agent_strategy_provider_name: tool!.provider_name, agent_strategy_label: tool!.tool_label, agent_output_schema: tool!.output_schema, + plugin_unique_identifier: tool!.provider_id, }) setOpen(false) }} @@ -147,4 +148,6 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { */} -} +}) + +AgentStrategySelector.displayName = 'AgentStrategySelector' diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 454c84833b..b92f4e9d7a 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -12,7 +12,7 @@ import Slider from '@/app/components/base/slider' import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector' import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector' import Field from './field' -import type { ComponentProps } from 'react' +import { type ComponentProps, memo } from 'react' import { useDefaultModel, useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import Editor from './prompt/editor' import { useWorkflowStore } from '../../../store' @@ -22,6 +22,7 @@ export type Strategy = { agent_strategy_name: string agent_strategy_label: string agent_output_schema: Record + plugin_unique_identifier: string } export type AgentStrategyProps = { @@ -47,7 +48,7 @@ type StringSchema = CustomSchema<'string', { type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema | StringSchema -export const AgentStrategy = (props: AgentStrategyProps) => { +export const AgentStrategy = memo((props: AgentStrategyProps) => { const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange } = props const { t } = useTranslation() const language = useLanguage() @@ -197,4 +198,6 @@ export const AgentStrategy = (props: AgentStrategyProps) => { /> } -} +}) + +AgentStrategy.displayName = 'AgentStrategy' diff --git a/web/app/components/workflow/nodes/_base/components/setting-item.tsx b/web/app/components/workflow/nodes/_base/components/setting-item.tsx index fdaadc476f..ca074ffbb7 100644 --- a/web/app/components/workflow/nodes/_base/components/setting-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/setting-item.tsx @@ -1,7 +1,7 @@ import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import classNames from '@/utils/classnames' -import type { ComponentProps, PropsWithChildren, ReactNode } from 'react' +import { type ComponentProps, type PropsWithChildren, type ReactNode, memo } from 'react' export type SettingItemProps = PropsWithChildren<{ label: string @@ -9,7 +9,7 @@ export type SettingItemProps = PropsWithChildren<{ tooltip?: ReactNode }> -export const SettingItem = ({ label, children, status, tooltip }: SettingItemProps) => { +export const SettingItem = memo(({ label, children, status, tooltip }: SettingItemProps) => { const indicator: ComponentProps['color'] = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined const needTooltip = ['error', 'warning'].includes(status as any) return
@@ -23,4 +23,6 @@ export const SettingItem = ({ label, children, status, tooltip }: SettingItemPro {indicator && }
-} +}) + +SettingItem.displayName = 'SettingItem' diff --git a/web/app/components/workflow/nodes/agent/components/tool-icon.tsx b/web/app/components/workflow/nodes/agent/components/tool-icon.tsx index 893472af5e..4228c5694d 100644 --- a/web/app/components/workflow/nodes/agent/components/tool-icon.tsx +++ b/web/app/components/workflow/nodes/agent/components/tool-icon.tsx @@ -1,7 +1,7 @@ import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import classNames from '@/utils/classnames' -import { useMemo, useRef } from 'react' +import { memo, useMemo, useRef } from 'react' import { useAllBuiltInTools, useAllCustomTools, useAllWorkflowTools } from '@/service/use-tools' export type ToolIconProps = { @@ -10,7 +10,7 @@ export type ToolIconProps = { providerName: string } -export const ToolIcon = ({ status, tooltip, providerName }: ToolIconProps) => { +export const ToolIcon = memo(({ status, tooltip, providerName }: ToolIconProps) => { const indicator = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined const containerRef = useRef(null) const notSuccess = (['error', 'warning'] as Array).includes(status) @@ -41,4 +41,6 @@ export const ToolIcon = ({ status, tooltip, providerName }: ToolIconProps) => { {indicator && } -} +}) + +ToolIcon.displayName = 'ToolIcon' diff --git a/web/app/components/workflow/nodes/agent/default.ts b/web/app/components/workflow/nodes/agent/default.ts index c748379cef..d7caba3b72 100644 --- a/web/app/components/workflow/nodes/agent/default.ts +++ b/web/app/components/workflow/nodes/agent/default.ts @@ -1,3 +1,4 @@ +import type { StrategyDetail, StrategyPluginDetail } from '@/app/components/plugins/types' import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '../../constants' import type { NodeDefault } from '../../types' import type { AgentNodeType } from './types' @@ -15,16 +16,29 @@ const nodeDefault: NodeDefault = { ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS }, - checkValid(payload, t, moreDataForCheckValid) { - let isValid = true - let errorMessages = '' - if (payload.type) { - isValid = true - errorMessages = '' + checkValid(payload, t, moreDataForCheckValid: { + strategyProvider: StrategyPluginDetail | undefined, + strategy: StrategyDetail | undefined + }) { + const { strategy } = moreDataForCheckValid + if (!strategy) { + return { + isValid: false, + errorMessage: 'Please select a strategy', + } } + for (const param of strategy.parameters) { + if (param.required && !payload.agent_parameters?.[param.name]?.value) { + return { + isValid: false, + errorMessage: `Please select ${param.name}`, + } + } + } + // TODO: tool selector valid? return { - isValid, - errorMessage: errorMessages, + isValid: true, + errorMessage: '', } }, } diff --git a/web/app/components/workflow/nodes/agent/node.tsx b/web/app/components/workflow/nodes/agent/node.tsx index cef0a4330b..15580f625e 100644 --- a/web/app/components/workflow/nodes/agent/node.tsx +++ b/web/app/components/workflow/nodes/agent/node.tsx @@ -1,4 +1,4 @@ -import { type FC, useMemo } from 'react' +import { type FC, memo, useMemo } from 'react' import type { NodeProps } from '../../types' import type { AgentNodeType } from './types' import { SettingItem } from '../_base/components/setting-item' @@ -126,4 +126,6 @@ const AgentNode: FC> = (props) => { } -export default AgentNode +AgentNode.displayName = 'AgentNode' + +export default memo(AgentNode) diff --git a/web/app/components/workflow/nodes/agent/panel.tsx b/web/app/components/workflow/nodes/agent/panel.tsx index af4ce6c863..1843031d42 100644 --- a/web/app/components/workflow/nodes/agent/panel.tsx +++ b/web/app/components/workflow/nodes/agent/panel.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import { useMemo } from 'react' +import { memo, useMemo } from 'react' import type { NodePanelProps } from '../../types' import type { AgentNodeType } from './types' import Field from '../_base/components/field' @@ -75,6 +75,7 @@ const AgentPanel: FC> = (props) => { agent_strategy_name: inputs.agent_strategy_name!, agent_strategy_label: inputs.agent_strategy_label!, agent_output_schema: inputs.output_schema, + plugin_unique_identifier: inputs.plugin_unique_identifier!, } : undefined} onStrategyChange={(strategy) => { setInputs({ @@ -83,6 +84,7 @@ const AgentPanel: FC> = (props) => { agent_strategy_name: strategy?.agent_strategy_name, agent_strategy_label: strategy?.agent_strategy_label, output_schema: strategy!.agent_output_schema, + plugin_unique_identifier: strategy!.plugin_unique_identifier, }) }} formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []} @@ -135,4 +137,6 @@ const AgentPanel: FC> = (props) => { } -export default AgentPanel +AgentPanel.displayName = 'AgentPanel' + +export default memo(AgentPanel) diff --git a/web/app/components/workflow/nodes/agent/types.ts b/web/app/components/workflow/nodes/agent/types.ts index e75079ae8c..1b5c96364f 100644 --- a/web/app/components/workflow/nodes/agent/types.ts +++ b/web/app/components/workflow/nodes/agent/types.ts @@ -7,4 +7,5 @@ export type AgentNodeType = CommonNodeType & { agent_strategy_label?: string agent_parameters?: ToolVarInputs output_schema: Record + plugin_unique_identifier?: string } diff --git a/web/app/components/workflow/nodes/agent/utils.ts b/web/app/components/workflow/nodes/agent/utils.ts deleted file mode 100644 index 25beff3eb2..0000000000 --- a/web/app/components/workflow/nodes/agent/utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { AgentNodeType } from './types' - -export const checkNodeValid = (payload: AgentNodeType) => { - return true -} diff --git a/web/app/components/workflow/store.ts b/web/app/components/workflow/store.ts index 6bd47eaa01..b05c6676c0 100644 --- a/web/app/components/workflow/store.ts +++ b/web/app/components/workflow/store.ts @@ -22,6 +22,9 @@ import type { } from './types' import { WorkflowContext } from './context' import type { NodeTracing, VersionHistory } from '@/types/workflow' +import type { + StrategyPluginDetail, +} from '@/app/components/plugins/types' // #TODO chatVar# // const MOCK_DATA = [ @@ -98,6 +101,7 @@ type Shape = { setCustomTools: (tools: ToolWithProvider[]) => void workflowTools: ToolWithProvider[] setWorkflowTools: (tools: ToolWithProvider[]) => void + agentStrategies: StrategyPluginDetail[], clipboardElements: Node[] setClipboardElements: (clipboardElements: Node[]) => void showDebugAndPreviewPanel: boolean @@ -230,6 +234,7 @@ export const createWorkflowStore = () => { setCustomTools: customTools => set(() => ({ customTools })), workflowTools: [], setWorkflowTools: workflowTools => set(() => ({ workflowTools })), + agentStrategies: [], clipboardElements: [], setClipboardElements: clipboardElements => set(() => ({ clipboardElements })), showDebugAndPreviewPanel: false, diff --git a/web/service/strategy.ts b/web/service/strategy.ts new file mode 100644 index 0000000000..bb032ba286 --- /dev/null +++ b/web/service/strategy.ts @@ -0,0 +1,10 @@ +import type { StrategyPluginDetail } from '@/app/components/plugins/types' +import { get } from './base' + +export const fetchStrategyList = () => { + return get('/workspaces/current/agent-providers') +} + +export const fetchStrategyDetail = (agentProvider: string) => { + return get(`/workspaces/current/agent-provider/${agentProvider}`) +} diff --git a/web/service/use-strategy.ts b/web/service/use-strategy.ts index cbc09509b3..49f852ebf5 100644 --- a/web/service/use-strategy.ts +++ b/web/service/use-strategy.ts @@ -1,4 +1,3 @@ -import { get } from './base' import type { StrategyPluginDetail, } from '@/app/components/plugins/types' @@ -6,6 +5,7 @@ import { useInvalid } from './use-base' import { useQuery, } from '@tanstack/react-query' +import { fetchStrategyDetail, fetchStrategyList } from './strategy' const NAME_SPACE = 'agent_strategy' @@ -13,7 +13,7 @@ const useStrategyListKey = [NAME_SPACE, 'strategyList'] export const useStrategyProviders = () => { return useQuery({ queryKey: useStrategyListKey, - queryFn: () => get('/workspaces/current/agent-providers'), + queryFn: fetchStrategyList, }) } @@ -24,7 +24,7 @@ export const useInvalidateStrategyProviders = () => { export const useStrategyProviderDetail = (agentProvider: string) => { return useQuery({ queryKey: [NAME_SPACE, 'detail', agentProvider], - queryFn: () => get(`/workspaces/current/agent-provider/${agentProvider}`), + queryFn: () => fetchStrategyDetail(agentProvider), enabled: !!agentProvider, }) }