add-custom-model.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import {
  2. memo,
  3. useCallback,
  4. useState,
  5. } from 'react'
  6. import { useTranslation } from 'react-i18next'
  7. import {
  8. RiAddCircleFill,
  9. RiAddLine,
  10. } from '@remixicon/react'
  11. import {
  12. Button,
  13. } from '@/app/components/base/button'
  14. import type {
  15. ConfigurationMethodEnum,
  16. CustomConfigurationModelFixedFields,
  17. ModelProvider,
  18. } from '@/app/components/header/account-setting/model-provider-page/declarations'
  19. import { ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
  20. import cn from '@/utils/classnames'
  21. import {
  22. PortalToFollowElem,
  23. PortalToFollowElemContent,
  24. PortalToFollowElemTrigger,
  25. } from '@/app/components/base/portal-to-follow-elem'
  26. import ModelIcon from '../model-icon'
  27. import { useCanAddedModels } from './hooks/use-custom-models'
  28. import { useAuth } from './hooks/use-auth'
  29. import Tooltip from '@/app/components/base/tooltip'
  30. type AddCustomModelProps = {
  31. provider: ModelProvider,
  32. configurationMethod: ConfigurationMethodEnum,
  33. currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
  34. open?: boolean
  35. onOpenChange?: (open: boolean) => void
  36. }
  37. const AddCustomModel = ({
  38. provider,
  39. configurationMethod,
  40. currentCustomConfigurationModelFixedFields,
  41. }: AddCustomModelProps) => {
  42. const { t } = useTranslation()
  43. const [open, setOpen] = useState(false)
  44. const canAddedModels = useCanAddedModels(provider)
  45. const noModels = !canAddedModels.length
  46. const {
  47. handleOpenModal: handleOpenModalForAddNewCustomModel,
  48. } = useAuth(
  49. provider,
  50. configurationMethod,
  51. currentCustomConfigurationModelFixedFields,
  52. {
  53. isModelCredential: true,
  54. mode: ModelModalModeEnum.configCustomModel,
  55. },
  56. )
  57. const {
  58. handleOpenModal: handleOpenModalForAddCustomModelToModelList,
  59. } = useAuth(
  60. provider,
  61. configurationMethod,
  62. currentCustomConfigurationModelFixedFields,
  63. {
  64. isModelCredential: true,
  65. mode: ModelModalModeEnum.addCustomModelToModelList,
  66. },
  67. )
  68. const notAllowCustomCredential = provider.allow_custom_token === false
  69. const renderTrigger = useCallback((open?: boolean) => {
  70. const Item = (
  71. <Button
  72. variant='ghost'
  73. size='small'
  74. className={cn(
  75. 'text-text-tertiary',
  76. open && 'bg-components-button-ghost-bg-hover',
  77. notAllowCustomCredential && !!noModels && 'cursor-not-allowed opacity-50',
  78. )}
  79. >
  80. <RiAddCircleFill className='mr-1 h-3.5 w-3.5' />
  81. {t('common.modelProvider.addModel')}
  82. </Button>
  83. )
  84. if (notAllowCustomCredential && !!noModels) {
  85. return (
  86. <Tooltip asChild popupContent={t('plugin.auth.credentialUnavailable')}>
  87. {Item}
  88. </Tooltip>
  89. )
  90. }
  91. return Item
  92. }, [t, notAllowCustomCredential, noModels])
  93. return (
  94. <PortalToFollowElem
  95. open={open}
  96. onOpenChange={setOpen}
  97. placement='bottom-end'
  98. offset={{
  99. mainAxis: 4,
  100. crossAxis: 0,
  101. }}
  102. >
  103. <PortalToFollowElemTrigger onClick={() => {
  104. if (noModels) {
  105. if (notAllowCustomCredential)
  106. return
  107. handleOpenModalForAddNewCustomModel()
  108. return
  109. }
  110. setOpen(prev => !prev)
  111. }}>
  112. {renderTrigger(open)}
  113. </PortalToFollowElemTrigger>
  114. <PortalToFollowElemContent className='z-[1060]'>
  115. <div className='w-[320px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'>
  116. <div className='max-h-[304px] overflow-y-auto p-1'>
  117. {
  118. canAddedModels.map(model => (
  119. <div
  120. key={model.model}
  121. className='flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover'
  122. onClick={() => {
  123. handleOpenModalForAddCustomModelToModelList(undefined, model)
  124. setOpen(false)
  125. }}
  126. >
  127. <ModelIcon
  128. className='mr-1 h-5 w-5 shrink-0'
  129. iconClassName='h-5 w-5'
  130. provider={provider}
  131. modelName={model.model}
  132. />
  133. <div
  134. className='system-md-regular grow truncate text-text-primary'
  135. title={model.model}
  136. >
  137. {model.model}
  138. </div>
  139. </div>
  140. ))
  141. }
  142. </div>
  143. {
  144. !notAllowCustomCredential && (
  145. <div
  146. className='system-xs-medium flex cursor-pointer items-center border-t border-t-divider-subtle p-3 text-text-accent-light-mode-only'
  147. onClick={() => {
  148. handleOpenModalForAddNewCustomModel()
  149. setOpen(false)
  150. }}
  151. >
  152. <RiAddLine className='mr-1 h-4 w-4' />
  153. {t('common.modelProvider.auth.addNewModel')}
  154. </div>
  155. )
  156. }
  157. </div>
  158. </PortalToFollowElemContent>
  159. </PortalToFollowElem>
  160. )
  161. }
  162. export default memo(AddCustomModel)