You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import { useMemo } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import { useDebounce } from 'ahooks'
  4. import {
  5. RiAlertFill,
  6. RiBrainLine,
  7. } from '@remixicon/react'
  8. import SystemModelSelector from './system-model-selector'
  9. import ProviderAddedCard from './provider-added-card'
  10. import type {
  11. ConfigurationMethodEnum,
  12. CustomConfigurationModelFixedFields,
  13. ModelProvider,
  14. } from './declarations'
  15. import {
  16. CustomConfigurationStatusEnum,
  17. ModelTypeEnum,
  18. } from './declarations'
  19. import {
  20. useDefaultModel,
  21. useModelModalHandler,
  22. } from './hooks'
  23. import InstallFromMarketplace from './install-from-marketplace'
  24. import { useProviderContext } from '@/context/provider-context'
  25. import cn from '@/utils/classnames'
  26. import { useGlobalPublicStore } from '@/context/global-public-context'
  27. type Props = {
  28. searchText: string
  29. }
  30. const FixedModelProvider = ['langgenius/openai/openai', 'langgenius/anthropic/anthropic']
  31. const ModelProviderPage = ({ searchText }: Props) => {
  32. const debouncedSearchText = useDebounce(searchText, { wait: 500 })
  33. const { t } = useTranslation()
  34. const { data: textGenerationDefaultModel } = useDefaultModel(ModelTypeEnum.textGeneration)
  35. const { data: embeddingsDefaultModel } = useDefaultModel(ModelTypeEnum.textEmbedding)
  36. const { data: rerankDefaultModel } = useDefaultModel(ModelTypeEnum.rerank)
  37. const { data: speech2textDefaultModel } = useDefaultModel(ModelTypeEnum.speech2text)
  38. const { data: ttsDefaultModel } = useDefaultModel(ModelTypeEnum.tts)
  39. const { modelProviders: providers } = useProviderContext()
  40. const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
  41. const defaultModelNotConfigured = !textGenerationDefaultModel && !embeddingsDefaultModel && !speech2textDefaultModel && !rerankDefaultModel && !ttsDefaultModel
  42. const [configuredProviders, notConfiguredProviders] = useMemo(() => {
  43. const configuredProviders: ModelProvider[] = []
  44. const notConfiguredProviders: ModelProvider[] = []
  45. providers.forEach((provider) => {
  46. if (
  47. provider.custom_configuration.status === CustomConfigurationStatusEnum.active
  48. || (
  49. provider.system_configuration.enabled === true
  50. && provider.system_configuration.quota_configurations.find(item => item.quota_type === provider.system_configuration.current_quota_type)
  51. )
  52. )
  53. configuredProviders.push(provider)
  54. else
  55. notConfiguredProviders.push(provider)
  56. })
  57. configuredProviders.sort((a, b) => {
  58. if (FixedModelProvider.includes(a.provider) && FixedModelProvider.includes(b.provider))
  59. return FixedModelProvider.indexOf(a.provider) - FixedModelProvider.indexOf(b.provider) > 0 ? 1 : -1
  60. else if (FixedModelProvider.includes(a.provider))
  61. return -1
  62. else if (FixedModelProvider.includes(b.provider))
  63. return 1
  64. return 0
  65. })
  66. return [configuredProviders, notConfiguredProviders]
  67. }, [providers])
  68. const [filteredConfiguredProviders, filteredNotConfiguredProviders] = useMemo(() => {
  69. const filteredConfiguredProviders = configuredProviders.filter(
  70. provider => provider.provider.toLowerCase().includes(debouncedSearchText.toLowerCase())
  71. || Object.values(provider.label).some(text => text.toLowerCase().includes(debouncedSearchText.toLowerCase())),
  72. )
  73. const filteredNotConfiguredProviders = notConfiguredProviders.filter(
  74. provider => provider.provider.toLowerCase().includes(debouncedSearchText.toLowerCase())
  75. || Object.values(provider.label).some(text => text.toLowerCase().includes(debouncedSearchText.toLowerCase())),
  76. )
  77. return [filteredConfiguredProviders, filteredNotConfiguredProviders]
  78. }, [configuredProviders, debouncedSearchText, notConfiguredProviders])
  79. const handleOpenModal = useModelModalHandler()
  80. return (
  81. <div className='relative -mt-2 pt-1'>
  82. <div className={cn('mb-2 flex items-center')}>
  83. <div className='system-md-semibold grow text-text-primary'>{t('common.modelProvider.models')}</div>
  84. <div className={cn(
  85. 'relative flex shrink-0 items-center justify-end gap-2 rounded-lg border border-transparent p-px',
  86. defaultModelNotConfigured && 'border-components-panel-border bg-components-panel-bg-blur pl-2 shadow-xs',
  87. )}>
  88. {defaultModelNotConfigured && <div className='absolute bottom-0 left-0 right-0 top-0 opacity-40' style={{ background: 'linear-gradient(92deg, rgba(247, 144, 9, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%)' }} />}
  89. {defaultModelNotConfigured && (
  90. <div className='system-xs-medium flex items-center gap-1 text-text-primary'>
  91. <RiAlertFill className='h-4 w-4 text-text-warning-secondary' />
  92. {t('common.modelProvider.notConfigured')}
  93. </div>
  94. )}
  95. <SystemModelSelector
  96. notConfigured={defaultModelNotConfigured}
  97. textGenerationDefaultModel={textGenerationDefaultModel}
  98. embeddingsDefaultModel={embeddingsDefaultModel}
  99. rerankDefaultModel={rerankDefaultModel}
  100. speech2textDefaultModel={speech2textDefaultModel}
  101. ttsDefaultModel={ttsDefaultModel}
  102. />
  103. </div>
  104. </div>
  105. {!filteredConfiguredProviders?.length && (
  106. <div className='mb-2 rounded-[10px] bg-workflow-process-bg p-4'>
  107. <div className='flex h-10 w-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg backdrop-blur'>
  108. <RiBrainLine className='h-5 w-5 text-text-primary' />
  109. </div>
  110. <div className='system-sm-medium mt-2 text-text-secondary'>{t('common.modelProvider.emptyProviderTitle')}</div>
  111. <div className='system-xs-regular mt-1 text-text-tertiary'>{t('common.modelProvider.emptyProviderTip')}</div>
  112. </div>
  113. )}
  114. {!!filteredConfiguredProviders?.length && (
  115. <div className='relative'>
  116. {filteredConfiguredProviders?.map(provider => (
  117. <ProviderAddedCard
  118. key={provider.provider}
  119. provider={provider}
  120. onOpenModal={(configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => handleOpenModal(provider, configurationMethod, currentCustomConfigurationModelFixedFields)}
  121. />
  122. ))}
  123. </div>
  124. )}
  125. {!!filteredNotConfiguredProviders?.length && (
  126. <>
  127. <div className='system-md-semibold mb-2 flex items-center pt-2 text-text-primary'>{t('common.modelProvider.toBeConfigured')}</div>
  128. <div className='relative'>
  129. {filteredNotConfiguredProviders?.map(provider => (
  130. <ProviderAddedCard
  131. notConfigured
  132. key={provider.provider}
  133. provider={provider}
  134. onOpenModal={(configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => handleOpenModal(provider, configurationMethod, currentCustomConfigurationModelFixedFields)}
  135. />
  136. ))}
  137. </div>
  138. </>
  139. )}
  140. {
  141. enable_marketplace && (
  142. <InstallFromMarketplace
  143. providers={providers}
  144. searchText={searchText}
  145. />
  146. )
  147. }
  148. </div>
  149. )
  150. }
  151. export default ModelProviderPage