diff --git a/src/locales/en-US/common.ts b/src/locales/en-US/common.ts
index 07c35d43..6624b319 100644
--- a/src/locales/en-US/common.ts
+++ b/src/locales/en-US/common.ts
@@ -222,6 +222,12 @@ export default {
'common.title.delete.confirm': 'Confirm deletion',
'common.title.stop.confirm': 'Confirm stop',
'common.title.start.confirm': 'Confirm start',
+ 'common.title.activate.confirm': 'Confirm activate',
+ 'common.title.deactivate.confirm': 'Confirm deactivate',
+ 'common.activate.single.confirm':
+ 'Are you sure you want to activate? \n {name}',
+ 'common.deactivate.single.confirm':
+ 'Are you sure you want to deactivate? \n {name}',
'common.title.recreate.confirm': 'Confirm recreate',
'common.button.addLabel': 'Add Label',
'common.button.addSelector': 'Add Selector',
diff --git a/src/locales/en-US/gpuservice.ts b/src/locales/en-US/gpuservice.ts
index bdbfa9f5..93227b2b 100644
--- a/src/locales/en-US/gpuservice.ts
+++ b/src/locales/en-US/gpuservice.ts
@@ -122,6 +122,9 @@ export default {
'gpuservice.instance.mode.whole': 'Full GPU',
'gpuservice.instance.mode.sliced': 'By Ratio',
'gpuservice.instance.slice.memoryPercentage': 'VRAM Percentage (%)',
+ 'gpuservice.instance.slice.coresPercentage': 'Compute Percentage (%)',
+ 'gpuservice.instance.slice.cores.min':
+ 'The compute ratio must be no less than the VRAM ratio ({count}%)',
'gpuservice.instance.slice.fullCores': '100% Compute',
'gpuservice.instance.slice.percentage.required':
'Please select or enter a percentage',
diff --git a/src/locales/ja-JP/common.ts b/src/locales/ja-JP/common.ts
index 25af4459..53128334 100644
--- a/src/locales/ja-JP/common.ts
+++ b/src/locales/ja-JP/common.ts
@@ -221,6 +221,12 @@ export default {
'common.title.delete.confirm': '削除を確認',
'common.title.stop.confirm': '停止を確認',
'common.title.start.confirm': '開始を確認',
+ 'common.title.activate.confirm': '有効化を確認',
+ 'common.title.deactivate.confirm': '無効化を確認',
+ 'common.activate.single.confirm':
+ '{name} を有効化してもよろしいですか?',
+ 'common.deactivate.single.confirm':
+ '{name} を無効化してもよろしいですか?',
'common.title.recreate.confirm': '再作成を確認',
'common.button.addLabel': 'ラベルを追加',
'common.button.addSelector': 'セレクターを追加',
diff --git a/src/locales/ja-JP/gpuservice.ts b/src/locales/ja-JP/gpuservice.ts
index 35dd6d5e..22928c42 100644
--- a/src/locales/ja-JP/gpuservice.ts
+++ b/src/locales/ja-JP/gpuservice.ts
@@ -121,6 +121,9 @@ export default {
'gpuservice.instance.mode.whole': 'GPU 全体',
'gpuservice.instance.mode.sliced': '比率で',
'gpuservice.instance.slice.memoryPercentage': 'VRAM の割合(%)',
+ 'gpuservice.instance.slice.coresPercentage': '演算能力の割合(%)',
+ 'gpuservice.instance.slice.cores.min':
+ '演算能力の割合は VRAM の割合({count}%)以上である必要があります',
'gpuservice.instance.slice.fullCores': '100% コンピュート',
'gpuservice.instance.slice.percentage.required':
'パーセンテージを選択または入力してください',
diff --git a/src/locales/ru-RU/common.ts b/src/locales/ru-RU/common.ts
index 8df477ba..4ae3a2da 100644
--- a/src/locales/ru-RU/common.ts
+++ b/src/locales/ru-RU/common.ts
@@ -219,6 +219,12 @@ export default {
'common.title.delete.confirm': 'Подтверждение удаления',
'common.title.stop.confirm': 'Подтверждение остановки',
'common.title.start.confirm': 'Подтверждение запуска',
+ 'common.title.activate.confirm': 'Подтверждение активации',
+ 'common.title.deactivate.confirm': 'Подтверждение деактивации',
+ 'common.activate.single.confirm':
+ 'Вы уверены, что хотите активировать {name}?',
+ 'common.deactivate.single.confirm':
+ 'Вы уверены, что хотите деактивировать {name}?',
'common.title.recreate.confirm': 'Подтверждение пересоздания',
'common.button.addLabel': 'Добавить метку',
'common.button.addSelector': 'Добавить селектор',
diff --git a/src/locales/ru-RU/gpuservice.ts b/src/locales/ru-RU/gpuservice.ts
index 3600069d..97f39577 100644
--- a/src/locales/ru-RU/gpuservice.ts
+++ b/src/locales/ru-RU/gpuservice.ts
@@ -120,6 +120,9 @@ export default {
'gpuservice.instance.mode.whole': 'Весь GPU',
'gpuservice.instance.mode.sliced': 'По доле',
'gpuservice.instance.slice.memoryPercentage': 'Доля VRAM (%)',
+ 'gpuservice.instance.slice.coresPercentage': 'Доля вычислений (%)',
+ 'gpuservice.instance.slice.cores.min':
+ 'Доля вычислений должна быть не меньше доли VRAM ({count}%)',
'gpuservice.instance.slice.fullCores': '100% вычислений',
'gpuservice.instance.slice.percentage.required':
'Выберите или введите процент',
diff --git a/src/locales/tr-TR/common.ts b/src/locales/tr-TR/common.ts
index 62de4f36..ec191845 100644
--- a/src/locales/tr-TR/common.ts
+++ b/src/locales/tr-TR/common.ts
@@ -224,6 +224,12 @@ export default {
'common.title.delete.confirm': 'Silme onayı',
'common.title.stop.confirm': 'Durdurma onayı',
'common.title.start.confirm': 'Başlatma onayı',
+ 'common.title.activate.confirm': 'Etkinleştirme onayı',
+ 'common.title.deactivate.confirm': 'Devre dışı bırakma onayı',
+ 'common.activate.single.confirm':
+ 'Etkinleştirmek istediğinizden emin misiniz? \n {name}',
+ 'common.deactivate.single.confirm':
+ 'Devre dışı bırakmak istediğinizden emin misiniz? \n {name}',
'common.title.recreate.confirm': 'Yeniden oluşturma onayı',
'common.button.addLabel': 'Etiket Ekle',
'common.button.addSelector': 'Seçici Ekle',
diff --git a/src/locales/tr-TR/gpuservice.ts b/src/locales/tr-TR/gpuservice.ts
index 56dc1911..31115802 100644
--- a/src/locales/tr-TR/gpuservice.ts
+++ b/src/locales/tr-TR/gpuservice.ts
@@ -116,6 +116,9 @@ export default {
'gpuservice.instance.mode.whole': 'Tam GPU',
'gpuservice.instance.mode.sliced': 'Orana Göre',
'gpuservice.instance.slice.memoryPercentage': 'VRAM Yüzdesi (%)',
+ 'gpuservice.instance.slice.coresPercentage': 'İşlem Gücü Yüzdesi (%)',
+ 'gpuservice.instance.slice.cores.min':
+ 'İşlem gücü oranı VRAM oranından ({count}%) küçük olamaz',
'gpuservice.instance.slice.fullCores': '%100 İşlem Gücü',
'gpuservice.instance.slice.percentage.required':
'Lütfen bir yüzde seçin veya girin',
diff --git a/src/locales/zh-CN/common.ts b/src/locales/zh-CN/common.ts
index 44d26e0b..cca24501 100644
--- a/src/locales/zh-CN/common.ts
+++ b/src/locales/zh-CN/common.ts
@@ -212,6 +212,12 @@ export default {
'common.title.delete.confirm': '确认删除',
'common.title.stop.confirm': '确认停止',
'common.title.start.confirm': '确认启动',
+ 'common.title.activate.confirm': '确认启用',
+ 'common.title.deactivate.confirm': '确认停用',
+ 'common.activate.single.confirm':
+ '确定启用 {name}?',
+ 'common.deactivate.single.confirm':
+ '确定停用 {name}?',
'common.title.recreate.confirm': '确认重新创建',
'common.button.addLabel': '添加标签',
'common.button.addSelector': '添加选择器',
diff --git a/src/locales/zh-CN/gpuservice.ts b/src/locales/zh-CN/gpuservice.ts
index 56282404..631a7ca9 100644
--- a/src/locales/zh-CN/gpuservice.ts
+++ b/src/locales/zh-CN/gpuservice.ts
@@ -111,6 +111,8 @@ export default {
'gpuservice.instance.mode.whole': '整卡',
'gpuservice.instance.mode.sliced': '按比例',
'gpuservice.instance.slice.memoryPercentage': '显存占比(%)',
+ 'gpuservice.instance.slice.coresPercentage': '算力占比(%)',
+ 'gpuservice.instance.slice.cores.min': '算力占比需不小于显存占比 {count}%',
'gpuservice.instance.slice.fullCores': '100% 算力',
'gpuservice.instance.slice.percentage.required': '请选择或输入百分比',
'gpuservice.instance.slice.percentage.max': '比例需在 1% 到 {count}% 之间',
diff --git a/src/pages/gpu-service/instance-types/index.tsx b/src/pages/gpu-service/instance-types/index.tsx
index 8c0ecba9..edd933ca 100644
--- a/src/pages/gpu-service/instance-types/index.tsx
+++ b/src/pages/gpu-service/instance-types/index.tsx
@@ -39,7 +39,8 @@ const GPUServiceInstanceTypes: React.FC = () => {
const {
dataList,
loading: instanceTypesLoading,
- fetchInstanceTypes
+ fetchInstanceTypes,
+ startWatch
} = useQueryInstanceTypes();
const { fetchData: createInstanceType } = useCreateInstanceType();
const {
@@ -66,16 +67,18 @@ const GPUServiceInstanceTypes: React.FC = () => {
if (firstK8s?.id != null) {
setClusterId(firstK8s.id);
await fetchInstanceTypes(firstK8s.id);
+ startWatch(firstK8s.id);
}
setLoaded(true);
};
init();
}, []);
- const handleClusterChange = useMemoizedFn((value: number) => {
+ const handleClusterChange = useMemoizedFn(async (value: number) => {
setClusterId(value);
setKeyword('');
- fetchInstanceTypes(value);
+ await fetchInstanceTypes(value);
+ startWatch(value);
});
const handleRefresh = useMemoizedFn(() => {
@@ -125,18 +128,29 @@ const GPUServiceInstanceTypes: React.FC = () => {
});
const handleToggleActive = useMemoizedFn(
- async (record: ListItem, activate: boolean) => {
+ (record: ListItem, activate: boolean) => {
if (clusterId == null) return;
- try {
- const action = activate
- ? activateGPUInstanceType
- : deactivateGPUInstanceType;
- await action({ name: record.name, cluster_id: clusterId });
- message.success(intl.formatMessage({ id: 'common.message.success' }));
- fetchInstanceTypes(clusterId);
- } catch (error) {
- // handled by the request interceptor
- }
+ const action = activate
+ ? activateGPUInstanceType
+ : deactivateGPUInstanceType;
+ deleteModalRef.current?.show({
+ content: intl.formatMessage({ id: 'gpuservice.instanceType' }),
+ title: activate
+ ? 'common.title.activate.confirm'
+ : 'common.title.deactivate.confirm',
+ okText: activate
+ ? 'gpuservice.instanceType.activate'
+ : 'gpuservice.instanceType.deactivate',
+ operation: activate
+ ? 'common.activate.single.confirm'
+ : 'common.deactivate.single.confirm',
+ name: record.spec?.displayName || record.name,
+ async onOk() {
+ await action({ name: record.name, cluster_id: clusterId });
+ message.success(intl.formatMessage({ id: 'common.message.success' }));
+ fetchInstanceTypes(clusterId);
+ }
+ });
}
);
diff --git a/src/pages/gpu-service/instance-types/services/use-query-instance-types.ts b/src/pages/gpu-service/instance-types/services/use-query-instance-types.ts
index 45e7418e..847a268c 100644
--- a/src/pages/gpu-service/instance-types/services/use-query-instance-types.ts
+++ b/src/pages/gpu-service/instance-types/services/use-query-instance-types.ts
@@ -1,15 +1,49 @@
-import { createAxiosToken } from '@/hooks/use-chunk-request';
+import { WatchEventType } from '@/config';
+import useSetChunkRequest, {
+ createAxiosToken
+} from '@/hooks/use-chunk-request';
import { useRequest } from 'ahooks';
import { CancelTokenSource } from 'axios';
+import qs from 'query-string';
import { useEffect, useRef, useState } from 'react';
-import { queryGPUInstanceTypes } from '../apis';
+import { GPU_INSTANCE_TYPES_API, queryGPUInstanceTypes } from '../apis';
import { ListItem } from '../config/types';
+// Merge a batch of watch events into the current name-keyed list. Instance
+// types have no numeric id, so we upsert / remove by `name` rather than reuse
+// the shared id-based chunked-list helper.
+const mergeWatchEvents = (current: ListItem[], events: any[]) => {
+ let list = [...current];
+ events.forEach((event: any) => {
+ const collection: ListItem[] = event?.collection || [];
+ if (event?.type === WatchEventType.DELETE) {
+ const names = collection.map((item) => item.name);
+ list = list.filter((item) => !names.includes(item.name));
+ } else if (
+ event?.type === WatchEventType.CREATE ||
+ event?.type === WatchEventType.UPDATE
+ ) {
+ collection.forEach((item) => {
+ const index = list.findIndex((it) => it.name === item.name);
+ if (index > -1) {
+ list[index] = item;
+ } else {
+ list = [item, ...list];
+ }
+ });
+ }
+ });
+ return list;
+};
+
// Cluster-scoped instance types for the management list. Action-driven:
// call fetchInstanceTypes(clusterId) from the cluster picker / refresh, not
-// via an effect dependency.
+// via an effect dependency. startWatch(clusterId) opens a chunked watch that
+// keeps the list in sync with live create/update/delete events.
export default function useQueryInstanceTypes() {
const tokenRef = useRef(null);
+ const chunkRequestRef = useRef(null);
+ const { setChunkRequest } = useSetChunkRequest();
const [dataList, setDataList] = useState([]);
const {
@@ -43,10 +77,27 @@ export default function useQueryInstanceTypes() {
tokenRef.current?.cancel('CANCEL_PREVIOUS_REQUEST');
};
+ const stopWatch = () => {
+ chunkRequestRef.current?.current?.cancel?.();
+ };
+
+ const startWatch = (clusterId: number) => {
+ stopWatch();
+ chunkRequestRef.current = setChunkRequest({
+ url: `${GPU_INSTANCE_TYPES_API}?${qs.stringify({
+ cluster_id: clusterId
+ })}`,
+ handler: (events: any[]) => {
+ setDataList((pre) => mergeWatchEvents(pre, events));
+ }
+ });
+ };
+
useEffect(() => {
return () => {
cancel();
tokenRef.current?.cancel();
+ stopWatch();
};
}, []);
@@ -55,6 +106,8 @@ export default function useQueryInstanceTypes() {
loading,
fetchInstanceTypes,
cancelRequest,
+ startWatch,
+ stopWatch,
setDataList
};
}
diff --git a/src/pages/gpu-service/instances/forms/index.tsx b/src/pages/gpu-service/instances/forms/index.tsx
index 8386b0eb..af99f5d0 100644
--- a/src/pages/gpu-service/instances/forms/index.tsx
+++ b/src/pages/gpu-service/instances/forms/index.tsx
@@ -350,21 +350,39 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
} as any);
};
- // Single entry point for the sliced memory ratio: write the ratio (compute
- // stays pinned at 100%) and rescale CPU / RAM off it. Reused by the slider
- // onChange and by the sliced-mode defaults so both share one path.
+ // Single entry point for the sliced memory ratio: write the ratio and
+ // rescale CPU / RAM off it. The compute (cores) ratio must stay >= memory,
+ // so bump it up when memory overtakes it. Reused by the slider onChange.
const applySliceMemoryPercentage = (value: number) => {
+ const currentCores = _.toNumber(
+ form.getFieldValue([
+ 'spec',
+ 'resources',
+ 'acceleratorSlicedCoresPercentage'
+ ])
+ );
+ const coresPercentage = currentCores >= value ? currentCores : value;
form.setFieldsValue({
spec: {
resources: {
acceleratorSlicedMemoryPercentage: value,
- acceleratorSlicedCoresPercentage: 100
+ acceleratorSlicedCoresPercentage: coresPercentage
}
}
} as any);
applySlicedResourceScaling();
};
+ // Compute (cores) ratio — a GPU-slice-only parameter that rides along on
+ // submit. It does not scale CPU / RAM (those track the memory ratio), so
+ // just write the field.
+ const applySliceCoresPercentage = (value: number) => {
+ form.setFieldValue(
+ ['spec', 'resources', 'acceleratorSlicedCoresPercentage'],
+ value
+ );
+ };
+
const resolveAndApply = (
instanceType: InstanceTypeItem | undefined,
count: number,
@@ -436,14 +454,25 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
resolveAndApply(selectedInstanceType, count, false);
};
- // Seed the sliced-mode default ratio for an instance type: 10% but never
- // above the type's max sliceable ratio (status.onceMaxRequest
- // .acceleratorSliced). Shares applySliceMemoryPercentage with the slider.
+ // Seed the sliced-mode defaults for an instance type: memory ratio at 10%
+ // (never above the type's max sliceable ratio, status.onceMaxRequest
+ // .acceleratorSliced), and the cores ratio defaulting to the same value
+ // (cores >= memory). Set both together so a fresh selection doesn't carry a
+ // stale cores value from a previous type.
const applySlicedDefaults = (instanceType?: InstanceTypeItem) => {
const slicedMax =
_.toNumber(instanceType?.status?.onceMaxRequest?.acceleratorSliced) ||
0;
- applySliceMemoryPercentage(slicedMax ? Math.min(10, slicedMax) : 10);
+ const memoryPercentage = slicedMax ? Math.min(10, slicedMax) : 10;
+ form.setFieldsValue({
+ spec: {
+ resources: {
+ acceleratorSlicedMemoryPercentage: memoryPercentage,
+ acceleratorSlicedCoresPercentage: memoryPercentage
+ }
+ }
+ } as any);
+ applySlicedResourceScaling();
};
// Toggle between whole-card and sliced mode. Sliced fixes the accelerator
@@ -757,6 +786,7 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
sliceMode={sliceMode}
onSliceModeChange={handleSliceModeChange}
onSliceMemoryPercentageChange={applySliceMemoryPercentage}
+ onSliceCoresPercentageChange={applySliceCoresPercentage}
onGPUCountChange={handleAcceleratorChange}
/>
)
diff --git a/src/pages/gpu-service/instances/forms/instance-type.tsx b/src/pages/gpu-service/instances/forms/instance-type.tsx
index f715dc00..0eb88551 100644
--- a/src/pages/gpu-service/instances/forms/instance-type.tsx
+++ b/src/pages/gpu-service/instances/forms/instance-type.tsx
@@ -1,9 +1,9 @@
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import NumberSelection from '@/pages/_components/number-selection';
-import { InputNumber } from '@gpustack/core-ui';
+import { InputNumber, LabelInfo } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
-import { Flex, Form, Segmented } from 'antd';
+import { Divider, Flex, Form, Segmented } from 'antd';
import _ from 'lodash';
import { useContext, useMemo } from 'react';
import styled from 'styled-components';
@@ -76,6 +76,8 @@ interface InstanceTypeFormItemProps {
onSliceModeChange?: (mode: 'whole' | 'sliced') => void;
// Commit a new sliced memory ratio (writes the field + rescales CPU / RAM).
onSliceMemoryPercentageChange?: (value: number) => void;
+ // Commit a new sliced compute (cores) ratio (writes the field only).
+ onSliceCoresPercentageChange?: (value: number) => void;
onGPUCountChange?: (value: number) => void;
}
@@ -89,6 +91,7 @@ const InstanceTypeFormItem: React.FC = ({
sliceMode = 'whole',
onSliceModeChange,
onSliceMemoryPercentageChange,
+ onSliceCoresPercentageChange,
onGPUCountChange
}) => {
const intl = useIntl();
@@ -139,6 +142,21 @@ const InstanceTypeFormItem: React.FC = ({
onSliceMemoryPercentageChange?.(value);
};
+ // Compute (cores) percentage changed — forward the new value.
+ const handleCoresPercentageChange = (value: number) => {
+ onSliceCoresPercentageChange?.(value);
+ };
+
+ // The cores ratio must be >= the memory ratio, so ticks below the current
+ // memory percentage are disabled (min). Cores range is a fixed 10..100.
+ const slicedMemoryPercentage =
+ _.toNumber(
+ Form.useWatch(
+ ['spec', 'resources', 'acceleratorSlicedMemoryPercentage'],
+ form
+ )
+ ) || 1;
+
// Max selectable ratio in sliced mode: status.onceMaxRequest.acceleratorSliced
// (a percentage). Ticks above it stay visible but disabled.
const slicedMaxPercentage =
@@ -272,6 +290,13 @@ const InstanceTypeFormItem: React.FC = ({
)}
+ {showModeSwitch && (
+
+ )}
{!noAvailableTypes && (
key={isGPUType ? 'accelerator' : 'cpu'}
@@ -322,7 +347,6 @@ const InstanceTypeFormItem: React.FC = ({
max={maxComputeUnitCount}
step={1}
required
- labelExtra={sliceMode === 'whole' ? modeSegmented : undefined}
disabled={disabled || readonlyType}
label={`${intl.formatMessage({ id: 'common.max.count' }, { label: numberSelectionLabel.label })} (${intl.formatMessage(
{
@@ -335,66 +359,113 @@ const InstanceTypeFormItem: React.FC = ({
)}
{!noAvailableTypes && isSliced && (
-
- name={['spec', 'resources', 'acceleratorSlicedMemoryPercentage']}
- getValueProps={(value) => ({
- value: value != null ? _.toNumber(value) : undefined
- })}
- rules={[
- {
- required: true,
- validator: (_, value) => {
- const num = Number(value);
- if (value == null || value === '' || Number.isNaN(num)) {
- return Promise.reject(
- new Error(
- intl.formatMessage({
- id: 'gpuservice.instance.slice.percentage.required'
- })
- )
- );
- }
- if (num > slicedMaxPercentage || num <= 0) {
- return Promise.reject(
- new Error(
- intl.formatMessage(
- {
- id: 'gpuservice.instance.slice.percentage.max'
- },
- { count: slicedMaxPercentage }
- )
- )
- );
- }
- return Promise.resolve();
- }
- }
- ]}
- >
-
+
+ name={['spec', 'resources', 'acceleratorSlicedMemoryPercentage']}
+ getValueProps={(value) => ({
+ value: value != null ? _.toNumber(value) : undefined
})}
- />
-
- {/* Compute (cores) percentage is fixed at 100. Kept in the form via a
- hidden item so it rides along on submit. */}
-
- name={['spec', 'resources', 'acceleratorSlicedCoresPercentage']}
- hidden
- >
-
-
+ rules={[
+ {
+ required: true,
+ validator: (_, value) => {
+ const num = Number(value);
+ if (value == null || value === '' || Number.isNaN(num)) {
+ return Promise.reject(
+ new Error(
+ intl.formatMessage({
+ id: 'gpuservice.instance.slice.percentage.required'
+ })
+ )
+ );
+ }
+ if (num > slicedMaxPercentage || num <= 0) {
+ return Promise.reject(
+ new Error(
+ intl.formatMessage(
+ {
+ id: 'gpuservice.instance.slice.percentage.max'
+ },
+ { count: slicedMaxPercentage }
+ )
+ )
+ );
+ }
+ return Promise.resolve();
+ }
+ }
+ ]}
+ >
+
+
+ {/* Compute (cores) percentage. Fixed 10..100 ticks; ticks below the
+ chosen memory ratio are disabled (cores must be >= memory). */}
+
+ name={['spec', 'resources', 'acceleratorSlicedCoresPercentage']}
+ style={{ marginBottom: 0 }}
+ getValueProps={(value) => ({
+ value: value != null ? _.toNumber(value) : undefined
+ })}
+ rules={[
+ {
+ required: true,
+ validator: (_, value) => {
+ const num = Number(value);
+ if (value == null || value === '' || Number.isNaN(num)) {
+ return Promise.reject(
+ new Error(
+ intl.formatMessage({
+ id: 'gpuservice.instance.slice.percentage.required'
+ })
+ )
+ );
+ }
+ if (num < slicedMemoryPercentage || num > 100) {
+ return Promise.reject(
+ new Error(
+ intl.formatMessage(
+ { id: 'gpuservice.instance.slice.cores.min' },
+ { count: slicedMemoryPercentage }
+ )
+ )
+ );
+ }
+ return Promise.resolve();
+ }
+ }
+ ]}
+ >
+
+
+
)}
{/* A not-yet-re-typed edit renders a readonly card (no sliced UI), so