Resume training at breakpoint

This commit is contained in:
yanmuyuan 2025-05-28 15:30:12 +08:00
parent c3642b0f2b
commit b219475e04
3 changed files with 407 additions and 251 deletions

View file

@ -581,8 +581,66 @@ class CloudProgressHolder:
Returns:
Dict: Progress data
"""
return self.progress.to_dict()
return self.progress.data
def is_stage_completed(self, stage_name):
"""
检查指定阶段是否已完成
Args:
stage_name: 阶段名称格式化后的"activating_the_memory_matrix"
Returns:
bool: 如果阶段已完成返回True否则返回False
"""
try:
# 加载最新的进度数据
self._load_progress()
# 获取阶段对象
stage = self.progress.stage_map.get(stage_name)
if not stage:
logger.warning(f"Stage {stage_name} not found in progress data")
return False
# 检查阶段状态
return stage.get("status") == CloudStatus.COMPLETED and stage.get("progress") == 100.0
except Exception as e:
logger.error(f"Error checking stage completion status: {str(e)}")
return False
def is_step_completed(self, stage_name, step_name):
"""
检查指定阶段中的特定步骤是否已完成
Args:
stage_name: 阶段名称格式化后的"activating_the_memory_matrix"
step_name: 步骤名称"list_documents"
Returns:
bool: 如果步骤已完成返回True否则返回False
"""
try:
# 加载最新的进度数据
self._load_progress()
# 获取阶段对象
stage = self.progress.stage_map.get(stage_name)
if not stage:
logger.warning(f"Stage {stage_name} not found in progress data")
return False
# 查找步骤
for step in stage.get("steps", []):
if step.get("name") == step_name:
return step.get("completed", False) and step.get("status") == CloudStatus.COMPLETED
logger.warning(f"Step {step_name} not found in stage {stage_name}")
return False
except Exception as e:
logger.error(f"Error checking step completion status: {str(e)}")
return False
def update_message(self, message: str):
"""
更新进度消息

View file

