@@ -264,12 +264,104 @@ const COMMON_FLAGS = {
264264
265265/**
266266 * Text flags: text models consume the full hyper-parameter surface — training
267- * type selection plus n_epochs / batch_size / learning_rate / max_length (see
267+ * type selection, base parameters and explicit LoRA/evaluation/save settings (see
268268 * resolveTextHyperParameters). Only text exposes --training-type because only
269269 * text models support types other than the sft-lora default.
270270 */
271271const TEXT_FLAGS = {
272272 ...COMMON_FLAGS ,
273+ jobName : {
274+ type : "string" ,
275+ valueHint : "<value>" ,
276+ description : {
277+ "en-US" : "Training job display name (job_name)" ,
278+ "zh-CN" : "训练任务显示名称(job_name)" ,
279+ } ,
280+ } ,
281+ priority : {
282+ type : "string" ,
283+ valueHint : "<value>" ,
284+ description : {
285+ "en-US" : "Requested scheduling priority; verify the service response" ,
286+ "zh-CN" : "请求的调度优先级;请核对服务端回执" ,
287+ } ,
288+ choices : [ "L0" , "L1" , "L2" , "L3" ] as const ,
289+ } ,
290+ evalSteps : {
291+ type : "number" ,
292+ valueHint : "<value>" ,
293+ description : { "en-US" : "Validation interval in training steps" , "zh-CN" : "训练验证间隔步数" } ,
294+ } ,
295+ loraAlpha : {
296+ type : "number" ,
297+ valueHint : "<value>" ,
298+ description : { "en-US" : "LoRA scaling coefficient" , "zh-CN" : "LoRA 缩放系数" } ,
299+ } ,
300+ loraDropout : {
301+ type : "number" ,
302+ valueHint : "<value>" ,
303+ description : { "en-US" : "LoRA dropout probability" , "zh-CN" : "LoRA 丢弃率" } ,
304+ } ,
305+ loraRank : {
306+ type : "number" ,
307+ valueHint : "<value>" ,
308+ description : { "en-US" : "LoRA matrix rank" , "zh-CN" : "LoRA 矩阵秩" } ,
309+ } ,
310+ lrSchedulerType : {
311+ type : "string" ,
312+ valueHint : "<value>" ,
313+ description : {
314+ "en-US" : "Learning rate scheduler supported by the selected model" ,
315+ "zh-CN" : "所选模型支持的学习率调度策略" ,
316+ } ,
317+ } ,
318+ saveStrategy : {
319+ type : "string" ,
320+ valueHint : "<value>" ,
321+ description : { "en-US" : "Checkpoint saving strategy" , "zh-CN" : "Checkpoint 保存策略" } ,
322+ choices : [ "epoch" , "steps" ] as const ,
323+ } ,
324+ saveTotalLimit : {
325+ type : "number" ,
326+ valueHint : "<value>" ,
327+ description : {
328+ "en-US" : "Maximum number of saved checkpoints" ,
329+ "zh-CN" : "最多保存的 Checkpoint 数量" ,
330+ } ,
331+ } ,
332+ saveSteps : {
333+ type : "number" ,
334+ valueHint : "<value>" ,
335+ description : {
336+ "en-US" : "Checkpoint saving interval for strategy=steps" ,
337+ "zh-CN" : "按 steps 保存时的间隔" ,
338+ } ,
339+ } ,
340+ split : {
341+ type : "number" ,
342+ valueHint : "<value>" ,
343+ description : {
344+ "en-US" : "Training fraction when no validation dataset is supplied" ,
345+ "zh-CN" : "未指定验证集时训练集所占比例" ,
346+ } ,
347+ } ,
348+ maxSplitValDatasetSample : {
349+ type : "number" ,
350+ valueHint : "<value>" ,
351+ description : {
352+ "en-US" : "Maximum automatically split validation samples" ,
353+ "zh-CN" : "自动切分验证集的样本数量上限" ,
354+ } ,
355+ } ,
356+ dataAugmentation : {
357+ type : "string" ,
358+ valueHint : "<value>" ,
359+ description : {
360+ "en-US" : "Mix platform training data (true or false)" ,
361+ "zh-CN" : "是否混入平台训练数据(true 或 false)" ,
362+ } ,
363+ choices : [ "true" , "false" ] as const ,
364+ } ,
273365 trainingType : {
274366 type : "string" ,
275367 valueHint : "<t>" ,
@@ -348,7 +440,7 @@ const IMAGE_FLAGS = {
348440} satisfies FlagsDef ;
349441
350442const TEXT_USAGE =
351- "--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>]" ;
443+ "--base-model <model> --datasets <id|path,...> [--validations <id|path,...>] [--job-name <name>] [--priority <L0|L1|L2|L3>] [-- 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>]" ;
352444
353445const AUDIO_USAGE =
354446 "--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>]" ;
@@ -573,6 +665,77 @@ async function runCreate<F extends FlagsDef>(
573665 flags as Record < string , unknown > ,
574666 ) as FineTuneHyperParameters ;
575667
668+ if ( commandModality === "text" ) {
669+ const extraParameters : Record < string , string > = {
670+ evalSteps : "eval_steps" ,
671+ loraAlpha : "lora_alpha" ,
672+ loraDropout : "lora_dropout" ,
673+ loraRank : "lora_rank" ,
674+ lrSchedulerType : "lr_scheduler_type" ,
675+ saveStrategy : "save_strategy" ,
676+ saveTotalLimit : "save_total_limit" ,
677+ saveSteps : "save_steps" ,
678+ split : "split" ,
679+ maxSplitValDatasetSample : "max_split_val_dataset_sample" ,
680+ } ;
681+ for ( const [ flagName , parameterName ] of Object . entries ( extraParameters ) ) {
682+ const value = flags [ flagName ] ;
683+ if ( value !== undefined ) hp [ parameterName ] = value ;
684+ }
685+ for ( const parameterName of [
686+ "eval_steps" ,
687+ "lora_alpha" ,
688+ "lora_rank" ,
689+ "save_total_limit" ,
690+ "save_steps" ,
691+ "max_split_val_dataset_sample" ,
692+ ] ) {
693+ const value = hp [ parameterName ] ;
694+ if (
695+ value !== undefined &&
696+ ( typeof value !== "number" || ! Number . isInteger ( value ) || value <= 0 )
697+ ) {
698+ throw new BailianError (
699+ `${ parameterName } must be a positive integer. / 必须为正整数。` ,
700+ ExitCode . USAGE ,
701+ ) ;
702+ }
703+ }
704+ if (
705+ hp . lora_dropout !== undefined &&
706+ ( typeof hp . lora_dropout !== "number" ||
707+ ! Number . isFinite ( hp . lora_dropout ) ||
708+ hp . lora_dropout < 0 ||
709+ hp . lora_dropout >= 1 )
710+ ) {
711+ throw new BailianError (
712+ "lora_dropout must be in [0, 1). / LoRA 丢弃率必须在 [0, 1) 内。" ,
713+ ExitCode . USAGE ,
714+ ) ;
715+ }
716+ if (
717+ hp . split !== undefined &&
718+ ( typeof hp . split !== "number" ||
719+ ! Number . isFinite ( hp . split ) ||
720+ hp . split <= 0 ||
721+ hp . split >= 1 ||
722+ flags . validations )
723+ ) {
724+ throw new BailianError (
725+ "split must be in (0, 1) and cannot be combined with validations. / 切分比例须在 (0, 1) 内,且不能与独立验证集同时设置。" ,
726+ ExitCode . USAGE ,
727+ ) ;
728+ }
729+ if ( flags . dataAugmentation !== undefined )
730+ hp . data_augmentation = flags . dataAugmentation === "true" ;
731+ if ( hp . save_strategy === "steps" && hp . save_steps === undefined ) {
732+ throw new BailianError (
733+ "save_strategy=steps requires --save-steps. / 按步保存时必须指定 --save-steps。" ,
734+ ExitCode . USAGE ,
735+ ) ;
736+ }
737+ }
738+
576739 // Restore the batch-size clamping warning that was lost when the logic moved
577740 // into profiles. The profile silently clamps to [8, 1024]; surface it here
578741 // so the user has an audit trail. Skip modalities that bypass the batch_size
@@ -699,6 +862,8 @@ async function runCreate<F extends FlagsDef>(
699862 if ( validationFileIds && validationFileIds . length > 0 ) {
700863 body . validation_file_ids = validationFileIds ;
701864 }
865+ if ( typeof flags . jobName === "string" ) body . job_name = flags . jobName ;
866+ if ( typeof flags . priority === "string" ) body . priority = flags . priority ;
702867 if ( modelName ) body . model_name = modelName ;
703868 if ( suffix ) body . finetuned_output_suffix = suffix ;
704869
@@ -741,6 +906,7 @@ export const finetuneTextCreate = defineCommand({
741906 "--base-model qwen3-8b --datasets ./train.jsonl --validations ./eval.jsonl" ,
742907 "--base-model qwen3-8b --datasets file-aaa,./extra.jsonl" ,
743908 "--base-model qwen3-8b --datasets ./train.jsonl --training-type sft" ,
909+ "--base-model qwen3-8b --datasets ./jev-train.jsonl --job-name jev-train-v1 --priority L0 --lora-rank 8 --lora-alpha 16 --lora-dropout 0.1 --lr-scheduler-type linear --eval-steps 50 --save-strategy epoch --save-total-limit 3 --split 0.9 --max-split-val-dataset-sample 1000 --data-augmentation false --dry-run" ,
744910 '--base-model qwen3-8b --datasets file-xxx --learning-rate "1.6e-5" --n-epochs 4' ,
745911 "--base-model qwen3-8b --datasets file-xxx --output json" ,
746912 "--base-model qwen3-8b --datasets file-xxx --dry-run" ,
0 commit comments