Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins

This commit is contained in:
AkaraChen
2024-12-31 13:46:12 +08:00
7 changed files with 105 additions and 18 deletions

View File

@@ -32,7 +32,7 @@ const MenuDialog = ({
return ( return (
<Transition appear show={show} as={Fragment}> <Transition appear show={show} as={Fragment}>
<Dialog as="div" className="relative z-40" onClose={() => {}}> <Dialog as="div" className="relative z-[60]" onClose={() => {}}>
<div className="fixed inset-0"> <div className="fixed inset-0">
<div className="flex flex-col items-center justify-center min-h-full"> <div className="flex flex-col items-center justify-center min-h-full">
<Transition.Child <Transition.Child

View File

@@ -104,6 +104,7 @@ const ModelSelector: FC<ModelSelectorProps> = ({
modelList={modelList} modelList={modelList}
onSelect={handleSelect} onSelect={handleSelect}
scopeFeatures={scopeFeatures} scopeFeatures={scopeFeatures}
onHide={() => setOpen(false)}
/> />
</PortalToFollowElemContent> </PortalToFollowElemContent>
</div> </div>

View File

@@ -1,10 +1,25 @@
import type { FC } from 'react' import type { FC } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import {
RiFileTextLine,
RiFilmAiLine,
RiImageCircleAiLine,
RiVoiceAiFill,
} from '@remixicon/react'
import type { import type {
DefaultModel, DefaultModel,
Model, Model,
ModelItem, ModelItem,
} from '../declarations' } from '../declarations'
import {
ModelFeatureEnum,
ModelFeatureTextEnum,
ModelTypeEnum,
} from '../declarations'
import {
modelTypeFormat,
sizeFormat,
} from '../utils'
import { import {
useLanguage, useLanguage,
useUpdateModelList, useUpdateModelList,
@@ -12,15 +27,16 @@ import {
} from '../hooks' } from '../hooks'
import ModelIcon from '../model-icon' import ModelIcon from '../model-icon'
import ModelName from '../model-name' import ModelName from '../model-name'
import ModelBadge from '../model-badge'
import { import {
ConfigurationMethodEnum, ConfigurationMethodEnum,
MODEL_STATUS_TEXT,
ModelStatusEnum, ModelStatusEnum,
} from '../declarations' } from '../declarations'
import { Check } from '@/app/components/base/icons/src/vender/line/general' import { Check } from '@/app/components/base/icons/src/vender/line/general'
import { useModalContext } from '@/context/modal-context' import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context' import { useProviderContext } from '@/context/provider-context'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import cn from '@/utils/classnames'
type PopupItemProps = { type PopupItemProps = {
defaultModel?: DefaultModel defaultModel?: DefaultModel
@@ -71,34 +87,86 @@ const PopupItem: FC<PopupItemProps> = ({
model.models.map(modelItem => ( model.models.map(modelItem => (
<Tooltip <Tooltip
key={modelItem.model} key={modelItem.model}
popupContent={modelItem.status !== ModelStatusEnum.active ? MODEL_STATUS_TEXT[modelItem.status][language] : undefined}
position='right' position='right'
popupClassName='p-3 !w-[206px] bg-components-panel-bg-blur backdrop-blur-sm border-[0.5px] border-components-panel-border rounded-xl'
popupContent={
<div className='flex flex-col gap-1'>
<div className='flex flex-col gap-2'>
<ModelIcon
className={cn('shrink-0 w-5 h-5')}
provider={model}
modelName={modelItem.model}
/>
<div className='truncate text-text-primary system-md-medium'>{modelItem.label[language] || modelItem.label.en_US}</div>
</div>
{/* {currentProvider?.description && (
<div className='text-text-tertiary system-xs-regular'>{currentProvider?.description?.[language] || currentProvider?.description?.en_US}</div>
)} */}
<div className='flex flex-wrap gap-1'>
{modelItem.model_type && (
<ModelBadge>
{modelTypeFormat(modelItem.model_type)}
</ModelBadge>
)}
{modelItem.model_properties.mode && (
<ModelBadge>
{(modelItem.model_properties.mode as string).toLocaleUpperCase()}
</ModelBadge>
)}
{modelItem.model_properties.context_size && (
<ModelBadge>
{sizeFormat(modelItem.model_properties.context_size as number)}
</ModelBadge>
)}
</div>
{modelItem.model_type === ModelTypeEnum.textGeneration && modelItem.features?.some(feature => [ModelFeatureEnum.vision, ModelFeatureEnum.audio, ModelFeatureEnum.video, ModelFeatureEnum.document].includes(feature)) && (
<div className='pt-2'>
<div className='mb-1 text-text-tertiary system-2xs-medium-uppercase'>{t('common.model.capabilities')}</div>
<div className='flex flex-wrap gap-1'>
{modelItem.features?.includes(ModelFeatureEnum.vision) && (
<ModelBadge>
<RiImageCircleAiLine className='w-3.5 h-3.5 mr-0.5' />
<span>{ModelFeatureTextEnum.vision}</span>
</ModelBadge>
)}
{modelItem.features?.includes(ModelFeatureEnum.audio) && (
<ModelBadge>
<RiVoiceAiFill className='w-3.5 h-3.5 mr-0.5' />
<span>{ModelFeatureTextEnum.audio}</span>
</ModelBadge>
)}
{modelItem.features?.includes(ModelFeatureEnum.video) && (
<ModelBadge>
<RiFilmAiLine className='w-3.5 h-3.5 mr-0.5' />
<span>{ModelFeatureTextEnum.video}</span>
</ModelBadge>
)}
{modelItem.features?.includes(ModelFeatureEnum.document) && (
<ModelBadge>
<RiFileTextLine className='w-3.5 h-3.5 mr-0.5' />
<span>{ModelFeatureTextEnum.document}</span>
</ModelBadge>
)}
</div>
</div>
)}
</div>
}
> >
<div <div
key={modelItem.model} key={modelItem.model}
className={` className={cn('group relative flex items-center px-3 py-1.5 h-8 rounded-lg gap-1', modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-state-base-hover' : 'cursor-not-allowed hover:bg-state-base-hover-alt')}
group relative flex items-center px-3 py-1.5 h-8 rounded-lg gap-1
${modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-state-base-hover' : 'cursor-not-allowed hover:bg-state-base-hover-alt'}
`}
onClick={() => handleSelect(model.provider, modelItem)} onClick={() => handleSelect(model.provider, modelItem)}
> >
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<ModelIcon <ModelIcon
className={` className={cn('shrink-0 w-5 h-5')}
shrink-0 w-4 h-4
${modelItem.status !== ModelStatusEnum.active && 'opacity-60'}
`}
provider={model} provider={model}
modelName={modelItem.model} modelName={modelItem.model}
/> />
<ModelName <ModelName
className={` className={cn('text-text-secondary system-sm-medium', modelItem.status !== ModelStatusEnum.active && 'opacity-60')}
text-text-secondary system-sm-medium
${modelItem.status !== ModelStatusEnum.active && 'opacity-60'}
`}
modelItem={modelItem} modelItem={modelItem}
showMode
showFeatures
/> />
</div> </div>
{ {

View File

@@ -1,6 +1,8 @@
import type { FC } from 'react' import type { FC } from 'react'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { import {
RiArrowRightUpLine,
RiSearchLine, RiSearchLine,
} from '@remixicon/react' } from '@remixicon/react'
import type { import type {
@@ -12,21 +14,26 @@ import { ModelFeatureEnum } from '../declarations'
import { useLanguage } from '../hooks' import { useLanguage } from '../hooks'
import PopupItem from './popup-item' import PopupItem from './popup-item'
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
import { useModalContext } from '@/context/modal-context'
type PopupProps = { type PopupProps = {
defaultModel?: DefaultModel defaultModel?: DefaultModel
modelList: Model[] modelList: Model[]
onSelect: (provider: string, model: ModelItem) => void onSelect: (provider: string, model: ModelItem) => void
scopeFeatures?: string[] scopeFeatures?: string[]
onHide: () => void
} }
const Popup: FC<PopupProps> = ({ const Popup: FC<PopupProps> = ({
defaultModel, defaultModel,
modelList, modelList,
onSelect, onSelect,
scopeFeatures = [], scopeFeatures = [],
onHide,
}) => { }) => {
const { t } = useTranslation()
const language = useLanguage() const language = useLanguage()
const [searchText, setSearchText] = useState('') const [searchText, setSearchText] = useState('')
const { setShowAccountSettingModal } = useModalContext()
const filteredModelList = useMemo(() => { const filteredModelList = useMemo(() => {
return modelList.map((model) => { return modelList.map((model) => {
@@ -99,6 +106,13 @@ const Popup: FC<PopupProps> = ({
) )
} }
</div> </div>
<div className='sticky bottom-0 px-4 py-2 flex items-center border-t border-divider-subtle cursor-pointer text-text-accent-light-mode-only' onClick={() => {
onHide()
setShowAccountSettingModal({ payload: 'provider' })
}}>
<span className='system-xs-medium'>{t('common.model.settingsLink')}</span>
<RiArrowRightUpLine className='ml-0.5 w-3 h-3' />
</div>
</div> </div>
) )
} }

View File

@@ -205,7 +205,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
) )
} }
</PortalToFollowElemTrigger> </PortalToFollowElemTrigger>
<PortalToFollowElemContent className={cn('z-[60]', portalToFollowElemContentClassName)}> <PortalToFollowElemContent className={cn('z-50', portalToFollowElemContentClassName)}>
<div className={cn(popupClassName, 'w-[389px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg')}> <div className={cn(popupClassName, 'w-[389px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg')}>
<div className={cn('max-h-[420px] p-4 pt-3 overflow-y-auto')}> <div className={cn('max-h-[420px] p-4 pt-3 overflow-y-auto')}>
<div className='relative'> <div className='relative'>

View File

@@ -126,6 +126,8 @@ const translation = {
Custom: 'Custom', Custom: 'Custom',
}, },
addMoreModel: 'Go to settings to add more models', addMoreModel: 'Go to settings to add more models',
settingsLink: 'Model Provider Settings',
capabilities: 'MultiModal Capabilities',
}, },
menus: { menus: {
status: 'beta', status: 'beta',

View File

@@ -126,6 +126,8 @@ const translation = {
Custom: '自定义', Custom: '自定义',
}, },
addMoreModel: '添加更多模型', addMoreModel: '添加更多模型',
settingsLink: '模型设置',
capabilities: '多模态能力',
}, },
menus: { menus: {
status: 'beta', status: 'beta',