bailian-cli-commands 2.0.1 → 2.1.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/dist/index.d.mts CHANGED
@@ -6030,6 +6030,113 @@ declare const _default$30: import("bailian-cli-core").Command<{
6030
6030
  //#region src/commands/finetune/create.d.ts
6031
6031
  /** `bl finetune text create` — fine-tune a text model. Datasets are `.jsonl`. */
6032
6032
  export declare const finetuneTextCreate: import("bailian-cli-core").Command<{
6033
+ jobName: {
6034
+ type: "string";
6035
+ valueHint: string;
6036
+ description: {
6037
+ "en-US": string;
6038
+ "zh-CN": string;
6039
+ };
6040
+ };
6041
+ priority: {
6042
+ type: "string";
6043
+ valueHint: string;
6044
+ description: {
6045
+ "en-US": string;
6046
+ "zh-CN": string;
6047
+ };
6048
+ choices: readonly ["L0", "L1", "L2", "L3"];
6049
+ };
6050
+ evalSteps: {
6051
+ type: "number";
6052
+ valueHint: string;
6053
+ description: {
6054
+ "en-US": string;
6055
+ "zh-CN": string;
6056
+ };
6057
+ };
6058
+ loraAlpha: {
6059
+ type: "number";
6060
+ valueHint: string;
6061
+ description: {
6062
+ "en-US": string;
6063
+ "zh-CN": string;
6064
+ };
6065
+ };
6066
+ loraDropout: {
6067
+ type: "number";
6068
+ valueHint: string;
6069
+ description: {
6070
+ "en-US": string;
6071
+ "zh-CN": string;
6072
+ };
6073
+ };
6074
+ loraRank: {
6075
+ type: "number";
6076
+ valueHint: string;
6077
+ description: {
6078
+ "en-US": string;
6079
+ "zh-CN": string;
6080
+ };
6081
+ };
6082
+ lrSchedulerType: {
6083
+ type: "string";
6084
+ valueHint: string;
6085
+ description: {
6086
+ "en-US": string;
6087
+ "zh-CN": string;
6088
+ };
6089
+ };
6090
+ saveStrategy: {
6091
+ type: "string";
6092
+ valueHint: string;
6093
+ description: {
6094
+ "en-US": string;
6095
+ "zh-CN": string;
6096
+ };
6097
+ choices: readonly ["epoch", "steps"];
6098
+ };
6099
+ saveTotalLimit: {
6100
+ type: "number";
6101
+ valueHint: string;
6102
+ description: {
6103
+ "en-US": string;
6104
+ "zh-CN": string;
6105
+ };
6106
+ };
6107
+ saveSteps: {
6108
+ type: "number";
6109
+ valueHint: string;
6110
+ description: {
6111
+ "en-US": string;
6112
+ "zh-CN": string;
6113
+ };
6114
+ };
6115
+ split: {
6116
+ type: "number";
6117
+ valueHint: string;
6118
+ description: {
6119
+ "en-US": string;
6120
+ "zh-CN": string;
6121
+ };
6122
+ };
6123
+ maxSplitValDatasetSample: {
6124
+ type: "number";
6125
+ valueHint: string;
6126
+ description: {
6127
+ "en-US": string;
6128
+ "zh-CN": string;
6129
+ };
6130
+ };
6131
+ dataAugmentation: {
6132
+ type: "string";
6133
+ valueHint: string;
6134
+ description: {
6135
+ "en-US": string;
6136
+ "zh-CN": string;
6137
+ };
6138
+ choices: readonly ["true", "false"];
6139
+ };
6033
6140
  trainingType: {
6034
6141
  type: "string";
6035
6142
  valueHint: string;
package/dist/index.mjs CHANGED
@@ -2829,8 +2829,8 @@ Request/response content is only available when inference log delivery was enabl
2829
2829
  `:`One-key authorization closed; historical inference grants cleared.
2830
2830
  `);return}process.stdout.write(`Permissions ${c} (${i.join(`, `)}): ${a.join(`, `)}\n`)}var Oy=S({description:{"en-US":`Grant model permissions (inference / finetune / deploy)`,"zh-CN":`授予模型权限(推理 / 微调 / 部署)`},auth:`apiKey`,usageArgs:`--model <models> [--action <actions>] | --all`,flags:{model:{type:`string`,valueHint:`<models>`,description:{"en-US":`Model ID(s), comma-separated (max 20)`,"zh-CN":`模型 ID,多个以逗号分隔(最多 20 个)`}},action:{type:`string`,valueHint:`<actions>`,description:{"en-US":`Permission action(s), comma-separated: inference, finetune, deploy (default: inference)`,"zh-CN":`权限操作,多个以逗号分隔:inference、finetune、deploy(默认:inference)`}},all:{type:`switch`,description:{"en-US":`One-key grant inference for all models in the workspace (including future ones)`,"zh-CN":`一键授予 Workspace 中所有模型的推理权限(包括未来新增模型)`}}},exampleArgs:[`--model qwen-plus`,`--model qwen-plus,qwen3-max --action inference,finetune`,`--all`,`--model qwen-plus --dry-run --output json`],notes:[{"en-US":`Grants apply to the business workspace your API key belongs to.`,"zh-CN":`授权将应用于 API Key 所属的业务 Workspace。`},{"en-US":`--all maps to the server one-key switch (access_all_entities: OPEN) and only covers inference.`,"zh-CN":`--all 对应服务端一键授权开关(access_all_entities: OPEN),仅涵盖推理权限。`},{"en-US":`Actions you omit keep their current grants (server-side tri-state patch).`,"zh-CN":`未指定的操作将保留当前授权状态(服务端三态增量更新)。`}],validate:e=>Ey(e),async run(e){await Dy(e,e.flags,!0)}}),ky=S({description:{"en-US":`Revoke model permissions (inference / finetune / deploy)`,"zh-CN":`撤销模型权限(推理 / 微调 / 部署)`},auth:`apiKey`,risk:{level:`high`,message:{"en-US":`This revokes model permissions and may interrupt inference, fine-tuning, or deployment workloads. With --all, it also clears all historical inference grants.`,"zh-CN":`该操作会撤销模型权限,可能导致推理、精调或部署任务中断;使用 --all 时会清除全部历史推理授权。`}},usageArgs:`--model <models> [--action <actions>] | --all [flags]`,flags:{model:{type:`string`,valueHint:`<models>`,description:{"en-US":`Model ID(s), comma-separated (max 20)`,"zh-CN":`模型 ID,多个以逗号分隔(最多 20 个)`}},action:{type:`string`,valueHint:`<actions>`,description:{"en-US":`Permission action(s), comma-separated: inference, finetune, deploy (default: inference)`,"zh-CN":`权限操作,多个以逗号分隔:inference、finetune、deploy(默认:inference)`}},all:{type:`switch`,description:{"en-US":`Close one-key authorization and clear ALL historical inference grants`,"zh-CN":`关闭一键授权并清除所有历史推理授权`}}},exampleArgs:[`--model qwen-plus --yes`,`--model qwen-plus,qwen3-max --action inference,finetune --yes`,`--all --yes`,`--model qwen-plus --dry-run --output json`],notes:[{"en-US":`Grants apply to the business workspace your API key belongs to.`,"zh-CN":`授权将应用于 API Key 所属的业务 Workspace。`},{"en-US":`All revoke operations require --yes; use --dry-run to preview the request without confirmation.`,"zh-CN":`所有撤权操作均需使用 --yes;可通过 --dry-run 免确认预览请求。`},{"en-US":`--all maps to the server one-key switch (access_all_entities: CLOSE): it clears every historical inference grant and cannot be undone, so it requires --yes.`,"zh-CN":`--all 对应服务端一键授权开关(access_all_entities: CLOSE):它会清除所有历史推理授权且无法撤销,因此必须同时传入 --yes。`},{"en-US":`Actions you omit keep their current grants (server-side tri-state patch).`,"zh-CN":`未指定的操作将保留当前授权状态(服务端三态增量更新)。`}],validate:e=>Ey(e),async run(e){await Dy(e,e.flags,!1)}}),Ay=S({description:{"en-US":`Upload a dataset file (.jsonl or .zip) to Bailian`,"zh-CN":`将数据集文件(.jsonl 或 .zip)上传到百炼`},auth:`apiKey`,usageArgs:`--file <path> [--purpose <name>] [--schema <chatml|dpo|cpt|tts|image|video>] [--no-validate] [--full-validate]`,flags:{file:{type:`string`,valueHint:`<path>`,description:{"en-US":`Local dataset file (.jsonl or .zip; ≤200MB SFT/DPO, ≤300MB CPT, ≤2GB media zip)`,"zh-CN":`本地数据集文件(.jsonl 或 .zip;SFT/DPO 不超过 200MB,CPT 不超过 300MB,媒体 ZIP 不超过 2GB)`},required:!0},purpose:{type:`string`,valueHint:`<name>`,description:{"en-US":`Dataset purpose tag (default: "fine-tune"; e.g. "evaluation")`,"zh-CN":`数据集用途标签(默认:"fine-tune";例如 "evaluation")`}},schema:{type:`string`,valueHint:`<s>`,description:{"en-US":`Record schema: "chatml" (SFT), "dpo" (chosen/rejected), "cpt" (raw text), "tts" (audio), "image" (image generation), or "video" (video generation). Default auto-detects per record.`,"zh-CN":`记录 Schema:"chatml"(SFT)、"dpo"(chosen/rejected)、"cpt"(原始文本)、"tts"(音频)、"image"(图片生成)或 "video"(视频生成)。默认逐条自动识别。`}},noValidate:{type:`switch`,description:{"en-US":`Skip the local JSONL pre-flight check (not recommended)`,"zh-CN":`跳过本地 JSONL 预检查(不推荐)`}},fullValidate:{type:`switch`,description:{"en-US":`JSON.parse every line instead of sampling (slower)`,"zh-CN":`使用 JSON.parse 检查每一行,而不是抽样检查(速度较慢)`}}},exampleArgs:[`--file train.jsonl`,`--file dpo.jsonl --schema dpo`,`--file cpt.jsonl --schema cpt`,`--file audio.zip --schema tts`,`--file eval.jsonl --purpose evaluation`,`--file train.jsonl --full-validate`,`--file train.jsonl --no-validate`],notes:[{"en-US":`Supports .jsonl (text) and .zip (audio/image/video archives with a data.jsonl manifest). Six record schemas are recognized: chatml = {messages:[...]} (SFT); dpo = {messages:[...], chosen, rejected}; cpt = {text:"..."} (continual pre-training, raw text); tts = {wav_fn:"train/xxx.wav", text:"..."} (audio fine-tuning); image = {img_path:"..."} (image generation); video = {first_frame_path:...} (video generation).`,"zh-CN":`支持 .jsonl(文本)和 .zip(包含 data.jsonl 清单的音频、图片或视频归档)。可识别六种记录 Schema:chatml = {messages:[...]}(SFT);dpo = {messages:[...], chosen, rejected};cpt = {text:"..."}(持续预训练,原始文本);tts = {wav_fn:"train/xxx.wav", text:"..."}(音频微调);image = {img_path:"..."}(图片生成);video = {first_frame_path:...}(视频生成)。`},{"en-US":`With no --schema, a record carrying wav_fn is validated as TTS, img_path as image, chosen/rejected as DPO, text (no messages) as CPT, otherwise ChatML.`,"zh-CN":`未指定 --schema 时,包含 wav_fn 的记录按 TTS 验证,包含 img_path 的按 image 验证,包含 chosen/rejected 的按 DPO 验证,仅含 text(无 messages)的按 CPT 验证,其他记录按 ChatML 验证。`},{"en-US":`Upload cap: 200MB SFT/DPO text, 300MB CPT, 2GB media zip. Upload uses the OpenAI-compatible /compatible-mode/v1/files endpoint so the purpose tag is persisted (the DashScope-native /api/v1/files drops it).`,"zh-CN":`上传上限:SFT/DPO 文本 200MB、CPT 300MB、媒体 ZIP 2GB。上传使用 OpenAI 兼容的 /compatible-mode/v1/files Endpoint,以便保留 purpose 标签;DashScope 原生 /api/v1/files 会丢弃该标签。`}],async run(e){let{identity:t,settings:r,flags:i}=e,a=i.file,o=i.purpose||`fine-tune`,s=Zt(i.schema),c=s===`image`||s===`video`?f:s===`cpt`?u:d;if(!i.noValidate){let e=await $n(a,{fullValidate:i.fullValidate,schema:s,maxBytes:c});if(!e.valid){let r=[`Dataset validation failed for ${a}`,...e.errors.slice(0,10).map(Ze)];throw e.errors.length>10&&r.push(` … and ${e.errors.length-10} more error(s).`),r.push(``,`Hint: re-run \`${t.binName} dataset validate --file <path>\` for the full report,`,` or pass --no-validate to skip this check at your own risk.`),new n(r.join(`
2831
2831
  `),l.GENERAL)}if(e.warnings.length>0&&!r.quiet){process.stderr.write(`Dataset validation passed with ${e.warnings.length} warning(s):\n`);for(let t of e.warnings.slice(0,5))process.stderr.write(`${Ze(t)}\n`);e.warnings.length>5&&process.stderr.write(` … and ${e.warnings.length-5} more.\n`)}}if(r.dryRun){O({action:`dataset.upload`,file:a,purpose:o,max_bytes:c,validate:!i.noValidate,schema:s??`auto`},`json`);return}let{request_id:p,...m}=await Zn(e.client,{filePath:a,purpose:o});r.quiet?D(m.file_id):O({...m,request_id:p},`json`)}}),jy=S({description:{"en-US":`List uploaded dataset files`,"zh-CN":`列出已上传的数据集文件`},auth:`apiKey`,usageArgs:`[--page <n>] [--page-size <n>] [--purpose <name>]`,flags:{page:{type:`number`,valueHint:`<n>`,description:{"en-US":`Page number (default: 1)`,"zh-CN":`页码(默认:1)`}},pageSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Results per page (default: 10, max 100)`,"zh-CN":`每页结果数(默认:10,最多:100)`}},purpose:{type:`string`,valueHint:`<name>`,description:{"en-US":`Filter by purpose (e.g. "fine-tune", "evaluation"). Omit to list all.`,"zh-CN":`按用途筛选(例如 "fine-tune"、"evaluation")。省略时列出全部。`}}},exampleArgs:[``,`--purpose fine-tune`,`--purpose evaluation --page-size 20`,`--output json`],async run(e){let{settings:t,flags:n}=e;if(t.dryRun){O({action:`dataset.list`,page:n.page,page_size:n.pageSize,purpose:n.purpose},`json`);return}let r=await kt(e.client,{pageNo:n.page,pageSize:n.pageSize,purpose:n.purpose||void 0}),i=r.data?.files??[],a=r.data?.total,o=i.map(e=>({file_id:e.file_id??``,name:e.name??``,size:e.size===void 0?`?`:`${(e.size/1024).toFixed(1)} KB`,purpose:e.purpose??``}));if(t.quiet)for(let e of o)D(e.file_id);else O({items:o,total:a,request_id:r.request_id},`json`)}}),My=S({description:{"en-US":`Get details of a single dataset file`,"zh-CN":`获取单个数据集文件的详情`},auth:`apiKey`,usageArgs:`--file-id <id>`,flags:{fileId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Dataset file ID (required)`,"zh-CN":`数据集文件 ID(必填)`},required:!0}},exampleArgs:[`--file-id file-xxx`,`--file-id file-xxx --output json`],async run(e){let{settings:t,flags:n}=e,r=n.fileId;if(t.dryRun){O({action:`dataset.get`,file_id:r},`json`);return}let i=await it(e.client,r),a=i.data;if(!a){D(`No data returned for ${r}`);return}let o=a.size===void 0?`?`:`${(a.size/1024).toFixed(1)} KB`,s={file_id:a.file_id??r,name:a.name??``,size:o,md5:a.md5??``,purpose:a.purpose??``,created_at:a.gmt_create??``,description:a.description??``};t.quiet?D(s.file_id):O({...s,request_id:i.request_id},`json`)}}),Ny=S({description:{"en-US":`Delete a dataset file by ID`,"zh-CN":`通过 ID 删除数据集文件`},auth:`apiKey`,risk:{level:`high`,message:{"en-US":`This permanently deletes the specified dataset file and cannot be undone.`,"zh-CN":`该操作会永久删除指定的数据集文件,且无法撤销。`}},usageArgs:`--file-id <id>`,flags:{fileId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Dataset file ID (required)`,"zh-CN":`数据集文件 ID(必填)`},required:!0}},exampleArgs:[`--file-id file-id-xxx`,`--file-id file-id-xxx --dry-run`,`--file-id file-id-xxx --yes`],async run(e){let{settings:t,flags:n}=e,r=n.fileId;if(t.dryRun){O({action:`dataset.delete`,file_id:r},`json`);return}let i=await Ae(e.client,r);t.quiet?D(r):O(i,`json`)}}),Py=S({description:{"en-US":`Locally validate a dataset file (.jsonl or .zip) without uploading`,"zh-CN":`在本地验证数据集文件(.jsonl 或 .zip),不执行上传`},auth:`none`,usageArgs:`--file <path> [--full-validate] [--schema <chatml|dpo|cpt|tts|image|video>]`,flags:{file:{type:`string`,valueHint:`<path>`,description:{"en-US":`Local dataset file (.jsonl or .zip)`,"zh-CN":`本地数据集文件(.jsonl 或 .zip)`},required:!0},fullValidate:{type:`switch`,description:{"en-US":`JSON.parse every line instead of sampling (slower)`,"zh-CN":`使用 JSON.parse 检查每一行,而不是抽样检查(速度较慢)`}},schema:{type:`string`,valueHint:`<s>`,description:{"en-US":`Record schema: "chatml" (SFT), "dpo" (chosen/rejected), "cpt" (raw text), "tts" (audio), "image" (image generation), or "video" (video generation). Default auto-detects per record.`,"zh-CN":`记录 Schema:"chatml"(SFT)、"dpo"(chosen/rejected)、"cpt"(原始文本)、"tts"(音频)、"image"(图片生成)或 "video"(视频生成)。默认逐条自动识别。`}}},exampleArgs:[`--file train.jsonl`,`--file dpo.jsonl --schema dpo`,`--file cpt.jsonl --schema cpt`,`--file audio.zip --schema tts`,`--file wan-i2v-training-dataset.zip --schema video`,`--file eval.jsonl --full-validate`,`--file train.jsonl --output json`],notes:[{"en-US":`Default scan: every line gets a structural check, then ~160 lines (front 50, evenly spaced 100, last 10) are JSON.parsed against the active schema.`,"zh-CN":`默认扫描:先对每一行进行结构检查,再抽取约 160 行(前 50 行、均匀抽取 100 行、最后 10 行),使用 JSON.parse 按当前 Schema 验证。`},{"en-US":`Schemas: chatml = {messages:[...]} (SFT); dpo = {messages:[...], chosen, rejected}; cpt = {text:"..."} (continual pre-training, raw text); tts = {wav_fn:"train/xxx.wav", text:"..."} (audio fine-tuning); image = {img_path:"..."} (image generation); video = {first_frame_path:"...", video_path:"..."} (video generation, i2v first-frame or kf2v first+last-frame with last_frame_path).`,"zh-CN":`Schema:chatml = {messages:[...]}(SFT);dpo = {messages:[...], chosen, rejected};cpt = {text:"..."}(持续预训练,原始文本);tts = {wav_fn:"train/xxx.wav", text:"..."}(音频微调);image = {img_path:"..."}(图片生成);video = {first_frame_path:"...", video_path:"..."}(视频生成,支持 i2v 首帧或通过 last_frame_path 指定 kf2v 首尾帧)。`},{"en-US":`With no --schema, a record carrying wav_fn is validated as TTS, img_path as image, first_frame_path/video_path as video, chosen/rejected as DPO, text (no messages) as CPT, otherwise ChatML. Pass --schema to require a specific shape on every record.`,"zh-CN":`未指定 --schema 时,包含 wav_fn 的记录按 TTS 验证,包含 img_path 的按 image 验证,包含 first_frame_path/video_path 的按 video 验证,包含 chosen/rejected 的按 DPO 验证,仅含 text(无 messages)的按 CPT 验证,其他记录按 ChatML 验证。使用 --schema 要求每条记录符合指定结构。`},{"en-US":`ZIP archives (.zip) are validated structurally (data.jsonl present, media references resolve) in addition to per-record content checks. Use --full-validate to JSON.parse every line.`,"zh-CN":`ZIP 归档(.zip)除逐条检查记录内容外,还会执行结构验证(存在 data.jsonl、媒体引用可解析)。使用 --full-validate 对每一行执行 JSON.parse。`}],async run(e){let{settings:t,flags:r}=e,i=r.file,a=Zt(r.schema);if(t.dryRun){O({action:`dataset.validate`,file:i,full:r.fullValidate,schema:a??`auto`},`json`);return}let o=await $n(i,{fullValidate:r.fullValidate,schema:a});if(t.quiet?D(o.valid?`ok`:`fail`):O(o,`json`),!o.valid)throw new n(`Dataset validation failed: ${o.errors.length} error(s).`,l.GENERAL)}});function Fy(e){return Kr(e)&&Xr(e).isFile()}async function Iy(e,t,r,i,a,o,s,c){let u=r.split(`,`).map(e=>e.trim()).filter(Boolean);if(u.length===0)throw new n(`--${i} must contain at least one entry.`,l.USAGE);let d=[],f=[],p,m,h=!0;for(let r of u){if(!Fy(r)){d.push(r),h=!1;continue}if(d.push(r),f.push(r),e.dryRun)continue;let i=c&&c.path===r?c.modality:o,u=await a.validate(r,i,{model:s});if(!u.valid){let e=[`Dataset validation failed for ${r}`,...u.errors.slice(0,10).map(Ze)];throw u.errors.length>10&&e.push(` … and ${u.errors.length-10} more error(s).`),e.push(``,`Hint: re-run \`${t} dataset validate --file <path>\` for the full report,`,` or upload manually with \`${t} dataset upload --no-validate\` and`,` pass the resulting file-id here.`),new n(e.join(`
2832
- `),l.GENERAL)}if(u.warnings.length>0&&!e.quiet){process.stderr.write(`Dataset validation passed with ${u.warnings.length} warning(s) for ${r}:\n`);for(let e of u.warnings.slice(0,5))process.stderr.write(`${Ze(e)}\n`);u.warnings.length>5&&process.stderr.write(` … and ${u.warnings.length-5} more.\n`)}let g=u.stats.totalRecords;typeof g==`number`&&(m=(m??0)+g),p===void 0&&(p=Xr(r).size)}return{fileIds:d,localPaths:f,firstSize:p,recordCount:h?m:void 0}}async function Ly(e,t,r,i,a){let o=[];for(let[s,c]of r.fileIds.entries()){if(!Fy(c))continue;let u=await Zn(e,{filePath:c,purpose:i});if(!u.file_id)throw new n(`Upload of ${c} succeeded but no file_id was returned.`,l.GENERAL);o.push(u),r.fileIds[s]=u.file_id,t.quiet||process.stderr.write(`Uploaded ${Qr(c)} → ${u.file_id} (auto from --${a})\n`)}return o}const Ry={baseModel:{type:`string`,valueHint:`<model>`,description:{"en-US":`Base model to fine-tune (e.g. qwen3-8b; not the output model name)`,"zh-CN":`要微调的基础模型(例如 qwen3-8b;不是输出模型名称)`},required:!0},datasets:{type:`string`,valueHint:`<ids|paths>`,description:{"en-US":`Comma-separated dataset file IDs or local paths (.jsonl for text, .zip for audio/image/video). Local paths are uploaded (validated) first, then their file-ids are used.`,"zh-CN":`数据集文件 ID 或本地路径,以逗号分隔(文本使用 .jsonl,音频、图片和视频使用 .zip)。本地路径会先验证并上传,再使用对应的 file-id。`},required:!0},validations:{type:`string`,valueHint:`<ids|paths>`,description:{"en-US":`Comma-separated validation dataset file IDs or local paths (auto-uploaded like --datasets).`,"zh-CN":`验证数据集文件 ID 或本地路径,以逗号分隔(与 --datasets 一样自动上传)。`}},modelName:{type:`string`,valueHint:`<name>`,description:{"en-US":`Output model name (after training)`,"zh-CN":`训练完成后的输出模型名称`}},suffix:{type:`string`,valueHint:`<text>`,description:{"en-US":`Output suffix appended by the platform (finetuned_output_suffix)`,"zh-CN":`平台追加的输出后缀(finetuned_output_suffix)`}}},zy={...Ry,trainingType:{type:`string`,valueHint:`<t>`,description:{"en-US":`Training type: ${ee.join(` | `)} (default: ${s}). Mapping to the server happens at the interface boundary (e.g. sft-lora -> efficient_sft, dpo -> dpo_full).`,"zh-CN":`训练类型:${ee.join(` | `)}(默认:${s})。在接口边界转换为服务端值(例如 sft-lora -> efficient_sft、dpo -> dpo_full)。`}},nEpochs:{type:`number`,valueHint:`<n>`,description:{"en-US":`Number of epochs (default: 3)`,"zh-CN":`训练轮数(默认:3)`}},batchSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Per-device batch size (clamped to [8, 1024]). Auto-set to 8 for small datasets (<100KB)`,"zh-CN":`单设备 Batch Size(限制在 [8, 1024])。小数据集(<100KB)自动设为 8`}},learningRate:{type:`string`,valueHint:`<str>`,description:{"en-US":`Learning rate as a string to preserve precision (e.g. "1.6e-5")`,"zh-CN":`以字符串形式指定学习率以保留精度(例如 "1.6e-5")`}},maxLength:{type:`number`,valueHint:`<n>`,description:{"en-US":`Max sequence length`,"zh-CN":`最大序列长度`}}},By={...Ry},Vy={...Ry,generationType:{type:`string`,choices:[`t2i`,`i2i`],valueHint:`<t2i|i2i>`,description:{"en-US":`Generation type: t2i (default) | i2i. Sets generation_type/max_pixels. Required to train I2I from a file-id or with --dry-run (local data auto-detects input_img).`,"zh-CN":`生成类型:t2i(默认)| i2i。用于设置 generation_type/max_pixels。通过 file-id 训练 I2I 或使用 --dry-run 时必填(本地数据会自动识别 input_img)。`}},learningRate:{type:`string`,valueHint:`<str>`,description:{"en-US":`Learning rate as a string to preserve precision (e.g. "3e-5")`,"zh-CN":`以字符串形式指定学习率以保留精度(例如 "3e-5")`}}},Hy={...Ry,nEpochs:{type:`number`,valueHint:`<n>`,description:{"en-US":`Training epochs (default: 50)`,"zh-CN":`训练轮数(默认:50)`}},batchSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Batch size (default: model-specific, 1 for wan2.7, 4 for wan2.5/2.2)`,"zh-CN":`Batch Size(默认值因模型而异:wan2.7 为 1,wan2.5/2.2 为 4)`}},learningRate:{type:`string`,valueHint:`<str>`,description:{"en-US":`Learning rate as a string to preserve precision (default: "2e-5")`,"zh-CN":`以字符串形式指定学习率以保留精度(默认:"2e-5")`}}},Uy=[{"en-US":`Creating a job uploads any local datasets and consumes training quota.`,"zh-CN":`创建任务会上传所有本地数据集并消耗训练额度。`},{"en-US":`Use --dry-run to preview the request body without submitting.`,"zh-CN":`使用 --dry-run 预览请求体,不实际提交。`},{"en-US":"--datasets / --validations accept either file-ids (from `dataset upload`) or local paths. Local paths are validated and uploaded first, then their file-ids are submitted — a one-step upload-and-train.","zh-CN":"--datasets / --validations 可接受 file-id(来自 `dataset upload`)或本地路径。本地路径会先验证并上传,再提交对应的 file-id,实现一步上传并训练。"}],Wy=[...Uy,{"en-US":"Training-type values use the `<method>` / `<method>-lora` convention: sft (full) | sft-lora (LoRA) | dpo (full) | dpo-lora (LoRA) | cpt. These map to the server's training_type at the interface boundary, so the rest of the CLI never sees the raw server strings.","zh-CN":"训练类型遵循 `<method>` / `<method>-lora` 命名约定:sft(全量)| sft-lora(LoRA)| dpo(全量)| dpo-lora(LoRA)| cpt。这些值会在接口边界映射为服务端 training_type,因此 CLI 的其他部分不会接触服务端原始字符串。"},{"en-US":`Before submitting (non dry-run) the job, the model's training capability is checked via listFoundationModels (no console login required); an unsupported training type fails fast with the list the model actually supports.`,"zh-CN":`提交任务前(非 dry-run),会通过 listFoundationModels 检查模型训练能力(无需登录控制台);如果训练类型不受支持,会立即失败并列出该模型实际支持的训练类型。`},{"en-US":`n_epochs defaults to 3. Other hyper-parameters are platform defaults unless set.`,"zh-CN":`n_epochs 默认为 3。其他超参数未设置时使用平台默认值。`},{"en-US":`Learning rate is forwarded as a string to avoid JSON-number precision loss.`,"zh-CN":`学习率以字符串形式传递,避免 JSON 数字精度损失。`},{"en-US":`Pre-submit gate: if the training dataset's sample count is not greater than batch_size, the job is rejected before upload or quota consumption (the platform would otherwise fail ~10 min in, after data processing).`,"zh-CN":`提交前检查:如果训练数据集的样本数不大于 batch_size,会在上传或消耗额度前拒绝任务(否则平台会在数据处理约 10 分钟后才失败)。`}],Gy=[...Uy,{"en-US":`Audio TTS training runs sft-lora (efficient_sft) with fixed CosyVoice hyper-parameter defaults; there are no training-type or hyper-parameter knobs to set.`,"zh-CN":`音频 TTS 训练使用 sft-lora(efficient_sft)和固定的 CosyVoice 超参数默认值;没有可设置的训练类型或超参数选项。`}],Ky=[...Uy,{"en-US":`Image generation training runs sft-lora (efficient_sft) with fixed defaults; only --learning-rate is overridable. T2I vs I2I is declared with --generation-type (default t2i), which sets generation_type/max_pixels. For local data the type is auto-detected (records with input_img train I2I); pass --generation-type explicitly to train I2I from a file-id or in --dry-run.`,"zh-CN":`图片生成训练使用 sft-lora(efficient_sft)和固定默认值;仅 --learning-rate 可覆盖。通过 --generation-type(默认 t2i)声明 T2I 或 I2I,并设置 generation_type/max_pixels。本地数据会自动识别类型(包含 input_img 的记录训练 I2I);通过 file-id 训练 I2I 或使用 --dry-run 时,请显式传入 --generation-type。`}];async function qy(e,t){let{identity:r,settings:i}=t,a=t.flags,o=a.baseModel,c=a.datasets;if(e===`audio`){let e=c.split(`,`).map(e=>e.trim()).filter(Boolean);if(e.length>1)throw new n(`Audio (TTS) fine-tuning accepts exactly one training file, got ${e.length}.`,l.USAGE,`Merge your recordings into a single .zip (or pass one file-id).`)}let u=a.trainingType||s;if(!St(u))throw new n(`--training-type "${u}" is not supported.`,l.USAGE,`Supported values: ${ee.join(`, `)} (default: ${s}).`);let d=ut(u),f=c.split(`,`).map(e=>e.trim()).find(e=>Fy(e)),p=e;if(e===`image`){let e=a.generationType;(e===`i2i`||!e&&f&&!i.dryRun&&await Fe(f)===`image-i2i`)&&(p=`image-i2i`)}e===`video`&&f&&!i.dryRun&&await Fe(f)===`video-kf2v`&&(p=`video-kf2v`);let m=await Iy(i,r.binName,c,`datasets`,d,p,o,f?{path:f,modality:p}:void 0),h=m.fileIds,g=a.validations?await Iy(i,r.binName,a.validations,`validations`,d,p,o):void 0,_=g?.fileIds,v=a.modelName,y=a.suffix,b=d.resolveHyperParameters(p,a);if(a.batchSize!==void 0&&b.batch_size!==void 0&&!i.quiet&&!d.shouldSkipGate(`batch_size`,p)){let e=a.batchSize;b.batch_size!==e&&process.stderr.write(`warning: --batch-size ${e} clamped to ${b.batch_size} (server range [8, 1024] for the common training types).\n`)}if(a.batchSize!==void 0&&!i.quiet&&d.shouldSkipGate(`batch_size`,p)){let e=a.batchSize;b.batch_size!==void 0&&b.batch_size!==e&&process.stderr.write(`warning: --batch-size ${e} ignored for ${p} training (model uses a fixed batch_size of ${b.batch_size}).\n`)}if(p===`text`&&b.batch_size===void 0&&!i.dryRun){let e=m.firstSize??0;if(e===0)try{e=(await it(t.client,h[0])).data?.size??0}catch{}e>0&&e<102400&&(b.batch_size=8)}if(!i.dryRun&&m.recordCount!==void 0&&!d.shouldSkipGate(`batch_size`,p)){let e=b.batch_size??16,t=an({recordCount:m.recordCount,batchSize:e});if(!t.ok&&t.issue)throw new n(t.issue.message,l.GENERAL,t.hint)}if(!i.dryRun&&!d.shouldSkipCapabilityCheck(p)){let e;try{e=await Ve(i,o)}catch(e){i.quiet||process.stderr.write(`warning: model capability lookup failed (${e.message}); proceeding without local pre-flight.
2833
- `)}if(e&&!Ft(e).includes(u)){let t=Ft(e);throw new n(`Model "${o}" does not support training type "${u}".`,l.USAGE,t.length?`This model supports: ${t.join(`, `)}.`:`This model reports no supported training types.`)}}i.dryRun||(await Ly(t.client,i,m,`fine-tune`,`datasets`),g&&await Ly(t.client,i,g,`fine-tune`,`validations`));let x={model:o,training_file_ids:h,training_type:d.serverTrainingType,hyper_parameters:b};if(_&&_.length>0&&(x.validation_file_ids=_),v&&(x.model_name=v),y&&(x.finetuned_output_suffix=y),i.dryRun){let e=[...m.localPaths.map(e=>({field:`datasets`,path:e})),...(g?.localPaths??[]).map(e=>({field:`validations`,path:e}))];O(e.length>0?{action:`finetune.create`,body:x,pending_uploads:e}:{action:`finetune.create`,body:x},`json`);return}let te=await we(t.client,x),ne=te.output??te.data;i.quiet?ne?.job_id&&D(ne.job_id):O(te,`json`)}const Jy=S({description:{"en-US":`Create a text model fine-tune job (sft | sft-lora | dpo | dpo-lora | cpt)`,"zh-CN":`创建文本模型微调任务(sft | sft-lora | dpo | dpo-lora | cpt)`},auth:`apiKey`,usageArgs:`--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>]`,flags:zy,exampleArgs:[`--base-model qwen3-8b --datasets file-xxx`,`--base-model qwen3-8b --datasets ./train.jsonl`,`--base-model qwen3-8b --datasets ./train.jsonl --validations ./eval.jsonl`,`--base-model qwen3-8b --datasets file-aaa,./extra.jsonl`,`--base-model qwen3-8b --datasets ./train.jsonl --training-type sft`,`--base-model qwen3-8b --datasets file-xxx --learning-rate "1.6e-5" --n-epochs 4`,`--base-model qwen3-8b --datasets file-xxx --output json`,`--base-model qwen3-8b --datasets file-xxx --dry-run`],notes:Wy,run:e=>qy(`text`,e)}),Yy=S({description:{"en-US":`Create an audio TTS model fine-tune job (sft-lora)`,"zh-CN":`创建音频 TTS 模型微调任务(sft-lora)`},auth:`apiKey`,usageArgs:`--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>]`,flags:By,exampleArgs:[`--base-model cosyvoice-v3-flash --datasets ./audio.zip`,`--base-model cosyvoice-v3-flash --datasets file-xxx`,`--base-model cosyvoice-v3-flash --datasets ./audio.zip --model-name my-tts`,`--base-model cosyvoice-v3-flash --datasets file-xxx --output json`,`--base-model cosyvoice-v3-flash --datasets ./audio.zip --dry-run`],notes:Gy,run:e=>qy(`audio`,e)}),Xy=S({description:{"en-US":`Create an image generation model fine-tune job (sft-lora)`,"zh-CN":`创建图片生成模型微调任务(sft-lora)`},auth:`apiKey`,usageArgs:`--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>] [--generation-type <t2i|i2i>] [--learning-rate <str>]`,flags:Vy,exampleArgs:[`--base-model wan2.7-image-pro --datasets ./images.zip`,`--base-model wan2.7-image-pro --datasets file-xxx`,`--base-model wan2.7-image-pro --datasets file-xxx --generation-type i2i`,`--base-model wan2.7-image-pro --datasets ./images.zip --model-name my-wan`,`--base-model wan2.7-image-pro --datasets file-xxx --output json`,`--base-model wan2.7-image-pro --datasets ./images.zip --dry-run`],notes:Ky,run:e=>qy(`image`,e)}),Zy=[...Uy,{"en-US":`Video generation training (Wan i2v/kf2v) runs efficient_sft with model-specific defaults: wan2.7 (batch_size=1, max_pixels=102400), wan2.5/2.2 (batch_size=4, max_pixels per model). Override with --batch-size/--n-epochs.`,"zh-CN":`视频生成训练(Wan i2v/kf2v)使用 efficient_sft 和模型专属默认值:wan2.7(batch_size=1、max_pixels=102400),wan2.5/2.2(batch_size=4,max_pixels 因模型而异)。可通过 --batch-size/--n-epochs 覆盖。`},{"en-US":`Datasets are .zip archives with data.jsonl + frame images + videos.`,"zh-CN":`数据集为包含 data.jsonl、帧图片和视频的 .zip 归档。`},{"en-US":`Recommended: ≥10 training samples, 20-100 for stable results.`,"zh-CN":`建议至少准备 10 个训练样本;20–100 个样本可获得更稳定的效果。`}],Qy=S({description:{"en-US":`Create a video generation model fine-tune job (Wan i2v/kf2v, efficient_sft)`,"zh-CN":`创建视频生成模型微调任务(Wan i2v/kf2v,efficient_sft)`},auth:`apiKey`,usageArgs:`--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>] [--n-epochs <n>] [--batch-size <n>] [--learning-rate <str>]`,flags:Hy,exampleArgs:[`--base-model wan2.7-i2v --datasets file-xxx`,`--base-model wan2.7-i2v --datasets ./i2v-data.zip`,`--base-model wan2.2-kf2v-flash --datasets file-xxx --n-epochs 100`,`--base-model wan2.7-i2v --datasets file-xxx --dry-run`],notes:Zy,run:e=>qy(`video`,e)});var $y=S({description:{"en-US":`List fine-tune jobs`,"zh-CN":`列出微调任务`},auth:`apiKey`,usageArgs:`[--page <n>] [--page-size <n>] [--status <s>] [--base-model <model>]`,flags:{page:{type:`number`,valueHint:`<n>`,description:{"en-US":`Page number (default: 1)`,"zh-CN":`页码(默认:1)`}},pageSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Results per page (default: 10, max 100)`,"zh-CN":`每页结果数(默认:10,最多:100)`}},status:{type:`string`,valueHint:`<s>`,description:{"en-US":`Filter by status (PENDING / RUNNING / SUCCEEDED / FAILED / CANCELED)`,"zh-CN":`按状态筛选(PENDING / RUNNING / SUCCEEDED / FAILED / CANCELED)`}},baseModel:{type:`string`,valueHint:`<model>`,description:{"en-US":`Filter by base model ID (server-side)`,"zh-CN":`按基础模型 ID 筛选(服务端筛选)`}}},exampleArgs:[``,`--status RUNNING`,`--base-model qwen3-8b`,`--page-size 20`],async run(e){let{settings:t,flags:n}=e,r=n.page,i=n.pageSize,a=n.status||void 0,o=n.baseModel||void 0;if(t.dryRun){O({action:`finetune.list`,page:r,page_size:i,status:a,model:o},`json`);return}let s=await Mt(e.client,{pageNo:r,pageSize:i,status:a,model:o}),c=s.output??s.data,l=c?.jobs??[],u=c?.total,d=l.map(e=>({job_id:e.job_id??``,base_model:e.model??``,status:e.status??``,training_type:e.training_type??``,output_model:e.finetuned_output??``,created_at:e.create_time??e.gmt_create??``}));O({items:d,total:u,request_id:s.request_id},`json`)}});async function eb(e,t){let n=w(e),r=await ge({region:n.consoleRegion,site:n.consoleSite,switchAgent:n.consoleSwitchAgent},e.timeout,{api:p,data:{input:{pageNo:1,pageSize:10,group:!0,model:t,queryPrice:!0,querySampleCode:!1,queryGroupByModel:!0,queryQuota:!1,queryQpmInfo:!1,queryApplyStatus:!1,queryPermissions:!1,queryActivationStatus:!1}}}),i=qn(r).list??[];for(let e of i){let n=e.items??[];for(let e of n)if(e.model===t)return(e.prices??[]).find(e=>e.type===`ft`)??null;if(e.model===t)return(e.prices??[]).find(e=>e.type===`ft`)??null}return null}async function tb(e,t,n){try{let r=await eb(e,t),i=Number(r?.price);if(!Number.isFinite(i)||i<=0)return null;let a=r?.priceUnit??`每百万tokens`,o=n/1e6*i;return{cost:Number(o.toFixed(4)),unitPrice:i,priceUnit:a}}catch{return null}}var nb=S({description:{"en-US":`Get details of a single fine-tune job`,"zh-CN":`获取单个微调任务的详情`},auth:`apiKey`,usageArgs:`--job-id <id>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --output json`],async run(e){let{settings:t,flags:n}=e,r=n.jobId;if(t.dryRun){O({action:`finetune.get`,job_id:r},`json`);return}let i=await ot(e.client,r),a=i.output??i.data;if(!a){O({job_id:r,error:`No data returned`},`json`);return}let o=a.hyper_parameters,s=[];o?.n_epochs!==void 0&&s.push(`n_epochs=${o.n_epochs}`),o?.batch_size!==void 0&&s.push(`batch_size=${o.batch_size}`),o?.learning_rate!==void 0&&s.push(`learning_rate=${o.learning_rate}`),o?.max_length!==void 0&&s.push(`max_length=${o.max_length}`);let c=typeof a.usage==`number`?a.usage:void 0,l={job_id:a.job_id??r,base_model:a.model??``,status:a.status??``,training_type:a.training_type??``,training_files:a.training_file_ids??[],validation_files:a.validation_file_ids??[],hyper_params:s.length?s.join(` · `):``,output_model:a.finetuned_output??``,model_name:a.model_name??``,created_at:a.create_time??a.gmt_create??``,updated_at:a.end_time??a.gmt_modified??``,usage_tokens:c??``,charge_type:typeof a.charge_type==`string`?a.charge_type:``};if(c!==void 0&&c>0&&a.model){let e=await tb(t,a.model,c);e&&(l.training_cost=e.cost,l.cost_basis=`${e.unitPrice} 元/${e.priceUnit}`)}O({...l,request_id:i.request_id},`json`)}}),rb=S({description:{"en-US":`Cancel a running fine-tune job`,"zh-CN":`取消正在运行的微调任务`},auth:`apiKey`,usageArgs:`--job-id <id>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --dry-run`],notes:[{"en-US":`Only PENDING / RUNNING jobs can be cancelled. Completed / failed / already-cancelled jobs return a server-side error (passed through verbatim).`,"zh-CN":`只有 PENDING / RUNNING 状态的任务可以取消。已完成、失败或已取消的任务会返回服务端错误(原样透传)。`}],async run(e){let{settings:t,flags:n}=e,r=n.jobId;if(t.dryRun){O({action:`finetune.cancel`,job_id:r},`json`);return}let i=await _e(e.client,r);t.quiet?D(r):O(i,`json`)}}),ib=S({description:{"en-US":`Delete a fine-tune job record`,"zh-CN":`删除微调任务记录`},auth:`apiKey`,risk:{level:`high`,message:{"en-US":`This permanently deletes the specified fine-tune job record and cannot be undone.`,"zh-CN":`该操作会永久删除指定的微调任务记录,且无法撤销。`}},usageArgs:`--job-id <id>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --dry-run`,`--job-id ft-xxx --yes`],notes:[{"en-US":`Irreversible — the job record is permanently removed.`,"zh-CN":`该操作不可撤销——任务记录将被永久删除。`},{"en-US":"Cancel a RUNNING job first via `finetune cancel` — the platform refuses to delete jobs that are still in flight.","zh-CN":"请先通过 `finetune cancel` 取消 RUNNING 任务,平台拒绝删除仍在运行的任务。"}],async run(e){let{settings:t,flags:n}=e,r=n.jobId;if(t.dryRun){O({action:`finetune.delete`,job_id:r},`json`);return}let i=await Me(e.client,r);t.quiet?D(r):O(i,`json`)}});function ab(e){if(typeof e==`string`)return e;let t=e,n=t.timestamp??t.time??t.create_time??``,r=t.level??``,i=t.message??t.msg??t.log??``;return i||n||r?[n,r,i].filter(Boolean).join(` `):JSON.stringify(e)}function ob(e,t){return ab(e).toLowerCase().includes(t)}async function sb(e,t,n){let r=[],i=1,a=0;for(let o=0;o<200;o++){let o=await st(e,t,{pageNo:i,pageSize:n}),s=o.output??o.data,c=s?.logs??[];if(a=s?.total??a,c.length===0||(r.push(...c),a&&r.length>=a)||c.length<n)break;i++}return{entries:r,total:a}}var cb=S({description:{"en-US":`Fetch training logs for a fine-tune job`,"zh-CN":`获取微调任务的训练日志`},auth:`apiKey`,usageArgs:`--job-id <id> [--page <n>] [--page-size <n>] [--search <keyword>] [--tail <n>]`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0},page:{type:`number`,valueHint:`<n>`,description:{"en-US":`Page number (default: 1)`,"zh-CN":`页码(默认:1)`}},pageSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Lines per page (default: server-defined)`,"zh-CN":`每页行数(默认:由服务端决定)`}},search:{type:`string`,valueHint:`<keyword>`,description:{"en-US":`Case-insensitive substring filter. When set, all log pages are fetched and filtered client-side (--page is ignored).`,"zh-CN":`不区分大小写的子字符串筛选。设置后会获取全部日志页并在客户端筛选(忽略 --page)。`}},tail:{type:`number`,valueHint:`<n>`,description:{"en-US":`Keep only the last N entries. When set, all log pages are fetched and the trailing N are kept (--page is ignored).`,"zh-CN":`仅保留最后 N 条记录。设置后会获取全部日志页并保留末尾 N 条(忽略 --page)。`}}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --page-size 100 --output json`,`--job-id ft-xxx --search checkpoint`,`--job-id ft-xxx --search error --output json`,`--job-id ft-xxx --tail 20`,`--job-id ft-xxx --search checkpoint --tail 5`],async run(e){let{settings:t,flags:n}=e,r=n.jobId,i=n.page,a=n.pageSize,o=n.search||void 0,s=n.tail;if(t.dryRun){O({action:`finetune.logs`,job_id:r,page:i,page_size:a,search:o,tail:s},`json`);return}if(o||s!==void 0){let{entries:t,total:n}=await sb(e.client,r,a??100),i=t,c;if(o){let e=o.toLowerCase();i=t.filter(t=>ob(t,e)),c=i.length}let l=s!==void 0&&s>=0?Math.min(s,i.length):void 0,u=l===void 0?i:i.slice(i.length-l);O({...c===void 0?{}:{matched:c},scanned:t.length,total:n||t.length,...o?{search:o}:{},...l===void 0?{}:{tail:l},logs:u},`json`);return}let c=await st(e.client,r,{pageNo:i,pageSize:a});O(c,`json`)}}),lb=S({description:{"en-US":`List checkpoints produced by a fine-tune job`,"zh-CN":`列出微调任务生成的 Checkpoint`},auth:`apiKey`,usageArgs:`--job-id <id>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --output json`],notes:[{"en-US":"`model_name` (shown for SUCCEEDED checkpoints) is the direct input for `deploy create --model-name`.","zh-CN":"SUCCEEDED Checkpoint 中显示的 `model_name` 可直接作为 `deploy create --model-name` 的输入。"},{"en-US":"Checkpoints expire ~15 days after creation; `expire_time` shows the deadline. Export or deploy before expiry.","zh-CN":"Checkpoint 创建后约 15 天过期,`expire_time` 显示截止时间。请在过期前导出或部署。"}],async run(e){let{settings:t,flags:n}=e,r=n.jobId;if(t.dryRun){O({action:`finetune.checkpoints`,job_id:r},`json`);return}let i=await Ot(e.client,r),a=i.output??i.data,o=Array.isArray(a)?a:a?.checkpoints??[],s=Array.isArray(a)?a.length:a?.total??o.length,c=o.map(e=>({checkpoint:e.checkpoint??e.checkpoint_id??``,step:e.step===void 0?``:String(e.step),status:e.status??``,model_name:e.model_name??``,expire_time:e.expire_time??``}));O({items:c,total:s,request_id:i.request_id},`json`);let l=Date.now(),u=c.filter(e=>{if(!e.expire_time)return!1;let t=new Date(e.expire_time).getTime();if(Number.isNaN(t))return!1;let n=t-l;return n>0&&n<2592e5});u.length>0&&process.stderr.write(`\n[warning] ${u.length} checkpoint(s) will expire within 72 hours. Export or deploy before expiry to avoid losing the model artifact.
2832
+ `),l.GENERAL)}if(u.warnings.length>0&&!e.quiet){process.stderr.write(`Dataset validation passed with ${u.warnings.length} warning(s) for ${r}:\n`);for(let e of u.warnings.slice(0,5))process.stderr.write(`${Ze(e)}\n`);u.warnings.length>5&&process.stderr.write(` … and ${u.warnings.length-5} more.\n`)}let g=u.stats.totalRecords;typeof g==`number`&&(m=(m??0)+g),p===void 0&&(p=Xr(r).size)}return{fileIds:d,localPaths:f,firstSize:p,recordCount:h?m:void 0}}async function Ly(e,t,r,i,a){let o=[];for(let[s,c]of r.fileIds.entries()){if(!Fy(c))continue;let u=await Zn(e,{filePath:c,purpose:i});if(!u.file_id)throw new n(`Upload of ${c} succeeded but no file_id was returned.`,l.GENERAL);o.push(u),r.fileIds[s]=u.file_id,t.quiet||process.stderr.write(`Uploaded ${Qr(c)} → ${u.file_id} (auto from --${a})\n`)}return o}const Ry={baseModel:{type:`string`,valueHint:`<model>`,description:{"en-US":`Base model to fine-tune (e.g. qwen3-8b; not the output model name)`,"zh-CN":`要微调的基础模型(例如 qwen3-8b;不是输出模型名称)`},required:!0},datasets:{type:`string`,valueHint:`<ids|paths>`,description:{"en-US":`Comma-separated dataset file IDs or local paths (.jsonl for text, .zip for audio/image/video). Local paths are uploaded (validated) first, then their file-ids are used.`,"zh-CN":`数据集文件 ID 或本地路径,以逗号分隔(文本使用 .jsonl,音频、图片和视频使用 .zip)。本地路径会先验证并上传,再使用对应的 file-id。`},required:!0},validations:{type:`string`,valueHint:`<ids|paths>`,description:{"en-US":`Comma-separated validation dataset file IDs or local paths (auto-uploaded like --datasets).`,"zh-CN":`验证数据集文件 ID 或本地路径,以逗号分隔(与 --datasets 一样自动上传)。`}},modelName:{type:`string`,valueHint:`<name>`,description:{"en-US":`Output model name (after training)`,"zh-CN":`训练完成后的输出模型名称`}},suffix:{type:`string`,valueHint:`<text>`,description:{"en-US":`Output suffix appended by the platform (finetuned_output_suffix)`,"zh-CN":`平台追加的输出后缀(finetuned_output_suffix)`}}},zy={...Ry,jobName:{type:`string`,valueHint:`<value>`,description:{"en-US":`Training job display name (job_name)`,"zh-CN":`训练任务显示名称(job_name)`}},priority:{type:`string`,valueHint:`<value>`,description:{"en-US":`Requested scheduling priority; verify the service response`,"zh-CN":`请求的调度优先级;请核对服务端回执`},choices:[`L0`,`L1`,`L2`,`L3`]},evalSteps:{type:`number`,valueHint:`<value>`,description:{"en-US":`Validation interval in training steps`,"zh-CN":`训练验证间隔步数`}},loraAlpha:{type:`number`,valueHint:`<value>`,description:{"en-US":`LoRA scaling coefficient`,"zh-CN":`LoRA 缩放系数`}},loraDropout:{type:`number`,valueHint:`<value>`,description:{"en-US":`LoRA dropout probability`,"zh-CN":`LoRA 丢弃率`}},loraRank:{type:`number`,valueHint:`<value>`,description:{"en-US":`LoRA matrix rank`,"zh-CN":`LoRA 矩阵秩`}},lrSchedulerType:{type:`string`,valueHint:`<value>`,description:{"en-US":`Learning rate scheduler supported by the selected model`,"zh-CN":`所选模型支持的学习率调度策略`}},saveStrategy:{type:`string`,valueHint:`<value>`,description:{"en-US":`Checkpoint saving strategy`,"zh-CN":`Checkpoint 保存策略`},choices:[`epoch`,`steps`]},saveTotalLimit:{type:`number`,valueHint:`<value>`,description:{"en-US":`Maximum number of saved checkpoints`,"zh-CN":`最多保存的 Checkpoint 数量`}},saveSteps:{type:`number`,valueHint:`<value>`,description:{"en-US":`Checkpoint saving interval for strategy=steps`,"zh-CN":`按 steps 保存时的间隔`}},split:{type:`number`,valueHint:`<value>`,description:{"en-US":`Training fraction when no validation dataset is supplied`,"zh-CN":`未指定验证集时训练集所占比例`}},maxSplitValDatasetSample:{type:`number`,valueHint:`<value>`,description:{"en-US":`Maximum automatically split validation samples`,"zh-CN":`自动切分验证集的样本数量上限`}},dataAugmentation:{type:`string`,valueHint:`<value>`,description:{"en-US":`Mix platform training data (true or false)`,"zh-CN":`是否混入平台训练数据(true 或 false)`},choices:[`true`,`false`]},trainingType:{type:`string`,valueHint:`<t>`,description:{"en-US":`Training type: ${ee.join(` | `)} (default: ${s}). Mapping to the server happens at the interface boundary (e.g. sft-lora -> efficient_sft, dpo -> dpo_full).`,"zh-CN":`训练类型:${ee.join(` | `)}(默认:${s})。在接口边界转换为服务端值(例如 sft-lora -> efficient_sft、dpo -> dpo_full)。`}},nEpochs:{type:`number`,valueHint:`<n>`,description:{"en-US":`Number of epochs (default: 3)`,"zh-CN":`训练轮数(默认:3)`}},batchSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Per-device batch size (clamped to [8, 1024]). Auto-set to 8 for small datasets (<100KB)`,"zh-CN":`单设备 Batch Size(限制在 [8, 1024])。小数据集(<100KB)自动设为 8`}},learningRate:{type:`string`,valueHint:`<str>`,description:{"en-US":`Learning rate as a string to preserve precision (e.g. "1.6e-5")`,"zh-CN":`以字符串形式指定学习率以保留精度(例如 "1.6e-5")`}},maxLength:{type:`number`,valueHint:`<n>`,description:{"en-US":`Max sequence length`,"zh-CN":`最大序列长度`}}},By={...Ry},Vy={...Ry,generationType:{type:`string`,choices:[`t2i`,`i2i`],valueHint:`<t2i|i2i>`,description:{"en-US":`Generation type: t2i (default) | i2i. Sets generation_type/max_pixels. Required to train I2I from a file-id or with --dry-run (local data auto-detects input_img).`,"zh-CN":`生成类型:t2i(默认)| i2i。用于设置 generation_type/max_pixels。通过 file-id 训练 I2I 或使用 --dry-run 时必填(本地数据会自动识别 input_img)。`}},learningRate:{type:`string`,valueHint:`<str>`,description:{"en-US":`Learning rate as a string to preserve precision (e.g. "3e-5")`,"zh-CN":`以字符串形式指定学习率以保留精度(例如 "3e-5")`}}},Hy={...Ry,nEpochs:{type:`number`,valueHint:`<n>`,description:{"en-US":`Training epochs (default: 50)`,"zh-CN":`训练轮数(默认:50)`}},batchSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Batch size (default: model-specific, 1 for wan2.7, 4 for wan2.5/2.2)`,"zh-CN":`Batch Size(默认值因模型而异:wan2.7 为 1,wan2.5/2.2 为 4)`}},learningRate:{type:`string`,valueHint:`<str>`,description:{"en-US":`Learning rate as a string to preserve precision (default: "2e-5")`,"zh-CN":`以字符串形式指定学习率以保留精度(默认:"2e-5")`}}},Uy=[{"en-US":`Creating a job uploads any local datasets and consumes training quota.`,"zh-CN":`创建任务会上传所有本地数据集并消耗训练额度。`},{"en-US":`Use --dry-run to preview the request body without submitting.`,"zh-CN":`使用 --dry-run 预览请求体,不实际提交。`},{"en-US":"--datasets / --validations accept either file-ids (from `dataset upload`) or local paths. Local paths are validated and uploaded first, then their file-ids are submitted — a one-step upload-and-train.","zh-CN":"--datasets / --validations 可接受 file-id(来自 `dataset upload`)或本地路径。本地路径会先验证并上传,再提交对应的 file-id,实现一步上传并训练。"}],Wy=[...Uy,{"en-US":"Training-type values use the `<method>` / `<method>-lora` convention: sft (full) | sft-lora (LoRA) | dpo (full) | dpo-lora (LoRA) | cpt. These map to the server's training_type at the interface boundary, so the rest of the CLI never sees the raw server strings.","zh-CN":"训练类型遵循 `<method>` / `<method>-lora` 命名约定:sft(全量)| sft-lora(LoRA)| dpo(全量)| dpo-lora(LoRA)| cpt。这些值会在接口边界映射为服务端 training_type,因此 CLI 的其他部分不会接触服务端原始字符串。"},{"en-US":`Before submitting (non dry-run) the job, the model's training capability is checked via listFoundationModels (no console login required); an unsupported training type fails fast with the list the model actually supports.`,"zh-CN":`提交任务前(非 dry-run),会通过 listFoundationModels 检查模型训练能力(无需登录控制台);如果训练类型不受支持,会立即失败并列出该模型实际支持的训练类型。`},{"en-US":`n_epochs defaults to 3. Other hyper-parameters are platform defaults unless set.`,"zh-CN":`n_epochs 默认为 3。其他超参数未设置时使用平台默认值。`},{"en-US":`Learning rate is forwarded as a string to avoid JSON-number precision loss.`,"zh-CN":`学习率以字符串形式传递,避免 JSON 数字精度损失。`},{"en-US":`Pre-submit gate: if the training dataset's sample count is not greater than batch_size, the job is rejected before upload or quota consumption (the platform would otherwise fail ~10 min in, after data processing).`,"zh-CN":`提交前检查:如果训练数据集的样本数不大于 batch_size,会在上传或消耗额度前拒绝任务(否则平台会在数据处理约 10 分钟后才失败)。`}],Gy=[...Uy,{"en-US":`Audio TTS training runs sft-lora (efficient_sft) with fixed CosyVoice hyper-parameter defaults; there are no training-type or hyper-parameter knobs to set.`,"zh-CN":`音频 TTS 训练使用 sft-lora(efficient_sft)和固定的 CosyVoice 超参数默认值;没有可设置的训练类型或超参数选项。`}],Ky=[...Uy,{"en-US":`Image generation training runs sft-lora (efficient_sft) with fixed defaults; only --learning-rate is overridable. T2I vs I2I is declared with --generation-type (default t2i), which sets generation_type/max_pixels. For local data the type is auto-detected (records with input_img train I2I); pass --generation-type explicitly to train I2I from a file-id or in --dry-run.`,"zh-CN":`图片生成训练使用 sft-lora(efficient_sft)和固定默认值;仅 --learning-rate 可覆盖。通过 --generation-type(默认 t2i)声明 T2I 或 I2I,并设置 generation_type/max_pixels。本地数据会自动识别类型(包含 input_img 的记录训练 I2I);通过 file-id 训练 I2I 或使用 --dry-run 时,请显式传入 --generation-type。`}];async function qy(e,t){let{identity:r,settings:i}=t,a=t.flags,o=a.baseModel,c=a.datasets;if(e===`audio`){let e=c.split(`,`).map(e=>e.trim()).filter(Boolean);if(e.length>1)throw new n(`Audio (TTS) fine-tuning accepts exactly one training file, got ${e.length}.`,l.USAGE,`Merge your recordings into a single .zip (or pass one file-id).`)}let u=a.trainingType||s;if(!St(u))throw new n(`--training-type "${u}" is not supported.`,l.USAGE,`Supported values: ${ee.join(`, `)} (default: ${s}).`);let d=ut(u),f=c.split(`,`).map(e=>e.trim()).find(e=>Fy(e)),p=e;if(e===`image`){let e=a.generationType;(e===`i2i`||!e&&f&&!i.dryRun&&await Fe(f)===`image-i2i`)&&(p=`image-i2i`)}e===`video`&&f&&!i.dryRun&&await Fe(f)===`video-kf2v`&&(p=`video-kf2v`);let m=await Iy(i,r.binName,c,`datasets`,d,p,o,f?{path:f,modality:p}:void 0),h=m.fileIds,g=a.validations?await Iy(i,r.binName,a.validations,`validations`,d,p,o):void 0,_=g?.fileIds,v=a.modelName,y=a.suffix,b=d.resolveHyperParameters(p,a);if(e===`text`){for(let[e,t]of Object.entries({evalSteps:`eval_steps`,loraAlpha:`lora_alpha`,loraDropout:`lora_dropout`,loraRank:`lora_rank`,lrSchedulerType:`lr_scheduler_type`,saveStrategy:`save_strategy`,saveTotalLimit:`save_total_limit`,saveSteps:`save_steps`,split:`split`,maxSplitValDatasetSample:`max_split_val_dataset_sample`})){let n=a[e];n!==void 0&&(b[t]=n)}for(let e of[`eval_steps`,`lora_alpha`,`lora_rank`,`save_total_limit`,`save_steps`,`max_split_val_dataset_sample`]){let t=b[e];if(t!==void 0&&(typeof t!=`number`||!Number.isInteger(t)||t<=0))throw new n(`${e} must be a positive integer. / 必须为正整数。`,l.USAGE)}if(b.lora_dropout!==void 0&&(typeof b.lora_dropout!=`number`||!Number.isFinite(b.lora_dropout)||b.lora_dropout<0||b.lora_dropout>=1))throw new n(`lora_dropout must be in [0, 1). / LoRA 丢弃率必须在 [0, 1) 内。`,l.USAGE);if(b.split!==void 0&&(typeof b.split!=`number`||!Number.isFinite(b.split)||b.split<=0||b.split>=1||a.validations))throw new n(`split must be in (0, 1) and cannot be combined with validations. / 切分比例须在 (0, 1) 内,且不能与独立验证集同时设置。`,l.USAGE);if(a.dataAugmentation!==void 0&&(b.data_augmentation=a.dataAugmentation===`true`),b.save_strategy===`steps`&&b.save_steps===void 0)throw new n(`save_strategy=steps requires --save-steps. / 按步保存时必须指定 --save-steps。`,l.USAGE)}if(a.batchSize!==void 0&&b.batch_size!==void 0&&!i.quiet&&!d.shouldSkipGate(`batch_size`,p)){let e=a.batchSize;b.batch_size!==e&&process.stderr.write(`warning: --batch-size ${e} clamped to ${b.batch_size} (server range [8, 1024] for the common training types).\n`)}if(a.batchSize!==void 0&&!i.quiet&&d.shouldSkipGate(`batch_size`,p)){let e=a.batchSize;b.batch_size!==void 0&&b.batch_size!==e&&process.stderr.write(`warning: --batch-size ${e} ignored for ${p} training (model uses a fixed batch_size of ${b.batch_size}).\n`)}if(p===`text`&&b.batch_size===void 0&&!i.dryRun){let e=m.firstSize??0;if(e===0)try{e=(await it(t.client,h[0])).data?.size??0}catch{}e>0&&e<102400&&(b.batch_size=8)}if(!i.dryRun&&m.recordCount!==void 0&&!d.shouldSkipGate(`batch_size`,p)){let e=b.batch_size??16,t=an({recordCount:m.recordCount,batchSize:e});if(!t.ok&&t.issue)throw new n(t.issue.message,l.GENERAL,t.hint)}if(!i.dryRun&&!d.shouldSkipCapabilityCheck(p)){let e;try{e=await Ve(i,o)}catch(e){i.quiet||process.stderr.write(`warning: model capability lookup failed (${e.message}); proceeding without local pre-flight.
2833
+ `)}if(e&&!Ft(e).includes(u)){let t=Ft(e);throw new n(`Model "${o}" does not support training type "${u}".`,l.USAGE,t.length?`This model supports: ${t.join(`, `)}.`:`This model reports no supported training types.`)}}i.dryRun||(await Ly(t.client,i,m,`fine-tune`,`datasets`),g&&await Ly(t.client,i,g,`fine-tune`,`validations`));let x={model:o,training_file_ids:h,training_type:d.serverTrainingType,hyper_parameters:b};if(_&&_.length>0&&(x.validation_file_ids=_),typeof a.jobName==`string`&&(x.job_name=a.jobName),typeof a.priority==`string`&&(x.priority=a.priority),v&&(x.model_name=v),y&&(x.finetuned_output_suffix=y),i.dryRun){let e=[...m.localPaths.map(e=>({field:`datasets`,path:e})),...(g?.localPaths??[]).map(e=>({field:`validations`,path:e}))];O(e.length>0?{action:`finetune.create`,body:x,pending_uploads:e}:{action:`finetune.create`,body:x},`json`);return}let te=await we(t.client,x),ne=te.output??te.data;i.quiet?ne?.job_id&&D(ne.job_id):O(te,`json`)}const Jy=S({description:{"en-US":`Create a text model fine-tune job (sft | sft-lora | dpo | dpo-lora | cpt)`,"zh-CN":`创建文本模型微调任务(sft | sft-lora | dpo | dpo-lora | cpt)`},auth:`apiKey`,usageArgs:`--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>]`,flags:zy,exampleArgs:[`--base-model qwen3-8b --datasets file-xxx`,`--base-model qwen3-8b --datasets ./train.jsonl`,`--base-model qwen3-8b --datasets ./train.jsonl --validations ./eval.jsonl`,`--base-model qwen3-8b --datasets file-aaa,./extra.jsonl`,`--base-model qwen3-8b --datasets ./train.jsonl --training-type sft`,`--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`,`--base-model qwen3-8b --datasets file-xxx --learning-rate "1.6e-5" --n-epochs 4`,`--base-model qwen3-8b --datasets file-xxx --output json`,`--base-model qwen3-8b --datasets file-xxx --dry-run`],notes:Wy,run:e=>qy(`text`,e)}),Yy=S({description:{"en-US":`Create an audio TTS model fine-tune job (sft-lora)`,"zh-CN":`创建音频 TTS 模型微调任务(sft-lora)`},auth:`apiKey`,usageArgs:`--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>]`,flags:By,exampleArgs:[`--base-model cosyvoice-v3-flash --datasets ./audio.zip`,`--base-model cosyvoice-v3-flash --datasets file-xxx`,`--base-model cosyvoice-v3-flash --datasets ./audio.zip --model-name my-tts`,`--base-model cosyvoice-v3-flash --datasets file-xxx --output json`,`--base-model cosyvoice-v3-flash --datasets ./audio.zip --dry-run`],notes:Gy,run:e=>qy(`audio`,e)}),Xy=S({description:{"en-US":`Create an image generation model fine-tune job (sft-lora)`,"zh-CN":`创建图片生成模型微调任务(sft-lora)`},auth:`apiKey`,usageArgs:`--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>] [--generation-type <t2i|i2i>] [--learning-rate <str>]`,flags:Vy,exampleArgs:[`--base-model wan2.7-image-pro --datasets ./images.zip`,`--base-model wan2.7-image-pro --datasets file-xxx`,`--base-model wan2.7-image-pro --datasets file-xxx --generation-type i2i`,`--base-model wan2.7-image-pro --datasets ./images.zip --model-name my-wan`,`--base-model wan2.7-image-pro --datasets file-xxx --output json`,`--base-model wan2.7-image-pro --datasets ./images.zip --dry-run`],notes:Ky,run:e=>qy(`image`,e)}),Zy=[...Uy,{"en-US":`Video generation training (Wan i2v/kf2v) runs efficient_sft with model-specific defaults: wan2.7 (batch_size=1, max_pixels=102400), wan2.5/2.2 (batch_size=4, max_pixels per model). Override with --batch-size/--n-epochs.`,"zh-CN":`视频生成训练(Wan i2v/kf2v)使用 efficient_sft 和模型专属默认值:wan2.7(batch_size=1、max_pixels=102400),wan2.5/2.2(batch_size=4,max_pixels 因模型而异)。可通过 --batch-size/--n-epochs 覆盖。`},{"en-US":`Datasets are .zip archives with data.jsonl + frame images + videos.`,"zh-CN":`数据集为包含 data.jsonl、帧图片和视频的 .zip 归档。`},{"en-US":`Recommended: ≥10 training samples, 20-100 for stable results.`,"zh-CN":`建议至少准备 10 个训练样本;20–100 个样本可获得更稳定的效果。`}],Qy=S({description:{"en-US":`Create a video generation model fine-tune job (Wan i2v/kf2v, efficient_sft)`,"zh-CN":`创建视频生成模型微调任务(Wan i2v/kf2v,efficient_sft)`},auth:`apiKey`,usageArgs:`--base-model <model> --datasets <id|path> [--validations <id|path>] [--model-name <name>] [--suffix <text>] [--n-epochs <n>] [--batch-size <n>] [--learning-rate <str>]`,flags:Hy,exampleArgs:[`--base-model wan2.7-i2v --datasets file-xxx`,`--base-model wan2.7-i2v --datasets ./i2v-data.zip`,`--base-model wan2.2-kf2v-flash --datasets file-xxx --n-epochs 100`,`--base-model wan2.7-i2v --datasets file-xxx --dry-run`],notes:Zy,run:e=>qy(`video`,e)});var $y=S({description:{"en-US":`List fine-tune jobs`,"zh-CN":`列出微调任务`},auth:`apiKey`,usageArgs:`[--page <n>] [--page-size <n>] [--status <s>] [--base-model <model>]`,flags:{page:{type:`number`,valueHint:`<n>`,description:{"en-US":`Page number (default: 1)`,"zh-CN":`页码(默认:1)`}},pageSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Results per page (default: 10, max 100)`,"zh-CN":`每页结果数(默认:10,最多:100)`}},status:{type:`string`,valueHint:`<s>`,description:{"en-US":`Filter by status (PENDING / RUNNING / SUCCEEDED / FAILED / CANCELED)`,"zh-CN":`按状态筛选(PENDING / RUNNING / SUCCEEDED / FAILED / CANCELED)`}},baseModel:{type:`string`,valueHint:`<model>`,description:{"en-US":`Filter by base model ID (server-side)`,"zh-CN":`按基础模型 ID 筛选(服务端筛选)`}}},exampleArgs:[``,`--status RUNNING`,`--base-model qwen3-8b`,`--page-size 20`],async run(e){let{settings:t,flags:n}=e,r=n.page,i=n.pageSize,a=n.status||void 0,o=n.baseModel||void 0;if(t.dryRun){O({action:`finetune.list`,page:r,page_size:i,status:a,model:o},`json`);return}let s=await Mt(e.client,{pageNo:r,pageSize:i,status:a,model:o}),c=s.output??s.data,l=c?.jobs??[],u=c?.total,d=l.map(e=>({job_id:e.job_id??``,base_model:e.model??``,status:e.status??``,training_type:e.training_type??``,output_model:e.finetuned_output??``,created_at:e.create_time??e.gmt_create??``}));O({items:d,total:u,request_id:s.request_id},`json`)}});async function eb(e,t){let n=w(e),r=await ge({region:n.consoleRegion,site:n.consoleSite,switchAgent:n.consoleSwitchAgent},e.timeout,{api:p,data:{input:{pageNo:1,pageSize:10,group:!0,model:t,queryPrice:!0,querySampleCode:!1,queryGroupByModel:!0,queryQuota:!1,queryQpmInfo:!1,queryApplyStatus:!1,queryPermissions:!1,queryActivationStatus:!1}}}),i=qn(r).list??[];for(let e of i){let n=e.items??[];for(let e of n)if(e.model===t)return(e.prices??[]).find(e=>e.type===`ft`)??null;if(e.model===t)return(e.prices??[]).find(e=>e.type===`ft`)??null}return null}async function tb(e,t,n){try{let r=await eb(e,t),i=Number(r?.price);if(!Number.isFinite(i)||i<=0)return null;let a=r?.priceUnit??`每百万tokens`,o=n/1e6*i;return{cost:Number(o.toFixed(4)),unitPrice:i,priceUnit:a}}catch{return null}}var nb=S({description:{"en-US":`Get details of a single fine-tune job`,"zh-CN":`获取单个微调任务的详情`},auth:`apiKey`,usageArgs:`--job-id <id>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --output json`],async run(e){let{settings:t,flags:n}=e,r=n.jobId;if(t.dryRun){O({action:`finetune.get`,job_id:r},`json`);return}let i=await ot(e.client,r),a=i.output??i.data;if(!a){O({job_id:r,error:`No data returned`},`json`);return}let o=a.hyper_parameters,s=Object.entries(o??{}).map(([e,t])=>`${e}=${typeof t==`string`?t:JSON.stringify(t)}`),c=typeof a.usage==`number`?a.usage:void 0,l={job_id:a.job_id??r,job_name:a.job_name??``,priority:a.priority??``,hyper_parameters:o??{},max_output_cnt:a.max_output_cnt??null,base_model:a.model??``,status:a.status??``,training_type:a.training_type??``,training_files:a.training_file_ids??[],validation_files:a.validation_file_ids??[],hyper_params:s.length?s.join(` · `):``,output_model:a.finetuned_output??``,model_name:a.model_name??``,created_at:a.create_time??a.gmt_create??``,updated_at:a.end_time??a.gmt_modified??``,usage_tokens:c??``,charge_type:typeof a.charge_type==`string`?a.charge_type:``};if(c!==void 0&&c>0&&a.model){let e=await tb(t,a.model,c);e&&(l.training_cost=e.cost,l.cost_basis=`${e.unitPrice} 元/${e.priceUnit}`)}O({...l,request_id:i.request_id},`json`)}}),rb=S({description:{"en-US":`Cancel a running fine-tune job`,"zh-CN":`取消正在运行的微调任务`},auth:`apiKey`,usageArgs:`--job-id <id>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --dry-run`],notes:[{"en-US":`Only PENDING / RUNNING jobs can be cancelled. Completed / failed / already-cancelled jobs return a server-side error (passed through verbatim).`,"zh-CN":`只有 PENDING / RUNNING 状态的任务可以取消。已完成、失败或已取消的任务会返回服务端错误(原样透传)。`}],async run(e){let{settings:t,flags:n}=e,r=n.jobId;if(t.dryRun){O({action:`finetune.cancel`,job_id:r},`json`);return}let i=await _e(e.client,r);t.quiet?D(r):O(i,`json`)}}),ib=S({description:{"en-US":`Delete a fine-tune job record`,"zh-CN":`删除微调任务记录`},auth:`apiKey`,risk:{level:`high`,message:{"en-US":`This permanently deletes the specified fine-tune job record and cannot be undone.`,"zh-CN":`该操作会永久删除指定的微调任务记录,且无法撤销。`}},usageArgs:`--job-id <id>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --dry-run`,`--job-id ft-xxx --yes`],notes:[{"en-US":`Irreversible — the job record is permanently removed.`,"zh-CN":`该操作不可撤销——任务记录将被永久删除。`},{"en-US":"Cancel a RUNNING job first via `finetune cancel` — the platform refuses to delete jobs that are still in flight.","zh-CN":"请先通过 `finetune cancel` 取消 RUNNING 任务,平台拒绝删除仍在运行的任务。"}],async run(e){let{settings:t,flags:n}=e,r=n.jobId;if(t.dryRun){O({action:`finetune.delete`,job_id:r},`json`);return}let i=await Me(e.client,r);t.quiet?D(r):O(i,`json`)}});function ab(e){if(typeof e==`string`)return e;let t=e,n=t.timestamp??t.time??t.create_time??``,r=t.level??``,i=t.message??t.msg??t.log??``;return i||n||r?[n,r,i].filter(Boolean).join(` `):JSON.stringify(e)}function ob(e,t){return ab(e).toLowerCase().includes(t)}async function sb(e,t,n){let r=[],i=1,a=0;for(let o=0;o<200;o++){let o=await st(e,t,{pageNo:i,pageSize:n}),s=o.output??o.data,c=s?.logs??[];if(a=s?.total??a,c.length===0||(r.push(...c),a&&r.length>=a)||c.length<n)break;i++}return{entries:r,total:a}}var cb=S({description:{"en-US":`Fetch training logs for a fine-tune job`,"zh-CN":`获取微调任务的训练日志`},auth:`apiKey`,usageArgs:`--job-id <id> [--page <n>] [--page-size <n>] [--search <keyword>] [--tail <n>]`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0},page:{type:`number`,valueHint:`<n>`,description:{"en-US":`Page number (default: 1)`,"zh-CN":`页码(默认:1)`}},pageSize:{type:`number`,valueHint:`<n>`,description:{"en-US":`Lines per page (default: server-defined)`,"zh-CN":`每页行数(默认:由服务端决定)`}},search:{type:`string`,valueHint:`<keyword>`,description:{"en-US":`Case-insensitive substring filter. When set, all log pages are fetched and filtered client-side (--page is ignored).`,"zh-CN":`不区分大小写的子字符串筛选。设置后会获取全部日志页并在客户端筛选(忽略 --page)。`}},tail:{type:`number`,valueHint:`<n>`,description:{"en-US":`Keep only the last N entries. When set, all log pages are fetched and the trailing N are kept (--page is ignored).`,"zh-CN":`仅保留最后 N 条记录。设置后会获取全部日志页并保留末尾 N 条(忽略 --page)。`}}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --page-size 100 --output json`,`--job-id ft-xxx --search checkpoint`,`--job-id ft-xxx --search error --output json`,`--job-id ft-xxx --tail 20`,`--job-id ft-xxx --search checkpoint --tail 5`],async run(e){let{settings:t,flags:n}=e,r=n.jobId,i=n.page,a=n.pageSize,o=n.search||void 0,s=n.tail;if(t.dryRun){O({action:`finetune.logs`,job_id:r,page:i,page_size:a,search:o,tail:s},`json`);return}if(o||s!==void 0){let{entries:t,total:n}=await sb(e.client,r,a??100),i=t,c;if(o){let e=o.toLowerCase();i=t.filter(t=>ob(t,e)),c=i.length}let l=s!==void 0&&s>=0?Math.min(s,i.length):void 0,u=l===void 0?i:i.slice(i.length-l);O({...c===void 0?{}:{matched:c},scanned:t.length,total:n||t.length,...o?{search:o}:{},...l===void 0?{}:{tail:l},logs:u},`json`);return}let c=await st(e.client,r,{pageNo:i,pageSize:a});O(c,`json`)}}),lb=S({description:{"en-US":`List checkpoints produced by a fine-tune job`,"zh-CN":`列出微调任务生成的 Checkpoint`},auth:`apiKey`,usageArgs:`--job-id <id>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx`,`--job-id ft-xxx --output json`],notes:[{"en-US":"`model_name` (shown for SUCCEEDED checkpoints) is the direct input for `deploy create --model-name`.","zh-CN":"SUCCEEDED Checkpoint 中显示的 `model_name` 可直接作为 `deploy create --model-name` 的输入。"},{"en-US":"Checkpoints expire ~15 days after creation; `expire_time` shows the deadline. Export or deploy before expiry.","zh-CN":"Checkpoint 创建后约 15 天过期,`expire_time` 显示截止时间。请在过期前导出或部署。"}],async run(e){let{settings:t,flags:n}=e,r=n.jobId;if(t.dryRun){O({action:`finetune.checkpoints`,job_id:r},`json`);return}let i=await Ot(e.client,r),a=i.output??i.data,o=Array.isArray(a)?a:a?.checkpoints??[],s=Array.isArray(a)?a.length:a?.total??o.length,c=o.map(e=>({checkpoint:e.checkpoint??e.checkpoint_id??``,step:e.step===void 0?``:String(e.step),status:e.status??``,model_name:e.model_name??``,expire_time:e.expire_time??``}));O({items:c,total:s,request_id:i.request_id},`json`);let l=Date.now(),u=c.filter(e=>{if(!e.expire_time)return!1;let t=new Date(e.expire_time).getTime();if(Number.isNaN(t))return!1;let n=t-l;return n>0&&n<2592e5});u.length>0&&process.stderr.write(`\n[warning] ${u.length} checkpoint(s) will expire within 72 hours. Export or deploy before expiry to avoid losing the model artifact.
2834
2834
  `)}}),ub=S({description:{"en-US":`Publish a checkpoint as a deployable model`,"zh-CN":`将 Checkpoint 发布为可部署模型`},auth:`apiKey`,usageArgs:`--job-id <id> --checkpoint <name> --model-name <name>`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0},checkpoint:{type:`string`,valueHint:`<name>`,description:{"en-US":"Checkpoint identifier from `finetune checkpoints` (required)","zh-CN":"来自 `finetune checkpoints` 的 Checkpoint 标识(必填)"},required:!0},modelName:{type:`string`,valueHint:`<name>`,description:{"en-US":`Deployable model name (required)`,"zh-CN":`可部署模型名称(必填)`},required:!0}},exampleArgs:[`--job-id ft-xxx --checkpoint ckpt-3 --model-name my-qwen-sft`],notes:[{"en-US":"Required before `deploy <modality> create` can target a checkpoint. The platform may auto-export the best checkpoint when a job reaches SUCCEEDED — explicit export is the canonical path for non-best checkpoints.","zh-CN":"必须先执行此操作,`deploy <modality> create` 才能使用 Checkpoint。任务达到 SUCCEEDED 后,平台可能自动导出最佳 Checkpoint;对于非最佳 Checkpoint,显式导出是标准方式。"}],async run(e){let{settings:t,flags:n}=e,r=n.jobId,i=n.checkpoint,a=n.modelName;if(t.dryRun){O({action:`finetune.export`,job_id:r,checkpoint:i,model_name:a},`json`);return}let o=await Re(e.client,r,i,a),s=(o.output??o.data)?.model_name??a;t.quiet?D(s):O(o,`json`)}});const db=/* @__PURE__ */ new Set([`SUCCEEDED`,`FAILED`,`CANCELED`]);function fb(){let e=/* @__PURE__ */ new Date,t=e=>String(e).padStart(2,`0`);return`${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}`}function pb(e){let t=Math.floor(e/1e3),n=Math.floor(t/60),r=t%60;return n===0?`${r}s`:`${n}m ${r}s`}function mb(e,t){return new Promise((n,r)=>{if(t.aborted){r(/* @__PURE__ */ Error(`aborted`));return}let i=()=>{clearTimeout(a),r(/* @__PURE__ */ Error(`aborted`))},a=setTimeout(()=>{t.removeEventListener(`abort`,i),n()},e);t.addEventListener(`abort`,i,{once:!0})})}var hb=S({description:{"en-US":`Probe a fine-tune job's status (default: single non-blocking fetch). Pass --follow to poll until terminal.`,"zh-CN":`查询微调任务状态(默认:单次非阻塞获取)。使用 --follow 持续轮询至终态。`},auth:`apiKey`,usageArgs:`--job-id <id> [--follow] [--interval <sec>] [--poll-timeout <sec>]`,flags:{jobId:{type:`string`,valueHint:`<id>`,description:{"en-US":`Fine-tune job ID (required)`,"zh-CN":`微调任务 ID(必填)`},required:!0},follow:{type:`switch`,description:{"en-US":`Block and poll until a terminal state (the legacy behavior). Without it, a single status probe is performed and the command returns immediately.`,"zh-CN":`阻塞并轮询至终态(旧版行为)。不使用时仅查询一次状态并立即返回。`}},interval:{type:`number`,valueHint:`<sec>`,description:{"en-US":`Seconds between polls with --follow (default: 10, min: 1). Ignored without --follow.`,"zh-CN":`使用 --follow 时的轮询间隔秒数(默认:10,最小:1)。未使用 --follow 时忽略。`}},pollTimeout:{type:`number`,valueHint:`<sec>`,description:{"en-US":`With --follow, stop polling after this many seconds (default: no limit). Ignored without --follow.`,"zh-CN":`使用 --follow 时,在指定秒数后停止轮询(默认:无限制)。未使用 --follow 时忽略。`}}},exampleArgs:[{"en-US":`--job-id ft-xxx # single probe, returns immediately`,"zh-CN":`--job-id ft-xxx # 单次查询,立即返回`},{"en-US":`--job-id ft-xxx --output json # status probe for agents`,"zh-CN":`--job-id ft-xxx --output json # 供智能体查询状态`},{"en-US":`--job-id ft-xxx --follow # block until terminal`,"zh-CN":`--job-id ft-xxx --follow # 阻塞等待至终态`},`--job-id ft-xxx --follow --interval 5`,`--job-id ft-xxx --follow --poll-timeout 3600`],notes:[{"en-US":`Default (no --follow) is a NON-BLOCKING single status probe: one fetch, then return immediately. This is the mode meant for agents / scripts — the caller owns the polling cadence, so the CLI never holds the terminal.`,"zh-CN":`默认不使用 --follow,执行非阻塞的单次状态查询:获取一次后立即返回。该模式适用于 Agent / 脚本,由调用方控制轮询节奏,CLI 不会持续占用终端。`},{"en-US":`A terminal FAILED/CANCELED status raises a normal CLI error (non-zero exit); a SUCCEEDED or still-running status returns 0. With --follow, exceeding --poll-timeout raises a timeout error.`,"zh-CN":`终态 FAILED/CANCELED 会触发普通 CLI 错误(非零退出码);SUCCEEDED 或仍在运行时返回 0。使用 --follow 时,超过 --poll-timeout 会触发超时错误。`},{"en-US":`Use --follow for the blocking, human-terminal-follow experience; use the default mode when driving the loop yourself (e.g. from an agent).`,"zh-CN":`需要在人工终端中阻塞跟踪时使用 --follow;自行驱动轮询(例如通过 Agent)时使用默认模式。`},{"en-US":"For per-step training output (not status), use `finetune logs`.","zh-CN":"要查看逐步骤训练输出(而非状态),请使用 `finetune logs`。"}],async run(e){let{settings:t,flags:r}=e,i=r.jobId,a=r.follow,o=Math.max(1,r.interval??10),s=r.pollTimeout;if(t.dryRun){O({action:`finetune.watch`,job_id:i,follow:a,interval:o,timeout:s},`json`);return}if(!a){let r=await ot(e.client,i),a=r.output??r.data,o=String(a?.status??``).toUpperCase(),s=db.has(o);if(t.quiet)D(o||`UNKNOWN`);else{let e={job_id:i,status:o||`UNKNOWN`,terminal:s,request_id:r.request_id},n=typeof a?.usage==`number`?a.usage:void 0;if(s&&n&&n>0&&a?.model){e.usage_tokens=n;let r=await tb(t,a.model,n);r&&(e.training_cost=r.cost,e.cost_basis=`${r.unitPrice} 元/${r.priceUnit}`)}O(e,`json`)}if(s&&o!==`SUCCEEDED`)throw new n(`Fine-tune job ${i} ended in status ${o}.`,l.GENERAL);return}let c=new AbortController,u=()=>c.abort();process.on(`SIGINT`,u);try{let r=``,a=Date.now();for(;;){let u=await ot(e.client,i,c.signal),d=u.output??u.data,f=String(d?.status??``).toUpperCase();if(!t.quiet&&f!==r&&(process.stderr.write(`${fb()} ${i} ${f||`UNKNOWN`}\n`),r=f),db.has(f)){let e=Date.now()-a;if(t.quiet)D(f||`UNKNOWN`);else{let e=typeof d?.usage==`number`?d.usage:void 0,n={...u};if(e&&e>0&&d?.model){let r=await tb(t,d.model,e);r&&(n.training_cost=r.cost,n.usage_tokens=e,n.cost_basis=`${r.unitPrice} 元/${r.priceUnit}`)}O(n,`json`)}if(f!==`SUCCEEDED`)throw new n(`Fine-tune job ${i} ended in status ${f} (elapsed ${pb(e)}).`,l.GENERAL);return}if(s!==void 0&&(Date.now()-a)/1e3>=s)throw new n(`Watching fine-tune job ${i} timed out after ${pb(Date.now()-a)} (last status: ${f||`UNKNOWN`}).`,l.TIMEOUT);await mb(o*1e3,c.signal)}}catch(e){if(c.signal.aborted){process.stderr.write(`
2835
2835
  Interrupted.
2836
2836
  `);return}throw e}finally{process.off(`SIGINT`,u)}}});async function gb(e){return await Ge(ie(e))}const _b={baseModel:{type:`string`,valueHint:`<m>`,description:{"en-US":`List training types supported by this base model.`,"zh-CN":`列出该基础模型支持的训练类型。`}},trainingType:{type:`string`,valueHint:`<t>`,description:{"en-US":`List models supporting this training type: ${ee.join(` | `)}.`,"zh-CN":`列出支持该训练类型的模型:${ee.join(` | `)}。`}}};var vb=S({description:{"en-US":`Query fine-tune training capability — by model (which training types it supports) or by training type (which models support it)`,"zh-CN":`查询微调训练能力:按模型查询其支持的训练类型,或按训练类型查询支持它的模型`},auth:`none`,usageArgs:`--base-model <m> | --training-type <t>`,flags:_b,exampleArgs:[`--base-model qwen3-8b`,`--training-type sft-lora`,`--training-type cpt --output json`,`--training-type sft --quiet`],notes:[{"en-US":`Exactly one of --base-model / --training-type is required.`,"zh-CN":`--base-model 和 --training-type 必须且只能指定一个。`},{"en-US":"Training-type values use the `<method>` / `<method>-lora` convention: sft | sft-lora | dpo | dpo-lora | cpt. (cpt has no -lora variant server-side.)","zh-CN":"训练类型遵循 `<method>` / `<method>-lora` 命名约定:sft | sft-lora | dpo | dpo-lora | cpt。(服务端没有 cpt-lora 变体。)"},{"en-US":`Queries listFoundationModels, a public API — no console login needed.`,"zh-CN":`查询公开 API listFoundationModels,无需登录控制台。`}],validate:e=>{if(e.baseModel&&e.trainingType)return`--base-model and --training-type are mutually exclusive; pass one.`;if(!e.baseModel&&!e.trainingType)return`one of --base-model / --training-type is required.`},async run(e){let{settings:t,flags:n}=e,r=n.baseModel||void 0,i=n.trainingType||void 0;if(t.dryRun){O({action:`finetune.capability`,model:r,training_type:i},`json`);return}if(r){let e=await Ve(t,r);if(!e){O({model:r,error:`No foundation model found matching "${r}".`},`json`);return}let n=Ft(e);if(t.quiet){for(let e of n)D(e);return}O({model:e.model??r,supported:n,supports:e.supports,trainingTypes:e.trainingTypes},`json`);return}if(!i||!St(i))throw new x(`--training-type "${i}" is not supported. Valid: ${ee.join(`, `)}.`);let{method:a,variant:o}=Gn(i),s=(await gb(t)).filter(e=>Gt(e,i)).map(e=>({model:e.model,name:e.name??e.model})).filter(e=>!!e.model).sort((e,t)=>e.model.localeCompare(t.model));if(t.quiet){for(let e of s)D(e.model);return}O({training_type:i,method:a,variant:o,count:s.length,models:s},`json`)}});const yb={baseModel:{type:`string`,valueHint:`<model>`,description:{"en-US":`Base model to fine-tune (e.g. qwen3-8b; not the output model name)`,"zh-CN":`待微调的基座模型(例如 qwen3-8b;不是输出模型名称)`},required:!0},datasets:{type:`string`,valueHint:`<ids>`,description:{"en-US":`Training dataset file IDs, comma-separated (required)`,"zh-CN":`训练数据集文件 ID,多个以逗号分隔(必填)`},required:!0},trainingType:{type:`string`,valueHint:`<type>`,description:{"en-US":`Training type: sft | dpo | cpt (default: sft)`,"zh-CN":`训练类型:sft | dpo | cpt(默认:sft)`}},nEpochs:{type:`number`,valueHint:`<n>`,description:{"en-US":`Number of training epochs (default: 3)`,"zh-CN":`训练轮数(默认:3)`}}},bb=[`sft`,`dpo`,`cpt`];var xb=S({description:{"en-US":`Estimate the training cost for a fine-tune job (token billing)`,"zh-CN":`估算微调任务的训练费用(按 Token 计费)`},auth:`console`,usageArgs:`--base-model <model> --datasets <ids> [--training-type <type>] [--n-epochs <n>]`,flags:yb,exampleArgs:[`--base-model qwen3-8b --datasets file-ft-xxx`,`--base-model qwen3-8b --datasets file-ft-xxx,file-ft-yyy --n-epochs 2`,`--base-model qwen3-8b --datasets file-ft-xxx --training-type cpt`],notes:[{"en-US":`Estimate only — the server computes token usage from the datasets; final cost is subject to the bill.`,"zh-CN":`该结果仅为估算值——服务端根据数据集计算 Token 用量,最终费用以账单为准。`},{"en-US":`Covers token billing for sft / dpo / cpt. Training-unit (MTU) billing is not supported by this command.`,"zh-CN":`支持估算 sft / dpo / cpt 的 Token 计费;此命令不支持训练单元(MTU)计费估算。`},{"en-US":`Hyper-parameters other than --n-epochs are fixed at representative defaults for estimation.`,"zh-CN":`除 --n-epochs 外,其他超参数会使用具有代表性的固定默认值进行估算。`}],async run(e){let{settings:t,flags:r}=e,i=r.baseModel,a=r.datasets.split(`,`).map(e=>e.trim()).filter(Boolean),o=(r.trainingType??`sft`).toLowerCase(),s=r.nEpochs??3;if(!bb.includes(o))throw new n(`Unsupported training type "${o}". Supported: ${bb.join(`, `)}.`,l.USAGE);if(a.length===0)throw new n(`--datasets must contain at least one file ID.`,l.USAGE);if(t.dryRun){O({action:`finetune.price`,model:i,datasets:a,trainingType:o,nEpochs:s},`json`);return}let c=await Je(e.client,i),u=Number(c.price);if(!Number.isFinite(u))throw new n(`No training price found for model "${i}".`,l.GENERAL,void 0,{rawResponse:JSON.stringify(c)});let d=o===`cpt`?await Ie(e.client,i,a.join(`,`),s):await Le(e.client,a,{nEpochs:s,batchSize:16,maxLength:8192}),f=d.estimatedDatasetConsumedTokensMinPerEpoch??0,p=d.estimatedDatasetConsumedTokensMaxPerEpoch??0,m=d.estimatedMixedConsumedTokensMinPerEpoch??0,h=d.estimatedMixedConsumedTokensMaxPerEpoch??0,g=(f+m)*s,_=(p+h)*s,v=g/1e3*u,y=_/1e3*u;O({model:i,training_type:o,n_epochs:s,unit_price:u,price_unit:c.priceUnit??`千Token`,estimated_tokens:{min:g,max:_},estimated_fee_yuan:{min:Number(v.toFixed(4)),max:Number(y.toFixed(4))},disclaimer:`Server-side estimate; final cost is subject to the bill.`},`json`)}});const Sb={deployedModel:{type:`string`,valueHint:`<code>`,required:!0,description:{"en-US":`Deployed model identifier (ModelCode)`,"zh-CN":`部署调用标识(ModelCode)`}}},Cb={instanceId:{type:`string`,valueHint:`<id>`,required:!0,description:{"en-US":`Capacity instance ID returned by the API`,"zh-CN":`接口返回的容量实例 ID`}}},wb={...Sb,operationId:{type:`string`,valueHint:`<id>`,required:!0,description:{"en-US":`Capacity operation ID returned by a write request`,"zh-CN":`写请求返回的容量操作 ID`}}};function Tb(e){for(let[t,n]of Object.entries(e))if([`deployedModel`,`instanceId`,`operationId`].includes(t)&&typeof n==`string`&&!n.trim())return`Identifiers must not be empty. / 标识不能为空。`}function Eb(e){if(e.page!==void 0&&(!Number.isSafeInteger(e.page)||e.page<1))return`--page must be a positive integer. / --page 必须是正整数。`;if(e.pageSize!==void 0&&(!Number.isSafeInteger(e.pageSize)||e.pageSize<1||e.pageSize>100))return`--page-size must be an integer from 1 to 100. / --page-size 必须是 1–100 的整数。`}function Db(e){return e===void 0?void 0:e.split(`,`).map(e=>e.trim())}const Ob=2147483;function kb(e,t){return new Promise((n,r)=>{t.throwIfAborted();let i=()=>{clearTimeout(a),r(t.reason)},a=setTimeout(()=>{t.removeEventListener(`abort`,i),n()},e);t.addEventListener(`abort`,i,{once:!0})})}async function Ab(e,t,r,i){let a=new AbortController,o,s=i.initialResponse,c=()=>new n(`Capacity operation wait timed out; query the same operation ID before retrying any write. / 等待容量操作超时;重试写操作前请查询同一操作 ID。`,l.TIMEOUT,void 0,{cause:{deployed_model:t,operation_id:r,last_response:o}}),u=()=>a.abort(i.signal?.reason);i.signal?.addEventListener(`abort`,u,{once:!0}),i.signal?.aborted&&u();let d=Date.now()+i.pollTimeout*1e3,f=setTimeout(()=>a.abort(c()),i.pollTimeout*1e3),p=()=>{Date.now()>=d&&!a.signal.aborted&&a.abort(c()),a.signal.throwIfAborted()},m=i.interval*1e3,h=Math.max(m,3e4);try{for(;;){p();let c=s??await nt(e,t,r,a.signal);s=void 0,o=c,p();let u=c.output??c.data;if(u?.operation_status===`FAILED`)throw new n(u.error_message??u.error_code??`Capacity operation failed. / 容量操作失败。`,l.GENERAL,void 0,{api:{httpStatus:200,apiCode:u.error_code,requestId:c.request_id},cause:u,rawResponse:JSON.stringify(c)});if(u?.operation_status===`SUCCEEDED`){let n=u.instance_id??i.fallbackInstanceId,r=n?await tt(e,t,n,a.signal):void 0;p();let o=await at(e,t,a.signal);return p(),{...c,instance:r,deployment:o}}if(u?.operation_status!==`PROCESSING`)throw new n(`Missing or unsupported operation_status; cannot determine completion. / 缺少或不支持的 operation_status,无法判断完成状态。`,l.GENERAL,void 0,{cause:c,rawResponse:JSON.stringify(c)});await kb(Math.min(m,Math.max(0,d-Date.now())),a.signal),m=Math.min(m*2,h)}}catch(e){throw a.signal.aborted?a.signal.reason:e}finally{clearTimeout(f),i.signal?.removeEventListener(`abort`,u)}}const jb={...wb,interval:{type:`number`,valueHint:`<seconds>`,description:{"en-US":`Initial poll interval (1–3600 seconds, default: 2); doubles up to max(initial, 30)`,"zh-CN":`初始轮询间隔(1–3600 秒,默认:2);逐次翻倍至 max(初始值, 30)`}},pollTimeout:{type:`number`,valueHint:`<seconds>`,description:{"en-US":`Total wait budget including requests and refresh (default: 600 seconds)`,"zh-CN":`总等待时限,包含请求及刷新(默认:600 秒)`}}};var Mb=S({description:{"en-US":`Wait for a capacity operation and refresh confirmed capacity`,"zh-CN":`等待容量操作完成并刷新已确认容量`},auth:`apiKey`,flags:jb,usageArgs:`--deployed-model <code> --operation-id <id> [--interval <seconds>] [--poll-timeout <seconds>]`,exampleArgs:[`--deployed-model example-model-code --operation-id 100001`,`--deployed-model example-model-code --operation-id 100001 --interval 2 --poll-timeout 600`],notes:[{"en-US":`Read-only GET polling: PROCESSING continues, FAILED exits 1 with the server error, SUCCEEDED refreshes the instance (when an ID is returned) and deployment. Output preserves the operation envelope and adds instance/deployment response envelopes. Missing or unknown status fails rather than assuming success.`,"zh-CN":`只读 GET 轮询:PROCESSING 继续等待,FAILED 透传服务端错误并退出 1,SUCCEEDED 刷新实例(返回 ID 时)及部署。输出保留操作响应结构,追加 instance/deployment 响应结构。缺失或未知状态报错,不推定成功。`},{"en-US":`--timeout limits each HTTP request; --poll-timeout bounds the entire wait (exit 5 on expiry). Ctrl-C stops local waiting, not the remote operation. No write is retried; use the original operation ID after a timeout or interruption.`,"zh-CN":`--timeout 限制单次 HTTP 请求,--poll-timeout 限制整个等待过程(超时退出 5)。Ctrl-C 只停止本地等待,不取消远端操作。不重试任何写请求;超时或中断后仍使用原操作 ID 查询。`}],validate(e){let t=Tb(e);if(t)return t;if(e.interval!==void 0&&(!Number.isFinite(e.interval)||e.interval<1||e.interval>3600))return`--interval must be 1–3600 seconds. / --interval 必须为 1–3600 秒。`;if(e.pollTimeout!==void 0&&(!Number.isFinite(e.pollTimeout)||e.pollTimeout<=0||e.pollTimeout>Ob))return`--poll-timeout must be > 0 and <= ${Ob} seconds. / --poll-timeout 必须大于 0 且不超过 ${Ob} 秒。`},async run(e){let{flags:t,settings:n}=e,r=t.interval??2,i=t.pollTimeout??600;if(n.dryRun){O({action:`deploy.operation.wait`,deployed_model:t.deployedModel,operation_id:t.operationId,interval:r,poll_timeout:i},`json`);return}let a=new AbortController,o=()=>a.abort(/* @__PURE__ */ Error(`Interrupted. / 已中断。`));process.once(`SIGINT`,o);try{let n=await Ab(e.client,t.deployedModel,t.operationId,{interval:r,pollTimeout:i,signal:a.signal});O(n,`json`)}catch(e){if(!a.signal.aborted)throw e}finally{process.removeListener(`SIGINT`,o)}}});const Nb={inputTpm:{type:`number`,valueHint:`<kTPM>`,description:{"en-US":`Absolute input capacity in kTPM (1 kTPM = 1000 tokens/minute)`,"zh-CN":`输入绝对容量,单位 kTPM(1 kTPM = 1000 Tokens/分钟)`}},outputTpm:{type:`number`,valueHint:`<kTPM>`,description:{"en-US":`Absolute output capacity in kTPM`,"zh-CN":`输出绝对容量,单位 kTPM`}}},Pb={duration:{type:`number`,valueHint:`<days>`,description:{"en-US":`Prepaid purchase/renewal duration, positive integer days`,"zh-CN":`预付费购买或续订天数,正整数`}},autoRenewal:{type:`boolean`,valueHint:`<true|false>`,description:{"en-US":`Explicitly enable or disable automatic renewal for prepaid purchases`,"zh-CN":`显式开启或关闭预付费自动续费`}},autoRenewalDuration:{type:`number`,valueHint:`<days>`,description:{"en-US":`Automatic renewal duration; required when enabled`,"zh-CN":`自动续费天数,开启自动续费时必填`}},autoRenewalCycle:{type:`string`,valueHint:`<cycle>`,description:{"en-US":`Optional supported renewal cycle, e.g. Day`,"zh-CN":`可选续费周期单位,按服务支持的值传入,如 Day`}}},Fb={wait:{type:`switch`,description:{"en-US":`Wait for the returned capacity operation and refresh capacity`,"zh-CN":`等待返回的容量操作完成并刷新容量`}},interval:{type:`number`,valueHint:`<seconds>`,description:{"en-US":`Initial wait poll interval (1–3600, default: 2 seconds)`,"zh-CN":`等待初始轮询间隔(1–3600,默认:2 秒)`}},pollTimeout:{type:`number`,valueHint:`<seconds>`,description:{"en-US":`Wait budget after submission (default: 600 seconds)`,"zh-CN":`提交后的等待总时限(默认:600 秒)`}}},Ib={requestId:{type:`string`,valueHint:`<uuid>`,description:{"en-US":`Request identifier; generated if omitted. Reuse with identical parameters only for the same capacity operation`,"zh-CN":`请求标识,省略时生成;仅重试同一容量操作时保持标识和参数一致`}},...Fb},Lb={orderType:{type:`string`,valueHint:`<UPGRADE|DOWNGRADE>`,choices:[`UPGRADE`,`DOWNGRADE`],description:{"en-US":`Optional order direction; otherwise the server decides`,"zh-CN":`可选变配方向,省略时由服务端判定`}}},Rb=[{"en-US":`Use --dry-run before confirming with --yes. A write is submitted once, never automatically retried. HTTP 200 is not operation success: inspect operation_status or pass --wait. After interruption or timeout, query the saved ModelCode/operation ID before considering another write.`,"zh-CN":`先用 --dry-run 预览,确认后再用 --yes。写请求只提交一次,不自动重试。HTTP 200 不等于操作成功:检查 operation_status 或使用 --wait。中断或超时后,先查询已保存的 ModelCode/操作 ID,再考虑是否重试写请求。`},{"en-US":`Capacities are absolute kTPM for one instance, not increments or ModelCode totals. Zero capacity is not release. Model-specific steps, limits and purchase periods are validated by the server. Automatic renewal configuration is part of purchase/scale/renew, not a standalone free setting change.`,"zh-CN":`容量是单个实例的绝对 kTPM,不是增量或 ModelCode 总量;容量归零不等于释放。模型步长、上下限及购买时长限制由服务端校验。自动续费配置属于购买、变配或续订流程,不是独立免费设置操作。`}];function zb(e){if(e.requestId!==void 0&&(!e.requestId.trim()||/[\r\n]/.test(e.requestId)))return`--request-id must be non-empty and contain no line breaks. / --request-id 不能为空或包含换行。`;if(!e.wait&&(e.interval!==void 0||e.pollTimeout!==void 0))return`--interval/--poll-timeout require --wait. / --interval/--poll-timeout 需要同时指定 --wait。`;if(e.interval!==void 0&&(!Number.isFinite(e.interval)||e.interval<1||e.interval>3600))return`--interval must be 1–3600 seconds. / --interval 必须为 1–3600 秒。`;if(e.pollTimeout!==void 0&&(!Number.isFinite(e.pollTimeout)||e.pollTimeout<=0||e.pollTimeout>2147483))return`--poll-timeout must be > 0 and <= 2147483 seconds. / --poll-timeout 必须大于 0 且不超过 2147483 秒。`}async function Bb(e,t,r,i){let a=await tt(e,t,r),o=a.output??a.data;if(!o||o.deleted===!0||o[`can_${i}`]!==!0)throw new n(`Capacity instance does not currently allow ${i}; refresh its details. / 容量实例当前不允许 ${i},请刷新详情。`,l.USAGE,void 0,{cause:a});return o}async function Vb(e,t){try{return await t()}catch(t){let r={client_request_id:e,error:t instanceof Error?t.message:t},i=`Request ID: ${e}. No automatic retry; first check whether the operation was accepted. / 请求标识:${e}。未自动重试,请先核查操作是否已受理。`;if(t instanceof n)throw new n(t.message,t.exitCode,[t.hint,i].filter(Boolean).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bailian-cli-commands",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.",
5
5
  "homepage": "https://bailian.console.aliyun.com/cli",
6
6
  "bugs": {
@@ -32,8 +32,8 @@
32
32
  "chalk": "^5.6.2",
33
33
  "smol-toml": "^1.4.2",
34
34
  "yaml": "^2.8.3",
35
- "bailian-cli-core": "2.0.1",
36
- "bailian-cli-runtime": "2.0.1"
35
+ "bailian-cli-core": "2.1.0",
36
+ "bailian-cli-runtime": "2.1.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^24",