77 addNumberWithUnits ,
88 compareNumberWithUnits ,
99 convertToBinaryUnit ,
10+ InputSizeUnit ,
1011} from '../../helper' ;
1112import { useSuspendedBackendaiClient } from '../../hooks' ;
1213import { useResourceSlots } from '../../hooks/backendai' ;
@@ -62,6 +63,58 @@ export const isMinOversMaxValue = (min: number, max: number) => {
6263 return min >= max ;
6364} ;
6465
66+ /**
67+ * Returns true when the given accelerator slot name represents a unified
68+ * memory architecture, where the accelerator memory and the host memory
69+ * share a single physical pool. Identified by a `.unified` suffix on the
70+ * slot name (e.g. `cuda.unified`).
71+ */
72+ export const isUnifiedAcceleratorSlot = ( slotName ?: string | null ) : boolean => {
73+ return ! ! slotName && _ . endsWith ( slotName , '.unified' ) ;
74+ } ;
75+
76+ /**
77+ * Maps a slot's `display_unit` (e.g. `GiB`, `MiB`, `TiB`) to the
78+ * corresponding `InputSizeUnit` accepted by `convertToBinaryUnit`. Falls
79+ * back to `'g'` (GiB) when the display unit is missing or not a recognized
80+ * binary size unit.
81+ */
82+ export const displayUnitToInputSizeUnit = (
83+ displayUnit ?: string | null ,
84+ ) : InputSizeUnit => {
85+ const first = ( _ . first ( displayUnit ?? '' ) ?? '' ) . toLowerCase ( ) ;
86+ const validUnits : ReadonlyArray < InputSizeUnit > = [
87+ 'k' ,
88+ 'm' ,
89+ 'g' ,
90+ 't' ,
91+ 'p' ,
92+ 'e' ,
93+ ] ;
94+ const match = validUnits . find ( ( u ) => u === first ) ;
95+ return match ?? 'g' ;
96+ } ;
97+
98+ /**
99+ * Returns the accelerator-field number derived from the host memory value
100+ * for a unified-memory slot. Reads the slot's `display_unit` from
101+ * `mergedResourceSlots` and converts `mem` to that unit. Falls back to
102+ * `'g'` (GiB) when the display unit is unavailable.
103+ */
104+ export const getUnifiedAcceleratorValueFromMem = (
105+ mem : string | number | undefined ,
106+ slotName : string | undefined ,
107+ mergedResourceSlots :
108+ | Record < string , { display_unit ?: string } | undefined >
109+ | undefined ,
110+ ) : number => {
111+ const displayUnit = slotName
112+ ? mergedResourceSlots ?. [ slotName ] ?. display_unit
113+ : undefined ;
114+ const targetUnit = displayUnitToInputSizeUnit ( displayUnit ) ;
115+ return convertToBinaryUnit ( mem || '0g' , targetUnit ) ?. number ?? 0 ;
116+ } ;
117+
65118export interface ResourceAllocationFormValue {
66119 resource : {
67120 cpu : number ;
@@ -267,6 +320,28 @@ const ResourceAllocationFormItems: React.FC<
267320 }
268321 } ;
269322
323+ // Centralized helper that keeps the `resource.accelerator` field in sync
324+ // with `resource.mem` whenever the active accelerator slot is a unified
325+ // one. Call this from every code path that mutates `resource.mem` or
326+ // `resource.acceleratorType` so submit-time form state is consistent.
327+ const syncUnifiedAcceleratorIfNeeded = useEventNotStable ( ( ) => {
328+ const activeAcceleratorType = form . getFieldValue ( [
329+ 'resource' ,
330+ 'acceleratorType' ,
331+ ] ) ;
332+ if ( ! isUnifiedAcceleratorSlot ( activeAcceleratorType ) ) {
333+ return ;
334+ }
335+ form . setFieldValue (
336+ [ 'resource' , 'accelerator' ] ,
337+ getUnifiedAcceleratorValueFromMem (
338+ form . getFieldValue ( [ 'resource' , 'mem' ] ) ,
339+ activeAcceleratorType ,
340+ mergedResourceSlots ,
341+ ) ,
342+ ) ;
343+ } ) ;
344+
270345 const ensureValidAcceleratorType = useEventNotStable ( ( ) => {
271346 const currentAcceleratorType = form . getFieldValue ( [
272347 'resource' ,
@@ -283,6 +358,9 @@ const ResourceAllocationFormItems: React.FC<
283358 acceleratorType : nextAcceleratorType || currentAcceleratorType ,
284359 } ,
285360 } ) ;
361+ // The accelerator type may have changed; mirror `mem` into the
362+ // accelerator field if the resolved type is a unified slot.
363+ syncUnifiedAcceleratorIfNeeded ( ) ;
286364 } ) ;
287365
288366 const updateResourceFieldsBasedOnImage = useEventNotStable (
@@ -388,6 +466,9 @@ const ResourceAllocationFormItems: React.FC<
388466 if ( form . getFieldValue ( 'enabledAutomaticShmem' ) ) {
389467 runShmemAutomationRule ( form . getFieldValue ( [ 'resource' , 'mem' ] ) || '0g' ) ;
390468 }
469+ // mem and/or acceleratorType may have changed above; keep the
470+ // accelerator field consistent for unified slots.
471+ syncUnifiedAcceleratorIfNeeded ( ) ;
391472 form
392473 . validateFields ( [ 'resource' ] , {
393474 recursive : true ,
@@ -445,6 +526,9 @@ const ResourceAllocationFormItems: React.FC<
445526 if ( ! hasPresetShmem ) {
446527 runShmemAutomationRule ( mem || '0g' ) ;
447528 }
529+ // mem and/or acceleratorType may have changed above; keep the
530+ // accelerator field consistent for unified slots.
531+ syncUnifiedAcceleratorIfNeeded ( ) ;
448532
449533 form
450534 . validateFields ( [ 'resource' ] , {
@@ -864,6 +948,13 @@ const ResourceAllocationFormItems: React.FC<
864948 ) {
865949 runShmemAutomationRule ( M_plus_S || '0g' ) ;
866950 }
951+ // When the active accelerator slot is a
952+ // unified-memory slot, the accelerator field
953+ // shares a single pool with host memory.
954+ // Derive the accelerator value from `mem` at
955+ // the source of change so submit-time form
956+ // state is always consistent.
957+ syncUnifiedAcceleratorIfNeeded ( ) ;
867958 } }
868959 />
869960 </ Form . Item >
@@ -921,6 +1012,10 @@ const ResourceAllocationFormItems: React.FC<
9211012 currentAcceleratorType as keyof typeof resourceSlots
9221013 ] === 'unique' ;
9231014
1015+ const isUnifiedType = isUnifiedAcceleratorSlot (
1016+ currentAcceleratorType ,
1017+ ) ;
1018+
9241019 const isSingleCluster =
9251020 form . getFieldValue ( 'cluster_size' ) < 2 ;
9261021 const hasQuantumSize = _ . isNumber (
@@ -965,9 +1060,15 @@ const ResourceAllocationFormItems: React.FC<
9651060 />
9661061 ) ,
9671062 } }
1063+ extra = {
1064+ isUnifiedType
1065+ ? t ( 'session.launcher.UnifiedAcceleratorMemoryNote' )
1066+ : undefined
1067+ }
9681068 dependencies = { [
9691069 [ 'resource' , 'acceleratorType' ] ,
9701070 'cluster_size' ,
1071+ [ 'resource' , 'mem' ] ,
9711072 ] }
9721073 rules = { [
9731074 {
@@ -1139,7 +1240,8 @@ const ResourceAllocationFormItems: React.FC<
11391240 } ,
11401241 } }
11411242 disabled = {
1142- supportedAcceleratorTypesInRGByImage ?. length === 0
1243+ supportedAcceleratorTypesInRGByImage ?. length ===
1244+ 0 || isUnifiedType
11431245 }
11441246 min = { 0 }
11451247 max = {
@@ -1193,6 +1295,58 @@ const ResourceAllocationFormItems: React.FC<
11931295 } ;
11941296 } ,
11951297 ) }
1298+ onChange = { ( nextType : string ) => {
1299+ // Changing the slot type mutates the
1300+ // active allocation; the previously
1301+ // selected preset no longer matches.
1302+ form . setFieldValue (
1303+ 'allocationPreset' ,
1304+ 'custom' ,
1305+ ) ;
1306+ // Keep the accelerator field consistent
1307+ // at the moment the slot type changes,
1308+ // rather than relying on a watcher
1309+ // effect that runs after render.
1310+ if ( isUnifiedAcceleratorSlot ( nextType ) ) {
1311+ // Switching INTO a unified slot:
1312+ // mirror the current `mem` value
1313+ // converted to the slot's display
1314+ // unit. Write acceleratorType first
1315+ // so the shared helper sees the new
1316+ // active slot when it reads from the
1317+ // form.
1318+ form . setFieldValue (
1319+ [ 'resource' , 'acceleratorType' ] ,
1320+ nextType ,
1321+ ) ;
1322+ syncUnifiedAcceleratorIfNeeded ( ) ;
1323+ } else if (
1324+ isUnifiedAcceleratorSlot (
1325+ currentAcceleratorType ,
1326+ )
1327+ ) {
1328+ // Switching OUT of a unified slot
1329+ // into a discrete one: reset to the
1330+ // discrete slot's min so the stale
1331+ // mirrored GiB value from unified
1332+ // mode does not bleed through as a
1333+ // device count.
1334+ //
1335+ // Discrete-to-discrete switches are
1336+ // intentionally NOT reset here:
1337+ // ensureValidAcceleratorType clamps
1338+ // the existing value if it falls
1339+ // outside the new slot's range, so
1340+ // the user's current allocation is
1341+ // preserved across discrete slot
1342+ // changes.
1343+ form . setFieldValue (
1344+ [ 'resource' , 'accelerator' ] ,
1345+ resourceLimits . accelerators [ nextType ]
1346+ ?. min ?? 0 ,
1347+ ) ;
1348+ }
1349+ } }
11961350 />
11971351 </ Form . Item >
11981352 ) : undefined ,
0 commit comments