Skip to content

Commit d138b46

Browse files
committed
feat: add audio model train/deploy
1 parent b5f2b8b commit d138b46

35 files changed

Lines changed: 1297 additions & 222 deletions

AGENTS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,17 @@ CLI 只为「自己能权威解释的错误」发出语义化信号,服务端的
105105

106106
这些 flag 已在 `GLOBAL_OPTIONS``packages/core/src/types/command.ts`)中注册,由 `loadConfig` 写入 `config.consoleRegion` / `config.consoleSite` / `config.consoleSwitchAgent``callConsoleGateway` 自动读取——命令无需手动提取或传递。
107107

108+
### 5. 禁止单字母变量命名
109+
110+
所有变量、参数、回调形参必须使用有语义的命名,不允许单字母(如 `i``m``p``t``e``s`)。具体表现:
111+
112+
- 回调参数: `.map((m) => ...)``.map((model) => ...)`, `.find((t) => ...)``.find((template) => ...)`
113+
- catch 变量: `catch (e)``catch (error)`
114+
- for-of 循环: `for (const i of items)``for (const item of items)`
115+
- 临时变量: `const s = ...``const strategy = ...`
116+
117+
例外: 仅当作用域极小(≤3 行)且语义从上下文完全明确时,可使用 `k`/`v`(Object.entries 的 key/value)。
118+
108119
## 完成改动后的快速验证
109120

