add robust logging and localization

This commit is contained in:
leafspark 2024-08-03 17:27:38 -07:00 committed by GitHub
parent baa166c4d6
commit be3e44a6fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 5189 additions and 104 deletions

View File

@ -19,11 +19,16 @@
from TaskListItem import TaskListItem from TaskListItem import TaskListItem
from QuantizationThread import QuantizationThread from QuantizationThread import QuantizationThread
from KVOverrideEntry import KVOverrideEntry from KVOverrideEntry import KVOverrideEntry
from Logger import Logger
from localizations import *
class AutoGGUF(QMainWindow): class AutoGGUF(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setWindowTitle("AutoGGUF (automated GGUF model quantizer)") self.logger = Logger("AutoGGUF", "logs")
self.logger.info(INITIALIZING_AUTOGGUF)
self.setWindowTitle(WINDOW_TITLE)
self.setGeometry(100, 100, 1300, 1100) self.setGeometry(100, 100, 1300, 1100)
main_layout = QHBoxLayout() main_layout = QHBoxLayout()
@ -32,49 +37,49 @@ def __init__(self):
# System info # System info
self.ram_bar = QProgressBar() self.ram_bar = QProgressBar()
self.cpu_label = QLabel("CPU Usage: ") self.cpu_label = QLabel(CPU_USAGE)
left_layout.addWidget(QLabel("RAM Usage:")) left_layout.addWidget(QLabel(RAM_USAGE))
left_layout.addWidget(self.ram_bar) left_layout.addWidget(self.ram_bar)
left_layout.addWidget(self.cpu_label) left_layout.addWidget(self.cpu_label)
# Modify the backend selection # Modify the backend selection
backend_layout = QHBoxLayout() backend_layout = QHBoxLayout()
self.backend_combo = QComboBox() self.backend_combo = QComboBox()
self.refresh_backends_button = QPushButton("Refresh Backends") self.refresh_backends_button = QPushButton(REFRESH_BACKENDS)
self.refresh_backends_button.clicked.connect(self.refresh_backends) self.refresh_backends_button.clicked.connect(self.refresh_backends)
backend_layout.addWidget(QLabel("Llama.cpp Backend:")) backend_layout.addWidget(QLabel(BACKEND))
backend_layout.addWidget(self.backend_combo) backend_layout.addWidget(self.backend_combo)
backend_layout.addWidget(self.refresh_backends_button) backend_layout.addWidget(self.refresh_backends_button)
left_layout.addLayout(backend_layout) left_layout.addLayout(backend_layout)
# Modify the Download llama.cpp section # Modify the Download llama.cpp section
download_group = QGroupBox("Download llama.cpp") download_group = QGroupBox(DOWNLOAD_LLAMACPP)
download_layout = QFormLayout() download_layout = QFormLayout()
self.release_combo = QComboBox() self.release_combo = QComboBox()
self.refresh_releases_button = QPushButton("Refresh Releases") self.refresh_releases_button = QPushButton(REFRESH_RELEASES)
self.refresh_releases_button.clicked.connect(self.refresh_releases) self.refresh_releases_button.clicked.connect(self.refresh_releases)
release_layout = QHBoxLayout() release_layout = QHBoxLayout()
release_layout.addWidget(self.release_combo) release_layout.addWidget(self.release_combo)
release_layout.addWidget(self.refresh_releases_button) release_layout.addWidget(self.refresh_releases_button)
download_layout.addRow("Select Release:", release_layout) download_layout.addRow(SELECT_RELEASE, release_layout)
self.asset_combo = QComboBox() self.asset_combo = QComboBox()
self.asset_combo.currentIndexChanged.connect(self.update_cuda_option) self.asset_combo.currentIndexChanged.connect(self.update_cuda_option)
download_layout.addRow("Select Asset:", self.asset_combo) download_layout.addRow(SELECT_ASSET, self.asset_combo)
self.cuda_extract_checkbox = QCheckBox("Extract CUDA files") self.cuda_extract_checkbox = QCheckBox(EXTRACT_CUDA_FILES)
self.cuda_extract_checkbox.setVisible(False) self.cuda_extract_checkbox.setVisible(False)
download_layout.addRow(self.cuda_extract_checkbox) download_layout.addRow(self.cuda_extract_checkbox)
self.cuda_backend_label = QLabel("Select CUDA Backend:") self.cuda_backend_label = QLabel(SELECT_CUDA_BACKEND)
self.cuda_backend_label.setVisible(False) self.cuda_backend_label.setVisible(False)
self.backend_combo_cuda = QComboBox() self.backend_combo_cuda = QComboBox()
self.backend_combo_cuda.setVisible(False) self.backend_combo_cuda.setVisible(False)
download_layout.addRow(self.cuda_backend_label, self.backend_combo_cuda) download_layout.addRow(self.cuda_backend_label, self.backend_combo_cuda)
self.download_progress = QProgressBar() self.download_progress = QProgressBar()
self.download_button = QPushButton("Download") self.download_button = QPushButton(DOWNLOAD)
self.download_button.clicked.connect(self.download_llama_cpp) self.download_button.clicked.connect(self.download_llama_cpp)
download_layout.addRow(self.download_progress) download_layout.addRow(self.download_progress)
download_layout.addRow(self.download_button) download_layout.addRow(self.download_button)
@ -89,9 +94,9 @@ def __init__(self):
# Models path # Models path
models_layout = QHBoxLayout() models_layout = QHBoxLayout()
self.models_input = QLineEdit(os.path.abspath("models")) self.models_input = QLineEdit(os.path.abspath("models"))
models_button = QPushButton("Browse") models_button = QPushButton(BROWSE)
models_button.clicked.connect(self.browse_models) models_button.clicked.connect(self.browse_models)
models_layout.addWidget(QLabel("Models Path:")) models_layout.addWidget(QLabel(MODELS_PATH))
models_layout.addWidget(self.models_input) models_layout.addWidget(self.models_input)
models_layout.addWidget(models_button) models_layout.addWidget(models_button)
left_layout.addLayout(models_layout) left_layout.addLayout(models_layout)
@ -99,9 +104,9 @@ def __init__(self):
# Output path # Output path
output_layout = QHBoxLayout() output_layout = QHBoxLayout()
self.output_input = QLineEdit(os.path.abspath("quantized_models")) self.output_input = QLineEdit(os.path.abspath("quantized_models"))
output_button = QPushButton("Browse") output_button = QPushButton(BROWSE)
output_button.clicked.connect(self.browse_output) output_button.clicked.connect(self.browse_output)
output_layout.addWidget(QLabel("Output Path:")) output_layout.addWidget(QLabel(OUTPUT_PATH))
output_layout.addWidget(self.output_input) output_layout.addWidget(self.output_input)
output_layout.addWidget(output_button) output_layout.addWidget(output_button)
left_layout.addLayout(output_layout) left_layout.addLayout(output_layout)
@ -109,9 +114,9 @@ def __init__(self):
# Logs path # Logs path
logs_layout = QHBoxLayout() logs_layout = QHBoxLayout()
self.logs_input = QLineEdit(os.path.abspath("logs")) self.logs_input = QLineEdit(os.path.abspath("logs"))
logs_button = QPushButton("Browse") logs_button = QPushButton(BROWSE)
logs_button.clicked.connect(self.browse_logs) logs_button.clicked.connect(self.browse_logs)
logs_layout.addWidget(QLabel("Logs Path:")) logs_layout.addWidget(QLabel(LOGS_PATH))
logs_layout.addWidget(self.logs_input) logs_layout.addWidget(self.logs_input)
logs_layout.addWidget(logs_button) logs_layout.addWidget(logs_button)
left_layout.addLayout(logs_layout) left_layout.addLayout(logs_layout)
@ -119,7 +124,7 @@ def __init__(self):
# Model list # Model list
self.model_list = QListWidget() self.model_list = QListWidget()
self.load_models() self.load_models()
left_layout.addWidget(QLabel("Available Models:")) left_layout.addWidget(QLabel(AVAILABLE_MODELS))
left_layout.addWidget(self.model_list) left_layout.addWidget(self.model_list)
# Quantization options # Quantization options
@ -134,29 +139,29 @@ def __init__(self):
"IQ4_NL", "IQ4_XS", "Q4_K", "Q4_K_S", "Q4_K_M", "Q5_K", "Q5_K_S", "Q5_K_M", "Q6_K", "Q8_0", "IQ4_NL", "IQ4_XS", "Q4_K", "Q4_K_S", "Q4_K_M", "Q5_K", "Q5_K_S", "Q5_K_M", "Q6_K", "Q8_0",
"Q4_0_4_4", "Q4_0_4_8", "Q4_0_8_8", "F16", "BF16", "F32", "COPY" "Q4_0_4_4", "Q4_0_4_8", "Q4_0_8_8", "F16", "BF16", "F32", "COPY"
]) ])
quant_options_layout.addRow(self.create_label("Quantization Type:", "Select the quantization type"), self.quant_type) quant_options_layout.addRow(self.create_label(QUANTIZATION_TYPE, SELECT_QUANTIZATION_TYPE), self.quant_type)
self.allow_requantize = QCheckBox("Allow Requantize") self.allow_requantize = QCheckBox(ALLOW_REQUANTIZE)
self.leave_output_tensor = QCheckBox("Leave Output Tensor") self.leave_output_tensor = QCheckBox(LEAVE_OUTPUT_TENSOR)
self.pure = QCheckBox("Pure") self.pure = QCheckBox(PURE)
quant_options_layout.addRow(self.create_label("", "Allows requantizing tensors that have already been quantized"), self.allow_requantize) quant_options_layout.addRow(self.create_label("", ALLOWS_REQUANTIZING), self.allow_requantize)
quant_options_layout.addRow(self.create_label("", "Will leave output.weight un(re)quantized"), self.leave_output_tensor) quant_options_layout.addRow(self.create_label("", LEAVE_OUTPUT_WEIGHT), self.leave_output_tensor)
quant_options_layout.addRow(self.create_label("", "Disable k-quant mixtures and quantize all tensors to the same type"), self.pure) quant_options_layout.addRow(self.create_label("", DISABLE_K_QUANT_MIXTURES), self.pure)
self.imatrix = QLineEdit() self.imatrix = QLineEdit()
self.imatrix_button = QPushButton("Browse") self.imatrix_button = QPushButton(BROWSE)
self.imatrix_button.clicked.connect(self.browse_imatrix) self.imatrix_button.clicked.connect(self.browse_imatrix)
imatrix_layout = QHBoxLayout() imatrix_layout = QHBoxLayout()
imatrix_layout.addWidget(self.imatrix) imatrix_layout.addWidget(self.imatrix)
imatrix_layout.addWidget(self.imatrix_button) imatrix_layout.addWidget(self.imatrix_button)
quant_options_layout.addRow(self.create_label("IMatrix:", "Use data in file as importance matrix for quant optimizations"), imatrix_layout) quant_options_layout.addRow(self.create_label(IMATRIX, USE_DATA_AS_IMPORTANCE_MATRIX), imatrix_layout)
self.include_weights = QLineEdit() self.include_weights = QLineEdit()
self.exclude_weights = QLineEdit() self.exclude_weights = QLineEdit()
quant_options_layout.addRow(self.create_label("Include Weights:", "Use importance matrix for these tensors"), self.include_weights) quant_options_layout.addRow(self.create_label(INCLUDE_WEIGHTS, USE_IMPORTANCE_MATRIX_FOR_TENSORS), self.include_weights)
quant_options_layout.addRow(self.create_label("Exclude Weights:", "Don't use importance matrix for these tensors"), self.exclude_weights) quant_options_layout.addRow(self.create_label(EXCLUDE_WEIGHTS, DONT_USE_IMPORTANCE_MATRIX_FOR_TENSORS), self.exclude_weights)
self.use_output_tensor_type = QCheckBox("Use Output Tensor Type") self.use_output_tensor_type = QCheckBox(USE_OUTPUT_TENSOR_TYPE)
self.output_tensor_type = QComboBox() self.output_tensor_type = QComboBox()
self.output_tensor_type.addItems(["F32", "F16", "Q4_0", "Q4_1", "Q5_0", "Q5_1", "Q8_0"]) self.output_tensor_type.addItems(["F32", "F16", "Q4_0", "Q4_1", "Q5_0", "Q5_1", "Q8_0"])
self.output_tensor_type.setEnabled(False) self.output_tensor_type.setEnabled(False)
@ -164,9 +169,9 @@ def __init__(self):
output_tensor_layout = QHBoxLayout() output_tensor_layout = QHBoxLayout()
output_tensor_layout.addWidget(self.use_output_tensor_type) output_tensor_layout.addWidget(self.use_output_tensor_type)
output_tensor_layout.addWidget(self.output_tensor_type) output_tensor_layout.addWidget(self.output_tensor_type)
quant_options_layout.addRow(self.create_label("Output Tensor Type:", "Use this type for the output.weight tensor"), output_tensor_layout) quant_options_layout.addRow(self.create_label(OUTPUT_TENSOR_TYPE, USE_THIS_TYPE_FOR_OUTPUT_WEIGHT), output_tensor_layout)
self.use_token_embedding_type = QCheckBox("Use Token Embedding Type") self.use_token_embedding_type = QCheckBox(USE_TOKEN_EMBEDDING_TYPE)
self.token_embedding_type = QComboBox() self.token_embedding_type = QComboBox()
self.token_embedding_type.addItems(["F32", "F16", "Q4_0", "Q4_1", "Q5_0", "Q5_1", "Q8_0"]) self.token_embedding_type.addItems(["F32", "F16", "Q4_0", "Q4_1", "Q5_0", "Q5_1", "Q8_0"])
self.token_embedding_type.setEnabled(False) self.token_embedding_type.setEnabled(False)
@ -174,17 +179,17 @@ def __init__(self):
token_embedding_layout = QHBoxLayout() token_embedding_layout = QHBoxLayout()
token_embedding_layout.addWidget(self.use_token_embedding_type) token_embedding_layout.addWidget(self.use_token_embedding_type)
token_embedding_layout.addWidget(self.token_embedding_type) token_embedding_layout.addWidget(self.token_embedding_type)
quant_options_layout.addRow(self.create_label("Token Embedding Type:", "Use this type for the token embeddings tensor"), token_embedding_layout) quant_options_layout.addRow(self.create_label(TOKEN_EMBEDDING_TYPE, USE_THIS_TYPE_FOR_TOKEN_EMBEDDINGS), token_embedding_layout)
self.keep_split = QCheckBox("Keep Split") self.keep_split = QCheckBox(KEEP_SPLIT)
self.override_kv = QLineEdit() self.override_kv = QLineEdit()
quant_options_layout.addRow(self.create_label("", "Will generate quantized model in the same shards as input"), self.keep_split) quant_options_layout.addRow(self.create_label("", WILL_GENERATE_QUANTIZED_MODEL_IN_SAME_SHARDS), self.keep_split)
# KV Override section # KV Override section
self.kv_override_widget = QWidget() self.kv_override_widget = QWidget()
self.kv_override_layout = QVBoxLayout(self.kv_override_widget) self.kv_override_layout = QVBoxLayout(self.kv_override_widget)
self.kv_override_entries = [] self.kv_override_entries = []
add_override_button = QPushButton("Add new override") add_override_button = QPushButton(ADD_NEW_OVERRIDE)
add_override_button.clicked.connect(self.add_kv_override) add_override_button.clicked.connect(self.add_kv_override)
kv_override_scroll = QScrollArea() kv_override_scroll = QScrollArea()
@ -196,55 +201,63 @@ def __init__(self):
kv_override_main_layout.addWidget(kv_override_scroll) kv_override_main_layout.addWidget(kv_override_scroll)
kv_override_main_layout.addWidget(add_override_button) kv_override_main_layout.addWidget(add_override_button)
quant_options_layout.addRow(self.create_label("KV Overrides:", "Override model metadata"), kv_override_main_layout) quant_options_layout.addRow(self.create_label(KV_OVERRIDES, OVERRIDE_MODEL_METADATA), kv_override_main_layout)
quant_options_widget.setLayout(quant_options_layout) quant_options_widget.setLayout(quant_options_layout)
quant_options_scroll.setWidget(quant_options_widget) quant_options_scroll.setWidget(quant_options_widget)
quant_options_scroll.setWidgetResizable(True) quant_options_scroll.setWidgetResizable(True)
left_layout.addWidget(quant_options_scroll) left_layout.addWidget(quant_options_scroll)
# Quantize button # Quantize button layout
quantize_button = QPushButton("Quantize Selected Model") quantize_layout = QHBoxLayout()
quantize_button = QPushButton(QUANTIZE_MODEL)
quantize_button.clicked.connect(self.quantize_model) quantize_button.clicked.connect(self.quantize_model)
left_layout.addWidget(quantize_button) save_preset_button = QPushButton(SAVE_PRESET)
save_preset_button.clicked.connect(self.save_preset)
load_preset_button = QPushButton(LOAD_PRESET)
load_preset_button.clicked.connect(self.load_preset)
quantize_layout.addWidget(quantize_button)
quantize_layout.addWidget(save_preset_button)
quantize_layout.addWidget(load_preset_button)
left_layout.addLayout(quantize_layout)
# Task list # Task list
self.task_list = QListWidget() self.task_list = QListWidget()
self.task_list.setSelectionMode(QListWidget.SelectionMode.NoSelection) self.task_list.setSelectionMode(QListWidget.SelectionMode.NoSelection)
self.task_list.itemDoubleClicked.connect(self.show_task_details) self.task_list.itemDoubleClicked.connect(self.show_task_details)
left_layout.addWidget(QLabel("Tasks:")) left_layout.addWidget(QLabel(TASKS))
left_layout.addWidget(self.task_list) left_layout.addWidget(self.task_list)
# IMatrix section # IMatrix section
imatrix_group = QGroupBox("IMatrix Generation") imatrix_group = QGroupBox(IMATRIX_GENERATION)
imatrix_layout = QFormLayout() imatrix_layout = QFormLayout()
self.imatrix_datafile = QLineEdit() self.imatrix_datafile = QLineEdit()
self.imatrix_datafile_button = QPushButton("Browse") self.imatrix_datafile_button = QPushButton(BROWSE)
self.imatrix_datafile_button.clicked.connect(self.browse_imatrix_datafile) self.imatrix_datafile_button.clicked.connect(self.browse_imatrix_datafile)
imatrix_datafile_layout = QHBoxLayout() imatrix_datafile_layout = QHBoxLayout()
imatrix_datafile_layout.addWidget(self.imatrix_datafile) imatrix_datafile_layout.addWidget(self.imatrix_datafile)
imatrix_datafile_layout.addWidget(self.imatrix_datafile_button) imatrix_datafile_layout.addWidget(self.imatrix_datafile_button)
imatrix_layout.addRow(self.create_label("Data File:", "Input data file for IMatrix generation"), imatrix_datafile_layout) imatrix_layout.addRow(self.create_label(DATA_FILE, INPUT_DATA_FILE_FOR_IMATRIX), imatrix_datafile_layout)
self.imatrix_model = QLineEdit() self.imatrix_model = QLineEdit()
self.imatrix_model_button = QPushButton("Browse") self.imatrix_model_button = QPushButton(BROWSE)
self.imatrix_model_button.clicked.connect(self.browse_imatrix_model) self.imatrix_model_button.clicked.connect(self.browse_imatrix_model)
imatrix_model_layout = QHBoxLayout() imatrix_model_layout = QHBoxLayout()
imatrix_model_layout.addWidget(self.imatrix_model) imatrix_model_layout.addWidget(self.imatrix_model)
imatrix_model_layout.addWidget(self.imatrix_model_button) imatrix_model_layout.addWidget(self.imatrix_model_button)
imatrix_layout.addRow(self.create_label("Model:", "Model to be quantized"), imatrix_model_layout) imatrix_layout.addRow(self.create_label(MODEL, MODEL_TO_BE_QUANTIZED), imatrix_model_layout)
self.imatrix_output = QLineEdit() self.imatrix_output = QLineEdit()
self.imatrix_output_button = QPushButton("Browse") self.imatrix_output_button = QPushButton(BROWSE)
self.imatrix_output_button.clicked.connect(self.browse_imatrix_output) self.imatrix_output_button.clicked.connect(self.browse_imatrix_output)
imatrix_output_layout = QHBoxLayout() imatrix_output_layout = QHBoxLayout()
imatrix_output_layout.addWidget(self.imatrix_output) imatrix_output_layout.addWidget(self.imatrix_output)
imatrix_output_layout.addWidget(self.imatrix_output_button) imatrix_output_layout.addWidget(self.imatrix_output_button)
imatrix_layout.addRow(self.create_label("Output:", "Output path for the generated IMatrix"), imatrix_output_layout) imatrix_layout.addRow(self.create_label(OUTPUT, OUTPUT_PATH_FOR_GENERATED_IMATRIX), imatrix_output_layout)
self.imatrix_frequency = QLineEdit() self.imatrix_frequency = QLineEdit()
imatrix_layout.addRow(self.create_label("Output Frequency:", "How often to save the IMatrix"), self.imatrix_frequency) imatrix_layout.addRow(self.create_label(OUTPUT_FREQUENCY, HOW_OFTEN_TO_SAVE_IMATRIX), self.imatrix_frequency)
# GPU Offload for IMatrix # GPU Offload for IMatrix
gpu_offload_layout = QHBoxLayout() gpu_offload_layout = QHBoxLayout()
@ -257,15 +270,15 @@ def __init__(self):
self.gpu_offload_spinbox.valueChanged.connect(self.update_gpu_offload_slider) self.gpu_offload_spinbox.valueChanged.connect(self.update_gpu_offload_slider)
self.gpu_offload_spinbox.setMinimumWidth(75) # Set the minimum width to 75 pixels self.gpu_offload_spinbox.setMinimumWidth(75) # Set the minimum width to 75 pixels
self.gpu_offload_auto = QCheckBox("Auto") self.gpu_offload_auto = QCheckBox(AUTO)
self.gpu_offload_auto.stateChanged.connect(self.toggle_gpu_offload_auto) self.gpu_offload_auto.stateChanged.connect(self.toggle_gpu_offload_auto)
gpu_offload_layout.addWidget(self.gpu_offload_slider) gpu_offload_layout.addWidget(self.gpu_offload_slider)
gpu_offload_layout.addWidget(self.gpu_offload_spinbox) gpu_offload_layout.addWidget(self.gpu_offload_spinbox)
gpu_offload_layout.addWidget(self.gpu_offload_auto) gpu_offload_layout.addWidget(self.gpu_offload_auto)
imatrix_layout.addRow(self.create_label("GPU Offload:", "Set GPU offload value (-ngl)"), gpu_offload_layout) imatrix_layout.addRow(self.create_label(GPU_OFFLOAD, SET_GPU_OFFLOAD_VALUE), gpu_offload_layout)
imatrix_generate_button = QPushButton("Generate IMatrix") imatrix_generate_button = QPushButton(GENERATE_IMATRIX)
imatrix_generate_button.clicked.connect(self.generate_imatrix) imatrix_generate_button.clicked.connect(self.generate_imatrix)
imatrix_layout.addRow(imatrix_generate_button) imatrix_layout.addRow(imatrix_generate_button)
@ -288,9 +301,12 @@ def __init__(self):
self.timer.start(200) self.timer.start(200)
# Initialize threads # Initialize threads
self.quant_threads = [] self.quant_threads = []
self.logger.info(AUTOGGUF_INITIALIZATION_COMPLETE)
def refresh_backends(self): def refresh_backends(self):
self.logger.info(REFRESHING_BACKENDS)
llama_bin = os.path.abspath("llama_bin") llama_bin = os.path.abspath("llama_bin")
if not os.path.exists(llama_bin): if not os.path.exists(llama_bin):
os.makedirs(llama_bin) os.makedirs(llama_bin)
@ -307,22 +323,124 @@ def refresh_backends(self):
self.backend_combo.addItem(name, userData=path) self.backend_combo.addItem(name, userData=path)
self.backend_combo.setEnabled(True) # Enable the combo box if there are valid backends self.backend_combo.setEnabled(True) # Enable the combo box if there are valid backends
else: else:
self.backend_combo.addItem("No backends available") self.backend_combo.addItem(NO_BACKENDS_AVAILABLE)
self.backend_combo.setEnabled(False) self.backend_combo.setEnabled(False)
self.logger.info(FOUND_VALID_BACKENDS.format(self.backend_combo.count()))
def save_preset(self):
self.logger.info(SAVING_PRESET)
preset = {
"quant_type": self.quant_type.currentText(),
"allow_requantize": self.allow_requantize.isChecked(),
"leave_output_tensor": self.leave_output_tensor.isChecked(),
"pure": self.pure.isChecked(),
"imatrix": self.imatrix.text(),
"include_weights": self.include_weights.text(),
"exclude_weights": self.exclude_weights.text(),
"use_output_tensor_type": self.use_output_tensor_type.isChecked(),
"output_tensor_type": self.output_tensor_type.currentText(),
"use_token_embedding_type": self.use_token_embedding_type.isChecked(),
"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]
}
file_name, _ = QFileDialog.getSaveFileName(self, SAVE_PRESET, "", JSON_FILES)
if file_name:
with open(file_name, 'w') as f:
json.dump(preset, f, indent=4)
QMessageBox.information(self, PRESET_SAVED, PRESET_SAVED_TO.format(file_name))
self.logger.info(PRESET_SAVED_TO.format(file_name))
def load_preset(self):
self.logger.info(LOADING_PRESET)
file_name, _ = QFileDialog.getOpenFileName(self, LOAD_PRESET, "", JSON_FILES)
if file_name:
try:
with open(file_name, 'r') as f:
preset = json.load(f)
self.quant_type.setCurrentText(preset.get("quant_type", ""))
self.allow_requantize.setChecked(preset.get("allow_requantize", False))
self.leave_output_tensor.setChecked(preset.get("leave_output_tensor", False))
self.pure.setChecked(preset.get("pure", False))
self.imatrix.setText(preset.get("imatrix", ""))
self.include_weights.setText(preset.get("include_weights", ""))
self.exclude_weights.setText(preset.get("exclude_weights", ""))
self.use_output_tensor_type.setChecked(preset.get("use_output_tensor_type", False))
self.output_tensor_type.setCurrentText(preset.get("output_tensor_type", ""))
self.use_token_embedding_type.setChecked(preset.get("use_token_embedding_type", False))
self.token_embedding_type.setCurrentText(preset.get("token_embedding_type", ""))
self.keep_split.setChecked(preset.get("keep_split", False))
# Clear existing KV overrides and add new ones
for entry in self.kv_override_entries:
self.remove_kv_override(entry)
for override in preset.get("kv_overrides", []):
self.add_kv_override(override)
QMessageBox.information(self, PRESET_LOADED, PRESET_LOADED_FROM.format(file_name))
except Exception as e:
QMessageBox.critical(self, ERROR, FAILED_TO_LOAD_PRESET.format(str(e)))
self.logger.info(PRESET_LOADED_FROM.format(file_name))
def add_kv_override(self, override_string=None):
self.logger.debug(ADDING_KV_OVERRIDE.format(override_string))
entry = KVOverrideEntry()
entry.deleted.connect(self.remove_kv_override)
if override_string:
key, value = override_string.split('=')
type_, val = value.split(':')
entry.key_input.setText(key)
entry.type_combo.setCurrentText(type_)
entry.value_input.setText(val)
self.kv_override_layout.addWidget(entry)
self.kv_override_entries.append(entry)
def save_task_preset(self, task_item):
self.logger.info(SAVING_TASK_PRESET.format(task_item.task_name))
for thread in self.quant_threads:
if thread.log_file == task_item.log_file:
preset = {
"command": thread.command,
"backend_path": thread.cwd,
"log_file": thread.log_file
}
file_name, _ = QFileDialog.getSaveFileName(self, SAVE_TASK_PRESET, "", JSON_FILES)
if file_name:
with open(file_name, 'w') as f:
json.dump(preset, f, indent=4)
QMessageBox.information(self, TASK_PRESET_SAVED, TASK_PRESET_SAVED_TO.format(file_name))
break
def restart_task(self, task_item):
self.logger.info(RESTARTING_TASK.format(task_item.task_name))
for thread in self.quant_threads:
if thread.log_file == task_item.log_file:
new_thread = QuantizationThread(thread.command, thread.cwd, thread.log_file)
self.quant_threads.append(new_thread)
new_thread.status_signal.connect(task_item.update_status)
new_thread.finished_signal.connect(lambda: self.task_finished(new_thread))
new_thread.error_signal.connect(lambda err: self.handle_error(err, task_item))
new_thread.model_info_signal.connect(self.update_model_info)
new_thread.start()
task_item.update_status(IN_PROGRESS)
break
def download_finished(self, extract_dir): def download_finished(self, extract_dir):
self.logger.info(DOWNLOAD_FINISHED_EXTRACTED_TO.format(extract_dir))
self.download_button.setEnabled(True) self.download_button.setEnabled(True)
self.download_progress.setValue(100) self.download_progress.setValue(100)
if self.cuda_extract_checkbox.isChecked() and self.cuda_extract_checkbox.isVisible(): if self.cuda_extract_checkbox.isChecked() and self.cuda_extract_checkbox.isVisible():
cuda_backend = self.backend_combo_cuda.currentData() cuda_backend = self.backend_combo_cuda.currentData()
if cuda_backend and cuda_backend != "No suitable CUDA backends found": if cuda_backend and cuda_backend != NO_SUITABLE_CUDA_BACKENDS:
self.extract_cuda_files(extract_dir, cuda_backend) self.extract_cuda_files(extract_dir, cuda_backend)
QMessageBox.information(self, "Download Complete", f"llama.cpp binary downloaded and extracted to {extract_dir}\nCUDA files extracted to {cuda_backend}") QMessageBox.information(self, DOWNLOAD_COMPLETE, LLAMACPP_DOWNLOADED_AND_EXTRACTED.format(extract_dir, cuda_backend))
else: else:
QMessageBox.warning(self, "CUDA Extraction Failed", "No suitable CUDA backend found for extraction") QMessageBox.warning(self, CUDA_EXTRACTION_FAILED, NO_SUITABLE_CUDA_BACKEND_FOUND)
else: else:
QMessageBox.information(self, "Download Complete", f"llama.cpp binary downloaded and extracted to {extract_dir}") QMessageBox.information(self, DOWNLOAD_COMPLETE, LLAMACPP_BINARY_DOWNLOADED_AND_EXTRACTED.format(extract_dir))
self.refresh_backends() # Refresh the backends after successful download self.refresh_backends() # Refresh the backends after successful download
self.update_cuda_option() # Update CUDA options in case a CUDA-capable backend was downloaded self.update_cuda_option() # Update CUDA options in case a CUDA-capable backend was downloaded
@ -334,6 +452,7 @@ def download_finished(self, extract_dir):
self.backend_combo.setCurrentIndex(index) self.backend_combo.setCurrentIndex(index)
def refresh_releases(self): def refresh_releases(self):
self.logger.info(REFRESHING_LLAMACPP_RELEASES)
try: try:
response = requests.get("https://api.github.com/repos/ggerganov/llama.cpp/releases") response = requests.get("https://api.github.com/repos/ggerganov/llama.cpp/releases")
releases = response.json() releases = response.json()
@ -343,9 +462,10 @@ def refresh_releases(self):
self.release_combo.currentIndexChanged.connect(self.update_assets) self.release_combo.currentIndexChanged.connect(self.update_assets)
self.update_assets() self.update_assets()
except Exception as e: except Exception as e:
self.show_error(f"Error fetching releases: {str(e)}") self.show_error(ERROR_FETCHING_RELEASES.format(str(e)))
def update_assets(self): def update_assets(self):
self.logger.debug(UPDATING_ASSET_LIST)
self.asset_combo.clear() self.asset_combo.clear()
release = self.release_combo.currentData() release = self.release_combo.currentData()
if release: if release:
@ -354,6 +474,7 @@ def update_assets(self):
self.update_cuda_option() self.update_cuda_option()
def update_cuda_option(self): def update_cuda_option(self):
self.logger.debug(UPDATING_CUDA_OPTIONS)
asset = self.asset_combo.currentData() asset = self.asset_combo.currentData()
is_cuda = asset and "cudart" in asset['name'].lower() is_cuda = asset and "cudart" in asset['name'].lower()
self.cuda_extract_checkbox.setVisible(is_cuda) self.cuda_extract_checkbox.setVisible(is_cuda)
@ -363,9 +484,10 @@ def update_cuda_option(self):
self.update_cuda_backends() self.update_cuda_backends()
def download_llama_cpp(self): def download_llama_cpp(self):
self.logger.info(STARTING_LLAMACPP_DOWNLOAD)
asset = self.asset_combo.currentData() asset = self.asset_combo.currentData()
if not asset: if not asset:
self.show_error("No asset selected") self.show_error(NO_ASSET_SELECTED)
return return
llama_bin = os.path.abspath("llama_bin") llama_bin = os.path.abspath("llama_bin")
@ -384,6 +506,7 @@ def download_llama_cpp(self):
self.download_progress.setValue(0) self.download_progress.setValue(0)
def update_cuda_backends(self): def update_cuda_backends(self):
self.logger.debug(UPDATING_CUDA_BACKENDS)
self.backend_combo_cuda.clear() self.backend_combo_cuda.clear()
llama_bin = os.path.abspath("llama_bin") llama_bin = os.path.abspath("llama_bin")
if os.path.exists(llama_bin): if os.path.exists(llama_bin):
@ -394,7 +517,7 @@ def update_cuda_backends(self):
self.backend_combo_cuda.addItem(item, userData=item_path) self.backend_combo_cuda.addItem(item, userData=item_path)
if self.backend_combo_cuda.count() == 0: if self.backend_combo_cuda.count() == 0:
self.backend_combo_cuda.addItem("No suitable CUDA backends found") self.backend_combo_cuda.addItem(NO_SUITABLE_CUDA_BACKENDS)
self.backend_combo_cuda.setEnabled(False) self.backend_combo_cuda.setEnabled(False)
else: else:
self.backend_combo_cuda.setEnabled(True) self.backend_combo_cuda.setEnabled(True)
@ -410,15 +533,16 @@ def download_finished(self, extract_dir):
cuda_backend = self.backend_combo_cuda.currentData() cuda_backend = self.backend_combo_cuda.currentData()
if cuda_backend: if cuda_backend:
self.extract_cuda_files(extract_dir, cuda_backend) self.extract_cuda_files(extract_dir, cuda_backend)
QMessageBox.information(self, "Download Complete", f"llama.cpp binary downloaded and extracted to {extract_dir}\nCUDA files extracted to {cuda_backend}") QMessageBox.information(self, DOWNLOAD_COMPLETE, LLAMACPP_DOWNLOADED_AND_EXTRACTED.format(extract_dir, cuda_backend))
else: else:
QMessageBox.warning(self, "CUDA Extraction Failed", "No CUDA backend selected for extraction") QMessageBox.warning(self, CUDA_EXTRACTION_FAILED, NO_CUDA_BACKEND_SELECTED)
else: else:
QMessageBox.information(self, "Download Complete", f"llama.cpp binary downloaded and extracted to {extract_dir}") QMessageBox.information(self, DOWNLOAD_COMPLETE, LLAMACPP_BINARY_DOWNLOADED_AND_EXTRACTED.format(extract_dir))
self.refresh_backends() self.refresh_backends()
def extract_cuda_files(self, extract_dir, destination): def extract_cuda_files(self, extract_dir, destination):
self.logger.info(EXTRACTING_CUDA_FILES.format(extract_dir, destination))
for root, dirs, files in os.walk(extract_dir): for root, dirs, files in os.walk(extract_dir):
for file in files: for file in files:
if file.lower().endswith('.dll'): if file.lower().endswith('.dll'):
@ -428,9 +552,10 @@ def extract_cuda_files(self, extract_dir, destination):
def download_error(self, error_message): def download_error(self, error_message):
self.logger.error(DOWNLOAD_ERROR.format(error_message))
self.download_button.setEnabled(True) self.download_button.setEnabled(True)
self.download_progress.setValue(0) self.download_progress.setValue(0)
self.show_error(f"Download failed: {error_message}") self.show_error(DOWNLOAD_FAILED.format(error_message))
# Clean up any partially downloaded files # Clean up any partially downloaded files
asset = self.asset_combo.currentData() asset = self.asset_combo.currentData()
@ -440,31 +565,38 @@ def download_error(self, error_message):
os.remove(partial_file) os.remove(partial_file)
def show_task_context_menu(self, position): def show_task_context_menu(self, position):
self.logger.debug(SHOWING_TASK_CONTEXT_MENU)
item = self.task_list.itemAt(position) item = self.task_list.itemAt(position)
if item is not None: if item is not None:
context_menu = QMenu(self) context_menu = QMenu(self)
properties_action = QAction("Properties", self) properties_action = QAction(PROPERTIES, self)
properties_action.triggered.connect(lambda: self.show_task_properties(item)) properties_action.triggered.connect(lambda: self.show_task_properties(item))
context_menu.addAction(properties_action) context_menu.addAction(properties_action)
if self.task_list.itemWidget(item).status != "Completed": task_item = self.task_list.itemWidget(item)
cancel_action = QAction("Cancel", self) if task_item.status != COMPLETED:
cancel_action = QAction(CANCEL, self)
cancel_action.triggered.connect(lambda: self.cancel_task(item)) cancel_action.triggered.connect(lambda: self.cancel_task(item))
context_menu.addAction(cancel_action) context_menu.addAction(cancel_action)
if self.task_list.itemWidget(item).status == "Canceled": if task_item.status == CANCELED:
retry_action = QAction("Retry", self) restart_action = QAction(RESTART, self)
retry_action.triggered.connect(lambda: self.retry_task(item)) restart_action.triggered.connect(lambda: self.restart_task(task_item))
context_menu.addAction(retry_action) context_menu.addAction(restart_action)
delete_action = QAction("Delete", self) save_preset_action = QAction(SAVE_PRESET, self)
save_preset_action.triggered.connect(lambda: self.save_task_preset(task_item))
context_menu.addAction(save_preset_action)
delete_action = QAction(DELETE, self)
delete_action.triggered.connect(lambda: self.delete_task(item)) delete_action.triggered.connect(lambda: self.delete_task(item))
context_menu.addAction(delete_action) context_menu.addAction(delete_action)
context_menu.exec(self.task_list.viewport().mapToGlobal(position)) context_menu.exec(self.task_list.viewport().mapToGlobal(position))
def show_task_properties(self, item): def show_task_properties(self, item):
self.logger.debug(SHOWING_PROPERTIES_FOR_TASK.format(item.text()))
task_item = self.task_list.itemWidget(item) task_item = self.task_list.itemWidget(item)
for thread in self.quant_threads: for thread in self.quant_threads:
if thread.log_file == task_item.log_file: if thread.log_file == task_item.log_file:
@ -473,11 +605,12 @@ def show_task_properties(self, item):
break break
def cancel_task(self, item): def cancel_task(self, item):
self.logger.info(CANCELLING_TASK.format(item.text()))
task_item = self.task_list.itemWidget(item) task_item = self.task_list.itemWidget(item)
for thread in self.quant_threads: for thread in self.quant_threads:
if thread.log_file == task_item.log_file: if thread.log_file == task_item.log_file:
thread.terminate() thread.terminate()
task_item.update_status("Canceled") task_item.update_status(CANCELED)
break break
def retry_task(self, item): def retry_task(self, item):
@ -486,8 +619,9 @@ def retry_task(self, item):
pass pass
def delete_task(self, item): def delete_task(self, item):
reply = QMessageBox.question(self, 'Confirm Deletion', self.logger.info(DELETING_TASK.format(item.text()))
"Are you sure you want to delete this task?", reply = QMessageBox.question(self, CONFIRM_DELETION_TITLE,
CONFIRM_DELETION,
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No) QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
@ -507,35 +641,41 @@ def create_label(self, text, tooltip):
return label return label
def load_models(self): def load_models(self):
self.logger.info(LOADING_MODELS)
models_dir = self.models_input.text() models_dir = self.models_input.text()
ensure_directory(models_dir) ensure_directory(models_dir)
self.model_list.clear() self.model_list.clear()
for file in os.listdir(models_dir): for file in os.listdir(models_dir):
if file.endswith(".gguf"): if file.endswith(".gguf"):
self.model_list.addItem(file) self.model_list.addItem(file)
self.logger.info(LOADED_MODELS.format(self.model_list.count()))
def browse_models(self): def browse_models(self):
models_path = QFileDialog.getExistingDirectory(self, "Select Models Directory") self.logger.info(BROWSING_FOR_MODELS_DIRECTORY)
models_path = QFileDialog.getExistingDirectory(self, SELECT_MODELS_DIRECTORY)
if models_path: if models_path:
self.models_input.setText(os.path.abspath(models_path)) self.models_input.setText(os.path.abspath(models_path))
ensure_directory(models_path) ensure_directory(models_path)
self.load_models() self.load_models()
def browse_output(self): def browse_output(self):
output_path = QFileDialog.getExistingDirectory(self, "Select Output Directory") self.logger.info(BROWSING_FOR_OUTPUT_DIRECTORY)
output_path = QFileDialog.getExistingDirectory(self, SELECT_OUTPUT_DIRECTORY)
if output_path: if output_path:
self.output_input.setText(os.path.abspath(output_path)) self.output_input.setText(os.path.abspath(output_path))
ensure_directory(output_path) ensure_directory(output_path)
def browse_logs(self): def browse_logs(self):
logs_path = QFileDialog.getExistingDirectory(self, "Select Logs Directory") self.logger.info(BROWSING_FOR_LOGS_DIRECTORY)
logs_path = QFileDialog.getExistingDirectory(self, SELECT_LOGS_DIRECTORY)
if logs_path: if logs_path:
self.logs_input.setText(os.path.abspath(logs_path)) self.logs_input.setText(os.path.abspath(logs_path))
ensure_directory(logs_path) ensure_directory(logs_path)
def browse_imatrix(self): def browse_imatrix(self):
imatrix_file, _ = QFileDialog.getOpenFileName(self, "Select IMatrix File", "", "DAT Files (*.dat)") self.logger.info(BROWSING_FOR_IMATRIX_FILE)
imatrix_file, _ = QFileDialog.getOpenFileName(self, SELECT_IMATRIX_FILE, "", DAT_FILES)
if imatrix_file: if imatrix_file:
self.imatrix.setText(os.path.abspath(imatrix_file)) self.imatrix.setText(os.path.abspath(imatrix_file))
@ -543,18 +683,25 @@ def update_system_info(self):
ram = psutil.virtual_memory() ram = psutil.virtual_memory()
cpu = psutil.cpu_percent() cpu = psutil.cpu_percent()
self.ram_bar.setValue(int(ram.percent)) self.ram_bar.setValue(int(ram.percent))
self.ram_bar.setFormat(f"{ram.percent:.1f}% ({ram.used // 1024 // 1024} MB / {ram.total // 1024 // 1024} MB)") self.ram_bar.setFormat(RAM_USAGE_FORMAT.format(ram.percent, ram.used // 1024 // 1024, ram.total // 1024 // 1024))
self.cpu_label.setText(f"CPU Usage: {cpu:.1f}%") self.cpu_label.setText(CPU_USAGE_FORMAT.format(cpu))
def validate_quantization_inputs(self): def validate_quantization_inputs(self):
self.logger.debug(VALIDATING_QUANTIZATION_INPUTS)
errors = []
if not self.backend_combo.currentData(): if not self.backend_combo.currentData():
raise ValueError("No backend selected") errors.append(NO_BACKEND_SELECTED)
if not self.models_input.text(): if not self.models_input.text():
raise ValueError("Models path is required") errors.append(MODELS_PATH_REQUIRED)
if not self.output_input.text(): if not self.output_input.text():
raise ValueError("Output path is required") errors.append(OUTPUT_PATH_REQUIRED)
if not self.logs_input.text(): if not self.logs_input.text():
raise ValueError("Logs path is required") errors.append(LOGS_PATH_REQUIRED)
if not self.model_list.currentItem():
errors.append(NO_MODEL_SELECTED)
if errors:
raise ValueError("\n".join(errors))
def add_kv_override(self): def add_kv_override(self):
entry = KVOverrideEntry() entry = KVOverrideEntry()
@ -568,16 +715,17 @@ def remove_kv_override(self, entry):
entry.deleteLater() entry.deleteLater()
def quantize_model(self): def quantize_model(self):
self.logger.info(STARTING_MODEL_QUANTIZATION)
try: try:
self.validate_quantization_inputs() self.validate_quantization_inputs()
selected_model = self.model_list.currentItem() selected_model = self.model_list.currentItem()
if not selected_model: if not selected_model:
raise ValueError("No model selected") raise ValueError(NO_MODEL_SELECTED)
model_name = selected_model.text() model_name = selected_model.text()
backend_path = self.backend_combo.currentData() backend_path = self.backend_combo.currentData()
if not backend_path: if not backend_path:
raise ValueError("No backend selected") raise ValueError(NO_BACKEND_SELECTED)
quant_type = self.quant_type.currentText() quant_type = self.quant_type.currentText()
input_path = os.path.join(self.models_input.text(), model_name) input_path = os.path.join(self.models_input.text(), model_name)
@ -585,7 +733,7 @@ def quantize_model(self):
output_path = os.path.join(self.output_input.text(), output_name) output_path = os.path.join(self.output_input.text(), output_name)
if not os.path.exists(input_path): if not os.path.exists(input_path):
raise FileNotFoundError(f"Input file '{input_path}' does not exist.") raise FileNotFoundError(INPUT_FILE_NOT_EXIST.format(input_path))
command = [os.path.join(backend_path, "llama-quantize")] command = [os.path.join(backend_path, "llama-quantize")]
@ -624,7 +772,7 @@ def quantize_model(self):
thread = QuantizationThread(command, backend_path, log_file) thread = QuantizationThread(command, backend_path, log_file)
self.quant_threads.append(thread) self.quant_threads.append(thread)
task_item = TaskListItem(f"Quantizing {model_name} to {quant_type}", log_file) task_item = TaskListItem(QUANTIZING_MODEL_TO.format(model_name, quant_type), log_file)
list_item = QListWidgetItem(self.task_list) list_item = QListWidgetItem(self.task_list)
list_item.setSizeHint(task_item.sizeHint()) list_item.setSizeHint(task_item.sizeHint())
self.task_list.addItem(list_item) self.task_list.addItem(list_item)
@ -635,22 +783,28 @@ def quantize_model(self):
thread.error_signal.connect(lambda err: self.handle_error(err, task_item)) thread.error_signal.connect(lambda err: self.handle_error(err, task_item))
thread.model_info_signal.connect(self.update_model_info) thread.model_info_signal.connect(self.update_model_info)
thread.start() thread.start()
self.logger.info(QUANTIZATION_TASK_STARTED.format(model_name))
except ValueError as e:
self.show_error(str(e))
except Exception as e: except Exception as e:
self.show_error(f"Error starting quantization: {str(e)}") self.show_error(ERROR_STARTING_QUANTIZATION.format(str(e)))
def update_model_info(self, model_info): def update_model_info(self, model_info):
self.logger.debug(UPDATING_MODEL_INFO.format(model_info))
# TODO: Do something with this # TODO: Do something with this
pass pass
def task_finished(self, thread): def task_finished(self, thread):
self.logger.info(TASK_FINISHED.format(thread.log_file))
if thread in self.quant_threads: if thread in self.quant_threads:
self.quant_threads.remove(thread) self.quant_threads.remove(thread)
def show_task_details(self, item): def show_task_details(self, item):
self.logger.debug(SHOWING_TASK_DETAILS_FOR.format(item.text()))
task_item = self.task_list.itemWidget(item) task_item = self.task_list.itemWidget(item)
if task_item: if task_item:
log_dialog = QDialog(self) log_dialog = QDialog(self)
log_dialog.setWindowTitle(f"Log for {task_item.task_name}") log_dialog.setWindowTitle(LOG_FOR.format(task_item.task_name))
log_dialog.setGeometry(200, 200, 800, 600) log_dialog.setGeometry(200, 200, 800, 600)
log_text = QPlainTextEdit() log_text = QPlainTextEdit()
@ -674,17 +828,20 @@ def show_task_details(self, item):
log_dialog.exec() log_dialog.exec()
def browse_imatrix_datafile(self): def browse_imatrix_datafile(self):
datafile, _ = QFileDialog.getOpenFileName(self, "Select Data File", "", "All Files (*)") self.logger.info(BROWSING_FOR_IMATRIX_DATA_FILE)
datafile, _ = QFileDialog.getOpenFileName(self, SELECT_DATA_FILE, "", ALL_FILES)
if datafile: if datafile:
self.imatrix_datafile.setText(os.path.abspath(datafile)) self.imatrix_datafile.setText(os.path.abspath(datafile))
def browse_imatrix_model(self): def browse_imatrix_model(self):
model_file, _ = QFileDialog.getOpenFileName(self, "Select Model File", "", "GGUF Files (*.gguf)") self.logger.info(BROWSING_FOR_IMATRIX_MODEL_FILE)
model_file, _ = QFileDialog.getOpenFileName(self, SELECT_MODEL_FILE, "", GGUF_FILES)
if model_file: if model_file:
self.imatrix_model.setText(os.path.abspath(model_file)) self.imatrix_model.setText(os.path.abspath(model_file))
def browse_imatrix_output(self): def browse_imatrix_output(self):
output_file, _ = QFileDialog.getSaveFileName(self, "Select Output File", "", "DAT Files (*.dat)") self.logger.info(BROWSING_FOR_IMATRIX_OUTPUT_FILE)
output_file, _ = QFileDialog.getSaveFileName(self, SELECT_OUTPUT_FILE, "", DAT_FILES)
if output_file: if output_file:
self.imatrix_output.setText(os.path.abspath(output_file)) self.imatrix_output.setText(os.path.abspath(output_file))
@ -700,10 +857,11 @@ def toggle_gpu_offload_auto(self, state):
self.gpu_offload_spinbox.setEnabled(not is_auto) self.gpu_offload_spinbox.setEnabled(not is_auto)
def generate_imatrix(self): def generate_imatrix(self):
self.logger.info(STARTING_IMATRIX_GENERATION)
try: try:
backend_path = self.backend_combo.currentData() backend_path = self.backend_combo.currentData()
if not os.path.exists(backend_path): if not os.path.exists(backend_path):
raise FileNotFoundError(f"Backend path does not exist: {backend_path}") raise FileNotFoundError(BACKEND_PATH_NOT_EXIST.format(backend_path))
command = [ command = [
os.path.join(backend_path, "llama-imatrix"), os.path.join(backend_path, "llama-imatrix"),
@ -724,7 +882,7 @@ def generate_imatrix(self):
thread = QuantizationThread(command, backend_path, log_file) thread = QuantizationThread(command, backend_path, log_file)
self.quant_threads.append(thread) self.quant_threads.append(thread)
task_item = TaskListItem("Generating IMatrix", log_file) task_item = TaskListItem(GENERATING_IMATRIX, log_file)
list_item = QListWidgetItem(self.task_list) list_item = QListWidgetItem(self.task_list)
list_item.setSizeHint(task_item.sizeHint()) list_item.setSizeHint(task_item.sizeHint())
self.task_list.addItem(list_item) self.task_list.addItem(list_item)
@ -735,19 +893,23 @@ def generate_imatrix(self):
thread.error_signal.connect(lambda err: self.handle_error(err, task_item)) thread.error_signal.connect(lambda err: self.handle_error(err, task_item))
thread.start() thread.start()
except Exception as e: except Exception as e:
self.show_error(f"Error starting IMatrix generation: {str(e)}") self.show_error(ERROR_STARTING_IMATRIX_GENERATION.format(str(e)))
self.logger.info(IMATRIX_GENERATION_TASK_STARTED)
def show_error(self, message): def show_error(self, message):
QMessageBox.critical(self, "Error", 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):
self.logger.error(TASK_ERROR.format(error_message))
self.show_error(error_message) self.show_error(error_message)
task_item.set_error() task_item.set_error()
def closeEvent(self, event: QCloseEvent): def closeEvent(self, event: QCloseEvent):
self.logger.info(APPLICATION_CLOSING)
if self.quant_threads: if self.quant_threads:
reply = QMessageBox.question(self, 'Warning', reply = QMessageBox.question(self, WARNING,
"Some tasks are still running. Are you sure you want to quit?", TASK_RUNNING_WARNING,
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No) QMessageBox.StandardButton.No)
@ -759,9 +921,10 @@ def closeEvent(self, event: QCloseEvent):
event.ignore() event.ignore()
else: else:
event.accept() event.accept()
self.logger.info(APPLICATION_CLOSED)
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
window = AutoGGUF() window = AutoGGUF()
window.show() window.show()
sys.exit(app.exec()) sys.exit(app.exec())

46
src/Logger.py Normal file
View File

@ -0,0 +1,46 @@
import logging
from logging.handlers import RotatingFileHandler
import os
import sys
from datetime import datetime
class Logger:
def __init__(self, name, log_dir):
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.DEBUG)
# Create logs directory if it doesn't exist
os.makedirs(log_dir, exist_ok=True)
# Console handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_format = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_format)
# File handler
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = os.path.join(log_dir, f"latest_{timestamp}.log")
file_handler = RotatingFileHandler(log_file, maxBytes=10*1024*1024, backupCount=5, encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_format = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s')
file_handler.setFormatter(file_format)
# Add handlers to logger
self.logger.addHandler(console_handler)
self.logger.addHandler(file_handler)
def debug(self, message):
self.logger.debug(message)
def info(self, message):
self.logger.info(message)
def warning(self, message):
self.logger.warning(message)
def error(self, message):
self.logger.error(message)
def critical(self, message):
self.logger.critical(message)

4876
src/localizations.py Normal file

File diff suppressed because it is too large Load Diff