import ModalFooter from '@/components/modal-footer'; import { PageActionType } from '@/config/types'; import { CloseOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Button, Drawer } from 'antd'; import _, { debounce } from 'lodash'; import { FC, useCallback, useEffect, useRef, useState } from 'react'; import styled from 'styled-components'; import { backendOptionsMap, getSourceRepoConfigValue, modelSourceMap } from '../config'; import { FormContext } from '../config/form-context'; import { FormData, SourceType } from '../config/types'; import { useCheckCompatibility } from '../hooks'; import ColumnWrapper from './column-wrapper'; import CompatibilityAlert from './compatible-alert'; import DataForm from './data-form'; import HFModelFile from './hf-model-file'; import ModelCard from './model-card'; import SearchModel from './search-model'; import Separator from './separator'; import TitleWrapper from './title-wrapper'; const ModalFooterStyle = { padding: '16px 24px', display: 'flex', justifyContent: 'flex-end' }; const ColWrapper = styled.div` display: flex; flex: 1; maxwidth: 33.33%; `; const FormWrapper = styled.div` display: flex; flex: 1; maxwidth: 100%; `; type AddModalProps = { title: string; hasLinuxWorker?: boolean; action: PageActionType; open: boolean; source: SourceType; isGGUF?: boolean; width?: string | number; gpuOptions: any[]; modelFileOptions: any[]; initialValues?: any; deploymentType?: 'modelList' | 'modelFiles'; onOk: (values: FormData) => void; onCancel: () => void; }; const AddModal: FC = (props) => { const { title, open, onOk, onCancel, hasLinuxWorker, source, action, width = 600, deploymentType = 'modelList', initialValues } = props || {}; const SEARCH_SOURCE = [ modelSourceMap.huggingface_value, modelSourceMap.modelscope_value ]; const { handleShowCompatibleAlert, setWarningStatus, handleBackendChangeBefore, handleOnValuesChange, checkTokenRef, warningStatus, submitAnyway } = useCheckCompatibility(); const form = useRef({}); const intl = useIntl(); const [selectedModel, setSelectedModel] = useState({}); const [collapsed, setCollapsed] = useState(false); const [isGGUF, setIsGGUF] = useState(props.isGGUF || false); const modelFileRef = useRef(null); const handleSelectModelFile = useCallback((item: any) => { form.current?.setFieldsValue?.({ file_name: item.fakeName, backend: backendOptionsMap.llamaBox, ...item.evaluateResult?.default_spec }); if (item.fakeName) { handleShowCompatibleAlert(item.evaluateResult); } }, []); const handleOnSelectModel = (item: any) => { setSelectedModel(item); form.current?.handleOnSelectModel?.(item); if (!item.isGGUF) { setIsGGUF(false); handleShowCompatibleAlert(item.evaluateResult); form.current?.setFieldsValue?.({ ...item.evaluateResult?.default_spec }); } }; const handleOnOk = async (allValues: FormData) => { const result = getSourceRepoConfigValue(props.source, allValues).values; onOk(result); }; const handleSubmitAnyway = async () => { submitAnyway.current = true; form.current?.submit?.(); }; const handleSumit = () => { form.current?.submit?.(); }; const debounceFetchModelFiles = debounce(() => { modelFileRef.current?.fetchModelFiles?.(); }, 100); const handleSetIsGGUF = (flag: boolean) => { setIsGGUF(flag); if (flag) { debounceFetchModelFiles(); } }; const handleBackendChange = async (backend: string) => { if (backend === backendOptionsMap.llamaBox) { setIsGGUF(true); } else { setIsGGUF(false); } const data = form.current.form.getFieldsValue?.(); const res = handleBackendChangeBefore(data); if (res.show) { return; } if (data.local_path || props.source !== modelSourceMap.local_path_value) { handleOnValuesChange?.({ changedValues: {}, allValues: backend === backendOptionsMap.llamaBox ? data : _.omit(data, [ 'cpu_offloading', 'distributed_inference_across_workers' ]), source: props.source }); } }; const onValuesChange = async (changedValues: any, allValues: any) => { handleOnValuesChange?.({ changedValues, allValues, source: props.source }); }; const handleCancel = useCallback(() => { onCancel?.(); }, [onCancel]); const handleOnOpen = useCallback(() => { if (props.deploymentType === 'modelFiles') { form.current?.form?.setFieldsValue({ ...props.initialValues }); } else { const backend = source === modelSourceMap.ollama_library_value ? backendOptionsMap.llamaBox : backendOptionsMap.vllm; form.current?.setFieldValue?.('backend', backend); } }, [source, props.initialValues, props.deploymentType]); useEffect(() => { if (!open) { return; } else { handleOnOpen(); } return () => { setSelectedModel({}); setWarningStatus({ show: false, title: '', message: [] }); checkTokenRef.current?.cancel(); }; }, [open, handleOnOpen]); return ( {title} } open={open} onClose={handleCancel} destroyOnClose={true} closeIcon={false} maskClosable={false} keyboard={false} zIndex={2000} styles={{ body: { height: 'calc(100vh - 57px)', padding: '16px 0', overflowX: 'hidden' }, content: { borderRadius: '6px 0 0 6px' } }} width={width} footer={false} >
{SEARCH_SOURCE.includes(props.source) && deploymentType === 'modelList' && ( <> {isGGUF && ( )} )} { setWarningStatus({ show: false, message: '' }); }} warningStatus={warningStatus} contentStyle={{ paddingInline: 0 }} > {intl.formatMessage({ id: 'models.form.submit.anyway' })} ) } style={ModalFooterStyle} > } > <> {SEARCH_SOURCE.includes(source) && deploymentType === 'modelList' && ( {intl.formatMessage({ id: 'models.form.configurations' })} )}
); }; export default AddModal;