@ -1,31 +1,15 @@
import os
import re
import time
import json
from typing import Optional, Dict, Any, List
from pathlib import Path
import threading
import os
import time
from pathlib import Path
from typing import Dict, Any
from lpm_kernel.api.domains.cloud_service.service import CloudService
from lpm_kernel.api.domains.cloud_service.cloud_process_step import CloudProcessStep
from lpm_kernel.api.domains.cloud_service.cloud_progress_holder import CloudProgressHolder, CloudStatus
from lpm_kernel.api.domains.trainprocess.training_params_manager import TrainingParamsManager
from lpm_kernel.api.domains.cloud_service.service import CloudService
from lpm_kernel.api.domains.trainprocess.trainprocess_service import TrainProcessService
from lpm_kernel.api.services.user_llm_config_service import UserLLMConfigService
from lpm_kernel.configs.config import Config
from lpm_kernel.kernel.l1.l1_manager import (
extract_notes_from_documents,
document_service,
get_latest_status_bio,
get_latest_global_bio,
generate_l1_from_l0
)
from lpm_kernel.kernel.chunk_service import ChunkService
from lpm_kernel.file_data.chunker import DocumentChunker
from lpm_kernel.configs.logging import get_train_process_logger
from lpm_kernel.kernel.note_service import NoteService
logger = get_train_process_logger()
@ -71,35 +55,43 @@ class CloudTrainProcessService(TrainProcessService):
self.cloud_service = CloudService()
@classmethod
def get_instance(cls, current_model_name: str = None, base_model=None, training_type=None, hyper_parameters=None):
"""
Get the current instance of CloudTrainProcessService
def get_instance(cls):
"""Get the current instance of CloudTrainProcessService
Args:
current_model_name: Optional model name to update the instance with
base_model: Base model for cloud training (only used when creating a new instance)
training_type: Type of training (only used when creating a new instance)
hyper_parameters: Training hyperparameters (only used when creating a new instance)
Returns:
CloudTrainProcessService: The singleton instance
"""
if cls._instance is None:
if current_model_name is None:
logger.warning("current_model_name must be provided when creating a new instance")
return None
if base_model is None or training_type is None or hyper_parameters is None:
logger.warning("base_model, training_type, and hyper_parameters must be provided when creating a new instance")
return None
return cls(current_model_name=current_model_name, base_model=base_model,
training_type=training_type, hyper_parameters=hyper_parameters)
if current_model_name is not None:
# Update the existing instance with new model name
cls._instance.model_name = current_model_name
cls._instance.progress = CloudProgressHolder(current_model_name)
if cls._instance is not None:
return cls._instance
try:
return cls._instance
params_file = Path("data/cloud_progress/cloud_training_params.json")
if params_file.exists():
with open(params_file, "r", encoding="utf-8") as f:
params = json.load(f)
model_name = params.get("model_name")
base_model = params.get("base_model")
training_type = params.get("training_type", "efficient_sft")
hyper_parameters = params.get("hyper_parameters", {})
if model_name and base_model:
logger.info(f"Loaded training parameters for model {model_name} from file")
cls._instance = cls(current_model_name=model_name,
base_model=base_model,
training_type=training_type,
hyper_parameters=hyper_parameters)
return cls._instance
else:
logger.warning("Invalid training parameters in file: missing model_name or base_model")
except Exception as e:
logger.warning(f"Failed to load training parameters from file: {str(e)}")
logger.warning("No valid training parameters found in file")
return None
@ -110,43 +102,71 @@ class CloudTrainProcessService(TrainProcessService):
logger.info("Executing memory matrix activation steps...")
stage_name = "activating_the_memory_matrix"
stage = self.progress.progress.stage_map.get(stage_name)
total_steps = len(stage["steps"]) if stage else 4
# 1. 列出文档
logger.info("Step 1.1: Listing documents...")
if not super().list_documents():
logger.error("Failed to list documents")
return False
# 检查该阶段是否已完成,如果已完成则跳过
if self.progress.is_stage_completed(stage_name):
logger.info(f"Stage '{stage_name}' already completed, skipping...")
else:
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 更新第一步完成后的进度
if stage:
stage["progress"] = 25.0 # 第一步完成进度25%
stage["status"] = CloudStatus.IN_PROGRESS
# 更新步骤状态
if len(stage["steps"]) > 0:
stage["steps"][0]["completed"] = True
stage["steps"][0]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 25% after completing list_documents")
self._update_overall_progress()
# 1. 列出文档
logger.info("Step 1.1: Listing documents...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 检查该步骤是否已完成
if self.progress.is_step_completed(stage_name, "list_documents"):
logger.info("Step 'list_documents' already completed, skipping...")
else:
if not super().list_documents():
logger.error("Failed to list documents")
return False
# 2. 生成文档嵌入
logger.info("Step 1.2: Generating document embeddings...")
if not super().generate_document_embeddings():
logger.error("Failed to generate document embeddings")
return False
# 更新第一步完成后的进度
if stage:
stage["progress"] = 25.0 # 第一步完成进度25%
stage["status"] = CloudStatus.IN_PROGRESS
# 更新步骤状态
if len(stage["steps"]) > 0:
stage["steps"][0]["completed"] = True
stage["steps"][0]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 25% after completing list_documents")
self._update_overall_progress()
# 更新第二步完成后的进度
if stage:
stage["progress"] = 50.0 # 第二步完成进度50%
# 更新步骤状态
if len(stage["steps"]) > 1:
stage["steps"][1]["completed"] = True
stage["steps"][1]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 50% after completing generate_document_embeddings")
self._update_overall_progress()
# 2. 生成文档嵌入
logger.info("Step 1.2: Generating document embeddings...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 检查该步骤是否已完成
if self.progress.is_step_completed(stage_name, "generate_document_embeddings"):
logger.info("Step 'generate_document_embeddings' already completed, skipping...")
else:
if not super().generate_document_embeddings():
logger.error("Failed to generate document embeddings")
return False
# 更新第二步完成后的进度
if stage:
stage["progress"] = 50.0 # 第二步完成进度50%
# 更新步骤状态
if len(stage["steps"]) > 1:
stage["steps"][1]["completed"] = True
stage["steps"][1]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 50% after completing generate_document_embeddings")
self._update_overall_progress()
# 3. 处理文档分块
logger.info("Step 1.3: Processing document chunks...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
if not super().process_chunks():
logger.error("Failed to process document chunks")
return False
@ -163,6 +183,10 @@ class CloudTrainProcessService(TrainProcessService):
# 4. 生成分块嵌入
logger.info("Step 1.4: Generating chunk embeddings...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
if not super().chunk_embedding():
logger.error("Failed to generate chunk embeddings")
return False
@ -182,113 +206,167 @@ class CloudTrainProcessService(TrainProcessService):
logger.info("Executing life narrative synthesis steps...")
stage_name = "synthesize_your_life_narrative"
stage = self.progress.progress.stage_map.get(stage_name)
total_steps = len(stage["steps"]) if stage else 3
# 1. 提取维度主题
logger.info("Step 2.1: Extracting dimensional topics...")
if not super().extract_dimensional_topics():
logger.error("Failed to extract dimensional topics")
return False
# 检查该阶段是否已完成,如果已完成则跳过
if self.progress.is_stage_completed(stage_name):
logger.info(f"Stage '{stage_name}' already completed, skipping...")
else:
# 1. 提取维度主题
logger.info("Step 2.1: Extracting dimensional topics...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 检查该步骤是否已完成
if self.progress.is_step_completed(stage_name, "extract_dimensional_topics"):
logger.info("Step 'extract_dimensional_topics' already completed, skipping...")
else:
if not super().extract_dimensional_topics():
logger.error("Failed to extract dimensional topics")
return False
# 更新第一步完成后的进度
if stage:
stage["progress"] = 33.0 # 第一步完成进度33%
stage["status"] = CloudStatus.IN_PROGRESS
# 更新步骤状态
if len(stage["steps"]) > 0:
stage["steps"][0]["completed"] = True
stage["steps"][0]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 33% after completing extract_dimensional_topics")
self._update_overall_progress()
# 更新第一步完成后的进度
if stage:
stage["progress"] = 33.0 # 第一步完成进度33%
stage["status"] = CloudStatus.IN_PROGRESS
# 更新步骤状态
if len(stage["steps"]) > 0:
stage["steps"][0]["completed"] = True
stage["steps"][0]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 33% after completing extract_dimensional_topics")
self._update_overall_progress()
# 2. 生成传记
logger.info("Step 2.2: Generating biography...")
if not super().generate_biography():
logger.error("Failed to generate biography")
return False
# 2. 生成传记
logger.info("Step 2.2: Generating biography...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 检查该步骤是否已完成
if self.progress.is_step_completed(stage_name, "generate_biography"):
logger.info("Step 'generate_biography' already completed, skipping...")
else:
if not super().generate_biography():
logger.error("Failed to generate biography")
return False
# 更新第二步完成后的进度
if stage:
stage["progress"] = 66.0 # 第二步完成进度66%
# 更新步骤状态
if len(stage["steps"]) > 1:
stage["steps"][1]["completed"] = True
stage["steps"][1]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 66% after completing generate_biography")
self._update_overall_progress()
# 更新第二步完成后的进度
if stage:
stage["progress"] = 66.0 # 第二步完成进度66%
# 更新步骤状态
if len(stage["steps"]) > 1:
stage["steps"][1]["completed"] = True
stage["steps"][1]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 66% after completing generate_biography")
self._update_overall_progress()
# 3. 映射实体网络
logger.info("Step 2.3: Mapping entity network...")
if not super().map_your_entity_network():
logger.error("Failed to map entity network")
return False
# 3. 映射实体网络
logger.info("Step 2.3: Mapping entity network...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 检查该步骤是否已完成
if self.progress.is_step_completed(stage_name, "map_your_entity_network"):
logger.info("Step 'map_your_entity_network' already completed, skipping...")
else:
if not super().map_your_entity_network():
logger.error("Failed to map entity network")
return False
# 更新第二阶段完成后的进度为100%并标记为已完成
if stage:
stage["progress"] = 100.0 # 全部完成进度100%
stage["status"] = CloudStatus.COMPLETED
# 更新最后一个步骤状态
if len(stage["steps"]) > 2:
stage["steps"][2]["completed"] = True
stage["steps"][2]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 100% and status to COMPLETED")
self._update_overall_progress()
# 更新第二阶段完成后的进度为100%并标记为已完成
if stage:
stage["progress"] = 100.0 # 全部完成进度100%
stage["status"] = CloudStatus.COMPLETED
# 更新最后一个步骤状态
if len(stage["steps"]) > 2:
stage["steps"][2]["completed"] = True
stage["steps"][2]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 100% and status to COMPLETED")
self._update_overall_progress()
# 执行第三阶段的步骤Prepare Training Data for Deep Comprehension
logger.info("Executing training data preparation steps...")
stage_name = "prepare_training_data_for_deep_comprehension"
stage = self.progress.progress.stage_map.get(stage_name)
total_steps = len(stage["steps"]) if stage else 3
# 1. 解码偏好模式
logger.info("Step 3.1: Decoding preference patterns...")
if not super().decode_preference_patterns():
logger.error("Failed to decode preference patterns")
return False
# 检查该阶段是否已完成,如果已完成则跳过
if self.progress.is_stage_completed(stage_name):
logger.info(f"Stage '{stage_name}' already completed, skipping...")
else:
# 1. 解码偏好模式
logger.info("Step 3.1: Decoding preference patterns...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 检查该步骤是否已完成
if self.progress.is_step_completed(stage_name, "decode_preference_patterns"):
logger.info("Step 'decode_preference_patterns' already completed, skipping...")
else:
if not super().decode_preference_patterns():
logger.error("Failed to decode preference patterns")
return False
# 更新第一步完成后的进度
if stage:
stage["progress"] = 33.0 # 第一步完成进度33%
stage["status"] = CloudStatus.IN_PROGRESS
# 更新步骤状态
if len(stage["steps"]) > 0:
stage["steps"][0]["completed"] = True
stage["steps"][0]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 33% after completing decode_preference_patterns")
self._update_overall_progress()
# 更新第一步完成后的进度
if stage:
stage["progress"] = 33.0 # 第一步完成进度33%
stage["status"] = CloudStatus.IN_PROGRESS
# 更新步骤状态
if len(stage["steps"]) > 0:
stage["steps"][0]["completed"] = True
stage["steps"][0]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 33% after completing decode_preference_patterns")
self._update_overall_progress()
# 2. 强化身份
logger.info("Step 3.2: Reinforcing identity...")
if not super().reinforce_identity():
logger.error("Failed to reinforce identity")
return False
# 2. 强化身份
logger.info("Step 3.2: Reinforcing identity...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 检查该步骤是否已完成
if self.progress.is_step_completed(stage_name, "reinforce_identity"):
logger.info("Step 'reinforce_identity' already completed, skipping...")
else:
if not super().reinforce_identity():
logger.error("Failed to reinforce identity")
return False
# 更新第二步完成后的进度
if stage:
stage["progress"] = 66.0 # 第二步完成进度66%
# 更新步骤状态
if len(stage["steps"]) > 1:
stage["steps"][1]["completed"] = True
stage["steps"][1]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 66% after completing reinforce_identity")
self._update_overall_progress()
# 更新第二步完成后的进度
if stage:
stage["progress"] = 66.0 # 第二步完成进度66%
# 更新步骤状态
if len(stage["steps"]) > 1:
stage["steps"][1]["completed"] = True
stage["steps"][1]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 66% after completing reinforce_identity")
self._update_overall_progress()
# 3. 增强内容保留
logger.info("Step 3.3: Augmenting content retention...")
if not super().augment_content_retention():
logger.error("Failed to augment content retention")
return False
# 3. 增强内容保留
logger.info("Step 3.3: Augmenting content retention...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling data preparation")
return False
# 检查该步骤是否已完成
if self.progress.is_step_completed(stage_name, "augment_content_retention"):
logger.info("Step 'augment_content_retention' already completed, skipping...")
else:
if not super().augment_content_retention():
logger.error("Failed to augment content retention")
return False
# 更新第三阶段完成后的进度为100%并标记为已完成
if stage:
stage["progress"] = 100.0 # 全部完成进度100%
stage["status"] = CloudStatus.COMPLETED
# 更新最后一个步骤状态
if len(stage["steps"]) > 2:
stage["steps"][2]["completed"] = True
stage["steps"][2]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 100% and status to COMPLETED")
self._update_overall_progress()
# 更新第三阶段完成后的进度为100%并标记为已完成
if stage:
stage["progress"] = 100.0 # 全部完成进度100%
stage["status"] = CloudStatus.COMPLETED
# 更新最后一个步骤状态
if len(stage["steps"]) > 2:
stage["steps"][2]["completed"] = True
stage["steps"][2]["status"] = CloudStatus.COMPLETED
logger.info(f"Updated {stage_name} progress to 100% and status to COMPLETED")
self._update_overall_progress()
# 计算并更新整体进度
self._update_overall_progress()
@ -304,58 +382,62 @@ class CloudTrainProcessService(TrainProcessService):
if step["name"].lower().replace(" ", "_") == current_stage and step["current_step"]:
stage_name = current_stage
step_name = step["current_step"].lower().replace(" ", "_")
self.progress.mark_step_status(stage_name, step_name, CloudStatus.FAILED, f"Error: {str(e)}")
self.progress.mark_step_status(stage_name, step_name, CloudStatus.FAILED)
break
return False
def get_training_status(self) -> Dict[str, Any]:
"""Get current training status"""
return self.progress.get_progress()
def start_process(self) -> bool:
"""Start the cloud training process using CloudService"""
self.is_stopped = False
self.current_pid = os.getpid()
logger.info(f"Cloud training process started with PID: {self.current_pid}")
logger.info(f"Using base_model: {self.base_model}, training_type: {self.training_type}")
logger.info(f"CloudService initialized with API key: {self.cloud_service.api_key is not None}")
logger.info("Step 1: Preparing training data...")
success = self.prepare_training_data()
logger.info(f"Training data preparation result: {success}")
if not success:
logger.error("Failed to prepare training data")
return False
deploy_success = self.cloud_deploy()
logger.info(f"Cloud deploy result: {deploy_success}")
if not deploy_success:
logger.error("Failed to cloud deploy")
return False
return True
def cloud_deploy(self) -> bool:
try:
self.is_stopped = False
# Store the current process PID
self.current_pid = os.getpid()
logger.info(f"Cloud training process started with PID: {self.current_pid}")
logger.info(f"Using base_model: {self.base_model}, training_type: {self.training_type}")
logger.info(f"CloudService initialized with API key: {self.cloud_service.api_key is not None}")
# 1. 准备训练数据生成L2级别数据
logger.info("Step 1: Preparing training data...")
success = self.prepare_training_data()
logger.info(f"Training data preparation result: {success}")
if not success:
logger.error("Failed to prepare training data")
return False
# 7. 上传训练数据
logger.info("Step 7: Uploading training data...")
# 检查是否已停止
if self.is_stopped:
logger.info("Process has been stopped, cancelling cloud deployment")
return False
self.progress.mark_step_status(CloudProcessStep.UPLOAD_TRAINING_DATA, CloudStatus.IN_PROGRESS)
try:
# 直接使用upload_training_file的默认参数
file_id = self.cloud_service.upload_training_file()
logger.info(f"File upload result: file_id={file_id}")
except Exception as e:
logger.error(f"Exception during file upload: {str(e)}", exc_info=True)
self.progress.mark_step_status(CloudProcessStep.UPLOAD_TRAINING_DATA, CloudStatus.FAILED)
return False
if not file_id:
logger.error("Failed to upload training data")
self.progress.mark_step_status(CloudProcessStep.UPLOAD_TRAINING_DATA, CloudStatus.FAILED)
return False
self.progress.mark_step_status(CloudProcessStep.UPLOAD_TRAINING_DATA, CloudStatus.COMPLETED)
# 8. 创建微调任务
logger.info("Step 8: Creating fine-tune job...")
self.progress.mark_step_status(CloudProcessStep.CREATE_FINE_TUNE_JOB, CloudStatus.IN_PROGRESS)
try:
success_id = self.cloud_service.create_fine_tune_job(
base_model=self.base_model,
@ -364,18 +446,18 @@ class CloudTrainProcessService(TrainProcessService):
)
try:
current_dir = Path(__file__).parent
job_file_path = current_dir / "job_id.json"
job_info = {
"job_id": success_id,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"status": "completed"
}
with open(job_file_path, "w") as f:
json.dump(job_info, f, indent=2)
logger.info(f"Job ID information saved to {job_file_path}")
except Exception as e:
logger.error(f"Failed to write job ID to file: {str(e)}", exc_info=True)
@ -385,24 +467,24 @@ class CloudTrainProcessService(TrainProcessService):
logger.error(f"Exception during fine-tune job creation: {str(e)}", exc_info=True)
self.progress.mark_step_status(CloudProcessStep.CREATE_FINE_TUNE_JOB, CloudStatus.FAILED)
return False
if success_id is None:
logger.error("Failed to create fine-tune job")
self.progress.mark_step_status(CloudProcessStep.CREATE_FINE_TUNE_JOB, CloudStatus.FAILED)
return False
self.job_id = success_id
logger.info(f"Job ID set: {self.job_id}")
# 将job_id保存到进度条中
self.progress.job_id = self.job_id
self.progress.progress.data["job_id"] = self.job_id
self.progress.mark_step_status(CloudProcessStep.CREATE_FINE_TUNE_JOB, CloudStatus.COMPLETED)
# 9. 等待微调完成
logger.info("Step 9: Waiting for fine-tune job to complete...")
self.progress.mark_step_status(CloudProcessStep.WAIT_FOR_FINE_TUNE_COMPLETION, CloudStatus.IN_PROGRESS)
self._wait_for_completion_thread(self.cloud_service, self.job_id)
logger.info("Cloud training process completed successfully")
return True
except Exception as e:
@ -410,7 +492,7 @@ class CloudTrainProcessService(TrainProcessService):
if self.current_step:
self.progress.mark_step_status(self.current_step, CloudStatus.FAILED, f"Error: {str(e)}")
return False
def _wait_for_completion_thread(self, cloud_service, job_id):
try:
logger.info(f"Async thread: waiting for job {job_id} to complete")
@ -459,36 +541,31 @@ class CloudTrainProcessService(TrainProcessService):
logger.error(f"Fine-tuning job failed")
except Exception as e:
logger.error(f"Error in async wait thread: {str(e)}", exc_info=True)
self.progress.mark_step_status("cloud_training", "wait_for_fine-tune_completion", CloudStatus.FAILED, f"Error: {str(e)}")
self.progress.mark_step_status("cloud_training", "wait_for_fine-tune_completion", CloudStatus.FAILED)
def _update_overall_progress(self):
"""Calculate and update the overall progress based on the stages' progress"""
try:
# 获取所有阶段
stages = self.progress.data["stages"]
stages = self.progress.progress.data["stages"]
total_stages = len(stages)
completed_stages = 0
total_progress = 0.0
# 计算已完成的阶段数和总进度
for stage in stages:
total_progress += stage["progress"]
if stage["status"] == CloudStatus.COMPLETED:
completed_stages += 1
# 计算整体进度(所有阶段进度的平均值)
if total_stages > 0:
overall_progress = total_progress / total_stages
else:
overall_progress = 0.0
# 更新整体进度
self.progress.data["overall_progress"] = overall_progress
self.progress.progress.data["overall_progress"] = overall_progress
logger.info(f"Updated overall progress to {overall_progress:.2f}%")
# 如果所有阶段都已完成,将整体状态设置为已完成
if completed_stages == total_stages:
self.progress.data["status"] = CloudStatus.COMPLETED
self.progress.progress.data["status"] = CloudStatus.COMPLETED
logger.info("All stages completed, setting overall status to COMPLETED")
# 保存进度
@ -552,9 +629,6 @@ class CloudTrainProcessService(TrainProcessService):
break
break
# 设置整体消息
self.progress.set_message("Cloud process cancelled by user")
return any_operation_succeeded or not (self.job_id)
except Exception as e:

