Skip to content

Commit 278daf8

Browse files
committed
feat(FR-2858): add revision number display and expand filter/sort in revision history tab
1 parent 8d7fe21 commit 278daf8

25 files changed

Lines changed: 639 additions & 398 deletions

data/schema.graphql

Lines changed: 427 additions & 355 deletions
Large diffs are not rendered by default.

react/src/components/DeploymentConfigurationSection.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,12 @@ const DeploymentConfigurationSection: React.FC<
202202
}
203203
currentRevision @since(version: "26.4.3") {
204204
id
205+
revisionNumber
205206
...DeploymentRevisionDetail_revision
206207
}
207208
deployingRevision @since(version: "26.4.3") {
208209
id
210+
revisionNumber
209211
...DeploymentRevisionDetail_revision
210212
}
211213
...DeploymentRevisionHistoryTab_deployment
@@ -376,7 +378,10 @@ const DeploymentConfigurationSection: React.FC<
376378
icon={<LoadingOutlined spin />}
377379
showIcon
378380
title={t('deployment.DeployingRevisionApplying', {
379-
name: toLocalId(deployingRevision.id) ?? '',
381+
revisionNumber:
382+
deployingRevision.revisionNumber != null
383+
? `#${deployingRevision.revisionNumber}`
384+
: (toLocalId(deployingRevision.id) ?? ''),
380385
})}
381386
action={
382387
<Button

react/src/components/DeploymentRevisionDetail.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
BAIFlex,
1515
BAIResourceNumberWithIcon,
1616
BAITag,
17+
BAIText,
1718
filterOutEmpty,
1819
filterOutNullAndUndefined,
1920
toLocalId,
@@ -40,6 +41,7 @@ const DeploymentRevisionDetail: React.FC<{
4041
graphql`
4142
fragment DeploymentRevisionDetail_revision on ModelRevision {
4243
id
44+
revisionNumber
4345
createdAt
4446
clusterConfig {
4547
mode
@@ -129,11 +131,21 @@ const DeploymentRevisionDetail: React.FC<{
129131

130132
const baseItems: DescriptionsItemType[] = filterOutEmpty([
131133
{
132-
key: 'revision-name',
134+
key: 'revision-number',
135+
label: t('deployment.RevisionNumber'),
136+
children:
137+
revision.revisionNumber != null ? (
138+
<BAIText>{`#${revision.revisionNumber}`}</BAIText>
139+
) : (
140+
renderFallback()
141+
),
142+
},
143+
{
144+
key: 'revision-id',
133145
label: t('modelService.RevisionID'),
134146
children: revision.id ? (
135147
<BAIFlex gap="xs" align="center">
136-
<Typography.Text>{toLocalId(revision.id)}</Typography.Text>
148+
<BAIText copyable>{toLocalId(revision.id)}</BAIText>
137149
{status === 'current' && (
138150
<BAITag color="success">{t('deployment.Current')}</BAITag>
139151
)}

react/src/components/DeploymentRevisionHistoryTab.tsx

Lines changed: 129 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ import {
2424
BAITable,
2525
BAITag,
2626
BAIUnmountAfterClose,
27+
BAIId,
2728
INITIAL_FETCH_KEY,
2829
type GraphQLFilter,
2930
filterOutNullAndUndefined,
31+
isValidUUID,
3032
toLocalId,
3133
useBAILogger,
3234
useFetchKey,
35+
BAIText,
3336
} from 'backend.ai-ui';
3437
import dayjs from 'dayjs';
3538
import * as _ from 'lodash-es';
@@ -51,12 +54,20 @@ import {
5154
type RevisionNode = NonNullable<
5255
NonNullable<
5356
NonNullable<
54-
DeploymentRevisionHistoryTabListQuery['response']['deployment']
55-
>['revisionHistory']['edges'][number]
57+
NonNullable<
58+
DeploymentRevisionHistoryTabListQuery['response']['deployment']
59+
>['revisionHistory']
60+
>['edges'][number]
5661
>['node']
5762
>;
5863

59-
const availableRevisionSorterKeys = ['createdAt'] as const;
64+
const availableRevisionSorterKeys = [
65+
'revisionNumber',
66+
'createdAt',
67+
'resourceGroup',
68+
'clusterMode',
69+
'runtimeVariantName',
70+
] as const;
6071
const availableRevisionSorterValues = [
6172
...availableRevisionSorterKeys,
6273
...availableRevisionSorterKeys.map((key) => `-${key}` as const),
@@ -195,12 +206,16 @@ const DeploymentRevisionHistoryTab: React.FC<
195206
edges {
196207
node {
197208
id
209+
revisionNumber
198210
createdAt
199211
200212
clusterConfig {
201213
mode
202214
size
203215
}
216+
resourceConfig {
217+
resourceGroupName
218+
}
204219
modelRuntimeConfig {
205220
runtimeVariant {
206221
name
@@ -220,6 +235,13 @@ const DeploymentRevisionHistoryTab: React.FC<
220235
canonicalName
221236
}
222237
}
238+
modelMountConfig {
239+
vfolderId
240+
vfolder {
241+
id
242+
name
243+
}
244+
}
223245
...DeploymentRevisionDetail_revision
224246
}
225247
}
@@ -277,12 +299,11 @@ const DeploymentRevisionHistoryTab: React.FC<
277299
};
278300

279301
const handleRollback = (revision: RevisionNode): Promise<boolean> => {
280-
const revisionLabel = toLocalId(revision.id);
281302
return new Promise<boolean>((resolveOuter) => {
282303
modal.confirm({
283304
title: t('deployment.Deploy'),
284305
content: t('deployment.DeployConfirm', {
285-
revisionNumber: revisionLabel,
306+
revisionNumber: revision.revisionNumber,
286307
}),
287308
okText: t('deployment.Deploy'),
288309
okButtonProps: {
@@ -315,7 +336,7 @@ const DeploymentRevisionHistoryTab: React.FC<
315336
upsertNotification({
316337
open: true,
317338
message: t('deployment.DeploySuccess', {
318-
revisionNumber: revisionLabel,
339+
revisionNumber: revision.revisionNumber,
319340
}),
320341
});
321342
handleRefresh();
@@ -342,10 +363,11 @@ const DeploymentRevisionHistoryTab: React.FC<
342363

343364
const columns: BAIColumnType<RevisionNode>[] = [
344365
{
345-
title: t('deployment.RevisionNumber'),
366+
title: t('deployment.RevisionNumberWithID'),
346367
dataIndex: 'revisionNumber',
347368
key: 'revisionNumber',
348369
fixed: 'left',
370+
sorter: true,
349371
render: (_value, record) => {
350372
// `currentRevisionId` and `deployingRevisionId` come back as raw
351373
// UUIDs from the scalar fields, while `record.id` is the
@@ -378,8 +400,15 @@ const DeploymentRevisionHistoryTab: React.FC<
378400
})
379401
}
380402
>
381-
{recordLocalId ?? '-'}
403+
{record.revisionNumber != null
404+
? `#${record.revisionNumber}`
405+
: '-'}
382406
</Typography.Link>
407+
<BAIFlex gap={0} align="center">
408+
{'('}
409+
<BAIId globalId={record.id} />
410+
{')'}
411+
</BAIFlex>
383412
{isCurrent ? (
384413
<BAITag color="success">{t('deployment.Current')}</BAITag>
385414
) : null}
@@ -423,6 +452,7 @@ const DeploymentRevisionHistoryTab: React.FC<
423452
{
424453
title: t('deployment.ModelVersion'),
425454
key: 'modelVersion',
455+
defaultHidden: true,
426456
render: (_value, record) => {
427457
const model = record.modelDefinition?.models?.[0];
428458
if (!model) return '-';
@@ -441,40 +471,96 @@ const DeploymentRevisionHistoryTab: React.FC<
441471
},
442472
{
443473
title: t('deployment.RuntimeVariant'),
444-
key: 'runtimeVariant',
445-
dataIndex: 'runtimeVariant',
474+
key: 'runtimeVariantName',
475+
dataIndex: 'runtimeVariantName',
476+
sorter: true,
446477
render: (_value, record) =>
447478
record.modelRuntimeConfig?.runtimeVariant?.name ?? '-',
448479
},
449480
{
450481
title: t('deployment.Image'),
451482
key: 'image',
483+
defaultHidden: true,
452484
render: (_value, record) => {
453485
const canonicalName = record.imageV2?.identity?.canonicalName;
454-
if (!canonicalName) return '-';
486+
const imageGlobalId = record.imageV2?.id;
487+
if (!canonicalName && !imageGlobalId) return '-';
488+
return (
489+
<BAIFlex gap="xs" align="center" wrap="wrap">
490+
{canonicalName ? (
491+
<BAIText
492+
copyable
493+
ellipsis={{ tooltip: canonicalName }}
494+
style={{ maxWidth: 180 }}
495+
>
496+
{canonicalName}
497+
</BAIText>
498+
) : null}
499+
{imageGlobalId ? (
500+
<BAIFlex gap={0} align="center">
501+
{'('}
502+
<BAIId globalId={imageGlobalId} />
503+
{')'}
504+
</BAIFlex>
505+
) : null}
506+
</BAIFlex>
507+
);
508+
},
509+
},
510+
{
511+
title: t('deployment.ModelFolder'),
512+
key: 'modelFolder',
513+
defaultHidden: true,
514+
render: (_value, record) => {
515+
const vfolder = record.modelMountConfig?.vfolder;
516+
const vfolderId = record.modelMountConfig?.vfolderId;
517+
if (!vfolder?.name && !vfolderId) return '-';
455518
return (
456-
<Typography.Text
457-
copyable={{ text: canonicalName }}
458-
ellipsis={{ tooltip: canonicalName }}
459-
style={{ maxWidth: 240 }}
460-
>
461-
{canonicalName}
462-
</Typography.Text>
519+
<BAIFlex gap="xs" align="center" wrap="wrap">
520+
{vfolder?.name ? (
521+
<Typography.Text>{vfolder.name}</Typography.Text>
522+
) : null}
523+
{vfolder?.id ? (
524+
<BAIFlex gap={0} align="center">
525+
{'('}
526+
<BAIId globalId={vfolder.id} />
527+
{')'}
528+
</BAIFlex>
529+
) : vfolderId ? (
530+
<Typography.Text type="secondary">{vfolderId}</Typography.Text>
531+
) : null}
532+
</BAIFlex>
463533
);
464534
},
465535
},
466536
{
467537
title: t('deployment.ClusterSize'),
468-
key: 'clusterSize',
538+
key: 'clusterMode',
539+
dataIndex: 'clusterMode',
540+
sorter: true,
469541
render: (_value, record) => {
470542
const mode = record.clusterConfig?.mode;
471543
const size = record.clusterConfig?.size;
472544
if (size == null) return '-';
473545
return mode ? `${size} (${mode})` : `${size}`;
474546
},
475547
},
548+
{
549+
title: t('deployment.ResourceGroup'),
550+
key: 'resourceGroup',
551+
dataIndex: 'resourceGroup',
552+
sorter: true,
553+
defaultHidden: true,
554+
render: (_value, record) =>
555+
record.resourceConfig?.resourceGroupName ?? '-',
556+
},
476557
];
477558

559+
const uuidRule = {
560+
message: t('general.InvalidUUID'),
561+
validate: isValidUUID,
562+
};
563+
478564
const filterProperties = [
479565
{
480566
key: 'revisionNumber',
@@ -488,6 +574,30 @@ const DeploymentRevisionHistoryTab: React.FC<
488574
operators: ['after' as const, 'before' as const],
489575
defaultOperator: 'after' as const,
490576
},
577+
{
578+
key: 'resourceGroup',
579+
propertyLabel: t('deployment.ResourceGroup'),
580+
type: 'string' as const,
581+
},
582+
{
583+
key: 'clusterMode',
584+
propertyLabel: t('deployment.ClusterMode'),
585+
type: 'string' as const,
586+
},
587+
{
588+
key: 'imageId',
589+
propertyLabel: t('deployment.Image'),
590+
type: 'uuid' as const,
591+
fixedOperator: 'equals' as const,
592+
rule: uuidRule,
593+
},
594+
{
595+
key: 'modelVfolderId',
596+
propertyLabel: t('deployment.ModelFolder'),
597+
type: 'uuid' as const,
598+
fixedOperator: 'equals' as const,
599+
rule: uuidRule,
600+
},
491601
];
492602

493603
const filterValue: GraphQLFilter | undefined = queryParams.rvFilter

resources/i18n/de.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@
918918
"DeployDisabled": "Es kann keine Revision ausgewählt werden, die gerade bereitgestellt wird oder bereits bereitgestellt ist.",
919919
"DeploySuccess": "Die Bereitstellung von Revision #{{revisionNumber}} wurde angefordert.",
920920
"Deploying": "Wird bereitgestellt",
921-
"DeployingRevisionApplying": "Revision {{name}} wird bereitgestellt.",
921+
"DeployingRevisionApplying": "Revision {{revisionNumber}} wird bereitgestellt.",
922922
"DeployingRevisionDetail": "Details zur laufenden Bereitstellung",
923923
"Deployment": "Bereitstellung",
924924
"DeploymentCreated": "Bereitstellung wurde erstellt.",
@@ -987,6 +987,7 @@
987987
"RevisionId": "Revisions-ID",
988988
"RevisionIdPlaceholder": "Revisions-ID eingeben (optional)",
989989
"RevisionNumber": "Revisionsnummer",
990+
"RevisionNumberWithID": "Revision (ID)",
990991
"Revisions": "Revisionen",
991992
"Running": "Läuft",
992993
"RuntimeVariant": "Laufzeit",
@@ -1469,6 +1470,7 @@
14691470
"InProgress": "In Arbeit",
14701471
"Inactive": "Inaktiv",
14711472
"InvalidJSONFormat": "Ungültiges JSON-Format.",
1473+
"InvalidUUID": "Bitte geben Sie eine gültige UUID ein",
14721474
"LastUpdated": "Zuletzt aktualisiert",
14731475
"Loading": "Wird geladen...",
14741476
"Manual": "Handbuch",

resources/i18n/el.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@
918918
"DeployDisabled": "Δεν είναι δυνατή η επιλογή Revision που βρίσκεται σε ανάπτυξη ή είναι ήδη αναπτυγμένη.",
919919
"DeploySuccess": "Το αίτημα ανάπτυξης για τη Revision #{{revisionNumber}} υποβλήθηκε.",
920920
"Deploying": "Σε ανάπτυξη",
921-
"DeployingRevisionApplying": "Γίνεται ανάπτυξη της Revision {{name}}.",
921+
"DeployingRevisionApplying": "Γίνεται ανάπτυξη της Revision {{revisionNumber}}.",
922922
"DeployingRevisionDetail": "Λεπτομέρειες Εκτελούμενης Ανάπτυξης",
923923
"Deployment": "Ανάπτυξη",
924924
"DeploymentCreated": "Η ανάπτυξη δημιουργήθηκε.",
@@ -987,6 +987,7 @@
987987
"RevisionId": "ID Revision",
988988
"RevisionIdPlaceholder": "Εισάγετε ID Revision (προαιρετικό)",
989989
"RevisionNumber": "Αριθμός Αναθεώρησης",
990+
"RevisionNumberWithID": "Αναθεώρηση (ID)",
990991
"Revisions": "Revision",
991992
"Running": "Σε εκτέλεση",
992993
"RuntimeVariant": "Χρόνος εκτέλεσης",
@@ -1467,6 +1468,7 @@
14671468
"InProgress": "Σε εξέλιξη",
14681469
"Inactive": "Αδρανής",
14691470
"InvalidJSONFormat": "Μη έγκυρη μορφή JSON.",
1471+
"InvalidUUID": "Παρακαλώ εισάγετε έγκυρο UUID",
14701472
"LastUpdated": "Τελευταία ενημέρωση",
14711473
"Loading": "Φόρτωση...",
14721474
"Manual": "Χειροκίνητο",

0 commit comments

Comments
 (0)