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 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 type { Plugin } from '../../plugins/types'
import { PluginCategoryEnum } from '../../plugins/types'
import { useMarketplacePlugins } from '../../plugins/marketplace/hooks'
import { useGlobalPublicStore } from '@/context/global-public-context'
import RAGToolSuggestions from './rag-tool-suggestions'
import FeaturedTools from './featured-tools'
import { useCheckInstalled, useRecommendedMarketplacePlugins } from '@/service/use-plugins'
import { useInvalidateAllBuiltInTools } from '@/service/use-tools'
import Link from 'next/link'
type AllToolsProps = {
@ -50,6 +49,12 @@ type AllToolsProps = {
canChooseMCPTool?: boolean
onTagsChange?: Dispatch<SetStateAction<string[]>>
isInRAGPipeline?: boolean
featuredPlugins?: Plugin[]
featuredLoading?: boolean
featuredInstalledPluginIds?: Set<string>
featuredInstallLoading?: boolean
showFeatured?: boolean
onFeaturedInstallSuccess?: () => Promise<void> | void
}
const DEFAULT_TAGS: AllToolsProps['tags'] = []
@ -70,6 +75,12 @@ const AllTools = ({
canChooseMCPTool,
onTagsChange,
isInRAGPipeline = false,
featuredPlugins = [],
featuredLoading = false,
featuredInstalledPluginIds = new Set<string>(),
featuredInstallLoading = false,
showFeatured = false,
onFeaturedInstallSuccess,
}: AllToolsProps) => {
const { t } = useTranslation()
const language = useGetLanguage()
@ -149,26 +160,6 @@ const AllTools = ({
} = useMarketplacePlugins()
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(() => {
if (!enable_marketplace) return
@ -189,12 +180,12 @@ const AllTools = ({
const hasToolsContent = tools.length > 0
const hasPluginContent = enable_marketplace && notInstalledPlugins.length > 0
const shouldShowEmptyState = hasFilter && !hasToolsContent && !hasPluginContent
const shouldShowFeatured = enable_marketplace
const shouldShowFeatured = showFeatured
&& enable_marketplace
&& !isInRAGPipeline
&& activeTab === ToolTypeEnum.All
&& !hasFilter
&& !isLoadingRecommended
&& !isInRAGPipeline
&& recommendedPlugins.length > 0
&& (featuredLoading || featuredPlugins.length > 0)
return (
<div className={cn('min-w-[400px] max-w-[500px]', className)}>
@ -235,17 +226,16 @@ const AllTools = ({
)}
{shouldShowFeatured && (
<FeaturedTools
plugins={recommendedPlugins}
plugins={featuredPlugins}
providerMap={providerMap}
onSelect={onSelect}
selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
installedPluginIds={installedPluginIds}
loadingInstalledStatus={loadingRecommendedInstallStatus}
isLoading={isLoadingRecommended}
installedPluginIds={featuredInstalledPluginIds}
loadingInstalledStatus={featuredInstallLoading}
isLoading={featuredLoading}
onInstallSuccess={async () => {
invalidateBuiltInTools()
await installedCheck.refetch()
await onFeaturedInstallSuccess?.()
}}
/>
)}

View File

@ -1,6 +1,6 @@
import type { Dispatch, FC, SetStateAction } 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 {
BlockEnum,
NodeDefault,
@ -13,6 +13,8 @@ import AllStartBlocks from './all-start-blocks'
import AllTools from './all-tools'
import DataSources from './data-sources'
import cn from '@/utils/classnames'
import { useFeaturedToolsRecommendations } from '@/service/use-plugins'
import { useGlobalPublicStore } from '@/context/global-public-context'
export type TabsProps = {
activeTab: TabsEnum
@ -53,6 +55,16 @@ const Tabs: FC<TabsProps> = ({
const { data: customTools } = useAllCustomTools()
const { data: workflowTools } = useAllWorkflowTools()
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 (
<div onClick={e => e.stopPropagation()}>
@ -127,7 +139,16 @@ const Tabs: FC<TabsProps> = ({
mcpTools={mcpTools || []}
canChooseMCPTool
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'
import type { CustomCollectionBackend } from '@/app/components/tools/types'
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'
type Props = {
@ -61,11 +63,21 @@ const ToolPicker: FC<Props> = ({
const [searchText, setSearchText] = useState('')
const [tags, setTags] = useState<string[]>([])
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const { data: buildInTools } = useAllBuiltInTools()
const { data: customTools } = useAllCustomTools()
const invalidateCustomTools = useInvalidateAllCustomTools()
const { data: workflowTools } = useAllWorkflowTools()
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(() => {
if (scope === 'plugins') {
@ -179,6 +191,15 @@ const ToolPicker: FC<Props> = ({
selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
onTagsChange={setTags}
featuredPlugins={featuredPlugins}
featuredLoading={isFeaturedLoading}
featuredInstalledPluginIds={featuredInstalledIds}
featuredInstallLoading={featuredInstallLoading}
showFeatured={scope === 'all' && enable_marketplace}
onFeaturedInstallSuccess={async () => {
invalidateBuiltInTools()
await refetchFeaturedInstallStatus()
}}
/>
</div>
</PortalToFollowElemContent>

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect } from 'react'
import { useCallback, useEffect, useMemo } from 'react'
import type {
FormOption,
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) => {
const fetchPlugins = async ({ pageParam = 1 }) => {
const response = await get<InstalledPluginListWithTotalResponse>(