選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

provider-context.tsx 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. 'use client'
  2. import { createContext, useContext, useContextSelector } from 'use-context-selector'
  3. import useSWR from 'swr'
  4. import { useEffect, useState } from 'react'
  5. import dayjs from 'dayjs'
  6. import { useTranslation } from 'react-i18next'
  7. import {
  8. fetchModelList,
  9. fetchModelProviders,
  10. fetchSupportRetrievalMethods,
  11. } from '@/service/common'
  12. import {
  13. CurrentSystemQuotaTypeEnum,
  14. ModelStatusEnum,
  15. ModelTypeEnum,
  16. } from '@/app/components/header/account-setting/model-provider-page/declarations'
  17. import type { Model, ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations'
  18. import type { RETRIEVE_METHOD } from '@/types/app'
  19. import type { BasicPlan } from '@/app/components/billing/type'
  20. import { Plan, type UsagePlanInfo } from '@/app/components/billing/type'
  21. import { fetchCurrentPlanInfo } from '@/service/billing'
  22. import { parseCurrentPlan } from '@/app/components/billing/utils'
  23. import { defaultPlan } from '@/app/components/billing/config'
  24. import Toast from '@/app/components/base/toast'
  25. import {
  26. useEducationStatus,
  27. } from '@/service/use-education'
  28. import { noop } from 'lodash-es'
  29. import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
  30. import { ZENDESK_FIELD_IDS } from '@/config'
  31. type ProviderContextState = {
  32. modelProviders: ModelProvider[]
  33. refreshModelProviders: () => void
  34. textGenerationModelList: Model[]
  35. supportRetrievalMethods: RETRIEVE_METHOD[]
  36. isAPIKeySet: boolean
  37. plan: {
  38. type: BasicPlan
  39. usage: UsagePlanInfo
  40. total: UsagePlanInfo
  41. }
  42. isFetchedPlan: boolean
  43. enableBilling: boolean
  44. onPlanInfoChanged: () => void
  45. enableReplaceWebAppLogo: boolean
  46. modelLoadBalancingEnabled: boolean
  47. datasetOperatorEnabled: boolean
  48. enableEducationPlan: boolean
  49. isEducationWorkspace: boolean
  50. isEducationAccount: boolean
  51. allowRefreshEducationVerify: boolean
  52. educationAccountExpireAt: number | null
  53. isLoadingEducationAccountInfo: boolean
  54. isFetchingEducationAccountInfo: boolean
  55. webappCopyrightEnabled: boolean
  56. licenseLimit: {
  57. workspace_members: {
  58. size: number
  59. limit: number
  60. }
  61. },
  62. refreshLicenseLimit: () => void
  63. isAllowTransferWorkspace: boolean
  64. }
  65. const ProviderContext = createContext<ProviderContextState>({
  66. modelProviders: [],
  67. refreshModelProviders: noop,
  68. textGenerationModelList: [],
  69. supportRetrievalMethods: [],
  70. isAPIKeySet: true,
  71. plan: {
  72. type: Plan.sandbox,
  73. usage: {
  74. vectorSpace: 32,
  75. buildApps: 12,
  76. teamMembers: 1,
  77. annotatedResponse: 1,
  78. documentsUploadQuota: 50,
  79. },
  80. total: {
  81. vectorSpace: 200,
  82. buildApps: 50,
  83. teamMembers: 1,
  84. annotatedResponse: 10,
  85. documentsUploadQuota: 500,
  86. },
  87. },
  88. isFetchedPlan: false,
  89. enableBilling: false,
  90. onPlanInfoChanged: noop,
  91. enableReplaceWebAppLogo: false,
  92. modelLoadBalancingEnabled: false,
  93. datasetOperatorEnabled: false,
  94. enableEducationPlan: false,
  95. isEducationWorkspace: false,
  96. isEducationAccount: false,
  97. allowRefreshEducationVerify: false,
  98. educationAccountExpireAt: null,
  99. isLoadingEducationAccountInfo: false,
  100. isFetchingEducationAccountInfo: false,
  101. webappCopyrightEnabled: false,
  102. licenseLimit: {
  103. workspace_members: {
  104. size: 0,
  105. limit: 0,
  106. },
  107. },
  108. refreshLicenseLimit: noop,
  109. isAllowTransferWorkspace: false,
  110. })
  111. export const useProviderContext = () => useContext(ProviderContext)
  112. // Adding a dangling comma to avoid the generic parsing issue in tsx, see:
  113. // https://github.com/microsoft/TypeScript/issues/15713
  114. export const useProviderContextSelector = <T,>(selector: (state: ProviderContextState) => T): T =>
  115. useContextSelector(ProviderContext, selector)
  116. type ProviderContextProviderProps = {
  117. children: React.ReactNode
  118. }
  119. export const ProviderContextProvider = ({
  120. children,
  121. }: ProviderContextProviderProps) => {
  122. const { data: providersData, mutate: refreshModelProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
  123. const fetchModelListUrlPrefix = '/workspaces/current/models/model-types/'
  124. const { data: textGenerationModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.textGeneration}`, fetchModelList)
  125. const { data: supportRetrievalMethods } = useSWR('/datasets/retrieval-setting', fetchSupportRetrievalMethods)
  126. const [plan, setPlan] = useState(defaultPlan)
  127. const [isFetchedPlan, setIsFetchedPlan] = useState(false)
  128. const [enableBilling, setEnableBilling] = useState(true)
  129. const [enableReplaceWebAppLogo, setEnableReplaceWebAppLogo] = useState(false)
  130. const [modelLoadBalancingEnabled, setModelLoadBalancingEnabled] = useState(false)
  131. const [datasetOperatorEnabled, setDatasetOperatorEnabled] = useState(false)
  132. const [webappCopyrightEnabled, setWebappCopyrightEnabled] = useState(false)
  133. const [licenseLimit, setLicenseLimit] = useState({
  134. workspace_members: {
  135. size: 0,
  136. limit: 0,
  137. },
  138. })
  139. const [enableEducationPlan, setEnableEducationPlan] = useState(false)
  140. const [isEducationWorkspace, setIsEducationWorkspace] = useState(false)
  141. const { data: educationAccountInfo, isLoading: isLoadingEducationAccountInfo, isFetching: isFetchingEducationAccountInfo } = useEducationStatus(!enableEducationPlan)
  142. const [isAllowTransferWorkspace, setIsAllowTransferWorkspace] = useState(false)
  143. const fetchPlan = async () => {
  144. try {
  145. const data = await fetchCurrentPlanInfo()
  146. if (!data) {
  147. console.error('Failed to fetch plan info: data is undefined')
  148. return
  149. }
  150. // set default value to avoid undefined error
  151. setEnableBilling(data.billing?.enabled ?? false)
  152. setEnableEducationPlan(data.education?.enabled ?? false)
  153. setIsEducationWorkspace(data.education?.activated ?? false)
  154. setEnableReplaceWebAppLogo(data.can_replace_logo ?? false)
  155. if (data.billing?.enabled) {
  156. setPlan(parseCurrentPlan(data) as any)
  157. setIsFetchedPlan(true)
  158. }
  159. if (data.model_load_balancing_enabled)
  160. setModelLoadBalancingEnabled(true)
  161. if (data.dataset_operator_enabled)
  162. setDatasetOperatorEnabled(true)
  163. if (data.webapp_copyright_enabled)
  164. setWebappCopyrightEnabled(true)
  165. if (data.workspace_members)
  166. setLicenseLimit({ workspace_members: data.workspace_members })
  167. if (data.is_allow_transfer_workspace)
  168. setIsAllowTransferWorkspace(data.is_allow_transfer_workspace)
  169. }
  170. catch (error) {
  171. console.error('Failed to fetch plan info:', error)
  172. // set default value to avoid undefined error
  173. setEnableBilling(false)
  174. setEnableEducationPlan(false)
  175. setIsEducationWorkspace(false)
  176. setEnableReplaceWebAppLogo(false)
  177. }
  178. }
  179. useEffect(() => {
  180. fetchPlan()
  181. }, [])
  182. // #region Zendesk conversation fields
  183. useEffect(() => {
  184. if (ZENDESK_FIELD_IDS.PLAN && plan.type) {
  185. setZendeskConversationFields([{
  186. id: ZENDESK_FIELD_IDS.PLAN,
  187. value: `${plan.type}-plan`,
  188. }])
  189. }
  190. }, [plan.type])
  191. // #endregion Zendesk conversation fields
  192. const { t } = useTranslation()
  193. useEffect(() => {
  194. if (localStorage.getItem('anthropic_quota_notice') === 'true')
  195. return
  196. if (dayjs().isAfter(dayjs('2025-03-17')))
  197. return
  198. if (providersData?.data && providersData.data.length > 0) {
  199. const anthropic = providersData.data.find(provider => provider.provider === 'anthropic')
  200. if (anthropic && anthropic.system_configuration.current_quota_type === CurrentSystemQuotaTypeEnum.trial) {
  201. const quota = anthropic.system_configuration.quota_configurations.find(item => item.quota_type === anthropic.system_configuration.current_quota_type)
  202. if (quota && quota.is_valid && quota.quota_used < quota.quota_limit) {
  203. Toast.notify({
  204. type: 'info',
  205. message: t('common.provider.anthropicHosted.trialQuotaTip'),
  206. duration: 60000,
  207. onClose: () => {
  208. localStorage.setItem('anthropic_quota_notice', 'true')
  209. },
  210. })
  211. }
  212. }
  213. }
  214. }, [providersData, t])
  215. return (
  216. <ProviderContext.Provider value={{
  217. modelProviders: providersData?.data || [],
  218. refreshModelProviders,
  219. textGenerationModelList: textGenerationModelList?.data || [],
  220. isAPIKeySet: !!textGenerationModelList?.data.some(model => model.status === ModelStatusEnum.active),
  221. supportRetrievalMethods: supportRetrievalMethods?.retrieval_method || [],
  222. plan,
  223. isFetchedPlan,
  224. enableBilling,
  225. onPlanInfoChanged: fetchPlan,
  226. enableReplaceWebAppLogo,
  227. modelLoadBalancingEnabled,
  228. datasetOperatorEnabled,
  229. enableEducationPlan,
  230. isEducationWorkspace,
  231. isEducationAccount: educationAccountInfo?.is_student || false,
  232. allowRefreshEducationVerify: educationAccountInfo?.allow_refresh || false,
  233. educationAccountExpireAt: educationAccountInfo?.expire_at || null,
  234. isLoadingEducationAccountInfo,
  235. isFetchingEducationAccountInfo,
  236. webappCopyrightEnabled,
  237. licenseLimit,
  238. refreshLicenseLimit: fetchPlan,
  239. isAllowTransferWorkspace,
  240. }}>
  241. {children}
  242. </ProviderContext.Provider>
  243. )
  244. }
  245. export default ProviderContext