= ({
}
>
<>
-
- Configuration
-
+
+ {intl.formatMessage({ id: 'common.title.config' })}
+
>
diff --git a/src/pages/gpu-service/instances/components/instance-type-item.tsx b/src/pages/gpu-service/instances/components/instance-type-item.tsx
index bc2de5b9..9b96eef2 100644
--- a/src/pages/gpu-service/instances/components/instance-type-item.tsx
+++ b/src/pages/gpu-service/instances/components/instance-type-item.tsx
@@ -115,16 +115,38 @@ const InstanceTypeItem: React.FC
= ({
{convertKiToGi(item.spec?.memory) ?? '-'}
-
-
-
-
-
- {intl.formatMessage({ id: 'gpuservice.instance.stock' })}
+ {item.spec?.sliced && (
+ <>
+
+
+
+
+
+ {intl.formatMessage({
+ id: 'gpuservice.instance.sliced'
+ })}
+
+ {item.spec?.sliced ?? '-'}
+
- {item.status?.accelerator?.remaining ?? '-'}
-
-
+ >
+ )}
+ {item.status?.accelerator?.remaining && (
+ <>
+
+
+
+
+
+ {intl.formatMessage({
+ id: 'gpuservice.instance.stock'
+ })}
+
+ {item.status?.accelerator?.remaining ?? '-'}
+
+
+ >
+ )}
)}
diff --git a/src/pages/gpu-service/instances/config/index.ts b/src/pages/gpu-service/instances/config/index.ts
index 6a119caa..9d3d1b38 100644
--- a/src/pages/gpu-service/instances/config/index.ts
+++ b/src/pages/gpu-service/instances/config/index.ts
@@ -130,6 +130,10 @@ export const transformInstanceType = (
const status = item.status;
return {
...item,
+ spec: {
+ ...item.spec,
+ manufacturer: !item.spec?.acceleratable ? 'cpu' : item.spec.manufacturer
+ },
status: {
...status,
accelerator: transformInstanceTypeResource(
diff --git a/src/pages/gpu-service/instances/forms/index.tsx b/src/pages/gpu-service/instances/forms/index.tsx
index d6fbd169..2435521c 100644
--- a/src/pages/gpu-service/instances/forms/index.tsx
+++ b/src/pages/gpu-service/instances/forms/index.tsx
@@ -20,8 +20,10 @@ import {
} from 'react';
import useGetSshkey from '../../public-keys/services/use-get-sshkey';
import { DefaultImagePullPolicy } from '../../templates/config';
-import TemplateBasicForm from '../../templates/forms/basic';
-import { FormData, InstancePort, ListItem } from '../config/types';
+import TemplateBasicForm, {
+ BasicResourceMax
+} from '../../templates/forms/basic';
+import { FormData, InstanceTypeItem, ListItem } from '../config/types';
import Basic from './basic';
import InstanceTypeFormItem from './instance-type';
import StorageVolume from './storage-volume';
@@ -39,9 +41,18 @@ interface InstanceFormProps {
action: PageActionType;
currentData?: ListItem | null;
namespace?: string;
+ instanceTypeList?: InstanceTypeItem[];
onFinish: (values: FormData) => Promise;
}
+const parseQuantityToNumber = (value?: string): number | null => {
+ if (!value) return null;
+ const match = /^(-?\d+(?:\.\d+)?)/.exec(String(value));
+ if (!match) return null;
+ const num = Number(match[1]);
+ return Number.isFinite(num) && num > 0 ? num : null;
+};
+
const TABKeysMap = {
BASIC: 'basic',
INSTANCE_TYPE: 'instanceType',
@@ -75,6 +86,7 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
currentData,
open,
namespace = 'default',
+ instanceTypeList = [],
onFinish
} = props;
const intl = useIntl();
@@ -108,7 +120,7 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
{
value: TABKeysMap.BASIC,
label: intl.formatMessage({
- id: 'gpuservice.instance.section.basic'
+ id: 'common.title.basicInfo'
}),
icon: ,
field: 'name'
@@ -139,14 +151,31 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
[intl]
);
- const ports = (Form.useWatch(['spec', 'ports'], form) ||
- []) as InstancePort[];
+ const ports = Form.useWatch(['spec', 'ports'], form) || [];
const hasSshPort = useMemo(
() =>
ports.some((p) => Number(p?.port) === SSH_PORT && p.protocol === 'TCP'),
[ports]
);
+ const selectedTypeName = Form.useWatch(['spec', 'type'], form) as
+ | string
+ | undefined;
+
+ const onceMaxRequest = useMemo(() => {
+ const selected = instanceTypeList.find(
+ (item) => (item.metadata?.name || '') === selectedTypeName
+ );
+ const status = selected?.status;
+ return {
+ cpu: parseQuantityToNumber(status?.cpu?.onceMaxRequest),
+ memory: parseQuantityToNumber(status?.ram?.onceMaxRequest),
+ localStorage: parseQuantityToNumber(
+ status?.localStorage?.onceMaxRequest
+ )
+ };
+ }, [instanceTypeList, selectedTypeName]);
+
const onTargetChange = (key: string) => {
scrollTabsRef.current?.handleTargetChange(key);
};
@@ -277,7 +306,12 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
id: 'gpuservice.instance.section.template'
}),
forceRender: true,
- children:
+ children: (
+
+ )
},
{
key: TABKeysMap.STORAGE,
diff --git a/src/pages/gpu-service/instances/forms/instance-type.tsx b/src/pages/gpu-service/instances/forms/instance-type.tsx
index 6a0d60e5..fbc3943a 100644
--- a/src/pages/gpu-service/instances/forms/instance-type.tsx
+++ b/src/pages/gpu-service/instances/forms/instance-type.tsx
@@ -1,6 +1,6 @@
import { InputNumber as CInputNumber } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
-import { Form } from 'antd';
+import { Flex, Form } from 'antd';
import { useEffect, useMemo } from 'react';
import styled from 'styled-components';
import InstanceTypeItem from '../components/instance-type-item';
@@ -138,7 +138,19 @@ const InstanceTypeFormItem = () => {
min={1}
max={maxGpuCount}
precision={0}
- label={intl.formatMessage({ id: 'gpuservice.instance.gpuCount' })}
+ label={
+
+ {intl.formatMessage({ id: 'gpuservice.instance.gpuCount' })}
+
+ (
+ {intl.formatMessage(
+ { id: 'common.max' },
+ { count: maxGpuCount }
+ )}
+ )
+
+
+ }
required
/>
diff --git a/src/pages/gpu-service/instances/forms/template-selector.tsx b/src/pages/gpu-service/instances/forms/template-selector.tsx
index 6d3e5a1b..704cbcb8 100644
--- a/src/pages/gpu-service/instances/forms/template-selector.tsx
+++ b/src/pages/gpu-service/instances/forms/template-selector.tsx
@@ -33,7 +33,7 @@ const TemplateContent = styled.div`
}
.value {
- color: var(--ant-color-text);
+ color: var(--ant-color-text-secondary);
}
.icon {
diff --git a/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx b/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx
index 979eec5c..01db6711 100644
--- a/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx
+++ b/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx
@@ -134,7 +134,7 @@ const useInstancesColumns = ({
backgroundColor: 'var(--ant-color-fill-tertiary)'
}}
>
- {entry.port}
+ {entry.port}
diff --git a/src/pages/gpu-service/storage/apis/index.ts b/src/pages/gpu-service/storage/apis/index.ts
index 514903c7..401d5025 100644
--- a/src/pages/gpu-service/storage/apis/index.ts
+++ b/src/pages/gpu-service/storage/apis/index.ts
@@ -116,7 +116,10 @@ export async function queryStorageClass(
{
method: 'GET',
params: omitPathParams(params),
- cancelToken: options?.token
+ cancelToken: options?.token,
+ headers: {
+ skipErrorHandler: 'true'
+ }
}
);
}
diff --git a/src/pages/gpu-service/storage/forms/basic.tsx b/src/pages/gpu-service/storage/forms/basic.tsx
index af8cf42f..676d76a4 100644
--- a/src/pages/gpu-service/storage/forms/basic.tsx
+++ b/src/pages/gpu-service/storage/forms/basic.tsx
@@ -28,10 +28,7 @@ const Basic = ({ open }: { open: boolean }) => {
rules={[
{
required: true,
- message: getRuleMessage(
- 'input',
- intl.formatMessage({ id: 'common.table.name' })
- )
+ message: getRuleMessage('input', 'common.table.name')
}
]}
>
@@ -47,10 +44,7 @@ const Basic = ({ open }: { open: boolean }) => {
rules={[
{
required: true,
- message: getRuleMessage(
- 'select',
- intl.formatMessage({ id: 'gpuservice.storage.type' })
- )
+ message: getRuleMessage('select', 'gpuservice.storage.type')
}
]}
>
@@ -72,10 +66,7 @@ const Basic = ({ open }: { open: boolean }) => {
rules={[
{
required: true,
- message: getRuleMessage(
- 'input',
- intl.formatMessage({ id: 'gpuservice.storage.capacity' })
- )
+ message: getRuleMessage('input', 'gpuservice.storage.capacity')
}
]}
>
diff --git a/src/pages/gpu-service/storage/hooks/use-storage-columns.tsx b/src/pages/gpu-service/storage/hooks/use-storage-columns.tsx
index 7146ecff..2701aee4 100644
--- a/src/pages/gpu-service/storage/hooks/use-storage-columns.tsx
+++ b/src/pages/gpu-service/storage/hooks/use-storage-columns.tsx
@@ -52,7 +52,7 @@ const useStorageColumns = ({
dataIndex: ['spec', 'capacity'],
key: 'capacity',
sorter: false,
- render: (value: string) => value ?? '-'
+ render: (value: string) => (value ? value.replace(/Gi$/, 'GB') : '-')
},
{
title: intl.formatMessage({ id: 'common.table.status' }),
diff --git a/src/pages/gpu-service/storage/services/use-query-storage-class.ts b/src/pages/gpu-service/storage/services/use-query-storage-class.ts
index 1c63f4f4..94fc5132 100644
--- a/src/pages/gpu-service/storage/services/use-query-storage-class.ts
+++ b/src/pages/gpu-service/storage/services/use-query-storage-class.ts
@@ -21,8 +21,8 @@ export default function useQueryStorageClass() {
>({
key: 'storageClass',
fetchList,
- getLabel: (item) => item.metadata.name,
- getValue: (item) => item.metadata.name
+ getLabel: (item) => item?.metadata.name,
+ getValue: (item) => item?.metadata.name
});
return {
diff --git a/src/pages/gpu-service/templates/components/template-card.tsx b/src/pages/gpu-service/templates/components/template-card.tsx
index a7e58aeb..32da7994 100644
--- a/src/pages/gpu-service/templates/components/template-card.tsx
+++ b/src/pages/gpu-service/templates/components/template-card.tsx
@@ -7,7 +7,7 @@ import mooreLogo from '@/assets/logo/moore-logo.png';
import nvidiaLogo from '@/assets/logo/nvidia.png';
import theadLogoEN from '@/assets/logo/t-head-en.png';
import theadLogoZH from '@/assets/logo/t-head-zh.png';
-import { GPUDriverMap } from '@/pages/resources/config/gpu-driver';
+import { manfacturerValueMap } from '@/pages/resources/config/gpu-driver';
import {
AutoTooltip,
DropdownActions,
@@ -73,7 +73,7 @@ const InfoItem = styled.div`
color: var(--ant-color-text-quaternary);
}
.value {
- color: var(--ant-color-text);
+ color: var(--ant-color-text-secondary);
}
`;
@@ -99,28 +99,28 @@ const TemplateCardItem: React.FC = ({ data, onSelect }) => {
const renderLogo = () => {
switch (data.manufacturer) {
- case GPUDriverMap.NVIDIA:
+ case manfacturerValueMap.NVIDIA:
return ;
- case GPUDriverMap.AMD:
+ case manfacturerValueMap.AMD:
return (
);
- case GPUDriverMap.ASCEND:
+ case manfacturerValueMap.ASCEND:
return ;
- case GPUDriverMap.HYGON:
+ case manfacturerValueMap.HYGON:
return ;
- case GPUDriverMap.METAX:
+ case manfacturerValueMap.METAX:
return ;
- case GPUDriverMap.MOORE_THREADS:
+ case manfacturerValueMap.MOORE_THREADS:
return ;
- case GPUDriverMap.ILUVATAR:
+ case manfacturerValueMap.ILUVATAR:
return ;
- case GPUDriverMap.CAMBRICON:
+ case manfacturerValueMap.CAMBRICON:
return ;
- case GPUDriverMap.THEAD:
+ case manfacturerValueMap.THEAD:
return (
= ({ page = 'template' }) => {
+interface BasicProps {
+ page?: 'template' | 'instance';
+ onceMaxRequest?: BasicResourceMax;
+}
+
+const Basic: React.FC = ({ page = 'template', onceMaxRequest }) => {
const { getRuleMessage } = useAppUtils();
const intl = useIntl();
@@ -30,11 +37,26 @@ const Basic: React.FC = ({ page = 'template' }) => {
() =>
Object.values(GPUsConfigs).map((item) => ({
label: item.label,
- value: item.value
+ value: item.gpuVendor
})),
[]
);
+ const renderMaxLabel = (
+ label: React.ReactNode,
+ max?: number | null
+ ): React.ReactNode => {
+ if (max == null) return label;
+ return (
+
+ {label}
+
+ ({intl.formatMessage({ id: 'common.max' }, { count: max })})
+
+
+ );
+ };
+
return (
<>
@@ -135,9 +157,11 @@ const Basic: React.FC = ({ page = 'template' }) => {
})}
>
@@ -149,7 +173,10 @@ const Basic: React.FC