index.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import type { FC } from 'react'
  2. import { useState } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import {
  5. RiArrowRightSLine,
  6. RiInformation2Fill,
  7. RiLoader2Line,
  8. } from '@remixicon/react'
  9. import type {
  10. CustomConfigurationModelFixedFields,
  11. ModelItem,
  12. ModelProvider,
  13. } from '../declarations'
  14. import { ConfigurationMethodEnum } from '../declarations'
  15. import {
  16. MODEL_PROVIDER_QUOTA_GET_PAID,
  17. modelTypeFormat,
  18. } from '../utils'
  19. import ProviderIcon from '../provider-icon'
  20. import ModelBadge from '../model-badge'
  21. import CredentialPanel from './credential-panel'
  22. import QuotaPanel from './quota-panel'
  23. import ModelList from './model-list'
  24. import AddModelButton from './add-model-button'
  25. import { fetchModelProviderModelList } from '@/service/common'
  26. import { useEventEmitterContextContext } from '@/context/event-emitter'
  27. import { IS_CE_EDITION } from '@/config'
  28. import { useAppContext } from '@/context/app-context'
  29. import cn from '@/utils/classnames'
  30. export const UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST = 'UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST'
  31. type ProviderAddedCardProps = {
  32. notConfigured?: boolean
  33. provider: ModelProvider
  34. onOpenModal: (configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => void
  35. }
  36. const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
  37. notConfigured,
  38. provider,
  39. onOpenModal,
  40. }) => {
  41. const { t } = useTranslation()
  42. const { eventEmitter } = useEventEmitterContextContext()
  43. const [fetched, setFetched] = useState(false)
  44. const [loading, setLoading] = useState(false)
  45. const [collapsed, setCollapsed] = useState(true)
  46. const [modelList, setModelList] = useState<ModelItem[]>([])
  47. const configurationMethods = provider.configurate_methods.filter(method => method !== ConfigurationMethodEnum.fetchFromRemote)
  48. const systemConfig = provider.system_configuration
  49. const hasModelList = fetched && !!modelList.length
  50. const { isCurrentWorkspaceManager } = useAppContext()
  51. const showQuota = systemConfig.enabled && [...MODEL_PROVIDER_QUOTA_GET_PAID].includes(provider.provider) && !IS_CE_EDITION
  52. const showCredential = configurationMethods.includes(ConfigurationMethodEnum.predefinedModel) && isCurrentWorkspaceManager
  53. const getModelList = async (providerName: string) => {
  54. if (loading)
  55. return
  56. try {
  57. setLoading(true)
  58. const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${providerName}/models`)
  59. setModelList(modelsData.data)
  60. setCollapsed(false)
  61. setFetched(true)
  62. }
  63. finally {
  64. setLoading(false)
  65. }
  66. }
  67. const handleOpenModelList = () => {
  68. if (fetched) {
  69. setCollapsed(false)
  70. return
  71. }
  72. getModelList(provider.provider)
  73. }
  74. eventEmitter?.useSubscription((v: any) => {
  75. if (v?.type === UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST && v.payload === provider.provider)
  76. getModelList(v.payload)
  77. })
  78. return (
  79. <div
  80. className={cn(
  81. 'mb-2 rounded-xl border-[0.5px] border-divider-regular bg-third-party-model-bg-default shadow-xs',
  82. provider.provider === 'langgenius/openai/openai' && 'bg-third-party-model-bg-openai',
  83. provider.provider === 'langgenius/anthropic/anthropic' && 'bg-third-party-model-bg-anthropic',
  84. )}
  85. >
  86. <div className='flex rounded-t-xl py-2 pl-3 pr-2'>
  87. <div className='grow px-1 pb-0.5 pt-1'>
  88. <ProviderIcon
  89. className='mb-2'
  90. provider={provider}
  91. />
  92. <div className='flex gap-0.5'>
  93. {
  94. provider.supported_model_types.map(modelType => (
  95. <ModelBadge key={modelType}>
  96. {modelTypeFormat(modelType)}
  97. </ModelBadge>
  98. ))
  99. }
  100. </div>
  101. </div>
  102. {
  103. showQuota && (
  104. <QuotaPanel
  105. provider={provider}
  106. />
  107. )
  108. }
  109. {
  110. showCredential && (
  111. <CredentialPanel
  112. onSetup={() => onOpenModal(ConfigurationMethodEnum.predefinedModel)}
  113. provider={provider}
  114. />
  115. )
  116. }
  117. </div>
  118. {
  119. collapsed && (
  120. <div className='system-xs-medium group flex items-center justify-between border-t border-t-divider-subtle py-1.5 pl-2 pr-[11px] text-text-tertiary'>
  121. {(showQuota || !notConfigured) && (
  122. <>
  123. <div className='flex h-6 items-center pl-1 pr-1.5 leading-6 group-hover:hidden'>
  124. {
  125. hasModelList
  126. ? t('common.modelProvider.modelsNum', { num: modelList.length })
  127. : t('common.modelProvider.showModels')
  128. }
  129. {!loading && <RiArrowRightSLine className='h-4 w-4' />}
  130. </div>
  131. <div
  132. className='hidden h-6 cursor-pointer items-center rounded-lg pl-1 pr-1.5 hover:bg-components-button-ghost-bg-hover group-hover:flex'
  133. onClick={handleOpenModelList}
  134. >
  135. {
  136. hasModelList
  137. ? t('common.modelProvider.showModelsNum', { num: modelList.length })
  138. : t('common.modelProvider.showModels')
  139. }
  140. {!loading && <RiArrowRightSLine className='h-4 w-4' />}
  141. {
  142. loading && (
  143. <RiLoader2Line className='ml-0.5 h-3 w-3 animate-spin' />
  144. )
  145. }
  146. </div>
  147. </>
  148. )}
  149. {!showQuota && notConfigured && (
  150. <div className='flex h-6 items-center pl-1 pr-1.5'>
  151. <RiInformation2Fill className='mr-1 h-4 w-4 text-text-accent' />
  152. <span className='system-xs-medium text-text-secondary'>{t('common.modelProvider.configureTip')}</span>
  153. </div>
  154. )}
  155. {
  156. configurationMethods.includes(ConfigurationMethodEnum.customizableModel) && isCurrentWorkspaceManager && (
  157. <AddModelButton
  158. onClick={() => onOpenModal(ConfigurationMethodEnum.customizableModel)}
  159. className='flex'
  160. />
  161. )
  162. }
  163. </div>
  164. )
  165. }
  166. {
  167. !collapsed && (
  168. <ModelList
  169. provider={provider}
  170. models={modelList}
  171. onCollapse={() => setCollapsed(true)}
  172. onConfig={currentCustomConfigurationModelFixedFields => onOpenModal(ConfigurationMethodEnum.customizableModel, currentCustomConfigurationModelFixedFields)}
  173. onChange={(provider: string) => getModelList(provider)}
  174. />
  175. )
  176. }
  177. </div>
  178. )
  179. }
  180. export default ProviderAddedCard