Skip to content

Commit f30fff9

Browse files
committed
feat(finetune): clarify model flags; add price estimate & actual cost
- Rename for clarity: finetune --model → --base-model (create/price/ capability/list); deploy create --model → --model-name, --name → --display-name - Add `finetune price` (console domain) for pre-training cost estimate (sft/dpo/cpt) - Add actual training cost (fee.ts) enriched into finetune get/watch from catalog price × reported usage
1 parent a7245c0 commit f30fff9

24 files changed

Lines changed: 707 additions & 256 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,10 @@ bl auth login --console
130130

131131
# Fine-tune & deploy — a one-shot train-to-serve workflow
132132
bl dataset upload --file ./train.jsonl # Upload a .jsonl dataset (validated first)
133-
bl finetune text create --model qwen3-8b --datasets ./train.jsonl --training-type sft-lora # Local paths auto-upload
133+
bl finetune text create --base-model qwen3-8b --datasets ./train.jsonl --training-type sft-lora # Local paths auto-upload
134134
bl finetune watch --job-id ft-xxx --output json # Non-blocking probe (running/succeeded return 0; failed/canceled report an error)
135-
bl finetune capability --model qwen3-8b # Which training types a model supports
136-
bl deploy text create --model qwen3-8b --name my-svc --plan mu # Deploy the trained model as an endpoint
135+
bl finetune capability --base-model qwen3-8b # Which training types a model supports
136+
bl deploy text create --model-name qwen3-8b --display-name my-svc --plan mu # Deploy the trained model as an endpoint
137137

138138
# Browse models / apps / free-tier quota / usage statistics / workspaces
139139
bl model list # Browse model families and pricing

README.zh.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ bl auth login --console
128128

129129
# 微调与部署 — 从训练到服务的一站式流程
130130
bl dataset upload --file ./train.jsonl # 上传 .jsonl 数据集(先校验)
131-
bl finetune text create --model qwen3-8b --datasets ./train.jsonl --training-type sft-lora # 本地路径自动上传
131+
bl finetune text create --base-model qwen3-8b --datasets ./train.jsonl --training-type sft-lora # 本地路径自动上传
132132
bl finetune watch --job-id ft-xxx --output json # 非阻塞探测(运行中/成功返回 0;失败/取消报错)
133-
bl finetune capability --model qwen3-8b # 查询模型支持哪些训练方式
134-
bl deploy text create --model qwen3-8b --name my-svc --plan mu # 把训练好的模型部署为推理服务
133+
bl finetune capability --base-model qwen3-8b # 查询模型支持哪些训练方式
134+
bl deploy text create --model-name qwen3-8b --display-name my-svc --plan mu # 把训练好的模型部署为推理服务
135135

136136
# 浏览模型 / 应用 / 免费额度 / 用量统计 / 业务空间
137137
bl model list # 浏览模型系列与价格信息

packages/cli/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,10 @@ bl auth login --console
130130

131131
# Fine-tune & deploy — a one-shot train-to-serve workflow
132132
bl dataset upload --file ./train.jsonl # Upload a .jsonl dataset (validated first)
133-
bl finetune text create --model qwen3-8b --datasets ./train.jsonl --training-type sft-lora # Local paths auto-upload
133+
bl finetune text create --base-model qwen3-8b --datasets ./train.jsonl --training-type sft-lora # Local paths auto-upload
134134
bl finetune watch --job-id ft-xxx --output json # Non-blocking probe (running/succeeded return 0; failed/canceled report an error)
135-
bl finetune capability --model qwen3-8b # Which training types a model supports
136-
bl deploy text create --model qwen3-8b --name my-svc --plan mu # Deploy the trained model as an endpoint
135+
bl finetune capability --base-model qwen3-8b # Which training types a model supports
136+
bl deploy text create --model-name qwen3-8b --display-name my-svc --plan mu # Deploy the trained model as an endpoint
137137

138138
# Browse models / apps / free-tier quota / usage statistics / workspaces
139139
bl model list # Browse model families and pricing

packages/cli/README.zh.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ bl auth login --console
128128

