diff --git a/src/AutoGGUF.py b/src/AutoGGUF.py index 5569570..5fd7312 100644 --- a/src/AutoGGUF.py +++ b/src/AutoGGUF.py @@ -597,7 +597,7 @@ def save_preset(self): "token_embedding_type": self.token_embedding_type.currentText(), "keep_split": self.keep_split.isChecked(), "kv_overrides": [ - entry.get_override_string() for entry in self.kv_override_entries + entry.get_raw_override_string() for entry in self.kv_override_entries ], "extra_arguments": self.extra_arguments.text(), } @@ -795,6 +795,9 @@ def export_lora(self): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") log_file = os.path.join(logs_path, f"lora_export_{timestamp}.log") + command_str = " ".join(command) + self.logger.info(f"{LORA_EXPORT_COMMAND}: {command_str}") + thread = QuantizationThread(command, backend_path, log_file) self.quant_threads.append(thread) @@ -884,6 +887,9 @@ def convert_lora(self): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") log_file = os.path.join(logs_path, f"lora_conversion_{timestamp}.log") + command_str = " ".join(command) + self.logger.info(f"{LORA_CONVERSION_COMMAND}: {command_str}") + thread = QuantizationThread(command, os.getcwd(), log_file) self.quant_threads.append(thread) @@ -1186,15 +1192,24 @@ def delete_task(self, item): QMessageBox.StandardButton.No, ) if reply == QMessageBox.StandardButton.Yes: + # Retrieve the task_item before removing it from the list + task_item = self.task_list.itemWidget(item) + + # Remove the item from the list row = self.task_list.row(item) self.task_list.takeItem(row) + # If the task is still running, terminate it - task_item = self.task_list.itemWidget(item) - for thread in self.quant_threads: - if thread.log_file == task_item.log_file: - thread.terminate() - self.quant_threads.remove(thread) - break + if task_item and task_item.log_file: + for thread in self.quant_threads: + if thread.log_file == task_item.log_file: + thread.terminate() + self.quant_threads.remove(thread) + break + + # Delete the task_item widget + if task_item: + task_item.deleteLater() def create_label(self, text, tooltip): label = QLabel(text) @@ -1369,9 +1384,13 @@ def quantize_model(self): ) if self.keep_split.isChecked(): command.append("--keep-split") - if self.override_kv.text(): + if self.kv_override_entries: for entry in self.kv_override_entries: - override_string = entry.get_override_string() + override_string = entry.get_override_string( + model_name=model_name, + quant_type=quant_type, + output_path=output_path + ) if override_string: command.extend(["--override-kv", override_string]) @@ -1388,6 +1407,10 @@ def quantize_model(self): log_file = os.path.join( logs_path, f"{model_name}_{timestamp}_{quant_type}.log" ) + + # Log quant command + command_str = " ".join(command) + self.logger.info(f"{QUANTIZATION_COMMAND}: {command_str}") thread = QuantizationThread(command, backend_path, log_file) self.quant_threads.append(thread) @@ -1527,6 +1550,10 @@ def generate_imatrix(self): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") log_file = os.path.join(self.logs_input.text(), f"imatrix_{timestamp}.log") + + # Log command + command_str = " ".join(command) + self.logger.info(f"{IMATRIX_GENERATION_COMMAND}: {command_str}") thread = QuantizationThread(command, backend_path, log_file) self.quant_threads.append(thread) @@ -1552,10 +1579,11 @@ def show_error(self, message): self.logger.error(ERROR_MESSAGE.format(message)) QMessageBox.critical(self, ERROR, message) - def handle_error(self, error_message, task_item): + def handle_error(self, error_message, task_item, task_exists=True): self.logger.error(TASK_ERROR.format(error_message)) self.show_error(error_message) - task_item.set_error() + if task_exists: + task_item.set_error() def closeEvent(self, event: QCloseEvent): self.logger.info(APPLICATION_CLOSING) diff --git a/src/KVOverrideEntry.py b/src/KVOverrideEntry.py index 1144236..a382bd5 100644 --- a/src/KVOverrideEntry.py +++ b/src/KVOverrideEntry.py @@ -1,7 +1,11 @@ from PyQt6.QtWidgets import QWidget, QHBoxLayout, QLineEdit, QComboBox, QPushButton from PyQt6.QtCore import pyqtSignal, QRegularExpression from PyQt6.QtGui import QDoubleValidator, QIntValidator, QRegularExpressionValidator - +from datetime import datetime +import time +import os +import socket +import platform class KVOverrideEntry(QWidget): deleted = pyqtSignal(QWidget) @@ -40,8 +44,35 @@ def __init__(self, parent=None): def delete_clicked(self): self.deleted.emit(self) - def get_override_string(self): - return f"{self.key_input.text()}={self.type_combo.currentText()}:{self.value_input.text()}" + def get_override_string(self, model_name=None, quant_type=None, output_path=None): # Add arguments + key = self.key_input.text() + type_ = self.type_combo.currentText() + value = self.value_input.text() + + dynamic_params = { + "{system.time.milliseconds}": lambda: str(int(time.time() * 1000)), + "{system.time.seconds}": lambda: str(int(time.time())), + "{system.date.iso}": lambda: datetime.now().strftime("%Y-%m-%d"), + "{system.datetime.iso}": lambda: datetime.now().isoformat(), + "{system.username}": lambda: os.getlogin(), + "{system.hostname}": lambda: socket.gethostname(), + "{system.platform}": lambda: platform.system(), + "{system.python.version}": lambda: platform.python_version(), + "{system.time.milliseconds}": lambda: str(int(time.time() * 1000)), + "{system.date}": lambda: datetime.now().strftime("%Y-%m-%d"), + "{model.name}": lambda: model_name if model_name is not None else "Unknown Model", + "{quant.type}": lambda: quant_type if quant_type is not None else "Unknown Quant", + "{output.path}": lambda: output_path if output_path is not None else "Unknown Output Path", + } + + for param, func in dynamic_params.items(): + value = value.replace(param, func()) + + return f"{key}={type_}:{value}" + + def get_raw_override_string(self): + # Return the raw override string with placeholders intact + return f"{self.key_input.text()}={self.type_combo.currentText()}:{self.value_input.text()}" def update_validator(self, type_): if type_ == "int": diff --git a/src/localizations.py b/src/localizations.py index e9cb951..6b64b14 100644 --- a/src/localizations.py +++ b/src/localizations.py @@ -225,6 +225,10 @@ def __init__(self): self.GENERATING_IMATRIX_FOR = "" self.MODEL_PATH_REQUIRED_FOR_IMATRIX = "" self.NO_ASSET_SELECTED_FOR_CUDA_CHECK = "" + self.QUANTIZATION_COMMAND = "" + self.IMATRIX_GENERATION_COMMAND = "" + self.LORA_CONVERSION_COMMAND = "" + self.LORA_EXPORT_COMMAND = "" class _English(_Localization): def __init__(self): @@ -452,6 +456,10 @@ def __init__(self): self.GENERATING_IMATRIX_FOR = "Generating IMatrix for {}" self.MODEL_PATH_REQUIRED_FOR_IMATRIX = "Model path is required for IMatrix generation." self.NO_ASSET_SELECTED_FOR_CUDA_CHECK = "No asset selected for CUDA check" + self.QUANTIZATION_COMMAND = "Quantization command" + self.IMATRIX_GENERATION_COMMAND = "IMatrix generation command" + self.LORA_CONVERSION_COMMAND = "LoRA conversion command" + self.LORA_EXPORT_COMMAND = "LoRA export command" class _French: # French localization @@ -5233,7 +5241,8 @@ def set_language(lang_code): global ADDING_LORA_ADAPTER, DELETING_LORA_ADAPTER, LORA_FILES, SELECT_LORA_ADAPTER_FILE, STARTING_LORA_EXPORT global OUTPUT_TYPE, SELECT_OUTPUT_TYPE, GGUF_AND_BIN_FILES, BASE_MODEL, SELECT_BASE_MODEL_FILE global BASE_MODEL_PATH_REQUIRED, BROWSING_FOR_BASE_MODEL_FILE, SELECT_BASE_MODEL_FOLDER, BROWSING_FOR_BASE_MODEL_FOLDER - global LORA_CONVERSION_FROM_TO, GENERATING_IMATRIX_FOR, MODEL_PATH_REQUIRED_FOR_IMATRIX, NO_ASSET_SELECTED_FOR_CUDA_CHECK + global LORA_CONVERSION_FROM_TO, GENERATING_IMATRIX_FOR, MODEL_PATH_REQUIRED_FOR_IMATRIX, NO_ASSET_SELECTED_FOR_CUDA_CHECK, QUANTIZATION_COMMAND + global IMATRIX_GENERATION_COMMAND, LORA_CONVERSION_COMMAND, LORA_EXPORT_COMMAND loc = _languages.get(lang_code, _English)() english_loc = _English() # Create an instance of English localization for fallback