@red-hat-developer-hub/backstage-plugin-catalog-backend-module-model-catalog 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/README.md +5 -0
- package/dist/clients/BridgeResourceConnector.cjs.js +26 -0
- package/dist/clients/BridgeResourceConnector.cjs.js.map +1 -0
- package/dist/clients/ModelCatalogGenerator.cjs.js +202 -0
- package/dist/clients/ModelCatalogGenerator.cjs.js.map +1 -0
- package/dist/index.cjs.js +13 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/module.cjs.js +37 -0
- package/dist/module.cjs.js.map +1 -0
- package/dist/providers/ModelCatalogResourceEntityProvider.cjs.js +229 -0
- package/dist/providers/ModelCatalogResourceEntityProvider.cjs.js.map +1 -0
- package/dist/providers/config.cjs.js +29 -0
- package/dist/providers/config.cjs.js.map +1 -0
- package/package.json +65 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @red-hat-developer-hub/backstage-plugin-catalog-backend-module-model-catalog
|
|
2
|
+
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 7b81c3d: copy model/modelServer annotations to resource/component annotations
|
|
8
|
+
|
|
9
|
+
## 0.6.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 986f8ad: Backstage version bump to v1.42.5
|
|
14
|
+
|
|
15
|
+
## 0.5.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 7dc735d: a new backstage plugin, a sevice factory, is delivered to provide a URLReader that interacts with the model catalog bridges techdoc/model card endpoint; the catalog backend module is also updated so that the bridge configration information is exported and reusable by the techdoc plugin
|
|
20
|
+
|
|
21
|
+
## 0.4.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 21f35a2: Add TechDocs annotations to generated Resource entities when present in the JSON object
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- f99bda6: add a log message around registering the 'rhdh-rhoai-bridge' location type
|
|
30
|
+
- 23dccda: Bug fixes to the model catalog plugin to resolve issues setting the user, license URL and authentication metadata in the generated catalog entities
|
|
31
|
+
|
|
32
|
+
## 0.3.0
|
|
33
|
+
|
|
34
|
+
### Minor Changes
|
|
35
|
+
|
|
36
|
+
- 340bf72: simplifying model catalog entity provider configuration until running the bridge as a pod is officially supported
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- 13a7a3a: Add null check when retrieving keys from catalog bridge
|
|
41
|
+
|
|
42
|
+
## 0.2.0
|
|
43
|
+
|
|
44
|
+
### Minor Changes
|
|
45
|
+
|
|
46
|
+
- 4273bc9: Switch model catalog entity provider and custom processor to use JSON object provided by the bridge
|
|
47
|
+
|
|
48
|
+
## 0.1.1
|
|
49
|
+
|
|
50
|
+
### Patch Changes
|
|
51
|
+
|
|
52
|
+
- ff20aa6: Initial code drop of prototype model catalog plugin originally from redhat-ai-dev fork
|
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function fetchModelCatalogKeys(baseUrl) {
|
|
4
|
+
const res = await fetch(`${baseUrl}/list`, {
|
|
5
|
+
method: "GET"
|
|
6
|
+
});
|
|
7
|
+
if (!res.ok) {
|
|
8
|
+
throw new Error(res.statusText);
|
|
9
|
+
}
|
|
10
|
+
const modelCatalogKeys = await res.json();
|
|
11
|
+
return modelCatalogKeys.uris;
|
|
12
|
+
}
|
|
13
|
+
async function fetchModelCatalogFromKey(baseUrl, modelCatalogKey) {
|
|
14
|
+
const res = await fetch(`${baseUrl}${modelCatalogKey}`, {
|
|
15
|
+
method: "GET"
|
|
16
|
+
});
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
throw new Error(res.statusText);
|
|
19
|
+
}
|
|
20
|
+
const modelCatalog = await res.json();
|
|
21
|
+
return modelCatalog;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
exports.fetchModelCatalogFromKey = fetchModelCatalogFromKey;
|
|
25
|
+
exports.fetchModelCatalogKeys = fetchModelCatalogKeys;
|
|
26
|
+
//# sourceMappingURL=BridgeResourceConnector.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BridgeResourceConnector.cjs.js","sources":["../../src/clients/BridgeResourceConnector.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ModelCatalog } from '@redhat-ai-dev/model-catalog-types';\n\n/**\n * ModelCatalogKeys is the object used by the bridge to return the list of keys corresponding to each model catalog entry in the bridge\n * @public\n */\nexport interface ModelCatalogKeys {\n uris: string[];\n}\n\n/**\n * fetchModelCatalogKeys retrieves the keys for each of the model catalog entries in the model catalog bridge service\n * @public\n */\nexport async function fetchModelCatalogKeys(\n baseUrl: string,\n): Promise<string[]> {\n // ToDo: Discover catalog-info endpoint?\n const res = await fetch(`${baseUrl}/list`, {\n method: 'GET',\n });\n\n if (!res.ok) {\n throw new Error(res.statusText);\n }\n\n const modelCatalogKeys: ModelCatalogKeys = await res.json();\n return modelCatalogKeys.uris;\n}\n\n/**\n * fetchModelCatalogFromKey fetches a model catalog JSON object from the bridge based on a given key\n * @public\n */\nexport async function fetchModelCatalogFromKey(\n baseUrl: string,\n modelCatalogKey: string,\n): Promise<ModelCatalog> {\n const res = await fetch(`${baseUrl}${modelCatalogKey}`, {\n method: 'GET',\n });\n\n if (!res.ok) {\n throw new Error(res.statusText);\n }\n\n const modelCatalog: ModelCatalog = await res.json();\n return modelCatalog;\n}\n"],"names":[],"mappings":";;AA6BA,eAAsB,sBACpB,OACmB,EAAA;AAEnB,EAAA,MAAM,GAAM,GAAA,MAAM,KAAM,CAAA,CAAA,EAAG,OAAO,CAAS,KAAA,CAAA,EAAA;AAAA,IACzC,MAAQ,EAAA;AAAA,GACT,CAAA;AAED,EAAI,IAAA,CAAC,IAAI,EAAI,EAAA;AACX,IAAM,MAAA,IAAI,KAAM,CAAA,GAAA,CAAI,UAAU,CAAA;AAAA;AAGhC,EAAM,MAAA,gBAAA,GAAqC,MAAM,GAAA,CAAI,IAAK,EAAA;AAC1D,EAAA,OAAO,gBAAiB,CAAA,IAAA;AAC1B;AAMsB,eAAA,wBAAA,CACpB,SACA,eACuB,EAAA;AACvB,EAAA,MAAM,MAAM,MAAM,KAAA,CAAM,GAAG,OAAO,CAAA,EAAG,eAAe,CAAI,CAAA,EAAA;AAAA,IACtD,MAAQ,EAAA;AAAA,GACT,CAAA;AAED,EAAI,IAAA,CAAC,IAAI,EAAI,EAAA;AACX,IAAM,MAAA,IAAI,KAAM,CAAA,GAAA,CAAI,UAAU,CAAA;AAAA;AAGhC,EAAM,MAAA,YAAA,GAA6B,MAAM,GAAA,CAAI,IAAK,EAAA;AAClD,EAAO,OAAA,YAAA;AACT;;;;;"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var catalogModel = require('@backstage/catalog-model');
|
|
4
|
+
|
|
5
|
+
function GenerateCatalogEntities(modelCatalog, logger) {
|
|
6
|
+
let modelCatalogEntities = [];
|
|
7
|
+
const models = modelCatalog.models;
|
|
8
|
+
modelCatalogEntities = modelCatalogEntities.concat(
|
|
9
|
+
GenerateModelResourceEntities(models, modelCatalog.modelServer)
|
|
10
|
+
);
|
|
11
|
+
if (modelCatalog.modelServer !== void 0) {
|
|
12
|
+
const modelServer = modelCatalog.modelServer;
|
|
13
|
+
modelCatalogEntities.push(
|
|
14
|
+
GenerateModelServerComponentEntity(
|
|
15
|
+
modelServer,
|
|
16
|
+
modelCatalog.models)
|
|
17
|
+
);
|
|
18
|
+
if (modelServer.API !== void 0) {
|
|
19
|
+
const api = modelServer.API;
|
|
20
|
+
modelCatalogEntities.push(
|
|
21
|
+
GenerateModelServerAPI(api, modelServer)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return modelCatalogEntities;
|
|
26
|
+
}
|
|
27
|
+
function GenerateModelResourceEntities(models, modelServer, logger) {
|
|
28
|
+
const modelResourceEntities = [];
|
|
29
|
+
models.forEach((model) => {
|
|
30
|
+
const modelResourceEntity = {
|
|
31
|
+
apiVersion: "backstage.io/v1beta1",
|
|
32
|
+
kind: "Resource",
|
|
33
|
+
metadata: {
|
|
34
|
+
name: sanitizeMetadataName(model.name),
|
|
35
|
+
description: `${model.description}`,
|
|
36
|
+
tags: [],
|
|
37
|
+
links: []
|
|
38
|
+
},
|
|
39
|
+
spec: {
|
|
40
|
+
owner: `user:${model.owner}`,
|
|
41
|
+
type: "ai-model"
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
if (model.tags !== void 0) {
|
|
45
|
+
modelResourceEntity.metadata.tags = sanitizeTags(model.tags);
|
|
46
|
+
}
|
|
47
|
+
if (model.artifactLocationURL !== void 0) {
|
|
48
|
+
modelResourceEntity.metadata.links?.push({
|
|
49
|
+
title: "Artifact Location",
|
|
50
|
+
url: `${model.artifactLocationURL}`
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (model.howToUseURL !== void 0) {
|
|
54
|
+
modelResourceEntity.metadata.links?.push({
|
|
55
|
+
title: "How to use",
|
|
56
|
+
url: `${model.howToUseURL}`
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (model.license !== void 0) {
|
|
60
|
+
modelResourceEntity.metadata.links?.push({
|
|
61
|
+
title: `License`,
|
|
62
|
+
url: `${model.license}`
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
modelResourceEntity.spec.dependencyOf = [];
|
|
66
|
+
if (modelServer !== void 0) {
|
|
67
|
+
modelResourceEntity.spec.dependencyOf?.push(
|
|
68
|
+
`component:${modelServer.name}`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (model.annotations !== void 0) {
|
|
72
|
+
if (modelResourceEntity.metadata.annotations === void 0) {
|
|
73
|
+
modelResourceEntity.metadata.annotations = {};
|
|
74
|
+
}
|
|
75
|
+
Object.keys(model.annotations).forEach((key) => {
|
|
76
|
+
if (key === "TechDocs") {
|
|
77
|
+
let techdocsUrl = model.annotations[key];
|
|
78
|
+
techdocsUrl = techdocsUrl.trim();
|
|
79
|
+
if (techdocsUrl !== "") {
|
|
80
|
+
modelResourceEntity.metadata.annotations["backstage.io/techdocs-ref"] = `url:${techdocsUrl}`;
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
modelResourceEntity.metadata.annotations[key] = model.annotations[key];
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
modelResourceEntities.push(modelResourceEntity);
|
|
88
|
+
});
|
|
89
|
+
return modelResourceEntities;
|
|
90
|
+
}
|
|
91
|
+
function GenerateModelServerComponentEntity(modelServer, models, logger) {
|
|
92
|
+
const modelServerComponent = {
|
|
93
|
+
apiVersion: "backstage.io/v1beta1",
|
|
94
|
+
kind: "Component",
|
|
95
|
+
metadata: {
|
|
96
|
+
name: sanitizeMetadataName(modelServer.name),
|
|
97
|
+
description: `${modelServer.description}`,
|
|
98
|
+
tags: [],
|
|
99
|
+
links: []
|
|
100
|
+
},
|
|
101
|
+
spec: {
|
|
102
|
+
type: "model-server",
|
|
103
|
+
lifecycle: `${modelServer.lifecycle}`,
|
|
104
|
+
owner: `user:${modelServer.owner}`
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
modelServerComponent.spec.dependsOn = [];
|
|
108
|
+
models.forEach((model) => {
|
|
109
|
+
modelServerComponent.spec.dependsOn?.push(`resource:${model.name}`);
|
|
110
|
+
});
|
|
111
|
+
if (modelServer.tags !== void 0) {
|
|
112
|
+
modelServerComponent.metadata.tags = sanitizeTags(modelServer.tags);
|
|
113
|
+
}
|
|
114
|
+
if (modelServer.authentication === void 0 || !modelServer.authentication) {
|
|
115
|
+
modelServerComponent.metadata.tags?.push("auth-not-required");
|
|
116
|
+
} else {
|
|
117
|
+
modelServerComponent.metadata.tags?.push("auth-required");
|
|
118
|
+
}
|
|
119
|
+
modelServerComponent.metadata.links = [];
|
|
120
|
+
if (modelServer.API !== void 0) {
|
|
121
|
+
modelServerComponent.metadata.links.push({
|
|
122
|
+
title: `API`,
|
|
123
|
+
url: `${modelServer.API.url}`
|
|
124
|
+
});
|
|
125
|
+
modelServerComponent.spec.providesApis = [modelServer.name];
|
|
126
|
+
if (modelServer.API.annotations !== void 0) {
|
|
127
|
+
if (modelServerComponent.metadata.annotations === void 0) {
|
|
128
|
+
modelServerComponent.metadata.annotations = {};
|
|
129
|
+
}
|
|
130
|
+
Object.keys(modelServer.API.annotations).forEach((key) => {
|
|
131
|
+
modelServerComponent.metadata.annotations[key] = modelServer.API.annotations[key];
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (modelServer.homepageURL !== void 0) {
|
|
136
|
+
modelServerComponent.metadata.links.push({
|
|
137
|
+
title: "Homepage",
|
|
138
|
+
url: `${modelServer.homepageURL}`
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (modelServer.annotations !== void 0) {
|
|
142
|
+
if (modelServerComponent.metadata.annotations === void 0) {
|
|
143
|
+
modelServerComponent.metadata.annotations = {};
|
|
144
|
+
}
|
|
145
|
+
Object.keys(modelServer.annotations).forEach((key) => {
|
|
146
|
+
modelServerComponent.metadata.annotations[key] = modelServer.annotations[key];
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return modelServerComponent;
|
|
150
|
+
}
|
|
151
|
+
function GenerateModelServerAPI(api, modelServer, logger) {
|
|
152
|
+
const modelServerAPIEntity = {
|
|
153
|
+
apiVersion: `backstage.io/v1beta1`,
|
|
154
|
+
kind: `API`,
|
|
155
|
+
metadata: {
|
|
156
|
+
name: sanitizeMetadataName(modelServer.name),
|
|
157
|
+
tags: [],
|
|
158
|
+
links: [
|
|
159
|
+
{
|
|
160
|
+
url: `${api.url}`,
|
|
161
|
+
title: `API`
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
spec: {
|
|
166
|
+
type: `${api.type}`,
|
|
167
|
+
owner: `user:${modelServer.owner}`,
|
|
168
|
+
lifecycle: `${modelServer.lifecycle}`,
|
|
169
|
+
definition: `${api.spec}`
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
if (api.tags !== void 0) {
|
|
173
|
+
modelServerAPIEntity.metadata.tags = sanitizeTags(api.tags);
|
|
174
|
+
}
|
|
175
|
+
if (modelServer.authentication === void 0 || !modelServer.authentication) {
|
|
176
|
+
modelServerAPIEntity.metadata.tags?.push("auth-not-required");
|
|
177
|
+
} else {
|
|
178
|
+
modelServerAPIEntity.metadata.tags?.push("auth-required");
|
|
179
|
+
}
|
|
180
|
+
return modelServerAPIEntity;
|
|
181
|
+
}
|
|
182
|
+
function sanitizeMetadataName(modelName) {
|
|
183
|
+
return modelName.replace(/\s/g, "");
|
|
184
|
+
}
|
|
185
|
+
function sanitizeTags(tags, logger) {
|
|
186
|
+
const sanitizedTags = [];
|
|
187
|
+
tags.forEach((tag) => {
|
|
188
|
+
let sanitizedTag = tag;
|
|
189
|
+
sanitizedTag = sanitizedTag.replace(/\s/g, "");
|
|
190
|
+
if (!catalogModel.makeValidator().isValidTag(sanitizedTag)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
sanitizedTags.push(sanitizedTag);
|
|
194
|
+
});
|
|
195
|
+
return sanitizedTags;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
exports.GenerateCatalogEntities = GenerateCatalogEntities;
|
|
199
|
+
exports.GenerateModelResourceEntities = GenerateModelResourceEntities;
|
|
200
|
+
exports.GenerateModelServerAPI = GenerateModelServerAPI;
|
|
201
|
+
exports.GenerateModelServerComponentEntity = GenerateModelServerComponentEntity;
|
|
202
|
+
//# sourceMappingURL=ModelCatalogGenerator.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelCatalogGenerator.cjs.js","sources":["../../src/clients/ModelCatalogGenerator.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n ModelCatalog,\n Model,\n ModelServer,\n API,\n} from '@redhat-ai-dev/model-catalog-types';\nimport {\n Entity,\n ComponentEntity,\n ApiEntity,\n ResourceEntity,\n makeValidator,\n} from '@backstage/catalog-model';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nfunction isModelCatalog(o: any): o is ModelCatalog {\n return 'models' in o || 'modelServer' in o;\n}\n\nexport function ParseCatalogJSON(jsonStr: string): ModelCatalog {\n const modelCatalog: ModelCatalog = JSON.parse(jsonStr);\n if (isModelCatalog(modelCatalog)) {\n // do something with now correctly typed object\n return modelCatalog;\n }\n throw new Error(`model catalog JSON in unexpected format: ${jsonStr}`);\n}\n\n// Generate the Backstage catalog entities that correspond to the given model catalog json object\n// Note: These entities will need to have their location and origin location annotations set before ingestion into the catalog\n// An optional logger parameter can be passed if you wish log output from the generator\nexport function GenerateCatalogEntities(\n modelCatalog: ModelCatalog,\n logger?: LoggerService,\n): Entity[] {\n // Generate the Resource entities for the Model(s)\n let modelCatalogEntities: Entity[] = [];\n const models: Model[] = modelCatalog.models;\n modelCatalogEntities = modelCatalogEntities.concat(\n GenerateModelResourceEntities(models, modelCatalog.modelServer, logger),\n );\n\n // Generate the Model Server and Model Server APi entity, if present\n if (modelCatalog.modelServer !== undefined) {\n const modelServer = modelCatalog.modelServer;\n modelCatalogEntities.push(\n GenerateModelServerComponentEntity(\n modelServer,\n modelCatalog.models,\n logger,\n ),\n );\n\n // If there's an exposed API present, generate that entity as well, and update the model server entity accordingly\n if (modelServer.API !== undefined) {\n const api: API = modelServer.API;\n modelCatalogEntities.push(\n GenerateModelServerAPI(api, modelServer, logger),\n );\n }\n }\n\n return modelCatalogEntities;\n}\n\nexport function GenerateModelResourceEntities(\n models: Model[],\n modelServer?: ModelServer,\n logger?: LoggerService,\n): ResourceEntity[] {\n const modelResourceEntities: ResourceEntity[] = [];\n models.forEach(model => {\n const modelResourceEntity: ResourceEntity = {\n apiVersion: 'backstage.io/v1beta1',\n kind: 'Resource',\n metadata: {\n name: sanitizeMetadataName(model.name),\n description: `${model.description}`,\n tags: [],\n links: [],\n },\n spec: {\n owner: `user:${model.owner}`,\n type: 'ai-model',\n },\n };\n\n // Set optional parameters, if present\n if (model.tags !== undefined) {\n modelResourceEntity.metadata.tags = sanitizeTags(model.tags, logger);\n }\n if (model.artifactLocationURL !== undefined) {\n modelResourceEntity.metadata.links?.push({\n title: 'Artifact Location',\n url: `${model.artifactLocationURL}`,\n });\n }\n if (model.howToUseURL !== undefined) {\n modelResourceEntity.metadata.links?.push({\n title: 'How to use',\n url: `${model.howToUseURL}`,\n });\n }\n if (model.license !== undefined) {\n modelResourceEntity.metadata.links?.push({\n title: `License`,\n url: `${model.license}`,\n });\n }\n modelResourceEntity.spec.dependencyOf = [];\n if (modelServer !== undefined) {\n modelResourceEntity.spec.dependencyOf?.push(\n `component:${modelServer.name}`,\n );\n }\n\n // Handle any annotations present on the model resource\n if (model.annotations !== undefined) {\n // Initialize annotations object if needed\n if (modelResourceEntity.metadata.annotations === undefined) {\n modelResourceEntity.metadata.annotations = {};\n }\n\n // Copy all annotations from model to resource entity\n Object.keys(model.annotations).forEach(key => {\n // Special handling for TechDocs annotation\n if (key === 'TechDocs') {\n let techdocsUrl: string = model.annotations![key];\n techdocsUrl = techdocsUrl.trim();\n if (techdocsUrl !== '') {\n modelResourceEntity.metadata.annotations![\n 'backstage.io/techdocs-ref'\n ] = `url:${techdocsUrl}`;\n }\n } else {\n // Copy all other annotations as-is\n modelResourceEntity.metadata.annotations![key] =\n model.annotations![key];\n }\n });\n }\n modelResourceEntities.push(modelResourceEntity);\n });\n\n return modelResourceEntities;\n}\n\nexport function GenerateModelServerComponentEntity(\n modelServer: ModelServer,\n models: Model[],\n logger?: LoggerService,\n): ComponentEntity {\n const modelServerComponent: ComponentEntity = {\n apiVersion: 'backstage.io/v1beta1',\n kind: 'Component',\n metadata: {\n name: sanitizeMetadataName(modelServer.name),\n description: `${modelServer.description}`,\n tags: [],\n links: [],\n },\n spec: {\n type: 'model-server',\n lifecycle: `${modelServer.lifecycle}`,\n owner: `user:${modelServer.owner}`,\n },\n };\n\n // Add the models to the model server as dependants\n modelServerComponent.spec.dependsOn = [];\n models.forEach(model => {\n modelServerComponent.spec.dependsOn?.push(`resource:${model.name}`);\n });\n\n // Configure optional parameters\n // Set optional parameters, if present\n if (modelServer.tags !== undefined) {\n modelServerComponent.metadata.tags = sanitizeTags(modelServer.tags, logger);\n }\n // Add authentication tag\n if (modelServer.authentication === undefined || !modelServer.authentication) {\n modelServerComponent.metadata.tags?.push('auth-not-required');\n } else {\n modelServerComponent.metadata.tags?.push('auth-required');\n }\n modelServerComponent.metadata.links = [];\n if (modelServer.API !== undefined) {\n modelServerComponent.metadata.links.push({\n title: `API`,\n url: `${modelServer.API.url}`,\n });\n modelServerComponent.spec.providesApis = [modelServer.name];\n\n // Copy annotations from API to component metadata if they exist\n if (modelServer.API.annotations !== undefined) {\n if (modelServerComponent.metadata.annotations === undefined) {\n modelServerComponent.metadata.annotations = {};\n }\n // Copy all key-value pairs from API annotations to component annotations\n Object.keys(modelServer.API.annotations).forEach(key => {\n modelServerComponent.metadata.annotations![key] =\n modelServer.API!.annotations![key];\n });\n }\n }\n if (modelServer.homepageURL !== undefined) {\n modelServerComponent.metadata.links.push({\n title: 'Homepage',\n url: `${modelServer.homepageURL}`,\n });\n }\n\n // Copy annotations from modelServer to component metadata if they exist\n if (modelServer.annotations !== undefined) {\n if (modelServerComponent.metadata.annotations === undefined) {\n modelServerComponent.metadata.annotations = {};\n }\n // Copy all key-value pairs from modelServer annotations to component annotations\n Object.keys(modelServer.annotations).forEach(key => {\n modelServerComponent.metadata.annotations![key] =\n modelServer.annotations![key];\n });\n }\n\n return modelServerComponent;\n}\n\nexport function GenerateModelServerAPI(\n api: API,\n modelServer: ModelServer,\n logger?: LoggerService,\n): ApiEntity {\n const modelServerAPIEntity: ApiEntity = {\n apiVersion: `backstage.io/v1beta1`,\n kind: `API`,\n metadata: {\n name: sanitizeMetadataName(modelServer.name),\n tags: [],\n links: [\n {\n url: `${api.url}`,\n title: `API`,\n },\n ],\n },\n spec: {\n type: `${api.type}`,\n owner: `user:${modelServer.owner}`,\n lifecycle: `${modelServer.lifecycle}`,\n definition: `${api.spec}`,\n },\n };\n\n if (api.tags !== undefined) {\n modelServerAPIEntity.metadata.tags = sanitizeTags(api.tags, logger);\n }\n // Add authentication tag\n if (modelServer.authentication === undefined || !modelServer.authentication) {\n modelServerAPIEntity.metadata.tags?.push('auth-not-required');\n } else {\n modelServerAPIEntity.metadata.tags?.push('auth-required');\n }\n return modelServerAPIEntity;\n}\n\nfunction sanitizeMetadataName(modelName: string): string {\n return modelName.replace(/\\s/g, '');\n}\n\nfunction sanitizeTags(tags: string[], logger?: LoggerService): string[] {\n const sanitizedTags: string[] = [];\n tags.forEach(tag => {\n let sanitizedTag: string = tag;\n // Replace whitespace with empty character\n sanitizedTag = sanitizedTag.replace(/\\s/g, '');\n\n // Call the Backstage tag validator and check if the tag conforms\n // If the tag is not valid, skip it\n if (!makeValidator().isValidTag(sanitizedTag)) {\n if (logger !== undefined) {\n logger.error(\n `invalid tag: ${sanitizedTag}. Tags are expected to be less than 63 characters and conform to: ^[a-z0-9:+#]+(\\-[a-z0-9:+#]+)*$`,\n );\n }\n return; /* in typescript a return in a foreach skips to the next iteration */\n }\n sanitizedTags.push(sanitizedTag);\n });\n return sanitizedTags;\n}\n"],"names":["makeValidator"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var module$1 = require('./module.cjs.js');
|
|
6
|
+
var BridgeResourceConnector = require('./clients/BridgeResourceConnector.cjs.js');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports.default = module$1.catalogModuleModelCatalogResourceEntityProvider;
|
|
11
|
+
exports.fetchModelCatalogFromKey = BridgeResourceConnector.fetchModelCatalogFromKey;
|
|
12
|
+
exports.fetchModelCatalogKeys = BridgeResourceConnector.fetchModelCatalogKeys;
|
|
13
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
2
|
+
import { ModelCatalog } from '@redhat-ai-dev/model-catalog-types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* catalogModuleModelCatalogResourceEntityProvider defines the model catalog entity provider which runs on startup
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
declare const catalogModuleModelCatalogResourceEntityProvider: _backstage_backend_plugin_api.BackendFeature;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ModelCatalogKeys is the object used by the bridge to return the list of keys corresponding to each model catalog entry in the bridge
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
interface ModelCatalogKeys {
|
|
16
|
+
uris: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* fetchModelCatalogKeys retrieves the keys for each of the model catalog entries in the model catalog bridge service
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
declare function fetchModelCatalogKeys(baseUrl: string): Promise<string[]>;
|
|
23
|
+
/**
|
|
24
|
+
* fetchModelCatalogFromKey fetches a model catalog JSON object from the bridge based on a given key
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
declare function fetchModelCatalogFromKey(baseUrl: string, modelCatalogKey: string): Promise<ModelCatalog>;
|
|
28
|
+
|
|
29
|
+
export { catalogModuleModelCatalogResourceEntityProvider as default, fetchModelCatalogFromKey, fetchModelCatalogKeys };
|
|
30
|
+
export type { ModelCatalogKeys };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
+
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
5
|
+
var ModelCatalogResourceEntityProvider = require('./providers/ModelCatalogResourceEntityProvider.cjs.js');
|
|
6
|
+
|
|
7
|
+
const catalogModuleModelCatalogResourceEntityProvider = backendPluginApi.createBackendModule({
|
|
8
|
+
moduleId: "catalog-backend-module-model-catalog",
|
|
9
|
+
pluginId: "catalog",
|
|
10
|
+
register(env) {
|
|
11
|
+
env.registerInit({
|
|
12
|
+
deps: {
|
|
13
|
+
catalog: alpha.catalogProcessingExtensionPoint,
|
|
14
|
+
config: backendPluginApi.coreServices.rootConfig,
|
|
15
|
+
logger: backendPluginApi.coreServices.logger,
|
|
16
|
+
scheduler: backendPluginApi.coreServices.scheduler
|
|
17
|
+
},
|
|
18
|
+
async init({ catalog, config, logger, scheduler }) {
|
|
19
|
+
catalog.addEntityProvider(
|
|
20
|
+
ModelCatalogResourceEntityProvider.ModelCatalogResourceEntityProvider.fromConfig(
|
|
21
|
+
{ config, logger },
|
|
22
|
+
{
|
|
23
|
+
schedule: scheduler.createScheduledTaskRunner({
|
|
24
|
+
frequency: { seconds: 30 },
|
|
25
|
+
timeout: { minutes: 3 }
|
|
26
|
+
}),
|
|
27
|
+
scheduler
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
exports.catalogModuleModelCatalogResourceEntityProvider = catalogModuleModelCatalogResourceEntityProvider;
|
|
37
|
+
//# sourceMappingURL=module.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.cjs.js","sources":["../src/module.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';\n\nimport { ModelCatalogResourceEntityProvider } from './providers';\n\n/**\n * catalogModuleModelCatalogResourceEntityProvider defines the model catalog entity provider which runs on startup\n *\n * @public\n */\nexport const catalogModuleModelCatalogResourceEntityProvider =\n createBackendModule({\n moduleId: 'catalog-backend-module-model-catalog',\n pluginId: 'catalog',\n register(env) {\n env.registerInit({\n deps: {\n catalog: catalogProcessingExtensionPoint,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n },\n async init({ catalog, config, logger, scheduler }) {\n catalog.addEntityProvider(\n ModelCatalogResourceEntityProvider.fromConfig(\n { config, logger },\n {\n schedule: scheduler.createScheduledTaskRunner({\n frequency: { seconds: 30 },\n timeout: { minutes: 3 },\n }),\n scheduler: scheduler,\n },\n ),\n );\n },\n });\n },\n });\n"],"names":["createBackendModule","catalogProcessingExtensionPoint","coreServices","ModelCatalogResourceEntityProvider"],"mappings":";;;;;;AA4BO,MAAM,kDACXA,oCAAoB,CAAA;AAAA,EAClB,QAAU,EAAA,sCAAA;AAAA,EACV,QAAU,EAAA,SAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,OAAS,EAAAC,qCAAA;AAAA,QACT,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA,EAAE,SAAS,MAAQ,EAAA,MAAA,EAAQ,WAAa,EAAA;AACjD,QAAQ,OAAA,CAAA,iBAAA;AAAA,UACNC,qEAAmC,CAAA,UAAA;AAAA,YACjC,EAAE,QAAQ,MAAO,EAAA;AAAA,YACjB;AAAA,cACE,QAAA,EAAU,UAAU,yBAA0B,CAAA;AAAA,gBAC5C,SAAA,EAAW,EAAE,OAAA,EAAS,EAAG,EAAA;AAAA,gBACzB,OAAA,EAAS,EAAE,OAAA,EAAS,CAAE;AAAA,eACvB,CAAA;AAAA,cACD;AAAA;AACF;AACF,SACF;AAAA;AACF,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var catalogModel = require('@backstage/catalog-model');
|
|
4
|
+
var BridgeResourceConnector = require('../clients/BridgeResourceConnector.cjs.js');
|
|
5
|
+
var config = require('./config.cjs.js');
|
|
6
|
+
var errors = require('@backstage/errors');
|
|
7
|
+
var ModelCatalogGenerator = require('../clients/ModelCatalogGenerator.cjs.js');
|
|
8
|
+
|
|
9
|
+
class ModelCatalogResourceEntityProvider {
|
|
10
|
+
env;
|
|
11
|
+
baseUrl;
|
|
12
|
+
logger;
|
|
13
|
+
scheduleFn;
|
|
14
|
+
connection;
|
|
15
|
+
/**
|
|
16
|
+
* Fetches the configuration values specified for the entity provider in the app-config.yaml
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
static fromConfig(deps, options) {
|
|
20
|
+
const { config: config$1, logger } = deps;
|
|
21
|
+
const providerConfigs = config.readModelCatalogApiEntityConfigs(config$1);
|
|
22
|
+
return providerConfigs.map((providerConfig) => {
|
|
23
|
+
let taskRunner;
|
|
24
|
+
if ("scheduler" in options && providerConfig.schedule) {
|
|
25
|
+
taskRunner = options.scheduler.createScheduledTaskRunner(
|
|
26
|
+
providerConfig.schedule
|
|
27
|
+
);
|
|
28
|
+
} else if ("schedule" in options) {
|
|
29
|
+
taskRunner = options.schedule;
|
|
30
|
+
} else {
|
|
31
|
+
throw new errors.InputError(
|
|
32
|
+
`No schedule provided via config for ModelCatalogResourceEntityProvider:${providerConfig.id}.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return new ModelCatalogResourceEntityProvider(
|
|
36
|
+
providerConfig,
|
|
37
|
+
logger,
|
|
38
|
+
taskRunner
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** [1]: Set configuration values passed into the entity provider. Fields are defined in types.ts */
|
|
43
|
+
constructor(config, logger, taskRunner) {
|
|
44
|
+
this.env = config.id;
|
|
45
|
+
this.baseUrl = config.baseUrl;
|
|
46
|
+
this.logger = logger.child({
|
|
47
|
+
target: this.getProviderName()
|
|
48
|
+
});
|
|
49
|
+
this.scheduleFn = this.createScheduleFn(taskRunner);
|
|
50
|
+
}
|
|
51
|
+
/** Configure the schedule function and its logger */
|
|
52
|
+
createScheduleFn(taskRunner) {
|
|
53
|
+
return async () => {
|
|
54
|
+
const taskId = `${this.getProviderName()}:run`;
|
|
55
|
+
return taskRunner.run({
|
|
56
|
+
id: taskId,
|
|
57
|
+
fn: async () => {
|
|
58
|
+
try {
|
|
59
|
+
await this.run();
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (errors.isError(error)) {
|
|
62
|
+
this.logger.error(
|
|
63
|
+
`Error while syncing resources from Model Catalog ${this.baseUrl}`,
|
|
64
|
+
{
|
|
65
|
+
// Default Error properties:
|
|
66
|
+
name: error.name,
|
|
67
|
+
message: error.message,
|
|
68
|
+
stack: error.stack,
|
|
69
|
+
// Additional status code if available:
|
|
70
|
+
status: error.response?.status
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** [2]: Model Catalog entity provider must have a unique name */
|
|
80
|
+
getProviderName() {
|
|
81
|
+
return `ModelCatalogResourceEntityProvider:${this.env}`;
|
|
82
|
+
}
|
|
83
|
+
/** [3]: Connect Backstage catalog engine to ModelCatalogEntityProvider */
|
|
84
|
+
async connect(connection) {
|
|
85
|
+
this.connection = connection;
|
|
86
|
+
await this.scheduleFn();
|
|
87
|
+
}
|
|
88
|
+
/** [4]: Define the function that the entity provider will execute on a set schedule */
|
|
89
|
+
async run() {
|
|
90
|
+
if (!this.connection) {
|
|
91
|
+
throw new Error("Not initialized");
|
|
92
|
+
}
|
|
93
|
+
this.logger.info(
|
|
94
|
+
`Discovering ResourceEntities from Model Server ${this.baseUrl}`
|
|
95
|
+
);
|
|
96
|
+
let catalogKeys = await BridgeResourceConnector.fetchModelCatalogKeys(this.baseUrl);
|
|
97
|
+
if (catalogKeys === null || catalogKeys === void 0) {
|
|
98
|
+
catalogKeys = [];
|
|
99
|
+
}
|
|
100
|
+
let entityList = [];
|
|
101
|
+
this.logger.debug(`Found ${catalogKeys.length} model catalogs`);
|
|
102
|
+
this.logger.debug(`Fetched ModelCatalog: ${JSON.stringify(catalogKeys)}`);
|
|
103
|
+
await Promise.all(
|
|
104
|
+
catalogKeys.map(async (key) => {
|
|
105
|
+
const catalog = await BridgeResourceConnector.fetchModelCatalogFromKey(this.baseUrl, key);
|
|
106
|
+
const catalogEntities = ModelCatalogGenerator.GenerateCatalogEntities(catalog);
|
|
107
|
+
entityList = entityList.concat(catalogEntities);
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
entityList.forEach((entity) => {
|
|
111
|
+
if (entity.metadata.annotations === void 0) {
|
|
112
|
+
entity.metadata.annotations = {
|
|
113
|
+
[catalogModel.ANNOTATION_LOCATION]: this.getProviderName(),
|
|
114
|
+
[catalogModel.ANNOTATION_ORIGIN_LOCATION]: this.getProviderName()
|
|
115
|
+
};
|
|
116
|
+
} else {
|
|
117
|
+
entity.metadata.annotations[catalogModel.ANNOTATION_LOCATION] = this.getProviderName();
|
|
118
|
+
entity.metadata.annotations[catalogModel.ANNOTATION_ORIGIN_LOCATION] = this.getProviderName();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
this.logger.debug(`Found ${entityList.length} entities`);
|
|
122
|
+
this.logger.debug(`Fetched Entities: ${JSON.stringify(entityList)}`);
|
|
123
|
+
await this.connection.applyMutation({
|
|
124
|
+
type: "full",
|
|
125
|
+
entities: entityList.map((entity) => ({
|
|
126
|
+
entity,
|
|
127
|
+
locationKey: this.getProviderName()
|
|
128
|
+
}))
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/* private getEntityLocationRef(entity: LocationEntityV1alpha1): string {
|
|
132
|
+
const ref = entity.metadata.annotations?.[ANNOTATION_LOCATION];
|
|
133
|
+
if (!ref) {
|
|
134
|
+
throw new InputError(
|
|
135
|
+
`Entity '${entity.metadata.name}' does not have the annotation ${ANNOTATION_LOCATION}`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return ref;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private getLocationEntity(
|
|
142
|
+
urlStr: string,
|
|
143
|
+
): { entity: LocationEntityV1alpha1; locationKey: string }[] {
|
|
144
|
+
const entity = locationSpecToLocationEntity({
|
|
145
|
+
location: {
|
|
146
|
+
type: 'rhdh-rhoai-bridge',
|
|
147
|
+
target: urlStr,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
const locationKey = this.getEntityLocationRef(entity);
|
|
151
|
+
return [{ entity, locationKey }];
|
|
152
|
+
}*/
|
|
153
|
+
/* private convertModelNameToEntityName(modelName: string): string {
|
|
154
|
+
return modelName.replaceAll(/\:/g, '-');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private convertModeServerName(modelName: string): string {
|
|
158
|
+
return modelName.replaceAll(/\s/g, '-').toLowerCase();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private buildResourceEntityFromModelEndpoint(
|
|
162
|
+
models: ModelList,
|
|
163
|
+
modelServerName: string,
|
|
164
|
+
): ResourceEntity[] {
|
|
165
|
+
const modelResources: ResourceEntity[] = [];
|
|
166
|
+
models.data.forEach(model => {
|
|
167
|
+
const modelResourceEntity: ResourceEntity = {
|
|
168
|
+
kind: 'Resource',
|
|
169
|
+
apiVersion: 'backstage.io/v1alpha1',
|
|
170
|
+
metadata: {
|
|
171
|
+
annotations: {
|
|
172
|
+
[ANNOTATION_LOCATION]: this.getProviderName(),
|
|
173
|
+
[ANNOTATION_ORIGIN_LOCATION]: this.getProviderName(),
|
|
174
|
+
},
|
|
175
|
+
name: `${this.convertModelNameToEntityName(model.id)}`,
|
|
176
|
+
description: `STUB description`,
|
|
177
|
+
links: [
|
|
178
|
+
{
|
|
179
|
+
url: `${this.baseUrl}`,
|
|
180
|
+
title: 'Model API',
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
spec: {
|
|
185
|
+
type: `ai-model`,
|
|
186
|
+
owner: `${this.owner}`,
|
|
187
|
+
dependencyOf: [`component:${modelServerName}`],
|
|
188
|
+
...(this.system && { system: `${this.system}` }),
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
modelResources.push(modelResourceEntity);
|
|
192
|
+
});
|
|
193
|
+
return modelResources;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private buildComponentEntityFromModelEndpoint(
|
|
197
|
+
modelServerName: string,
|
|
198
|
+
): ComponentEntity {
|
|
199
|
+
const modelServerComponent: ComponentEntity = {
|
|
200
|
+
kind: 'Component',
|
|
201
|
+
apiVersion: 'backstage.io/v1alpha1',
|
|
202
|
+
metadata: {
|
|
203
|
+
annotations: {
|
|
204
|
+
[ANNOTATION_LOCATION]: this.getProviderName(),
|
|
205
|
+
[ANNOTATION_ORIGIN_LOCATION]: this.getProviderName(),
|
|
206
|
+
},
|
|
207
|
+
name: `${this.convertModeServerName(modelServerName)}`,
|
|
208
|
+
description: `${this.name}`,
|
|
209
|
+
links: [
|
|
210
|
+
{
|
|
211
|
+
url: `${this.baseUrl}`,
|
|
212
|
+
title: 'Model Server API',
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
spec: {
|
|
217
|
+
type: `model-server`,
|
|
218
|
+
lifecycle: 'production',
|
|
219
|
+
owner: `${this.owner}`,
|
|
220
|
+
...(this.system && { system: `${this.system}` }),
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
return modelServerComponent;
|
|
225
|
+
}*/
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
exports.ModelCatalogResourceEntityProvider = ModelCatalogResourceEntityProvider;
|
|
229
|
+
//# sourceMappingURL=ModelCatalogResourceEntityProvider.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelCatalogResourceEntityProvider.cjs.js","sources":["../../src/providers/ModelCatalogResourceEntityProvider.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type {\n LoggerService,\n SchedulerService,\n SchedulerServiceTaskRunner,\n} from '@backstage/backend-plugin-api';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n Entity,\n} from '@backstage/catalog-model';\n\nimport {\n fetchModelCatalogFromKey,\n fetchModelCatalogKeys,\n} from '../clients/BridgeResourceConnector';\nimport { ModelCatalogConfig } from './types';\nimport { readModelCatalogApiEntityConfigs } from './config';\nimport type { Config } from '@backstage/config';\nimport { InputError, isError } from '@backstage/errors';\nimport { GenerateCatalogEntities } from '../clients/ModelCatalogGenerator';\n/**\n * Provides entities from the model catalog service, allowing models and model servers to be imported into RHDH\n *\n * @public\n */\nexport class ModelCatalogResourceEntityProvider implements EntityProvider {\n private readonly env: string;\n private readonly baseUrl: string;\n private readonly logger: LoggerService;\n private readonly scheduleFn: () => Promise<void>;\n private connection?: EntityProviderConnection;\n\n /**\n * Fetches the configuration values specified for the entity provider in the app-config.yaml\n * @public\n */\n static fromConfig(\n deps: {\n config: Config;\n logger: LoggerService;\n },\n options:\n | { schedule: SchedulerServiceTaskRunner }\n | { scheduler: SchedulerService },\n ): ModelCatalogResourceEntityProvider[] {\n const { config, logger } = deps;\n\n const providerConfigs = readModelCatalogApiEntityConfigs(config);\n\n return providerConfigs.map(providerConfig => {\n let taskRunner;\n if ('scheduler' in options && providerConfig.schedule) {\n taskRunner = options.scheduler.createScheduledTaskRunner(\n providerConfig.schedule,\n );\n } else if ('schedule' in options) {\n taskRunner = options.schedule;\n } else {\n throw new InputError(\n `No schedule provided via config for ModelCatalogResourceEntityProvider:${providerConfig.id}.`,\n );\n }\n\n return new ModelCatalogResourceEntityProvider(\n providerConfig,\n logger,\n taskRunner,\n );\n });\n }\n /** [1]: Set configuration values passed into the entity provider. Fields are defined in types.ts */\n private constructor(\n config: ModelCatalogConfig,\n logger: LoggerService,\n taskRunner: SchedulerServiceTaskRunner,\n ) {\n this.env = config.id;\n this.baseUrl = config.baseUrl;\n this.logger = logger.child({\n target: this.getProviderName(),\n });\n this.scheduleFn = this.createScheduleFn(taskRunner);\n }\n\n /** Configure the schedule function and its logger */\n createScheduleFn(\n taskRunner: SchedulerServiceTaskRunner,\n ): () => Promise<void> {\n return async () => {\n const taskId = `${this.getProviderName()}:run`;\n return taskRunner.run({\n id: taskId,\n fn: async () => {\n try {\n await this.run();\n } catch (error) {\n if (isError(error)) {\n // Ensure that we don't log any sensitive internal data:\n this.logger.error(\n `Error while syncing resources from Model Catalog ${this.baseUrl}`,\n {\n // Default Error properties:\n name: error.name,\n message: error.message,\n stack: error.stack,\n // Additional status code if available:\n status: (error.response as { status?: string })?.status,\n },\n );\n }\n }\n },\n });\n };\n }\n\n /** [2]: Model Catalog entity provider must have a unique name */\n getProviderName(): string {\n return `ModelCatalogResourceEntityProvider:${this.env}`;\n }\n\n /** [3]: Connect Backstage catalog engine to ModelCatalogEntityProvider */\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n await this.scheduleFn();\n }\n\n /** [4]: Define the function that the entity provider will execute on a set schedule */\n async run(): Promise<void> {\n if (!this.connection) {\n throw new Error('Not initialized');\n }\n this.logger.info(\n `Discovering ResourceEntities from Model Server ${this.baseUrl}`,\n );\n\n /** [5]: Fetch the model catalog keys from the bridge and fetch the corresponding catalog entries. */\n let catalogKeys = await fetchModelCatalogKeys(this.baseUrl);\n // If no models are registered yet, the catalogKeys array may be null, so handle it by setting it to be an emptyList\n if (catalogKeys === null || catalogKeys === undefined) {\n catalogKeys = [];\n }\n let entityList: Entity[] = [];\n this.logger.debug(`Found ${catalogKeys.length} model catalogs`);\n this.logger.debug(`Fetched ModelCatalog: ${JSON.stringify(catalogKeys)}`);\n\n await Promise.all(\n catalogKeys.map(async key => {\n const catalog = await fetchModelCatalogFromKey(this.baseUrl, key);\n const catalogEntities = GenerateCatalogEntities(catalog);\n entityList = entityList.concat(catalogEntities);\n }),\n );\n\n entityList.forEach(entity => {\n if (entity.metadata.annotations === undefined) {\n entity.metadata.annotations = {\n [ANNOTATION_LOCATION]: this.getProviderName(),\n [ANNOTATION_ORIGIN_LOCATION]: this.getProviderName(),\n };\n } else {\n entity.metadata.annotations[ANNOTATION_LOCATION] =\n this.getProviderName();\n entity.metadata.annotations[ANNOTATION_ORIGIN_LOCATION] =\n this.getProviderName();\n }\n });\n this.logger.debug(`Found ${entityList.length} entities`);\n this.logger.debug(`Fetched Entities: ${JSON.stringify(entityList)}`);\n /** [6]: Add/update the catalog entities that correspond to the models */\n await this.connection.applyMutation({\n type: 'full',\n entities: entityList.map(entity => ({\n entity,\n locationKey: this.getProviderName(),\n })),\n });\n }\n\n /* private getEntityLocationRef(entity: LocationEntityV1alpha1): string {\n const ref = entity.metadata.annotations?.[ANNOTATION_LOCATION];\n if (!ref) {\n throw new InputError(\n `Entity '${entity.metadata.name}' does not have the annotation ${ANNOTATION_LOCATION}`,\n );\n }\n return ref;\n }\n\n private getLocationEntity(\n urlStr: string,\n ): { entity: LocationEntityV1alpha1; locationKey: string }[] {\n const entity = locationSpecToLocationEntity({\n location: {\n type: 'rhdh-rhoai-bridge',\n target: urlStr,\n },\n });\n const locationKey = this.getEntityLocationRef(entity);\n return [{ entity, locationKey }];\n }*/\n\n /* private convertModelNameToEntityName(modelName: string): string {\n return modelName.replaceAll(/\\:/g, '-');\n }\n\n private convertModeServerName(modelName: string): string {\n return modelName.replaceAll(/\\s/g, '-').toLowerCase();\n }\n\n private buildResourceEntityFromModelEndpoint(\n models: ModelList,\n modelServerName: string,\n ): ResourceEntity[] {\n const modelResources: ResourceEntity[] = [];\n models.data.forEach(model => {\n const modelResourceEntity: ResourceEntity = {\n kind: 'Resource',\n apiVersion: 'backstage.io/v1alpha1',\n metadata: {\n annotations: {\n [ANNOTATION_LOCATION]: this.getProviderName(),\n [ANNOTATION_ORIGIN_LOCATION]: this.getProviderName(),\n },\n name: `${this.convertModelNameToEntityName(model.id)}`,\n description: `STUB description`,\n links: [\n {\n url: `${this.baseUrl}`,\n title: 'Model API',\n },\n ],\n },\n spec: {\n type: `ai-model`,\n owner: `${this.owner}`,\n dependencyOf: [`component:${modelServerName}`],\n ...(this.system && { system: `${this.system}` }),\n },\n };\n modelResources.push(modelResourceEntity);\n });\n return modelResources;\n }\n\n private buildComponentEntityFromModelEndpoint(\n modelServerName: string,\n ): ComponentEntity {\n const modelServerComponent: ComponentEntity = {\n kind: 'Component',\n apiVersion: 'backstage.io/v1alpha1',\n metadata: {\n annotations: {\n [ANNOTATION_LOCATION]: this.getProviderName(),\n [ANNOTATION_ORIGIN_LOCATION]: this.getProviderName(),\n },\n name: `${this.convertModeServerName(modelServerName)}`,\n description: `${this.name}`,\n links: [\n {\n url: `${this.baseUrl}`,\n title: 'Model Server API',\n },\n ],\n },\n spec: {\n type: `model-server`,\n lifecycle: 'production',\n owner: `${this.owner}`,\n ...(this.system && { system: `${this.system}` }),\n },\n };\n\n return modelServerComponent;\n }*/\n}\n"],"names":["config","readModelCatalogApiEntityConfigs","InputError","isError","fetchModelCatalogKeys","fetchModelCatalogFromKey","GenerateCatalogEntities","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
+
|
|
5
|
+
function readModelCatalogApiEntityConfigs(config) {
|
|
6
|
+
const providerConfigs = config.getOptionalConfig(
|
|
7
|
+
"catalog.providers.modelCatalog"
|
|
8
|
+
);
|
|
9
|
+
if (!providerConfigs) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
return providerConfigs.keys().map(
|
|
13
|
+
(id) => readModelCatalogApiEntityConfig(id, providerConfigs.getConfig(id))
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
function readModelCatalogApiEntityConfig(id, config) {
|
|
17
|
+
const baseUrl = config.getString("baseUrl");
|
|
18
|
+
const schedule = config.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
|
|
19
|
+
config.getConfig("schedule")
|
|
20
|
+
) : void 0;
|
|
21
|
+
return {
|
|
22
|
+
id,
|
|
23
|
+
baseUrl,
|
|
24
|
+
schedule
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
exports.readModelCatalogApiEntityConfigs = readModelCatalogApiEntityConfigs;
|
|
29
|
+
//# sourceMappingURL=config.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.cjs.js","sources":["../../src/providers/config.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\n\nimport type { ModelCatalogConfig } from './types';\n\n/**\n * readModelCatalogApiEntityConfigs reads the configuration for the ModelCatalog provider\n *\n * @public\n */\nexport function readModelCatalogApiEntityConfigs(\n config: Config,\n): ModelCatalogConfig[] {\n const providerConfigs = config.getOptionalConfig(\n 'catalog.providers.modelCatalog',\n );\n if (!providerConfigs) {\n return [];\n }\n return providerConfigs\n .keys()\n .map(id =>\n readModelCatalogApiEntityConfig(id, providerConfigs.getConfig(id)),\n );\n}\n\nfunction readModelCatalogApiEntityConfig(\n id: string,\n config: Config,\n): ModelCatalogConfig {\n const baseUrl = config.getString('baseUrl');\n\n const schedule = config.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('schedule'),\n )\n : undefined;\n\n return {\n id,\n baseUrl,\n schedule,\n };\n}\n"],"names":["readSchedulerServiceTaskScheduleDefinitionFromConfig"],"mappings":";;;;AAyBO,SAAS,iCACd,MACsB,EAAA;AACtB,EAAA,MAAM,kBAAkB,MAAO,CAAA,iBAAA;AAAA,IAC7B;AAAA,GACF;AACA,EAAA,IAAI,CAAC,eAAiB,EAAA;AACpB,IAAA,OAAO,EAAC;AAAA;AAEV,EAAO,OAAA,eAAA,CACJ,MACA,CAAA,GAAA;AAAA,IAAI,QACH,+BAAgC,CAAA,EAAA,EAAI,eAAgB,CAAA,SAAA,CAAU,EAAE,CAAC;AAAA,GACnE;AACJ;AAEA,SAAS,+BAAA,CACP,IACA,MACoB,EAAA;AACpB,EAAM,MAAA,OAAA,GAAU,MAAO,CAAA,SAAA,CAAU,SAAS,CAAA;AAE1C,EAAA,MAAM,QAAW,GAAA,MAAA,CAAO,GAAI,CAAA,UAAU,CAClC,GAAAA,qEAAA;AAAA,IACE,MAAA,CAAO,UAAU,UAAU;AAAA,GAE7B,GAAA,MAAA;AAEJ,EAAO,OAAA;AAAA,IACL,EAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@red-hat-developer-hub/backstage-plugin-catalog-backend-module-model-catalog",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"description": "The model-catalog backend module for the catalog plugin.",
|
|
6
|
+
"main": "dist/index.cjs.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public",
|
|
10
|
+
"main": "dist/index.cjs.js",
|
|
11
|
+
"types": "dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/redhat-developer/rhdh-plugins",
|
|
16
|
+
"directory": "workspaces/ai-integrations/plugins/catalog-backend-module-model-catalog"
|
|
17
|
+
},
|
|
18
|
+
"backstage": {
|
|
19
|
+
"role": "backend-plugin-module",
|
|
20
|
+
"pluginId": "catalog",
|
|
21
|
+
"pluginPackage": "@backstage/plugin-catalog-backend",
|
|
22
|
+
"pluginPackages": [
|
|
23
|
+
"@red-hat-developer-hub/backstage-plugin-catalog-backend-module-model-catalog"
|
|
24
|
+
],
|
|
25
|
+
"features": {
|
|
26
|
+
".": "@backstage/BackendFeature"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"start": "backstage-cli package start",
|
|
31
|
+
"build": "backstage-cli package build",
|
|
32
|
+
"lint": "backstage-cli package lint",
|
|
33
|
+
"test": "backstage-cli package test",
|
|
34
|
+
"clean": "backstage-cli package clean",
|
|
35
|
+
"prepack": "backstage-cli package prepack",
|
|
36
|
+
"postpack": "backstage-cli package postpack"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@backstage/backend-plugin-api": "^1.4.2",
|
|
40
|
+
"@backstage/catalog-client": "^1.11.0",
|
|
41
|
+
"@backstage/catalog-model": "^1.7.5",
|
|
42
|
+
"@backstage/errors": "^1.2.7",
|
|
43
|
+
"@backstage/plugin-catalog-backend": "^3.0.1",
|
|
44
|
+
"@backstage/plugin-catalog-common": "^1.1.5",
|
|
45
|
+
"@backstage/plugin-catalog-node": "^1.18.0",
|
|
46
|
+
"@redhat-ai-dev/model-catalog-types": "^1.1.0",
|
|
47
|
+
"js-yaml": "^4.1.0",
|
|
48
|
+
"stream": "^0.0.3",
|
|
49
|
+
"yaml": "^2.7.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@backstage/backend-test-utils": "^1.8.0",
|
|
53
|
+
"@backstage/cli": "^0.34.1"
|
|
54
|
+
},
|
|
55
|
+
"files": [
|
|
56
|
+
"dist"
|
|
57
|
+
],
|
|
58
|
+
"typesVersions": {
|
|
59
|
+
"*": {
|
|
60
|
+
"package.json": [
|
|
61
|
+
"package.json"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|