129129
# 微调与部署 — 从训练到服务的一站式流程
130130
bl dataset upload --file ./train.jsonl # 上传 .jsonl 数据集(先校验)
131-
bl finetune text create --model qwen3-8b --datasets ./train.jsonl --training-type sft-lora # 本地路径自动上传
131+
bl finetune text create --base-model qwen3-8b --datasets ./train.jsonl --training-type sft-lora # 本地路径自动上传
132132
bl finetune watch --job-id ft-xxx --output json # 非阻塞探测(运行中/成功返回 0;失败/取消报错)
133-
bl finetune capability --model qwen3-8b # 查询模型支持哪些训练方式
134-
bl deploy text create --model qwen3-8b --name my-svc --plan mu # 把训练好的模型部署为推理服务
133+
bl finetune capability --base-model qwen3-8b # 查询模型支持哪些训练方式
134+
bl deploy text create --model-name qwen3-8b --display-name my-svc --plan mu # 把训练好的模型部署为推理服务
135135

136136
# 浏览模型 / 应用 / 免费额度 / 用量统计 / 业务空间
137137
bl model list # 浏览模型系列与价格信息

packages/cli/src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import {
7171
finetuneExport,
7272
finetuneWatch,
7373
finetuneCapability,
74+
finetunePrice,
7475
deployTextCreate,
7576
deployAudioCreate,
7677
deployImageCreate,
@@ -191,6 +192,7 @@ export const commands: Record<string, AnyCommand> = {
191192
"finetune export": finetuneExport,
192193
"finetune watch": finetuneWatch,
193194
"finetune capability": finetuneCapability,
195+
"finetune price": finetunePrice,
194196
"deploy text create": deployTextCreate,
195197
"deploy audio create": deployAudioCreate,
196198
"deploy image create": deployImageCreate,

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

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ import {
1313
import { emitResult, emitBare } from "bailian-cli-runtime";
1414

1515
const CREATE_FLAGS = {
16-
model: {
16+
modelName: {
1717
type: "string",
18-
valueHint: "<name>",
19-
description: "Model name (catalog model or fine-tuned output) (required)",
18+
valueHint: "<model_name>",
19+
description: "Model to deploy — fine-tuned output name or catalog model (required)",
2020
required: true,
2121
},
22-
name: {
22+
displayName: {
2323
type: "string",
2424
valueHint: "<display_name>",
2525
description: "Console display name for the deployment (required)",
@@ -63,7 +63,7 @@ const CREATE_FLAGS = {
6363
} satisfies FlagsDef;
6464

6565
const CREATE_USAGE =
66-
"--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>]";
66+
"--model-name <model_name> --display-name <display_name> [--plan <plan>] [--deploy-spec <id>] [--capacity <n>] [--billing-method <m>] [--input-tpm <n>] [--output-tpm <n>] [--thinking-output-tpm <n>]";
6767

6868
const CREATE_NOTES = [
6969
"Plan defaults to `lora` (Token-billed) for text/image and `mu` (model-unit-",
@@ -77,14 +77,11 @@ const CREATE_NOTES = [
7777
"Use `bl deploy models --source base` to inspect available templates.",
7878
"After creation, status starts at PENDING and transitions to RUNNING.",
7979
"Invoke the deployed model with: bl text chat --model <deployed_model>",
80-
"WARNING: --model is overloaded across commands and refers to DIFFERENT",
81-
"values. `bl deploy <modality> create --model` takes the exported model_name",
82-
"(e.g. `qwen3-8b-ft-...`), but the create response also returns a",
83-
"`deployed_model` field (the deployment instance id, e.g.",
84-
"`qwen3-8b-5ecb5f068d79`). The inference call `bl text chat --model` must use",
85-
"the `deployed_model` from the create response — NOT the `model_name` you",
86-
"passed to `deploy <modality> create`. Do not reuse the value across the two",
87-
"commands.",
80+
"NOTE: --model-name is the model being deployed (e.g. `qwen3-8b-ft-...`).",
81+
"The create response also returns a `deployed_model` field — the deployment",
82+
"instance id (e.g. `qwen3-8b-5ecb5f068d79`). Use that id for inference",
83+
"(`bl text chat --model <deployed_model>`) and lifecycle commands",
84+
"(`deploy get/scale/pause/resume/delete --deployed-model <id>`).",
8885
];
8986

9087
/**
@@ -118,8 +115,8 @@ async function runCreate(
118115
ctx: CommandContext<typeof CREATE_FLAGS>,
119116
): Promise<void> {
120117
const { identity, settings, flags } = ctx;
121-
const model = flags.model as string;
122-
const name = flags.name as string;
118+
const model = flags.modelName as string;
119+
const name = flags.displayName as string;
123120
const plan = (flags.plan as string | undefined) || defaultDeployPlan(modality);
124121

125122
// Plan-specific behaviour is owned by core `plans.ts`. The strategy resolves
@@ -165,10 +162,10 @@ export const deployTextCreate = defineCommand({
165162
usageArgs: CREATE_USAGE,
166163
flags: CREATE_FLAGS,
167164
exampleArgs: [
168-
"--model my-qwen-sft --name my-sft-test",
169-
"--model qwen3.6-flash-2026-04-16 --name my-flash --plan ptu --input-tpm 10000 --output-tpm 1000",
170-
"--model qwen3-8b --name my-qwen3-mu --plan mu",
171-
"--model qwen3-8b --name my-qwen3 --plan mu --deploy-spec MU1 --capacity 2",
165+
"--model-name my-qwen-sft --display-name my-sft-test",
166+
"--model-name qwen3.6-flash-2026-04-16 --display-name my-flash --plan ptu --input-tpm 10000 --output-tpm 1000",
167+
"--model-name qwen3-8b --display-name my-qwen3-mu --plan mu",
168+
"--model-name qwen3-8b --display-name my-qwen3 --plan mu --deploy-spec MU1 --capacity 2",
172169
],
173170
notes: CREATE_NOTES,
174171
validate: (flags) => validateCreate("text", flags),
@@ -182,9 +179,9 @@ export const deployAudioCreate = defineCommand({
182179
usageArgs: CREATE_USAGE,
183180
flags: CREATE_FLAGS,
184181
exampleArgs: [
185-
"--model my-cosyvoice-ft --name my-tts",
186-
"--model my-cosyvoice-ft --name my-tts --deploy-spec dps-xxxx --capacity 1",
187-
"--model my-cosyvoice-ft --name my-tts --dry-run",
182+
"--model-name my-cosyvoice-ft --display-name my-tts",
183+
"--model-name my-cosyvoice-ft --display-name my-tts --deploy-spec dps-xxxx --capacity 1",
184+
"--model-name my-cosyvoice-ft --display-name my-tts --dry-run",
188185
],
189186
notes: CREATE_NOTES,
190187
validate: (flags) => validateCreate("audio", flags),
@@ -198,9 +195,9 @@ export const deployImageCreate = defineCommand({
198195
usageArgs: CREATE_USAGE,
199196
flags: CREATE_FLAGS,
200197
exampleArgs: [
201-
"--model my-wan-ft --name my-wan",
202-
"--model my-wan-ft --name my-wan-mu --plan mu",
203-
"--model my-wan-ft --name my-wan --dry-run",
198+
"--model-name my-wan-ft --display-name my-wan",
199+
"--model-name my-wan-ft --display-name my-wan-mu --plan mu",
200+
"--model-name my-wan-ft --display-name my-wan --dry-run",
204201
],
205202
notes: CREATE_NOTES,
206203
validate: (flags) => validateCreate("image", flags),

packages/commands/src/commands/finetune/capability.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ async function fetchAllFoundationModels(settings: Settings): Promise<ModelCapabi
4343
}
4444

4545
const CAPABILITY_FLAGS = {
46-
model: {
46+
baseModel: {
4747
type: "string",
4848
valueHint: "<m>",
4949
description: "List training types supported by this base model.",
@@ -59,29 +59,30 @@ export default defineCommand({
5959
description:
6060
"Query fine-tune training capability — by model (which training types it supports) or by training type (which models support it)",
6161
auth: "none",
62-
usageArgs: "--model <m> | --training-type <t>",
62+
usageArgs: "--base-model <m> | --training-type <t>",
6363
flags: CAPABILITY_FLAGS,
6464
exampleArgs: [
65-
"--model qwen3-8b",
65+
"--base-model qwen3-8b",
6666
"--training-type sft-lora",
6767
"--training-type cpt --output json",
6868
"--training-type sft --quiet",
6969
],
7070
notes: [
71-
"Exactly one of --model / --training-type is required.",
71+
"Exactly one of --base-model / --training-type is required.",
7272
"Training-type values use the `<method>` / `<method>-lora` convention:",
7373
"sft | sft-lora | dpo | dpo-lora | cpt. (cpt has no -lora variant server-side.)",
7474
"Queries listFoundationModels, a public API — no console login needed.",
7575
],
7676
validate: (f) => {
77-
if (f.model && f.trainingType)
78-
return "--model and --training-type are mutually exclusive; pass one.";
79-
if (!f.model && !f.trainingType) return "one of --model / --training-type is required.";
77+
if (f.baseModel && f.trainingType)
78+
return "--base-model and --training-type are mutually exclusive; pass one.";
79+
if (!f.baseModel && !f.trainingType)
80+
return "one of --base-model / --training-type is required.";
8081
return undefined;
8182
},
8283
async run(ctx) {
8384
const { settings, flags } = ctx;
84-
const model = flags.model || undefined;
85+
const model = flags.baseModel || undefined;
8586
const trainingType = flags.trainingType || undefined;
8687

8788
if (settings.dryRun) {

packages/commands/src/commands/finetune/checkpoints.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default defineCommand({
1919
flags: CHECKPOINTS_FLAGS,
2020
exampleArgs: ["--job-id ft-xxx", "--job-id ft-xxx --output json"],
2121
notes: [
22-
"`model_name` (shown for SUCCEEDED checkpoints) is the direct input for `deploy create --model`.",
22+
"`model_name` (shown for SUCCEEDED checkpoints) is the direct input for `deploy create --model-name`.",
2323
"Checkpoints expire ~15 days after creation; `expire_time` shows the deadline. Export or deploy before expiry.",
2424
],
2525
async run(ctx) {

packages/commands/src/commands/finetune/create.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,10 @@ type CommandModality = "text" | "audio" | "image";
215215
* output. Every modality's model consumes these.
216216
*/
217217
const COMMON_FLAGS = {
218-
model: {
218+
baseModel: {
219219
type: "string",
220220
valueHint: "<model>",
221-
description: "Base model to fine-tune",
221+
description: "Base model to fine-tune (e.g. qwen3-8b; not the output model name)",
222222
required: true,
223223
},
224224
datasets: {
@@ -316,13 +316,13 @@ const IMAGE_FLAGS = {
316316
} satisfies FlagsDef;
317317

318318
const TEXT_USAGE =
319-
"--model <model> --datasets <id|path,...> [--validations <id|path,...>] [--model-name <name>] [--suffix <text>] [--n-epochs <n>] [--batch-size <n>] [--learning-rate <str>] [--max-length <n>] [--training-type <sft|sft-lora|dpo|dpo-lora|cpt>]";
319+
"--base-model <model> --datasets <id|path,...> [--validations <id|path,...>] [--model-name <name>] [--suffix <text>] [--n-epochs <n>] [--batch-size <n>] [--learning-rate <str>] [--max-length <n>] [--training-type <sft|sft-lora|dpo|dpo-lora|cpt>]";
320320

321321
const AUDIO_USAGE =
322-
"--model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>]";
322+
"--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>]";
323323

324324
const IMAGE_USAGE =
325-
"--model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>] [--generation-type <t2i|i2i>] [--learning-rate <str>]";
325+
"--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>] [--generation-type <t2i|i2i>] [--learning-rate <str>]";
326326

327327
const COMMON_NOTES = [
328328
"Creating a job uploads any local datasets and consumes training quota.",
@@ -382,7 +382,7 @@ async function runCreate<F extends FlagsDef>(
382382
): Promise<void> {
383383
const { identity, settings } = ctx;
384384
const flags = ctx.flags as Record<string, unknown>;
385-
const model = flags.model as string;
385+
const model = flags.baseModel as string;
386386
const datasetsRaw = flags.datasets as string;
387387

388388
// CosyVoice audio fine-tuning accepts exactly one training file
@@ -636,14 +636,14 @@ export const finetuneTextCreate = defineCommand({
636636
usageArgs: TEXT_USAGE,
637637
flags: TEXT_FLAGS,
638638
exampleArgs: [
639-
"--model qwen3-8b --datasets file-xxx",
640-
"--model qwen3-8b --datasets ./train.jsonl",
641-
"--model qwen3-8b --datasets ./train.jsonl --validations ./eval.jsonl",
642-
"--model qwen3-8b --datasets file-aaa,./extra.jsonl",
643-
"--model qwen3-8b --datasets ./train.jsonl --training-type sft",
644-
'--model qwen3-8b --datasets file-xxx --learning-rate "1.6e-5" --n-epochs 4',
645-
"--model qwen3-8b --datasets file-xxx --output json",
646-
"--model qwen3-8b --datasets file-xxx --dry-run",
639+
"--base-model qwen3-8b --datasets file-xxx",
640+
"--base-model qwen3-8b --datasets ./train.jsonl",
641+
"--base-model qwen3-8b --datasets ./train.jsonl --validations ./eval.jsonl",
642+
"--base-model qwen3-8b --datasets file-aaa,./extra.jsonl",
643+
"--base-model qwen3-8b --datasets ./train.jsonl --training-type sft",
644+
'--base-model qwen3-8b --datasets file-xxx --learning-rate "1.6e-5" --n-epochs 4',
645+
"--base-model qwen3-8b --datasets file-xxx --output json",
646+
"--base-model qwen3-8b --datasets file-xxx --dry-run",
647647
],
648648
notes: TEXT_NOTES,
649649
run: (ctx) => runCreate("text", ctx),
@@ -656,11 +656,11 @@ export const finetuneAudioCreate = defineCommand({
656656
usageArgs: AUDIO_USAGE,
657657
flags: AUDIO_FLAGS,
658658
exampleArgs: [
659-
"--model cosyvoice-v3-flash --datasets ./audio.zip",
660-
"--model cosyvoice-v3-flash --datasets file-xxx",
661-
"--model cosyvoice-v3-flash --datasets ./audio.zip --model-name my-tts",
662-
"--model cosyvoice-v3-flash --datasets file-xxx --output json",
663-
"--model cosyvoice-v3-flash --datasets ./audio.zip --dry-run",
659+
"--base-model cosyvoice-v3-flash --datasets ./audio.zip",
660+
"--base-model cosyvoice-v3-flash --datasets file-xxx",
661+
"--base-model cosyvoice-v3-flash --datasets ./audio.zip --model-name my-tts",
662+
"--base-model cosyvoice-v3-flash --datasets file-xxx --output json",
663+
"--base-model cosyvoice-v3-flash --datasets ./audio.zip --dry-run",
664664
],
665665
notes: AUDIO_NOTES,
666666
run: (ctx) => runCreate("audio", ctx),
@@ -673,12 +673,12 @@ export const finetuneImageCreate = defineCommand({
673673
usageArgs: IMAGE_USAGE,
674674
flags: IMAGE_FLAGS,
675675
exampleArgs: [
676-
"--model wan2.7-image-pro --datasets ./images.zip",
677-
"--model wan2.7-image-pro --datasets file-xxx",
678-
"--model wan2.7-image-pro --datasets file-xxx --generation-type i2i",
679-
"--model wan2.7-image-pro --datasets ./images.zip --model-name my-wan",
680-
"--model wan2.7-image-pro --datasets file-xxx --output json",
681-
"--model wan2.7-image-pro --datasets ./images.zip --dry-run",
676+
"--base-model wan2.7-image-pro --datasets ./images.zip",
677+
"--base-model wan2.7-image-pro --datasets file-xxx",
678+
"--base-model wan2.7-image-pro --datasets file-xxx --generation-type i2i",
679+
"--base-model wan2.7-image-pro --datasets ./images.zip --model-name my-wan",
680+
"--base-model wan2.7-image-pro --datasets file-xxx --output json",
681+
"--base-model wan2.7-image-pro --datasets ./images.zip --dry-run",
682682
],
683683
notes: IMAGE_NOTES,
684684
run: (ctx) => runCreate("image", ctx),

0 commit comments

Comments
 (0)