fix: update onceMaxRequest field

This commit is contained in:
jialin
2026-05-28 15:51:51 +08:00
committed by jialin
parent ef61134b8b
commit b590aa746b
10 changed files with 44 additions and 42 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ export default {
'menu.playground.text2images': 'Image', 'menu.playground.text2images': 'Image',
'menu.playground.video': 'Video', 'menu.playground.video': 'Video',
'menu.compare': 'Compare', 'menu.compare': 'Compare',
'menu.models': 'Models', 'menu.models': 'Model Service',
'menu.models.modelList': 'Deploy & Manage', 'menu.models.modelList': 'Deploy & Manage',
'menu.models.modelCatalog': 'Catalog', 'menu.models.modelCatalog': 'Catalog',
'menu.models.catalog': 'Model Catalog', 'menu.models.catalog': 'Model Catalog',
+1 -1
View File
@@ -8,7 +8,7 @@ export default {
'menu.playground.text2images': '画像生成', 'menu.playground.text2images': '画像生成',
'menu.playground.video': '動画', 'menu.playground.video': '動画',
'menu.compare': '比較', 'menu.compare': '比較',
'menu.models': 'モデル', 'menu.models': 'モデルサービス',
'menu.models.modelList': 'デプロイと管理', 'menu.models.modelList': 'デプロイと管理',
'menu.models.modelCatalog': 'カタログ', 'menu.models.modelCatalog': 'カタログ',
'menu.models.catalog': 'モデルカタログ', 'menu.models.catalog': 'モデルカタログ',
+1 -1
View File
@@ -8,7 +8,7 @@ export default {
'menu.playground.text2images': 'Генерация изображений', 'menu.playground.text2images': 'Генерация изображений',
'menu.playground.video': 'Видео', 'menu.playground.video': 'Видео',
'menu.compare': 'Сравнение', 'menu.compare': 'Сравнение',
'menu.models': 'Модели', 'menu.models': 'Сервисы моделей',
'menu.models.modelList': 'Развертывание и управление', 'menu.models.modelList': 'Развертывание и управление',
'menu.models.modelCatalog': 'Каталог', 'menu.models.modelCatalog': 'Каталог',
'menu.models.catalog': 'Каталог моделей', 'menu.models.catalog': 'Каталог моделей',
+1 -1
View File
@@ -8,7 +8,7 @@ export default {
'menu.playground.text2images': 'Görsel', 'menu.playground.text2images': 'Görsel',
'menu.playground.video': 'Video', 'menu.playground.video': 'Video',
'menu.compare': 'Karşılaştır', 'menu.compare': 'Karşılaştır',
'menu.models': 'Modeller', 'menu.models': 'Model Hizmetleri',
'menu.models.modelList': 'Dağıt ve Yönet', 'menu.models.modelList': 'Dağıt ve Yönet',
'menu.models.modelCatalog': 'Katalog', 'menu.models.modelCatalog': 'Katalog',
'menu.models.catalog': 'Model Kataloğu', 'menu.models.catalog': 'Model Kataloğu',
+1 -1
View File
@@ -8,7 +8,7 @@ export default {
'menu.playground.text2images': '图像', 'menu.playground.text2images': '图像',
'menu.playground.video': '视频', 'menu.playground.video': '视频',
'menu.compare': '多模型对比', 'menu.compare': '多模型对比',
'menu.models': '模型', 'menu.models': '模型服务',
'menu.models.modelList': '部署与管理', 'menu.models.modelList': '部署与管理',
'menu.models.modelCatalog': '模型库', 'menu.models.modelCatalog': '模型库',
'menu.models.deployment': '部署', 'menu.models.deployment': '部署',
@@ -86,8 +86,8 @@ const useClusterColumns = (
// `clusterDetail.linkableName`. Without a plugin we render the // `clusterDetail.linkableName`. Without a plugin we render the
// name as plain text (matches the pre-restore behaviour); with one // name as plain text (matches the pre-restore behaviour); with one
// we use Typography.Link wired to the parent's `onCellClick`. // we use Typography.Link wired to the parent's `onCellClick`.
const nameLinkable: boolean = !!getGPUStackPlugin()?.clusterDetail const nameLinkable: boolean =
?.linkableName; !!getGPUStackPlugin()?.clusterDetail?.linkableName;
const setActionsItems = (row: ClusterListItem) => { const setActionsItems = (row: ClusterListItem) => {
return clusterActionList.filter((item) => { return clusterActionList.filter((item) => {
@@ -112,9 +112,7 @@ const useClusterColumns = (
<> <>
<AutoTooltip ghost title={text}> <AutoTooltip ghost title={text}>
{nameLinkable ? ( {nameLinkable ? (
<Typography.Link <Typography.Link onClick={() => onCellClick?.(record, 'name')}>
onClick={() => onCellClick?.(record, 'name')}
>
{record.name} {record.name}
</Typography.Link> </Typography.Link>
) : ( ) : (
@@ -83,13 +83,11 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({ item }) => {
const specData = item.spec || {}; const specData = item.spec || {};
const acceleratable = specData.acceleratable; const acceleratable = specData.acceleratable;
const manufacturerKey = specData.manufacturer || (acceleratable ? '' : 'cpu'); const manufacturer = acceleratable ? specData.manufacturer || '' : 'cpu';
const manufacturer = manufacturerKey?.toUpperCase(); const manufacturerColor = manufactureColorMap[manufacturer] ?? 'purple';
const manufacturerColor =
(manufacturerKey && manufactureColorMap[manufacturerKey]) ?? 'purple';
// resource remaining status // resource once-max-request status
const remainingData = item.status?.remaining || {}; const onceMaxRequestData = item.status?.onceMaxRequest || {};
const renderName = () => { const renderName = () => {
const displayName = specData.acceleratable const displayName = specData.acceleratable
@@ -113,7 +111,7 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({ item }) => {
}} }}
> >
<ThemeTag color={manufacturerColor} disabled={false}> <ThemeTag color={manufacturerColor} disabled={false}>
{manufacturer} {manufacturer?.toUpperCase()}
</ThemeTag> </ThemeTag>
</span> </span>
)} )}
@@ -157,14 +155,14 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({ item }) => {
showDot={false} showDot={false}
icon="icon-ram-02" icon="icon-ram-02"
label={intl.formatMessage({ id: 'gpuservice.instance.ram' })} label={intl.formatMessage({ id: 'gpuservice.instance.ram' })}
value={toDisplayUnit(convertKiToGi(remainingData.ram)) ?? '-'} value={toDisplayUnit(convertKiToGi(onceMaxRequestData.ram)) ?? '-'}
></MetaItem> ></MetaItem>
<MetaItem <MetaItem
show={!!remainingData.cpu} show={!!onceMaxRequestData.cpu}
showDot={true} showDot={true}
icon="icon-cpu" icon="icon-cpu"
label="CPU" label="CPU"
value={remainingData.cpu ?? '-'} value={onceMaxRequestData.cpu ?? '-'}
></MetaItem> ></MetaItem>
</span> </span>
</Meta> </Meta>
@@ -183,20 +183,20 @@ const parseQuantity = (value?: string | null): number => {
}; };
// Returns the slider max for the accelerator count: the largest // Returns the slider max for the accelerator count: the largest
// tier.onceMaxRequest across all acceleratorTiers (not from candidates). // tier.onceMaxRequest.accelerator across all acceleratorTiers (not from candidates).
export const getAcceleratorMax = ( export const getAcceleratorMax = (
tiers?: { onceMaxRequest: string }[] | null tiers?: { onceMaxRequest: { accelerator?: string } }[] | null
) => { ) => {
if (!tiers?.length) return 0; if (!tiers?.length) return 0;
return tiers.reduce((acc, tier) => { return tiers.reduce((acc, tier) => {
const n = parseQuantity(tier.onceMaxRequest); const n = parseQuantity(tier.onceMaxRequest?.accelerator);
return n > acc ? n : acc; return n > acc ? n : acc;
}, 0); }, 0);
}; };
// Picks the candidate (cluster + type name) that should fulfill a requested // Picks the candidate (cluster + type name) that should fulfill a requested
// accelerator count: the first candidate of the smallest tier whose // accelerator count: the first candidate of the smallest tier whose
// onceMaxRequest is >= the requested count and whose cpu/ram/localStorage // onceMaxRequest.accelerator is >= the requested count and whose cpu/ram/localStorage
// remaining are all > 0. // remaining are all > 0.
export const pickCandidateForAccelerator = < export const pickCandidateForAccelerator = <
C extends { C extends {
@@ -208,7 +208,10 @@ export const pickCandidateForAccelerator = <
} }
>( >(
tiers: tiers:
| { onceMaxRequest: string; candidates?: C[] | null }[] | {
onceMaxRequest: { accelerator?: string };
candidates?: C[] | null;
}[]
| undefined | undefined
| null, | null,
count: number count: number
@@ -221,12 +224,14 @@ export const pickCandidateForAccelerator = <
parseQuantity(c.localStorage?.remaining) > 0; parseQuantity(c.localStorage?.remaining) > 0;
const sorted = [...tiers].sort( const sorted = [...tiers].sort(
(a, b) => parseQuantity(a.onceMaxRequest) - parseQuantity(b.onceMaxRequest) (a, b) =>
parseQuantity(a.onceMaxRequest?.accelerator) -
parseQuantity(b.onceMaxRequest?.accelerator)
); );
// count === 0 ? parseQuantity(tier.onceMaxRequest) > count; this is CPU-only case. // count === 0 ? parseQuantity(tier.onceMaxRequest.accelerator) > count; this is CPU-only case.
for (const tier of sorted) { for (const tier of sorted) {
const fits = parseQuantity(tier.onceMaxRequest) >= count; const fits = parseQuantity(tier.onceMaxRequest?.accelerator) >= count;
if (!fits) continue; if (!fits) continue;
const candidate = tier.candidates?.find(hasResources); const candidate = tier.candidates?.find(hasResources);
if (candidate) return candidate; if (candidate) return candidate;
+10 -11
View File
@@ -28,9 +28,6 @@ export interface FormData {
cpu?: string; cpu?: string;
ram?: string; ram?: string;
localStorage?: string; localStorage?: string;
// Stored in the form as a number so NumberSelection's strict
// equality picks up the active item. Serialized to a string at the
// API boundary in handleFinish.
accelerator?: number | string; accelerator?: number | string;
}; };
volume: { volume: {
@@ -101,12 +98,19 @@ export interface InstanceTypeCandidate {
localStorage: InstanceTypeResource; localStorage: InstanceTypeResource;
} }
export interface InstanceTypeTierOnceMaxRequestResource {
accelerator?: string;
cpu: string;
ram: string;
localStorage: string;
}
export interface InstanceTypeTier { export interface InstanceTypeTier {
onceMaxRequest: string; onceMaxRequest: InstanceTypeTierOnceMaxRequestResource;
candidates?: InstanceTypeCandidate[] | null; candidates?: InstanceTypeCandidate[] | null;
} }
export interface InstanceTypeRemainingResource { export interface InstanceTypeOnceMaxRequestResource {
accelerator?: string | null; accelerator?: string | null;
cpu: string; cpu: string;
ram: string; ram: string;
@@ -125,7 +129,7 @@ export interface InstanceTypeSpec {
} }
export interface InstanceTypeStatus { export interface InstanceTypeStatus {
remaining: InstanceTypeRemainingResource; onceMaxRequest: InstanceTypeOnceMaxRequestResource;
acceleratorTiers?: InstanceTypeTier[] | null; acceleratorTiers?: InstanceTypeTier[] | null;
} }
@@ -137,11 +141,6 @@ export interface InstanceTypeItem {
status: InstanceTypeStatus; status: InstanceTypeStatus;
} }
// =========== Events / logs (placeholder) ===========
// The /v2/gpu-instances API does not yet expose log/event endpoints. These
// shapes are retained as minimal placeholders so disabled UI continues to
// compile until backend support lands.
export interface InstanceEventItem { export interface InstanceEventItem {
type?: string; type?: string;
reason?: string; reason?: string;
@@ -25,7 +25,6 @@ import {
useState useState
} from 'react'; } from 'react';
import { FormData as PublicKeyFormData } from '../../public-keys/config/types'; import { FormData as PublicKeyFormData } from '../../public-keys/config/types';
import useCreateSshkey from '../../public-keys/services/use-create-sshkey';
import useQuerySshkeys from '../../public-keys/services/use-query-sshkeys'; import useQuerySshkeys from '../../public-keys/services/use-query-sshkeys';
import { DefaultImagePullPolicy } from '../../templates/config'; import { DefaultImagePullPolicy } from '../../templates/config';
import TemplateBasicForm, { import TemplateBasicForm, {
@@ -109,7 +108,11 @@ const requiredFields = {
}, },
[TABKeysMap.STORAGE]: { [TABKeysMap.STORAGE]: {
sort: 4, sort: 4,
fields: ['spec.volume.persistent.name', 'spec.volume.ephemeral.capacity'] fields: [
'spec.volume.persistent.name',
'spec.volume.ephemeral.capacity',
'spec.sshPublicKeys'
]
} }
}; };
@@ -132,7 +135,6 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
realAction === PageAction.CREATE ? PageAction.CREATE : action; realAction === PageAction.CREATE ? PageAction.CREATE : action;
const sshEnabled = Form.useWatch('enable_ssh', form); const sshEnabled = Form.useWatch('enable_ssh', form);
const { sshkeyOptions, fetchData: fetchSSHData } = useQuerySshkeys(); const { sshkeyOptions, fetchData: fetchSSHData } = useQuerySshkeys();
const { fetchData: createSshkey } = useCreateSshkey();
const [sshOverlayOpen, setSshOverlayOpen] = useState(false); const [sshOverlayOpen, setSshOverlayOpen] = useState(false);
const { getScrollElementScrollableHeight } = useWrapperContext(); const { getScrollElementScrollableHeight } = useWrapperContext();
const { const {