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/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 { @@ -31,7 +31,13 @@ const RAGToolSuggestions: React.FC = ({ 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]) @@ -53,7 +59,7 @@ const RAGToolSuggestions: React.FC = ({ )} - {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length === 0 && ( + {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length === 0 && unInstalledPlugins.length === 0 && (

= ({ />

)} - {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length > 0 && ( + {!isFetchingRAGRecommendedPlugins && (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..e8e8748b45 --- /dev/null +++ b/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx @@ -0,0 +1,58 @@ +'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)} +
+
+ {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/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/service/use-plugins.ts b/web/service/use-plugins.ts index f59e500792..2877ef15f2 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -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() } }