Skip to content

Commit 23dccda

Browse files
authored
fix(ai-integrations): Properly generate user, license, and authentication in catalog entities (#704)
* fix(ai-integrations): Properly generate user, license, and authentication in catalog entities Signed-off-by: John Collier <jcollier@redhat.com> * Add changeset Signed-off-by: John Collier <jcollier@redhat.com> * Update tests Signed-off-by: John Collier <jcollier@redhat.com> --------- Signed-off-by: John Collier <jcollier@redhat.com>
1 parent 1f689a6 commit 23dccda

4 files changed

Lines changed: 154 additions & 13 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-catalog-backend-module-model-catalog': patch
3+
---
4+
5+
Bug fixes to the model catalog plugin to resolve issues setting the user, license URL and authentication metadata in the generated catalog entities

workspaces/ai-integrations/plugins/catalog-backend-module-model-catalog/src/clients/ModelCatalogGenerator.test.ts

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('Model Catalog Generator', () => {
5454
},
5555
spec: {
5656
dependencyOf: [],
57-
owner: 'example-user',
57+
owner: 'user:example-user',
5858
type: 'ai-model',
5959
},
6060
},
@@ -94,7 +94,7 @@ describe('Model Catalog Generator', () => {
9494
},
9595
spec: {
9696
dependencyOf: [],
97-
owner: 'example-user',
97+
owner: 'user:example-user',
9898
type: 'ai-model',
9999
},
100100
},
@@ -134,7 +134,7 @@ describe('Model Catalog Generator', () => {
134134
},
135135
spec: {
136136
dependencyOf: [],
137-
owner: 'example-user',
137+
owner: 'user:example-user',
138138
type: 'ai-model',
139139
},
140140
},
@@ -156,6 +156,7 @@ describe('Model Catalog Generator', () => {
156156
tags: ['openapi', 'openai', '3scale'],
157157
},
158158
lifecycle: 'production',
159+
authentication: true,
159160
},
160161
models: [
161162
{
@@ -166,6 +167,7 @@ describe('Model Catalog Generator', () => {
166167
tags: ['ibm', 'granite', 'vllm', '20b'],
167168
owner: 'example-user',
168169
lifecycle: 'production',
170+
license: 'https://www.apache.org/licenses/LICENSE-2.0',
169171
},
170172
{
171173
name: 'mistral-7b',
@@ -204,10 +206,14 @@ describe('Model Catalog Generator', () => {
204206
url: 'https://huggingface.co/ibm-granite/granite-20b-code-instruct',
205207
title: 'Artifact Location',
206208
},
209+
{
210+
url: 'https://www.apache.org/licenses/LICENSE-2.0',
211+
title: 'License',
212+
},
207213
],
208214
},
209215
spec: {
210-
owner: 'example-user',
216+
owner: 'user:example-user',
211217
type: 'ai-model',
212218
dependencyOf: ['component:developer-model-service'],
213219
},
@@ -227,7 +233,7 @@ describe('Model Catalog Generator', () => {
227233
],
228234
},
229235
spec: {
230-
owner: 'example-user',
236+
owner: 'user:example-user',
231237
type: 'ai-model',
232238
dependencyOf: ['component:developer-model-service'],
233239
},
@@ -251,7 +257,7 @@ describe('Model Catalog Generator', () => {
251257
],
252258
},
253259
spec: {
254-
owner: 'example-user',
260+
owner: 'user:example-user',
255261
type: 'ai-model',
256262
dependencyOf: ['component:developer-model-service'],
257263
},
@@ -263,7 +269,7 @@ describe('Model Catalog Generator', () => {
263269
metadata: {
264270
name: 'developer-model-service',
265271
description: 'Developer model service running on vLLM',
266-
tags: ['vllm', 'granite', 'ibm'],
272+
tags: ['vllm', 'granite', 'ibm', 'auth-required'],
267273
links: [
268274
{
269275
url: 'https://api.example.com',
@@ -278,7 +284,7 @@ describe('Model Catalog Generator', () => {
278284
spec: {
279285
type: 'model-server',
280286
lifecycle: 'production',
281-
owner: 'example-user',
287+
owner: 'user:example-user',
282288
dependsOn: [
283289
'resource:ibm-granite-20b',
284290
'resource:mistral-7b',
@@ -293,7 +299,7 @@ describe('Model Catalog Generator', () => {
293299
kind: `API`,
294300
metadata: {
295301
name: 'developer-model-service',
296-
tags: ['openapi', 'openai', '3scale'],
302+
tags: ['openapi', 'openai', '3scale', 'auth-required'],
297303
links: [
298304
{
299305
url: `https://api.example.com`,
@@ -303,7 +309,119 @@ describe('Model Catalog Generator', () => {
303309
},
304310
spec: {
305311
type: 'openapi',
312+
owner: 'user:example-user',
313+
lifecycle: 'production',
314+
definition:
315+
'https://raw.githubusercontent.com/redhat-ai-dev/model-catalog-example/refs/heads/main/developer-model-service/openapi.json',
316+
},
317+
};
318+
const expectedEntities: Entity[] = expectedModelEntities;
319+
expectedEntities.push(expectedModelServerEntity);
320+
expectedEntities.push(expectedModelServerAPIEntity);
321+
expect(modelCatalogEntities).toEqual(expectedModelEntities);
322+
});
323+
it('should generate catalog entities for a model server that doesn not require authentication', () => {
324+
const modelCatalog: ModelCatalog = {
325+
modelServer: {
326+
name: 'developer-model-service',
306327
owner: 'example-user',
328+
description: 'Developer model service running on vLLM',
329+
homepageURL: 'https://example.com',
330+
tags: ['vllm', 'granite', 'ibm'],
331+
API: {
332+
url: 'https://api.example.com',
333+
type: Type.Openapi,
334+
spec: 'https://raw.githubusercontent.com/redhat-ai-dev/model-catalog-example/refs/heads/main/developer-model-service/openapi.json',
335+
tags: ['openapi', 'openai', '3scale'],
336+
},
337+
lifecycle: 'production',
338+
},
339+
models: [
340+
{
341+
name: 'ibm-granite-20b',
342+
description: 'IBM Granite 20b model running on vLLM',
343+
artifactLocationURL:
344+
'https://huggingface.co/ibm-granite/granite-20b-code-instruct',
345+
tags: ['ibm', 'granite', 'vllm', '20b'],
346+
owner: 'example-user',
347+
lifecycle: 'production',
348+
license: 'https://www.apache.org/licenses/LICENSE-2.0',
349+
},
350+
],
351+
};
352+
const modelCatalogEntities = GenerateCatalogEntities(modelCatalog);
353+
expect(modelCatalog.modelServer !== undefined).toBe(true);
354+
expect(modelCatalog.models.length).toBe(1);
355+
356+
const expectedModelEntities: Entity[] = [
357+
{
358+
apiVersion: 'backstage.io/v1beta1',
359+
kind: 'Resource',
360+
metadata: {
361+
name: 'ibm-granite-20b',
362+
description: 'IBM Granite 20b model running on vLLM',
363+
tags: ['ibm', 'granite', 'vllm', '20b'],
364+
links: [
365+
{
366+
url: 'https://huggingface.co/ibm-granite/granite-20b-code-instruct',
367+
title: 'Artifact Location',
368+
},
369+
{
370+
url: 'https://www.apache.org/licenses/LICENSE-2.0',
371+
title: 'License',
372+
},
373+
],
374+
},
375+
spec: {
376+
owner: 'user:example-user',
377+
type: 'ai-model',
378+
dependencyOf: ['component:developer-model-service'],
379+
},
380+
},
381+
];
382+
const expectedModelServerEntity: ComponentEntity = {
383+
apiVersion: 'backstage.io/v1beta1',
384+
kind: 'Component',
385+
metadata: {
386+
name: 'developer-model-service',
387+
description: 'Developer model service running on vLLM',
388+
tags: ['vllm', 'granite', 'ibm', 'auth-not-required'],
389+
links: [
390+
{
391+
url: 'https://api.example.com',
392+
title: 'API',
393+
},
394+
{
395+
url: 'https://example.com',
396+
title: 'Homepage',
397+
},
398+
],
399+
},
400+
spec: {
401+
type: 'model-server',
402+
lifecycle: 'production',
403+
owner: 'user:example-user',
404+
dependsOn: ['resource:ibm-granite-20b'],
405+
providesApis: ['developer-model-service'],
406+
},
407+
};
408+
409+
const expectedModelServerAPIEntity: ApiEntity = {
410+
apiVersion: `backstage.io/v1beta1`,
411+
kind: `API`,
412+
metadata: {
413+
name: 'developer-model-service',
414+
tags: ['openapi', 'openai', '3scale', 'auth-not-required'],
415+
links: [
416+
{
417+
url: `https://api.example.com`,
418+
title: `API`,
419+
},
420+
],
421+
},
422+
spec: {
423+
type: 'openapi',
424+
owner: 'user:example-user',
307425
lifecycle: 'production',
308426
definition:
309427
'https://raw.githubusercontent.com/redhat-ai-dev/model-catalog-example/refs/heads/main/developer-model-service/openapi.json',

workspaces/ai-integrations/plugins/catalog-backend-module-model-catalog/src/clients/ModelCatalogGenerator.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export function GenerateModelResourceEntities(
9595
links: [],
9696
},
9797
spec: {
98-
owner: `${model.owner}`,
98+
owner: `user:${model.owner}`,
9999
type: 'ai-model',
100100
},
101101
};
@@ -116,6 +116,12 @@ export function GenerateModelResourceEntities(
116116
url: `${model.howToUseURL}`,
117117
});
118118
}
119+
if (model.license !== undefined) {
120+
modelResourceEntity.metadata.links?.push({
121+
title: `License`,
122+
url: `${model.license}`,
123+
});
124+
}
119125
modelResourceEntity.spec.dependencyOf = [];
120126
if (modelServer !== undefined) {
121127
modelResourceEntity.spec.dependencyOf?.push(
@@ -160,7 +166,7 @@ export function GenerateModelServerComponentEntity(
160166
spec: {
161167
type: 'model-server',
162168
lifecycle: `${modelServer.lifecycle}`,
163-
owner: `${modelServer.owner}`,
169+
owner: `user:${modelServer.owner}`,
164170
},
165171
};
166172

@@ -175,6 +181,12 @@ export function GenerateModelServerComponentEntity(
175181
if (modelServer.tags !== undefined) {
176182
modelServerComponent.metadata.tags = sanitizeTags(modelServer.tags, logger);
177183
}
184+
// Add authentication tag
185+
if (modelServer.authentication === undefined || !modelServer.authentication) {
186+
modelServerComponent.metadata.tags?.push('auth-not-required');
187+
} else {
188+
modelServerComponent.metadata.tags?.push('auth-required');
189+
}
178190
modelServerComponent.metadata.links = [];
179191
if (modelServer.API !== undefined) {
180192
modelServerComponent.metadata.links.push({
@@ -212,7 +224,7 @@ export function GenerateModelServerAPI(
212224
},
213225
spec: {
214226
type: `${api.type}`,
215-
owner: `${modelServer.owner}`,
227+
owner: `user:${modelServer.owner}`,
216228
lifecycle: `${modelServer.lifecycle}`,
217229
definition: `${api.spec}`,
218230
},
@@ -221,6 +233,12 @@ export function GenerateModelServerAPI(
221233
if (api.tags !== undefined) {
222234
modelServerAPIEntity.metadata.tags = sanitizeTags(api.tags, logger);
223235
}
236+
// Add authentication tag
237+
if (modelServer.authentication === undefined || !modelServer.authentication) {
238+
modelServerAPIEntity.metadata.tags?.push('auth-not-required');
239+
} else {
240+
modelServerAPIEntity.metadata.tags?.push('auth-required');
241+
}
224242
return modelServerAPIEntity;
225243
}
226244

workspaces/ai-integrations/plugins/catalog-backend-module-model-catalog/src/providers/__snapshots__/ModelCatalogResourceEntityProvider.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ exports[`ModelCatalogResourceEntityProvider should connect and run should resolv
2121
},
2222
"spec": {
2323
"dependencyOf": [],
24-
"owner": "example-user",
24+
"owner": "user:example-user",
2525
"type": "ai-model",
2626
},
2727
},

0 commit comments

Comments
 (0)