mirror of
https://github.com/langgenius/dify.git
synced 2026-01-08 07:14:14 +00:00
Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'>
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -126,6 +126,8 @@ const translation = {
|
|||||||
Custom: '自定义',
|
Custom: '自定义',
|
||||||
},
|
},
|
||||||
addMoreModel: '添加更多模型',
|
addMoreModel: '添加更多模型',
|
||||||
|
settingsLink: '模型设置',
|
||||||
|
capabilities: '多模态能力',
|
||||||
},
|
},
|
||||||
menus: {
|
menus: {
|
||||||
status: 'beta',
|
status: 'beta',
|
||||||
|
|||||||
Reference in New Issue
Block a user