chore: scheduling group

This commit is contained in:
jialin
2025-10-27 14:13:47 +08:00
parent df65d3e2f6
commit 61065ee0b7
7 changed files with 138 additions and 113 deletions
+2 -1
View File
@@ -212,5 +212,6 @@ export default {
'models.form.kvCache.tips':
'Available only with built-in backends (vLLM / SGLang) — switch backend in <span class="bold-text">Advanced</span> to enable.',
'models.form.kvCache.tips2':
'KV cache is only supported when using built-in inference backends (vLLM or SGLang).'
'KV cache is only supported when using built-in inference backends (vLLM or SGLang).',
'models.form.scheduling': 'Scheduling'
};
+4 -2
View File
@@ -209,7 +209,8 @@ export default {
'models.form.kvCache.tips':
'Available only with built-in backends (vLLM / SGLang) — switch backend in <span class="bold-text">Advanced</span> to enable.',
'models.form.kvCache.tips2':
'KV cache is only supported when using built-in inference backends (vLLM or SGLang).'
'KV cache is only supported when using built-in inference backends (vLLM or SGLang).',
'models.form.scheduling': 'Scheduling'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -255,5 +256,6 @@ export default {
// 42. 'models.mymodels.status.active': 'Active'
// 43. 'models.form.remoteURL.tips': 'Refer to the <a href="https://docs.lmcache.ai/api_reference/configurations.html" target="_blank">configuration documentation</a> for details.',
// 44. 'models.form.kvCache.tips': 'Available only with built-in backends (vLLM / SGLang) — switch backend in <span class="bold-text">Advanced</span> to enable.'
// 45. 'models.form.kvCache.tips2': 'KV cache is only supported when using built-in inference backends (vLLM or SGLang).'
// 45. 'models.form.kvCache.tips2': 'KV cache is only supported when using built-in inference backends (vLLM or SGLang).',
// 46. 'models.form.scheduling': 'Scheduling'
// ========== End of To-Do List ==========
+3 -1
View File
@@ -212,7 +212,8 @@ export default {
'models.form.kvCache.tips':
'Available only with built-in backends (vLLM / SGLang) — switch backend in <span class="bold-text">Advanced</span> to enable.',
'models.form.kvCache.tips2':
'KV cache is only supported when using built-in inference backends (vLLM or SGLang).'
'KV cache is only supported when using built-in inference backends (vLLM or SGLang).',
'models.form.scheduling': 'Scheduling'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -222,4 +223,5 @@ export default {
// 5. 'models.form.remoteURL.tips': 'Refer to the <a href="https://docs.lmcache.ai/api_reference/configurations.html" target="_blank">configuration documentation</a> for details.',
// 6. 'models.form.kvCache.tips': 'Available only with built-in backends (vLLM / SGLang) — switch backend in <span class="bold-text">Advanced</span> to enable.'
// 7. 'models.form.kvCache.tips2': 'KV cache is only supported when using built-in inference backends (vLLM or SGLang).';
// 8. 'models.form.scheduling': 'Scheduling'
// ========== End of To-Do List ==========
+2 -1
View File
@@ -200,5 +200,6 @@ export default {
'models.form.kvCache.tips':
'仅在内置后端(vLLM / SGLang)可用 —— 请在<span class="bold-text">高级</span>配置中切换后端以启用。',
'models.form.kvCache.tips2':
'仅在使用内置推理后端(vLLM 或 SGLang)时支持 KV 缓存。'
'仅在使用内置推理后端(vLLM 或 SGLang)时支持 KV 缓存。',
'models.form.scheduling': '调度'
};
+2 -103
View File
@@ -1,50 +1,25 @@
import LabelSelector from '@/components/label-selector';
import { LabelSelectorContext } from '@/components/label-selector/context';
import CheckboxField from '@/components/seal-form/checkbox-field';
import SealSelect from '@/components/seal-form/seal-select';
import TooltipList from '@/components/tooltip-list';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import _ from 'lodash';
import { useCallback } from 'react';
import {
modelCategories,
placementStrategyOptions,
ScheduleValueMap
} from '../config';
import { modelCategories, ScheduleValueMap } from '../config';
import { backendOptionsMap } from '../config/backend-parameters';
import { useFormContext } from '../config/form-context';
import { FormData } from '../config/types';
import Backend from '../forms/backend';
import BackendParametersList from '../forms/backend-parameters-list';
import ScheduleTypeForm from '../forms/schedule-type';
const placementStrategyTips = [
{
title: 'Spread',
tips: 'resources.form.spread.tips'
},
{
title: 'Binpack',
tips: 'resources.form.binpack.tips'
}
];
const AdvanceConfig = () => {
const intl = useIntl();
const form = Form.useFormInstance();
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 { onValuesChange, workerLabelOptions } = useFormContext();
const { onValuesChange } = useFormContext();
const handleWorkerLabelsChange = useCallback(
(labels: Record<string, any>) => {
form.setFieldValue('worker_selector', labels);
},
[]
);
const handleEnviromentVarsChange = useCallback(
(labels: Record<string, any>) => {
form.setFieldValue('env', labels);
@@ -63,14 +38,6 @@ const AdvanceConfig = () => {
}
};
const handleSelectorOnBlur = () => {
onSelectorChange('worker_selector');
};
const handleDeleteWorkerSelector = (index: number) => {
onValuesChange?.({}, form.getFieldsValue());
};
const handleEnvSelectorOnBlur = () => {
onSelectorChange('env', true);
};
@@ -96,75 +63,7 @@ const AdvanceConfig = () => {
options={modelCategories}
></SealSelect>
</Form.Item>
<ScheduleTypeForm></ScheduleTypeForm>
<Backend></Backend>
{scheduleType === ScheduleValueMap.Auto && (
<>
<Form.Item<FormData> name="placement_strategy">
<SealSelect
label={intl.formatMessage({
id: 'resources.form.placementStrategy'
})}
options={placementStrategyOptions}
description={
<TooltipList list={placementStrategyTips}></TooltipList>
}
></SealSelect>
</Form.Item>
<LabelSelectorContext.Provider
value={{ options: workerLabelOptions }}
>
<Form.Item<FormData>
name="worker_selector"
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (
getFieldValue('scheduleType') === ScheduleValueMap.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();
}
})
]}
>
<LabelSelector
isAutoComplete
label={intl.formatMessage({
id: 'resources.form.workerSelector'
})}
labels={wokerSelector}
onChange={handleWorkerLabelsChange}
onBlur={handleSelectorOnBlur}
onDelete={handleDeleteWorkerSelector}
description={
<span>
{intl.formatMessage({
id: 'resources.form.workerSelector.description'
})}
</span>
}
></LabelSelector>
</Form.Item>
</LabelSelectorContext.Provider>
</>
)}
<BackendParametersList></BackendParametersList>
<Form.Item<FormData> name="env">
<LabelSelector
+12 -2
View File
@@ -28,6 +28,7 @@ import useQueryBackends from '../hooks/use-query-backends';
import AdvanceConfig from './advance-config';
import BasicForm from './basic';
import Performance from './performance';
import ScheduleTypeForm from './schedule-type';
const advancedRequiredFields = [
'gpu_selector',
@@ -103,6 +104,11 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
label: intl.formatMessage({ id: 'common.title.basicInfo' }),
field: 'name'
},
{
value: 'scheduling',
label: intl.formatMessage({ id: 'models.form.scheduling' }),
field: 'scheduleType'
},
{
value: 'performance',
label: intl.formatMessage({ id: 'models.form.performance' }),
@@ -378,18 +384,22 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
accordion={false}
onChange={handleOnCollapseChange}
items={[
{
key: 'scheduling',
label: intl.formatMessage({ id: 'models.form.scheduling' }),
forceRender: true,
children: <ScheduleTypeForm></ScheduleTypeForm>
},
{
key: 'performance',
label: intl.formatMessage({ id: 'models.form.performance' }),
forceRender: true,
extra: <div ref={performanceRef}></div>,
children: <Performance></Performance>
},
{
key: 'advanced',
label: intl.formatMessage({ id: 'resources.form.advanced' }),
forceRender: true,
extra: <div ref={advanceRef}></div>,
children: <AdvanceConfig></AdvanceConfig>
}
]}
+113 -3
View File
@@ -1,3 +1,5 @@
import LabelSelector from '@/components/label-selector';
import { LabelSelectorContext } from '@/components/label-selector/context';
import SealInputNumber from '@/components/seal-form/input-number';
import SealCascader from '@/components/seal-form/seal-cascader';
import SealSelect from '@/components/seal-form/seal-select';
@@ -5,11 +7,29 @@ import TooltipList from '@/components/tooltip-list';
import useAppUtils from '@/hooks/use-app-utils';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import _ from 'lodash';
import React from 'react';
import GPUCard from '../components/gpu-card';
import { gpusCountTypeMap, scheduleList, ScheduleValueMap } from '../config';
import {
gpusCountTypeMap,
placementStrategyOptions,
scheduleList,
ScheduleValueMap
} from '../config';
import { backendOptionsMap } from '../config/backend-parameters';
import { useFormContext } from '../config/form-context';
import { FormData } from '../config/types';
const placementStrategyTips = [
{
title: 'Spread',
tips: 'resources.form.spread.tips'
},
{
title: 'Binpack',
tips: 'resources.form.binpack.tips'
}
];
const scheduleTypeTips = [
{
@@ -47,11 +67,12 @@ const gpuAllocateTypeTips = [
const ScheduleTypeForm: React.FC = () => {
const intl = useIntl();
const { onValuesChange, gpuOptions } = useFormContext();
const { onValuesChange, gpuOptions, workerLabelOptions } = useFormContext();
const { getRuleMessage } = useAppUtils();
const form = Form.useFormInstance();
const scheduleType = Form.useWatch('scheduleType', form);
const gpusCountType = Form.useWatch('gpusCountType', form);
const wokerSelector = Form.useWatch('worker_selector', form);
const handleScheduleTypeChange = (value: string) => {
if (value === ScheduleValueMap.Auto) {
@@ -105,9 +126,32 @@ const ScheduleTypeForm: React.FC = () => {
}
};
const onSelectorChange = (field: string, allowEmpty?: boolean) => {
const workerSelector = form.getFieldValue(field);
// check if all keys have values
const hasEmptyValue = _.some(_.keys(workerSelector), (k: string) => {
return !workerSelector[k];
});
if (!hasEmptyValue || allowEmpty) {
onValuesChange?.({}, form.getFieldsValue());
}
};
const handleWorkerLabelsChange = (labels: Record<string, any>) => {
form.setFieldValue('worker_selector', labels);
};
const handleSelectorOnBlur = () => {
onSelectorChange('worker_selector');
};
const handleDeleteWorkerSelector = (index: number) => {
onValuesChange?.({}, form.getFieldsValue());
};
return (
<>
<Form.Item name="scheduleType">
<Form.Item name="scheduleType" data-field="scheduleType">
<SealSelect
onChange={handleScheduleTypeChange}
label={intl.formatMessage({ id: 'models.form.scheduletype' })}
@@ -208,6 +252,72 @@ const ScheduleTypeForm: React.FC = () => {
)}
</>
)}
{scheduleType === ScheduleValueMap.Auto && (
<>
<Form.Item<FormData> name="placement_strategy">
<SealSelect
label={intl.formatMessage({
id: 'resources.form.placementStrategy'
})}
options={placementStrategyOptions}
description={
<TooltipList list={placementStrategyTips}></TooltipList>
}
></SealSelect>
</Form.Item>
<LabelSelectorContext.Provider
value={{ options: workerLabelOptions }}
>
<Form.Item<FormData>
name="worker_selector"
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (
scheduleType === ScheduleValueMap.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();
}
})
]}
>
<LabelSelector
isAutoComplete
label={intl.formatMessage({
id: 'resources.form.workerSelector'
})}
labels={wokerSelector}
onChange={handleWorkerLabelsChange}
onBlur={handleSelectorOnBlur}
onDelete={handleDeleteWorkerSelector}
description={
<span>
{intl.formatMessage({
id: 'resources.form.workerSelector.description'
})}
</span>
}
></LabelSelector>
</Form.Item>
</LabelSelectorContext.Provider>
</>
)}
</>
);
};