@zhuoyuezs/ml-platform 0.1.13 → 0.1.15

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/checksums.json CHANGED
@@ -7,23 +7,23 @@
7
7
  },
8
8
  {
9
9
  "path": "runtime/business-client/package-lock.json",
10
- "sha256": "sha256:667e3849ddf32e18495b60e2a064fa7488dc70c48c3cc433472c3882be936bda",
10
+ "sha256": "sha256:a966c6f4dd7a74efaec7ca14c41b0a72c42ab53d2360bc45cb680874e1669720",
11
11
  "size_bytes": 381
12
12
  },
13
13
  {
14
14
  "path": "runtime/business-client/package.json",
15
- "sha256": "sha256:60df549a9afa300d1bd890ad12785bcdf2d7aa03975ad2251c8045268c07ffd6",
15
+ "sha256": "sha256:f2277bf940755b1237fb923e27596483341b2c3a93c16cd0113ce61c5496280a",
16
16
  "size_bytes": 501
17
17
  },
18
18
  {
19
19
  "path": "runtime/business-client/src/catalog.js",
20
- "sha256": "sha256:e286c76dc954a11f5d005ed2626fe9d08681015c3a19c95d427cd44999e79169",
21
- "size_bytes": 54553
20
+ "sha256": "sha256:8c636034f22f7fc70c018f7714923c23a641dd7f1b38f709e6feb3a0e773f105",
21
+ "size_bytes": 55999
22
22
  },
23
23
  {
24
24
  "path": "runtime/business-client/src/cli.js",
25
- "sha256": "sha256:5a31e407dba79205e56d2ed1e74260213d0b89c6bb5a665ec0f52e92927ebcaf",
26
- "size_bytes": 44897
25
+ "sha256": "sha256:4f135325b57725c614d266285013df799cdd9a2096d8fbe0a9e9608dae2bcdb7",
26
+ "size_bytes": 44947
27
27
  },
