diff --git a/backend/utils/datasets/alpaca_unsloth.json b/backend/assets/datasets/alpaca_unsloth.json similarity index 100% rename from backend/utils/datasets/alpaca_unsloth.json rename to backend/assets/datasets/alpaca_unsloth.json diff --git a/backend/backend/__init__.py b/backend/backend/__init__.py index 55da3dbee7..33a28c425c 100644 --- a/backend/backend/__init__.py +++ b/backend/backend/__init__.py @@ -13,8 +13,8 @@ from .training import TrainingBackend, get_training_backend, create_training_han from .model_config import is_vision_model, ModelConfig, scan_trained_loras # Utilities from .path_utils import normalize_path, is_local_path, is_model_cached -from .utils import without_hf_auth, format_error_message, get_gpu_memory_info, search_hf_models -from .dataset_utils import format_and_template_dataset +from utils.utils import without_hf_auth, format_error_message, get_gpu_memory_info, search_hf_models +from utils.datasets.dataset_utils import format_and_template_dataset __all__ = [ # Inference diff --git a/backend/backend/inference.py b/backend/backend/inference.py index 90ec3f214c..117487cb79 100644 --- a/backend/backend/inference.py +++ b/backend/backend/inference.py @@ -11,7 +11,7 @@ import torch from typing import Optional, Generator, Tuple from .model_config import ModelConfig, get_base_model_from_lora from .path_utils import is_model_cached -from .utils import format_error_message, log_gpu_memory +from utils.utils import format_error_message, log_gpu_memory from io import StringIO import logging @@ -489,7 +489,7 @@ class InferenceBackend: # Step 1: Apply get_chat_template if model is in mapper try: - from backend.dataset_utils import MODEL_TO_TEMPLATE_MAPPER, get_tokenizer_chat_template + from utils.datasets.dataset_utils import MODEL_TO_TEMPLATE_MAPPER, get_tokenizer_chat_template model_name_lower = self.active_model_name.lower() @@ -954,7 +954,7 @@ class InferenceBackend: } try: - from backend.dataset_utils import MODEL_TO_TEMPLATE_MAPPER + from utils.datasets.dataset_utils import MODEL_TO_TEMPLATE_MAPPER #Try exact match first model_name_lower = model_name.lower() if model_name_lower in MODEL_TO_TEMPLATE_MAPPER: diff --git a/backend/backend/model_config.py b/backend/backend/model_config.py index 7beb8ba2a1..85bdd2cf42 100644 --- a/backend/backend/model_config.py +++ b/backend/backend/model_config.py @@ -5,7 +5,7 @@ from transformers import AutoConfig from dataclasses import dataclass from typing import Optional, Dict, Any from .path_utils import normalize_path, is_local_path, is_model_cached -from .utils import without_hf_auth +from utils.utils import without_hf_auth import logging from pathlib import Path from typing import List, Tuple diff --git a/backend/backend/trainer.py b/backend/backend/trainer.py index a135f2a358..ce66e8ea6f 100644 --- a/backend/backend/trainer.py +++ b/backend/backend/trainer.py @@ -20,8 +20,8 @@ from datasets import Dataset, load_dataset # Add the parent directory to sys.path to import unsloth modules #sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from .model_config import is_vision_model -from .dataset_utils import format_and_template_dataset -from .dataset_utils import MODEL_TO_TEMPLATE_MAPPER, TEMPLATE_TO_RESPONSES_MAPPER +from utils.datasets.dataset_utils import format_and_template_dataset +from utils.datasets.dataset_utils import MODEL_TO_TEMPLATE_MAPPER, TEMPLATE_TO_RESPONSES_MAPPER from trl import SFTTrainer, SFTConfig # Import Unsloth trainers @@ -313,15 +313,23 @@ class UnslothTrainer: # Load local datasets all_data = [] for dataset_file in local_datasets: - file_path = os.path.join("datasets", dataset_file) - if file_path.endswith('.json'): + # dataset_file may already be an absolute path from routes/training.py + if os.path.isabs(dataset_file): + file_path = dataset_file + else: + # Fallback: try relative to assets/datasets + script_dir = Path(__file__).parent.parent + assets_datasets_dir = script_dir / "assets" / "datasets" + file_path = assets_datasets_dir / dataset_file + + if str(file_path).endswith('.json'): with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) if isinstance(data, list): all_data.extend(data) else: all_data.append(data) - elif file_path.endswith('.csv'): + elif str(file_path).endswith('.csv'): df = pd.read_csv(file_path) all_data.extend(df.to_dict('records')) diff --git a/backend/backend/training.py b/backend/backend/training.py index ad05c19868..02e96128eb 100644 --- a/backend/backend/training.py +++ b/backend/backend/training.py @@ -66,6 +66,8 @@ class TrainingBackend: weight_decay: float, random_seed: int, packing: bool, + optim: str, + lr_scheduler_type: str, # LoRA parameters use_lora: bool, # Should be derived from training_type @@ -89,9 +91,7 @@ class TrainingBackend: wandb_token: str, wandb_project: str, enable_tensorboard: bool, - tensorboard_dir: str, - optim: str = "adamw_8bit", - lr_scheduler_type: str = "linear") -> Generator[Tuple, None, None]: + tensorboard_dir: str) -> Generator[Tuple, None, None]: """ Start training - yields UI updates as generator. @@ -601,12 +601,13 @@ def create_training_handlers(train_components: Dict[str, Any]) -> Dict[str, Any] hf_dataset, local_datasets, format_type, num_epochs, learning_rate, batch_size, gradient_accumulation_steps, warmup_steps, warmup_ratio, max_steps, save_steps, weight_decay, random_seed, packing, + optim, lr_scheduler_type, use_lora, lora_r, lora_alpha, lora_dropout, target_modules, gradient_checkpointing, use_rslora, use_loftq, train_on_completions, finetune_vision_layers, finetune_language_layers, finetune_attention_modules, finetune_mlp_modules, enable_wandb, wandb_token, wandb_project, - enable_tensorboard, tensorboard_dir, optim, lr_scheduler_type) = args + enable_tensorboard, tensorboard_dir) = args # Start training with correctly named parameters - this is a generator for update_tuple in backend.start_training( @@ -629,6 +630,8 @@ def create_training_handlers(train_components: Dict[str, Any]) -> Dict[str, Any] weight_decay=weight_decay, random_seed=random_seed, packing=packing, + optim=optim, + lr_scheduler_type=lr_scheduler_type, use_lora=use_lora, lora_r=lora_r, lora_alpha=lora_alpha, diff --git a/backend/models/training.py b/backend/models/training.py index 5fa214a3dc..9105eaa7d3 100644 --- a/backend/models/training.py +++ b/backend/models/training.py @@ -31,6 +31,8 @@ class TrainingStartRequest(BaseModel): weight_decay: float = Field(0.01, description="Weight decay") random_seed: int = Field(42, description="Random seed") packing: bool = Field(False, description="Enable sequence packing") + optim: str = Field("adamw_8bit", description="Optimizer") + lr_scheduler_type: str = Field("linear", description="Learning rate scheduler type") # LoRA parameters use_lora: bool = Field(True, description="Use LoRA (derived from training_type)") @@ -55,8 +57,6 @@ class TrainingStartRequest(BaseModel): wandb_project: Optional[str] = Field(None, description="W&B project name") enable_tensorboard: bool = Field(False, description="Enable TensorBoard logging") tensorboard_dir: Optional[str] = Field(None, description="TensorBoard directory") - optim: str = Field("adamw_8bit", description="Optimizer") - lr_scheduler_type: str = Field("linear", description="Learning rate scheduler type") class TrainingStartResponse(BaseModel): diff --git a/backend/routes/models.py b/backend/routes/models.py index be4096b9c4..c58bf0a138 100644 --- a/backend/routes/models.py +++ b/backend/routes/models.py @@ -14,7 +14,7 @@ if str(backend_path) not in sys.path: # Import backend functions try: - from backend.utils import search_hf_models + from utils.utils import search_hf_models from backend.model_config import ( scan_trained_loras, load_model_defaults, @@ -28,7 +28,7 @@ except ImportError: parent_backend = backend_path.parent / "backend" if str(parent_backend) not in sys.path: sys.path.insert(0, str(parent_backend)) - from backend.utils import search_hf_models + from utils.utils import search_hf_models from backend.model_config import ( scan_trained_loras, load_model_defaults, diff --git a/backend/routes/training.py b/backend/routes/training.py index c8839008ba..361e5a7b6b 100644 --- a/backend/routes/training.py +++ b/backend/routes/training.py @@ -73,7 +73,7 @@ async def start_training(request: TrainingStartRequest): validated_datasets = [] # Get the backend directory (where this file is located) backend_dir = Path(__file__).parent.parent - utils_datasets_dir = backend_dir / "utils" / "datasets" + assets_datasets_dir = backend_dir / "assets" / "datasets" for dataset_path in request.local_datasets: dataset_file = Path(dataset_path) @@ -83,11 +83,11 @@ async def start_training(request: TrainingStartRequest): # First try: relative to current working directory candidate = Path.cwd() / dataset_path if not candidate.exists(): - # Second try: relative to utils/datasets folder - candidate = utils_datasets_dir / dataset_path + # Second try: relative to assets/datasets folder + candidate = assets_datasets_dir / dataset_path if not candidate.exists(): - # Third try: just the filename in utils/datasets - candidate = utils_datasets_dir / dataset_file.name + # Third try: just the filename in assets/datasets + candidate = assets_datasets_dir / dataset_file.name dataset_file = candidate if not dataset_file.exists(): @@ -118,6 +118,8 @@ async def start_training(request: TrainingStartRequest): "weight_decay": request.weight_decay, "random_seed": request.random_seed, "packing": request.packing, + "optim": request.optim, + "lr_scheduler_type": request.lr_scheduler_type, "use_lora": request.use_lora, "lora_r": request.lora_r, "lora_alpha": request.lora_alpha, @@ -136,8 +138,6 @@ async def start_training(request: TrainingStartRequest): "wandb_project": request.wandb_project or "", "enable_tensorboard": request.enable_tensorboard, "tensorboard_dir": request.tensorboard_dir or "", - "optim": request.optim, - "lr_scheduler_type": request.lr_scheduler_type, } # Generate job ID diff --git a/backend/backend/dataset_utils.py b/backend/utils/datasets/dataset_utils.py similarity index 100% rename from backend/backend/dataset_utils.py rename to backend/utils/datasets/dataset_utils.py diff --git a/backend/backend/utils.py b/backend/utils/utils.py similarity index 100% rename from backend/backend/utils.py rename to backend/utils/utils.py