feat: 调优创建支持 SFT 自动合并权重

FineTuneTask 类型新增 auto_merge 字段,SFT 训练表单新增自动合并权重开关并在创建/启动时随参提交,回归脚本补充 auto_merge 与模型对话框断言。
This commit is contained in:
caoxiaozhu
2026-07-11 11:00:39 +08:00
parent ee50350dff
commit ba465b5016
3 changed files with 26 additions and 5 deletions

View File

@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url'
const root = resolve(fileURLToPath(new URL('..', import.meta.url))) const root = resolve(fileURLToPath(new URL('..', import.meta.url)))
const source = readFileSync(resolve(root, 'src/views/fine-tune/FineTuneCreateView.vue'), 'utf8') const source = readFileSync(resolve(root, 'src/views/fine-tune/FineTuneCreateView.vue'), 'utf8')
const modelDialogSource = readFileSync(resolve(root, 'src/components/ModelSelectDialog.vue'), 'utf8')
function assert(condition, message) { function assert(condition, message) {
if (!condition) { if (!condition) {
@@ -18,10 +19,16 @@ assert(source.includes('gpu-usage-bar'), 'GPU cards should visualize usage with
assert(source.includes('is-busy'), 'GPU cards should have a distinct busy state for usage over 80%') assert(source.includes('is-busy'), 'GPU cards should have a distinct busy state for usage over 80%')
assert(source.includes('gpu.gpu_percent > 80'), 'GPU busy state should be driven by usage over 80%') assert(source.includes('gpu.gpu_percent > 80'), 'GPU busy state should be driven by usage over 80%')
assert(source.includes('modelDialogVisible'), 'Model selection should open a dialog instead of a plain select') assert(source.includes('modelDialogVisible'), 'Model selection should open a dialog instead of a plain select')
assert(!source.includes('width="78vw"'), 'Model dialog should not use the previous oversized 78vw width') assert(!modelDialogSource.includes('width="78vw"'), 'Model dialog should not use the previous oversized 78vw width')
assert(source.includes('width="860px"'), 'Model dialog should use a compact enterprise modal width') assert(modelDialogSource.includes('width="860px"'), 'Model dialog should use a compact enterprise modal width')
assert(source.includes('model-series-list'), 'Model dialog should include a model series list column') assert(modelDialogSource.includes('model-series-list'), 'Model dialog should include a model series list column')
assert(source.includes('model-version-list'), 'Model dialog should include a snapshot/version list column') assert(modelDialogSource.includes('model-version-list'), 'Model dialog should include a snapshot/version list column')
assert(source.includes('confirmModelSelection'), 'Model dialog should confirm the selected model before updating the form') assert(modelDialogSource.includes('handleConfirm'), 'Model dialog should confirm the selected model before updating the form')
assert(source.includes("auto_merge: false"), 'Auto merge should default to disabled')
assert(source.includes('v-if="form.train_type === \'SFT\'"'), 'Merge model settings should only be visible for SFT')
assert(source.includes('content-position="left">合并模型'), 'SFT form should include a merge model section below data configuration')
assert(source.includes('v-model="form.auto_merge"'), 'Merge model section should provide an auto merge selector')
assert(source.includes('label="自动合并权重并保存"'), 'Auto merge selector should use a clear visible label')
assert((source.match(/auto_merge: form\.train_type === 'SFT' && form\.auto_merge/g) || []).length === 2, 'Auto merge should be sent for SFT when creating and starting the task')
console.log('fine-tune create UI regression checks passed') console.log('fine-tune create UI regression checks passed')

View File

@@ -94,6 +94,7 @@ export interface FineTuneTask {
template?: string template?: string
base_model: number | string base_model: number | string
train_dataset_id?: number | string train_dataset_id?: number | string
auto_merge?: boolean
output_model_name?: string output_model_name?: string
gpus?: number[] gpus?: number[]
batch_size?: number batch_size?: number

View File

@@ -34,6 +34,7 @@ const form = reactive({
template: 'qwen', template: 'qwen',
train_method: 'lora' as 'lora' | 'full', train_method: 'lora' as 'lora' | 'full',
train_dataset_id: '' as string | number, train_dataset_id: '' as string | number,
auto_merge: false,
// 训练参数 // 训练参数
batch_size: 1, batch_size: 1,
learning_rate: 0.0001, learning_rate: 0.0001,
@@ -216,6 +217,7 @@ async function handleSubmit() {
train_method: form.train_method, train_method: form.train_method,
gpus: selectedGpus.value, gpus: selectedGpus.value,
train_dataset_id: form.train_dataset_id, train_dataset_id: form.train_dataset_id,
auto_merge: form.train_type === 'SFT' && form.auto_merge,
output_model_name: form.name, output_model_name: form.name,
batch_size: form.batch_size, batch_size: form.batch_size,
learning_rate: form.learning_rate, learning_rate: form.learning_rate,
@@ -244,6 +246,7 @@ async function handleSubmit() {
train_type: form.train_type, train_type: form.train_type,
train_method: form.train_method, train_method: form.train_method,
train_dataset_id: form.train_dataset_id, train_dataset_id: form.train_dataset_id,
auto_merge: form.train_type === 'SFT' && form.auto_merge,
output_model_name: form.name, output_model_name: form.name,
gpus: selectedGpus.value, gpus: selectedGpus.value,
batch_size: form.batch_size, batch_size: form.batch_size,
@@ -424,6 +427,16 @@ onMounted(() => {
</el-select> </el-select>
</el-form-item> </el-form-item>
<template v-if="form.train_type === 'SFT'">
<el-divider content-position="left">合并模型</el-divider>
<el-form-item label="自动合并权重并保存">
<el-select v-model="form.auto_merge" style="width: 420px;">
<el-option label="否" :value="false" />
<el-option label="是" :value="true" />
</el-select>
</el-form-item>
</template>
<!-- 训练命令预览 --> <!-- 训练命令预览 -->
<el-divider content-position="left">训练命令预览</el-divider> <el-divider content-position="left">训练命令预览</el-divider>
<div class="command-preview-wrapper"> <div class="command-preview-wrapper">