feat(workflow): polish featured tools recommendations

This commit is contained in:
lyzno1 2025-10-22 12:29:14 +08:00
parent d99644237b
commit 77e9bae3ff
No known key found for this signature in database
6 changed files with 101 additions and 37 deletions

View File

@ -25,13 +25,12 @@ import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general'
import { useGetLanguage } from '@/context/i18n' import { useGetLanguage } from '@/context/i18n'
import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list' import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list'
import PluginList, { type ListProps } from '@/app/components/workflow/block-selector/market-place-plugin/list' import PluginList, { type ListProps } from '@/app/components/workflow/block-selector/market-place-plugin/list'
import type { Plugin } from '../../plugins/types'
import { PluginCategoryEnum } from '../../plugins/types' import { PluginCategoryEnum } from '../../plugins/types'
import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' import { useMarketplacePlugins } from '../../plugins/marketplace/hooks'
import { useGlobalPublicStore } from '@/context/global-public-context' import { useGlobalPublicStore } from '@/context/global-public-context'
import RAGToolSuggestions from './rag-tool-suggestions' import RAGToolSuggestions from './rag-tool-suggestions'
import FeaturedTools from './featured-tools' import FeaturedTools from './featured-tools'
import { useCheckInstalled, useRecommendedMarketplacePlugins } from '@/service/use-plugins'
import { useInvalidateAllBuiltInTools } from '@/service/use-tools'
import Link from 'next/link' import Link from 'next/link'
type AllToolsProps = { type AllToolsProps = {
@ -50,6 +49,12 @@ type AllToolsProps = {
canChooseMCPTool?: boolean canChooseMCPTool?: boolean
onTagsChange?: Dispatch<SetStateAction<string[]>> onTagsChange?: Dispatch<SetStateAction<string[]>>
isInRAGPipeline?: boolean isInRAGPipeline?: boolean
featuredPlugins?: Plugin[]
featuredLoading?: boolean
featuredInstalledPluginIds?: Set<string>
featuredInstallLoading?: boolean
showFeatured?: boolean
onFeaturedInstallSuccess?: () => Promise<void> | void
} }
const DEFAULT_TAGS: AllToolsProps['tags'] = [] const DEFAULT_TAGS: AllToolsProps['tags'] = []
@ -70,6 +75,12 @@ const AllTools = ({
canChooseMCPTool, canChooseMCPTool,
onTagsChange, onTagsChange,
isInRAGPipeline = false, isInRAGPipeline = false,
featuredPlugins = [],
featuredLoading = false,
featuredInstalledPluginIds = new Set<string>(),
featuredInstallLoading = false,
showFeatured = false,
onFeaturedInstallSuccess,
}: AllToolsProps) => { }: AllToolsProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const language = useGetLanguage() const language = useGetLanguage()
@ -149,26 +160,6 @@ const AllTools = ({
} = useMarketplacePlugins() } = useMarketplacePlugins()
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const {
data: recommendedPlugins = [],
isLoading: isLoadingRecommended,
} = useRecommendedMarketplacePlugins({
enabled: enable_marketplace && !isInRAGPipeline,
})
const recommendedPluginIds = useMemo(
() => recommendedPlugins.map(plugin => plugin.plugin_id),
[recommendedPlugins],
)
const installedCheck = useCheckInstalled({
pluginIds: recommendedPluginIds,
enabled: recommendedPluginIds.length > 0,
})
const installedPluginIds = useMemo(
() => new Set(installedCheck.data?.plugins.map(plugin => plugin.plugin_id) ?? []),
[installedCheck.data],
)
const loadingRecommendedInstallStatus = installedCheck.isLoading || installedCheck.isRefetching
const invalidateBuiltInTools = useInvalidateAllBuiltInTools()
useEffect(() => { useEffect(() => {
if (!enable_marketplace) return if (!enable_marketplace) return
@ -189,12 +180,12 @@ const AllTools = ({
const hasToolsContent = tools.length > 0 const hasToolsContent = tools.length > 0
const hasPluginContent = enable_marketplace && notInstalledPlugins.length > 0 const hasPluginContent = enable_marketplace && notInstalledPlugins.length > 0
const shouldShowEmptyState = hasFilter && !hasToolsContent && !hasPluginContent const shouldShowEmptyState = hasFilter && !hasToolsContent && !hasPluginContent
const shouldShowFeatured = enable_marketplace const shouldShowFeatured = showFeatured
&& enable_marketplace
&& !isInRAGPipeline
&& activeTab === ToolTypeEnum.All && activeTab === ToolTypeEnum.All
&& !hasFilter && !hasFilter
&& !isLoadingRecommended && (featuredLoading || featuredPlugins.length > 0)
&& !isInRAGPipeline
&& recommendedPlugins.length > 0
return ( return (
<div className={cn('min-w-[400px] max-w-[500px]', className)}> <div className={cn('min-w-[400px] max-w-[500px]', className)}>
@ -235,17 +226,16 @@ const AllTools = ({
)} )}
{shouldShowFeatured && ( {shouldShowFeatured && (
<FeaturedTools <FeaturedTools
plugins={recommendedPlugins} plugins={featuredPlugins}
providerMap={providerMap} providerMap={providerMap}
onSelect={onSelect} onSelect={onSelect}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool} canChooseMCPTool={canChooseMCPTool}
installedPluginIds={installedPluginIds} installedPluginIds={featuredInstalledPluginIds}
loadingInstalledStatus={loadingRecommendedInstallStatus} loadingInstalledStatus={featuredInstallLoading}
isLoading={isLoadingRecommended} isLoading={featuredLoading}
onInstallSuccess={async () => { onInstallSuccess={async () => {
invalidateBuiltInTools() await onFeaturedInstallSuccess?.()
await installedCheck.refetch()
}} }}
/> />
)} )}

View File

@ -1,6 +1,6 @@
import type { Dispatch, FC, SetStateAction } from 'react' import type { Dispatch, FC, SetStateAction } from 'react'
import { memo } from 'react' import { memo } from 'react'
import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools } from '@/service/use-tools' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllBuiltInTools } from '@/service/use-tools'
import type { import type {
BlockEnum, BlockEnum,
NodeDefault, NodeDefault,
@ -13,6 +13,8 @@ import AllStartBlocks from './all-start-blocks'
import AllTools from './all-tools' import AllTools from './all-tools'
import DataSources from './data-sources' import DataSources from './data-sources'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { useFeaturedToolsRecommendations } from '@/service/use-plugins'
import { useGlobalPublicStore } from '@/context/global-public-context'
export type TabsProps = { export type TabsProps = {
activeTab: TabsEnum activeTab: TabsEnum
@ -53,6 +55,16 @@ const Tabs: FC<TabsProps> = ({
const { data: customTools } = useAllCustomTools() const { data: customTools } = useAllCustomTools()
const { data: workflowTools } = useAllWorkflowTools() const { data: workflowTools } = useAllWorkflowTools()
const { data: mcpTools } = useAllMCPTools() const { data: mcpTools } = useAllMCPTools()
const invalidateBuiltInTools = useInvalidateAllBuiltInTools()
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const inRAGPipeline = dataSources.length > 0
const {
plugins: featuredPlugins = [],
isLoading: isFeaturedLoading,
installedIds: featuredInstalledIds,
installStatusLoading: featuredInstallLoading,
refetchInstallStatus: refetchFeaturedInstallStatus,
} = useFeaturedToolsRecommendations(enable_marketplace && !inRAGPipeline)
return ( return (
<div onClick={e => e.stopPropagation()}> <div onClick={e => e.stopPropagation()}>
@ -127,7 +139,16 @@ const Tabs: FC<TabsProps> = ({
mcpTools={mcpTools || []} mcpTools={mcpTools || []}
canChooseMCPTool canChooseMCPTool
onTagsChange={onTagsChange} onTagsChange={onTagsChange}
isInRAGPipeline={dataSources.length > 0} isInRAGPipeline={inRAGPipeline}
featuredPlugins={featuredPlugins}
featuredLoading={isFeaturedLoading}
featuredInstalledPluginIds={featuredInstalledIds}
featuredInstallLoading={featuredInstallLoading}
showFeatured={enable_marketplace && !inRAGPipeline}
onFeaturedInstallSuccess={async () => {
invalidateBuiltInTools()
await refetchFeaturedInstallStatus()
}}
/> />
) )
} }

View File

@ -23,7 +23,9 @@ import {
} from '@/service/tools' } from '@/service/tools'
import type { CustomCollectionBackend } from '@/app/components/tools/types' import type { CustomCollectionBackend } from '@/app/components/tools/types'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllCustomTools } from '@/service/use-tools' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllBuiltInTools, useInvalidateAllCustomTools } from '@/service/use-tools'
import { useFeaturedToolsRecommendations } from '@/service/use-plugins'
import { useGlobalPublicStore } from '@/context/global-public-context'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
type Props = { type Props = {
@ -61,11 +63,21 @@ const ToolPicker: FC<Props> = ({
const [searchText, setSearchText] = useState('') const [searchText, setSearchText] = useState('')
const [tags, setTags] = useState<string[]>([]) const [tags, setTags] = useState<string[]>([])
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const { data: buildInTools } = useAllBuiltInTools() const { data: buildInTools } = useAllBuiltInTools()
const { data: customTools } = useAllCustomTools() const { data: customTools } = useAllCustomTools()
const invalidateCustomTools = useInvalidateAllCustomTools() const invalidateCustomTools = useInvalidateAllCustomTools()
const { data: workflowTools } = useAllWorkflowTools() const { data: workflowTools } = useAllWorkflowTools()
const { data: mcpTools } = useAllMCPTools() const { data: mcpTools } = useAllMCPTools()
const invalidateBuiltInTools = useInvalidateAllBuiltInTools()
const {
plugins: featuredPlugins = [],
isLoading: isFeaturedLoading,
installedIds: featuredInstalledIds,
installStatusLoading: featuredInstallLoading,
refetchInstallStatus: refetchFeaturedInstallStatus,
} = useFeaturedToolsRecommendations(enable_marketplace)
const { builtinToolList, customToolList, workflowToolList } = useMemo(() => { const { builtinToolList, customToolList, workflowToolList } = useMemo(() => {
if (scope === 'plugins') { if (scope === 'plugins') {
@ -179,6 +191,15 @@ const ToolPicker: FC<Props> = ({
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool} canChooseMCPTool={canChooseMCPTool}
onTagsChange={setTags} onTagsChange={setTags}
featuredPlugins={featuredPlugins}
featuredLoading={isFeaturedLoading}
featuredInstalledPluginIds={featuredInstalledIds}
featuredInstallLoading={featuredInstallLoading}
showFeatured={scope === 'all' && enable_marketplace}
onFeaturedInstallSuccess={async () => {
invalidateBuiltInTools()
await refetchFeaturedInstallStatus()
}}
/> />
</div> </div>
</PortalToFollowElemContent> </PortalToFollowElemContent>

View File

@ -283,6 +283,7 @@ const translation = {
'pluginByAuthor': 'By {{author}}', 'pluginByAuthor': 'By {{author}}',
'usePlugin': 'Select tool', 'usePlugin': 'Select tool',
'hideActions': 'Hide tools', 'hideActions': 'Hide tools',
'noFeaturedPlugins': 'Discover more tools in Marketplace',
}, },
blocks: { blocks: {
'start': 'User Input', 'start': 'User Input',

View File

@ -269,6 +269,7 @@ const translation = {
'pluginByAuthor': '来自 {{author}}', 'pluginByAuthor': '来自 {{author}}',
'usePlugin': '选择工具', 'usePlugin': '选择工具',
'hideActions': '收起工具', 'hideActions': '收起工具',
'noFeaturedPlugins': '前往插件市场查看更多工具',
}, },
blocks: { blocks: {
'start': '用户输入', 'start': '用户输入',
@ -358,7 +359,7 @@ const translation = {
panel: { panel: {
userInputField: '用户输入字段', userInputField: '用户输入字段',
changeBlock: '更改节点', changeBlock: '更改节点',
helpLink: '帮助文档', helpLink: '帮助链接',
about: '关于', about: '关于',
createdBy: '作者', createdBy: '作者',
nextStep: '下一步', nextStep: '下一步',

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect } from 'react' import { useCallback, useEffect, useMemo } from 'react'
import type { import type {
FormOption, FormOption,
ModelProvider, ModelProvider,
@ -96,6 +96,36 @@ export const useRecommendedMarketplacePlugins = ({
}) })
} }
export const useFeaturedToolsRecommendations = (enabled: boolean, limit = 15) => {
const {
data: plugins = [],
isLoading,
} = useRecommendedMarketplacePlugins({
enabled,
limit,
})
const pluginIds = useMemo(
() => plugins.map(plugin => plugin.plugin_id),
[plugins],
)
const installedCheck = useCheckInstalled({
pluginIds,
enabled: enabled && pluginIds.length > 0,
})
const installedIds = useMemo(
() => new Set(installedCheck.data?.plugins.map(plugin => plugin.plugin_id) ?? []),
[installedCheck.data],
)
const installStatusLoading = installedCheck.isLoading || installedCheck.isRefetching
return {
plugins,
isLoading,
installedIds,
installStatusLoading,
refetchInstallStatus: installedCheck.refetch,
}
}
export const useInstalledPluginList = (disable?: boolean, pageSize = 100) => { export const useInstalledPluginList = (disable?: boolean, pageSize = 100) => {
const fetchPlugins = async ({ pageParam = 1 }) => { const fetchPlugins = async ({ pageParam = 1 }) => {
const response = await get<InstalledPluginListWithTotalResponse>( const response = await get<InstalledPluginListWithTotalResponse>(