|
| 1 | +import { |
| 2 | + defineCommand, |
| 3 | + getFineTuneParams, |
| 4 | + isTrainingTypeCli, |
| 5 | + TRAINING_TYPES_CLI, |
| 6 | + DEFAULT_TRAINING_TYPE, |
| 7 | + TRAINING_TYPE_MAP, |
| 8 | + BailianError, |
| 9 | + ExitCode, |
| 10 | + type FlagsDef, |
| 11 | +} from "bailian-cli-core"; |
| 12 | + |
| 13 | +/** |
| 14 | + * Build the training-option context from the dedicated flags. The OpenAPI |
| 15 | + * expects it as one `hyper_parameters` query parameter holding a JSON string |
| 16 | + * (e.g. {"modality":"vision","reasoning_mode":"thinking"}); models without |
| 17 | + * dynamic train options simply omit it. |
| 18 | + */ |
| 19 | +function buildTrainOptions( |
| 20 | + modality: string | undefined, |
| 21 | + reasoningMode: string | undefined, |
| 22 | +): Record<string, string> { |
| 23 | + const options: Record<string, string> = {}; |
| 24 | + if (modality) options.modality = modality; |
| 25 | + if (reasoningMode) options.reasoning_mode = reasoningMode; |
| 26 | + return options; |
| 27 | +} |
| 28 | + |
| 29 | +const PARAMS_FLAGS = { |
| 30 | + baseModel: { |
| 31 | + type: "string", |
| 32 | + valueHint: "<model>", |
| 33 | + description: { |
| 34 | + "en-US": "Base model to query training parameters for (e.g. qwen3-8b)", |
| 35 | + "zh-CN": "要查询训练参数的基础模型(例如 qwen3-8b)", |
| 36 | + }, |
| 37 | + required: true, |
| 38 | + }, |
| 39 | + trainingType: { |
| 40 | + type: "string", |
| 41 | + valueHint: "<t>", |
| 42 | + description: { |
| 43 | + "en-US": `Training type: ${TRAINING_TYPES_CLI.join(" | ")} (default: ${DEFAULT_TRAINING_TYPE}). Mapped to the server's training_type (e.g. sft-lora -> efficient_sft).`, |
| 44 | + "zh-CN": `训练类型:${TRAINING_TYPES_CLI.join(" | ")}(默认:${DEFAULT_TRAINING_TYPE})。会映射为服务端 training_type(例如 sft-lora -> efficient_sft)。`, |
| 45 | + }, |
| 46 | + }, |
| 47 | + mtuSpecCode: { |
| 48 | + type: "string", |
| 49 | + valueHint: "<code>", |
| 50 | + description: { |
| 51 | + "en-US": "MTU spec code, required for resource-group queries (e.g. MTU2)", |
| 52 | + "zh-CN": "训练单元(MTU)规格编码,查询资源类参数时需要(例如 MTU2)", |
| 53 | + }, |
| 54 | + }, |
| 55 | + paramType: { |
| 56 | + type: "string", |
| 57 | + valueHint: "<type>", |
| 58 | + description: { |
| 59 | + "en-US": "Parameter group: hyper | resource. Omit to return all groups.", |
| 60 | + "zh-CN": "参数类别:hyper | resource。不传返回全部分类。", |
| 61 | + }, |
| 62 | + choices: ["hyper", "resource"] as const, |
| 63 | + }, |
| 64 | + modality: { |
| 65 | + type: "string", |
| 66 | + valueHint: "<text|vision>", |
| 67 | + description: { |
| 68 | + "en-US": |
| 69 | + "Training modality: text | vision. Only meaningful for models with dynamic train options; sent as modality inside hyper_parameters.", |
| 70 | + "zh-CN": |
| 71 | + "训练模态:text | vision。仅对配置了动态训练选项的模型有意义;作为 hyper_parameters 中的 modality 发送。", |
| 72 | + }, |
| 73 | + choices: ["text", "vision"] as const, |
| 74 | + }, |
| 75 | + reasoningMode: { |
| 76 | + type: "string", |
| 77 | + valueHint: "<instruct|thinking>", |
| 78 | + description: { |
| 79 | + "en-US": |
| 80 | + "Reasoning mode: instruct (non-thinking) | thinking. Only meaningful for models with dynamic train options; sent as reasoning_mode inside hyper_parameters.", |
| 81 | + "zh-CN": |
| 82 | + "思考模式:instruct(非思考)| thinking(思考)。仅对配置了动态训练选项的模型有意义;作为 hyper_parameters 中的 reasoning_mode 发送。", |
| 83 | + }, |
| 84 | + choices: ["instruct", "thinking"] as const, |
| 85 | + }, |
| 86 | + lang: { |
| 87 | + type: "string", |
| 88 | + valueHint: "<locale>", |
| 89 | + description: { |
| 90 | + "en-US": "Locale for server-side display text (e.g. en, zh). Omit for the server default.", |
| 91 | + "zh-CN": "服务端展示文案的语言(例如 en、zh)。不传由服务端决定。", |
| 92 | + }, |
| 93 | + }, |
| 94 | + workspaceId: { |
| 95 | + type: "string", |
| 96 | + valueHint: "<id>", |
| 97 | + description: { |
| 98 | + "en-US": |
| 99 | + "Workspace ID sent as X-DashScope-WorkSpace; omit when your API key is already workspace-bound.", |
| 100 | + "zh-CN": "以 X-DashScope-WorkSpace 头发送的业务空间 ID;API Key 已绑定空间时可省略。", |
| 101 | + }, |
| 102 | + }, |
| 103 | +} satisfies FlagsDef; |
| 104 | + |
| 105 | +export default defineCommand({ |
| 106 | + defaultOutput: "json", |
| 107 | + description: { |
| 108 | + "en-US": "Query the training hyper-parameter configuration for a base model", |
| 109 | + "zh-CN": "查询基础模型的训练超参配置", |
| 110 | + }, |
| 111 | + auth: "apiKey", |
| 112 | + usageArgs: |
| 113 | + "--base-model <model> [--training-type <t>] [--param-type <hyper|resource>] [--mtu-spec-code <code>] [--modality <text|vision>] [--reasoning-mode <instruct|thinking>] [--lang <locale>] [--workspace-id <id>]", |
| 114 | + flags: PARAMS_FLAGS, |
| 115 | + exampleArgs: [ |
| 116 | + "--base-model qwen3-8b", |
| 117 | + "--base-model qwen3-8b --training-type sft", |
| 118 | + "--base-model qwen3-8b --training-type dpo-lora --param-type hyper", |
| 119 | + "--base-model qwen3.7-plus-2026-05-26 --modality vision --reasoning-mode thinking", |
| 120 | + "--base-model qwen3-8b --param-type resource --mtu-spec-code MTU2", |
| 121 | + "--base-model qwen3-8b --output json", |
| 122 | + "--base-model qwen3-8b --dry-run", |
| 123 | + ], |
| 124 | + notes: [ |
| 125 | + { |
| 126 | + "en-US": |
| 127 | + "Queries GET /api/v1/fine-tunes/fine-tunes-params. Each hyper_params entry keeps server field names verbatim (recommend_value, defaultValue, is_advanced_parameter, ...).", |
| 128 | + "zh-CN": |
| 129 | + "调用 GET /api/v1/fine-tunes/fine-tunes-params。每条 hyper_params 原样保留服务端字段名(recommend_value、defaultValue、is_advanced_parameter 等)。", |
| 130 | + }, |
| 131 | + { |
| 132 | + "en-US": |
| 133 | + "--modality / --reasoning-mode travel as one hyper_parameters query parameter (JSON). Supported values and combinations vary per model; invalid combinations are rejected by the server and surfaced as-is. Models without dynamic train options ignore these flags.", |
| 134 | + "zh-CN": |
| 135 | + "--modality / --reasoning-mode 会合并为单个 hyper_parameters query 参数(JSON)发送。各模型支持的取值与组合不同;非法组合由服务端拒绝并原样透传错误。未配置动态训练选项的模型可忽略这两个 flag。", |
| 136 | + }, |
| 137 | + { |
| 138 | + "en-US": |
| 139 | + "resume_from_checkpoint in the output reports whether the training type supports resuming training.", |
| 140 | + "zh-CN": "输出中的 resume_from_checkpoint 表示该训练类型是否支持断点续训。", |
| 141 | + }, |
| 142 | + ], |
| 143 | + async run(ctx) { |
| 144 | + const { settings, flags } = ctx; |
| 145 | + |
| 146 | + const trainingTypeCli = flags.trainingType || DEFAULT_TRAINING_TYPE; |
| 147 | + if (!isTrainingTypeCli(trainingTypeCli)) { |
| 148 | + throw new BailianError( |
| 149 | + `--training-type "${trainingTypeCli}" is not supported.`, |
| 150 | + ExitCode.USAGE, |
| 151 | + `Supported values: ${TRAINING_TYPES_CLI.join(", ")} (default: ${DEFAULT_TRAINING_TYPE}).`, |
| 152 | + ); |
| 153 | + } |
| 154 | + const trainingType = TRAINING_TYPE_MAP[trainingTypeCli].server; |
| 155 | + const trainOptions = buildTrainOptions(flags.modality, flags.reasoningMode); |
| 156 | + |
| 157 | + if (settings.dryRun) { |
| 158 | + const resultData = { |
| 159 | + action: "finetune.params", |
| 160 | + base_model: flags.baseModel, |
| 161 | + training_type: trainingType, |
| 162 | + ...(flags.paramType ? { param_type: flags.paramType } : {}), |
| 163 | + ...(flags.mtuSpecCode ? { mtu_spec_code: flags.mtuSpecCode } : {}), |
| 164 | + ...(Object.keys(trainOptions).length > 0 |
| 165 | + ? { hyper_parameters: JSON.stringify(trainOptions) } |
| 166 | + : {}), |
| 167 | + ...(flags.lang ? { lang: flags.lang } : {}), |
| 168 | + ...(flags.workspaceId ? { workspace_id: flags.workspaceId } : {}), |
| 169 | + }; |
| 170 | + ctx.output.result( |
| 171 | + resultData, |
| 172 | + ctx.settings.output === "text" ? { text: JSON.stringify(resultData, null, 2) } : undefined, |
| 173 | + ); |
| 174 | + return; |
| 175 | + } |
| 176 | + |
| 177 | + const response = await getFineTuneParams(ctx.client, { |
| 178 | + inputModel: flags.baseModel, |
| 179 | + trainingType, |
| 180 | + mtuSpecCode: flags.mtuSpecCode || undefined, |
| 181 | + paramType: flags.paramType || undefined, |
| 182 | + trainOptions, |
| 183 | + lang: flags.lang || undefined, |
| 184 | + workspaceId: flags.workspaceId || undefined, |
| 185 | + }); |
| 186 | + |
| 187 | + ctx.output.result( |
| 188 | + response, |
| 189 | + ctx.settings.output === "text" ? { text: JSON.stringify(response, null, 2) } : undefined, |
| 190 | + ); |
| 191 | + }, |
| 192 | +}); |
0 commit comments