@red-hat-developer-hub/backstage-plugin-catalog-backend-module-model-catalog 0.10.0 → 1.0.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 +26 -0
- package/dist/clients/ModelCatalogGenerator.cjs.js +58 -169
- package/dist/clients/ModelCatalogGenerator.cjs.js.map +1 -1
- package/dist/providers/ModelCatalogResourceEntityProvider.cjs.js +14 -9
- package/dist/providers/ModelCatalogResourceEntityProvider.cjs.js.map +1 -1
- package/dist/providers/config.cjs.js +0 -5
- package/dist/providers/config.cjs.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @red-hat-developer-hub/backstage-plugin-catalog-backend-module-model-catalog
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 8bf7bc3: Add AiModelServerAPI kind (`spec.type: ai-model-server`) in a dedicated
|
|
8
|
+
catalog-model package with types, JSON schema, KindValidator, type guard,
|
|
9
|
+
and CatalogModelLayer registration. Uses a dedicated kind to avoid
|
|
10
|
+
colliding with the upstream API kind; the schema mirrors
|
|
11
|
+
backstage/backstage#34476 exactly. The backend module registers the
|
|
12
|
+
AiModelServerAPI kind with the catalog via `catalogModelExtensionPoint`.
|
|
13
|
+
|
|
14
|
+
The model catalog entity provider now emits a single AiModelServerAPI
|
|
15
|
+
entity per model server instead of separate Component, Resource, and API
|
|
16
|
+
entities. Model names are collected into `spec.models.available`.
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [8bf7bc3]
|
|
21
|
+
- @red-hat-developer-hub/backstage-plugin-catalog-model-ai-model-server@0.2.0
|
|
22
|
+
|
|
23
|
+
## 0.11.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- 6c752b6: K8s credentials (url, serviceAccountToken, skipTLSVerify, caData) are now configurable via app-config.yaml, either directly or via kubernetesPluginRef. The `baseUrl` config field has been removed from model-catalog; the connector base URL is now resolved via Backstage's DiscoveryService.
|
|
28
|
+
|
|
3
29
|
## 0.10.0
|
|
4
30
|
|
|
5
31
|
### Minor Changes
|
|
@@ -2,190 +2,82 @@
|
|
|
2
2
|
|
|
3
3
|
var catalogModel = require('@backstage/catalog-model');
|
|
4
4
|
|
|
5
|
+
const TECHDOCS_KEY = "techdocs";
|
|
5
6
|
function GenerateCatalogEntities(modelCatalog, svcUrl, logger) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
modelCatalogEntities = modelCatalogEntities.concat(
|
|
9
|
-
GenerateModelResourceEntities(
|
|
10
|
-
models,
|
|
11
|
-
modelCatalog.modelServer,
|
|
12
|
-
logger,
|
|
13
|
-
svcUrl
|
|
14
|
-
)
|
|
15
|
-
);
|
|
16
|
-
if (modelCatalog.modelServer !== void 0) {
|
|
17
|
-
const modelServer = modelCatalog.modelServer;
|
|
18
|
-
modelCatalogEntities.push(
|
|
19
|
-
GenerateModelServerComponentEntity(
|
|
20
|
-
modelServer,
|
|
21
|
-
modelCatalog.models)
|
|
22
|
-
);
|
|
23
|
-
if (modelServer.API !== void 0) {
|
|
24
|
-
const api = modelServer.API;
|
|
25
|
-
modelCatalogEntities.push(
|
|
26
|
-
GenerateModelServerAPI(api, modelServer)
|
|
27
|
-
);
|
|
28
|
-
}
|
|
7
|
+
if (!modelCatalog.modelServer) {
|
|
8
|
+
return [];
|
|
29
9
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
function GenerateModelResourceEntities(models, modelServer, logger, svcUrl) {
|
|
33
|
-
const modelResourceEntities = [];
|
|
34
|
-
models.forEach((model) => {
|
|
35
|
-
const modelResourceEntity = {
|
|
36
|
-
apiVersion: "backstage.io/v1beta1",
|
|
37
|
-
kind: "Resource",
|
|
38
|
-
metadata: {
|
|
39
|
-
name: sanitizeMetadataName(model.name),
|
|
40
|
-
description: `${model.description}`,
|
|
41
|
-
tags: [],
|
|
42
|
-
links: []
|
|
43
|
-
},
|
|
44
|
-
spec: {
|
|
45
|
-
owner: `user:${model.owner}`,
|
|
46
|
-
type: "ai-model"
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
if (model.tags !== void 0) {
|
|
50
|
-
modelResourceEntity.metadata.tags = sanitizeTags(model.tags);
|
|
51
|
-
}
|
|
52
|
-
if (model.artifactLocationURL !== void 0) {
|
|
53
|
-
modelResourceEntity.metadata.links?.push({
|
|
54
|
-
title: "Artifact Location",
|
|
55
|
-
url: `${model.artifactLocationURL}`
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
if (model.howToUseURL !== void 0) {
|
|
59
|
-
modelResourceEntity.metadata.links?.push({
|
|
60
|
-
title: "How to use",
|
|
61
|
-
url: `${model.howToUseURL}`
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
if (model.license !== void 0) {
|
|
65
|
-
modelResourceEntity.metadata.links?.push({
|
|
66
|
-
title: `License`,
|
|
67
|
-
url: `${model.license}`
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
modelResourceEntity.spec.dependencyOf = [];
|
|
71
|
-
if (modelServer !== void 0) {
|
|
72
|
-
modelResourceEntity.spec.dependencyOf?.push(
|
|
73
|
-
`component:${modelServer.name}`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
if (model.annotations !== void 0) {
|
|
77
|
-
if (modelResourceEntity.metadata.annotations === void 0) {
|
|
78
|
-
modelResourceEntity.metadata.annotations = {};
|
|
79
|
-
}
|
|
80
|
-
Object.keys(model.annotations).forEach((key) => {
|
|
81
|
-
if (key === "TechDocs") {
|
|
82
|
-
let techdocsUrl = model.annotations[key];
|
|
83
|
-
techdocsUrl = techdocsUrl.trim();
|
|
84
|
-
if (techdocsUrl !== "") {
|
|
85
|
-
if (svcUrl !== void 0) {
|
|
86
|
-
techdocsUrl = svcUrl + techdocsUrl;
|
|
87
|
-
}
|
|
88
|
-
modelResourceEntity.metadata.annotations["backstage.io/techdocs-ref"] = `url:${techdocsUrl}`;
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
modelResourceEntity.metadata.annotations[key] = model.annotations[key];
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
modelResourceEntities.push(modelResourceEntity);
|
|
96
|
-
});
|
|
97
|
-
return modelResourceEntities;
|
|
98
|
-
}
|
|
99
|
-
function GenerateModelServerComponentEntity(modelServer, models, logger) {
|
|
100
|
-
const modelServerComponent = {
|
|
101
|
-
apiVersion: "backstage.io/v1beta1",
|
|
102
|
-
kind: "Component",
|
|
103
|
-
metadata: {
|
|
104
|
-
name: sanitizeMetadataName(modelServer.name),
|
|
105
|
-
description: `${modelServer.description}`,
|
|
106
|
-
tags: [],
|
|
107
|
-
links: []
|
|
108
|
-
},
|
|
109
|
-
spec: {
|
|
110
|
-
type: "model-server",
|
|
111
|
-
lifecycle: `${modelServer.lifecycle}`,
|
|
112
|
-
owner: `user:${modelServer.owner}`
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
modelServerComponent.spec.dependsOn = [];
|
|
116
|
-
models.forEach((model) => {
|
|
117
|
-
modelServerComponent.spec.dependsOn?.push(`resource:${model.name}`);
|
|
118
|
-
});
|
|
119
|
-
if (modelServer.tags !== void 0) {
|
|
120
|
-
modelServerComponent.metadata.tags = sanitizeTags(modelServer.tags);
|
|
10
|
+
if (!modelCatalog.modelServer.API?.url) {
|
|
11
|
+
return [];
|
|
121
12
|
}
|
|
13
|
+
const modelServer = modelCatalog.modelServer;
|
|
14
|
+
const models = modelCatalog.models;
|
|
15
|
+
const tags = sanitizeTags(modelServer.tags ?? []);
|
|
122
16
|
if (modelServer.authentication === void 0 || !modelServer.authentication) {
|
|
123
|
-
|
|
17
|
+
tags.push("auth-not-required");
|
|
124
18
|
} else {
|
|
125
|
-
|
|
19
|
+
tags.push("auth-required");
|
|
20
|
+
}
|
|
21
|
+
const links = [];
|
|
22
|
+
if (modelServer.API) {
|
|
23
|
+
links.push({ title: "API", url: modelServer.API.url });
|
|
126
24
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
25
|
+
if (modelServer.homepageURL) {
|
|
26
|
+
links.push({ title: "Homepage", url: modelServer.homepageURL });
|
|
27
|
+
}
|
|
28
|
+
const annotations = {};
|
|
29
|
+
if (modelServer.API?.annotations) {
|
|
30
|
+
Object.assign(annotations, modelServer.API.annotations);
|
|
31
|
+
}
|
|
32
|
+
if (modelServer.annotations) {
|
|
33
|
+
Object.assign(annotations, modelServer.annotations);
|
|
34
|
+
}
|
|
35
|
+
for (const model of models) {
|
|
36
|
+
if (model.annotations?.[TECHDOCS_KEY]) {
|
|
37
|
+
let techdocsUrl = model.annotations[TECHDOCS_KEY].trim();
|
|
38
|
+
if (techdocsUrl !== "") {
|
|
39
|
+
if (svcUrl && techdocsUrl.startsWith("/")) {
|
|
40
|
+
techdocsUrl = svcUrl + techdocsUrl;
|
|
41
|
+
}
|
|
42
|
+
annotations["backstage.io/techdocs-ref"] = `url:${techdocsUrl}`;
|
|
43
|
+
break;
|
|
137
44
|
}
|
|
138
|
-
Object.keys(modelServer.API.annotations).forEach((key) => {
|
|
139
|
-
modelServerComponent.metadata.annotations[key] = modelServer.API.annotations[key];
|
|
140
|
-
});
|
|
141
45
|
}
|
|
142
46
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (modelServer.annotations !== void 0) {
|
|
150
|
-
if (modelServerComponent.metadata.annotations === void 0) {
|
|
151
|
-
modelServerComponent.metadata.annotations = {};
|
|
47
|
+
for (const model of models) {
|
|
48
|
+
if (model.artifactLocationURL) {
|
|
49
|
+
links.push({
|
|
50
|
+
title: `${model.name} artifact`,
|
|
51
|
+
url: model.artifactLocationURL
|
|
52
|
+
});
|
|
152
53
|
}
|
|
153
|
-
Object.keys(modelServer.annotations).forEach((key) => {
|
|
154
|
-
modelServerComponent.metadata.annotations[key] = modelServer.annotations[key];
|
|
155
|
-
});
|
|
156
54
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const modelServerAPIEntity = {
|
|
161
|
-
apiVersion: `backstage.io/v1beta1`,
|
|
162
|
-
kind: `API`,
|
|
55
|
+
const entity = {
|
|
56
|
+
apiVersion: "backstage.io/v1alpha1",
|
|
57
|
+
kind: "AiModelServerAPI",
|
|
163
58
|
metadata: {
|
|
164
59
|
name: sanitizeMetadataName(modelServer.name),
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
title: `API`
|
|
170
|
-
}
|
|
171
|
-
]
|
|
60
|
+
description: modelServer.description,
|
|
61
|
+
tags,
|
|
62
|
+
links,
|
|
63
|
+
...Object.keys(annotations).length > 0 && { annotations }
|
|
172
64
|
},
|
|
173
65
|
spec: {
|
|
174
|
-
type:
|
|
66
|
+
type: "ai-model-server",
|
|
67
|
+
lifecycle: modelServer.lifecycle,
|
|
175
68
|
owner: `user:${modelServer.owner}`,
|
|
176
|
-
|
|
177
|
-
|
|
69
|
+
serverType: modelServer.API?.type ?? "unknown",
|
|
70
|
+
serverUrl: modelServer.API?.url ?? "",
|
|
71
|
+
requiresApiKey: modelServer.authentication ?? false,
|
|
72
|
+
models: {
|
|
73
|
+
available: models.map((m) => sanitizeMetadataName(m.name)),
|
|
74
|
+
...models.length > 0 && {
|
|
75
|
+
default: sanitizeMetadataName(models[0].name)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
178
78
|
}
|
|
179
79
|
};
|
|
180
|
-
|
|
181
|
-
modelServerAPIEntity.metadata.tags = sanitizeTags(api.tags);
|
|
182
|
-
}
|
|
183
|
-
if (modelServer.authentication === void 0 || !modelServer.authentication) {
|
|
184
|
-
modelServerAPIEntity.metadata.tags?.push("auth-not-required");
|
|
185
|
-
} else {
|
|
186
|
-
modelServerAPIEntity.metadata.tags?.push("auth-required");
|
|
187
|
-
}
|
|
188
|
-
return modelServerAPIEntity;
|
|
80
|
+
return [entity];
|
|
189
81
|
}
|
|
190
82
|
function sanitizeMetadataName(modelName) {
|
|
191
83
|
return modelName.replace(/\s/g, "");
|
|
@@ -204,7 +96,4 @@ function sanitizeTags(tags, logger) {
|
|
|
204
96
|
}
|
|
205
97
|
|
|
206
98
|
exports.GenerateCatalogEntities = GenerateCatalogEntities;
|
|
207
|
-
exports.GenerateModelResourceEntities = GenerateModelResourceEntities;
|
|
208
|
-
exports.GenerateModelServerAPI = GenerateModelServerAPI;
|
|
209
|
-
exports.GenerateModelServerComponentEntity = GenerateModelServerComponentEntity;
|
|
210
99
|
//# sourceMappingURL=ModelCatalogGenerator.cjs.js.map
|
|
@@ -1 +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 svcUrl?: string,\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(\n models,\n modelCatalog.modelServer,\n logger,\n svcUrl,\n ),\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 svcUrl?: string,\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 // TODO GGM prepend svc URL\n if (techdocsUrl !== '') {\n if (svcUrl !== undefined) {\n techdocsUrl = svcUrl + techdocsUrl;\n }\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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"}
|
|
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 { ModelCatalog } from '@redhat-ai-dev/model-catalog-types';\nimport { Entity, makeValidator } from '@backstage/catalog-model';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport type { AiModelServerApiEntity } from '@red-hat-developer-hub/backstage-plugin-catalog-model-ai-model-server';\n\nconst TECHDOCS_KEY = 'techdocs';\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 return modelCatalog;\n }\n throw new Error(`model catalog JSON in unexpected format: ${jsonStr}`);\n}\n\nexport function GenerateCatalogEntities(\n modelCatalog: ModelCatalog,\n svcUrl?: string,\n logger?: LoggerService,\n): Entity[] {\n if (!modelCatalog.modelServer) {\n logger?.debug(\n 'ModelCatalog has no modelServer; skipping AiModelServerAPI generation',\n );\n return [];\n }\n\n if (!modelCatalog.modelServer.API?.url) {\n logger?.debug(\n 'ModelCatalog modelServer has no API url; skipping AiModelServerAPI generation',\n );\n return [];\n }\n\n const modelServer = modelCatalog.modelServer;\n const models = modelCatalog.models;\n\n const tags = sanitizeTags(modelServer.tags ?? [], logger);\n if (modelServer.authentication === undefined || !modelServer.authentication) {\n tags.push('auth-not-required');\n } else {\n tags.push('auth-required');\n }\n\n const links: Array<{ title: string; url: string }> = [];\n if (modelServer.API) {\n links.push({ title: 'API', url: modelServer.API.url });\n }\n if (modelServer.homepageURL) {\n links.push({ title: 'Homepage', url: modelServer.homepageURL });\n }\n\n const annotations: Record<string, string> = {};\n\n // Copy API annotations\n if (modelServer.API?.annotations) {\n Object.assign(annotations, modelServer.API.annotations);\n }\n // Copy model server annotations (overrides API on conflict)\n if (modelServer.annotations) {\n Object.assign(annotations, modelServer.annotations);\n }\n\n // Collect techdocs from models — use the first one found\n for (const model of models) {\n if (model.annotations?.[TECHDOCS_KEY]) {\n let techdocsUrl = model.annotations[TECHDOCS_KEY].trim();\n if (techdocsUrl !== '') {\n if (svcUrl && techdocsUrl.startsWith('/')) {\n techdocsUrl = svcUrl + techdocsUrl;\n }\n annotations['backstage.io/techdocs-ref'] = `url:${techdocsUrl}`;\n break;\n }\n }\n }\n\n // Collect per-model artifact links\n for (const model of models) {\n if (model.artifactLocationURL) {\n links.push({\n title: `${model.name} artifact`,\n url: model.artifactLocationURL,\n });\n }\n }\n\n const entity: AiModelServerApiEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'AiModelServerAPI',\n metadata: {\n name: sanitizeMetadataName(modelServer.name),\n description: modelServer.description,\n tags,\n links,\n ...(Object.keys(annotations).length > 0 && { annotations }),\n },\n spec: {\n type: 'ai-model-server',\n lifecycle: modelServer.lifecycle,\n owner: `user:${modelServer.owner}`,\n serverType: modelServer.API?.type ?? 'unknown',\n serverUrl: modelServer.API?.url ?? '',\n requiresApiKey: modelServer.authentication ?? false,\n models: {\n available: models.map(m => sanitizeMetadataName(m.name)),\n ...(models.length > 0 && {\n default: sanitizeMetadataName(models[0].name),\n }),\n },\n },\n };\n\n return [entity];\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 sanitizedTag = sanitizedTag.replace(/\\s/g, '');\n\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;\n }\n sanitizedTags.push(sanitizedTag);\n });\n return sanitizedTags;\n}\n"],"names":["makeValidator"],"mappings":";;;;AAoBA,MAAM,YAAA,GAAe,UAAA;AAcd,SAAS,uBAAA,CACd,YAAA,EACA,MAAA,EACA,MAAA,EACU;AACV,EAAA,IAAI,CAAC,aAAa,WAAA,EAAa;AAI7B,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,IAAI,CAAC,YAAA,CAAa,WAAA,CAAY,GAAA,EAAK,GAAA,EAAK;AAItC,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,cAAc,YAAA,CAAa,WAAA;AACjC,EAAA,MAAM,SAAS,YAAA,CAAa,MAAA;AAE5B,EAAA,MAAM,OAAO,YAAA,CAAa,WAAA,CAAY,IAAA,IAAQ,EAAU,CAAA;AACxD,EAAA,IAAI,WAAA,CAAY,cAAA,KAAmB,MAAA,IAAa,CAAC,YAAY,cAAA,EAAgB;AAC3E,IAAA,IAAA,CAAK,KAAK,mBAAmB,CAAA;AAAA,EAC/B,CAAA,MAAO;AACL,IAAA,IAAA,CAAK,KAAK,eAAe,CAAA;AAAA,EAC3B;AAEA,EAAA,MAAM,QAA+C,EAAC;AACtD,EAAA,IAAI,YAAY,GAAA,EAAK;AACnB,IAAA,KAAA,CAAM,IAAA,CAAK,EAAE,KAAA,EAAO,KAAA,EAAO,KAAK,WAAA,CAAY,GAAA,CAAI,KAAK,CAAA;AAAA,EACvD;AACA,EAAA,IAAI,YAAY,WAAA,EAAa;AAC3B,IAAA,KAAA,CAAM,KAAK,EAAE,KAAA,EAAO,YAAY,GAAA,EAAK,WAAA,CAAY,aAAa,CAAA;AAAA,EAChE;AAEA,EAAA,MAAM,cAAsC,EAAC;AAG7C,EAAA,IAAI,WAAA,CAAY,KAAK,WAAA,EAAa;AAChC,IAAA,MAAA,CAAO,MAAA,CAAO,WAAA,EAAa,WAAA,CAAY,GAAA,CAAI,WAAW,CAAA;AAAA,EACxD;AAEA,EAAA,IAAI,YAAY,WAAA,EAAa;AAC3B,IAAA,MAAA,CAAO,MAAA,CAAO,WAAA,EAAa,WAAA,CAAY,WAAW,CAAA;AAAA,EACpD;AAGA,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,WAAA,GAAc,YAAY,CAAA,EAAG;AACrC,MAAA,IAAI,WAAA,GAAc,KAAA,CAAM,WAAA,CAAY,YAAY,EAAE,IAAA,EAAK;AACvD,MAAA,IAAI,gBAAgB,EAAA,EAAI;AACtB,QAAA,IAAI,MAAA,IAAU,WAAA,CAAY,UAAA,CAAW,GAAG,CAAA,EAAG;AACzC,UAAA,WAAA,GAAc,MAAA,GAAS,WAAA;AAAA,QACzB;AACA,QAAA,WAAA,CAAY,2BAA2B,CAAA,GAAI,CAAA,IAAA,EAAO,WAAW,CAAA,CAAA;AAC7D,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,MAAM,mBAAA,EAAqB;AAC7B,MAAA,KAAA,CAAM,IAAA,CAAK;AAAA,QACT,KAAA,EAAO,CAAA,EAAG,KAAA,CAAM,IAAI,CAAA,SAAA,CAAA;AAAA,QACpB,KAAK,KAAA,CAAM;AAAA,OACZ,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAAiC;AAAA,IACrC,UAAA,EAAY,uBAAA;AAAA,IACZ,IAAA,EAAM,kBAAA;AAAA,IACN,QAAA,EAAU;AAAA,MACR,IAAA,EAAM,oBAAA,CAAqB,WAAA,CAAY,IAAI,CAAA;AAAA,MAC3C,aAAa,WAAA,CAAY,WAAA;AAAA,MACzB,IAAA;AAAA,MACA,KAAA;AAAA,MACA,GAAI,OAAO,IAAA,CAAK,WAAW,EAAE,MAAA,GAAS,CAAA,IAAK,EAAE,WAAA;AAAY,KAC3D;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,IAAA,EAAM,iBAAA;AAAA,MACN,WAAW,WAAA,CAAY,SAAA;AAAA,MACvB,KAAA,EAAO,CAAA,KAAA,EAAQ,WAAA,CAAY,KAAK,CAAA,CAAA;AAAA,MAChC,UAAA,EAAY,WAAA,CAAY,GAAA,EAAK,IAAA,IAAQ,SAAA;AAAA,MACrC,SAAA,EAAW,WAAA,CAAY,GAAA,EAAK,GAAA,IAAO,EAAA;AAAA,MACnC,cAAA,EAAgB,YAAY,cAAA,IAAkB,KAAA;AAAA,MAC9C,MAAA,EAAQ;AAAA,QACN,WAAW,MAAA,CAAO,GAAA,CAAI,OAAK,oBAAA,CAAqB,CAAA,CAAE,IAAI,CAAC,CAAA;AAAA,QACvD,GAAI,MAAA,CAAO,MAAA,GAAS,CAAA,IAAK;AAAA,UACvB,OAAA,EAAS,oBAAA,CAAqB,MAAA,CAAO,CAAC,EAAE,IAAI;AAAA;AAC9C;AACF;AACF,GACF;AAEA,EAAA,OAAO,CAAC,MAAM,CAAA;AAChB;AAEA,SAAS,qBAAqB,SAAA,EAA2B;AACvD,EAAA,OAAO,SAAA,CAAU,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACpC;AAEA,SAAS,YAAA,CAAa,MAAgB,MAAA,EAAkC;AACtE,EAAA,MAAM,gBAA0B,EAAC;AACjC,EAAA,IAAA,CAAK,QAAQ,CAAA,GAAA,KAAO;AAClB,IAAA,IAAI,YAAA,GAAuB,GAAA;AAC3B,IAAA,YAAA,GAAe,YAAA,CAAa,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAE7C,IAAA,IAAI,CAACA,0BAAA,EAAc,CAAE,UAAA,CAAW,YAAY,CAAA,EAAG;AAM7C,MAAA;AAAA,IACF;AACA,IAAA,aAAA,CAAc,KAAK,YAAY,CAAA;AAAA,EACjC,CAAC,CAAA;AACD,EAAA,OAAO,aAAA;AACT;;;;"}
|
|
@@ -8,7 +8,6 @@ var ModelCatalogGenerator = require('../clients/ModelCatalogGenerator.cjs.js');
|
|
|
8
8
|
|
|
9
9
|
class ModelCatalogResourceEntityProvider {
|
|
10
10
|
name;
|
|
11
|
-
baseUrl;
|
|
12
11
|
logger;
|
|
13
12
|
discovery;
|
|
14
13
|
auth;
|
|
@@ -46,7 +45,6 @@ class ModelCatalogResourceEntityProvider {
|
|
|
46
45
|
/** [1]: Set configuration values passed into the entity provider. Fields are defined in types.ts */
|
|
47
46
|
constructor(config, logger, discovery, auth, taskRunner) {
|
|
48
47
|
this.name = config.id;
|
|
49
|
-
this.baseUrl = config.baseUrl;
|
|
50
48
|
this.discovery = discovery;
|
|
51
49
|
this.auth = auth;
|
|
52
50
|
this.logger = logger.child({
|
|
@@ -97,18 +95,25 @@ class ModelCatalogResourceEntityProvider {
|
|
|
97
95
|
throw new Error("Not initialized");
|
|
98
96
|
}
|
|
99
97
|
this.logger.info(
|
|
100
|
-
`Discovering
|
|
98
|
+
`Discovering AiModelServerAPI entities from Model Server ${this.name}`
|
|
101
99
|
);
|
|
102
|
-
const
|
|
103
|
-
let url = this.baseUrl;
|
|
104
|
-
if (svcUrl.length > 0 && url.length === 0) {
|
|
105
|
-
url = svcUrl;
|
|
106
|
-
}
|
|
100
|
+
const url = await this.discovery.getBaseUrl(this.name);
|
|
107
101
|
const backendToken = await this.auth.getPluginRequestToken({
|
|
108
102
|
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
|
109
103
|
targetPluginId: this.name
|
|
110
104
|
});
|
|
111
|
-
let catalogKeys
|
|
105
|
+
let catalogKeys;
|
|
106
|
+
try {
|
|
107
|
+
catalogKeys = await BridgeResourceConnector.fetchModelCatalogKeys(url, backendToken);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (errors.isError(error) && error.message === "Not Found") {
|
|
110
|
+
this.logger.debug(
|
|
111
|
+
`Connector plugin '${this.name}' not ready yet (404); will retry on next schedule`
|
|
112
|
+
);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
112
117
|
if (catalogKeys === null || catalogKeys === void 0) {
|
|
113
118
|
catalogKeys = [];
|
|
114
119
|
}
|
|
@@ -1 +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 AuthService,\n DiscoveryService,\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} 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 name: string;\n private readonly baseUrl: string;\n private readonly logger: LoggerService;\n private readonly discovery: DiscoveryService;\n private readonly auth: AuthService;\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 discovery: DiscoveryService;\n auth: AuthService;\n },\n options:\n | { schedule: SchedulerServiceTaskRunner }\n | { scheduler: SchedulerService },\n ): ModelCatalogResourceEntityProvider[] {\n const { config, logger, discovery, auth } = 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 discovery,\n auth,\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 discovery: DiscoveryService,\n auth: AuthService,\n taskRunner: SchedulerServiceTaskRunner,\n ) {\n this.name = config.id;\n this.baseUrl = config.baseUrl;\n this.discovery = discovery;\n this.auth = auth;\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.name}`,\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.name}`;\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.name}`,\n );\n\n /** [5]: Fetch the model catalog keys from the bridge and fetch the corresponding catalog entries. */\n // the api docs for this method says to run it each time vs. once during init, as the URL can change\n const svcUrl = await this.discovery.getBaseUrl(this.name);\n let url = this.baseUrl;\n if (svcUrl.length > 0 && url.length === 0) {\n url = svcUrl;\n }\n const backendToken = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: this.name,\n });\n let catalogKeys = await fetchModelCatalogKeys(url, backendToken);\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 this.logger.debug(`Found ${catalogKeys.length} model catalogs`);\n this.logger.debug(`Fetched ModelCatalog: ${JSON.stringify(catalogKeys)}`);\n\n const entityList = (\n await Promise.all(\n catalogKeys.map(async key => {\n const catalog = await fetchModelCatalogFromKey(\n url,\n key,\n backendToken,\n );\n return GenerateCatalogEntities(catalog, url);\n }),\n )\n ).flat();\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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"}
|
|
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 AuthService,\n DiscoveryService,\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} 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 name: string;\n private readonly logger: LoggerService;\n private readonly discovery: DiscoveryService;\n private readonly auth: AuthService;\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 discovery: DiscoveryService;\n auth: AuthService;\n },\n options:\n | { schedule: SchedulerServiceTaskRunner }\n | { scheduler: SchedulerService },\n ): ModelCatalogResourceEntityProvider[] {\n const { config, logger, discovery, auth } = 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 discovery,\n auth,\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 discovery: DiscoveryService,\n auth: AuthService,\n taskRunner: SchedulerServiceTaskRunner,\n ) {\n this.name = config.id;\n this.discovery = discovery;\n this.auth = auth;\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.name}`,\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.name}`;\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 AiModelServerAPI entities from Model Server ${this.name}`,\n );\n\n /** [5]: Fetch the model catalog keys from the bridge and fetch the corresponding catalog entries. */\n // the api docs for this method says to run it each time vs. once during init, as the URL can change\n const url = await this.discovery.getBaseUrl(this.name);\n const backendToken = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: this.name,\n });\n let catalogKeys: string[];\n try {\n catalogKeys = await fetchModelCatalogKeys(url, backendToken);\n } catch (error) {\n // 404 is expected during startup — the connector plugin hasn't\n // registered its routes yet. The scheduler will retry.\n if (isError(error) && error.message === 'Not Found') {\n this.logger.debug(\n `Connector plugin '${this.name}' not ready yet (404); will retry on next schedule`,\n );\n return;\n }\n throw error;\n }\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 this.logger.debug(`Found ${catalogKeys.length} model catalogs`);\n this.logger.debug(`Fetched ModelCatalog: ${JSON.stringify(catalogKeys)}`);\n\n const entityList = (\n await Promise.all(\n catalogKeys.map(async key => {\n const catalog = await fetchModelCatalogFromKey(\n url,\n key,\n backendToken,\n );\n return GenerateCatalogEntities(catalog, url);\n }),\n )\n ).flat();\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":";;;;;;;;AA6CO,MAAM,kCAAA,CAA6D;AAAA,EACvD,IAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,IAAA;AAAA,EACA,UAAA;AAAA,EACT,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,OAAO,UAAA,CACL,IAAA,EAMA,OAAA,EAGsC;AACtC,IAAA,MAAM,UAAEA,QAAA,EAAQ,MAAA,EAAQ,SAAA,EAAW,MAAK,GAAI,IAAA;AAE5C,IAAA,MAAM,eAAA,GAAkBC,wCAAiCD,QAAM,CAAA;AAE/D,IAAA,OAAO,eAAA,CAAgB,IAAI,CAAA,cAAA,KAAkB;AAC3C,MAAA,IAAI,UAAA;AACJ,MAAA,IAAI,WAAA,IAAe,OAAA,IAAW,cAAA,CAAe,QAAA,EAAU;AACrD,QAAA,UAAA,GAAa,QAAQ,SAAA,CAAU,yBAAA;AAAA,UAC7B,cAAA,CAAe;AAAA,SACjB;AAAA,MACF,CAAA,MAAA,IAAW,cAAc,OAAA,EAAS;AAChC,QAAA,UAAA,GAAa,OAAA,CAAQ,QAAA;AAAA,MACvB,CAAA,MAAO;AACL,QAAA,MAAM,IAAIE,iBAAA;AAAA,UACR,CAAA,uEAAA,EAA0E,eAAe,EAAE,CAAA,CAAA;AAAA,SAC7F;AAAA,MACF;AAEA,MAAA,OAAO,IAAI,kCAAA;AAAA,QACT,cAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,IAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAEQ,WAAA,CACN,MAAA,EACA,MAAA,EACA,SAAA,EACA,MACA,UAAA,EACA;AACA,IAAA,IAAA,CAAK,OAAO,MAAA,CAAO,EAAA;AACnB,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,OAAO,KAAA,CAAM;AAAA,MACzB,MAAA,EAAQ,KAAK,eAAA;AAAgB,KAC9B,CAAA;AACD,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA,CAAK,gBAAA,CAAiB,UAAU,CAAA;AAAA,EACpD;AAAA;AAAA,EAGA,iBACE,UAAA,EACqB;AACrB,IAAA,OAAO,YAAY;AACjB,MAAA,MAAM,MAAA,GAAS,CAAA,EAAG,IAAA,CAAK,eAAA,EAAiB,CAAA,IAAA,CAAA;AACxC,MAAA,OAAO,WAAW,GAAA,CAAI;AAAA,QACpB,EAAA,EAAI,MAAA;AAAA,QACJ,IAAI,YAAY;AACd,UAAA,IAAI;AACF,YAAA,MAAM,KAAK,GAAA,EAAI;AAAA,UACjB,SAAS,KAAA,EAAO;AACd,YAAA,IAAIC,cAAA,CAAQ,KAAK,CAAA,EAAG;AAElB,cAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,gBACV,CAAA,iDAAA,EAAoD,KAAK,IAAI,CAAA,CAAA;AAAA,gBAC7D;AAAA;AAAA,kBAEE,MAAM,KAAA,CAAM,IAAA;AAAA,kBACZ,SAAS,KAAA,CAAM,OAAA;AAAA,kBACf,OAAO,KAAA,CAAM,KAAA;AAAA;AAAA,kBAEb,MAAA,EAAS,MAAM,QAAA,EAAkC;AAAA;AACnD,eACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,OACD,CAAA;AAAA,IACH,CAAA;AAAA,EACF;AAAA;AAAA,EAGA,eAAA,GAA0B;AACxB,IAAA,OAAO,CAAA,mCAAA,EAAsC,KAAK,IAAI,CAAA,CAAA;AAAA,EACxD;AAAA;AAAA,EAGA,MAAM,QAAQ,UAAA,EAAqD;AACjE,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,MAAM,KAAK,UAAA,EAAW;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,GAAA,GAAqB;AACzB,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACpB,MAAA,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAAA,IACnC;AACA,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MACV,CAAA,wDAAA,EAA2D,KAAK,IAAI,CAAA;AAAA,KACtE;AAIA,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,SAAA,CAAU,UAAA,CAAW,KAAK,IAAI,CAAA;AACrD,IAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,IAAA,CAAK,qBAAA,CAAsB;AAAA,MACzD,UAAA,EAAY,MAAM,IAAA,CAAK,IAAA,CAAK,wBAAA,EAAyB;AAAA,MACrD,gBAAgB,IAAA,CAAK;AAAA,KACtB,CAAA;AACD,IAAA,IAAI,WAAA;AACJ,IAAA,IAAI;AACF,MAAA,WAAA,GAAc,MAAMC,6CAAA,CAAsB,GAAA,EAAK,YAAY,CAAA;AAAA,IAC7D,SAAS,KAAA,EAAO;AAGd,MAAA,IAAID,cAAA,CAAQ,KAAK,CAAA,IAAK,KAAA,CAAM,YAAY,WAAA,EAAa;AACnD,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,UACV,CAAA,kBAAA,EAAqB,KAAK,IAAI,CAAA,kDAAA;AAAA,SAChC;AACA,QAAA;AAAA,MACF;AACA,MAAA,MAAM,KAAA;AAAA,IACR;AAEA,IAAA,IAAI,WAAA,KAAgB,IAAA,IAAQ,WAAA,KAAgB,MAAA,EAAW;AACrD,MAAA,WAAA,GAAc,EAAC;AAAA,IACjB;AACA,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,MAAA,EAAS,WAAA,CAAY,MAAM,CAAA,eAAA,CAAiB,CAAA;AAC9D,IAAA,IAAA,CAAK,OAAO,KAAA,CAAM,CAAA,sBAAA,EAAyB,KAAK,SAAA,CAAU,WAAW,CAAC,CAAA,CAAE,CAAA;AAExE,IAAA,MAAM,UAAA,GAAA,CACJ,MAAM,OAAA,CAAQ,GAAA;AAAA,MACZ,WAAA,CAAY,GAAA,CAAI,OAAM,GAAA,KAAO;AAC3B,QAAA,MAAM,UAAU,MAAME,gDAAA;AAAA,UACpB,GAAA;AAAA,UACA,GAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,OAAOC,6CAAA,CAAwB,SAAS,GAAG,CAAA;AAAA,MAC7C,CAAC;AAAA,OAEH,IAAA,EAAK;AAEP,IAAA,UAAA,CAAW,QAAQ,CAAA,MAAA,KAAU;AAC3B,MAAA,IAAI,MAAA,CAAO,QAAA,CAAS,WAAA,KAAgB,MAAA,EAAW;AAC7C,QAAA,MAAA,CAAO,SAAS,WAAA,GAAc;AAAA,UAC5B,CAACC,gCAAmB,GAAG,IAAA,CAAK,eAAA,EAAgB;AAAA,UAC5C,CAACC,uCAA0B,GAAG,IAAA,CAAK,eAAA;AAAgB,SACrD;AAAA,MACF,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,QAAA,CAAS,WAAA,CAAYD,gCAAmB,CAAA,GAC7C,KAAK,eAAA,EAAgB;AACvB,QAAA,MAAA,CAAO,QAAA,CAAS,WAAA,CAAYC,uCAA0B,CAAA,GACpD,KAAK,eAAA,EAAgB;AAAA,MACzB;AAAA,IACF,CAAC,CAAA;AACD,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,MAAA,EAAS,UAAA,CAAW,MAAM,CAAA,SAAA,CAAW,CAAA;AACvD,IAAA,IAAA,CAAK,OAAO,KAAA,CAAM,CAAA,kBAAA,EAAqB,KAAK,SAAA,CAAU,UAAU,CAAC,CAAA,CAAE,CAAA;AAEnE,IAAA,MAAM,IAAA,CAAK,WAAW,aAAA,CAAc;AAAA,MAClC,IAAA,EAAM,MAAA;AAAA,MACN,QAAA,EAAU,UAAA,CAAW,GAAA,CAAI,CAAA,MAAA,MAAW;AAAA,QAClC,MAAA;AAAA,QACA,WAAA,EAAa,KAAK,eAAA;AAAgB,OACpC,CAAE;AAAA,KACH,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkGF;;;;"}
|
|
@@ -14,16 +14,11 @@ function readModelCatalogApiEntityConfigs(config) {
|
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
16
|
function readModelCatalogApiEntityConfig(id, config) {
|
|
17
|
-
let baseUrl = "";
|
|
18
|
-
if (config.has("baseUrl")) {
|
|
19
|
-
baseUrl = config.getString("baseUrl");
|
|
20
|
-
}
|
|
21
17
|
const schedule = config.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
|
|
22
18
|
config.getConfig("schedule")
|
|
23
19
|
) : void 0;
|
|
24
20
|
return {
|
|
25
21
|
id,
|
|
26
|
-
baseUrl,
|
|
27
22
|
schedule
|
|
28
23
|
};
|
|
29
24
|
}
|
|
@@ -1 +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
|
|
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 schedule = config.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('schedule'),\n )\n : undefined;\n\n return {\n id,\n schedule,\n };\n}\n"],"names":["readSchedulerServiceTaskScheduleDefinitionFromConfig"],"mappings":";;;;AAyBO,SAAS,iCACd,MAAA,EACsB;AACtB,EAAA,MAAM,kBAAkB,MAAA,CAAO,iBAAA;AAAA,IAC7B;AAAA,GACF;AACA,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,OAAO,eAAA,CACJ,MAAK,CACL,GAAA;AAAA,IAAI,QACH,+BAAA,CAAgC,EAAA,EAAI,eAAA,CAAgB,SAAA,CAAU,EAAE,CAAC;AAAA,GACnE;AACJ;AAEA,SAAS,+BAAA,CACP,IACA,MAAA,EACoB;AACpB,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,UAAU,CAAA,GAClCA,qEAAA;AAAA,IACE,MAAA,CAAO,UAAU,UAAU;AAAA,GAC7B,GACA,MAAA;AAEJ,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@red-hat-developer-hub/backstage-plugin-catalog-backend-module-model-catalog",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "The model-catalog backend module for the catalog plugin.",
|
|
6
6
|
"main": "dist/index.cjs.js",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@backstage/plugin-catalog-backend": "^3.8.0",
|
|
44
44
|
"@backstage/plugin-catalog-common": "^1.1.10",
|
|
45
45
|
"@backstage/plugin-catalog-node": "^2.2.2",
|
|
46
|
+
"@red-hat-developer-hub/backstage-plugin-catalog-model-ai-model-server": "^0.2.0",
|
|
46
47
|
"@redhat-ai-dev/model-catalog-types": "^1.1.0",
|
|
47
48
|
"js-yaml": "^4.1.0",
|
|
48
49
|
"stream": "^0.0.3",
|