diff --git a/web/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list.tsx b/web/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list.tsx index 024444cd6a..7c3ab29c49 100644 --- a/web/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list.tsx +++ b/web/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list.tsx @@ -2,7 +2,7 @@ import { useModelList } from '@/app/components/header/account-setting/model-prov import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useProviderContext } from '@/context/provider-context' import { useInvalidateInstalledPluginList } from '@/service/use-plugins' -import { useInvalidateAllBuiltInTools, useInvalidateAllToolProviders } from '@/service/use-tools' +import { useInvalidateAllBuiltInTools, useInvalidateAllToolProviders, useInvalidateRAGRecommendedPlugins } from '@/service/use-tools' import { useInvalidateStrategyProviders } from '@/service/use-strategy' import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../../types' import { PluginType } from '../../types' @@ -23,6 +23,8 @@ const useRefreshPluginList = () => { const invalidateDataSourceListAuth = useInvalidDataSourceListAuth() const invalidateStrategyProviders = useInvalidateStrategyProviders() + + const invalidateRAGRecommendedPlugins = useInvalidateRAGRecommendedPlugins() return { refreshPluginList: (manifest?: PluginManifestInMarket | Plugin | PluginDeclaration | null, refreshAllType?: boolean) => { // installed list @@ -32,6 +34,7 @@ const useRefreshPluginList = () => { if ((manifest && PluginType.tool.includes(manifest.category)) || refreshAllType) { invalidateAllToolProviders() invalidateAllBuiltInTools() + invalidateRAGRecommendedPlugins() // TODO: update suggested tools. It's a function in hook useMarketplacePlugins,handleUpdatePlugins } diff --git a/web/app/components/plugins/plugin-auth/hooks/use-credential.ts b/web/app/components/plugins/plugin-auth/hooks/use-credential.ts index 5a7a497ad9..9c342f2ced 100644 --- a/web/app/components/plugins/plugin-auth/hooks/use-credential.ts +++ b/web/app/components/plugins/plugin-auth/hooks/use-credential.ts @@ -15,6 +15,7 @@ import { import { useGetApi } from './use-get-api' import type { PluginPayload } from '../types' import type { CredentialTypeEnum } from '../types' +import { useInvalidToolsByType } from '@/service/use-tools' export const useGetPluginCredentialInfoHook = (pluginPayload: PluginPayload, enable?: boolean) => { const apiMap = useGetApi(pluginPayload) @@ -29,8 +30,14 @@ export const useDeletePluginCredentialHook = (pluginPayload: PluginPayload) => { export const useInvalidPluginCredentialInfoHook = (pluginPayload: PluginPayload) => { const apiMap = useGetApi(pluginPayload) + const invalidPluginCredentialInfo = useInvalidPluginCredentialInfo(apiMap.getCredentialInfo) + const providerType = pluginPayload.providerType + const invalidToolsByType = useInvalidToolsByType(providerType) - return useInvalidPluginCredentialInfo(apiMap.getCredentialInfo) + return () => { + invalidPluginCredentialInfo() + invalidToolsByType() + } } export const useSetPluginDefaultCredentialHook = (pluginPayload: PluginPayload) => { diff --git a/web/app/components/plugins/plugin-auth/types.ts b/web/app/components/plugins/plugin-auth/types.ts index 6366c80de3..fb23269b4b 100644 --- a/web/app/components/plugins/plugin-auth/types.ts +++ b/web/app/components/plugins/plugin-auth/types.ts @@ -1,3 +1,5 @@ +import type { CollectionType } from '../../tools/types' + export type { AddApiKeyButtonProps } from './authorize/add-api-key-button' export type { AddOAuthButtonProps } from './authorize/add-oauth-button' @@ -10,6 +12,7 @@ export enum AuthCategory { export type PluginPayload = { category: AuthCategory provider: string + providerType: CollectionType | string } export enum CredentialTypeEnum { diff --git a/web/app/components/workflow/block-selector/all-tools.tsx b/web/app/components/workflow/block-selector/all-tools.tsx index 6a2e07a411..7db8b9acf5 100644 --- a/web/app/components/workflow/block-selector/all-tools.tsx +++ b/web/app/components/workflow/block-selector/all-tools.tsx @@ -25,7 +25,7 @@ import PluginList, { type ListProps } from '@/app/components/workflow/block-sele import { PluginType } from '../../plugins/types' import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' import { useGlobalPublicStore } from '@/context/global-public-context' -import RAGToolSuggestions from './rag-tool-suggestions' +import RAGToolRecommendations from './rag-tool-recommendations' type AllToolsProps = { className?: string @@ -148,7 +148,7 @@ const AllTools = ({ onScroll={pluginRef.current?.handleScroll} > {isShowRAGRecommendations && ( - > } -const RAGToolSuggestions: React.FC = ({ +const RAGToolRecommendations = ({ viewType, onSelect, onTagsChange, -}) => { +}: RAGToolRecommendationsProps) => { const { t } = useTranslation() const { data: ragRecommendedPlugins, + isLoading: isLoadingRAGRecommendedPlugins, isFetching: isFetchingRAGRecommendedPlugins, } = useRAGRecommendedPlugins() const recommendedPlugins = useMemo(() => { if (ragRecommendedPlugins) - return [...ragRecommendedPlugins.installed_recommended_plugins] + return ragRecommendedPlugins.installed_recommended_plugins + return [] + }, [ragRecommendedPlugins]) + + const unInstalledPlugins = useMemo(() => { + if (ragRecommendedPlugins) + return (ragRecommendedPlugins.uninstalled_recommended_plugins).map(getFormattedPlugin) return [] }, [ragRecommendedPlugins]) @@ -48,15 +55,16 @@ const RAGToolSuggestions: React.FC = ({
{t('pipeline.ragToolSuggestions.title')}
- {isFetchingRAGRecommendedPlugins && ( + {/* For first time loading, show loading */} + {isLoadingRAGRecommendedPlugins && (
)} - {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length === 0 && ( + {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length === 0 && unInstalledPlugins.length === 0 && (

= ({ />

)} - {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length > 0 && ( + {(recommendedPlugins.length > 0 || unInstalledPlugins.length > 0) && ( <> -
= ({ ) } -export default React.memo(RAGToolSuggestions) +export default React.memo(RAGToolRecommendations) diff --git a/web/app/components/workflow/block-selector/rag-tool-recommendations/list.tsx b/web/app/components/workflow/block-selector/rag-tool-recommendations/list.tsx new file mode 100644 index 0000000000..19378caf48 --- /dev/null +++ b/web/app/components/workflow/block-selector/rag-tool-recommendations/list.tsx @@ -0,0 +1,102 @@ +import { + useMemo, + useRef, +} from 'react' +import type { BlockEnum, ToolWithProvider } from '../../types' +import type { ToolDefaultValue } from '../types' +import { ViewType } from '../view-type-select' +import { useGetLanguage } from '@/context/i18n' +import { groupItems } from '../index-bar' +import cn from '@/utils/classnames' +import ToolListTreeView from '../tool/tool-list-tree-view/list' +import ToolListFlatView from '../tool/tool-list-flat-view/list' +import UninstalledItem from './uninstalled-item' +import type { Plugin } from '@/app/components/plugins/types' + +type ListProps = { + onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void + tools: ToolWithProvider[] + viewType: ViewType + unInstalledPlugins: Plugin[] + className?: string +} + +const List = ({ + onSelect, + tools, + viewType, + unInstalledPlugins, + className, +}: ListProps) => { + const language = useGetLanguage() + const isFlatView = viewType === ViewType.flat + + const { letters, groups: withLetterAndGroupViewToolsData } = groupItems(tools, tool => tool.label[language][0]) + const treeViewToolsData = useMemo(() => { + const result: Record = {} + Object.keys(withLetterAndGroupViewToolsData).forEach((letter) => { + Object.keys(withLetterAndGroupViewToolsData[letter]).forEach((groupName) => { + if (!result[groupName]) + result[groupName] = [] + result[groupName].push(...withLetterAndGroupViewToolsData[letter][groupName]) + }) + }) + return result + }, [withLetterAndGroupViewToolsData]) + + const listViewToolData = useMemo(() => { + const result: ToolWithProvider[] = [] + letters.forEach((letter) => { + Object.keys(withLetterAndGroupViewToolsData[letter]).forEach((groupName) => { + result.push(...withLetterAndGroupViewToolsData[letter][groupName].map((item) => { + return { + ...item, + letter, + } + })) + }) + }) + + return result + }, [withLetterAndGroupViewToolsData, letters]) + + const toolRefs = useRef({}) + + return ( +
+ {!!tools.length && ( + isFlatView ? ( + + ) : ( + + ) + )} + { + unInstalledPlugins.map((item) => { + return ( + + ) + }) + } +
+ ) +} + +export default List diff --git a/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx b/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx new file mode 100644 index 0000000000..98395ec25a --- /dev/null +++ b/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx @@ -0,0 +1,63 @@ +'use client' +import React from 'react' +import { useContext } from 'use-context-selector' +import { useTranslation } from 'react-i18next' +import type { Plugin } from '@/app/components/plugins/types' +import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' +import I18n from '@/context/i18n' +import { useBoolean } from 'ahooks' +import { BlockEnum } from '../../types' +import BlockIcon from '../../block-icon' + +type UninstalledItemProps = { + payload: Plugin +} + +const UninstalledItem = ({ + payload, +}: UninstalledItemProps) => { + const { t } = useTranslation() + const { locale } = useContext(I18n) + + const getLocalizedText = (obj: Record | undefined) => + obj?.[locale] || obj?.['en-US'] || obj?.en_US || '' + const [isShowInstallModal, { + setTrue: showInstallModal, + setFalse: hideInstallModal, + }] = useBoolean(false) + + return ( +
+ +
+
+ + {getLocalizedText(payload.label)} + + + {payload.org} + +
+
+ {t('plugin.installAction')} +
+ {isShowInstallModal && ( + + )} +
+
+ ) +} +export default React.memo(UninstalledItem) diff --git a/web/app/components/workflow/block-selector/tools.tsx b/web/app/components/workflow/block-selector/tools.tsx index feb34d2651..71ed4092a3 100644 --- a/web/app/components/workflow/block-selector/tools.tsx +++ b/web/app/components/workflow/block-selector/tools.tsx @@ -30,7 +30,7 @@ type ToolsProps = { canChooseMCPTool?: boolean isShowRAGRecommendations?: boolean } -const Blocks = ({ +const Tools = ({ onSelect, canNotSelectMultiple, onSelectMultiple, @@ -146,4 +146,4 @@ const Blocks = ({ ) } -export default memo(Blocks) +export default memo(Tools) diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts index 32945a8927..d29827f273 100644 --- a/web/app/components/workflow/hooks/use-checklist.ts +++ b/web/app/components/workflow/hooks/use-checklist.ts @@ -45,14 +45,19 @@ import { getNodeUsedVars, isSpecialVar } from '../nodes/_base/components/variabl import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { KnowledgeBaseNodeType } from '../nodes/knowledge-base/types' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllWorkflowTools, +} from '@/service/use-tools' export const useChecklist = (nodes: Node[], edges: Edge[]) => { const { t } = useTranslation() const language = useGetLanguage() const { nodesMap: nodesExtraData } = useNodesMetaData() - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() const dataSourceList = useStore(s => s.dataSourceList) const { data: strategyProviders } = useStrategyProviders() const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail) @@ -104,7 +109,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { let usedVars: ValueSelector[] = [] if (node.data.type === BlockEnum.Tool) - moreDataForCheckValid = getToolCheckParams(node.data as ToolNodeType, buildInTools, customTools, workflowTools, language) + moreDataForCheckValid = getToolCheckParams(node.data as ToolNodeType, buildInTools || [], customTools || [], workflowTools || [], language) if (node.data.type === BlockEnum.DataSource) moreDataForCheckValid = getDataSourceCheckParams(node.data as DataSourceNodeType, dataSourceList || [], language) @@ -194,6 +199,9 @@ export const useChecklistBeforePublish = () => { const { getNodesAvailableVarList } = useGetNodesAvailableVarList() const { data: embeddingModelList } = useModelList(ModelTypeEnum.textEmbedding) const { data: rerankModelList } = useModelList(ModelTypeEnum.rerank) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() const getCheckData = useCallback((data: CommonNodeType<{}>, datasets: DataSet[]) => { let checkData = data @@ -221,7 +229,7 @@ export const useChecklistBeforePublish = () => { } as CommonNodeType } return checkData - }, []) + }, [embeddingModelList, rerankModelList]) const handleCheckBeforePublish = useCallback(async () => { const { @@ -230,9 +238,6 @@ export const useChecklistBeforePublish = () => { } = store.getState() const { dataSourceList, - buildInTools, - customTools, - workflowTools, } = workflowStore.getState() const nodes = getNodes() const filteredNodes = nodes.filter(node => node.type === CUSTOM_NODE) @@ -275,7 +280,7 @@ export const useChecklistBeforePublish = () => { let moreDataForCheckValid let usedVars: ValueSelector[] = [] if (node.data.type === BlockEnum.Tool) - moreDataForCheckValid = getToolCheckParams(node.data as ToolNodeType, buildInTools, customTools, workflowTools, language) + moreDataForCheckValid = getToolCheckParams(node.data as ToolNodeType, buildInTools || [], customTools || [], workflowTools || [], language) if (node.data.type === BlockEnum.DataSource) moreDataForCheckValid = getDataSourceCheckParams(node.data as DataSourceNodeType, dataSourceList || [], language) @@ -340,7 +345,7 @@ export const useChecklistBeforePublish = () => { } return true - }, [store, notify, t, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, getStartNodes, workflowStore]) + }, [store, notify, t, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, getStartNodes, workflowStore, buildInTools, customTools, workflowTools]) return { handleCheckBeforePublish, diff --git a/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts b/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts index 1527fb82e2..60f839b93d 100644 --- a/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts +++ b/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts @@ -11,6 +11,12 @@ import useMatchSchemaType, { getMatchedSchemaType } from '../nodes/_base/compone import { toNodeOutputVars } from '../nodes/_base/components/variable/utils' import type { SchemaTypeDefinition } from '@/service/use-common' import { useCallback } from 'react' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' type Params = { flowType: FlowType @@ -27,17 +33,17 @@ export const useSetWorkflowVarsWithValue = ({ const invalidateSysVarValues = useInvalidateSysVarValues(flowType, flowId) const { handleCancelAllNodeSuccessStatus } = useNodesInteractionsWithoutSync() const { schemaTypeDefinitions } = useMatchSchemaType() - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) - const mcpTools = useStore(s => s.mcpTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() const dataSourceList = useStore(s => s.dataSourceList) const allPluginInfoList = { - buildInTools, - customTools, - workflowTools, - mcpTools, - dataSourceList: dataSourceList ?? [], + buildInTools: buildInTools || [], + customTools: customTools || [], + workflowTools: workflowTools || [], + mcpTools: mcpTools || [], + dataSourceList: dataSourceList || [], } const setInspectVarsToStore = (inspectVars: VarInInspect[], passedInAllPluginInfoList?: Record, passedInSchemaTypeDefinitions?: SchemaTypeDefinition[]) => { diff --git a/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts b/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts index f35f0c7dab..6b7acd0a85 100644 --- a/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts +++ b/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts @@ -18,6 +18,12 @@ import type { FlowType } from '@/types/common' import useFLow from '@/service/use-flow' import { useStoreApi } from 'reactflow' import type { SchemaTypeDefinition } from '@/service/use-common' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' type Params = { flowId: string @@ -51,6 +57,11 @@ export const useInspectVarsCrudCommon = ({ const { mutateAsync: doEditInspectorVar } = useEditInspectorVar(flowId) const { handleCancelNodeSuccessStatus } = useNodesInteractionsWithoutSync() const { handleEdgeCancelRunningStatus } = useEdgesInteractionsWithoutSync() + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() + const getNodeInspectVars = useCallback((nodeId: string) => { const { nodesWithInspectVars } = workflowStore.getState() const node = nodesWithInspectVars.find(node => node.nodeId === nodeId) @@ -98,10 +109,6 @@ export const useInspectVarsCrudCommon = ({ const fetchInspectVarValue = useCallback(async (selector: ValueSelector, schemaTypeDefinitions: SchemaTypeDefinition[]) => { const { setNodeInspectVars, - buildInTools, - customTools, - workflowTools, - mcpTools, dataSourceList, } = workflowStore.getState() const nodeId = selector[0] @@ -119,11 +126,11 @@ export const useInspectVarsCrudCommon = ({ const nodeArr = getNodes() const currentNode = nodeArr.find(node => node.id === nodeId) const allPluginInfoList = { - buildInTools, - customTools, - workflowTools, - mcpTools, - dataSourceList: dataSourceList ?? [], + buildInTools: buildInTools || [], + customTools: customTools || [], + workflowTools: workflowTools || [], + mcpTools: mcpTools || [], + dataSourceList: dataSourceList || [], } const currentNodeOutputVars = toNodeOutputVars([currentNode], false, () => true, [], [], [], allPluginInfoList, schemaTypeDefinitions) const vars = await fetchNodeInspectVars(flowType, flowId, nodeId) @@ -135,7 +142,7 @@ export const useInspectVarsCrudCommon = ({ } }) setNodeInspectVars(nodeId, varsWithSchemaType) - }, [workflowStore, flowType, flowId, invalidateSysVarValues, invalidateConversationVarValues]) + }, [workflowStore, flowType, flowId, invalidateSysVarValues, invalidateConversationVarValues, buildInTools, customTools, workflowTools, mcpTools]) // after last run would call this const appendNodeInspectVars = useCallback((nodeId: string, payload: VarInInspect[], allNodes: Node[]) => { diff --git a/web/app/components/workflow/hooks/use-nodes-meta-data.ts b/web/app/components/workflow/hooks/use-nodes-meta-data.ts index cfeb41de34..fd63f23590 100644 --- a/web/app/components/workflow/hooks/use-nodes-meta-data.ts +++ b/web/app/components/workflow/hooks/use-nodes-meta-data.ts @@ -7,6 +7,11 @@ import { CollectionType } from '@/app/components/tools/types' import { useStore } from '@/app/components/workflow/store' import { canFindTool } from '@/utils' import { useGetLanguage } from '@/context/i18n' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllWorkflowTools, +} from '@/service/use-tools' export const useNodesMetaData = () => { const availableNodesMetaData = useHooksStore(s => s.availableNodesMetaData) @@ -21,9 +26,9 @@ export const useNodesMetaData = () => { export const useNodeMetaData = (node: Node) => { const language = useGetLanguage() - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() const dataSourceList = useStore(s => s.dataSourceList) const availableNodesMetaData = useNodesMetaData() const { data } = node @@ -34,10 +39,10 @@ export const useNodeMetaData = (node: Node) => { if (data.type === BlockEnum.Tool) { if (data.provider_type === CollectionType.builtIn) - return buildInTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.author + return buildInTools?.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.author if (data.provider_type === CollectionType.workflow) - return workflowTools.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.author - return customTools.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.author + return workflowTools?.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.author + return customTools?.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.author } return nodeMetaData?.metaData.author }, [data, buildInTools, customTools, workflowTools, nodeMetaData, dataSourceList]) @@ -47,10 +52,10 @@ export const useNodeMetaData = (node: Node) => { return dataSourceList?.find(dataSource => dataSource.plugin_id === data.plugin_id)?.description[language] if (data.type === BlockEnum.Tool) { if (data.provider_type === CollectionType.builtIn) - return buildInTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.description[language] + return buildInTools?.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.description[language] if (data.provider_type === CollectionType.workflow) - return workflowTools.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.description[language] - return customTools.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.description[language] + return workflowTools?.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.description[language] + return customTools?.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.description[language] } return nodeMetaData?.metaData.description }, [data, buildInTools, customTools, workflowTools, nodeMetaData, dataSourceList, language]) diff --git a/web/app/components/workflow/hooks/use-tool-icon.ts b/web/app/components/workflow/hooks/use-tool-icon.ts index 734a7da390..32d65365db 100644 --- a/web/app/components/workflow/hooks/use-tool-icon.ts +++ b/web/app/components/workflow/hooks/use-tool-icon.ts @@ -14,12 +14,18 @@ import { } from '../store' import { CollectionType } from '@/app/components/tools/types' import { canFindTool } from '@/utils' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' export const useToolIcon = (data?: Node['data']) => { - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) - const mcpTools = useStore(s => s.mcpTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() const dataSourceList = useStore(s => s.dataSourceList) // const a = useStore(s => s.data) const toolIcon = useMemo(() => { @@ -27,15 +33,15 @@ export const useToolIcon = (data?: Node['data']) => { return '' if (data.type === BlockEnum.Tool) { // eslint-disable-next-line sonarjs/no-dead-store - let targetTools = buildInTools + let targetTools = buildInTools || [] if (data.provider_type === CollectionType.builtIn) - targetTools = buildInTools + targetTools = buildInTools || [] else if (data.provider_type === CollectionType.custom) - targetTools = customTools + targetTools = customTools || [] else if (data.provider_type === CollectionType.mcp) - targetTools = mcpTools + targetTools = mcpTools || [] else - targetTools = workflowTools + targetTools = workflowTools || [] return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon } if (data.type === BlockEnum.DataSource) @@ -46,24 +52,24 @@ export const useToolIcon = (data?: Node['data']) => { } export const useGetToolIcon = () => { + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() const workflowStore = useWorkflowStore() const getToolIcon = useCallback((data: Node['data']) => { const { - buildInTools, - customTools, - workflowTools, dataSourceList, } = workflowStore.getState() if (data.type === BlockEnum.Tool) { // eslint-disable-next-line sonarjs/no-dead-store - let targetTools = buildInTools + let targetTools = buildInTools || [] if (data.provider_type === CollectionType.builtIn) - targetTools = buildInTools + targetTools = buildInTools || [] else if (data.provider_type === CollectionType.custom) - targetTools = customTools + targetTools = customTools || [] else - targetTools = workflowTools + targetTools = workflowTools || [] return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon } diff --git a/web/app/components/workflow/hooks/use-workflow-search.tsx b/web/app/components/workflow/hooks/use-workflow-search.tsx index 095ae4577a..68ad9873f9 100644 --- a/web/app/components/workflow/hooks/use-workflow-search.tsx +++ b/web/app/components/workflow/hooks/use-workflow-search.tsx @@ -8,11 +8,16 @@ import { workflowNodesAction } from '@/app/components/goto-anything/actions/work import BlockIcon from '@/app/components/workflow/block-icon' import { setupNodeSelectionListener } from '../utils/node-navigation' import { BlockEnum } from '../types' -import { useStore } from '../store' import type { Emoji } from '@/app/components/tools/types' import { CollectionType } from '@/app/components/tools/types' import { canFindTool } from '@/utils' import type { LLMNodeType } from '../nodes/llm/types' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' /** * Hook to register workflow nodes search functionality @@ -22,23 +27,23 @@ export const useWorkflowSearch = () => { const { handleNodeSelect } = useNodesInteractions() // Filter and process nodes for search - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) - const mcpTools = useStore(s => s.mcpTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() // Extract tool icon logic - clean separation of concerns const getToolIcon = useCallback((nodeData: CommonNodeType): string | Emoji | undefined => { if (nodeData?.type !== BlockEnum.Tool) return undefined const toolCollections: Record = { - [CollectionType.builtIn]: buildInTools, - [CollectionType.custom]: customTools, - [CollectionType.mcp]: mcpTools, + [CollectionType.builtIn]: buildInTools || [], + [CollectionType.custom]: customTools || [], + [CollectionType.mcp]: mcpTools || [], } const targetTools = (nodeData.provider_type && toolCollections[nodeData.provider_type]) || workflowTools - return targetTools.find((tool: any) => canFindTool(tool.id, nodeData.provider_id))?.icon + return targetTools?.find((tool: any) => canFindTool(tool.id, nodeData.provider_id))?.icon }, [buildInTools, customTools, workflowTools, mcpTools]) // Extract model info logic - clean extraction diff --git a/web/app/components/workflow/hooks/use-workflow-variables.ts b/web/app/components/workflow/hooks/use-workflow-variables.ts index 8422a7fd0d..871937365a 100644 --- a/web/app/components/workflow/hooks/use-workflow-variables.ts +++ b/web/app/components/workflow/hooks/use-workflow-variables.ts @@ -10,20 +10,25 @@ import type { } from '@/app/components/workflow/types' import { useIsChatMode } from './use-workflow' import { useStoreApi } from 'reactflow' -import { useStore } from '@/app/components/workflow/store' import type { Type } from '../nodes/llm/types' import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' export const useWorkflowVariables = () => { const { t } = useTranslation() const workflowStore = useWorkflowStore() const { schemaTypeDefinitions } = useMatchSchemaType() - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) - const mcpTools = useStore(s => s.mcpTools) - const dataSourceList = useStore(s => s.dataSourceList) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() + const getNodeAvailableVars = useCallback(({ parentNode, beforeNodes, @@ -43,6 +48,7 @@ export const useWorkflowVariables = () => { conversationVariables, environmentVariables, ragPipelineVariables, + dataSourceList, } = workflowStore.getState() return toNodeAvailableVars({ parentNode, @@ -54,15 +60,15 @@ export const useWorkflowVariables = () => { ragVariables: ragPipelineVariables, filterVar, allPluginInfoList: { - buildInTools, - customTools, - workflowTools, - mcpTools, - dataSourceList: dataSourceList ?? [], + buildInTools: buildInTools || [], + customTools: customTools || [], + workflowTools: workflowTools || [], + mcpTools: mcpTools || [], + dataSourceList: dataSourceList || [], }, schemaTypeDefinitions, }) - }, [t, workflowStore, schemaTypeDefinitions, buildInTools]) + }, [t, workflowStore, schemaTypeDefinitions, buildInTools, customTools, workflowTools, mcpTools]) const getCurrentVariableType = useCallback(({ parentNode, @@ -87,10 +93,6 @@ export const useWorkflowVariables = () => { conversationVariables, environmentVariables, ragPipelineVariables, - buildInTools, - customTools, - workflowTools, - mcpTools, dataSourceList, } = workflowStore.getState() return getVarType({ @@ -105,16 +107,16 @@ export const useWorkflowVariables = () => { conversationVariables, ragVariables: ragPipelineVariables, allPluginInfoList: { - buildInTools, - customTools, - workflowTools, - mcpTools, + buildInTools: buildInTools || [], + customTools: customTools || [], + workflowTools: workflowTools || [], + mcpTools: mcpTools || [], dataSourceList: dataSourceList ?? [], }, schemaTypeDefinitions, preferSchemaType, }) - }, [workflowStore, getVarType, schemaTypeDefinitions]) + }, [workflowStore, getVarType, schemaTypeDefinitions, buildInTools, customTools, workflowTools, mcpTools]) return { getNodeAvailableVars, diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index 3f9f8106cf..66c499dc59 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -32,15 +32,9 @@ import { CUSTOM_NOTE_NODE } from '../note-node/constants' import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils' import { useAvailableBlocks } from './use-available-blocks' import { useStore as useAppStore } from '@/app/components/app/store' -import { - fetchAllBuiltInTools, - fetchAllCustomTools, - fetchAllMCPTools, - fetchAllWorkflowTools, -} from '@/service/tools' + import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants' -import { basePath } from '@/utils/var' import { useNodesMetaData } from '.' export const useIsChatMode = () => { @@ -416,51 +410,6 @@ export const useWorkflow = () => { } } -export const useFetchToolsData = () => { - const workflowStore = useWorkflowStore() - - const handleFetchAllTools = useCallback(async (type: string) => { - if (type === 'builtin') { - const buildInTools = await fetchAllBuiltInTools() - - if (basePath) { - buildInTools.forEach((item) => { - if (typeof item.icon == 'string' && !item.icon.includes(basePath)) - item.icon = `${basePath}${item.icon}` - }) - } - workflowStore.setState({ - buildInTools: buildInTools || [], - }) - } - if (type === 'custom') { - const customTools = await fetchAllCustomTools() - - workflowStore.setState({ - customTools: customTools || [], - }) - } - if (type === 'workflow') { - const workflowTools = await fetchAllWorkflowTools() - - workflowStore.setState({ - workflowTools: workflowTools || [], - }) - } - if (type === 'mcp') { - const mcpTools = await fetchAllMCPTools() - - workflowStore.setState({ - mcpTools: mcpTools || [], - }) - } - }, [workflowStore]) - - return { - handleFetchAllTools, - } -} - export const useWorkflowReadOnly = () => { const workflowStore = useWorkflowStore() const workflowRunningData = useStore(s => s.workflowRunningData) diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index b289cafefd..86c6bf153e 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -37,7 +37,6 @@ import { } from './types' import { useEdgesInteractions, - useFetchToolsData, useNodesInteractions, useNodesReadOnly, useNodesSyncDraft, @@ -92,6 +91,12 @@ import useMatchSchemaType from './nodes/_base/components/variable/use-match-sche import type { VarInInspect } from '@/types/workflow' import { fetchAllInspectVars } from '@/service/workflow' import cn from '@/utils/classnames' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' const Confirm = dynamic(() => import('@/app/components/base/confirm'), { ssr: false, @@ -242,13 +247,6 @@ export const Workflow: FC = memo(({ }) } }) - const { handleFetchAllTools } = useFetchToolsData() - useEffect(() => { - handleFetchAllTools('builtin') - handleFetchAllTools('custom') - handleFetchAllTools('workflow') - handleFetchAllTools('mcp') - }, [handleFetchAllTools]) const { handleNodeDragStart, @@ -299,10 +297,10 @@ export const Workflow: FC = memo(({ const { schemaTypeDefinitions } = useMatchSchemaType() const { fetchInspectVars } = useSetWorkflowVarsWithValue() - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) - const mcpTools = useStore(s => s.mcpTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() const dataSourceList = useStore(s => s.dataSourceList) // buildInTools, customTools, workflowTools, mcpTools, dataSourceList const configsMap = useHooksStore(s => s.configsMap) @@ -323,10 +321,10 @@ export const Workflow: FC = memo(({ passInVars: true, vars, passedInAllPluginInfoList: { - buildInTools, - customTools, - workflowTools, - mcpTools, + buildInTools: buildInTools || [], + customTools: customTools || [], + workflowTools: workflowTools || [], + mcpTools: mcpTools || [], dataSourceList: dataSourceList ?? [], }, passedInSchemaTypeDefinitions: schemaTypeDefinitions, diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index 03b142ba43..29aebd4fd5 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -75,6 +75,7 @@ import { DataSourceClassification } from '@/app/components/workflow/nodes/data-s import { useModalContext } from '@/context/modal-context' import DataSourceBeforeRunForm from '@/app/components/workflow/nodes/data-source/before-run-form' import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' +import { useAllBuiltInTools } from '@/service/use-tools' const getCustomRunForm = (params: CustomRunFormProps): React.JSX.Element => { const nodeType = params.payload.type @@ -259,9 +260,9 @@ const BasePanel: FC = ({ return {} })() - const buildInTools = useStore(s => s.buildInTools) + const { data: buildInTools } = useAllBuiltInTools() const currCollection = useMemo(() => { - return buildInTools.find(item => canFindTool(item.id, data.provider_id)) + return buildInTools?.find(item => canFindTool(item.id, data.provider_id)) }, [buildInTools, data.provider_id]) const showPluginAuth = useMemo(() => { return data.type === BlockEnum.Tool && currCollection?.allow_delete @@ -450,6 +451,7 @@ const BasePanel: FC = ({ className='px-4 pb-2' pluginPayload={{ provider: currCollection?.name || '', + providerType: currCollection?.type || '', category: AuthCategory.tool, }} > @@ -461,6 +463,7 @@ const BasePanel: FC = ({ = { [BlockEnum.LLM]: checkLLMValid, @@ -133,21 +140,23 @@ const useOneStepRun = ({ const availableNodesIncludeParent = getBeforeNodesInSameBranchIncludeParent(id) const workflowStore = useWorkflowStore() const { schemaTypeDefinitions } = useMatchSchemaType() + + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() + const getVar = (valueSelector: ValueSelector): Var | undefined => { const isSystem = valueSelector[0] === 'sys' const { - buildInTools, - customTools, - workflowTools, - mcpTools, dataSourceList, } = workflowStore.getState() const allPluginInfoList = { - buildInTools, - customTools, - workflowTools, - mcpTools, - dataSourceList: dataSourceList ?? [], + buildInTools: buildInTools || [], + customTools: customTools || [], + workflowTools: workflowTools || [], + mcpTools: mcpTools || [], + dataSourceList: dataSourceList || [], } const allOutputVars = toNodeOutputVars(availableNodes, isChatMode, undefined, undefined, conversationVariables, [], allPluginInfoList, schemaTypeDefinitions) diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx index 9bcd4b9671..65dac6f5be 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx @@ -42,6 +42,12 @@ import BoolValue from '@/app/components/workflow/panel/chat-variable-panel/compo import { getVarType } from '@/app/components/workflow/nodes/_base/components/variable/utils' import { useIsChatMode } from '@/app/components/workflow/hooks/use-workflow' import useMatchSchemaType from '../../../_base/components/variable/use-match-schema-type' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName' type ConditionItemProps = { @@ -91,15 +97,12 @@ const ConditionItem = ({ const [isHovered, setIsHovered] = useState(false) const [open, setOpen] = useState(false) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() + const workflowStore = useWorkflowStore() - const { - setControlPromptEditorRerenderKey, - buildInTools, - customTools, - mcpTools, - workflowTools, - dataSourceList, - } = workflowStore.getState() const doUpdateCondition = useCallback((newCondition: Condition) => { if (isSubVariableKey) @@ -213,6 +216,8 @@ const ConditionItem = ({ const handleVarChange = useCallback((valueSelector: ValueSelector, _varItem: Var) => { const { conversationVariables, + setControlPromptEditorRerenderKey, + dataSourceList, } = workflowStore.getState() const resolvedVarType = getVarType({ valueSelector, @@ -220,11 +225,11 @@ const ConditionItem = ({ availableNodes, isChatMode, allPluginInfoList: { - buildInTools, - customTools, - mcpTools, - workflowTools, - dataSourceList: dataSourceList ?? [], + buildInTools: buildInTools || [], + customTools: customTools || [], + mcpTools: mcpTools || [], + workflowTools: workflowTools || [], + dataSourceList: dataSourceList || [], }, schemaTypeDefinitions, }) @@ -241,12 +246,12 @@ const ConditionItem = ({ }) doUpdateCondition(newCondition) setOpen(false) - }, [condition, doUpdateCondition, availableNodes, isChatMode, setControlPromptEditorRerenderKey, schemaTypeDefinitions]) + }, [condition, doUpdateCondition, availableNodes, isChatMode, schemaTypeDefinitions, buildInTools, customTools, mcpTools, workflowTools]) const showBooleanInput = useMemo(() => { if(condition.varType === VarType.boolean) return true - // eslint-disable-next-line sonarjs/prefer-single-boolean-return + if(condition.varType === VarType.arrayBoolean && [ComparisonOperator.contains, ComparisonOperator.notContains].includes(condition.comparison_operator!)) return true return false diff --git a/web/app/components/workflow/nodes/iteration/use-config.ts b/web/app/components/workflow/nodes/iteration/use-config.ts index 9fd31d0484..2e47bb3740 100644 --- a/web/app/components/workflow/nodes/iteration/use-config.ts +++ b/web/app/components/workflow/nodes/iteration/use-config.ts @@ -15,6 +15,12 @@ import type { Item } from '@/app/components/base/select' import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' import { isEqual } from 'lodash-es' import { useStore } from '../../store' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' const useConfig = (id: string, payload: IterationNodeType) => { const { @@ -40,17 +46,17 @@ const useConfig = (id: string, payload: IterationNodeType) => { // output const { getIterationNodeChildren } = useWorkflow() const iterationChildrenNodes = getIterationNodeChildren(id) - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) - const mcpTools = useStore(s => s.mcpTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() const dataSourceList = useStore(s => s.dataSourceList) const allPluginInfoList = { - buildInTools, - customTools, - workflowTools, - mcpTools, - dataSourceList: dataSourceList ?? [], + buildInTools: buildInTools || [], + customTools: customTools || [], + workflowTools: workflowTools || [], + mcpTools: mcpTools || [], + dataSourceList: dataSourceList || [], } const childrenNodeVars = toNodeOutputVars(iterationChildrenNodes, isChatMode, undefined, [], [], [], allPluginInfoList) diff --git a/web/app/components/workflow/nodes/loop/use-config.ts b/web/app/components/workflow/nodes/loop/use-config.ts index fcf437eb96..e8504fb5e9 100644 --- a/web/app/components/workflow/nodes/loop/use-config.ts +++ b/web/app/components/workflow/nodes/loop/use-config.ts @@ -15,9 +15,24 @@ import useNodeCrud from '../_base/hooks/use-node-crud' import { toNodeOutputVars } from '../_base/components/variable/utils' import { getOperators } from './utils' import { LogicalOperator } from './types' -import type { HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition, LoopNodeType } from './types' +import type { + HandleAddCondition, + HandleAddSubVariableCondition, + HandleRemoveCondition, + HandleToggleConditionLogicalOperator, + HandleToggleSubVariableConditionLogicalOperator, + HandleUpdateCondition, + HandleUpdateSubVariableCondition, + LoopNodeType, +} from './types' import useIsVarFileAttribute from './use-is-var-file-attribute' import { useStore } from '@/app/components/workflow/store' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' const useConfig = (id: string, payload: LoopNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() @@ -38,17 +53,17 @@ const useConfig = (id: string, payload: LoopNodeType) => { // output const { getLoopNodeChildren } = useWorkflow() const loopChildrenNodes = [{ id, data: payload } as any, ...getLoopNodeChildren(id)] - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) - const mcpTools = useStore(s => s.mcpTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() const dataSourceList = useStore(s => s.dataSourceList) const allPluginInfoList = { - buildInTools, - customTools, - workflowTools, - mcpTools, - dataSourceList: dataSourceList ?? [], + buildInTools: buildInTools || [], + customTools: customTools || [], + workflowTools: workflowTools || [], + mcpTools: mcpTools || [], + dataSourceList: dataSourceList || [], } const childrenNodeVars = toNodeOutputVars(loopChildrenNodes, isChatMode, undefined, [], conversationVariables, [], allPluginInfoList) diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx index d93d08a0ac..9392f28736 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx @@ -8,7 +8,6 @@ import { useTranslation } from 'react-i18next' import BlockSelector from '../../../../block-selector' import type { Param, ParamType } from '../../types' import cn from '@/utils/classnames' -import { useStore } from '@/app/components/workflow/store' import type { DataSourceDefaultValue, ToolDefaultValue, @@ -18,6 +17,11 @@ import { CollectionType } from '@/app/components/tools/types' import type { BlockEnum } from '@/app/components/workflow/types' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { canFindTool } from '@/utils' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllWorkflowTools, +} from '@/service/use-tools' const i18nPrefix = 'workflow.nodes.parameterExtractor' @@ -42,9 +46,9 @@ const ImportFromTool: FC = ({ const { t } = useTranslation() const language = useLanguage() - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() const handleSelectTool = useCallback((_type: BlockEnum, toolInfo?: ToolDefaultValue | DataSourceDefaultValue) => { if (!toolInfo || 'datasource_name' in toolInfo) @@ -54,11 +58,11 @@ const ImportFromTool: FC = ({ const currentTools = (() => { switch (provider_type) { case CollectionType.builtIn: - return buildInTools + return buildInTools || [] case CollectionType.custom: - return customTools + return customTools || [] case CollectionType.workflow: - return workflowTools + return workflowTools || [] default: return [] } diff --git a/web/app/components/workflow/nodes/tool/use-config.ts b/web/app/components/workflow/nodes/tool/use-config.ts index 5b8827936c..fe3fe543e9 100644 --- a/web/app/components/workflow/nodes/tool/use-config.ts +++ b/web/app/components/workflow/nodes/tool/use-config.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { produce } from 'immer' import { useBoolean } from 'ahooks' -import { useStore, useWorkflowStore } from '../../store' +import { useWorkflowStore } from '../../store' import type { ToolNodeType, ToolVarInputs } from './types' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' @@ -15,15 +15,20 @@ import { import Toast from '@/app/components/base/toast' import type { InputVar } from '@/app/components/workflow/types' import { - useFetchToolsData, useNodesReadOnly, } from '@/app/components/workflow/hooks' import { canFindTool } from '@/utils' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, + useInvalidToolsByType, +} from '@/service/use-tools' const useConfig = (id: string, payload: ToolNodeType) => { const workflowStore = useWorkflowStore() const { nodesReadOnly: readOnly } = useNodesReadOnly() - const { handleFetchAllTools } = useFetchToolsData() const { t } = useTranslation() const language = useLanguage() @@ -43,21 +48,21 @@ const useConfig = (id: string, payload: ToolNodeType) => { tool_parameters, } = inputs const isBuiltIn = provider_type === CollectionType.builtIn - const buildInTools = useStore(s => s.buildInTools) - const customTools = useStore(s => s.customTools) - const workflowTools = useStore(s => s.workflowTools) - const mcpTools = useStore(s => s.mcpTools) + const { data: buildInTools } = useAllBuiltInTools() + const { data: customTools } = useAllCustomTools() + const { data: workflowTools } = useAllWorkflowTools() + const { data: mcpTools } = useAllMCPTools() const currentTools = useMemo(() => { switch (provider_type) { case CollectionType.builtIn: - return buildInTools + return buildInTools || [] case CollectionType.custom: - return customTools + return customTools || [] case CollectionType.workflow: - return workflowTools + return workflowTools || [] case CollectionType.mcp: - return mcpTools + return mcpTools || [] default: return [] } @@ -75,6 +80,7 @@ const useConfig = (id: string, payload: ToolNodeType) => { { setTrue: showSetAuthModal, setFalse: hideSetAuthModal }, ] = useBoolean(false) + const invalidToolsByType = useInvalidToolsByType(provider_type) const handleSaveAuth = useCallback( async (value: any) => { await updateBuiltInToolCredential(currCollection?.name as string, value) @@ -83,14 +89,14 @@ const useConfig = (id: string, payload: ToolNodeType) => { type: 'success', message: t('common.api.actionSuccess'), }) - handleFetchAllTools(provider_type) + invalidToolsByType() hideSetAuthModal() }, [ currCollection?.name, hideSetAuthModal, t, - handleFetchAllTools, + invalidToolsByType, provider_type, ], ) @@ -241,17 +247,15 @@ const useConfig = (id: string, payload: ToolNodeType) => { name: outputKey, type: output.type === 'array' - ? `Array[${ - output.items?.type - ? output.items.type.slice(0, 1).toLocaleUpperCase() - + output.items.type.slice(1) - : 'Unknown' + ? `Array[${output.items?.type + ? output.items.type.slice(0, 1).toLocaleUpperCase() + + output.items.type.slice(1) + : 'Unknown' }]` - : `${ - output.type - ? output.type.slice(0, 1).toLocaleUpperCase() - + output.type.slice(1) - : 'Unknown' + : `${output.type + ? output.type.slice(0, 1).toLocaleUpperCase() + + output.type.slice(1) + : 'Unknown' }`, description: output.description, }) diff --git a/web/app/components/workflow/store/workflow/tool-slice.ts b/web/app/components/workflow/store/workflow/tool-slice.ts index d6d89abcf0..c5180022fc 100644 --- a/web/app/components/workflow/store/workflow/tool-slice.ts +++ b/web/app/components/workflow/store/workflow/tool-slice.ts @@ -1,30 +1,11 @@ import type { StateCreator } from 'zustand' -import type { - ToolWithProvider, -} from '@/app/components/workflow/types' export type ToolSliceShape = { - buildInTools: ToolWithProvider[] - setBuildInTools: (tools: ToolWithProvider[]) => void - customTools: ToolWithProvider[] - setCustomTools: (tools: ToolWithProvider[]) => void - workflowTools: ToolWithProvider[] - setWorkflowTools: (tools: ToolWithProvider[]) => void - mcpTools: ToolWithProvider[] - setMcpTools: (tools: ToolWithProvider[]) => void toolPublished: boolean setToolPublished: (toolPublished: boolean) => void } export const createToolSlice: StateCreator = set => ({ - buildInTools: [], - setBuildInTools: buildInTools => set(() => ({ buildInTools })), - customTools: [], - setCustomTools: customTools => set(() => ({ customTools })), - workflowTools: [], - setWorkflowTools: workflowTools => set(() => ({ workflowTools })), - mcpTools: [], - setMcpTools: mcpTools => set(() => ({ mcpTools })), toolPublished: false, setToolPublished: toolPublished => set(() => ({ toolPublished })), }) diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index f6a706a982..324443cfd1 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -19,7 +19,7 @@ import type { } from '@/app/components/workflow/nodes/_base/components/error-handle/types' import type { WorkflowRetryConfig } from '@/app/components/workflow/nodes/_base/components/retry/types' import type { StructuredOutput } from '@/app/components/workflow/nodes/llm/types' -import type { PluginMeta } from '../plugins/types' +import type { Plugin, PluginMeta } from '@/app/components/plugins/types' import type { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' import type { SchemaTypeDefinition } from '@/service/use-common' @@ -451,16 +451,9 @@ export type ToolWithProvider = Collection & { meta: PluginMeta } -export type UninstalledRecommendedPlugin = { - plugin_id: string - name: string - icon: string - plugin_unique_identifier: string -} - export type RAGRecommendedPlugins = { installed_recommended_plugins: ToolWithProvider[] - uninstalled_recommended_plugins: UninstalledRecommendedPlugin[] + uninstalled_recommended_plugins: Plugin[] } export enum SupportUploadFileTypes { diff --git a/web/i18n/en-US/pipeline.ts b/web/i18n/en-US/pipeline.ts index 4b29bdbb00..8e5fd8a3e0 100644 --- a/web/i18n/en-US/pipeline.ts +++ b/web/i18n/en-US/pipeline.ts @@ -33,7 +33,7 @@ const translation = { }, ragToolSuggestions: { title: 'Suggestions for RAG', - noRecommendationPluginsInstalled: 'No recommended plugins installed, find more in Marketplace', + noRecommendationPlugins: 'No recommended plugins, find more in Marketplace', }, } diff --git a/web/i18n/ja-JP/pipeline.ts b/web/i18n/ja-JP/pipeline.ts index 64700acc09..9ec1b68273 100644 --- a/web/i18n/ja-JP/pipeline.ts +++ b/web/i18n/ja-JP/pipeline.ts @@ -33,7 +33,7 @@ const translation = { }, ragToolSuggestions: { title: 'RAGのための提案', - noRecommendationPluginsInstalled: '推奨プラグインがインストールされていません。マーケットプレイスで詳細をご確認ください', + noRecommendationPlugins: '推奨プラグインがありません。マーケットプレイスで詳細をご確認ください', }, } diff --git a/web/i18n/zh-Hans/pipeline.ts b/web/i18n/zh-Hans/pipeline.ts index 3c3a7a6506..1ae087fcfd 100644 --- a/web/i18n/zh-Hans/pipeline.ts +++ b/web/i18n/zh-Hans/pipeline.ts @@ -33,7 +33,7 @@ const translation = { }, ragToolSuggestions: { title: 'RAG 工具推荐', - noRecommendationPluginsInstalled: '暂无已安装的推荐插件,更多插件请在 Marketplace 中查找', + noRecommendationPlugins: '暂无推荐插件,更多插件请在 Marketplace 中查找', }, } diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index f59e500792..1dec97cdfa 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from 'react' +import { useCallback, useEffect, useState } from 'react' import type { FormOption, ModelProvider, @@ -39,7 +39,7 @@ import { useQuery, useQueryClient, } from '@tanstack/react-query' -import { useInvalidateAllBuiltInTools, useInvalidateRAGRecommendedPlugins } from './use-tools' +import { useInvalidateAllBuiltInTools } from './use-tools' import useReferenceSetting from '@/app/components/plugins/plugin-page/use-reference-setting' import { uninstallPlugin } from '@/service/plugins' import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list' @@ -135,14 +135,12 @@ export const useInstalledLatestVersion = (pluginIds: string[]) => { export const useInvalidateInstalledPluginList = () => { const queryClient = useQueryClient() const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools() - const invalidateRAGRecommendedPlugins = useInvalidateRAGRecommendedPlugins() return () => { queryClient.invalidateQueries( { queryKey: useInstalledPluginListKey, }) invalidateAllBuiltInTools() - invalidateRAGRecommendedPlugins() } } @@ -489,6 +487,7 @@ export const useFetchPluginsInMarketPlaceByInfo = (infos: Record[]) const usePluginTaskListKey = [NAME_SPACE, 'pluginTaskList'] export const usePluginTaskList = (category?: PluginType) => { + const [initialized, setInitialized] = useState(false) const { canManagement, } = useReferenceSetting() @@ -512,7 +511,8 @@ export const usePluginTaskList = (category?: PluginType) => { useEffect(() => { // After first fetch, refresh plugin list each time all tasks are done - if (!isRefetching) { + // Skip initialization period, because the query cache is not updated yet + if (initialized && !isRefetching) { const lastData = cloneDeep(data) const taskDone = lastData?.tasks.every(task => task.status === TaskStatus.success || task.status === TaskStatus.failed) const taskAllFailed = lastData?.tasks.every(task => task.status === TaskStatus.failed) @@ -523,6 +523,10 @@ export const usePluginTaskList = (category?: PluginType) => { } }, [isRefetching]) + useEffect(() => { + setInitialized(true) + }, []) + const handleRefetch = useCallback(() => { refetch() }, [refetch]) diff --git a/web/service/use-tools.ts b/web/service/use-tools.ts index a881441cd5..306cb903df 100644 --- a/web/service/use-tools.ts +++ b/web/service/use-tools.ts @@ -4,9 +4,11 @@ import type { MCPServerDetail, Tool, } from '@/app/components/tools/types' +import { CollectionType } from '@/app/components/tools/types' import type { RAGRecommendedPlugins, ToolWithProvider } from '@/app/components/workflow/types' import type { AppIconType } from '@/types/app' import { useInvalid } from './use-base' +import type { QueryKey } from '@tanstack/react-query' import { useMutation, useQuery, @@ -76,6 +78,16 @@ export const useInvalidateAllMCPTools = () => { return useInvalid(useAllMCPToolsKey) } +const useInvalidToolsKeyMap: Record = { + [CollectionType.builtIn]: useAllBuiltInToolsKey, + [CollectionType.custom]: useAllCustomToolsKey, + [CollectionType.workflow]: useAllWorkflowToolsKey, + [CollectionType.mcp]: useAllMCPToolsKey, +} +export const useInvalidToolsByType = (type: CollectionType | string) => { + return useInvalid(useInvalidToolsKeyMap[type]) +} + export const useCreateMCP = () => { return useMutation({ mutationKey: [NAME_SPACE, 'create-mcp'],