feat: 训练任务支持从最新 checkpoint 继续训练
- 后端: 新增 resume/checkpoints 端点,compute 客户端与适配器支持续训 - 计算节点: llama_factory 适配器断点续训支持及测试 - 前端: fine-tune API 封装与列表页续训/断点展示
This commit is contained in:
@@ -49,6 +49,15 @@ export interface FineTuneGpuStatus {
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface FineTuneCheckpoint {
|
||||
id: string
|
||||
step: number
|
||||
name: string
|
||||
path: string
|
||||
size_bytes?: number
|
||||
create_time?: string
|
||||
}
|
||||
|
||||
/** 训练任务列表 */
|
||||
export const getFineTuneList = () => get<FineTuneTask[]>('/fine-tune')
|
||||
|
||||
@@ -89,6 +98,13 @@ export const updateFineTune = (id: string | number, data: Partial<FineTuneTask>)
|
||||
/** 停止训练任务 */
|
||||
export const stopFineTune = (id: string | number) => post(`/fine-tune/stop/${id}`)
|
||||
|
||||
/** 从最新 checkpoint 继续训练 */
|
||||
export const resumeFineTune = (id: string | number) => post(`/fine-tune/${id}/resume`)
|
||||
|
||||
/** 获取训练断点 */
|
||||
export const getFineTuneCheckpoints = (id: string | number) =>
|
||||
get<FineTuneCheckpoint[]>(`/fine-tune/${id}/checkpoints`)
|
||||
|
||||
/** 删除训练任务 */
|
||||
export const deleteFineTune = (id: string | number) => del(`/fine-tune/${id}`)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import DataTablePage from '@/components/DataTablePage.vue'
|
||||
import ModelStatusTag from '@/components/ModelStatusTag.vue'
|
||||
import { usePolling } from '@/composables/usePolling'
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
getFineTuneList,
|
||||
deleteFineTune,
|
||||
stopFineTune,
|
||||
resumeFineTune,
|
||||
getFineTuneProgress,
|
||||
getFineTune,
|
||||
} from '@/api/modules/fineTune'
|
||||
@@ -87,9 +88,29 @@ async function handleDelete(row: any) {
|
||||
}
|
||||
|
||||
async function handleStop(row: any) {
|
||||
await ElMessageBox.confirm(
|
||||
'停止后将保留已生成的 checkpoint,可在资源可用时继续训练。是否停止当前任务?',
|
||||
'停止训练',
|
||||
{ type: 'warning' },
|
||||
)
|
||||
await stopFineTune(row.id)
|
||||
ElMessage.success('训练任务已停止')
|
||||
loadData()
|
||||
ElMessage.success('训练任务已停止,可继续训练')
|
||||
await loadData()
|
||||
}
|
||||
|
||||
async function handleResume(row: any) {
|
||||
await ElMessageBox.confirm(
|
||||
'系统将使用最新 checkpoint 继续训练,并重新检查基座模型、数据集、算力节点和 GPU 是否可用。是否继续?',
|
||||
'继续训练',
|
||||
{ type: 'info' },
|
||||
)
|
||||
try {
|
||||
await resumeFineTune(row.id)
|
||||
ElMessage.success('继续训练已提交')
|
||||
await loadData()
|
||||
} catch {
|
||||
// 请求拦截器已展示后端返回的具体预检原因
|
||||
}
|
||||
}
|
||||
|
||||
function viewLog(row: any) {
|
||||
@@ -217,7 +238,7 @@ onMounted(async () => {
|
||||
<template #actions="{ row }">
|
||||
<div class="action-buttons">
|
||||
<el-button
|
||||
v-if="row.status === 'running'"
|
||||
v-if="['syncing', 'queued', 'running'].includes(row.status)"
|
||||
type="warning"
|
||||
link
|
||||
size="small"
|
||||
@@ -225,6 +246,15 @@ onMounted(async () => {
|
||||
>
|
||||
<i class="fa fa-stop-circle-o" style="margin-right: 4px" /> 停止
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'stopped' || row.status === 'failed'"
|
||||
type="success"
|
||||
link
|
||||
size="small"
|
||||
@click="handleResume(row)"
|
||||
>
|
||||
<i class="fa fa-play-circle-o" style="margin-right: 4px" /> 继续
|
||||
</el-button>
|
||||
<el-button type="primary" link size="small" @click="viewLog(row)">
|
||||
<i class="fa fa-file-text-o" style="margin-right: 4px" /> 日志
|
||||
</el-button>
|
||||
|
||||
Reference in New Issue
Block a user