| | |
| | | import type { FC } from 'react' |
| | | import { useTranslation } from 'react-i18next' |
| | | import { |
| | | RiFileTextLine, |
| | | RiFilmAiLine, |
| | | RiImageCircleAiLine, |
| | | RiVoiceAiFill, |
| | | } from '@remixicon/react' |
| | | import type { |
| | | DefaultModel, |
| | | Model, |
| | | ModelItem, |
| | | } from '../declarations' |
| | | import { |
| | | ModelFeatureEnum, |
| | | ModelFeatureTextEnum, |
| | | ModelTypeEnum, |
| | | } from '../declarations' |
| | | import { |
| | | modelTypeFormat, |
| | | sizeFormat, |
| | | } from '../utils' |
| | | import { |
| | | useLanguage, |
| | | useUpdateModelList, |
| | |
| | | } from '../hooks' |
| | | import ModelIcon from '../model-icon' |
| | | import ModelName from '../model-name' |
| | | import ModelBadge from '../model-badge' |
| | | import { |
| | | ConfigurationMethodEnum, |
| | | MODEL_STATUS_TEXT, |
| | | ModelStatusEnum, |
| | | } from '../declarations' |
| | | import { Check } from '@/app/components/base/icons/src/vender/line/general' |
| | | import { useModalContext } from '@/context/modal-context' |
| | | import { useProviderContext } from '@/context/provider-context' |
| | | import Tooltip from '@/app/components/base/tooltip' |
| | | import cn from '@/utils/classnames' |
| | | |
| | | type PopupItemProps = { |
| | | defaultModel?: DefaultModel |
| | |
| | | |
| | | return ( |
| | | <div className='mb-1'> |
| | | <div className='flex h-[22px] items-center px-3 text-xs font-medium text-text-tertiary'> |
| | | <div className='flex items-center px-3 h-[22px] text-xs font-medium text-gray-500'> |
| | | {model.label[language] || model.label.en_US} |
| | | </div> |
| | | { |
| | | model.models.map(modelItem => ( |
| | | <Tooltip |
| | | key={modelItem.model} |
| | | popupContent={modelItem.status !== ModelStatusEnum.active ? MODEL_STATUS_TEXT[modelItem.status][language] : undefined} |
| | | 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 items-start gap-2'> |
| | | <ModelIcon |
| | | className={cn('h-5 w-5 shrink-0')} |
| | | provider={model} |
| | | modelName={modelItem.model} |
| | | /> |
| | | <div className='system-md-medium text-wrap break-words text-text-primary'>{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='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('common.model.capabilities')}</div> |
| | | <div className='flex flex-wrap gap-1'> |
| | | {modelItem.features?.includes(ModelFeatureEnum.vision) && ( |
| | | <ModelBadge> |
| | | <RiImageCircleAiLine className='mr-0.5 h-3.5 w-3.5' /> |
| | | <span>{ModelFeatureTextEnum.vision}</span> |
| | | </ModelBadge> |
| | | )} |
| | | {modelItem.features?.includes(ModelFeatureEnum.audio) && ( |
| | | <ModelBadge> |
| | | <RiVoiceAiFill className='mr-0.5 h-3.5 w-3.5' /> |
| | | <span>{ModelFeatureTextEnum.audio}</span> |
| | | </ModelBadge> |
| | | )} |
| | | {modelItem.features?.includes(ModelFeatureEnum.video) && ( |
| | | <ModelBadge> |
| | | <RiFilmAiLine className='mr-0.5 h-3.5 w-3.5' /> |
| | | <span>{ModelFeatureTextEnum.video}</span> |
| | | </ModelBadge> |
| | | )} |
| | | {modelItem.features?.includes(ModelFeatureEnum.document) && ( |
| | | <ModelBadge> |
| | | <RiFileTextLine className='mr-0.5 h-3.5 w-3.5' /> |
| | | <span>{ModelFeatureTextEnum.document}</span> |
| | | </ModelBadge> |
| | | )} |
| | | </div> |
| | | </div> |
| | | )} |
| | | </div> |
| | | } |
| | | > |
| | | <div |
| | | key={modelItem.model} |
| | | className={cn('group relative flex h-8 items-center gap-1 rounded-lg px-3 py-1.5', modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-state-base-hover' : 'cursor-not-allowed hover:bg-state-base-hover-alt')} |
| | | className={` |
| | | group relative flex items-center px-3 py-1.5 h-8 rounded-lg |
| | | ${modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-gray-50' : 'cursor-not-allowed hover:bg-gray-50/60'} |
| | | `} |
| | | onClick={() => handleSelect(model.provider, modelItem)} |
| | | > |
| | | <div className='flex items-center gap-2'> |
| | | <ModelIcon |
| | | className={cn('h-5 w-5 shrink-0')} |
| | | className={` |
| | | shrink-0 mr-2 w-4 h-4 |
| | | ${modelItem.status !== ModelStatusEnum.active && 'opacity-60'} |
| | | `} |
| | | provider={model} |
| | | modelName={modelItem.model} |
| | | /> |
| | | <ModelName |
| | | className={cn('system-sm-medium text-text-secondary', modelItem.status !== ModelStatusEnum.active && 'opacity-60')} |
| | | className={` |
| | | grow text-sm font-normal text-gray-900 |
| | | ${modelItem.status !== ModelStatusEnum.active && 'opacity-60'} |
| | | `} |
| | | modelItem={modelItem} |
| | | showMode |
| | | showFeatures |
| | | /> |
| | | </div> |
| | | { |
| | | defaultModel?.model === modelItem.model && defaultModel.provider === currentProvider.provider && ( |
| | | <Check className='h-4 w-4 shrink-0 text-text-accent' /> |
| | | <Check className='shrink-0 w-4 h-4 text-primary-600' /> |
| | | ) |
| | | } |
| | | { |
| | | modelItem.status === ModelStatusEnum.noConfigure && ( |
| | | <div |
| | | className='hidden cursor-pointer text-xs font-medium text-text-accent group-hover:block' |
| | | className='hidden group-hover:block text-xs font-medium text-primary-600 cursor-pointer' |
| | | onClick={handleOpenModelModal} |
| | | > |
| | | {t('common.operation.add').toLocaleUpperCase()} |