28
28
  {
29
29
  "path": "runtime/business-client/src/config.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhuoyuezs/ml-platform",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "ML 数据平台 business CLI 与 Agent Skills 管理工具",
5
5
  "bin": {
6
6
  "ml-platform": "scripts/main.js"
package/release.json CHANGED
@@ -13,8 +13,8 @@
13
13
  "entrypoint": "src/cli.js",
14
14
  "name": "ml-platform",
15
15
  "path": "runtime/business-client",
16
- "sha256": "sha256:373996d6b3a6ca50b810f745637c0b2c8999bd87ce3a38055973a845ddaa6f29",
17
- "version": "0.7.3"
16
+ "sha256": "sha256:02dd0494eccb72dc5da160b65c7c17b266b4da9ce09a31122f434b553f8afbd0",
17
+ "version": "0.7.5"
18
18
  },
19
19
  "policy_sha256": "sha256:02fbde0134696b78c0c365d6e0b76c9814227208da5b2b3d4e5dbd82dc4a3606",
20
20
  "public_schema_versions": [
@@ -25,7 +25,7 @@
25
25
  "ml_data_platform.feature_set/v1",
26
26
  "ml_data_platform.dataset_manifest/v1"
27
27
  ],
28
- "release_version": "0.1.13",
28
+ "release_version": "0.1.15",
29
29
  "runtime_requirements": {
30
30
  "node": ">=18",
31
31
  "os": [
@@ -37,16 +37,16 @@
37
37
  "skills": {
38
38
  "feature-management": {
39
39
  "path": "skills/feature-management",
40
- "requires_cli": ">=0.7.3 <0.8.0",
41
- "revision": "0.1.13",
40
+ "requires_cli": ">=0.7.5 <0.8.0",
41
+ "revision": "0.1.15",
42
42
  "sha256": "sha256:182b6f2df4fe58c946e482dfaf416c7d5b78c6fde9c8c8d7293103246618cb4e"
43
43
  },
44
44
  "model-lifecycle-management": {
45
45
  "path": "skills/model-lifecycle-management",
46
- "requires_cli": ">=0.7.3 <0.8.0",
47
- "revision": "0.1.13",
46
+ "requires_cli": ">=0.7.5 <0.8.0",
47
+ "revision": "0.1.15",
48
48
  "sha256": "sha256:61ba17f955b36d1c2e8329a10454d040ee7eca5afdb90ff0914d3f85b6e2d203"
49
49
  }
50
50
  },
51
- "source_commit": "4d04a0ecf38e1272883d7f00b11e1b4193174a43"
51
+ "source_commit": "b91c349695595e23b0295ec48342b61cd5f80c8e"
52
52
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@zhuoyuezs/ml-platform-business-client",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@zhuoyuezs/ml-platform-business-client",
9
- "version": "0.7.3",
9
+ "version": "0.7.5",
10
10
  "license": "UNLICENSED",
11
11
  "bin": {
12
12
  "ml-platform": "src/cli.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhuoyuezs/ml-platform-business-client",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "private": true,
5
5
  "description": "JavaScript implementation of the ML Platform business client",
6
6
  "bin": {
@@ -167,7 +167,22 @@ function loadCatalog(directory) {
167
167
  }
168
168
 
169
169
  async function fetchAll(client, endpoint, params = {}) { const rows = []; for (let offset = 0; ; offset += 500) { const response = await client.get(endpoint, { ...params, limit: 500, offset }); if (Array.isArray(response)) return response; const page = response.items; if (!Array.isArray(page)) throw new Error(`${endpoint} returned an invalid list response`); rows.push(...page); if (response.total == null || rows.length >= response.total || page.length < 500) return rows; } }
170
- async function snapshot(client, targetProject) { const scope = { project: targetProject ?? "default" }; const [parameters, operators, features, featureSets, datasets] = await Promise.all([fetchAll(client, "/parameters", scope), fetchAll(client, "/operators", scope), fetchAll(client, "/features", scope), fetchAll(client, "/feature-sets", scope), fetchAll(client, "/datasets", { ...scope, include_deleted: true })]); return { parameters: new Map(parameters.map((x) => [key(x), x])), operators: new Map(operators.map((x) => [key(normalizeRegistryOperator(x)), normalizeRegistryOperator(x)])), features: new Map(features.map((x) => [key(x), x])), feature_sets: new Map(featureSets.map((x) => [key(normalizeFeatureSet(x)), normalizeFeatureSet(x)])), datasets: new Map(datasets.filter((x) => !x.deleted_at).map((x) => [datasetKey(normalizeRegistryDataset(x)), normalizeRegistryDataset(x)])) }; }
170
+ async function snapshot(client, targetProject) { const scope = { project: targetProject ?? "default" }; const [parameters, operators, features, featureSets, datasets] = await Promise.all([fetchAll(client, "/parameters", scope), fetchAll(client, "/operators", scope), fetchAll(client, "/features", scope), fetchAll(client, "/feature-sets", scope), fetchAll(client, "/datasets", { ...scope, include_deleted: true })]); return { parameters: new Map(parameters.map((x) => [key(x), x])), operators: new Map(operators.map((x) => [key(x), x])), features: new Map(features.map((x) => [key(x), x])), feature_sets: new Map(featureSets.map((x) => [key(x), x])), datasets: new Map(datasets.filter((x) => !x.deleted_at).map((x) => [datasetKey(x), x])) }; }
171
+
172
+ async function hydrate(client, current, assets, endpoint, normalizer, keyFn = key, seen = new Set()) { for (const asset of assets) { const assetKey = keyFn(asset); if (current.has(assetKey) && !seen.has(assetKey)) { current.set(assetKey, normalizer(await client.get(endpoint(asset)))); seen.add(assetKey); } } }
173
+ async function hydrateCatalog(client, catalog, current) {
174
+ const hydrated = { parameters: new Set(), operators: new Set(), features: new Set(), feature_sets: new Set(), datasets: new Set() };
175
+ const resource = (prefix) => (asset) => `/${prefix}/${encodeURIComponent(asset.project)}/${encodeURIComponent(asset.name)}/${encodeURIComponent(asset.version)}`;
176
+ await hydrate(client, current.parameters, catalog.parameters, resource("parameters"), normalizeParameter, key, hydrated.parameters);
177
+ const operatorRefs = catalog.features.map((asset) => ({ project: asset.project, name: asset.operator, version: asset.operator_version }));
178
+ await hydrate(client, current.operators, [...catalog.operators.map((item) => item.spec), ...operatorRefs], resource("operators"), normalizeOperator, key, hydrated.operators);
179
+ const featureSetRefs = catalog.datasets.map((asset) => ({ project: asset.project, ...asset.feature_set }));
180
+ await hydrate(client, current.feature_sets, [...catalog.feature_sets, ...featureSetRefs], resource("feature-sets"), normalizeFeatureSet, key, hydrated.feature_sets);
181
+ await hydrate(client, current.datasets, catalog.datasets, (asset) => `/datasets/${encodeURIComponent(asset.project)}/${encodeURIComponent(asset.dataset_id)}/${encodeURIComponent(asset.dataset_version)}`, normalizeDataset, datasetKey, hydrated.datasets);
182
+ const declaredFeatureSets = new Map(catalog.feature_sets.map((asset) => [key(asset), asset]));
183
+ const featureRefs = catalog.datasets.flatMap((asset) => (declaredFeatureSets.get(referenceKey(asset.feature_set)) ?? current.feature_sets.get(referenceKey(asset.feature_set)) ?? { features: [] }).features.map((feature) => ({ project: asset.project, ...feature })));
184
+ await hydrate(client, current.features, [...catalog.features, ...featureRefs], resource("features"), normalizeFeature, key, hydrated.features);
185
+ }
171
186
 
172
187
  function requiresPackage(spec) { return ["custom_python", "feature", "spark_udf"].includes(spec.type) && spec.code_artifact == null && !String(spec.package_uri ?? "").startsWith("pythonpath://"); }
173
188
  function validateCatalog(catalog, current) {
@@ -198,16 +213,14 @@ function assertImmutable(label, assetKey, candidate, existing) { if (existing &&
198
213
  function sha256File(file) { return `sha256:${crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex")}`; }
199
214
 
200
215
  async function applyCatalog(catalog, client, dryRun, targetProject) {
201
- validateCatalogProject(catalog, targetProject); const current = await snapshot(client, targetProject); validateCatalog(catalog, current); const groups = { parameters: catalog.parameters.length, operators: catalog.operators.length, features: catalog.features.length, feature_sets: catalog.feature_sets.length, datasets: catalog.datasets.length }; const summary = Object.fromEntries(Object.entries(groups).map(([name, count]) => [name, { declared: count, planned: 0, applied: 0, unchanged: 0 }]));
202
- for (const asset of catalog.parameters) { const assetKey = key(asset); if (current.parameters.has(assetKey)) current.parameters.set(assetKey, normalizeParameter(await client.get(`/parameters/${encodeURIComponent(asset.project)}/${encodeURIComponent(asset.name)}/${encodeURIComponent(asset.version)}`))); }
203
- for (const asset of catalog.features) { const assetKey = key(asset); if (current.features.has(assetKey)) current.features.set(assetKey, normalizeFeature(await client.get(`/features/${encodeURIComponent(asset.project)}/${encodeURIComponent(asset.name)}/${encodeURIComponent(asset.version)}`))); }
216
+ validateCatalogProject(catalog, targetProject); const current = await snapshot(client, targetProject); await hydrateCatalog(client, catalog, current); validateCatalog(catalog, current); const groups = { parameters: catalog.parameters.length, operators: catalog.operators.length, features: catalog.features.length, feature_sets: catalog.feature_sets.length, datasets: catalog.datasets.length }; const summary = Object.fromEntries(Object.entries(groups).map(([name, count]) => [name, { declared: count, planned: 0, applied: 0, unchanged: 0 }]));
204
217
  for (const asset of catalog.parameters) assertImmutable("parameter", key(asset), asset, current.parameters.get(key(asset)));
205
218
  for (const item of catalog.operators) { const existing = current.operators.get(key(item.spec)); if (!existing) continue; let candidate = item.spec; if (item.package_path) { if (existing.code_hash !== sha256File(item.package_path) || !existing.code_artifact) throw new Error(`operator version is immutable: ${key(item.spec)}; publish a new version`); candidate = normalizeOperator({ ...item.spec, code_hash: existing.code_artifact.sha256, package_uri: existing.code_artifact.uri, code_artifact: existing.code_artifact }); } assertImmutable("operator", key(item.spec), candidate, existing); }
206
219
  const plain = async (assets, existing, label, endpoint, keyFn = key, params) => { for (const asset of assets) { const assetKey = keyFn(asset); assertImmutable(label, assetKey, asset, existing.get(assetKey)); if (existing.has(assetKey)) summary[label].unchanged += 1; else if (dryRun) summary[label].planned += 1; else { await client.post(endpoint, asset, params); summary[label].applied += 1; } } };
207
220
  try {
208
221
  await plain(catalog.parameters, current.parameters, "parameters", "/parameters");
209
222
  for (const item of catalog.operators) { const assetKey = key(item.spec); if (current.operators.has(assetKey)) summary.operators.unchanged += 1; else if (dryRun) summary.operators.planned += 1; else { let candidate = item.spec; if (item.package_path) { const uploaded = await client.uploadOperatorPackage(item.spec.project, item.spec.name, item.spec.version, item.package_path); candidate = normalizeOperator({ ...item.spec, code_hash: uploaded.code_artifact.sha256, package_uri: uploaded.code_artifact.uri, code_artifact: uploaded.code_artifact }); } await client.post("/operators", candidate); summary.operators.applied += 1; } }
210
- await plain(catalog.features, current.features, "features", "/features"); await plain(catalog.feature_sets, current.feature_sets, "feature_sets", "/feature-sets"); await plain(catalog.datasets, current.datasets, "datasets", "/datasets", datasetKey, { force: false });
223
+ await plain(catalog.features, current.features, "features", "/features"); await plain(catalog.feature_sets, current.feature_sets, "feature_sets", "/feature-sets"); await plain(catalog.datasets, current.datasets, "datasets", "/datasets", datasetKey);
211
224
  } catch (error) { throw new Error(`catalog apply failed after progress=${JSON.stringify(canonical(summary))}: ${error.message}`); }
212
225
  return { status: dryRun ? "validated" : "applied", profile: "server", catalog: { name: catalog.manifest.name, version: catalog.manifest.version, path: catalog.path }, summary };
213
226
  }
@@ -405,8 +405,9 @@ async function runBusinessCli(argv) {
405
405
  ({ result, exitCode } = await submitBuild(api, manifest, build));
406
406
  } else if (["add-dataset", "update-dataset"].includes(options.command)) {
407
407
  const manifest = readDataset(positional(rest, "manifest JSON")); const force = boolean(rest, "--force"); const project = manifest.project || "default";
408
- if (options.command === "add-dataset") result = await client(options).post("/datasets", manifest, { force });
409
- else result = await client(options).put(`/datasets/${encodeURIComponent(project)}/${encodeURIComponent(manifest.dataset_id)}/${encodeURIComponent(manifest.dataset_version)}`, manifest, { force });
408
+ const params = force ? { force: true } : undefined;
409
+ if (options.command === "add-dataset") result = await client(options).post("/datasets", manifest, params);
410
+ else result = await client(options).put(`/datasets/${encodeURIComponent(project)}/${encodeURIComponent(manifest.dataset_id)}/${encodeURIComponent(manifest.dataset_version)}`, manifest, params);
410
411
  } else if (options.command === "delete-dataset") {
411
412
  const id = positional(rest, "dataset id"); const version = positional(rest, "dataset version"); const project = take(rest, "--project", "default");
412
413
  result = await client(options).delete(`/datasets/${encodeURIComponent(project)}/${encodeURIComponent(id)}/${encodeURIComponent(version)}`);