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': 'models.accessSettings.public.tips':
'When set to public, anyone can access this model without authentication, which may lead to data exposure risks.', '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.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': 'models.accessSettings.public.tips':
'When set to public, anyone can access this model without authentication, which may lead to data exposure risks.', '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.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) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -226,6 +234,12 @@ export default {
// 29. 'models.accessSettings.allowedUsers': 'Allowed users', // 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.', // 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', // 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 ========== // ========== End of To-Do List ==========
+16 -2
View File
@@ -195,7 +195,15 @@ export default {
'models.accessSettings.public.tips': 'models.accessSettings.public.tips':
'When set to public, anyone can access this model without authentication, which may lead to data exposure risks.', '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.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) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -221,5 +229,11 @@ export default {
// 21. 'models.accessSettings.allowedUsers': 'Allowed users', // 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.', // 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', // 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 ========== // ========== End of To-Do List ==========
+8 -1
View File
@@ -184,5 +184,12 @@ export default {
'models.accessSettings.public.tips': 'models.accessSettings.public.tips':
'公开后,任何人无需认证即可访问,可能存在数据泄露风险。', '公开后,任何人无需认证即可访问,可能存在数据泄露风险。',
'models.table.button.deploy': '立即部署', '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' SpecificGPUType: 'specific_gpu_type'
}; };
export const gpusCountTypeMap = {
Auto: 'auto',
Custom: 'custom'
};
export const scheduleList = [ export const scheduleList = [
{ {
label: 'models.form.scheduletype.auto', label: 'models.form.scheduletype.auto',
@@ -324,6 +329,7 @@ export const excludeFields = [
'backend_version', 'backend_version',
'ollama_library_model_name', 'ollama_library_model_name',
'scheduleType', 'scheduleType',
'gpusCountType',
'placement_strategy', 'placement_strategy',
'backend', 'backend',
'gpu_selector', 'gpu_selector',
+5
View File
@@ -27,6 +27,7 @@ export interface ListItem {
access_policy: 'public' | 'authed' | 'allowed_users'; access_policy: 'public' | 'authed' | 'allowed_users';
gpu_selector?: { gpu_selector?: {
gpu_ids: string[]; gpu_ids: string[];
gpus_per_replica?: number;
}; };
worker_selector?: object; worker_selector?: object;
} }
@@ -63,11 +64,13 @@ export interface FormData {
gpu_ids?: string[]; gpu_ids?: string[];
gpu_type?: string; gpu_type?: string;
gpu_count?: number; gpu_count?: number;
gpus_per_replica?: number;
}; };
placement_strategy?: string; placement_strategy?: string;
cpu_offloading?: boolean; cpu_offloading?: boolean;
worker_selector?: object; worker_selector?: object;
scheduleType?: string; scheduleType?: string;
gpusCountType?: string;
name: string; name: string;
replicas: number; replicas: number;
description: string; description: string;
@@ -191,6 +194,7 @@ export interface CatalogSpec {
worker_selector: Record<string, any>; worker_selector: Record<string, any>;
gpu_selector: { gpu_selector: {
gpu_ids: string[]; gpu_ids: string[];
gpus_per_replica: number;
}; };
backend: string; backend: string;
backend_version: string; backend_version: string;
@@ -220,6 +224,7 @@ export interface EvaluateSpec {
worker_selector?: Record<string, any>; worker_selector?: Record<string, any>;
gpu_selector?: { gpu_selector?: {
gpu_ids: string[]; gpu_ids: string[];
gpus_per_replica: number;
}; };
backend?: string; backend?: string;
backend_version?: string; backend_version?: string;
+15 -2
View File
@@ -1,4 +1,5 @@
import _ from 'lodash'; import _ from 'lodash';
import { gpusCountTypeMap } from '.';
import { backendOptionsMap } from '../config/backend-parameters'; import { backendOptionsMap } from '../config/backend-parameters';
import { FormData } from './types'; 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) => { export const generateGPUIds = (data: FormData) => {
const gpu_ids = _.get(data, 'gpu_selector.gpu_ids', []); const gpu_ids = _.get(data, 'gpu_selector.gpu_ids', []);
console.log('generateGPUIds', 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 { return {
gpu_selector: { 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 { Form } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React, { forwardRef, useImperativeHandle } from 'react'; import React, { forwardRef, useImperativeHandle } from 'react';
import { excludeFields, ScheduleValueMap } from '../config'; import { excludeFields, gpusCountTypeMap, ScheduleValueMap } from '../config';
import { backendOptionsMap } from '../config/backend-parameters'; import { backendOptionsMap } from '../config/backend-parameters';
import { FormContext } from '../config/form-context'; import { FormContext } from '../config/form-context';
import { import {
@@ -120,7 +120,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
data.categories = data.categories ? [data.categories] : []; data.categories = data.categories ? [data.categories] : [];
const gpuSelector = generateGPUIds(data); const gpuSelector = generateGPUIds(data);
const allValues = { const allValues = {
..._.omit(data, ['scheduleType']), ..._.omit(data, ['scheduleType', 'gpusCountType']),
...gpuSelector ...gpuSelector
}; };
@@ -250,6 +250,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
placement_strategy: 'spread', placement_strategy: 'spread',
cpu_offloading: true, cpu_offloading: true,
scheduleType: ScheduleValueMap.Auto, scheduleType: ScheduleValueMap.Auto,
gpusCountType: gpusCountTypeMap.Auto,
categories: null, categories: null,
restart_on_error: true, restart_on_error: true,
distributed_inference_across_workers: 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 SealCascader from '@/components/seal-form/seal-cascader';
import SealSelect from '@/components/seal-form/seal-select'; import SealSelect from '@/components/seal-form/seal-select';
import TooltipList from '@/components/tooltip-list'; import TooltipList from '@/components/tooltip-list';
@@ -6,9 +7,9 @@ import { useIntl } from '@umijs/max';
import { Form } from 'antd'; import { Form } from 'antd';
import React from 'react'; import React from 'react';
import GPUCard from '../components/gpu-card'; import GPUCard from '../components/gpu-card';
import { scheduleList, ScheduleValueMap } from '../config'; import { gpusCountTypeMap, scheduleList, ScheduleValueMap } from '../config';
import { backendOptionsMap } from '../config/backend-parameters'; import { backendOptionsMap } from '../config/backend-parameters';
import { useCatalogFormContext, useFormContext } from '../config/form-context'; import { useFormContext } from '../config/form-context';
const scheduleTypeTips = [ 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 ScheduleTypeForm: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const { onValuesChange, gpuOptions } = useFormContext(); const { onValuesChange, gpuOptions } = useFormContext();
const { onQuantizationChange } = useCatalogFormContext();
const { getRuleMessage } = useAppUtils(); const { getRuleMessage } = useAppUtils();
const form = Form.useFormInstance(); const form = Form.useFormInstance();
const scheduleType = Form.useWatch('scheduleType', form); 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) => { const handleScheduleTypeChange = (value: string) => {
if (value === ScheduleValueMap.Auto) { if (value === ScheduleValueMap.Auto) {
@@ -41,8 +60,11 @@ const ScheduleTypeForm: React.FC = () => {
} }
}; };
const handleOnQuantizationChange = (val: any) => { const handleGpusCountTypeChange = (val: string) => {
onQuantizationChange?.(val); if (val === 'custom') {
form.setFieldValue(['gpu_selector', 'gpus_per_replica'], 2);
}
onValuesChange?.({}, form.getFieldsValue());
}; };
const handleBeforeGpuSelectorChange = (gpuIds: any[]) => {}; const handleBeforeGpuSelectorChange = (gpuIds: any[]) => {};
@@ -52,6 +74,23 @@ const ScheduleTypeForm: React.FC = () => {
onValuesChange?.({}, form.getFieldsValue()); 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 ( return (
<> <>
<Form.Item name="scheduleType"> <Form.Item name="scheduleType">
@@ -115,6 +154,43 @@ const ScheduleTypeForm: React.FC = () => {
onChange={handleGpuSelectorChange} onChange={handleGpuSelectorChange}
></SealCascader> ></SealCascader>
</Form.Item> </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 { useAtom } from 'jotai';
import { useState } from 'react'; import { useState } from 'react';
import { queryGPUList } from '../apis'; import { queryGPUList } from '../apis';
import { ScheduleValueMap } from '../config'; import { gpusCountTypeMap, ScheduleValueMap } from '../config';
import { GPUListItem, ListItem } from '../config/types'; import { GPUListItem, ListItem } from '../config/types';
type EmptyObject = Record<never, never>; 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 generateFormValues = (data: ListItem, gpuOptions: any[]) => {
const formData = { const formData = {
...data, ...data,
categories: data?.categories?.length ? data.categories[0] : null, categories: data?.categories?.length ? data.categories[0] : null,
scheduleType: data?.gpu_selector scheduleType: data?.gpu_selector
? ScheduleValueMap.Manual ? ScheduleValueMap.Manual
: ScheduleValueMap.Auto : ScheduleValueMap.Auto,
gpusCountType: data?.gpu_selector?.gpus_per_replica
? gpusCountTypeMap.Custom
: gpusCountTypeMap.Auto
}; };
return formData; return formData;
}; };