import IconFont from '@/components/icon-font'; import LabelSelector from '@/components/label-selector'; import ListInput from '@/components/list-input'; import SealCascader from '@/components/seal-form/seal-cascader'; import SealInput from '@/components/seal-form/seal-input'; import SealSelect from '@/components/seal-form/seal-select'; import TooltipList from '@/components/tooltip-list'; import { PageActionType } from '@/config/types'; import useAppUtils from '@/hooks/use-app-utils'; import { QuestionCircleOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Checkbox, Collapse, Form, FormInstance, Tooltip, Typography } from 'antd'; import { CheckboxChangeEvent } from 'antd/es/checkbox'; import _ from 'lodash'; import React, { useCallback, useMemo } from 'react'; import { backendLabelMap, backendOptionsMap, backendParamsHolderTips, modelCategories, placementStrategyOptions } from '../config'; import { useFormContext } from '../config/form-context'; import llamaConfig from '../config/llama-config'; import { FormData } from '../config/types'; import vllmConfig from '../config/vllm-config'; import dataformStyles from '../style/data-form.less'; import GPUCard from './gpu-card'; interface AdvanceConfigProps { isGGUF: boolean; form: FormInstance; gpuOptions: Array; action: PageActionType; source: string; } const CheckboxField: React.FC<{ title: string; label: string; checked?: boolean; onChange?: (e: CheckboxChangeEvent) => void; }> = ({ title, label, checked, onChange }) => { return ( {label} ); }; const AdvanceConfig: React.FC = (props) => { const { form, isGGUF, gpuOptions, source } = props; const { getRuleMessage } = useAppUtils(); const intl = useIntl(); const wokerSelector = Form.useWatch('worker_selector', form); const EnviromentVars = Form.useWatch('env', form); const scheduleType = Form.useWatch('scheduleType', form); const backend = Form.useWatch('backend', form); const backend_parameters = Form.useWatch('backend_parameters', form); const categories = Form.useWatch('categories', form); const backend_version = Form.useWatch('backend_version', form); const placement_strategy = Form.useWatch('placement_strategy', form); const gpuSelectorIds = Form.useWatch(['gpu_selector', 'gpu_ids'], form); const worker_selector = Form.useWatch('worker_selector', form); const { onValuesChange } = useFormContext(); const placementStrategyTips = [ { title: 'Spread', tips: intl.formatMessage({ id: 'resources.form.spread.tips' }) }, { title: 'Binpack', tips: intl.formatMessage({ id: 'resources.form.binpack.tips' }) } ]; const scheduleTypeTips = [ { title: intl.formatMessage({ id: 'models.form.scheduletype.auto' }), tips: intl.formatMessage({ id: 'models.form.scheduletype.auto.tips' }) }, { title: intl.formatMessage({ id: 'models.form.scheduletype.manual' }), tips: intl.formatMessage({ id: 'models.form.scheduletype.manual.tips' }) } ]; const paramsConfig = useMemo(() => { if (backend === backendOptionsMap.llamaBox) { return llamaConfig; } if (backend === backendOptionsMap.vllm) { return vllmConfig; } return []; }, [backend]); const backendParamsTips = useMemo(() => { if (backend === backendOptionsMap.llamaBox) { return { backend: 'llama-box', releases: 'https://github.com/gpustack/llama-box/releases', link: 'https://github.com/gpustack/llama-box?tab=readme-ov-file#usage' }; } if (backend === backendOptionsMap.vllm) { return { backend: 'vLLM', releases: 'https://github.com/vllm-project/vllm/releases', link: 'https://docs.vllm.ai/en/stable/serving/openai_compatible_server.html#cli-reference' }; } return null; }, [backend]); const handleWorkerLabelsChange = useCallback( (labels: Record) => { form.setFieldValue('worker_selector', labels); }, [] ); const handleEnviromentVarsChange = useCallback( (labels: Record) => { form.setFieldValue('env', labels); }, [] ); const handleBackendParametersChange = useCallback((list: string[]) => { form.setFieldValue('backend_parameters', list); }, []); const handleBackendParametersOnBlur = () => { const backendParams = form.getFieldValue('backend_parameters'); onValuesChange?.({}, form.getFieldsValue()); }; const handleDeleteBackendParameters = (index: number) => { onValuesChange?.({}, form.getFieldsValue()); }; const handleSelectorOnBlur = () => { const workerSelector = form.getFieldValue('worker_selector'); // check if all keys have values const hasEmptyValue = _.some(_.keys(workerSelector), (k: string) => { return !workerSelector[k]; }); if (!hasEmptyValue) { onValuesChange?.({}, form.getFieldsValue()); } }; const handleDeleteWorkerSelector = (index: number) => { onValuesChange?.({}, form.getFieldsValue()); }; const handleBackendVersionOnBlur = () => { const backendVersion = form.getFieldValue('backend_version'); onValuesChange?.({}, form.getFieldsValue()); }; const handleScheduleTypeChange = (value: string) => { if (value === 'auto') { onValuesChange?.({}, form.getFieldsValue()); } }; const handleGpuSelectorChange = (value: any[] | string) => { onValuesChange?.({}, form.getFieldsValue()); }; const collapseItems = useMemo(() => { const children = ( <> name="categories"> } options={[ { label: intl.formatMessage({ id: 'models.form.scheduletype.auto' }), value: 'auto' }, { label: intl.formatMessage({ id: 'models.form.scheduletype.manual' }), value: 'manual' } ]} > {scheduleType === 'auto' && ( <> name="placement_strategy"> } > name="worker_selector" rules={[ ({ getFieldValue }) => ({ validator(rule, value) { if ( getFieldValue('scheduleType') === 'auto' && _.keys(value).length > 0 ) { if (_.some(_.keys(value), (k: string) => !value[k])) { return Promise.reject( intl.formatMessage( { id: 'common.validate.value' }, { name: intl.formatMessage({ id: 'models.form.selector' }) } ) ); } } return Promise.resolve(); } }) ]} > {intl.formatMessage({ id: 'resources.form.workerSelector.description' })} } > )} {scheduleType === 'manual' && ( <> triggerNode.parentNode} onChange={handleGpuSelectorChange} > )} {intl.formatMessage({ id: 'models.form.releases' })} ) } )} > name="backend_parameters"> {intl.formatMessage( { id: 'models.form.backend_parameters.vllm.tips' }, { backend: backendParamsTips.backend || '' } )}{' '} {intl.formatMessage({ id: 'common.text.here' })} ) } > name="env" rules={[ () => ({ validator(rule, value) { if (_.keys(value).length > 0) { if (_.some(_.keys(value), (k: string) => !value[k])) { return Promise.reject( intl.formatMessage( { id: 'common.validate.value' }, { name: intl.formatMessage({ id: 'common.text.variable' }) } ) ); } } return Promise.resolve(); } }) ]} > {backend === backendOptionsMap.llamaBox && (
name="cpu_offloading" valuePropName="checked" style={{ padding: '0 10px', marginBottom: 0 }} noStyle >
)} {scheduleType === 'auto' && [backendOptionsMap.llamaBox, backendOptionsMap.vllm].includes( backend ) && (
name="distributed_inference_across_workers" valuePropName="checked" style={{ padding: '0 10px', marginBottom: 0 }} noStyle >
)}
name="restart_on_error" valuePropName="checked" style={{ padding: '0 10px', marginBottom: 0 }} noStyle >
); return [ { key: '1', label: ( {intl.formatMessage({ id: 'resources.form.advanced' })} ), forceRender: true, children } ]; }, [ form, source, intl, gpuOptions, paramsConfig, scheduleType, wokerSelector, backend, backend_parameters, isGGUF, categories, backend_version, placement_strategy, gpuSelectorIds, EnviromentVars, worker_selector ]); return ( ( )} items={collapseItems} > ); }; export default React.memo(AdvanceConfig);