33 Copyright (c) 2015-2026 Lablup Inc. All rights reserved.
44 */
55import { DeploymentAddRevisionModalAddMutation } from '../__generated__/DeploymentAddRevisionModalAddMutation.graphql' ;
6- import { DeploymentAddRevisionModalDeployMutation } from '../__generated__/DeploymentAddRevisionModalDeployMutation.graphql' ;
76import { DeploymentAddRevisionModalImageNameQuery } from '../__generated__/DeploymentAddRevisionModalImageNameQuery.graphql' ;
87import type { DeploymentAddRevisionModalPresetTransferFragment$key } from '../__generated__/DeploymentAddRevisionModalPresetTransferFragment.graphql' ;
98import type { DeploymentAddRevisionModalQuery } from '../__generated__/DeploymentAddRevisionModalQuery.graphql' ;
@@ -17,9 +16,7 @@ import {
1716 mergeExtraArgs ,
1817 reverseMapExtraArgs ,
1918} from '../helper/runtimeExtraArgsParser' ;
20- import { useWebUINavigate } from '../hooks' ;
2119import { useBAISettingUserState } from '../hooks/useBAISetting' ;
22- import { useCurrentProjectValue } from '../hooks/useCurrentProject' ;
2320import { useModelStoreProject } from '../hooks/useModelStoreProject' ;
2421import {
2522 buildArgsSchemaKeySet ,
@@ -70,7 +67,6 @@ import {
7067 BAIFlex ,
7168 BAIModal ,
7269 BAIModalProps ,
73- BAIProjectResourceGroupSelect ,
7470 BAIProjectVfolderSelect ,
7571 BAIRuntimeVariantSelect ,
7672 convertToUUID ,
@@ -118,7 +114,6 @@ export type FormValues = ImageEnvironmentFormInput &
118114
119115export type PresetFormValues = {
120116 revisionPresetId : string ;
121- resourceGroup : string ;
122117 modelFolderId : string ;
123118} ;
124119
@@ -187,9 +182,7 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
187182 const { t } = useTranslation ( ) ;
188183 const { token } = theme . useToken ( ) ;
189184 const { message } = App . useApp ( ) ;
190- const navigate = useWebUINavigate ( ) ;
191185 const relayEnvironment = useRelayEnvironment ( ) ;
192- const { id : projectId , name : projectName } = useCurrentProjectValue ( ) ;
193186 // The model folder picker scopes to the MODEL_STORE project, not the
194187 // deployment's own project — model cards live in the domain-wide model
195188 // store regardless of which project owns the deployment.
@@ -206,8 +199,8 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
206199 const [ presetForm ] = Form . useForm < PresetFormValues > ( ) ;
207200 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
208201 // FR-2862 feedback: hoist `autoActivate` from the Custom body into the
209- // modal so it can be rendered in the modal footer (visible only in Custom
210- // mode, since Preset mode always auto-activates server-side) .
202+ // modal so it can be rendered in the modal footer. Both modes forward
203+ // the value via `AddRevisionOptions.autoActivate` on `addModelRevision` .
211204 const [ autoActivate , setAutoActivate ] = useState ( true ) ;
212205
213206 const [ mode , setMode ] = useBAISettingUserState (
@@ -463,20 +456,6 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
463456 ` ,
464457 ) ;
465458
466- const [ commitDeploy ] = useMutation < DeploymentAddRevisionModalDeployMutation > (
467- graphql `
468- mutation DeploymentAddRevisionModalDeployMutation(
469- $vfolderId: UUID!
470- $input: DeployVFolderV2Input!
471- ) {
472- deployVfolderV2(vfolderId: $vfolderId, input: $input) {
473- deploymentId
474- deploymentName
475- }
476- }
477- ` ,
478- ) ;
479-
480459 // Build a Custom-form prefill object from a preset's transfer-fragment ref.
481460 // `image canonicalName` is fetched async because
482461 // `ImageEnvironmentSelectFormItems` matches the form's `environments.version`
@@ -575,16 +554,12 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
575554
576555 if ( effectiveMode === 'preset' && next === 'custom' ) {
577556 // Carry the currently selected preset (if any) into the Custom form.
578- // Also carry the resource group / model folder the user picked in
579- // Preset mode (spec (d)).
557+ // Also carry the model folder the user picked in Preset mode (spec (d)).
580558 const presetValues = presetForm . getFieldsValue ( ) ;
581559 let prefill : Partial < FormValues > = { } ;
582560 if ( selectedPresetFrgmt ) {
583561 prefill = await buildPrefillFromPreset ( selectedPresetFrgmt ) ;
584562 }
585- if ( presetValues . resourceGroup ) {
586- prefill . resourceGroup = presetValues . resourceGroup ;
587- }
588563 if ( presetValues . modelFolderId ) {
589564 prefill . modelFolderId = presetValues . modelFolderId ;
590565 }
@@ -977,7 +952,6 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
977952 size : values . cluster_size ,
978953 } ,
979954 resourceConfig : {
980- resourceGroup : { name : values . resourceGroup } ,
981955 resourceSlots : { entries : slotEntries } ,
982956 resourceOpts :
983957 optsEntries . length > 0 ? { entries : optsEntries } : null ,
@@ -1031,48 +1005,59 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
10311005 } ;
10321006
10331007 const handlePresetFinish = ( values : PresetFormValues ) : void => {
1034- if ( ! projectId ) {
1035- message . error ( t ( 'general.ErrorOccurred' ) ) ;
1036- return ;
1037- }
10381008 setIsSubmitting ( true ) ;
1039- commitDeploy ( {
1009+ // Preset mode adds a revision to the current deployment using the
1010+ // selected `revisionPresetId`. Cluster / resource / image / runtime
1011+ // configs are derived server-side from the preset; the client only
1012+ // forwards the user-picked model folder via `modelMountConfig` and the
1013+ // `autoActivate` option.
1014+ commitAdd ( {
10401015 variables : {
1041- vfolderId : convertToUUID ( values . modelFolderId ) ,
10421016 input : {
1043- projectId ,
1017+ deploymentId : toLocalId ( deploymentId ) ?? deploymentId ,
10441018 revisionPresetId : values . revisionPresetId ,
1045- resourceGroup : values . resourceGroup ,
1046- desiredReplicaCount : 1 ,
1019+ modelMountConfig : {
1020+ vfolderId : convertToUUID ( values . modelFolderId ) ,
1021+ mountDestination : '/models' ,
1022+ } ,
1023+ options : { autoActivate } ,
10471024 } ,
10481025 } ,
1049- onCompleted : ( response , errors ) => {
1026+ onCompleted : ( _ , errors ) => {
10501027 setIsSubmitting ( false ) ;
10511028 if ( errors && errors . length > 0 ) {
1052- const errMsg = errors . map ( ( e ) => e . message ) . join ( '\n' ) ;
1029+ const err = errors [ 0 ] ;
1030+ const isInProgress = err ?. message ?. includes (
1031+ 'Another deployment is already in progress' ,
1032+ ) ;
10531033 logger . error (
1054- '[DeploymentAddRevisionModal] deployVfolderV2 returned errors' ,
1034+ '[DeploymentAddRevisionModal] addModelRevision (preset) returned errors' ,
10551035 errors ,
10561036 ) ;
1057- message . error ( errMsg || t ( 'modelStore.DeployFailed' ) ) ;
1037+ message . error (
1038+ isInProgress
1039+ ? t ( 'deployment.AnotherDeploymentInProgress' )
1040+ : ( err ?. message ?? t ( 'general.ErrorOccurred' ) ) ,
1041+ ) ;
10581042 return ;
10591043 }
1060- const newDeploymentId = response . deployVfolderV2 ?. deploymentId
1061- ? String ( response . deployVfolderV2 . deploymentId )
1062- : undefined ;
1063- message . success ( t ( 'modelStore.DeploySuccess' ) ) ;
1044+ presetForm . resetFields ( ) ;
10641045 onRequestClose ( true ) ;
1065- if ( newDeploymentId ) {
1066- navigate ( `/deployments/${ newDeploymentId } ` ) ;
1067- }
10681046 } ,
10691047 onError : ( error ) => {
10701048 setIsSubmitting ( false ) ;
1049+ const isInProgress = error . message ?. includes (
1050+ 'Another deployment is already in progress' ,
1051+ ) ;
10711052 logger . error (
1072- '[DeploymentAddRevisionModal] deployVfolderV2 failed' ,
1053+ '[DeploymentAddRevisionModal] addModelRevision (preset) failed' ,
10731054 error ,
10741055 ) ;
1075- message . error ( error . message || t ( 'modelStore.DeployFailed' ) ) ;
1056+ message . error (
1057+ isInProgress
1058+ ? t ( 'deployment.AnotherDeploymentInProgress' )
1059+ : ( error . message ?? t ( 'general.ErrorOccurred' ) ) ,
1060+ ) ;
10761061 } ,
10771062 } ) ;
10781063 } ;
@@ -1099,16 +1084,13 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
10991084 // success → `onFinish` (handle*Finish), failure → `onFinishFailed`
11001085 // (handleFinishFailed). Calling `validateFields()` separately and
11011086 // swallowing rejections in a `.catch` would silently drop submits
1102- // when validation fails — including transient races during the
1103- // BAIProjectResourceGroupSelect auto-select.
1087+ // when validation fails — including transient races during
1088+ // hidden-picker auto-select flows .
11041089 const activeForm = effectiveMode === 'preset' ? presetForm : customForm ;
11051090 activeForm . submit ( ) ;
11061091 } ;
11071092
1108- const okText =
1109- effectiveMode === 'preset'
1110- ? t ( 'modelStore.Deploy' )
1111- : t ( 'deployment.AddRevision' ) ;
1093+ const okText = t ( 'deployment.AddRevision' ) ;
11121094
11131095 return (
11141096 < BAIModal
@@ -1137,18 +1119,15 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
11371119 width = { 720 }
11381120 footer = {
11391121 < BAIFlex direction = "row" align = "center" justify = "between" gap = "sm" >
1140- { effectiveMode === 'custom' ? (
1141- < Checkbox
1142- checked = { autoActivate }
1143- onChange = { ( e : CheckboxChangeEvent ) =>
1144- setAutoActivate ( e . target . checked )
1145- }
1146- >
1147- { t ( 'deployment.AutoActivate' ) }
1148- </ Checkbox >
1149- ) : (
1150- < span />
1151- ) }
1122+ < Checkbox
1123+ checked = { autoActivate }
1124+ onChange = { ( e : CheckboxChangeEvent ) =>
1125+ setAutoActivate ( e . target . checked )
1126+ }
1127+ disabled = { effectiveMode === 'preset' && hasNoPresets }
1128+ >
1129+ { t ( 'deployment.AutoActivate' ) }
1130+ </ Checkbox >
11521131 < BAIFlex direction = "row" align = "center" gap = "xs" >
11531132 < Button onClick = { ( ) => onRequestClose ( ) } >
11541133 { t ( 'button.Cancel' ) }
@@ -1233,19 +1212,6 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
12331212 </ BAIFlex >
12341213 </ Form . Item >
12351214
1236- < Form . Item
1237- name = "resourceGroup"
1238- label = { t ( 'modelStore.ResourceGroup' ) }
1239- tooltip = { t ( 'modelStore.ResourceGroupTooltip' ) }
1240- rules = { [ { required : true } ] }
1241- >
1242- < BAIProjectResourceGroupSelect
1243- projectName = { projectName ?? '' }
1244- autoSelectDefault
1245- style = { { width : '100%' } }
1246- />
1247- </ Form . Item >
1248-
12491215 < Form . Item
12501216 name = "modelFolderId"
12511217 label = { t ( 'deployment.ModelFolder' ) }
@@ -1281,6 +1247,13 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
12811247 commandInterval : 10 ,
12821248 commandMaxWaitTime : 15 ,
12831249 environ : [ ] ,
1250+ // Resource group is hidden in the UI — pre-fill from the
1251+ // deployment's current revision so new revisions reuse the
1252+ // same RG. If there is no current revision (first-revision
1253+ // flow) the hidden picker inside ResourceAllocationFormItems
1254+ // falls back to its `autoSelectFirstResourceGroup` default.
1255+ resourceGroup :
1256+ currentRevision ?. resourceConfig ?. resourceGroupName ?? undefined ,
12841257 } ) }
12851258 >
12861259 { currentRevision ? (
@@ -1531,9 +1504,19 @@ const DeploymentAddRevisionModal: React.FC<DeploymentAddRevisionModalProps> = ({
15311504 { t ( 'deployment.step.ClusterAndResources' ) }
15321505 </ SectionHeader >
15331506 < Suspense fallback = { < Skeleton active paragraph = { { rows : 4 } } /> } >
1507+ { /*
1508+ hideResourceGroupFormItem: the resource group selector is
1509+ hidden from the UI — revisions inherit the deployment's RG and
1510+ `AddRevisionInput.resourceConfig` no longer accepts a resource
1511+ group. The form value is still tracked (prefilled from
1512+ `currentRevision` or auto-selected) so that
1513+ `ResourcePresetSelect` can filter allocatable presets by the
1514+ same RG the deployment runs in.
1515+ */ }
15341516 < ResourceAllocationFormItems
15351517 enableResourcePresets
15361518 autoSelectFirstResourceGroup
1519+ hideResourceGroupFormItem
15371520 />
15381521 </ Suspense >
15391522
0 commit comments