View file

@ -10,6 +10,7 @@ import time
import json
import tempfile
import threading
from datetime import datetime
from pathlib import Path
from ....configs.config import Config
from .service import CloudService
@ -98,6 +99,36 @@ def list_available_models():
# ============= Cloud Training Process Routes =============
@cloud_bp.route("/train/resume", methods=["POST"])
def resume_cloud_training():
"""Resume cloud training process from the last checkpoint
Request: JSON object, containing:
- model_name: str, optional, the model name to resume training for
- base_model: str, optional, the base model to use for fine-tuning
- training_type: str, optional, the training type to use
- hyper_parameters: dict, optional, hyperparameters for training
If model_name is not provided, the system will attempt to resume the most recent training process.
"""
try:
cloud_train_service = CloudTrainProcessService.get_instance()
if cloud_train_service is None:
logger.warning("No training parameters found in file")
return jsonify(APIResponse.error("No training parameters found. Please use /train/start endpoint for initial training."))
thread = threading.Thread(target=cloud_train_service.start_process)
thread.daemon = True
thread.start()
return jsonify(APIResponse.success("Cloud resume training process started successfully"))
except Exception as e:
logger.error(f"Failed to resume cloud training: {str(e)}", exc_info=True)
return jsonify(APIResponse.error(f"Failed to resume cloud training: {str(e)}"))
@cloud_bp.route("/train/stop", methods=["POST"])
def stop_cloud_training():
"""Stop cloud training process
@ -108,44 +139,19 @@ def stop_cloud_training():
If model_name is not provided, the system will attempt to stop the most recent training process.
"""
try:
data = request.json or {}
model_name = data.get("model_name")
# 如果没有提供model_name尝试从job_id.json文件中获取最近的训练任务
if not model_name:
try:
current_dir = Path(__file__).parent
job_file_path = current_dir / "job_id.json"
if job_file_path.exists():
with open(job_file_path, "r") as f:
job_info = json.load(f)
job_id = job_info.get("job_id")
if job_id:
logger.info(f"Found job_id {job_id} from job_id.json")
# 使用时间戳作为模型名称
model_name = time.strftime("%Y%m%d_%H%M%S")
else:
logger.warning("No job_id.json file found")
except Exception as e:
logger.error(f"Failed to read job ID from file: {str(e)}", exc_info=True)
if not model_name:
return jsonify(APIResponse.error("No model_name provided and no active training job found"))
# 获取CloudTrainProcessService实例
train_service = CloudTrainProcessService.get_instance(current_model_name=model_name)
train_service = CloudTrainProcessService.get_instance()
if not train_service:
return jsonify(APIResponse.error(f"No training service found for model: {model_name}"))
return jsonify(APIResponse.error("No training parameters found. Please use /train/start endpoint for initial training."))
# 停止训练进程
success = train_service.stop_process()
if success:
return jsonify(APIResponse.success(message=f"Cloud training process for model {model_name} stopped successfully"))
return jsonify(APIResponse.success(message=f"Cloud training process stopped successfully"))
else:
return jsonify(APIResponse.error(f"Failed to stop cloud training process for model {model_name}"))
return jsonify(APIResponse.error(f"Failed to stop cloud training process"))
except Exception as e:
logger.error(f"Failed to stop cloud training process: {str(e)}", exc_info=True)
@ -164,6 +170,24 @@ def start_cloud_training():
training_type = data.get("training_type", "efficient_sft")
hyper_parameters = data.get("hyper_parameters", {})
training_params = {
"model_name": model_name,
"base_model": base_model,
"training_type": training_type,
"hyper_parameters": hyper_parameters,
"created_at": datetime.now().isoformat()
}
params_dir = Path("data/cloud_progress")
params_dir.mkdir(parents=True, exist_ok=True)
params_file = params_dir / "cloud_training_params.json"
with open(params_file, "w", encoding="utf-8") as f:
json.dump(training_params, f, indent=2, ensure_ascii=False)
logger.info(f"Training parameters saved to {params_file}")
train_service = CloudTrainProcessService(current_model_name=model_name, base_model=base_model, training_type=training_type, hyper_parameters=hyper_parameters)
def async_train_process():