fix: parse quality data

This commit is contained in:
jialin
2026-05-31 22:23:32 +08:00
committed by jialin
parent 16a97772d3
commit 55028cf3a3
8 changed files with 214 additions and 122 deletions
@@ -268,6 +268,7 @@ const AddModal: React.FC<AddModalProps> = ({
await onOk({
...values
});
console.log('submit form values', values);
} finally {
setLoading(false);
}
@@ -1,4 +1,3 @@
import { ceilMilliToCore } from '@/pages/gpu-service/utils';
import { AutoTooltip, IconFont, ThemeTag } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { Flex } from 'antd';
@@ -62,7 +61,7 @@ interface InstanceTypeItemProps {
const MetaItem: React.FC<{
icon: string;
label?: string;
value?: string | null;
value?: string | null | number;
showDot?: boolean;
show?: boolean;
}> = ({ icon, label, value, showDot = true, show = true }) => {
@@ -91,15 +90,13 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({ item }) => {
const onceMaxRequestData = item.status?.onceMaxRequest || {};
// RAM resource
const ramRaw = acceleratable
? specData.unitResources?.ram
const ramUnit = acceleratable
? specData.unitResourcesParsed?.ram?.value
: onceMaxRequestData.ram;
console.log('InstanceTypeItem', item.name, 'ramRaw', ramRaw);
// CPU resource
const cpuRaw = acceleratable
? specData.unitResources?.cpu
const cpuUnitCores = acceleratable
? specData.unitResourcesParsed?.cpu?.cores
: onceMaxRequestData.cpu;
const renderName = () => {
@@ -159,7 +156,7 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({ item }) => {
},
{ count: '' }
)}
value={`${item.maxAccelerator || '-'}`}
value={`${item.maxAccelerator || 0}`}
/>
</>
)}
@@ -169,14 +166,14 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({ item }) => {
showDot={false}
icon="icon-ram-02"
label={intl.formatMessage({ id: 'gpuservice.instance.ram' })}
value={toDisplayUnit(convertKiToGi(ramRaw)) ?? '-'}
value={ramUnit ? `${ramUnit}GB` : '-'}
/>
<MetaItem
show={!!cpuRaw}
show={!!cpuUnitCores}
showDot={true}
icon="icon-cpu"
label="CPU"
value={ceilMilliToCore(cpuRaw) ?? '-'}
value={cpuUnitCores || '-'}
/>
</span>
</Meta>
@@ -41,10 +41,10 @@ export interface FormData {
}[];
volumeMount: string;
resources: {
cpu?: string;
ram?: string;
localStorage?: string;
accelerator?: number | string;
cpu: string | null | number;
ram: string | null | number;
localStorage: string | null | number;
accelerator: number | string | null;
};
volume: {
ephemeral?: {
@@ -162,6 +162,18 @@ export interface InstanceTypeSpec {
cpu: QuanityCPU;
ram: QuanityMemory;
};
unitResourcesParsed?: {
cpu: {
cores?: number;
unit: string;
num: number;
} | null;
ram: {
value: number;
unit: string;
num: number;
} | null;
};
}
export interface InstanceTypeStatus {
+76 -53
View File
@@ -1,10 +1,6 @@
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import {
ceilMilliToCore,
parseQuantityToGi,
parseQuantityToNumber
} from '@/pages/gpu-service/utils';
import { ceilMilliToCore, parseQuantityToGi } from '@/pages/gpu-service/utils';
import { PlusOutlined } from '@ant-design/icons';
import {
CheckboxField,
@@ -198,10 +194,52 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
localStorage: null
});
const buildResourcesData = (
instanceType: InstanceTypeItem | undefined,
options: {
count: number;
}
) => {
const unitResourcesParsed = instanceType?.spec?.unitResourcesParsed;
const acceleratable = instanceType?.spec?.acceleratable;
const { count = 0 } = options;
if (acceleratable) {
return {
accelerator: _.toString(count),
cpu: unitResourcesParsed?.cpu?.cores
? count * unitResourcesParsed?.cpu?.cores
: undefined,
ram: unitResourcesParsed?.ram?.value
? count * unitResourcesParsed?.ram?.value
: undefined
};
}
return {};
};
const buildResourcesDataForSubmit = (values: FormData) => {
const unitResourcesParsed = getUnitResources();
const accelerator = _.toNumber(values.spec?.resources?.accelerator) || 0;
const cpuNum = unitResourcesParsed?.cpu?.num;
const ramNum = unitResourcesParsed?.ram?.num;
return {
cpu:
accelerator > 0 && cpuNum
? `${accelerator * cpuNum}${unitResourcesParsed?.cpu?.unit || ''}`
: `${values.spec?.resources?.cpu}`,
ram:
accelerator > 0 && ramNum
? `${accelerator * ramNum}${unitResourcesParsed?.ram?.unit || ''}`
: values.spec?.resources?.ram
};
};
const resolveAndApply = (
instanceType: InstanceTypeItem | undefined,
count: number,
options: { writeAccelerator?: boolean } = {}
count: number
) => {
if (!instanceType) {
setSelectedInstanceType(undefined);
@@ -214,40 +252,22 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
);
setSelectedInstanceType(instanceType);
setOnceMaxRequest({
cpu: ceilMilliToCore(candidate?.cpu?.onceMaxRequest),
memory: parseQuantityToGi(candidate?.ram?.onceMaxRequest),
cpu: ceilMilliToCore(candidate?.cpu?.onceMaxRequest)?.cores,
memory: parseQuantityToGi(candidate?.ram?.onceMaxRequest)?.value,
localStorage: parseQuantityToGi(candidate?.localStorage?.onceMaxRequest)
?.value
});
const unitResources = instanceType.spec?.unitResources;
const unitCpu = parseQuantityToNumber(unitResources?.cpu);
const unitRam = parseQuantityToNumber(unitResources?.ram);
const acceleratable = instanceType.spec?.acceleratable;
const cpuCores =
unitCpu != null ? ceilMilliToCore(`${count * unitCpu}m`) : null;
const ramGi =
unitRam != null ? parseQuantityToGi(`${count * unitRam}Mi`) : null;
form.setFieldsValue({
clusterId: candidate?.cluster ? _.toNumber(candidate.cluster) : null,
spec: {
type: candidate?.name || '',
...(options.writeAccelerator
? { resources: { accelerator: _.toString(count) } }
: {}),
...(acceleratable
? {
resources: {
...(options.writeAccelerator
? { accelerator: _.toString(count) }
: {}),
...(cpuCores != null ? { cpu: cpuCores } : {}),
...(ramGi != null ? { ram: `${ramGi}Gi` } : {})
}
}
: {})
resources: {
...buildResourcesData(instanceType, {
count
})
}
} as any
});
};
@@ -293,8 +313,22 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
action === PageAction.VIEW ||
realAction === PageAction.CREATE
) {
console.log('currentData', currentData);
form.setFieldsValue({
...currentData,
spec: {
...currentData?.spec,
resources: {
...currentData?.spec?.resources,
...buildResourcesData(
JSON.parse(currentData?.description || '{}'),
{
count:
_.toNumber(currentData?.spec?.resources?.accelerator) || 0
}
)
}
},
enable_ssh: !!currentData?.spec?.sshPublicKeys?.length,
storageMode: detectMode(currentData?.spec?.volume)
});
@@ -302,13 +336,13 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
}, [action, currentData, form, open, realAction, instanceTypeList]);
const getUnitResources = () => {
if (selectedInstanceType?.spec?.unitResources) {
return selectedInstanceType.spec.unitResources;
if (selectedInstanceType?.spec?.unitResourcesParsed) {
return selectedInstanceType.spec.unitResourcesParsed;
}
try {
return (
JSON.parse(currentData?.description || '{}')?.spec?.unitResources ??
undefined
JSON.parse(currentData?.description || '{}')?.spec
?.unitResourcesParsed ?? undefined
);
} catch {
return undefined;
@@ -329,26 +363,15 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
});
}
const accelerator = _.toNumber(values.spec?.resources?.accelerator);
const submittedResources = { ...(values.spec?.resources ?? {}) };
if (accelerator > 0) {
const unitResources = getUnitResources();
const unitCpu = parseQuantityToNumber(unitResources?.cpu);
const unitRam = parseQuantityToNumber(unitResources?.ram);
if (unitCpu != null) {
submittedResources.cpu = `${accelerator * unitCpu}m`;
}
if (unitRam != null) {
submittedResources.ram = `${accelerator * unitRam}Mi`;
}
}
await onFinish({
..._.omit(values, ['enable_ssh']),
spec: {
...values.spec,
ports: submittedPorts,
resources: submittedResources
resources: {
...values.spec?.resources,
...buildResourcesDataForSubmit(values)
}
}
});
};
@@ -377,7 +400,7 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
getFieldsValue: () => form.getFieldsValue(),
applyInstanceType: (instanceType: InstanceTypeItem) => {
const count = instanceType.spec?.acceleratable ? 1 : 0;
resolveAndApply(instanceType, count, { writeAccelerator: true });
resolveAndApply(instanceType, count);
}
}));
@@ -8,7 +8,6 @@ import _ from 'lodash';
import { useMemo } from 'react';
import styled from 'styled-components';
import { BasicResourceMax } from '../../templates/forms/basic';
import { ceilMilliToCore, parseQuantityToGi } from '../../utils';
import InstanceTypeItem from '../components/instance-type-item';
import { getAcceleratorMax } from '../config';
import {
@@ -102,7 +101,7 @@ const InstanceTypeFormItem: React.FC<InstanceTypeFormItemProps> = ({
};
const renderMemoryLabel = (): React.ReactNode => {
if (isGPU) {
if (isGPU || action === PageAction.EDIT || !onceMaxRequest?.memory) {
return intl.formatMessage({ id: 'gpuservice.template.memory' });
}
@@ -235,40 +234,24 @@ const InstanceTypeFormItem: React.FC<InstanceTypeFormItemProps> = ({
<div style={{ flex: 1 }}>
<Form.Item<FormData>
name={['spec', 'resources', 'ram']}
normalize={(value) => (value ? `${value}Gi` : undefined)}
getValueProps={(value) => {
if (!value) return { value: '' };
const str = String(value);
if (/Gi$/.test(str)) return { value: str.replace(/Gi$/, '') };
if (/(Ki|Mi|Ti)$/.test(str)) {
return { value: parseQuantityToGi(str) ?? '' };
}
return { value: str };
}}
normalize={(value) => (value ? `${value}Gi` : null)}
getValueProps={(value) => ({
value: _.toString(value).replace(/Gi$/, '')
})}
>
<InputNumber
disabled={isGPU || disabled}
disabled={isGPU || disabled || action === PageAction.EDIT}
label={renderMemoryLabel()}
max={onceMaxRequest?.memory ?? undefined}
/>
</Form.Item>
</div>
<div style={{ flex: 1 }}>
<Form.Item<FormData>
name={['spec', 'resources', 'cpu']}
normalize={(value) => (value ? `${value}` : '')}
getValueProps={(value) => {
if (!value) return { value: '' };
const str = String(value);
return {
value: /m$/.test(str) ? (ceilMilliToCore(str) ?? '') : str
};
}}
>
<Form.Item<FormData> name={['spec', 'resources', 'cpu']}>
<InputNumber
label={renderMaxLabel('CPU', onceMaxRequest?.cpu)}
max={onceMaxRequest?.cpu ?? undefined}
disabled={disabled || isGPU}
disabled={disabled || isGPU || action === PageAction.EDIT}
/>
</Form.Item>
</div>
+1 -1
View File
@@ -88,7 +88,7 @@ const GPUService: React.FC = () => {
} = useViewEvents();
const {
fetchClusterList,
cancelRequest: cancelClusterRequest,
cancelRequest,
clusterList,
loading: clusterLoading
} = useQueryClusterList();
@@ -1,5 +1,6 @@
import { useQueryData } from '@gpustack/core-ui';
import React from 'react';
import { ceilMilliToCore, parseQuantityToGi } from '../../utils';
import { queryGPUServiceInstanceTypes } from '../apis';
import { getAcceleratorMax } from '../config';
import { InstanceTypeItem } from '../config/types';
@@ -44,13 +45,33 @@ export default function useQueryInstanceTypes() {
const list = (res?.items || []).map((item) => {
const remainingData = isAvailable(item);
const rawMax = item.status?.onceMaxRequest;
return {
...item,
spec: {
...item.spec,
unitResourcesParsed: {
cpu: ceilMilliToCore(item.spec?.unitResources?.cpu ?? null),
ram: parseQuantityToGi(item.spec?.unitResources?.ram ?? null)
}
},
status: {
...item.status,
onceMaxRequest: {
...rawMax,
cpu: rawMax?.cpu ? `${ceilMilliToCore(rawMax.cpu)?.cores}` : '',
ram: rawMax?.ram ? `${parseQuantityToGi(rawMax.ram)?.value}` : '',
localStorage: rawMax?.localStorage
? `${parseQuantityToGi(rawMax.localStorage)?.value}`
: ''
}
},
maxAccelerator: remainingData.maxAccelerator,
disabled: !remainingData.available
};
});
console.log('queryInstanceTypes', list);
setDataList(list);
return list;
};