import type { Dispatch, SetStateAction } from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { RiDeleteBinLine, } from '@remixicon/react' import type { ConfigurationMethodEnum, CustomConfigurationModelFixedFields, ModelLoadBalancingConfig, ModelLoadBalancingConfigEntry, ModelProvider } from '../declarations' import Indicator from '../../../indicator' import CooldownTimer from './cooldown-timer' import classNames from '@/utils/classnames' import Tooltip from '@/app/components/base/tooltip' import Switch from '@/app/components/base/switch' import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' import { Edit02, Plus02 } from '@/app/components/base/icons/src/vender/line/general' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { useModalContextSelector } from '@/context/modal-context' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import s from '@/app/components/custom/style.module.css' import GridMask from '@/app/components/base/grid-mask' import { useProviderContextSelector } from '@/context/provider-context' import { IS_CE_EDITION } from '@/config' export type ModelLoadBalancingConfigsProps = { draftConfig?: ModelLoadBalancingConfig setDraftConfig: Dispatch> provider: ModelProvider configurationMethod: ConfigurationMethodEnum currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields withSwitch?: boolean className?: string } const ModelLoadBalancingConfigs = ({ draftConfig, setDraftConfig, provider, configurationMethod, currentCustomConfigurationModelFixedFields, withSwitch = false, className, }: ModelLoadBalancingConfigsProps) => { const { t } = useTranslation() const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled) const updateConfigEntry = useCallback( ( index: number, modifier: (entry: ModelLoadBalancingConfigEntry) => ModelLoadBalancingConfigEntry | undefined, ) => { setDraftConfig((prev) => { if (!prev) return prev const newConfigs = [...prev.configs] const modifiedConfig = modifier(newConfigs[index]) if (modifiedConfig) newConfigs[index] = modifiedConfig else newConfigs.splice(index, 1) return { ...prev, configs: newConfigs, } }) }, [setDraftConfig], ) const toggleModalBalancing = useCallback((enabled: boolean) => { if ((modelLoadBalancingEnabled || !enabled) && draftConfig) { setDraftConfig({ ...draftConfig, enabled, }) } }, [draftConfig, modelLoadBalancingEnabled, setDraftConfig]) const toggleConfigEntryEnabled = useCallback((index: number, state?: boolean) => { updateConfigEntry(index, entry => ({ ...entry, enabled: typeof state === 'boolean' ? state : !entry.enabled, })) }, [updateConfigEntry]) const setShowModelLoadBalancingEntryModal = useModalContextSelector(state => state.setShowModelLoadBalancingEntryModal) const toggleEntryModal = useCallback((index?: number, entry?: ModelLoadBalancingConfigEntry) => { setShowModelLoadBalancingEntryModal({ payload: { currentProvider: provider, currentConfigurationMethod: configurationMethod, currentCustomConfigurationModelFixedFields, entry, index, }, onSaveCallback: ({ entry: result }) => { if (entry) { // edit setDraftConfig(prev => ({ ...prev, enabled: !!prev?.enabled, configs: prev?.configs.map((config, i) => i === index ? result! : config) || [], })) } else { // add setDraftConfig(prev => ({ ...prev, enabled: !!prev?.enabled, configs: (prev?.configs || []).concat([{ ...result!, enabled: true }]), })) } }, onRemoveCallback: ({ index }) => { if (index !== undefined && (draftConfig?.configs?.length ?? 0) > index) { setDraftConfig(prev => ({ ...prev, enabled: !!prev?.enabled, configs: prev?.configs.filter((_, i) => i !== index) || [], })) } }, }) }, [ configurationMethod, currentCustomConfigurationModelFixedFields, draftConfig?.configs?.length, provider, setDraftConfig, setShowModelLoadBalancingEntryModal, ]) const clearCountdown = useCallback((index: number) => { updateConfigEntry(index, ({ ttl: _, ...entry }) => { return { ...entry, in_cooldown: false, } }) }, [updateConfigEntry]) if (!draftConfig) return null return ( <>
toggleModalBalancing(true) : undefined} >
{t('common.modelProvider.loadBalancing')}
{t('common.modelProvider.loadBalancingDescription')}
{ withSwitch && ( toggleModalBalancing(value)} /> ) }
{draftConfig.enabled && (
{draftConfig.configs.map((config, index) => { const isProviderManaged = config.name === '__inherit__' return (
{(config.in_cooldown && Boolean(config.ttl)) ? ( clearCountdown(index)} /> ) : ( )}
{isProviderManaged ? t('common.modelProvider.defaultConfig') : config.name}
{isProviderManaged && ( {t('common.modelProvider.providerManaged')} )}
{!isProviderManaged && ( <>
toggleEntryModal(index, config)} > updateConfigEntry(index, () => undefined)} >
)} toggleConfigEntryEnabled(index, value)} />
) })}
toggleEntryModal()} >
{t('common.modelProvider.addConfig')}
)} { draftConfig.enabled && draftConfig.configs.length < 2 && (
{t('common.modelProvider.loadBalancingLeastKeyWarning')}
) }
{!modelLoadBalancingEnabled && !IS_CE_EDITION && (
{t('common.modelProvider.upgradeForLoadBalancing')}
)} ) } export default ModelLoadBalancingConfigs