@zhuoyuezs/ml-platform 0.2.0 → 0.2.2-alpha.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/DEVELOPMENT.md +23 -5
- package/README.md +8 -4
- package/checksums.json +20 -20
- package/package.json +9 -3
- package/release.json +12 -12
- package/runtime/business-client/README.md +5 -0
- package/runtime/business-client/package-lock.json +37 -2
- package/runtime/business-client/package.json +8 -3
- package/runtime/business-client/src/catalog.js +471 -183
- package/runtime/business-client/src/cli.js +886 -619
- package/runtime/business-client/src/config.js +48 -41
- package/runtime/business-client/src/http.js +294 -224
- package/scripts/lib.js +1326 -1019
- package/scripts/main.js +138 -125
- package/scripts/verify-release-package.js +15 -19
- package/skills/model-deployment-management/references/operations.md +15 -0
- package/skills/model-lifecycle-management/references/evaluation.md +10 -0
- package/skills/model-lifecycle-management/references/training-contracts.md +8 -4
package/scripts/main.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
2
|
"use strict";
|
|
4
|
-
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
4
|
const path = require("path");
|
|
6
5
|
const { spawnSync } = require("child_process");
|
|
7
|
-
const {
|
|
8
|
-
doctor,
|
|
9
|
-
install,
|
|
10
|
-
migrate,
|
|
11
|
-
parseOptions,
|
|
12
|
-
status,
|
|
13
|
-
} = require("./lib");
|
|
14
|
-
|
|
6
|
+
const { doctor, install, migrate, parseOptions, status, } = require("./lib");
|
|
15
7
|
const MANAGEMENT_COMMANDS = new Set(["install", "upgrade", "migrate", "status", "doctor"]);
|
|
16
8
|
const BUSINESS_ENTRY = path.resolve(__dirname, "..", "runtime", "business-client", "src", "cli.js");
|
|
17
|
-
|
|
18
9
|
function usage() {
|
|
19
|
-
|
|
10
|
+
let businessCommands;
|
|
11
|
+
try {
|
|
12
|
+
businessCommands = [...loadBusinessCommands()].map((command) => ` ${command}`).join("\n");
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
const reason = String(error.message).split("\n", 1)[0];
|
|
16
|
+
businessCommands = ` [不可用] ${reason}`;
|
|
17
|
+
}
|
|
18
|
+
return `用法:
|
|
20
19
|
ml-platform install [--scope user|project] [--json]
|
|
21
20
|
ml-platform upgrade [--scope user|project] [--json]
|
|
22
21
|
ml-platform migrate [--scope user|project] [--json]
|
|
@@ -43,142 +42,156 @@ function usage() {
|
|
|
43
42
|
安装管理命令:
|
|
44
43
|
install, upgrade, migrate, status, doctor
|
|
45
44
|
|
|
46
|
-
|
|
45
|
+
业务命令:
|
|
46
|
+
${businessCommands}
|
|
47
|
+
|
|
48
|
+
查看具体命令参数:
|
|
49
|
+
ml-platform <business-command> --help
|
|
47
50
|
`;
|
|
48
51
|
}
|
|
49
|
-
|
|
50
52
|
function managementUsage(command) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
const signatures = {
|
|
54
|
+
install: "install [--scope user|project] [--project-dir PATH] [--json]",
|
|
55
|
+
upgrade: "upgrade [--scope user|project] [--project-dir PATH] [--json]",
|
|
56
|
+
migrate: "migrate [--scope user|project] [--project-dir PATH] [--json]",
|
|
57
|
+
status: "status [--scope user|project] [--project-dir PATH] [--json]",
|
|
58
|
+
doctor: "doctor [--scope user|project] [--project-dir PATH] [--api-url URL] [--json]",
|
|
59
|
+
};
|
|
60
|
+
return `用法: ml-platform ${signatures[command]}\n`;
|
|
59
61
|
}
|
|
60
|
-
|
|
61
62
|
function printJson(payload) {
|
|
62
|
-
|
|
63
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
63
64
|
}
|
|
64
|
-
|
|
65
65
|
function formatSummary(command, payload) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
if (command === "status") {
|
|
67
|
+
const lines = [`ML Platform: ${payload.installed ? "已安装" : "未安装"}`];
|
|
68
|
+
if (payload.installed_release)
|
|
69
|
+
lines.push(`Release: ${payload.installed_release}`);
|
|
70
|
+
if (payload.cli?.version)
|
|
71
|
+
lines.push(`CLI: ${payload.cli.version} (${payload.cli.path})`);
|
|
72
|
+
if (payload.skill?.revision)
|
|
73
|
+
lines.push(`Skill: ${payload.skill.name}@${payload.skill.revision} (${payload.skill.path})`);
|
|
74
|
+
if (payload.record_error)
|
|
75
|
+
lines.push(`错误: ${payload.record_error}`);
|
|
76
|
+
return `${lines.join("\n")}\n`;
|
|
77
|
+
}
|
|
78
|
+
if (command === "doctor") {
|
|
79
|
+
if (!Array.isArray(payload.checks)) {
|
|
80
|
+
return `ML Platform 检查失败: ${payload.error || "未知错误"}\n`;
|
|
81
|
+
}
|
|
82
|
+
const failed = payload.checks.filter((check) => check.ok === false);
|
|
83
|
+
const passed = payload.checks.filter((check) => check.ok === true);
|
|
84
|
+
const skipped = payload.checks.filter((check) => check.skipped);
|
|
85
|
+
const lines = [
|
|
86
|
+
`ML Platform 检查: ${payload.ok ? "通过" : "失败"}`,
|
|
87
|
+
`Release: ${payload.release_version || "未知"} CLI: ${payload.cli_version || "未知"}`,
|
|
88
|
+
`检查项: ${passed.length} 通过, ${failed.length} 失败, ${skipped.length} 跳过`,
|
|
89
|
+
];
|
|
90
|
+
const api = payload.checks.find((check) => check.name === "platform_api");
|
|
91
|
+
if (api?.api_url)
|
|
92
|
+
lines.push(`API: ${api.api_url} (${api.source})`);
|
|
93
|
+
else if (api?.skipped)
|
|
94
|
+
lines.push(`API: 未配置 (${api.reason})`);
|
|
95
|
+
for (const check of failed)
|
|
96
|
+
lines.push(`失败: ${check.name} - ${check.error || check.detail || "检查未通过"}`);
|
|
97
|
+
return `${lines.join("\n")}\n`;
|
|
77
98
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
const actionLabels = {
|
|
100
|
+
installed: "安装完成",
|
|
101
|
+
upgraded: "升级完成",
|
|
102
|
+
unchanged: "已是当前版本",
|
|
103
|
+
migrated: "迁移完成",
|
|
104
|
+
};
|
|
81
105
|
const lines = [
|
|
82
|
-
|
|
83
|
-
`Release: ${payload.release_version || "未知"} CLI: ${payload.cli_version || "未知"}`,
|
|
84
|
-
`检查项: ${passed.length} 通过, ${failed.length} 失败, ${skipped.length} 跳过`,
|
|
106
|
+
`ML Platform: ${actionLabels[payload.action] || payload.action || (payload.ok ? "完成" : "失败")}`,
|
|
85
107
|
];
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
108
|
+
if (payload.release_version)
|
|
109
|
+
lines.push(`Release: ${payload.release_version} CLI: ${payload.cli_version}`);
|
|
110
|
+
if (payload.cli_shim)
|
|
111
|
+
lines.push(`命令: ${payload.cli_shim}`);
|
|
112
|
+
if (payload.skill_root)
|
|
113
|
+
lines.push(`Skill: ${payload.skill_root}`);
|
|
114
|
+
if (payload.path_ready === false && payload.path_setup)
|
|
115
|
+
lines.push(`PATH: ${payload.path_setup}`);
|
|
116
|
+
if (payload.restart_agent_session)
|
|
117
|
+
lines.push("请重启 Agent 会话以加载最新 Skill。");
|
|
90
118
|
return `${lines.join("\n")}\n`;
|
|
91
|
-
}
|
|
92
|
-
const actionLabels = {
|
|
93
|
-
installed: "安装完成",
|
|
94
|
-
upgraded: "升级完成",
|
|
95
|
-
unchanged: "已是当前版本",
|
|
96
|
-
migrated: "迁移完成",
|
|
97
|
-
};
|
|
98
|
-
const lines = [
|
|
99
|
-
`ML Platform: ${actionLabels[payload.action] || payload.action || (payload.ok ? "完成" : "失败")}`,
|
|
100
|
-
];
|
|
101
|
-
if (payload.release_version) lines.push(`Release: ${payload.release_version} CLI: ${payload.cli_version}`);
|
|
102
|
-
if (payload.cli_shim) lines.push(`命令: ${payload.cli_shim}`);
|
|
103
|
-
if (payload.skill_root) lines.push(`Skill: ${payload.skill_root}`);
|
|
104
|
-
if (payload.path_ready === false && payload.path_setup) lines.push(`PATH: ${payload.path_setup}`);
|
|
105
|
-
if (payload.restart_agent_session) lines.push("请重启 Agent 会话以加载最新 Skill。");
|
|
106
|
-
return `${lines.join("\n")}\n`;
|
|
107
119
|
}
|
|
108
|
-
|
|
109
120
|
function printResult(command, payload, summary) {
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
if (summary)
|
|
122
|
+
process.stdout.write(formatSummary(command, payload));
|
|
123
|
+
else
|
|
124
|
+
printJson(payload);
|
|
112
125
|
}
|
|
113
|
-
|
|
114
126
|
function loadBusinessCommands() {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
try {
|
|
128
|
+
const { BUSINESS_COMMANDS } = require(BUSINESS_ENTRY);
|
|
129
|
+
if (!(BUSINESS_COMMANDS instanceof Set))
|
|
130
|
+
throw new Error("BUSINESS_COMMANDS export is missing");
|
|
131
|
+
return BUSINESS_COMMANDS;
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
throw new Error(`Business CLI 不完整: ${error.message}`);
|
|
135
|
+
}
|
|
122
136
|
}
|
|
123
|
-
|
|
124
137
|
function dispatchBusiness(argv) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
const commands = loadBusinessCommands();
|
|
139
|
+
if (!argv.some((arg) => commands.has(arg))) {
|
|
140
|
+
throw new Error(`未知命令: ${argv.find((arg) => !arg.startsWith("-")) || argv[0]}`);
|
|
141
|
+
}
|
|
142
|
+
const result = spawnSync(process.execPath, [BUSINESS_ENTRY, ...argv], {
|
|
143
|
+
stdio: "inherit",
|
|
144
|
+
env: process.env,
|
|
145
|
+
});
|
|
146
|
+
if (result.error)
|
|
147
|
+
throw result.error;
|
|
148
|
+
return result.status === null ? 1 : result.status;
|
|
135
149
|
}
|
|
136
|
-
|
|
137
150
|
function main(argv) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
151
|
+
const [command, ...rest] = argv;
|
|
152
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
153
|
+
process.stdout.write(usage());
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
if (!MANAGEMENT_COMMANDS.has(command))
|
|
157
|
+
return dispatchBusiness(argv);
|
|
158
|
+
if (rest.includes("--help") || rest.includes("-h")) {
|
|
159
|
+
process.stdout.write(managementUsage(command));
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
const options = parseOptions(rest);
|
|
163
|
+
if (command === "install" || command === "upgrade") {
|
|
164
|
+
if (command === "upgrade")
|
|
165
|
+
options.upgrade = true;
|
|
166
|
+
printResult(command, install(options), options.summary);
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
if (command === "migrate") {
|
|
170
|
+
const result = migrate(options);
|
|
171
|
+
printResult(command, result, options.summary);
|
|
172
|
+
return result.ok ? 0 : 1;
|
|
173
|
+
}
|
|
174
|
+
if (command === "status") {
|
|
175
|
+
const result = status(options);
|
|
176
|
+
printResult(command, result, options.summary);
|
|
177
|
+
return result.ok ? 0 : 1;
|
|
178
|
+
}
|
|
179
|
+
const result = doctor(options);
|
|
163
180
|
printResult(command, result, options.summary);
|
|
164
181
|
return result.ok ? 0 : 1;
|
|
165
|
-
}
|
|
166
|
-
const result = doctor(options);
|
|
167
|
-
printResult(command, result, options.summary);
|
|
168
|
-
return result.ok ? 0 : 1;
|
|
169
182
|
}
|
|
170
|
-
|
|
171
183
|
if (require.main === module) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
try {
|
|
185
|
+
process.exitCode = main(process.argv.slice(2));
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (process.argv.slice(2).includes("--json")) {
|
|
189
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: error.message }, null, 2)}\n`);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
process.stderr.write(`ML Platform: 失败\n错误: ${error.message}\n`);
|
|
193
|
+
}
|
|
194
|
+
process.exitCode = 2;
|
|
179
195
|
}
|
|
180
|
-
process.exitCode = 2;
|
|
181
|
-
}
|
|
182
196
|
}
|
|
183
|
-
|
|
184
197
|
module.exports = { BUSINESS_ENTRY, MANAGEMENT_COMMANDS, dispatchBusiness, formatSummary, main, managementUsage, usage };
|
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const path = require("node:path");
|
|
5
|
-
|
|
6
5
|
const { verifyPackage } = require("./lib");
|
|
7
|
-
|
|
8
6
|
function verifyReleasePackage(packageRoot = path.resolve(__dirname, "..")) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
7
|
+
try {
|
|
8
|
+
return verifyPackage(packageRoot);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
throw new Error(`拒绝发布未验证的 ML Platform 包:${error.message}。`
|
|
12
|
+
+ " 请只发布 build_ml_platform_release.py 生成的 .tgz 归档。");
|
|
13
|
+
}
|
|
17
14
|
}
|
|
18
|
-
|
|
19
15
|
if (require.main === module) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
try {
|
|
17
|
+
verifyReleasePackage();
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
process.stderr.write(`${error.message}\n`);
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
}
|
|
26
23
|
}
|
|
27
|
-
|
|
28
24
|
module.exports = { verifyReleasePackage };
|
|
@@ -29,6 +29,21 @@ Use `get-deployment` to inspect the full revision history and endpoints. Use
|
|
|
29
29
|
`list-deployments` for discovery and `get-deployment-events` for paginated
|
|
30
30
|
audit evidence.
|
|
31
31
|
|
|
32
|
+
Discover deployments associated with a packaged model without knowing the
|
|
33
|
+
deployment ID first:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ml-platform --profile server list-deployments \
|
|
37
|
+
--package-id <package-id> \
|
|
38
|
+
--package-version <package-version> \
|
|
39
|
+
--model-artifact-id <artifact-id>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The filters may be used independently and combined with `--service`,
|
|
43
|
+
`--environment`, `--state`, `--q`, `--limit`, and `--offset`. These results are
|
|
44
|
+
ITSMP platform deployments; external model-center delivery callbacks are a
|
|
45
|
+
separate status surface.
|
|
46
|
+
|
|
32
47
|
For direct model access, declare `service_exposure.type=NodePort` in the same
|
|
33
48
|
validated DeploymentSpec. After the revision is `READY`, read the observed
|
|
34
49
|
`nodeport` endpoint instead of constructing a Service name. Given an observed
|
|
@@ -27,6 +27,16 @@ Inspect `get-metric-definition regression.mae v1` before relying on this metric.
|
|
|
27
27
|
Release mode additionally requires at least one required validation rule with a
|
|
28
28
|
business-approved threshold; do not invent thresholds to obtain PASS.
|
|
29
29
|
|
|
30
|
+
For a fixed-length multi-step forecast, `regression.mae` and
|
|
31
|
+
`regression.rmse` cover only the terminal point at the maximum trained horizon.
|
|
32
|
+
Use `regression.trajectory_mae` and `regression.trajectory_rmse` for a release
|
|
33
|
+
gate over the complete trajectory. These metrics flatten every valid
|
|
34
|
+
`(anchor, horizon_step)` cell; they do not multiply the terminal metric by the
|
|
35
|
+
prediction length. Inspect `forecast_metrics.parquet` for the per-step curve.
|
|
36
|
+
When a trajectory metric is requested, prediction coverage requires every step
|
|
37
|
+
of an anchor to be finite. A non-forecast ModelArtifact is rejected before
|
|
38
|
+
Worker execution.
|
|
39
|
+
|
|
30
40
|
Example `evaluation-request.json` shape; replace illustrative identities before
|
|
31
41
|
validation or submission:
|
|
32
42
|
|
|
@@ -265,7 +265,11 @@ contracts. Training `evaluation.split=auto` prefers nonempty test, then validati
|
|
|
265
265
|
then train; choose an explicit split when reserving the test set for final review.
|
|
266
266
|
Training metrics are not a governed EvaluationResult or release gate pass.
|
|
267
267
|
|
|
268
|
-
For sequence outputs,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
For sequence outputs, training feedback and governed `regression.mae` /
|
|
269
|
+
`regression.rmse` retain terminal-point semantics at the trained maximum
|
|
270
|
+
horizon. Governed evaluation additionally supports
|
|
271
|
+
`regression.trajectory_mae` and `regression.trajectory_rmse`: they aggregate all
|
|
272
|
+
valid `(anchor, horizon_step)` cells, while `forecast_metrics.parquet` records
|
|
273
|
+
the per-step curve. Use a trajectory metric for multi-point release gates; do
|
|
274
|
+
not multiply the terminal metric by `prediction_length` or report it as whole-
|
|
275
|
+
trajectory quality.
|