diff --git a/src/locales/en-US/common.ts b/src/locales/en-US/common.ts index a0e6b03e..60ca329e 100644 --- a/src/locales/en-US/common.ts +++ b/src/locales/en-US/common.ts @@ -283,5 +283,5 @@ export default { 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.image.limit.width': 'Image width must be {width}.', 'common.image.limit.height': 'Image height must be {height}.', - 'common.max': 'Max {count}' + 'common.remaining': 'Remaining {count}' }; diff --git a/src/locales/en-US/gpuservice.ts b/src/locales/en-US/gpuservice.ts index 9f657000..e2521f3d 100644 --- a/src/locales/en-US/gpuservice.ts +++ b/src/locales/en-US/gpuservice.ts @@ -55,7 +55,9 @@ export default { 'gpuservice.instance.gpuCount': 'GPU Count', 'gpuservice.instance.gpuCount.required': 'Please enter the GPU count', 'gpuservice.instance.gpuCount.max': - 'The current instance type supports at most {count} GPU(s)', + 'Please select at most {count} GPU card(s)', + 'gpuservice.instance.gpuCount.min': + 'Please select at least {count} GPU card(s)', 'gpuservice.instance.stock': 'Stock', 'gpuservice.instance.sliced': 'Sliced', 'gpuservice.instance.memory': 'Memory', diff --git a/src/locales/ja-JP/common.ts b/src/locales/ja-JP/common.ts index c9c98ea0..cddd73fa 100644 --- a/src/locales/ja-JP/common.ts +++ b/src/locales/ja-JP/common.ts @@ -283,7 +283,7 @@ export default { 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.image.limit.width': 'Image width must be {width}.', 'common.image.limit.height': 'Image height must be {height}.', - 'common.max': '最大 {count}' + 'common.remaining': '残り {count}' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== diff --git a/src/locales/ja-JP/gpuservice.ts b/src/locales/ja-JP/gpuservice.ts index c189dd55..0ecf0f35 100644 --- a/src/locales/ja-JP/gpuservice.ts +++ b/src/locales/ja-JP/gpuservice.ts @@ -53,7 +53,9 @@ export default { 'gpuservice.instance.gpuCount': 'GPU 数', 'gpuservice.instance.gpuCount.required': 'GPU 数を入力してください', 'gpuservice.instance.gpuCount.max': - '現在のインスタンスタイプは最大 {count} 個の GPU をサポートします', + '最大 {count} 枚の GPU カードを選択してください', + 'gpuservice.instance.gpuCount.min': + '少なくとも {count} 枚の GPU カードを選択してください', 'gpuservice.instance.stock': '在庫', 'gpuservice.instance.sliced': '分割', 'gpuservice.instance.memory': 'Memory', diff --git a/src/locales/ru-RU/common.ts b/src/locales/ru-RU/common.ts index f0220935..38a5f1d5 100644 --- a/src/locales/ru-RU/common.ts +++ b/src/locales/ru-RU/common.ts @@ -282,7 +282,7 @@ export default { 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.image.limit.width': 'Image width must be {width}.', 'common.image.limit.height': 'Image height must be {height}.', - 'common.max': 'Макс. {count}' + 'common.remaining': 'Остаток {count}' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== diff --git a/src/locales/ru-RU/gpuservice.ts b/src/locales/ru-RU/gpuservice.ts index 8d6e820f..4d0606e8 100644 --- a/src/locales/ru-RU/gpuservice.ts +++ b/src/locales/ru-RU/gpuservice.ts @@ -55,8 +55,8 @@ export default { 'gpuservice.instance.type.required': 'Выберите тип экземпляра', 'gpuservice.instance.gpuCount': 'Количество GPU', 'gpuservice.instance.gpuCount.required': 'Введите количество GPU', - 'gpuservice.instance.gpuCount.max': - 'Текущий тип экземпляра поддерживает максимум {count} GPU', + 'gpuservice.instance.gpuCount.max': 'Выберите максимум {count} GPU-карт', + 'gpuservice.instance.gpuCount.min': 'Выберите минимум {count} GPU-карт', 'gpuservice.instance.stock': 'Остаток', 'gpuservice.instance.sliced': 'Разделено', 'gpuservice.instance.memory': 'Память', diff --git a/src/locales/tr-TR/common.ts b/src/locales/tr-TR/common.ts index 5ecea64d..82ec9b71 100644 --- a/src/locales/tr-TR/common.ts +++ b/src/locales/tr-TR/common.ts @@ -286,5 +286,5 @@ export default { 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.image.limit.width': 'Image width must be {width}.', 'common.image.limit.height': 'Image height must be {height}.', - 'common.max': 'Maks. {count}' + 'common.remaining': 'Kalan {count}' }; diff --git a/src/locales/tr-TR/gpuservice.ts b/src/locales/tr-TR/gpuservice.ts index 72b96300..fa1d2c47 100644 --- a/src/locales/tr-TR/gpuservice.ts +++ b/src/locales/tr-TR/gpuservice.ts @@ -54,8 +54,8 @@ export default { 'gpuservice.instance.type.required': 'Lütfen bir örnek türü seçin', 'gpuservice.instance.gpuCount': 'GPU Sayısı', 'gpuservice.instance.gpuCount.required': 'Lütfen GPU sayısını girin', - 'gpuservice.instance.gpuCount.max': - 'Mevcut örnek türü en fazla {count} GPU destekler', + 'gpuservice.instance.gpuCount.max': 'En fazla {count} GPU kartı seçin', + 'gpuservice.instance.gpuCount.min': 'En az {count} GPU kartı seçin', 'gpuservice.instance.stock': 'Stok', 'gpuservice.instance.sliced': 'Bölünmüş', 'gpuservice.instance.memory': 'Bellek', diff --git a/src/locales/zh-CN/common.ts b/src/locales/zh-CN/common.ts index e5330fcf..38b54753 100644 --- a/src/locales/zh-CN/common.ts +++ b/src/locales/zh-CN/common.ts @@ -275,5 +275,5 @@ export default { 'common.file.format.limit': '文件格式不正确,仅支持{formats}。', 'common.image.limit.width': '图片宽度须为{width}。', 'common.image.limit.height': '图片高度须为{height}。', - 'common.max': '最大 {count}' + 'common.remaining': '剩余 {count}' }; diff --git a/src/locales/zh-CN/gpuservice.ts b/src/locales/zh-CN/gpuservice.ts index eed4bc9e..f29301ba 100644 --- a/src/locales/zh-CN/gpuservice.ts +++ b/src/locales/zh-CN/gpuservice.ts @@ -50,7 +50,8 @@ export default { 'gpuservice.instance.type.required': '请选择实例类型', 'gpuservice.instance.gpuCount': 'GPU 数量', 'gpuservice.instance.gpuCount.required': '请输入 GPU 数量', - 'gpuservice.instance.gpuCount.max': '当前实例类型最多支持 {count} 个 GPU', + 'gpuservice.instance.gpuCount.max': '最多选择 {count} 张卡', + 'gpuservice.instance.gpuCount.min': '至少选择 {count} 张卡', 'gpuservice.instance.stock': '库存', 'gpuservice.instance.sliced': '切分', 'gpuservice.instance.memory': '显存', 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 886cbc0e..5ee9d637 100644 --- a/src/pages/gpu-service/instances/components/instance-type-item.tsx +++ b/src/pages/gpu-service/instances/components/instance-type-item.tsx @@ -9,6 +9,9 @@ import { } from '../config'; import { InstanceTypeItem as InstanceTypeItemModel } from '../config/types'; +const toDisplayUnit = (value?: string) => + value ? value.replace(/Gi$/, 'GB').replace(/Ti$/, 'TB') : value; + const Title = styled.div` display: flex; align-items: center; @@ -112,7 +115,9 @@ const InstanceTypeItem: React.FC = ({ {intl.formatMessage({ id: 'gpuservice.instance.memory' })} - {convertKiToGi(item.spec?.memory) ?? '-'} + + {toDisplayUnit(convertKiToGi(item.spec?.memory)) ?? '-'} + {item.spec?.sliced && ( @@ -157,7 +162,7 @@ const InstanceTypeItem: React.FC = ({ {intl.formatMessage({ id: 'gpuservice.instance.ram' })} - {item.status?.ram?.capacity ?? '-'} + {toDisplayUnit(item.status?.ram?.capacity) ?? '-'} diff --git a/src/pages/gpu-service/instances/components/view-logs-modal.tsx b/src/pages/gpu-service/instances/components/view-logs-modal.tsx index e6075cd3..751b44de 100644 --- a/src/pages/gpu-service/instances/components/view-logs-modal.tsx +++ b/src/pages/gpu-service/instances/components/view-logs-modal.tsx @@ -1,7 +1,8 @@ import { LogsViewer } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; +import { useMemoizedFn } from 'ahooks'; import { Modal } from 'antd'; -import React, { useCallback, useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; type ViewModalProps = { open: boolean; @@ -12,16 +13,17 @@ type ViewModalProps = { }; const ViewLogsModal: React.FC = (props) => { + console.log('ViewLogsModal props:', props); const intl = useIntl(); - const { open, onCancel, url } = props || {}; + const { open, onCancel, url, tail } = props || {}; const logsViewerRef = React.useRef(null); const requestRef = React.useRef(null); const contentRef = React.useRef(null); - const handleCancel = useCallback(() => { + const handleCancel = useMemoizedFn(() => { logsViewerRef.current?.abort(); onCancel(); - }, [onCancel]); + }); useEffect(() => { const handleKeyDown = (e: any) => { @@ -59,6 +61,15 @@ const ViewLogsModal: React.FC = (props) => { }; }, [url, open]); + const params = useMemo(() => { + return { + watchable: false, + watch: false, + tailLines: 1000, + follow: true + }; + }, []); + return ( = (props) => { ref={logsViewerRef} diffHeight={78} url={url} - tail={undefined} + tail={1000} enableScorllLoad={true} isDownloading={false} - params={{ - watchable: false, - watch: false, - tailLines: 1000, - follow: true - }} + params={params} > diff --git a/src/pages/gpu-service/instances/forms/index.tsx b/src/pages/gpu-service/instances/forms/index.tsx index a40f68fb..0b9e433a 100644 --- a/src/pages/gpu-service/instances/forms/index.tsx +++ b/src/pages/gpu-service/instances/forms/index.tsx @@ -11,7 +11,6 @@ import { } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; -import _ from 'lodash'; import { forwardRef, useEffect, @@ -51,7 +50,7 @@ const parseQuantityToNumber = (value?: string): number | null => { const match = /^(-?\d+(?:\.\d+)?)/.exec(String(value)); if (!match) return null; const num = Number(match[1]); - return Number.isFinite(num) && num > 0 ? _.floor(num, 1) : null; + return Number.isFinite(num) && num > 0 ? num : null; }; const TABKeysMap = { @@ -224,14 +223,13 @@ const GPUServiceInstanceForm: React.FC = forwardRef( }, [action, currentData, form, open, namespace]); const handleFinish = async (values: InstanceFormValues) => { - console.log('values=========', values); - // await onFinish({ - // ...values, - // spec: { - // ...values.spec, - // sshPublicKey: { name: sshKeyData?.name } - // } - // }); + await onFinish({ + ...values, + spec: { + ...values.spec, + sshPublicKey: { name: sshKeyData?.name } + } + }); }; useImperativeHandle(ref, () => ({ diff --git a/src/pages/gpu-service/instances/forms/instance-type.tsx b/src/pages/gpu-service/instances/forms/instance-type.tsx index 17070707..fbb1f2ad 100644 --- a/src/pages/gpu-service/instances/forms/instance-type.tsx +++ b/src/pages/gpu-service/instances/forms/instance-type.tsx @@ -116,7 +116,7 @@ const InstanceTypeFormItem: React.FC = ({ - {selectedInstanceType?.spec?.acceleratable && ( + {!selectedInstanceType?.spec?.acceleratable && ( = ({ ) ); } + if (value < 1) { + return Promise.reject( + new Error( + intl.formatMessage({ + id: 'gpuservice.instance.gpuCount.min' + }) + ) + ); + } return Promise.resolve(); } } 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 698fc990..8a650b16 100644 --- a/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx +++ b/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx @@ -6,11 +6,11 @@ import { StatusTag } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; -import { Button, Flex } from 'antd'; +import { Button } from 'antd'; import type { ColumnsType } from 'antd/lib/table'; import dayjs from 'dayjs'; import _ from 'lodash'; -import { useMemo } from 'react'; +import { Fragment, useMemo } from 'react'; import { InstanceStatusLabelMap, rowActionList, status } from '../config'; import { ListItem } from '../config/types'; @@ -35,6 +35,7 @@ const getConnectEntries = (record: ListItem): ConnectEntry[] => { const ip = record.status?.hostIPs?.[0]?.ip; const ports = record.status?.ports || []; const hasSshKey = !!record.spec?.sshPublicKey?.name; + const configPorts = record.spec?.ports || []; if (!ip) { return []; @@ -54,7 +55,9 @@ const getConnectEntries = (record: ListItem): ConnectEntry[] => { } : { type: 'http', - name: p.name || 'HTTP', + name: + configPorts.find((port) => port.port === p.port)?.name || + _.toUpper(p.protocol), key: `http-${p.nodePort}`, port: p.port, protocol: _.toUpper(p.protocol), @@ -100,16 +103,32 @@ const useInstancesColumns = ({ } return ( - - {entries.map((entry) => - entry.type === 'ssh' ? ( - - - SSH - +
+ {entries.map((entry) => ( + + + {entry.type === 'ssh' + ? 'SSH' + : entry.name || entry.protocol} + + {entry.type === 'ssh' ? ( - - ) : ( - - - {entry.name || entry.protocol} - + ) : ( - - ) - )} - + )} + + ))} +
); } }, diff --git a/src/pages/gpu-service/templates/forms/basic.tsx b/src/pages/gpu-service/templates/forms/basic.tsx index 927bf014..692555c3 100644 --- a/src/pages/gpu-service/templates/forms/basic.tsx +++ b/src/pages/gpu-service/templates/forms/basic.tsx @@ -57,7 +57,7 @@ const Basic: React.FC = ({ {label} - ({intl.formatMessage({ id: 'common.max' }, { count: max })}) + ({intl.formatMessage({ id: 'common.remaining' }, { count: max })}) );