From fe24f9b53d97543376a2f31950acf199ce09bff1 Mon Sep 17 00:00:00 2001 From: jialin Date: Sat, 4 Jul 2026 12:40:13 +0800 Subject: [PATCH] fix(gpu-service): address PR review on sliced scaling and theme logo - Scale sliced CPU in millicores and RAM in MiB for precise, k8s-valid fractional allocations; show rounded decimals in the disabled inputs - Disable the by-ratio option when the type has no sliced capacity - Use the dark logo map under the dark theme in template cards --- .../gpu-service/instances/forms/index.tsx | 31 ++++++++++++------- .../instances/forms/instance-type.tsx | 18 ++++++----- .../templates/components/template-card.tsx | 2 +- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/pages/gpu-service/instances/forms/index.tsx b/src/pages/gpu-service/instances/forms/index.tsx index 25c63572..a22c8e80 100644 --- a/src/pages/gpu-service/instances/forms/index.tsx +++ b/src/pages/gpu-service/instances/forms/index.tsx @@ -282,30 +282,36 @@ const GPUServiceInstanceForm: React.FC = forwardRef( const fallbackCpu = resources.cpu; - // Sliced mode: scale a single card's unit resources by the chosen - // percentage (floored) — both CPU and RAM. Whole/CPU mode: multiply the - // unit by the count. const percentage = _.toNumber( resources.acceleratorSlicedMemoryPercentage ); const sliced = isGPUType && percentage > 0; const wholeFactor = isGPUType ? accelerator : cpuCount; - const scale = (num: number, unit: string) => - sliced - ? `${Math.max(1, _.floor((num * percentage) / 100))}${unit}` - : `${wholeFactor * num}${unit}`; + // Sliced mode: scale a single card's unit resources by the chosen + // percentage. Scale CPU in millicores and RAM in MiB so fractional + // slices stay precise and k8s-valid (integers) — e.g. 10% of a 4-core / + // 16Gi card → 400m / 1638Mi, not a rounded-up 1 core / 1Gi. + if (sliced && unitResourcesParsed) { + const cpuCores = unitResourcesParsed.cpu?.cores ?? 0; + const ramValue = unitResourcesParsed.ram?.value ?? 0; + return { + cpu: `${Math.max(1, _.floor((cpuCores * 1000 * percentage) / 100))}m`, + ram: `${Math.max(1, _.floor((ramValue * 1024 * percentage) / 100))}Mi` + }; + } + // Whole / CPU mode: multiply the unit by the count. return { cpu: cpuNum - ? scale(cpuNum, unitResourcesParsed?.cpu?.unit || '') + ? `${wholeFactor * cpuNum}${unitResourcesParsed?.cpu?.unit || ''}` : // Don't stringify an unset value — `${undefined}` becomes the // literal "undefined", which fails k8s quantity validation. fallbackCpu ? `${fallbackCpu}` : undefined, ram: ramNum - ? scale(ramNum, unitResourcesParsed?.ram?.unit || '') + ? `${wholeFactor * ramNum}${unitResourcesParsed?.ram?.unit || ''}` : resources.ram }; }; @@ -329,13 +335,16 @@ const GPUServiceInstanceForm: React.FC = forwardRef( form.setFieldsValue({ spec: { resources: { + // Display the precise (rounded) fractional values — the inputs are + // disabled, so decimals are fine and match the submitted + // millicore / MiB allocation better than a floored integer. cpu: cpuCores != null && percentage > 0 - ? Math.max(1, _.floor((cpuCores * percentage) / 100)) + ? _.round((cpuCores * percentage) / 100, 2) : null, ram: ramValue != null && percentage > 0 - ? Math.max(1, _.floor((ramValue * percentage) / 100)) + ? _.round((ramValue * percentage) / 100, 2) : null } } diff --git a/src/pages/gpu-service/instances/forms/instance-type.tsx b/src/pages/gpu-service/instances/forms/instance-type.tsx index 12f7f570..159097ea 100644 --- a/src/pages/gpu-service/instances/forms/instance-type.tsx +++ b/src/pages/gpu-service/instances/forms/instance-type.tsx @@ -134,6 +134,13 @@ const InstanceTypeFormItem: React.FC = ({ onSliceMemoryPercentageChange?.(value); }; + // Max selectable ratio in sliced mode: status.onceMaxRequest.acceleratorSliced + // (a percentage). Ticks above it stay visible but disabled. + const slicedMaxPercentage = + _.toNumber( + selectedInstanceType?.status?.onceMaxRequest?.acceleratorSliced + ) || 0; + const modeSegmented = showModeSwitch ? ( = ({ }, { label: intl.formatMessage({ id: 'gpuservice.instance.mode.sliced' }), - value: 'sliced' + value: 'sliced', + // No sliced capacity → keep the option visible but unselectable. + disabled: slicedMaxPercentage <= 0 } ]} /> @@ -157,13 +166,6 @@ const InstanceTypeFormItem: React.FC = ({ const isSliced = showModeSwitch && sliceMode === 'sliced'; - // Max selectable ratio in sliced mode: status.onceMaxRequest.acceleratorSliced - // (a percentage). Ticks above it stay visible but disabled. - const slicedMaxPercentage = - _.toNumber( - selectedInstanceType?.status?.onceMaxRequest?.acceleratorSliced - ) || 0; - // When the max ratio is below 10%, switch the ticks to a finer 1..10 scale // so small slices are still selectable; otherwise use the 10..100 scale. const sliceTicks = diff --git a/src/pages/gpu-service/templates/components/template-card.tsx b/src/pages/gpu-service/templates/components/template-card.tsx index d0203c4b..c475a891 100644 --- a/src/pages/gpu-service/templates/components/template-card.tsx +++ b/src/pages/gpu-service/templates/components/template-card.tsx @@ -56,7 +56,7 @@ const matchImageLogo = ( isDark: boolean ): { logo: string; type: string } | null => { if (!image) return null; - const logoMap = imageLogoLightMap; + const logoMap = isDark ? imageLogoDarkMap : imageLogoLightMap; const lower = image.toLowerCase(); let matched: keyof typeof logoMap | null = null; let earliest = Infinity;