@@ -23,14 +23,15 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
2323 } ) ;
2424
2525 test ( "finetune create --help 正常退出并展示必填项" , async ( ) => {
26- const { stderr, exitCode } = await runCli ( [ "finetune" , "create" , "--help" ] ) ;
26+ const { stderr, exitCode } = await runCli ( [ "finetune" , "text" , " create", "--help" ] ) ;
2727 expect ( exitCode , stderr ) . toBe ( 0 ) ;
2828 expect ( stderr ) . toMatch ( / - - m o d e l | - - d a t a s e t s / i) ;
2929 } ) ;
3030
3131 test ( "finetune create --dry-run 构造 SFT 默认请求体" , async ( ) => {
3232 const { stdout, stderr, exitCode } = await runCli ( [
3333 "finetune" ,
34+ "text" ,
3435 "create" ,
3536 "--model" ,
3637 "qwen3-8b" ,
@@ -64,6 +65,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
6465 test ( "finetune create --dry-run 转发训练类型与超参" , async ( ) => {
6566 const { stdout, stderr, exitCode } = await runCli ( [
6667 "finetune" ,
68+ "text" ,
6769 "create" ,
6870 "--model" ,
6971 "qwen3-8b" ,
@@ -125,6 +127,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
125127 async ( cliType , serverType ) => {
126128 const { stdout, stderr, exitCode } = await runCli ( [
127129 "finetune" ,
130+ "text" ,
128131 "create" ,
129132 "--model" ,
130133 "qwen3-8b" ,
@@ -146,6 +149,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
146149 test ( "finetune create --training-type 拒绝不支持的训练类型值" , async ( ) => {
147150 const { stdout, stderr, exitCode } = await runCli ( [
148151 "finetune" ,
152+ "text" ,
149153 "create" ,
150154 "--model" ,
151155 "qwen3-8b" ,
@@ -164,6 +168,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
164168 const localPath = join ( cliPackageRoot , "tests" , "e2e" , ".dataset-valid.jsonl" ) ;
165169 const { stdout, stderr, exitCode } = await runCli ( [
166170 "finetune" ,
171+ "text" ,
167172 "create" ,
168173 "--model" ,
169174 "qwen3-8b" ,
@@ -194,6 +199,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
194199 test ( "finetune create --datasets 为空时拒绝" , async ( ) => {
195200 const { stdout, stderr, exitCode } = await runCli ( [
196201 "finetune" ,
202+ "text" ,
197203 "create" ,
198204 "--model" ,
199205 "qwen3-8b" ,
@@ -214,6 +220,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
214220 const localPath = join ( cliPackageRoot , "tests" , "e2e" , ".dataset-valid.jsonl" ) ;
215221 const { stdout, stderr, exitCode } = await runCli ( [
216222 "finetune" ,
223+ "text" ,
217224 "create" ,
218225 "--model" ,
219226 "qwen3-8b" ,
@@ -235,6 +242,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
235242 const localPath = join ( cliPackageRoot , "tests" , "e2e" , ".dataset-valid.jsonl" ) ;
236243 const { stdout, stderr, exitCode } = await runCli ( [
237244 "finetune" ,
245+ "text" ,
238246 "create" ,
239247 "--model" ,
240248 "qwen3-8b" ,
@@ -276,6 +284,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
276284 test ( "finetune create --dry-run 解析多 datasets 中的空白" , async ( ) => {
277285 const { stdout, stderr, exitCode } = await runCli ( [
278286 "finetune" ,
287+ "text" ,
279288 "create" ,
280289 "--model" ,
281290 "qwen3-8b" ,
@@ -291,6 +300,60 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
291300 } > ( stdout ) ;
292301 expect ( data . body . training_file_ids ) . toEqual ( [ "file-a" , "file-b" ] ) ;
293302 } ) ;
303+
304+ test ( "finetune audio create --dry-run 用 sft-lora 默认 + audio 超参" , async ( ) => {
305+ // Audio has no --training-type flag; the command fixes modality=audio and
306+ // defaults to sft-lora (efficient_sft) with the fixed CosyVoice hyper-params.
307+ const { stdout, stderr, exitCode } = await runCli ( [
308+ "finetune" ,
309+ "audio" ,
310+ "create" ,
311+ "--model" ,
312+ "cosyvoice-v3-flash" ,
313+ "--datasets" ,
314+ "file-audio" ,
315+ "--dry-run" ,
316+ "--output" ,
317+ "json" ,
318+ ] ) ;
319+ expect ( exitCode , stderr ) . toBe ( 0 ) ;
320+ const data = parseStdoutJson < {
321+ action : string ;
322+ body : { training_type : string ; hyper_parameters : Record < string , unknown > } ;
323+ } > ( stdout ) ;
324+ expect ( data . action ) . toBe ( "finetune.create" ) ;
325+ expect ( data . body . training_type ) . toBe ( "efficient_sft" ) ;
326+ // Audio TTS defaults are fixed (not the text n_epochs/batch_size surface).
327+ expect ( data . body . hyper_parameters . lm_max_epoch ) . toBeDefined ( ) ;
328+ } ) ;
329+
330+ test ( "finetune audio create --help 不暴露文本超参 flag" , async ( ) => {
331+ // Audio models don't consume --training-type / --n-epochs / --batch-size /
332+ // --learning-rate / --max-length, so those flags are not offered.
333+ const { stderr, exitCode } = await runCli ( [ "finetune" , "audio" , "create" , "--help" ] ) ;
334+ expect ( exitCode , stderr ) . toBe ( 0 ) ;
335+ expect ( stderr ) . toMatch ( / - - m o d e l | - - d a t a s e t s / i) ;
336+ expect ( stderr ) . not . toMatch ( / - - t r a i n i n g - t y p e | - - n - e p o c h s | - - b a t c h - s i z e | - - m a x - l e n g t h / ) ;
337+ } ) ;
338+
339+ test ( "finetune image create --dry-run 用 sft-lora 默认" , async ( ) => {
340+ const { stdout, stderr, exitCode } = await runCli ( [
341+ "finetune" ,
342+ "image" ,
343+ "create" ,
344+ "--model" ,
345+ "wan2.7-image-pro" ,
346+ "--datasets" ,
347+ "file-image" ,
348+ "--dry-run" ,
349+ "--output" ,
350+ "json" ,
351+ ] ) ;
352+ expect ( exitCode , stderr ) . toBe ( 0 ) ;
353+ const data = parseStdoutJson < { action : string ; body : { training_type : string } } > ( stdout ) ;
354+ expect ( data . action ) . toBe ( "finetune.create" ) ;
355+ expect ( data . body . training_type ) . toBe ( "efficient_sft" ) ;
356+ } ) ;
294357} ) ;
295358
296359describe . skipIf ( ! isDashScopeE2EReady ( ) ) ( "e2e: finetune (DashScope)" , ( ) => {
0 commit comments