Skip to content

Commit 7c0ca89

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

25 files changed

Lines changed: 641 additions & 396 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: 131 additions & 17 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,7 +299,10 @@ const DeploymentRevisionHistoryTab: React.FC<
277299
};
278300

279301
const handleRollback = (revision: RevisionNode): Promise<boolean> => {
280-
const revisionLabel = toLocalId(revision.id);
302+
const revisionLabel =
303+
revision.revisionNumber != null
304+
? revision.revisionNumber
305+
: toLocalId(revision.id);
281306
return new Promise<boolean>((resolveOuter) => {
282307
modal.confirm({
283308
title: t('deployment.Deploy'),
@@ -342,10 +367,11 @@ const DeploymentRevisionHistoryTab: React.FC<
342367

343368
const columns: BAIColumnType<RevisionNode>[] = [
344369
{
345-
title: t('deployment.RevisionNumber'),
370+
title: t('deployment.RevisionNumberWithID'),
346371
dataIndex: 'revisionNumber',
347372
key: 'revisionNumber',
348373
fixed: 'left',
374+
sorter: true,
349375
render: (_value, record) => {
350376
// `currentRevisionId` and `deployingRevisionId` come back as raw
351377
// UUIDs from the scalar fields, while `record.id` is the
@@ -378,8 +404,15 @@ const DeploymentRevisionHistoryTab: React.FC<
378404
})
379405
}
380406
>
381-
{recordLocalId ?? '-'}
407+
{record.revisionNumber != null
408+
? `#${record.revisionNumber}`
409+
: '-'}
382410
</Typography.Link>
411+
<BAIFlex gap={0} align="center">
412+
{'('}
413+
<BAIId globalId={record.id} />
414+
{')'}
415+
</BAIFlex>
383416
{isCurrent ? (
384417
<BAITag color="success">{t('deployment.Current')}</BAITag>
385418
) : null}
@@ -423,6 +456,7 @@ const DeploymentRevisionHistoryTab: React.FC<
423456
{
424457
title: t('deployment.ModelVersion'),
425458
key: 'modelVersion',
459+
defaultHidden: true,
426460
render: (_value, record) => {
427461
const model = record.modelDefinition?.models?.[0];
428462
if (!model) return '-';
@@ -441,40 +475,96 @@ const DeploymentRevisionHistoryTab: React.FC<
441475
},
442476
{
443477
title: t('deployment.RuntimeVariant'),
444-
key: 'runtimeVariant',
445-
dataIndex: 'runtimeVariant',
478+
key: 'runtimeVariantName',
479+
dataIndex: 'runtimeVariantName',
480+
sorter: true,
446481
render: (_value, record) =>
447482
record.modelRuntimeConfig?.runtimeVariant?.name ?? '-',
448483
},
449484
{
450485
title: t('deployment.Image'),
451486
key: 'image',
487+
defaultHidden: true,
452488
render: (_value, record) => {
453489
const canonicalName = record.imageV2?.identity?.canonicalName;
454-
if (!canonicalName) return '-';
490+
const imageGlobalId = record.imageV2?.id;
491+
if (!canonicalName && !imageGlobalId) return '-';
492+
return (
493+
<BAIFlex gap="xs" align="center" wrap="wrap">
494+
{canonicalName ? (
495+
<BAIText
496+
copyable
497+
ellipsis={{ tooltip: canonicalName }}
498+
style={{ maxWidth: 180 }}
499+
>
500+
{canonicalName}
501+
</BAIText>
502+
) : null}
503+
{imageGlobalId ? (
504+
<BAIFlex gap={0} align="center">
505+
{'('}
506+
<BAIId globalId={imageGlobalId} />
507+
{')'}
508+
</BAIFlex>
509+
) : null}
510+
</BAIFlex>
511+
);
512+
},
513+
},
514+
{
515+
title: t('deployment.ModelFolder'),
516+
key: 'modelFolder',
517+
defaultHidden: true,
518+
render: (_value, record) => {
519+
const vfolder = record.modelMountConfig?.vfolder;
520+
const vfolderId = record.modelMountConfig?.vfolderId;
521+
if (!vfolder?.name && !vfolderId) return '-';
455522
return (
456-
<Typography.Text
457-
copyable={{ text: canonicalName }}
458-
ellipsis={{ tooltip: canonicalName }}
459-
style={{ maxWidth: 240 }}
460-
>
461-
{canonicalName}
462-
</Typography.Text>
523+
<BAIFlex gap="xs" align="center" wrap="wrap">
524+
{vfolder?.name ? (
525+
<Typography.Text>{vfolder.name}</Typography.Text>
526+
) : null}
527+
{vfolder?.id ? (
528+
<BAIFlex gap={0} align="center">
529+
{'('}
530+
<BAIId globalId={vfolder.id} />
531+
{')'}
532+
</BAIFlex>
533+
) : vfolderId ? (
534+
<Typography.Text type="secondary">{vfolderId}</Typography.Text>
535+
) : null}
536+
</BAIFlex>
463537
);
464538
},
465539
},
466540
{
467541
title: t('deployment.ClusterSize'),
468-
key: 'clusterSize',
542+
key: 'clusterMode',
543+
dataIndex: 'clusterMode',
544+
sorter: true,
469545
render: (_value, record) => {
470546
const mode = record.clusterConfig?.mode;
471547
const size = record.clusterConfig?.size;
472548
if (size == null) return '-';
473549
return mode ? `${size} (${mode})` : `${size}`;
474550
},
475551
},
552+
{
553+
title: t('deployment.ResourceGroup'),
554+
key: 'resourceGroup',
555+
dataIndex: 'resourceGroup',
556+
sorter: true,
557+
defaultHidden: true,
558+
render: (_value, record) =>
559+
record.resourceConfig?.resourceGroupName ?? '-',
560+
},
476561
];
477562

563+
const uuidRule = {
564+
message: t('general.InvalidUUID'),
565+
validate: isValidUUID,
566+
};
567+
478568
const filterProperties = [
479569
{
480570
key: 'revisionNumber',
@@ -488,6 +578,30 @@ const DeploymentRevisionHistoryTab: React.FC<
488578
operators: ['after' as const, 'before' as const],
489579
defaultOperator: 'after' as const,
490580
},
581+
{
582+
key: 'resourceGroup',
583+
propertyLabel: t('deployment.ResourceGroup'),
584+
type: 'string' as const,
585+
},
586+
{
587+
key: 'clusterMode',
588+
propertyLabel: t('deployment.ClusterMode'),
589+
type: 'string' as const,
590+
},
591+
{
592+
key: 'imageId',
593+
propertyLabel: t('deployment.Image'),
594+
type: 'uuid' as const,
595+
fixedOperator: 'equals' as const,
596+
rule: uuidRule,
597+
},
598+
{
599+
key: 'modelVfolderId',
600+
propertyLabel: t('deployment.ModelFolder'),
601+
type: 'uuid' as const,
602+
fixedOperator: 'equals' as const,
603+
rule: uuidRule,
604+
},
491605
];
492606

493607
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": "Χειροκίνητο",

resources/i18n/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@
919919
"DeployDisabled": "Cannot select a revision that is being deployed or is currently deployed.",
920920
"DeploySuccess": "Deploy of revision #{{revisionNumber}} has been requested.",
921921
"Deploying": "Deploying",
922-
"DeployingRevisionApplying": "Deploying revision {{name}}.",
922+
"DeployingRevisionApplying": "Deploying revision {{revisionNumber}}.",
923923
"DeployingRevisionDetail": "Deploying Revision Detail",
924924
"Deployment": "Deployment",
925925
"DeploymentCreated": "Deployment has been created.",
@@ -989,6 +989,7 @@
989989
"RevisionId": "Revision ID",
990990
"RevisionIdPlaceholder": "Leave blank to auto-generate",
991991
"RevisionNumber": "Revision Number",
992+
"RevisionNumberWithID": "Revision (ID)",
992993
"Revisions": "Revisions",
993994
"Running": "Running",
994995
"RuntimeVariant": "Runtime",
@@ -1469,6 +1470,7 @@
14691470
"InProgress": "In progress",
14701471
"Inactive": "Inactive",
14711472
"InvalidJSONFormat": "Invalid JSON format.",
1473+
"InvalidUUID": "Please enter a valid UUID",
14721474
"LastUpdated": "Last Updated",
14731475
"Loading": "Loading...",
14741476
"Manual": "Manual",

0 commit comments

Comments
 (0)