feat: add gpus_per_replicas

This commit is contained in:
jialin
2025-10-20 14:53:33 +08:00
parent 19e275a17a
commit 9b843151fe
10 changed files with 170 additions and 17 deletions
+9 -1
View File
@@ -195,5 +195,13 @@ export default {
'models.accessSettings.public.tips':
'When set to public, anyone can access this model without authentication, which may lead to data exposure risks.',
'models.table.button.deploy': 'Deploy Now',
'models.form.backendVersion.holder': 'Enter or select a version'
'models.form.backendVersion.holder': 'Enter or select a version',
'models.form.gpusperreplica': 'GPUs per Replica',
'models.form.gpusAllocationType': 'GPU Allocation Type',
'models.form.gpusAllocationType.auto': 'Auto',
'models.form.gpusAllocationType.custom': 'Custom',
'models.form.gpusAllocationType.auto.tips':
'System calculates GPUs per replica automatically.',
'models.form.gpusAllocationType.custom.tips':
'You can specify the exact number of GPUs per replica.'
};
+16 -2
View File
@@ -192,7 +192,15 @@ export default {
'models.accessSettings.public.tips':
'When set to public, anyone can access this model without authentication, which may lead to data exposure risks.',
'models.table.button.deploy': 'Deploy Now',
'models.form.backendVersion.holder': 'Enter or select a version'
'models.form.backendVersion.holder': 'Enter or select a version',
'models.form.gpusperreplica': 'GPUs per Replica',
'models.form.gpusAllocationType': 'GPU Allocation Type',
'models.form.gpusAllocationType.auto': 'Auto',
'models.form.gpusAllocationType.custom': 'Custom',
'models.form.gpusAllocationType.auto.tips':
'System calculates GPUs per replica automatically.',
'models.form.gpusAllocationType.custom.tips':
'You can specify the exact number of GPUs per replica.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -226,6 +234,12 @@ export default {
// 29. 'models.accessSettings.allowedUsers': 'Allowed users',
// 30. 'models.accessSettings.public.tips': 'When set to public, anyone can access this model without authentication, which may lead to data exposure risks.',
// 31. 'models.table.button.deploy': 'Deploy Now',
// 32. 'models.form.backendVersion.holder': 'Enter or select a version'
// 32. 'models.form.backendVersion.holder': 'Enter or select a version',
// 33. 'models.form.gpusperreplica': 'GPUs per Replica',
// 34. 'models.form.gpusAllocationType': 'GPU Allocation Type',
// 35. 'models.form.gpusAllocationType.auto': 'Auto',
// 36. 'models.form.gpusAllocationType.custom': 'Custom',
// 37. 'models.form.gpusAllocationType.auto.tips': 'System calculates GPUs per replica automatically.',
// 38. 'models.form.gpusAllocationType.custom.tips': 'You can specify the exact number of GPUs per replica.'
// ========== End of To-Do List ==========
+16 -2
View File
@@ -195,7 +195,15 @@ export default {
'models.accessSettings.public.tips':
'When set to public, anyone can access this model without authentication, which may lead to data exposure risks.',
'models.table.button.deploy': 'Deploy Now',
'models.form.backendVersion.holder': 'Enter or select a version'
'models.form.backendVersion.holder': 'Enter or select a version',
'models.form.gpusperreplica': 'GPUs per Replica',
'models.form.gpusAllocationType': 'GPU Allocation Type',
'models.form.gpusAllocationType.auto': 'Auto',
'models.form.gpusAllocationType.custom': 'Custom',
'models.form.gpusAllocationType.auto.tips':
'System calculates GPUs per replica automatically.',
'models.form.gpusAllocationType.custom.tips':
'You can specify the exact number of GPUs per replica.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -221,5 +229,11 @@ export default {
// 21. 'models.accessSettings.allowedUsers': 'Allowed users',
// 22. 'models.accessSettings.public.tips': 'When set to public, anyone can access this model without authentication, which may lead to data exposure risks.',
// 23. 'models.table.button.deploy': 'Deploy Now',
// 24. 'models.form.backendVersion.holder': 'Enter or select a version'
// 24. 'models.form.backendVersion.holder': 'Enter or select a version',
// 25. 'models.form.gpusperreplica': 'GPUs per Replica',
// 26. 'models.form.gpusAllocationType': 'GPU Allocation Type',
// 27. 'models.form.gpusAllocationType.auto': 'Auto',
// 28. 'models.form.gpusAllocationType.custom': 'Custom',
// 29. 'models.form.gpusAllocationType.auto.tips': 'System calculates GPUs per replica automatically.',
// 30. 'models.form.gpusAllocationType.custom.tips': 'You can specify the exact number of GPUs per replica.'
// ========== End of To-Do List ==========
+8 -1
View File
@@ -184,5 +184,12 @@ export default {
'models.accessSettings.public.tips':
'公开后,任何人无需认证即可访问,可能存在数据泄露风险。',
'models.table.button.deploy': '立即部署',
'models.form.backendVersion.holder': '输入或选择一个版本'
'models.form.backendVersion.holder': '输入或选择一个版本',
'models.form.gpusperreplica': '每副本 GPU 数量',
'models.form.gpusAllocationType': 'GPU 分配方式',
'models.form.gpusAllocationType.auto': '自动',
'models.form.gpusAllocationType.custom': '自定义',
'models.form.gpusAllocationType.auto.tips':
'系统自动计算每个副本的 GPU 数量。',
'models.form.gpusAllocationType.custom.tips': '您可以指定每个副本的 GPU 数量'
};
+6
View File
@@ -194,6 +194,11 @@ export const ScheduleValueMap = {
SpecificGPUType: 'specific_gpu_type'
};
export const gpusCountTypeMap = {
Auto: 'auto',
Custom: 'custom'
};
export const scheduleList = [
{
label: 'models.form.scheduletype.auto',
@@ -324,6 +329,7 @@ export const excludeFields = [
'backend_version',
'ollama_library_model_name',
'scheduleType',
'gpusCountType',
'placement_strategy',
'backend',
'gpu_selector',
+5
View File
@@ -27,6 +27,7 @@ export interface ListItem {
access_policy: 'public' | 'authed' | 'allowed_users';
gpu_selector?: {
gpu_ids: string[];
gpus_per_replica?: number;
};
worker_selector?: object;
}
@@ -63,11 +64,13 @@ export interface FormData {
gpu_ids?: string[];
gpu_type?: string;
gpu_count?: number;
gpus_per_replica?: number;
};
placement_strategy?: string;
cpu_offloading?: boolean;
worker_selector?: object;
scheduleType?: string;
gpusCountType?: string;
name: string;
replicas: number;
description: string;
@@ -191,6 +194,7 @@ export interface CatalogSpec {
worker_selector: Record<string, any>;
gpu_selector: {
gpu_ids: string[];
gpus_per_replica: number;
};
backend: string;
backend_version: string;
@@ -220,6 +224,7 @@ export interface EvaluateSpec {
worker_selector?: Record<string, any>;
gpu_selector?: {
gpu_ids: string[];
gpus_per_replica: number;
};
backend?: string;
backend_version?: string;
+15 -2
View File
@@ -1,4 +1,5 @@
import _ from 'lodash';
import { gpusCountTypeMap } from '.';
import { backendOptionsMap } from '../config/backend-parameters';
import { FormData } from './types';
@@ -33,6 +34,11 @@ export const generateGPUSelector = (data: any, gpuOptions: any[]) => {
};
};
/**
* before submit the form, generate the gpu_selector field
* @param data
* @returns
*/
export const generateGPUIds = (data: FormData) => {
const gpu_ids = _.get(data, 'gpu_selector.gpu_ids', []);
console.log('generateGPUIds', gpu_ids);
@@ -54,10 +60,17 @@ export const generateGPUIds = (data: FormData) => {
},
[]
);
if (gpusCountTypeMap.Auto === data.gpusCountType) {
return {
gpu_selector: {
gpu_ids: result || []
}
};
}
return {
gpu_selector: {
gpu_ids: result || []
gpu_ids: result || [],
gpus_per_replica: data.gpu_selector?.gpus_per_replica || null
}
};
};
+3 -2
View File
@@ -5,7 +5,7 @@ import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import _ from 'lodash';
import React, { forwardRef, useImperativeHandle } from 'react';
import { excludeFields, ScheduleValueMap } from '../config';
import { excludeFields, gpusCountTypeMap, ScheduleValueMap } from '../config';
import { backendOptionsMap } from '../config/backend-parameters';
import { FormContext } from '../config/form-context';
import {
@@ -120,7 +120,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
data.categories = data.categories ? [data.categories] : [];
const gpuSelector = generateGPUIds(data);
const allValues = {
..._.omit(data, ['scheduleType']),
..._.omit(data, ['scheduleType', 'gpusCountType']),
...gpuSelector
};
@@ -250,6 +250,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
placement_strategy: 'spread',
cpu_offloading: true,
scheduleType: ScheduleValueMap.Auto,
gpusCountType: gpusCountTypeMap.Auto,
categories: null,
restart_on_error: true,
distributed_inference_across_workers: true,
+81 -5
View File
@@ -1,3 +1,4 @@
import SealInputNumber from '@/components/seal-form/input-number';
import SealCascader from '@/components/seal-form/seal-cascader';
import SealSelect from '@/components/seal-form/seal-select';
import TooltipList from '@/components/tooltip-list';
@@ -6,9 +7,9 @@ import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import GPUCard from '../components/gpu-card';
import { scheduleList, ScheduleValueMap } from '../config';
import { gpusCountTypeMap, scheduleList, ScheduleValueMap } from '../config';
import { backendOptionsMap } from '../config/backend-parameters';
import { useCatalogFormContext, useFormContext } from '../config/form-context';
import { useFormContext } from '../config/form-context';
const scheduleTypeTips = [
{
@@ -27,13 +28,31 @@ const scheduleTypeTips = [
}
];
const gpuAllocateTypeTips = [
{
title: {
text: 'models.form.gpusAllocationType.auto',
locale: true
},
tips: 'models.form.gpusAllocationType.auto.tips'
},
{
title: {
text: 'models.form.gpusAllocationType.custom',
locale: true
},
tips: 'models.form.gpusAllocationType.custom.tips'
}
];
const ScheduleTypeForm: React.FC = () => {
const intl = useIntl();
const { onValuesChange, gpuOptions } = useFormContext();
const { onQuantizationChange } = useCatalogFormContext();
const { getRuleMessage } = useAppUtils();
const form = Form.useFormInstance();
const scheduleType = Form.useWatch('scheduleType', form);
const gpusCountType = Form.useWatch('gpusCountType', form);
const gpuSelectorIds = Form.useWatch(['gpu_selector', 'gpu_ids'], form);
const handleScheduleTypeChange = (value: string) => {
if (value === ScheduleValueMap.Auto) {
@@ -41,8 +60,11 @@ const ScheduleTypeForm: React.FC = () => {
}
};
const handleOnQuantizationChange = (val: any) => {
onQuantizationChange?.(val);
const handleGpusCountTypeChange = (val: string) => {
if (val === 'custom') {
form.setFieldValue(['gpu_selector', 'gpus_per_replica'], 2);
}
onValuesChange?.({}, form.getFieldsValue());
};
const handleBeforeGpuSelectorChange = (gpuIds: any[]) => {};
@@ -52,6 +74,23 @@ const ScheduleTypeForm: React.FC = () => {
onValuesChange?.({}, form.getFieldsValue());
};
const handleOnStepReplicaStep = (
value: number,
info: { offset: number; type: 'up' | 'down' }
) => {
let newValue = value;
const isPowerOfTwo = (n: number) => (n & (n - 1)) === 0 && n !== 0; // check power of two
if (!isPowerOfTwo(value)) {
if (info.type === 'up') {
newValue = Math.pow(2, Math.ceil(Math.log2(value)));
} else {
newValue = Math.pow(2, Math.floor(Math.log2(value)));
}
}
form.setFieldValue(['gpu_selector', 'gpus_per_replica'], newValue);
onValuesChange?.({}, form.getFieldsValue());
};
return (
<>
<Form.Item name="scheduleType">
@@ -115,6 +154,43 @@ const ScheduleTypeForm: React.FC = () => {
onChange={handleGpuSelectorChange}
></SealCascader>
</Form.Item>
<Form.Item name="gpusCountType">
<SealSelect
onChange={handleGpusCountTypeChange}
label={intl.formatMessage({
id: 'models.form.gpusAllocationType'
})}
description={
<TooltipList list={gpuAllocateTypeTips}></TooltipList>
}
options={[
{
label: intl.formatMessage({
id: 'models.form.gpusAllocationType.auto'
}),
value: gpusCountTypeMap.Auto
},
{
label: intl.formatMessage({
id: 'models.form.gpusAllocationType.custom'
}),
value: gpusCountTypeMap.Custom
}
]}
></SealSelect>
</Form.Item>
{gpusCountType === gpusCountTypeMap.Custom && (
<Form.Item name={['gpu_selector', 'gpus_per_replica']}>
<SealInputNumber
label={intl.formatMessage({
id: 'models.form.gpusperreplica'
})}
min={1}
step={1}
onStep={handleOnStepReplicaStep}
/>
</Form.Item>
)}
</>
)}
</>
@@ -10,7 +10,7 @@ import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
import { useAtom } from 'jotai';
import { useState } from 'react';
import { queryGPUList } from '../apis';
import { ScheduleValueMap } from '../config';
import { gpusCountTypeMap, ScheduleValueMap } from '../config';
import { GPUListItem, ListItem } from '../config/types';
type EmptyObject = Record<never, never>;
@@ -226,13 +226,22 @@ export default function useFormInitialValues() {
}
};
/**
* before set the form initial values, generate the form values
* @param data
* @param gpuOptions
* @returns
*/
const generateFormValues = (data: ListItem, gpuOptions: any[]) => {
const formData = {
...data,
categories: data?.categories?.length ? data.categories[0] : null,
scheduleType: data?.gpu_selector
? ScheduleValueMap.Manual
: ScheduleValueMap.Auto
: ScheduleValueMap.Auto,
gpusCountType: data?.gpu_selector?.gpus_per_replica
? gpusCountTypeMap.Custom
: gpusCountTypeMap.Auto
};
return formData;
};