123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- import { memo, useCallback, useEffect, useMemo, useState } from 'react'
- import { useTranslation } from 'react-i18next'
- import useSWR from 'swr'
- import type { ModelItem, ModelLoadBalancingConfig, ModelLoadBalancingConfigEntry, ModelProvider } from '../declarations'
- import { FormTypeEnum } from '../declarations'
- import ModelIcon from '../model-icon'
- import ModelName from '../model-name'
- import { savePredefinedLoadBalancingConfig } from '../utils'
- import ModelLoadBalancingConfigs from './model-load-balancing-configs'
- import classNames from '@/utils/classnames'
- import Modal from '@/app/components/base/modal'
- import Button from '@/app/components/base/button'
- import { fetchModelLoadBalancingConfig } from '@/service/common'
- import Loading from '@/app/components/base/loading'
- import { useToastContext } from '@/app/components/base/toast'
- export type ModelLoadBalancingModalProps = {
- provider: ModelProvider
- model: ModelItem
- open?: boolean
- onClose?: () => void
- onSave?: (provider: string) => void
- }
- // model balancing config modal
- const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSave }: ModelLoadBalancingModalProps) => {
- const { t } = useTranslation()
- const { notify } = useToastContext()
- const [loading, setLoading] = useState(false)
- const { data, mutate } = useSWR(
- `/workspaces/current/model-providers/${provider.provider}/models/credentials?model=${model.model}&model_type=${model.model_type}`,
- fetchModelLoadBalancingConfig,
- )
- const originalConfig = data?.load_balancing
- const [draftConfig, setDraftConfig] = useState<ModelLoadBalancingConfig>()
- const originalConfigMap = useMemo(() => {
- if (!originalConfig)
- return {}
- return originalConfig?.configs.reduce((prev, config) => {
- if (config.id)
- prev[config.id] = config
- return prev
- }, {} as Record<string, ModelLoadBalancingConfigEntry>)
- }, [originalConfig])
- useEffect(() => {
- if (originalConfig)
- setDraftConfig(originalConfig)
- }, [originalConfig])
- const toggleModalBalancing = useCallback((enabled: boolean) => {
- if (draftConfig) {
- setDraftConfig({
- ...draftConfig,
- enabled,
- })
- }
- }, [draftConfig])
- const extendedSecretFormSchemas = useMemo(
- () => provider.provider_credential_schema.credential_form_schemas.filter(
- ({ type }) => type === FormTypeEnum.secretInput,
- ),
- [provider.provider_credential_schema.credential_form_schemas],
- )
- const encodeConfigEntrySecretValues = useCallback((entry: ModelLoadBalancingConfigEntry) => {
- const result = { ...entry }
- extendedSecretFormSchemas.forEach(({ variable }) => {
- if (entry.id && result.credentials[variable] === originalConfigMap[entry.id]?.credentials?.[variable])
- result.credentials[variable] = '[__HIDDEN__]'
- })
- return result
- }, [extendedSecretFormSchemas, originalConfigMap])
- const handleSave = async () => {
- try {
- setLoading(true)
- const res = await savePredefinedLoadBalancingConfig(
- provider.provider,
- ({
- ...(data?.credentials ?? {}),
- __model_type: model.model_type,
- __model_name: model.model,
- }),
- {
- ...draftConfig,
- enabled: Boolean(draftConfig?.enabled),
- configs: draftConfig!.configs.map(encodeConfigEntrySecretValues),
- },
- )
- if (res.result === 'success') {
- notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
- mutate()
- onSave?.(provider.provider)
- onClose?.()
- }
- }
- finally {
- setLoading(false)
- }
- }
- return (
- <Modal
- isShow={Boolean(model) && open}
- onClose={onClose}
- className='max-w-none pt-8 px-8 w-[640px]'
- title={
- <div className='pb-3 font-semibold'>
- <div className='h-[30px]'>{t('common.modelProvider.configLoadBalancing')}</div>
- {Boolean(model) && (
- <div className='flex items-center h-5'>
- <ModelIcon
- className='shrink-0 mr-2'
- provider={provider}
- modelName={model!.model}
- />
- <ModelName
- className='grow text-sm font-normal text-gray-900'
- modelItem={model!}
- showModelType
- showMode
- showContextSize
- />
- </div>
- )}
- </div>
- }
- >
- {!draftConfig
- ? <Loading type='area' />
- : (
- <>
- <div className='py-2'>
- <div
- className={classNames(
- 'min-h-16 bg-gray-50 border rounded-xl transition-colors',
- draftConfig.enabled ? 'border-gray-200 cursor-pointer' : 'border-primary-400 cursor-default',
- )}
- onClick={draftConfig.enabled ? () => toggleModalBalancing(false) : undefined}
- >
- <div className='flex items-center px-[15px] py-3 gap-2 select-none'>
- <div className='grow-0 shrink-0 flex items-center justify-center w-8 h-8 bg-white border rounded-lg'>
- {Boolean(model) && (
- <ModelIcon className='shrink-0' provider={provider} modelName={model!.model} />
- )}
- </div>
- <div className='grow'>
- <div className='text-sm'>{t('common.modelProvider.providerManaged')}</div>
- <div className='text-xs text-gray-500'>{t('common.modelProvider.providerManagedDescription')}</div>
- </div>
- </div>
- </div>
- <ModelLoadBalancingConfigs {...{
- draftConfig,
- setDraftConfig,
- provider,
- currentCustomConfigurationModelFixedFields: {
- __model_name: model.model,
- __model_type: model.model_type,
- },
- configurationMethod: model.fetch_from,
- className: 'mt-2',
- }} />
- </div>
- <div className='flex items-center justify-end gap-2 mt-6'>
- <Button onClick={onClose}>{t('common.operation.cancel')}</Button>
- <Button
- variant='primary'
- onClick={handleSave}
- disabled={
- loading
- || (draftConfig?.enabled && (draftConfig?.configs.filter(config => config.enabled).length ?? 0) < 2)
- }
- >{t('common.operation.save')}</Button>
- </div>
- </>
- )
- }
- </Modal >
- )
- }
- export default memo(ModelLoadBalancingModal)
|