| fetchModelProviderCredentials, | fetchModelProviderCredentials, | ||||
| fetchModelProviders, | fetchModelProviders, | ||||
| getPayUrl, | getPayUrl, | ||||
| submitFreeQuota, | |||||
| } from '@/service/common' | } from '@/service/common' | ||||
| import { useProviderContext } from '@/context/provider-context' | import { useProviderContext } from '@/context/provider-context' | ||||
| return handleGetPayUrl | return handleGetPayUrl | ||||
| } | } | ||||
| export const useFreeQuota = (onSuccess: () => void) => { | |||||
| const [loading, setLoading] = useState(false) | |||||
| const handleClick = async (type: string) => { | |||||
| if (loading) | |||||
| return | |||||
| try { | |||||
| setLoading(true) | |||||
| const res = await submitFreeQuota(`/workspaces/current/model-providers/${type}/free-quota-submit`) | |||||
| if (res.type === 'redirect' && res.redirect_url) | |||||
| window.location.href = res.redirect_url | |||||
| else if (res.type === 'submit' && res.result === 'success') | |||||
| onSuccess() | |||||
| } | |||||
| finally { | |||||
| setLoading(false) | |||||
| } | |||||
| } | |||||
| return handleClick | |||||
| } | |||||
| export const useModelProviders = () => { | export const useModelProviders = () => { | ||||
| const { data: providersData, mutate, isLoading } = useSWR('/workspaces/current/model-providers', fetchModelProviders) | const { data: providersData, mutate, isLoading } = useSWR('/workspaces/current/model-providers', fetchModelProviders) | ||||
| import { ConfigurateMethodEnum } from '../declarations' | import { ConfigurateMethodEnum } from '../declarations' | ||||
| import { | import { | ||||
| DEFAULT_BACKGROUND_COLOR, | DEFAULT_BACKGROUND_COLOR, | ||||
| MODEL_PROVIDER_QUOTA_GET_FREE, | |||||
| MODEL_PROVIDER_QUOTA_GET_PAID, | MODEL_PROVIDER_QUOTA_GET_PAID, | ||||
| modelTypeFormat, | modelTypeFormat, | ||||
| } from '../utils' | } from '../utils' | ||||
| const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) | const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) | ||||
| const systemConfig = provider.system_configuration | const systemConfig = provider.system_configuration | ||||
| const hasModelList = fetched && !!modelList.length | const hasModelList = fetched && !!modelList.length | ||||
| const showQuota = systemConfig.enabled && [...MODEL_PROVIDER_QUOTA_GET_FREE, ...MODEL_PROVIDER_QUOTA_GET_PAID].includes(provider.provider) && !IS_CE_EDITION | |||||
| const showQuota = systemConfig.enabled && [...MODEL_PROVIDER_QUOTA_GET_PAID].includes(provider.provider) && !IS_CE_EDITION | |||||
| const getModelList = async (providerName: string) => { | const getModelList = async (providerName: string) => { | ||||
| if (loading) | if (loading) |
| QuotaUnitEnum, | QuotaUnitEnum, | ||||
| } from '../declarations' | } from '../declarations' | ||||
| import { | import { | ||||
| useAnthropicBuyQuota, | |||||
| useFreeQuota, | |||||
| useUpdateModelProviders, | |||||
| } from '../hooks' | |||||
| import { | |||||
| MODEL_PROVIDER_QUOTA_GET_FREE, | |||||
| MODEL_PROVIDER_QUOTA_GET_PAID, | MODEL_PROVIDER_QUOTA_GET_PAID, | ||||
| } from '../utils' | } from '../utils' | ||||
| import PriorityUseTip from './priority-use-tip' | import PriorityUseTip from './priority-use-tip' | ||||
| import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' | import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' | ||||
| import Button from '@/app/components/base/button' | |||||
| import TooltipPlus from '@/app/components/base/tooltip-plus' | import TooltipPlus from '@/app/components/base/tooltip-plus' | ||||
| import { formatNumber } from '@/utils/format' | import { formatNumber } from '@/utils/format' | ||||
| provider, | provider, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const updateModelProviders = useUpdateModelProviders() | |||||
| const handlePay = useAnthropicBuyQuota() | |||||
| const handleFreeQuotaSuccess = () => { | |||||
| updateModelProviders() | |||||
| } | |||||
| const handleFreeQuota = useFreeQuota(handleFreeQuotaSuccess) | |||||
| const customConfig = provider.custom_configuration | const customConfig = provider.custom_configuration | ||||
| const priorityUseType = provider.preferred_provider_type | const priorityUseType = provider.preferred_provider_type | ||||
| const systemConfig = provider.system_configuration | const systemConfig = provider.system_configuration | ||||
| </div> | </div> | ||||
| ) | ) | ||||
| } | } | ||||
| { | |||||
| !currentQuota && MODEL_PROVIDER_QUOTA_GET_FREE.includes(provider.provider) && ( | |||||
| <Button | |||||
| className='h-6 bg-white text-xs font-medium rounded-md' | |||||
| onClick={() => handleFreeQuota(provider.provider)} | |||||
| > | |||||
| {t('common.modelProvider.getFreeTokens')} | |||||
| </Button> | |||||
| ) | |||||
| } | |||||
| { | { | ||||
| priorityUseType === PreferredProviderTypeEnum.system && customConfig.status === CustomConfigurationStatusEnum.active && ( | priorityUseType === PreferredProviderTypeEnum.system && customConfig.status === CustomConfigurationStatusEnum.active && ( | ||||
| <PriorityUseTip /> | <PriorityUseTip /> |
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import type { | import type { | ||||
| ModelProvider, | ModelProvider, | ||||
| TypeWithI18N, | |||||
| } from '../declarations' | } from '../declarations' | ||||
| import { ConfigurateMethodEnum } from '../declarations' | import { ConfigurateMethodEnum } from '../declarations' | ||||
| import { | import { | ||||
| DEFAULT_BACKGROUND_COLOR, | DEFAULT_BACKGROUND_COLOR, | ||||
| MODEL_PROVIDER_QUOTA_GET_FREE, | |||||
| modelTypeFormat, | modelTypeFormat, | ||||
| } from '../utils' | } from '../utils' | ||||
| import { | import { | ||||
| useAnthropicBuyQuota, | |||||
| useFreeQuota, | |||||
| useLanguage, | useLanguage, | ||||
| useUpdateModelProviders, | |||||
| } from '../hooks' | } from '../hooks' | ||||
| import ModelBadge from '../model-badge' | import ModelBadge from '../model-badge' | ||||
| import ProviderIcon from '../provider-icon' | import ProviderIcon from '../provider-icon' | ||||
| import s from './index.module.css' | import s from './index.module.css' | ||||
| import { Plus, Settings01 } from '@/app/components/base/icons/src/vender/line/general' | import { Plus, Settings01 } from '@/app/components/base/icons/src/vender/line/general' | ||||
| import Button from '@/app/components/base/button' | import Button from '@/app/components/base/button' | ||||
| import { IS_CE_EDITION } from '@/config' | |||||
| type ProviderCardProps = { | type ProviderCardProps = { | ||||
| provider: ModelProvider | provider: ModelProvider | ||||
| onOpenModal: (configurateMethod: ConfigurateMethodEnum) => void | onOpenModal: (configurateMethod: ConfigurateMethodEnum) => void | ||||
| } | } | ||||
| const TIP_MAP: { [k: string]: TypeWithI18N } = { | |||||
| minimax: { | |||||
| en_US: 'Earn 1 million tokens for free', | |||||
| zh_Hans: '免费获取 100 万个 token', | |||||
| }, | |||||
| spark: { | |||||
| en_US: 'Earn 3 million tokens (v3.0) for free', | |||||
| zh_Hans: '免费获取 300 万个 token (v3.0)', | |||||
| }, | |||||
| zhipuai: { | |||||
| en_US: 'Earn 10 million tokens for free', | |||||
| zh_Hans: '免费获取 1000 万个 token', | |||||
| }, | |||||
| } | |||||
| const ProviderCard: FC<ProviderCardProps> = ({ | const ProviderCard: FC<ProviderCardProps> = ({ | ||||
| provider, | provider, | ||||
| onOpenModal, | onOpenModal, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const language = useLanguage() | const language = useLanguage() | ||||
| const updateModelProviders = useUpdateModelProviders() | |||||
| const handlePay = useAnthropicBuyQuota() | |||||
| const handleFreeQuotaSuccess = () => { | |||||
| updateModelProviders() | |||||
| } | |||||
| const handleFreeQuota = useFreeQuota(handleFreeQuotaSuccess) | |||||
| const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) | const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) | ||||
| const canGetFreeQuota = MODEL_PROVIDER_QUOTA_GET_FREE.includes(provider.provider) && !IS_CE_EDITION && provider.system_configuration.enabled | |||||
| return ( | return ( | ||||
| <div | <div | ||||
| } | } | ||||
| </div> | </div> | ||||
| <div> | <div> | ||||
| <div className={`flex flex-wrap group-hover:hidden gap-0.5 ${canGetFreeQuota && 'pb-[18px]'}`}> | |||||
| <div className={'flex flex-wrap group-hover:hidden gap-0.5'}> | |||||
| { | { | ||||
| provider.supported_model_types.map(modelType => ( | provider.supported_model_types.map(modelType => ( | ||||
| <ModelBadge key={modelType}> | <ModelBadge key={modelType}> | ||||
| </ModelBadge> | </ModelBadge> | ||||
| )) | )) | ||||
| } | } | ||||
| { | |||||
| canGetFreeQuota && ( | |||||
| <div className='absolute left-0 right-0 bottom-0 flex items-center h-[26px] px-4 bg-white/50 rounded-b-xl'> | |||||
| 📣 | |||||
| <div | |||||
| className={`${s.vender} text-xs font-medium text-transparent truncate`} | |||||
| title={TIP_MAP[provider.provider][language]} | |||||
| > | |||||
| {TIP_MAP[provider.provider][language]} | |||||
| </div> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| </div> | </div> | ||||
| { | |||||
| canGetFreeQuota && ( | |||||
| <div className='hidden group-hover:block'> | |||||
| <Button | |||||
| className='mb-1 w-full h-7 text-xs' | |||||
| type='primary' | |||||
| onClick={() => handleFreeQuota(provider.provider)} | |||||
| > | |||||
| {t('common.modelProvider.getFreeTokens')} | |||||
| </Button> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| <div className={`hidden group-hover:grid grid-cols-${configurateMethods.length} gap-1`}> | <div className={`hidden group-hover:grid grid-cols-${configurateMethods.length} gap-1`}> | ||||
| { | { | ||||
| configurateMethods.map((method) => { | configurateMethods.map((method) => { |
| validateModelProvider, | validateModelProvider, | ||||
| } from '@/service/common' | } from '@/service/common' | ||||
| export const MODEL_PROVIDER_QUOTA_GET_FREE = ['minimax', 'spark', 'zhipuai'] | |||||
| export const MODEL_PROVIDER_QUOTA_GET_PAID = ['anthropic', 'openai', 'azure_openai'] | export const MODEL_PROVIDER_QUOTA_GET_PAID = ['anthropic', 'openai', 'azure_openai'] | ||||
| export const DEFAULT_BACKGROUND_COLOR = '#F3F4F6' | export const DEFAULT_BACKGROUND_COLOR = '#F3F4F6' |
| return get<{ data: ModelParameterRule[] }>(url) | return get<{ data: ModelParameterRule[] }>(url) | ||||
| } | } | ||||
| export const submitFreeQuota: Fetcher<{ type: string; redirect_url?: string; result?: string }, string> = (url) => { | |||||
| return post<{ type: string; redirect_url?: string; result?: string }>(url) | |||||
| } | |||||
| export const fetchFileUploadConfig: Fetcher<FileUploadConfigResponse, { url: string }> = ({ url }) => { | export const fetchFileUploadConfig: Fetcher<FileUploadConfigResponse, { url: string }> = ({ url }) => { | ||||
| return get<FileUploadConfigResponse>(url) | return get<FileUploadConfigResponse>(url) | ||||
| } | } |