110121
```sh

packages/commands/src/commands/dataset/upload.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ import { failIfMissing } from "bailian-cli-runtime";
1616
import { emitResult, emitBare } from "bailian-cli-runtime";
1717

1818
export default defineCommand({
19-
description: "Upload a dataset file (.jsonl) to Bailian",
19+
description: "Upload a dataset file (.jsonl or .zip) to Bailian",
2020
usageArgs:
21-
"--file <path> [--purpose <name>] [--schema <chatml|dpo|cpt>] [--no-validate] [--full-validate]",
21+
"--file <path> [--purpose <name>] [--schema <chatml|dpo|cpt|tts>] [--no-validate] [--full-validate]",
2222
options: [
2323
{
2424
flag: "--file <path>",
25-
description: "Local .jsonl dataset file (≤300MB)",
25+
description: "Local dataset file (.jsonl or .zip, ≤300MB)",
2626
required: true,
2727
},
2828
{
@@ -32,7 +32,7 @@ export default defineCommand({
3232
{
3333
flag: "--schema <s>",
3434
description:
35-
'Record schema: "chatml" (SFT), "dpo" (chosen/rejected), or "cpt" (raw text). Default auto-detects per record.',
35+
'Record schema: "chatml" (SFT), "dpo" (chosen/rejected), "cpt" (raw text), or "tts" (audio wav_fn/text). Default auto-detects per record.',
3636
},
3737
{
3838
flag: "--no-validate",
@@ -49,21 +49,21 @@ export default defineCommand({
4949
"--file train.jsonl",
5050
"--file dpo.jsonl --schema dpo",
5151
"--file cpt.jsonl --schema cpt",
52+
"--file audio.zip --schema tts",
5253
"--file eval.jsonl --purpose evaluation",
5354
"--file train.jsonl --full-validate",
5455
"--file train.jsonl --no-validate",
5556
],
5657
notes: [
57-
"Only .jsonl is supported in this release. Three record schemas are",
58-
"recognized: chatml = {messages:[...]} (SFT); dpo = {messages:[...],",
59-
"chosen, rejected} where chosen/rejected are single assistant messages;",
60-
'cpt = {text:"..."} (continual pre-training, raw text). With no --schema,',
61-
"a record carrying chosen/rejected is validated as DPO, one with text (and",
62-
"no messages) as CPT, otherwise as ChatML. Pass --schema dpo / cpt to",
63-
"require that shape on every record, or --schema chatml to ignore the",
64-
"preference / text fields. Other purposes may carry a different schema in",
65-
"the future and would be served by a purpose-specific validator.",
66-
"The dataset upload cap is 300MB per file.",
58+
"Supports .jsonl (text) and .zip (audio/image/video archives with a",
59+
"data.jsonl manifest). Four record schemas are recognized: chatml =",
60+
"{messages:[...]} (SFT); dpo = {messages:[...], chosen, rejected};",
61+
'cpt = {text:"..."} (continual pre-training, raw text); tts =',
62+
'{wav_fn:"train/xxx.wav", text:"..."} (audio fine-tuning). With no',
63+
"--schema, a record carrying wav_fn is validated as TTS, one with",
64+
"chosen/rejected as DPO, one with text (and no messages) as CPT,",
65+
"otherwise as ChatML. Pass --schema to require a specific shape on",
66+
"every record. The dataset upload cap is 300MB per file.",
6767
"Upload uses the OpenAI-compatible /compatible-mode/v1/files endpoint so",
6868
"the purpose tag is persisted (the DashScope-native /api/v1/files drops it).",
6969
],

packages/commands/src/commands/dataset/validate.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ function formatStats(result: ValidationResult): string[] {
2424
}
2525

2626
export default defineCommand({
27-
description: "Locally validate a dataset file (.jsonl) without uploading",
27+
description: "Locally validate a dataset file (.jsonl or .zip) without uploading",
2828
// 纯本地校验,不触网、不需 API key(与 `pipeline validate` 一致)。
2929
skipDefaultApiKeySetup: true,
30-
usageArgs: "--file <path> [--full-validate] [--schema <chatml|dpo|cpt>]",
30+
usageArgs: "--file <path> [--full-validate] [--schema <chatml|dpo|cpt|tts>]",
3131
options: [
32-
{ flag: "--file <path>", description: "Local .jsonl dataset file", required: true },
32+
{ flag: "--file <path>", description: "Local dataset file (.jsonl or .zip)", required: true },
3333
{
3434
flag: "--full-validate",
3535
description: "JSON.parse every line instead of sampling (slower)",
@@ -38,26 +38,29 @@ export default defineCommand({
3838
{
3939
flag: "--schema <s>",
4040
description:
41-
'Record schema: "chatml" (SFT), "dpo" (chosen/rejected), or "cpt" (raw text). Default auto-detects per record.',
41+
'Record schema: "chatml" (SFT), "dpo" (chosen/rejected), "cpt" (raw text), or "tts" (audio wav_fn/text). Default auto-detects per record.',
4242
},
4343
],
4444
exampleArgs: [
4545
"--file train.jsonl",
4646
"--file dpo.jsonl --schema dpo",
4747
"--file cpt.jsonl --schema cpt",
48+
"--file audio.zip --schema tts",
4849
"--file eval.jsonl --full-validate",
4950
"--file train.jsonl --output json",
5051
],
5152
notes: [
5253
"Default scan: every line gets a structural check, then ~160 lines (front 50,",
5354
"evenly spaced 100, last 10) are JSON.parsed against the active schema.",
5455
"Schemas: chatml = {messages:[...]} (SFT); dpo = {messages:[...], chosen,",
55-
"rejected} where chosen/rejected are single assistant messages; cpt =",
56-
'{text:"..."} (continual pre-training, raw text). With no --schema, a',
57-
"record carrying chosen/rejected is validated as DPO, one with text (and no",
58-
"messages) as CPT, otherwise as ChatML. Pass --schema dpo / cpt to require",
59-
"that shape on every record (strict), or --schema chatml to ignore the",
60-
"preference / text fields. Use --full-validate to JSON.parse every line.",
56+
'rejected}; cpt = {text:"..."} (continual pre-training, raw text);',
57+
'tts = {wav_fn:"train/xxx.wav", text:"..."} (audio fine-tuning). With no',
58+
"--schema, a record carrying wav_fn is validated as TTS, one with",
59+
"chosen/rejected as DPO, one with text (and no messages) as CPT, otherwise",
60+
"as ChatML. Pass --schema to require a specific shape on every record.",
61+
"ZIP archives (.zip) are validated structurally (data.jsonl present, media",
62+
"references resolve) in addition to per-record content checks.",
63+
"Use --full-validate to JSON.parse every line.",
6164
],
6265
async run(config: Config, flags: GlobalFlags) {
6366
const filePath = flags.file as string | undefined;

packages/commands/src/commands/deploy/create.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { pickPlanStrategy } from "./plans.ts";
2525
export default defineCommand({
2626
description: "Create a model deployment",
2727
usageArgs:
28-
"--model <model_name> --name <display_name> [--plan <plan>] [--template-id <id>] [--capacity <n>] [--billing-method <m>] [--input-tpm <n>] [--output-tpm <n>] [--thinking-output-tpm <n>] [--yes]",
28+
"--model <model_name> --name <display_name> [--plan <plan>] [--deploy-spec <id>] [--capacity <n>] [--billing-method <m>] [--input-tpm <n>] [--output-tpm <n>] [--thinking-output-tpm <n>] [--yes]",
2929
options: [
3030
{
3131
flag: "--model <name>",
@@ -42,8 +42,8 @@ export default defineCommand({
4242
description: "Billing plan: lora (default, Token-billed) | ptu (Token-billed) | mu",
4343
},
4444
{
45-
flag: "--template-id <id>",
46-
description: "Template id (only used by plan=mu; auto-picked if omitted)",
45+
flag: "--deploy-spec <id>",
46+
description: "Deploy spec (only used by plan=mu; auto-picked if omitted)",
4747
},
4848
{
4949
flag: "--capacity <n>",
@@ -76,15 +76,15 @@ export default defineCommand({
7676
"--model my-qwen-sft --name my-sft-test",
7777
"--model qwen3.6-flash-2026-04-16 --name my-flash --plan ptu --input-tpm 10000 --output-tpm 1000",
7878
"--model qwen3-8b --name my-qwen3-mu --plan mu",
79-
"--model qwen3-8b --name my-qwen3 --plan mu --template-id MU1 --capacity 2 --yes",
79+
"--model qwen3-8b --name my-qwen3 --plan mu --deploy-spec MU1 --capacity 2 --yes",
8080
],
8181
notes: [
8282
"Plan defaults to `lora` (Token-billed). Pass --plan to override.",
8383
"For plan=ptu (Token-billed, provisioned throughput), --input-tpm and",
8484
"--output-tpm are required (the platform rejects creation without an",
8585
"explicit ptu_capacity despite the doc listing defaults).",
86-
"For plan=mu, `capacity`, `billing_method` and `template_id` are required.",
87-
"billing_method defaults to POST_PAY (only supported value); template_id",
86+
"For plan=mu, `capacity`, `billing_method` and `deploy_spec` are required.",
87+
"billing_method defaults to POST_PAY (only supported value); deploy_spec",
8888
"and capacity are auto-picked from GET /deployments/models when omitted.",
8989
"Use `bl deploy models --source base` to inspect available templates.",
9090
"After creation, status starts at PENDING and transitions to RUNNING.",

packages/commands/src/commands/deploy/delete.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ export default defineCommand({
6060
ExitCode.USAGE,
6161
);
6262
}
63-
} catch (e) {
64-
if (e instanceof BailianError) throw e;
63+
} catch (error) {
64+
if (error instanceof BailianError) throw error;
6565
// If the get itself failed (e.g. not found), let the DELETE call surface the real error.
6666
}
6767
}

packages/commands/src/commands/deploy/list.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,13 @@ export default defineCommand({
6060
return;
6161
}
6262
const headers = ["DEPLOYED_MODEL", "MODEL_NAME", "STATUS", "PLAN", "CAPACITY", "CREATED_AT"];
63-
const rows = items.map((i) => [
64-
i.deployed_model,
65-
i.model_name,
66-
i.status,
67-
i.plan,
68-
i.capacity,
69-
i.created_at,
63+
const rows = items.map((item) => [
64+
item.deployed_model,
65+
item.model_name,
66+
item.status,
67+
item.plan,
68+
item.capacity,
69+
item.created_at,
7070
]);
7171
for (const line of formatTable(headers, rows)) emitBare(line);
7272
if (total !== undefined) emitBare(`\nTotal: ${total}`);

packages/commands/src/commands/deploy/models.ts

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -74,44 +74,44 @@ export default defineCommand({
7474
// downstream tooling can drive `bl deploy create --template-id <…>` without
7575
// a second round-trip. For text: keep the compact one-line summary.
7676
if (format === "json") {
77-
const items = models.map((m) => {
77+
const items = models.map((model) => {
7878
const out: Record<string, unknown> = {
79-
model_name: m.model_name ?? "",
79+
model_name: model.model_name ?? "",
8080
};
81-
if (m.base_model) out.base_model = m.base_model;
82-
if (m.model_source) out.model_source = m.model_source;
83-
if (m.supported_plans && m.supported_plans.length > 0) {
84-
out.supported_plans = m.supported_plans;
81+
if (model.base_model) out.base_model = model.base_model;
82+
if (model.model_source) out.model_source = model.model_source;
83+
if (model.supported_plans && model.supported_plans.length > 0) {
84+
out.supported_plans = model.supported_plans;
8585
}
86-
if (m.plans && m.plans.length > 0) {
87-
out.plans = m.plans.map((p) => {
88-
const planEntry: Record<string, unknown> = { plan: p.plan ?? "" };
89-
if (p.cu_specs && p.cu_specs.length > 0) {
90-
planEntry.cu_specs = p.cu_specs;
86+
if (model.plans && model.plans.length > 0) {
87+
out.plans = model.plans.map((plan) => {
88+
const planEntry: Record<string, unknown> = { plan: plan.plan ?? "" };
89+
if (plan.cu_specs && plan.cu_specs.length > 0) {
90+
planEntry.cu_specs = plan.cu_specs;
9191
}
92-
if (p.templates && p.templates.length > 0) {
92+
if (plan.templates && plan.templates.length > 0) {
9393
// Pull the top 6 fields most useful for `bl deploy create`.
9494
// Drop noisy/redundant: template_source, template_type,
9595
// template_version, deploy_spec (typically == template_id).
96-
planEntry.templates = p.templates.map((t) => {
96+
planEntry.templates = plan.templates.map((template) => {
9797
const tpl: Record<string, unknown> = {};
98-
if (t.template_id) tpl.template_id = t.template_id;
99-
if (t.template_name) tpl.template_name = t.template_name;
100-
if (t.charge_type) tpl.charge_type = t.charge_type;
98+
if (template.template_id) tpl.template_id = template.template_id;
99+
if (template.template_name) tpl.template_name = template.template_name;
100+
if (template.charge_type) tpl.charge_type = template.charge_type;
101101
// Flatten roles.unified for the common COUPLED case.
102-
const unified = t.roles?.unified;
102+
const unified = template.roles?.unified;
103103
if (unified?.model_unit_spec) tpl.model_unit_spec = unified.model_unit_spec;
104104
if (unified?.capacity_unit_per_instance !== undefined)
105105
tpl.capacity_unit_per_instance = unified.capacity_unit_per_instance;
106106
// Preserve split-role configs (SEPERATED) as-is so callers
107107
// can still drive prefill/decode sizing.
108-
if (t.roles?.prefill || t.roles?.decode) {
108+
if (template.roles?.prefill || template.roles?.decode) {
109109
tpl.roles = {
110-
prefill: t.roles?.prefill,
111-
decode: t.roles?.decode,
110+
prefill: template.roles?.prefill,
111+
decode: template.roles?.decode,
112112
};
113113
}
114-
if (t.template_desc) tpl.template_desc = t.template_desc;
114+
if (template.template_desc) tpl.template_desc = template.template_desc;
115115
return tpl;
116116
});
117117
}
@@ -125,19 +125,19 @@ export default defineCommand({
125125
}
126126

127127
// text / quiet — keep the compact single-line summary table.
128-
const textItems = models.map((m) => {
128+
const textItems = models.map((model) => {
129129
let plansSummary = "";
130-
if (m.supported_plans && m.supported_plans.length > 0) {
131-
plansSummary = m.supported_plans.join(",");
132-
} else if (m.plans && m.plans.length > 0) {
133-
plansSummary = m.plans
134-
.map((p) => {
135-
const planName = p.plan ?? "?";
136-
if (p.templates && p.templates.length > 0) {
137-
return `${planName}(${p.templates.length}t)`;
130+
if (model.supported_plans && model.supported_plans.length > 0) {
131+
plansSummary = model.supported_plans.join(",");
132+
} else if (model.plans && model.plans.length > 0) {
133+
plansSummary = model.plans
134+
.map((plan) => {
135+
const planName = plan.plan ?? "?";
136+
if (plan.templates && plan.templates.length > 0) {
137+
return `${planName}(${plan.templates.length}t)`;
138138
}
139-
if (p.cu_specs && p.cu_specs.length > 0) {
140-
return `${planName}(${p.cu_specs.join("/")})`;
139+
if (plan.cu_specs && plan.cu_specs.length > 0) {
140+
return `${planName}(${plan.cu_specs.join("/")})`;
141141
}
142142
return planName;
143143
})
@@ -146,9 +146,9 @@ export default defineCommand({
146146
plansSummary = "-";
147147
}
148148
return {
149-
model_name: m.model_name ?? "",
150-
base_model: m.base_model ?? "",
151-
source: m.model_source ?? "",
149+
model_name: model.model_name ?? "",
150+
base_model: model.base_model ?? "",
151+
source: model.model_source ?? "",
152152
plans: plansSummary,
153153
};
154154
});
@@ -158,7 +158,12 @@ export default defineCommand({
158158
return;
159159
}
160160
const headers = ["MODEL_NAME", "BASE_MODEL", "SOURCE", "PLANS"];
161-
const rows = textItems.map((i) => [i.model_name, i.base_model, i.source, i.plans]);
161+
const rows = textItems.map((item) => [
162+
item.model_name,
163+
item.base_model,
164+
item.source,
165+
item.plans,
166+
]);
162167
for (const line of formatTable(headers, rows)) emitBare(line);
163168
if (total !== undefined) emitBare(`\nTotal: ${total}`);
164169
},

0 commit comments

Comments
 (0)