mirror of
https://github.com/langgenius/dify.git
synced 2026-06-07 16:23:44 +08:00
fix(web): respect marketplace feature flag in model selector (#36883)
This commit is contained in:
parent
07c0c4e7b1
commit
bcd573e560
@ -1264,6 +1264,36 @@ describe('hooks', () => {
|
||||
expect(result.current.plugins).toEqual(searchPlugins)
|
||||
expect(result.current.plugins?.some(p => p.plugin_id === 'collection-only')).toBe(false)
|
||||
})
|
||||
|
||||
it('should skip marketplace queries when disabled', () => {
|
||||
const queryPlugins = vi.fn()
|
||||
const queryPluginsWithDebounced = vi.fn()
|
||||
const cancelQueryPluginsWithDebounced = vi.fn()
|
||||
const resetPlugins = vi.fn()
|
||||
|
||||
; (useMarketplacePluginsByCollectionId as Mock).mockReturnValue({
|
||||
plugins: [{ plugin_id: 'collection-only', type: 'plugin' }],
|
||||
isLoading: true,
|
||||
})
|
||||
; (useMarketplacePlugins as Mock).mockReturnValue({
|
||||
plugins: [{ plugin_id: 'search-result', type: 'plugin' }],
|
||||
queryPlugins,
|
||||
queryPluginsWithDebounced,
|
||||
cancelQueryPluginsWithDebounced,
|
||||
resetPlugins,
|
||||
isLoading: true,
|
||||
})
|
||||
|
||||
const { result } = renderHook(() => useMarketplaceAllPlugins([], '', false))
|
||||
|
||||
expect(useMarketplacePluginsByCollectionId).toHaveBeenCalledWith(undefined)
|
||||
expect(queryPlugins).not.toHaveBeenCalled()
|
||||
expect(queryPluginsWithDebounced).not.toHaveBeenCalled()
|
||||
expect(cancelQueryPluginsWithDebounced).toHaveBeenCalled()
|
||||
expect(resetPlugins).toHaveBeenCalled()
|
||||
expect(result.current.plugins).toEqual([])
|
||||
expect(result.current.isLoading).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('useRefreshModel', () => {
|
||||
|
||||
@ -267,22 +267,30 @@ export const useUpdateModelProviders = () => {
|
||||
return updateModelProviders
|
||||
}
|
||||
|
||||
export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText: string) => {
|
||||
export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText: string, enabled = true) => {
|
||||
const exclude = useMemo(() => {
|
||||
return providers.map(provider => provider.provider.replace(/(.+)\/([^/]+)$/, '$1'))
|
||||
}, [providers])
|
||||
const {
|
||||
plugins: collectionPlugins = [],
|
||||
isLoading: isCollectionLoading,
|
||||
} = useMarketplacePluginsByCollectionId('__model-settings-pinned-models')
|
||||
} = useMarketplacePluginsByCollectionId(enabled ? '__model-settings-pinned-models' : undefined)
|
||||
const {
|
||||
plugins,
|
||||
queryPlugins,
|
||||
queryPluginsWithDebounced,
|
||||
cancelQueryPluginsWithDebounced = () => {},
|
||||
resetPlugins = () => {},
|
||||
isLoading: isPluginsLoading,
|
||||
} = useMarketplacePlugins()
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
cancelQueryPluginsWithDebounced()
|
||||
resetPlugins()
|
||||
return
|
||||
}
|
||||
|
||||
if (searchText) {
|
||||
queryPluginsWithDebounced({
|
||||
query: searchText,
|
||||
@ -304,9 +312,12 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText:
|
||||
sort_order: 'DESC',
|
||||
})
|
||||
}
|
||||
}, [queryPlugins, queryPluginsWithDebounced, searchText, exclude])
|
||||
}, [cancelQueryPluginsWithDebounced, enabled, queryPlugins, queryPluginsWithDebounced, resetPlugins, searchText, exclude])
|
||||
|
||||
const allPlugins = useMemo(() => {
|
||||
if (!enabled)
|
||||
return []
|
||||
|
||||
const allPlugins = collectionPlugins.filter(plugin => !exclude.includes(plugin.plugin_id))
|
||||
|
||||
if (plugins?.length) {
|
||||
@ -319,11 +330,11 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText:
|
||||
}
|
||||
|
||||
return allPlugins
|
||||
}, [plugins, collectionPlugins, exclude])
|
||||
}, [enabled, plugins, collectionPlugins, exclude])
|
||||
|
||||
return {
|
||||
plugins: searchText ? plugins : allPlugins,
|
||||
isLoading: isCollectionLoading || isPluginsLoading,
|
||||
plugins: enabled && searchText ? plugins : allPlugins,
|
||||
isLoading: enabled && (isCollectionLoading || isPluginsLoading),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -103,8 +103,18 @@ function PopupHarness(props: PopupTestProps) {
|
||||
)
|
||||
}
|
||||
|
||||
const renderPopup = (ui: ReactElement<PopupTestProps>) => renderWithSystemFeatures(ui, {
|
||||
trialModels: mockTrialModels.current,
|
||||
const renderPopup = (
|
||||
ui: ReactElement<PopupTestProps>,
|
||||
options: Parameters<typeof renderWithSystemFeatures>[1] = {},
|
||||
) => renderWithSystemFeatures(ui, {
|
||||
...options,
|
||||
systemFeatures: options.systemFeatures === null
|
||||
? null
|
||||
: {
|
||||
enable_marketplace: true,
|
||||
...(options.systemFeatures ?? {}),
|
||||
},
|
||||
trialModels: options.trialModels ?? mockTrialModels.current,
|
||||
})
|
||||
|
||||
const mockTrialCredits = vi.hoisted(() => ({
|
||||
@ -830,6 +840,26 @@ describe('Popup', () => {
|
||||
expect(screen.getByText(/modelProvider\.selector\.discoverMoreInMarketplace/))!.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should hide marketplace providers when marketplace is disabled', () => {
|
||||
mockContextModelProviders.current = [makeContextProvider({ provider: 'test-openai' })]
|
||||
|
||||
renderPopup(
|
||||
<PopupHarness
|
||||
modelList={[makeModel({ provider: 'test-openai' })]}
|
||||
onHide={vi.fn()}
|
||||
/>,
|
||||
{
|
||||
systemFeatures: { enable_marketplace: false },
|
||||
},
|
||||
)
|
||||
|
||||
expect(screen.getByText('test-openai'))!.toBeInTheDocument()
|
||||
expect(screen.queryByText('TestAnthropic')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText(/modelProvider\.selector\.fromMarketplace/)).not.toBeInTheDocument()
|
||||
expect(screen.queryByText(/modelProvider\.selector\.discoverMoreInMarketplace/)).not.toBeInTheDocument()
|
||||
expect(screen.queryByText(/common\.modelProvider\.selector\.install/)).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show installed marketplace providers without models when AI credits are available', () => {
|
||||
mockContextModelProviders.current = [makeContextProvider({
|
||||
provider: 'test-anthropic',
|
||||
|
||||
@ -3,7 +3,7 @@ import type { ModelSelectorPreviewPayload } from './popup-item'
|
||||
import type { ModelProviderQuotaGetPaid } from '@/types/model-provider'
|
||||
import { ComboboxList } from '@langgenius/dify-ui/combobox'
|
||||
import { createPreviewCardHandle, PreviewCard, PreviewCardContent } from '@langgenius/dify-ui/preview-card'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -15,6 +15,7 @@ import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useSearchParams } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
import { systemFeaturesQueryOptions } from '@/service/system-features'
|
||||
import { useInstallPackageFromMarketPlace } from '@/service/use-plugins'
|
||||
import { CustomConfigurationStatusEnum, ModelFeatureEnum, ModelStatusEnum, ModelTypeEnum } from '../declarations'
|
||||
import { useLanguage, useMarketplaceAllPlugins } from '../hooks'
|
||||
@ -55,10 +56,14 @@ function Popup({
|
||||
const [marketplaceCollapsed, setMarketplaceCollapsed] = useState(false)
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const { modelProviders } = useProviderContext()
|
||||
const { data: enableMarketplace } = useSuspenseQuery({
|
||||
...systemFeaturesQueryOptions(),
|
||||
select: systemFeatures => systemFeatures.enable_marketplace,
|
||||
})
|
||||
const {
|
||||
plugins: allPlugins,
|
||||
isLoading: isMarketplacePluginsLoading,
|
||||
} = useMarketplaceAllPlugins(modelProviders, '')
|
||||
} = useMarketplaceAllPlugins(modelProviders, '', enableMarketplace)
|
||||
const { mutateAsync: installPackageFromMarketPlace } = useInstallPackageFromMarketPlace()
|
||||
const { refreshPluginList } = useRefreshPluginList()
|
||||
const [installingProvider, setInstallingProvider] = useState<ModelProviderQuotaGetPaid | null>(null)
|
||||
@ -71,7 +76,7 @@ function Popup({
|
||||
modelProviders.map(provider => [provider.provider, provider]),
|
||||
), [modelProviders])
|
||||
const aiCreditVisibleProviders = useMemo(() => {
|
||||
if (isCreditsExhausted)
|
||||
if (!enableMarketplace || isCreditsExhausted)
|
||||
return new Set<string>()
|
||||
|
||||
return new Set(
|
||||
@ -79,8 +84,9 @@ function Popup({
|
||||
.filter(provider => providerSupportsCredits(provider, trialModels))
|
||||
.map(provider => provider.provider),
|
||||
)
|
||||
}, [isCreditsExhausted, modelProviders, trialModels])
|
||||
const showCreditsExhaustedAlert = isCreditsExhausted
|
||||
}, [enableMarketplace, isCreditsExhausted, modelProviders, trialModels])
|
||||
const showCreditsExhaustedAlert = enableMarketplace
|
||||
&& isCreditsExhausted
|
||||
&& modelProviders.some(provider => providerSupportsCredits(provider, trialModels))
|
||||
const hasApiKeyFallback = modelProviders.some((provider) => {
|
||||
const isApiKeyActive = provider.custom_configuration?.status === CustomConfigurationStatusEnum.active
|
||||
@ -88,7 +94,7 @@ function Popup({
|
||||
})
|
||||
|
||||
const handleInstallPlugin = useCallback(async (key: ModelProviderQuotaGetPaid) => {
|
||||
if (!allPlugins || isMarketplacePluginsLoading || installingProvider)
|
||||
if (!enableMarketplace || !allPlugins || isMarketplacePluginsLoading || installingProvider)
|
||||
return
|
||||
const pluginId = providerKeyToPluginId[key]
|
||||
const plugin = allPlugins.find(p => p.plugin_id === pluginId)
|
||||
@ -109,7 +115,7 @@ function Popup({
|
||||
finally {
|
||||
setInstallingProvider(null)
|
||||
}
|
||||
}, [allPlugins, installPackageFromMarketPlace, installingProvider, isMarketplacePluginsLoading, refreshPluginList])
|
||||
}, [allPlugins, enableMarketplace, installPackageFromMarketPlace, installingProvider, isMarketplacePluginsLoading, refreshPluginList])
|
||||
|
||||
const installedModelList = useMemo(() => {
|
||||
const modelMap = new Map(modelList.map(model => [model.provider, model]))
|
||||
@ -154,9 +160,12 @@ function Popup({
|
||||
}), [aiCreditVisibleProviders, defaultModel, inputValue, installedModelList, scopeFeatures, searchIndex])
|
||||
|
||||
const marketplaceProviders = useMemo(() => {
|
||||
if (!enableMarketplace)
|
||||
return []
|
||||
|
||||
const installedProviders = new Set(modelProviders.map(provider => provider.provider))
|
||||
return MODEL_PROVIDER_QUOTA_GET_PAID.filter(key => !installedProviders.has(key))
|
||||
}, [modelProviders])
|
||||
}, [enableMarketplace, modelProviders])
|
||||
|
||||
const handleOpenSettings = useCallback(() => {
|
||||
onHide()
|
||||
@ -208,15 +217,17 @@ function Popup({
|
||||
{scopeFeatures.length > 0 && (
|
||||
<CompatibleModelsNotice />
|
||||
)}
|
||||
<MarketplaceSection
|
||||
marketplaceProviders={marketplaceProviders}
|
||||
marketplaceCollapsed={marketplaceCollapsed}
|
||||
installingProvider={installingProvider}
|
||||
isMarketplacePluginsLoading={isMarketplacePluginsLoading}
|
||||
theme={theme}
|
||||
onMarketplaceCollapsedChange={setMarketplaceCollapsed}
|
||||
onInstallPlugin={handleInstallPlugin}
|
||||
/>
|
||||
{enableMarketplace && (
|
||||
<MarketplaceSection
|
||||
marketplaceProviders={marketplaceProviders}
|
||||
marketplaceCollapsed={marketplaceCollapsed}
|
||||
installingProvider={installingProvider}
|
||||
isMarketplacePluginsLoading={isMarketplacePluginsLoading}
|
||||
theme={theme}
|
||||
onMarketplaceCollapsedChange={setMarketplaceCollapsed}
|
||||
onInstallPlugin={handleInstallPlugin}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ModelSelectorScrollBody>
|
||||
<PreviewCard handle={previewCardHandle}>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user