import type { ModelProviderSummaryResponse } from '@dify/contracts/api/console/workspaces/types.gen' import type { FC } from 'react' import type { ModelProvider } from '../declarations' import type { ModelProviderPluginSummary } from '../index' import type { ModelProviderQuotaGetPaid } from '../utils' import { cn } from '@langgenius/dify-ui/cn' import { useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query' import { useAtomValue } from 'jotai' import { memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' import { PluginCategoryEnum } from '@/app/components/plugins/types' import { workspacePermissionKeysAtom } from '@/context/permission-state' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import { useCredentialPermissions } from '@/hooks/use-credential-permissions' import { renderI18nObject } from '@/i18n/metadata' import { consoleQuery } from '@/service/console' import { commonQueryKeys } from '@/service/use-common' import { useInvalidateInstalledPluginList } from '@/service/use-plugins' import { hasPermission } from '@/utils/permission' import { useModelProviderListExpanded, useSetModelProviderListExpanded } from '../atoms' import { ConfigurationMethodEnum } from '../declarations' import { useLanguage } from '../hooks' import ModelBadge from '../model-badge' import ProviderIcon from '../provider-icon' import { MODEL_PROVIDER_QUOTA_GET_PAID, modelTypeFormat, normalizeModelProviderModelsResponse, } from '../utils' import CredentialPanel from './credential-panel' import LazyCustomModelActions from './lazy-custom-model-actions' import ModelList from './model-list' import ProviderCardActions from './provider-card-actions' type ProviderAddedCardProps = { layout?: 'list' | 'grid' notConfigured?: boolean provider: ModelProviderSummaryResponse | ModelProvider pluginSummary?: ModelProviderPluginSummary } const ProviderAddedCard: FC = ({ layout = 'list', notConfigured, provider, pluginSummary, }) => { const { t } = useTranslation() const { data: { deploymentEdition, rbacEnabled }, } = useSuspenseQuery({ ...systemFeaturesQueryOptions(), select: ({ deployment_edition, rbac_enabled }) => ({ deploymentEdition: deployment_edition, rbacEnabled: rbac_enabled, }), }) const language = useLanguage() const queryClient = useQueryClient() const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const currentProviderName = provider.provider const expanded = useModelProviderListExpanded(currentProviderName) const setExpanded = useSetModelProviderListExpanded(currentProviderName) const supportsPredefinedModel = provider.configurate_methods.some( (method) => method === ConfigurationMethodEnum.predefinedModel, ) const supportsCustomizableModel = provider.configurate_methods.some( (method) => method === ConfigurationMethodEnum.customizableModel, ) const systemConfig = provider.system_configuration const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom) const canSetPluginPreferences = hasPermission( workspacePermissionKeys, 'plugin.plugin_preferences', ) const { data: modelList = [], isFetching: loading, isSuccess: hasFetchedModelList, refetch: refetchModelList, } = useQuery( consoleQuery.workspaces.current.modelProviders.byProvider.models.get.queryOptions({ input: { params: { provider: currentProviderName } }, enabled: expanded && canSetPluginPreferences, refetchOnWindowFocus: false, select: normalizeModelProviderModelsResponse, }), ) const hasModelList = hasFetchedModelList && !!modelList.length const showCollapsedSection = !canSetPluginPreferences || !expanded || !hasFetchedModelList const showModelProvider = systemConfig.enabled && MODEL_PROVIDER_QUOTA_GET_PAID.includes(currentProviderName as ModelProviderQuotaGetPaid) && deploymentEdition === 'CLOUD' const canConfigureModels = hasPermission(workspacePermissionKeys, 'plugin.model_config') const { canUseCredential, canCreateCredential, canManageCredential } = useCredentialPermissions() const canAccessCredentials = rbacEnabled ? canUseCredential || canCreateCredential || canManageCredential : canManageCredential const showCredential = supportsPredefinedModel && canAccessCredentials const showCustomModelActions = supportsCustomizableModel && canConfigureModels const refreshModelList = useCallback( (targetProviderName: string) => { if (targetProviderName !== currentProviderName) return if (!expanded) setExpanded(true) refetchModelList().catch(() => {}) }, [currentProviderName, expanded, refetchModelList, setExpanded], ) const refreshPluginData = useCallback(async () => { await Promise.all([ queryClient.invalidateQueries({ queryKey: consoleQuery.workspaces.current.modelProviders.summary.get.key(), }), queryClient.invalidateQueries({ queryKey: commonQueryKeys.modelProviderDetails }), invalidateInstalledPluginList(PluginCategoryEnum.model), ]) }, [invalidateInstalledPluginList, queryClient]) const handleOpenModelList = useCallback(() => { if (loading) return if (!expanded) { setExpanded(true) return } refetchModelList().catch(() => {}) }, [expanded, loading, refetchModelList, setExpanded]) const providerLabel = renderI18nObject(provider.label, language) const description = renderI18nObject(provider.description || provider.label, language) const organization = currentProviderName.split('/')[0] if (layout === 'grid') { return (
{providerLabel}
{pluginSummary && ( )}
{organization}
{description}
{provider.supported_model_types.slice(0, 4).map((modelType) => ( {modelTypeFormat(modelType)} ))}
{canSetPluginPreferences && (showModelProvider || !notConfigured) && ( )} {!showModelProvider && notConfigured && (
{t(($) => $['modelProvider.configureTip'], { ns: 'common' })}
)} {showCredential && } {showCustomModelActions && (
)}
{!showCollapsedSection && (
setExpanded(false)} onChange={refreshModelList} />
)}
) } return (
{pluginSummary && ( )}
{provider.supported_model_types.map((modelType) => ( {modelTypeFormat(modelType)} ))}
{showCredential && }
{showCollapsedSection && (
{canSetPluginPreferences && (showModelProvider || !notConfigured) && ( )} {!showModelProvider && notConfigured && (
{t(($) => $['modelProvider.configureTip'], { ns: 'common' })}
)} {showCustomModelActions && (
)}
)} {!showCollapsedSection && ( setExpanded(false)} onChange={refreshModelList} /> )}
) } export default memo(ProviderAddedCard)