mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 00:48:04 +08:00
Merge branch 'feat/plugins' into dev/plugin-deploy
This commit is contained in:
@ -16,6 +16,7 @@ import type {
|
||||
} from './declarations'
|
||||
import {
|
||||
ConfigurationMethodEnum,
|
||||
CustomConfigurationStatusEnum,
|
||||
ModelStatusEnum,
|
||||
} from './declarations'
|
||||
import I18n from '@/context/i18n'
|
||||
@ -33,6 +34,9 @@ import {
|
||||
import type { Plugin } from '@/app/components/plugins/types'
|
||||
import { PluginType } from '@/app/components/plugins/types'
|
||||
import { getMarketplacePluginsByCollectionId } from '@/app/components/plugins/marketplace/utils'
|
||||
import { useModalContextSelector } from '@/context/modal-context'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card'
|
||||
|
||||
type UseDefaultModelAndModelList = (
|
||||
defaultModel: DefaultModelResponse | undefined,
|
||||
@ -304,3 +308,42 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText:
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
|
||||
export const useModelModalHandler = () => {
|
||||
const setShowModelModal = useModalContextSelector(state => state.setShowModelModal)
|
||||
const updateModelProviders = useUpdateModelProviders()
|
||||
const updateModelList = useUpdateModelList()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
|
||||
return (
|
||||
provider: ModelProvider,
|
||||
configurationMethod: ConfigurationMethodEnum,
|
||||
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||
) => {
|
||||
setShowModelModal({
|
||||
payload: {
|
||||
currentProvider: provider,
|
||||
currentConfigurationMethod: configurationMethod,
|
||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
||||
},
|
||||
onSaveCallback: () => {
|
||||
updateModelProviders()
|
||||
|
||||
provider.supported_model_types.forEach((type) => {
|
||||
updateModelList(type)
|
||||
})
|
||||
|
||||
if (configurationMethod === ConfigurationMethodEnum.customizableModel
|
||||
&& provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
||||
eventEmitter?.emit({
|
||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||
payload: provider.provider,
|
||||
} as any)
|
||||
|
||||
if (CustomConfigurationModelFixedFields?.__model_type)
|
||||
updateModelList(CustomConfigurationModelFixedFields.__model_type)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,19 +9,21 @@ import {
|
||||
RiBrainLine,
|
||||
} from '@remixicon/react'
|
||||
import SystemModelSelector from './system-model-selector'
|
||||
import ProviderAddedCard, { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card'
|
||||
import ProviderAddedCard from './provider-added-card'
|
||||
import type {
|
||||
ConfigurationMethodEnum,
|
||||
CustomConfigurationModelFixedFields,
|
||||
|
||||
ModelProvider,
|
||||
} from './declarations'
|
||||
import {
|
||||
ConfigurationMethodEnum,
|
||||
CustomConfigurationStatusEnum,
|
||||
ModelTypeEnum,
|
||||
} from './declarations'
|
||||
import {
|
||||
useDefaultModel,
|
||||
useMarketplaceAllPlugins,
|
||||
useModelModalHandler,
|
||||
useUpdateModelList,
|
||||
useUpdateModelProviders,
|
||||
} from './hooks'
|
||||
@ -87,37 +89,7 @@ const ModelProviderPage = ({ searchText }: Props) => {
|
||||
return [filteredConfiguredProviders, filteredNotConfiguredProviders]
|
||||
}, [configuredProviders, debouncedSearchText, notConfiguredProviders])
|
||||
|
||||
const handleOpenModal = (
|
||||
provider: ModelProvider,
|
||||
configurationMethod: ConfigurationMethodEnum,
|
||||
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||
) => {
|
||||
setShowModelModal({
|
||||
payload: {
|
||||
currentProvider: provider,
|
||||
currentConfigurationMethod: configurationMethod,
|
||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
||||
},
|
||||
onSaveCallback: () => {
|
||||
updateModelProviders()
|
||||
|
||||
provider.supported_model_types.forEach((type) => {
|
||||
updateModelList(type)
|
||||
})
|
||||
|
||||
if (configurationMethod === ConfigurationMethodEnum.customizableModel && provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
||||
eventEmitter?.emit({
|
||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||
payload: provider.provider,
|
||||
} as any)
|
||||
|
||||
if (CustomConfigurationModelFixedFields?.__model_type)
|
||||
updateModelList(CustomConfigurationModelFixedFields?.__model_type)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const handleOpenModal = useModelModalHandler()
|
||||
const [collapse, setCollapse] = useState(false)
|
||||
const locale = getLocaleOnClient()
|
||||
const {
|
||||
|
||||
@ -2,27 +2,25 @@ import type { FC } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type {
|
||||
CustomConfigurationModelFixedFields,
|
||||
ModelItem,
|
||||
ModelProvider,
|
||||
} from '../declarations'
|
||||
import {
|
||||
ConfigurationMethodEnum,
|
||||
CustomConfigurationStatusEnum,
|
||||
ModelTypeEnum,
|
||||
} from '../declarations'
|
||||
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from '../provider-added-card'
|
||||
import type { PluginInfoFromMarketPlace } from '@/app/components/plugins/types'
|
||||
import { useInstallPackageFromMarketPlace } from '@/service/use-plugins'
|
||||
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||
import ConfigurationButton from './configuration-button'
|
||||
import { PluginType } from '@/app/components/plugins/types'
|
||||
import {
|
||||
useModelModalHandler,
|
||||
useUpdateModelList,
|
||||
useUpdateModelProviders,
|
||||
} from '../hooks'
|
||||
import ModelIcon from '../model-icon'
|
||||
import ModelDisplay from './model-display'
|
||||
import InstallButton from '@/app/components/base/install-button'
|
||||
import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
|
||||
import StatusIndicators from './status-indicators'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
@ -72,10 +70,9 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||
}, [modelProviders, providerName])
|
||||
const [pluginInfo, setPluginInfo] = useState<PluginInfoFromMarketPlace | null>(null)
|
||||
const [isPluginChecked, setIsPluginChecked] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [installed, setInstalled] = useState(false)
|
||||
const { mutateAsync: installPackageFromMarketPlace } = useInstallPackageFromMarketPlace()
|
||||
|
||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||
const handleOpenModal = useModelModalHandler()
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (providerName && !modelProvider) {
|
||||
@ -101,66 +98,6 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||
if (modelId && !isPluginChecked)
|
||||
return null
|
||||
|
||||
const handleOpenModal = (
|
||||
provider: ModelProvider,
|
||||
configurationMethod: ConfigurationMethodEnum,
|
||||
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||
) => {
|
||||
setShowModelModal({
|
||||
payload: {
|
||||
currentProvider: provider,
|
||||
currentConfigurationMethod: configurationMethod,
|
||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
||||
},
|
||||
onSaveCallback: () => {
|
||||
updateModelProviders()
|
||||
|
||||
provider.supported_model_types.forEach((type) => {
|
||||
updateModelList(type)
|
||||
})
|
||||
|
||||
if (configurationMethod === ConfigurationMethodEnum.customizableModel
|
||||
&& provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
||||
eventEmitter?.emit({
|
||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||
payload: provider.provider,
|
||||
} as any)
|
||||
|
||||
if (CustomConfigurationModelFixedFields?.__model_type)
|
||||
updateModelList(CustomConfigurationModelFixedFields.__model_type)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const handleInstall = async (pluginInfo: PluginInfoFromMarketPlace) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const { all_installed } = await installPackageFromMarketPlace(pluginInfo.latest_package_identifier)
|
||||
if (all_installed) {
|
||||
[
|
||||
ModelTypeEnum.textGeneration,
|
||||
ModelTypeEnum.textEmbedding,
|
||||
ModelTypeEnum.rerank,
|
||||
ModelTypeEnum.moderation,
|
||||
ModelTypeEnum.speech2text,
|
||||
ModelTypeEnum.tts,
|
||||
].forEach((type: ModelTypeEnum) => {
|
||||
if (scope?.includes(type))
|
||||
updateModelList(type)
|
||||
})
|
||||
updateModelProviders()
|
||||
setInstalled(true)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Installation failed:', error)
|
||||
}
|
||||
finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@ -193,13 +130,27 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||
t={t}
|
||||
/>
|
||||
{!installed && !modelProvider && pluginInfo && (
|
||||
<InstallButton
|
||||
loading={loading}
|
||||
onInstall={(e) => {
|
||||
e.stopPropagation()
|
||||
handleInstall(pluginInfo)
|
||||
<InstallPluginButton
|
||||
onClick={e => e.stopPropagation()}
|
||||
size={'small'}
|
||||
uniqueIdentifier={pluginInfo.latest_package_identifier}
|
||||
onSuccess={() => {
|
||||
[
|
||||
ModelTypeEnum.textGeneration,
|
||||
ModelTypeEnum.textEmbedding,
|
||||
ModelTypeEnum.rerank,
|
||||
ModelTypeEnum.moderation,
|
||||
ModelTypeEnum.speech2text,
|
||||
ModelTypeEnum.tts,
|
||||
].forEach((type: ModelTypeEnum) => {
|
||||
if (scope?.includes(type))
|
||||
updateModelList(type)
|
||||
},
|
||||
)
|
||||
updateModelProviders()
|
||||
invalidateInstalledPluginList()
|
||||
setInstalled(true)
|
||||
}}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
{modelProvider && !disabled && !needsConfiguration && (
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
@keyframes realistic-blink {
|
||||
0% { fill: #37ff37; opacity: 0.4; }
|
||||
15% { fill: #37ff37; opacity: 0.9; }
|
||||
25% { fill: #37ff37; opacity: 0.3; }
|
||||
38% { fill: #ff4444; opacity: 0.8; }
|
||||
42% { fill: #ff4444; opacity: 0.3; }
|
||||
58% { fill: #37ff37; opacity: 0.9; }
|
||||
65% { fill: #37ff37; opacity: 0.4; }
|
||||
79% { fill: #ff4444; opacity: 0.8; }
|
||||
84% { fill: #ff4444; opacity: 0.3; }
|
||||
92% { fill: #37ff37; opacity: 0.8; }
|
||||
100% { fill: #37ff37; opacity: 0.4; }
|
||||
}
|
||||
|
||||
@keyframes drop {
|
||||
0% {
|
||||
transform: translateY(-4px);
|
||||
opacity: 0;
|
||||
}
|
||||
5% {
|
||||
transform: translateY(-4px);
|
||||
opacity: 1;
|
||||
}
|
||||
65% {
|
||||
transform: translateY(2px);
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
transform: translateY(2px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(2px);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#downloadingIconLight {
|
||||
animation: realistic-blink 3s infinite ease-in-out;
|
||||
}
|
||||
|
||||
#downloadingIconArrow {
|
||||
animation: drop 1.2s cubic-bezier(0.4, 0, 1, 1) infinite;
|
||||
}
|
||||
17
web/app/components/header/plugins-nav/downloading-icon.tsx
Normal file
17
web/app/components/header/plugins-nav/downloading-icon.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import s from './downloading-icon.module.css'
|
||||
|
||||
const DownloadingIcon = () => {
|
||||
return (
|
||||
<div className="inline-flex text-components-button-secondary-text">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="install-icon">
|
||||
<g id="install-line">
|
||||
<path d="M8 2V4H5L4.999 14H18.999L19 4H16V2H20C20.5523 2 21 2.44772 21 3V21C21 21.5523 20.5523 22 20 22H4C3.44772 22 3 21.5523 3 21V3C3 2.44772 3.44772 2 4 2H8ZM18.999 16H4.999L5 20H19L18.999 16Z" fill="currentColor"/>
|
||||
<path id={s.downloadingIconLight} d="M17 19V17H15V19H17Z"/>
|
||||
<path id={s.downloadingIconArrow} d="M13 2V7H16L12 11L8 7H11V2H13Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DownloadingIcon
|
||||
@ -5,6 +5,9 @@ import Link from 'next/link'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { Group } from '@/app/components/base/icons/src/vender/other'
|
||||
import { useSelectedLayoutSegment } from 'next/navigation'
|
||||
import DownloadingIcon from './downloading-icon'
|
||||
import { usePluginTaskStatus } from '@/app/components/plugins/plugin-page/plugin-tasks/hooks'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
|
||||
type PluginsNavProps = {
|
||||
className?: string
|
||||
@ -16,17 +19,43 @@ const PluginsNav = ({
|
||||
const { t } = useTranslation()
|
||||
const selectedSegment = useSelectedLayoutSegment()
|
||||
const activated = selectedSegment === 'plugins'
|
||||
const {
|
||||
isInstalling,
|
||||
isInstallingWithError,
|
||||
isFailed,
|
||||
} = usePluginTaskStatus()
|
||||
|
||||
return (
|
||||
<Link href="/plugins" className={classNames(
|
||||
className, 'group',
|
||||
)}>
|
||||
<div className={`flex flex-row h-8 p-1.5 gap-0.5 items-center justify-center
|
||||
rounded-xl system-sm-medium-uppercase ${activated
|
||||
? 'border border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active shadow-md text-components-main-nav-nav-button-text'
|
||||
: 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary'}`}>
|
||||
<div
|
||||
className={classNames(
|
||||
'relative flex flex-row h-8 p-1.5 gap-0.5 border border-transparent items-center justify-center rounded-xl system-sm-medium-uppercase',
|
||||
activated && 'border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active shadow-md text-components-main-nav-nav-button-text',
|
||||
!activated && 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
(isInstallingWithError || isFailed) && !activated && 'border-components-panel-border-subtle',
|
||||
)}
|
||||
>
|
||||
{
|
||||
(isFailed || isInstallingWithError) && !activated && (
|
||||
<Indicator
|
||||
color='red'
|
||||
className='absolute top-[-1px] left-[-1px]'
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div className='flex mr-0.5 w-5 h-5 justify-center items-center'>
|
||||
<Group className='w-4 h-4' />
|
||||
{
|
||||
(!(isInstalling || isInstallingWithError) || activated) && (
|
||||
<Group className='w-4 h-4' />
|
||||
)
|
||||
}
|
||||
{
|
||||
(isInstalling || isInstallingWithError) && !activated && (
|
||||
<DownloadingIcon />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<span className='px-0.5'>{t('common.menus.plugins')}</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user