import IconFont from '@/components/icon-font'; import ModalFooter from '@/components/modal-footer'; import SealAutoComplete from '@/components/seal-form/auto-complete'; import SealInput from '@/components/seal-form/seal-input'; import SealSelect from '@/components/seal-form/seal-select'; import TooltipList from '@/components/tooltip-list'; import { PageAction } from '@/config'; import { PageActionType } from '@/config/types'; import useAppUtils from '@/hooks/use-app-utils'; import { useIntl } from '@umijs/max'; import { Button, Form, Modal, Typography } from 'antd'; import _ from 'lodash'; import React, { useEffect, useMemo, useRef } from 'react'; import { backendLabelMap, backendOptionsMap, backendTipsList, updateExcludeFields as excludeFields, getSourceRepoConfigValue, localPathTipsList, modelSourceMap, ollamaModelOptions, sourceOptions } from '../config'; import { FormContext } from '../config/form-context'; import { FormData, ListItem } from '../config/types'; import { useCheckCompatibility } from '../hooks'; import AdvanceConfig from './advance-config'; import ColumnWrapper from './column-wrapper'; import CompatibilityAlert from './compatible-alert'; type AddModalProps = { title: string; action: PageActionType; open: boolean; updateFormInitials: { data?: ListItem; gpuOptions: any[]; isGGUF: boolean; }; onOk: (values: FormData) => void; onCancel: () => void; }; const SEARCH_SOURCE = [ modelSourceMap.huggingface_value, modelSourceMap.modelscope_value ]; const UpdateModal: React.FC = (props) => { const { title, action, open, onOk, onCancel, updateFormInitials: { gpuOptions, isGGUF, data: formData } } = props || {}; const intl = useIntl(); const { setWarningStatus, generateGPUIds, handleBackendChangeBefore, checkTokenRef, warningStatus } = useCheckCompatibility(); const { getRuleMessage } = useAppUtils(); const [form] = Form.useForm(); const localPathCache = useRef(''); const submitAnyway = useRef(false); const originFormData = useRef(null); const timer0 = useRef(null); const setOriginalFormData = () => { if (!originFormData.current) { clearTimeout(timer0.current); timer0.current = setTimeout(() => { originFormData.current = form.getFieldsValue?.(); }, 200); } }; const handleOnValuesChange = _.debounce((data: any) => { const formdata = form.getFieldsValue?.(); let alldata = {}; if (formdata.scheduleType === 'manual') { alldata = { ..._.omit(formdata, ['worker_selector']), gpu_selector: formdata.gpu_selector?.gpu_ids?.length > 0 ? originFormData.current?.gpu_selector : null }; } else { alldata = { ..._.omit(formdata, ['gpu_selector']), worker_selector: originFormData.current?.worker_selector || null }; } const isEqual = _.isEqual(alldata, originFormData.current); if (isEqual) { setWarningStatus({ show: false, message: '' }); } else { setWarningStatus({ show: true, isDefault: true, message: intl.formatMessage({ id: 'models.form.update.tips' }) }); } }, 300); // voxbox is not support multi gpu const handleSetGPUIds = (backend: string) => { const gpuids = form.getFieldValue(['gpu_selector', 'gpu_ids']) || []; if (backend === backendOptionsMap.voxBox && gpuids.length > 0) { form.setFieldValue(['gpu_selector', 'gpu_ids'], [gpuids[0]]); } }; const handleBackendChange = (backend: string) => { const updates = { backend_version: '', backend_parameters: [], env: {} }; if (backend === backendOptionsMap.llamaBox) { Object.assign(updates, { distributed_inference_across_workers: true, cpu_offloading: true }); } form.setFieldsValue(updates); handleSetGPUIds(backend); const data = form.getFieldsValue?.(); const res = handleBackendChangeBefore(data); if (res.show) { return; } if (data.local_path || data.source !== modelSourceMap.local_path_value) { handleOnValuesChange?.({ changedValues: {}, allValues: backend === backendOptionsMap.llamaBox ? data : _.omit(data, [ 'cpu_offloading', 'distributed_inference_across_workers' ]), source: data.source }); } }; const handleOnFocus = () => { localPathCache.current = form.getFieldValue('local_path'); }; const handleLocalPathBlur = (e: any) => { const value = e.target.value; if (value === localPathCache.current && value) { return; } const isEndwithGGUF = _.endsWith(value, '.gguf'); const isBlobFile = value.split('/').pop().includes('sha256'); let backend = backendOptionsMap.llamaBox; if (!isEndwithGGUF || !isBlobFile) { backend = backendOptionsMap.vllm; } form.setFieldValue('backend', backend); handleBackendChange?.(backend); }; const handleOnBlur = (e: any) => { const value = e.target.value; if (value) { handleOnValuesChange?.({ changedValues: {}, allValues: { ...form.getFieldsValue?.() }, source: formData?.source }); } }; const renderHuggingfaceFields = () => { return ( <> name="repo_id" rules={[ { required: true, message: intl.formatMessage( { id: 'common.form.rule.input' }, { name: intl.formatMessage({ id: 'models.form.repoid' }) } ) } ]} > {isGGUF && ( name="file_name" rules={[ { required: true, message: intl.formatMessage( { id: 'common.form.rule.input' }, { name: intl.formatMessage({ id: 'models.form.filename' }) } ) } ]} > )} ); }; const renderOllamaModelFields = () => { return ( <> name="ollama_library_model_name" rules={[ { required: true, message: intl.formatMessage( { id: 'common.form.rule.input' }, { name: intl.formatMessage({ id: 'models.table.name' }) } ) } ]} > {intl.formatMessage({ id: 'models.form.ollamalink' })} } label={intl.formatMessage({ id: 'model.form.ollama.model' })} required > ); }; const renderLocalPathFields = () => { return ( <> name="local_path" key="local_path" rules={[ { required: true, message: intl.formatMessage( { id: 'common.form.rule.input' }, { name: intl.formatMessage({ id: 'models.form.filePath' }) } ) } ]} > } required > ); }; const renderFieldsBySource = useMemo(() => { if (SEARCH_SOURCE.includes(formData?.source || '')) { return renderHuggingfaceFields(); } if (formData?.source === modelSourceMap.ollama_library_value) { return renderOllamaModelFields(); } if (formData?.source === modelSourceMap.local_path_value) { return renderLocalPathFields(); } return null; }, [formData?.source, isGGUF, intl]); const handleSumit = () => { form.submit(); }; const handleSubmitAnyway = async () => { submitAnyway.current = true; form.submit?.(); }; const handleOk = async (data: FormData) => { const formdata = getSourceRepoConfigValue(data.source, data).values; let submitData = {} as FormData; const isVoxBox = [backendOptionsMap.voxBox].includes(formdata.backend); submitData = { ..._.omit(formdata, ['scheduleType']), categories: formdata.categories ? [formdata.categories] : [], worker_selector: formdata.scheduleType === 'manual' ? null : formdata.worker_selector, ...(isVoxBox ? { distributed_inference_across_workers: false, cpu_offloading: false } : {}), ...generateGPUIds(formdata) }; onOk(submitData); }; const onValuesChange = (changedValues: any, allValues: any) => { const fieldName = Object.keys(changedValues)[0]; if (excludeFields.includes(fieldName)) { return; } handleOnValuesChange({ changedValues, allValues, source: formData?.source as string }); }; const handleManulOnValuesChange = (changedValues: any, allValues: any) => { handleOnValuesChange({ changedValues, allValues, source: formData?.source as string }); }; const handleOnClose = () => { onCancel?.(); }; useEffect(() => { if (open && formData) { setOriginalFormData(); } if (!open) { checkTokenRef.current?.cancel?.(); originFormData.current = null; setWarningStatus({ show: false, message: '' }); } }, [open, formData]); return ( {intl.formatMessage({ id: 'models.form.submit.anyway' })} ) } > } > { setWarningStatus({ show: false, message: '' }); }} warningStatus={warningStatus} contentStyle={{ paddingInline: 0 }} > } >
name="name" rules={[ { required: true, message: getRuleMessage('input', 'common.table.name') } ]} > name="source" rules={[ { required: true, message: getRuleMessage('select', 'models.form.source') } ]} > {action === PageAction.EDIT && ( )} {renderFieldsBySource} } options={[ { label: backendLabelMap[backendOptionsMap.llamaBox], value: backendOptionsMap.llamaBox, disabled: formData?.source === modelSourceMap.local_path_value ? false : !isGGUF }, { label: backendLabelMap[backendOptionsMap.vllm], value: backendOptionsMap.vllm, disabled: formData?.source === modelSourceMap.local_path_value ? false : isGGUF }, { label: backendLabelMap[backendOptionsMap.ascendMindie], value: backendOptionsMap.ascendMindie, disabled: formData?.source === modelSourceMap.local_path_value ? false : isGGUF }, { label: backendLabelMap[backendOptionsMap.voxBox], value: backendOptionsMap.voxBox, disabled: formData?.source === modelSourceMap.local_path_value ? false : isGGUF } ]} disabled={ action === PageAction.EDIT && formData?.source !== modelSourceMap.local_path_value } > name="replicas" rules={[ { required: true, message: getRuleMessage('input', 'models.form.replicas') } ]} > name="description">
); }; export default UpdateModal;