Skip to content

Commit 89545f3

Browse files
authored
Performance artifact bff call (kubeflow#1740)
* added bff call Signed-off-by: rsun19 <[email protected]> * added promise reject for non validated models Signed-off-by: rsun19 <[email protected]> * fixed tests Signed-off-by: rsun19 <[email protected]> * addressed comments Signed-off-by: rsun19 <[email protected]> * added test for prev button Signed-off-by: rsun19 <[email protected]> --------- Signed-off-by: rsun19 <[email protected]>
1 parent f95bad1 commit 89545f3

File tree

11 files changed

+274
-80
lines changed

11 files changed

+274
-80
lines changed

clients/ui/bff/internal/mocks/model_catalog_client_mock.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,27 @@ func (m *ModelCatalogClientMock) GetCatalogModelArtifacts(client httpclient.HTTP
171171
var allMockModelArtifacts models.CatalogModelArtifactList
172172

173173
if sourceId == "sample-source" && modelName == "repo1%2Fgranite-8b-code-instruct" {
174-
allMockModelArtifacts = GetCatalogPerformanceMetricsArtifactListMock()
174+
performanceArtifacts := GetCatalogPerformanceMetricsArtifactListMock(3)
175+
accuracyArtifacts := GetCatalogAccuracyMetricsArtifactListMock()
176+
modelArtifacts := GetCatalogModelArtifactListMock()
177+
combinedItems := append(performanceArtifacts.Items, accuracyArtifacts.Items...)
178+
combinedItems = append(combinedItems, modelArtifacts.Items...)
179+
allMockModelArtifacts = models.CatalogModelArtifactList{
180+
Items: combinedItems,
181+
Size: int32(len(combinedItems)),
182+
PageSize: performanceArtifacts.PageSize,
183+
NextPageToken: "",
184+
}
175185
} else if sourceId == "sample-source" && modelName == "repo1%2Fgranite-7b-instruct" {
176-
allMockModelArtifacts = GetCatalogAccuracyMetricsArtifactListMock()
186+
accuracyArtifacts := GetCatalogAccuracyMetricsArtifactListMock()
187+
modelArtifacts := GetCatalogModelArtifactListMock()
188+
combinedItems := append(accuracyArtifacts.Items, modelArtifacts.Items...)
189+
allMockModelArtifacts = models.CatalogModelArtifactList{
190+
Items: combinedItems,
191+
Size: int32(len(combinedItems)),
192+
PageSize: accuracyArtifacts.PageSize,
193+
NextPageToken: "",
194+
}
177195
} else if sourceId == "sample-source" && modelName == "repo1%2Fgranite-3b-code-base" {
178196
allMockModelArtifacts = models.CatalogModelArtifactList{
179197
Items: []models.CatalogArtifact{},

clients/ui/bff/internal/mocks/static_data_mock.go

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,7 @@ func GetCatalogModelArtifactMock() []models.CatalogArtifact {
864864
}
865865
}
866866

867-
func performanceMetricsCustomProperties() *map[string]openapi.MetadataValue {
867+
func performanceMetricsCustomProperties(customProperties map[string]openapi.MetadataValue) *map[string]openapi.MetadataValue {
868868
result := map[string]openapi.MetadataValue{
869869
"config_id": {
870870
MetadataStringValue: &openapi.MetadataStringValue{
@@ -1057,26 +1057,88 @@ func performanceMetricsCustomProperties() *map[string]openapi.MetadataValue {
10571057
},
10581058
},
10591059
}
1060+
for key, value := range customProperties {
1061+
result[key] = value
1062+
}
10601063
return &result
10611064
}
10621065

1063-
func GetCatalogPerformanceMetricsArtifactMock() []models.CatalogArtifact {
1064-
return []models.CatalogArtifact{
1066+
func GetCatalogPerformanceMetricsArtifactMock(itemCount int32) []models.CatalogArtifact {
1067+
artifacts := []models.CatalogArtifact{
10651068
{
10661069
ArtifactType: *stringToPointer("metrics-artifact"),
10671070
MetricsType: stringToPointer("performance-metrics"),
10681071
CreateTimeSinceEpoch: stringToPointer("1693526400000"),
10691072
LastUpdateTimeSinceEpoch: stringToPointer("1704067200000"),
1070-
CustomProperties: performanceMetricsCustomProperties(),
1071-
}, {
1072-
1073-
ArtifactType: *stringToPointer("model-artifact"),
1074-
Uri: stringToPointer("oci://registry.sample.io/repo1/modelcar-granite-7b-starter:1.4.0"),
1075-
CreateTimeSinceEpoch: stringToPointer("1693526400000"),
1076-
1073+
CustomProperties: performanceMetricsCustomProperties(map[string]openapi.MetadataValue{}),
1074+
},
1075+
{
1076+
ArtifactType: *stringToPointer("metrics-artifact"),
1077+
MetricsType: stringToPointer("performance-metrics"),
1078+
CreateTimeSinceEpoch: stringToPointer("1693526400000"),
10771079
LastUpdateTimeSinceEpoch: stringToPointer("1704067200000"),
1078-
CustomProperties: newCustomProperties(),
1079-
}}
1080+
CustomProperties: performanceMetricsCustomProperties(map[string]openapi.MetadataValue{
1081+
"hardware": {
1082+
MetadataStringValue: &openapi.MetadataStringValue{
1083+
StringValue: "RTX 4090",
1084+
MetadataType: "MetadataStringValue",
1085+
},
1086+
},
1087+
"hardware_count": {
1088+
MetadataIntValue: &openapi.MetadataIntValue{
1089+
IntValue: "33",
1090+
MetadataType: "MetadataIntValue",
1091+
},
1092+
},
1093+
"requests_per_second": {
1094+
MetadataDoubleValue: &openapi.MetadataDoubleValue{
1095+
DoubleValue: 10,
1096+
MetadataType: "MetadataDoubleValue",
1097+
},
1098+
},
1099+
"ttft_mean": {
1100+
MetadataDoubleValue: &openapi.MetadataDoubleValue{
1101+
DoubleValue: 67.14892749816,
1102+
MetadataType: "MetadataDoubleValue",
1103+
},
1104+
},
1105+
}),
1106+
},
1107+
{
1108+
ArtifactType: *stringToPointer("metrics-artifact"),
1109+
MetricsType: stringToPointer("performance-metrics"),
1110+
CreateTimeSinceEpoch: stringToPointer("1693526400000"),
1111+
LastUpdateTimeSinceEpoch: stringToPointer("1704067200000"),
1112+
CustomProperties: performanceMetricsCustomProperties(map[string]openapi.MetadataValue{
1113+
"hardware": {
1114+
MetadataStringValue: &openapi.MetadataStringValue{
1115+
StringValue: "A100",
1116+
MetadataType: "MetadataStringValue",
1117+
},
1118+
},
1119+
"hardware_count": {
1120+
MetadataIntValue: &openapi.MetadataIntValue{
1121+
IntValue: "40",
1122+
MetadataType: "MetadataIntValue",
1123+
},
1124+
},
1125+
"requests_per_second": {
1126+
MetadataDoubleValue: &openapi.MetadataDoubleValue{
1127+
DoubleValue: 15,
1128+
MetadataType: "MetadataDoubleValue",
1129+
},
1130+
},
1131+
"ttft_mean": {
1132+
MetadataDoubleValue: &openapi.MetadataDoubleValue{
1133+
DoubleValue: 42.123791232,
1134+
MetadataType: "MetadataDoubleValue",
1135+
},
1136+
},
1137+
}),
1138+
},
1139+
}
1140+
artifacts = artifacts[:itemCount]
1141+
return artifacts
10801142
}
10811143

10821144
func accuracyMetricsCustomProperties() *map[string]openapi.MetadataValue {
@@ -1106,14 +1168,7 @@ func GetCatalogAccuracyMetricsArtifactMock() []models.CatalogArtifact {
11061168
LastUpdateTimeSinceEpoch: stringToPointer("1704067200000"),
11071169
CustomProperties: accuracyMetricsCustomProperties(),
11081170
},
1109-
{
1110-
ArtifactType: *stringToPointer("model-artifact"),
1111-
Uri: stringToPointer("oci://registry.sample.io/repo1/modelcar-granite-7b-starter:1.4.0"),
1112-
CreateTimeSinceEpoch: stringToPointer("1693526400000"),
1113-
1114-
LastUpdateTimeSinceEpoch: stringToPointer("1704067200000"),
1115-
CustomProperties: newCustomProperties(),
1116-
}}
1171+
}
11171172
}
11181173

11191174
func GetCatalogModelArtifactListMock() models.CatalogModelArtifactList {
@@ -1127,8 +1182,8 @@ func GetCatalogModelArtifactListMock() models.CatalogModelArtifactList {
11271182
}
11281183
}
11291184

1130-
func GetCatalogPerformanceMetricsArtifactListMock() models.CatalogModelArtifactList {
1131-
allArtifactMock := GetCatalogPerformanceMetricsArtifactMock()
1185+
func GetCatalogPerformanceMetricsArtifactListMock(itemCount int32) models.CatalogModelArtifactList {
1186+
allArtifactMock := GetCatalogPerformanceMetricsArtifactMock(itemCount)
11321187

11331188
return models.CatalogModelArtifactList{
11341189
Items: allArtifactMock,

clients/ui/frontend/src/__mocks__/mockCatalogModelArtifactList.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ export const mockCatalogPerformanceMetricsArtifact = (
5757
metadataType: ModelRegistryMetadataType.INT,
5858
int_value: '2',
5959
},
60+
hardware: {
61+
metadataType: ModelRegistryMetadataType.STRING,
62+
string_value: 'H100-80',
63+
},
64+
requests_per_second: {
65+
metadataType: ModelRegistryMetadataType.DOUBLE,
66+
double_value: 7,
67+
},
6068
ttft_mean: {
6169
metadataType: ModelRegistryMetadataType.DOUBLE,
6270
double_value: 35.48818160947744,

clients/ui/frontend/src/__tests__/cypress/cypress/pages/modelCatalog.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,6 @@ class ModelCatalog {
107107
return this.findModelCatalogCards().first().should('be.visible');
108108
}
109109

110-
findLastModelCatalogCard() {
111-
return this.findModelCatalogCards().last().should('be.visible');
112-
}
113-
114110
findModelCatalogDetailLink() {
115111
return cy.findAllByTestId('model-catalog-detail-link');
116112
}
@@ -119,6 +115,14 @@ class ModelCatalog {
119115
return cy.findAllByTestId('validated-model-benchmark-link');
120116
}
121117

118+
findValidatedModelBenchmarkNext() {
119+
return cy.findAllByTestId('validated-model-benchmark-next');
120+
}
121+
122+
findValidatedModelBenchmarkPrev() {
123+
return cy.findAllByTestId('validated-model-benchmark-prev');
124+
}
125+
122126
findModelCatalogDescription() {
123127
return cy.findByTestId('model-catalog-card-description');
124128
}
@@ -245,6 +249,18 @@ class ModelCatalog {
245249
findHardwareConfigurationPagination() {
246250
return cy.get('[data-testid="hardware-configuration-table"] .pf-v6-c-pagination');
247251
}
252+
253+
findValidatedModelHardware() {
254+
return cy.findByTestId('validated-model-hardware');
255+
}
256+
257+
findValidatedModelRps() {
258+
return cy.findByTestId('validated-model-rps');
259+
}
260+
261+
findValidatedModelTtft() {
262+
return cy.findByTestId('validated-model-ttft');
263+
}
248264
}
249265

250266
export const modelCatalog = new ModelCatalog();

clients/ui/frontend/src/__tests__/cypress/cypress/tests/mocked/modelCatalog/modelCatalog.cy.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { modelCatalog } from '~/__tests__/cypress/cypress/pages/modelCatalog';
22
import {
3+
mockCatalogAccuracyMetricsArtifact,
34
mockCatalogModel,
5+
mockCatalogModelArtifact,
46
mockCatalogModelList,
7+
mockCatalogPerformanceMetricsArtifact,
58
mockCatalogSource,
69
mockCatalogSourceList,
710
} from '~/__mocks__';
@@ -58,6 +61,24 @@ const initIntercepts = ({
5861
},
5962
mockCatalogFilterOptionsList(),
6063
);
64+
65+
cy.interceptApi(
66+
`GET /api/:apiVersion/model_catalog/sources/:sourceId/artifacts/:modelName`,
67+
{
68+
path: {
69+
apiVersion: MODEL_CATALOG_API_VERSION,
70+
sourceId: 'sample-source',
71+
modelName: 'repo1/model1',
72+
},
73+
},
74+
{
75+
items: [
76+
mockCatalogPerformanceMetricsArtifact({}),
77+
mockCatalogAccuracyMetricsArtifact({}),
78+
mockCatalogModelArtifact({}),
79+
],
80+
},
81+
);
6182
};
6283

6384
describe('Model Catalog Page', () => {

clients/ui/frontend/src/__tests__/cypress/cypress/tests/mocked/modelCatalog/modelCatalogCard.cy.ts

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
/* eslint-disable camelcase */
12
import { modelCatalog } from '~/__tests__/cypress/cypress/pages/modelCatalog';
23
import {
4+
mockCatalogAccuracyMetricsArtifact,
35
mockCatalogModel,
6+
mockCatalogModelArtifact,
47
mockCatalogModelArtifactList,
58
mockCatalogModelList,
9+
mockCatalogPerformanceMetricsArtifact,
610
mockCatalogSource,
711
mockCatalogSourceList,
812
} from '~/__mocks__';
@@ -105,6 +109,64 @@ const initIntercepts = ({
105109
},
106110
mockCatalogFilterOptionsList(),
107111
);
112+
113+
cy.interceptApi(
114+
`GET /api/:apiVersion/model_catalog/sources/:sourceId/artifacts/:modelName`,
115+
{
116+
path: {
117+
apiVersion: MODEL_CATALOG_API_VERSION,
118+
sourceId: 'source-2',
119+
modelName: 'validated-model',
120+
},
121+
},
122+
{
123+
items: [
124+
mockCatalogPerformanceMetricsArtifact({}),
125+
mockCatalogPerformanceMetricsArtifact({
126+
customProperties: {
127+
hardware: {
128+
metadataType: ModelRegistryMetadataType.STRING,
129+
string_value: 'RTX 4090',
130+
},
131+
hardware_count: {
132+
metadataType: ModelRegistryMetadataType.INT,
133+
int_value: '33',
134+
},
135+
requests_per_second: {
136+
metadataType: ModelRegistryMetadataType.DOUBLE,
137+
double_value: 10,
138+
},
139+
ttft_mean: {
140+
metadataType: ModelRegistryMetadataType.DOUBLE,
141+
double_value: 67.14892749816,
142+
},
143+
},
144+
}),
145+
mockCatalogPerformanceMetricsArtifact({
146+
customProperties: {
147+
hardware: {
148+
metadataType: ModelRegistryMetadataType.STRING,
149+
string_value: 'A100',
150+
},
151+
hardware_count: {
152+
metadataType: ModelRegistryMetadataType.INT,
153+
int_value: '40',
154+
},
155+
requests_per_second: {
156+
metadataType: ModelRegistryMetadataType.DOUBLE,
157+
double_value: 15,
158+
},
159+
ttft_mean: {
160+
metadataType: ModelRegistryMetadataType.DOUBLE,
161+
double_value: 42.123791232,
162+
},
163+
},
164+
}),
165+
mockCatalogAccuracyMetricsArtifact({}),
166+
mockCatalogModelArtifact({}),
167+
],
168+
},
169+
).as('getCatalogModelArtifacts');
108170
};
109171

110172
describe('ModelCatalogCard Component', () => {
@@ -167,8 +229,26 @@ describe('ModelCatalogCard Component', () => {
167229
modelCatalog.visit();
168230
});
169231
it('should show validated model correctly', () => {
170-
modelCatalog.findValidatedModelBenchmarkLink().first().click();
171-
cy.url().should('include', 'performance-insights');
232+
cy.wait('@getCatalogModelArtifacts');
233+
modelCatalog.findFirstModelCatalogCard().within(() => {
234+
modelCatalog.findValidatedModelHardware().should('contain.text', '2xH100-80');
235+
modelCatalog.findValidatedModelRps().should('contain.text', '7');
236+
modelCatalog.findValidatedModelTtft().should('contain.text', '35.48818160947744');
237+
modelCatalog.findValidatedModelBenchmarkNext().click();
238+
modelCatalog.findValidatedModelHardware().should('contain.text', '33xRTX 4090');
239+
modelCatalog.findValidatedModelRps().should('contain.text', '10');
240+
modelCatalog.findValidatedModelTtft().should('contain.text', '67.14892749816');
241+
modelCatalog.findValidatedModelBenchmarkNext().click();
242+
modelCatalog.findValidatedModelHardware().should('contain.text', '40xA100');
243+
modelCatalog.findValidatedModelRps().should('contain.text', '15');
244+
modelCatalog.findValidatedModelTtft().should('contain.text', '42.123791232');
245+
modelCatalog.findValidatedModelBenchmarkPrev().click();
246+
modelCatalog.findValidatedModelHardware().should('contain.text', '33xRTX 4090');
247+
modelCatalog.findValidatedModelRps().should('contain.text', '10');
248+
modelCatalog.findValidatedModelTtft().should('contain.text', '67.14892749816');
249+
modelCatalog.findValidatedModelBenchmarkLink().click();
250+
cy.url().should('include', 'performance-insights');
251+
});
172252
});
173253
});
174254
});

clients/ui/frontend/src/app/hooks/modelCatalog/useCatalogModelArtifacts.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { useModelCatalogAPI } from './useModelCatalogAPI';
66
export const useCatalogModelArtifacts = (
77
sourceId: string,
88
modelName: string,
9+
isValidated?: boolean,
10+
onlyFetchIfValidated = false,
911
): FetchState<CatalogArtifactList> => {
1012
const { api, apiAvailable } = useModelCatalogAPI();
1113

@@ -20,9 +22,12 @@ export const useCatalogModelArtifacts = (
2022
if (!modelName) {
2123
return Promise.reject(new NotReadyError('No model name'));
2224
}
25+
if (onlyFetchIfValidated && !isValidated) {
26+
return Promise.reject(new NotReadyError('Model is not validated'));
27+
}
2328
return api.getListCatalogModelArtifacts(opts, sourceId, modelName);
2429
},
25-
[api, apiAvailable, sourceId, modelName],
30+
[apiAvailable, sourceId, modelName, isValidated, api, onlyFetchIfValidated],
2631
);
2732
return useFetchState(
2833
call,

0 commit comments

Comments
 (0)