mirror of https://github.com/leafspark/AutoGGUF
add robust logging and localization
This commit is contained in:
parent
baa166c4d6
commit
be3e44a6fa
371
src/AutoGGUF.py
371
src/AutoGGUF.py
|
@ -19,11 +19,16 @@
|
|||
from TaskListItem import TaskListItem
|
||||
from QuantizationThread import QuantizationThread
|
||||
from KVOverrideEntry import KVOverrideEntry
|
||||
from Logger import Logger
|
||||
from localizations import *
|
||||
|
||||
class AutoGGUF(QMainWindow):
|
||||
def __init__(self):
|
||||
def __init__(self):
|
||||
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)
|
||||
|
||||
main_layout = QHBoxLayout()
|
||||
|
@ -32,49 +37,49 @@ def __init__(self):
|
|||
|
||||
# System info
|
||||
self.ram_bar = QProgressBar()
|
||||
self.cpu_label = QLabel("CPU Usage: ")
|
||||
left_layout.addWidget(QLabel("RAM Usage:"))
|
||||
self.cpu_label = QLabel(CPU_USAGE)
|
||||
left_layout.addWidget(QLabel(RAM_USAGE))
|
||||
left_layout.addWidget(self.ram_bar)
|
||||
left_layout.addWidget(self.cpu_label)
|
||||
|
||||
# Modify the backend selection
|
||||
backend_layout = QHBoxLayout()
|
||||
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)
|
||||
backend_layout.addWidget(QLabel("Llama.cpp Backend:"))
|
||||
backend_layout.addWidget(QLabel(BACKEND))
|
||||
backend_layout.addWidget(self.backend_combo)
|
||||
backend_layout.addWidget(self.refresh_backends_button)
|
||||
left_layout.addLayout(backend_layout)
|
||||
|
||||
# Modify the Download llama.cpp section
|
||||
download_group = QGroupBox("Download llama.cpp")
|
||||
download_group = QGroupBox(DOWNLOAD_LLAMACPP)
|
||||
download_layout = QFormLayout()
|
||||
|
||||
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)
|
||||
release_layout = QHBoxLayout()
|
||||
release_layout.addWidget(self.release_combo)
|
||||
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.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)
|
||||
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.backend_combo_cuda = QComboBox()
|
||||
self.backend_combo_cuda.setVisible(False)
|
||||
download_layout.addRow(self.cuda_backend_label, self.backend_combo_cuda)
|
||||
|
||||
self.download_progress = QProgressBar()
|
||||
self.download_button = QPushButton("Download")
|
||||
self.download_button = QPushButton(DOWNLOAD)
|
||||
self.download_button.clicked.connect(self.download_llama_cpp)
|
||||
download_layout.addRow(self.download_progress)
|
||||
download_layout.addRow(self.download_button)
|
||||
|
@ -89,9 +94,9 @@ def __init__(self):
|
|||
# Models path
|
||||
models_layout = QHBoxLayout()
|
||||
self.models_input = QLineEdit(os.path.abspath("models"))
|
||||
models_button = QPushButton("Browse")
|
||||
models_button = QPushButton(BROWSE)
|
||||
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(models_button)
|
||||
left_layout.addLayout(models_layout)
|
||||
|
@ -99,9 +104,9 @@ def __init__(self):
|
|||
# Output path
|
||||
output_layout = QHBoxLayout()
|
||||
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_layout.addWidget(QLabel("Output Path:"))
|
||||
output_layout.addWidget(QLabel(OUTPUT_PATH))
|
||||
output_layout.addWidget(self.output_input)
|
||||
output_layout.addWidget(output_button)
|
||||
left_layout.addLayout(output_layout)
|
||||
|
@ -109,9 +114,9 @@ def __init__(self):
|
|||
# Logs path
|
||||
logs_layout = QHBoxLayout()
|
||||
self.logs_input = QLineEdit(os.path.abspath("logs"))
|
||||
logs_button = QPushButton("Browse")
|
||||
logs_button = QPushButton(BROWSE)
|
||||
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(logs_button)
|
||||
left_layout.addLayout(logs_layout)
|
||||
|
@ -119,7 +124,7 @@ def __init__(self):
|
|||
# Model list
|
||||
self.model_list = QListWidget()
|
||||
self.load_models()
|
||||
left_layout.addWidget(QLabel("Available Models:"))
|
||||
left_layout.addWidget(QLabel(AVAILABLE_MODELS))
|
||||
left_layout.addWidget(self.model_list)
|
||||
|
||||
# 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",
|
||||
"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.leave_output_tensor = QCheckBox("Leave Output Tensor")
|
||||
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("", "Will leave output.weight un(re)quantized"), 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)
|
||||
self.allow_requantize = QCheckBox(ALLOW_REQUANTIZE)
|
||||
self.leave_output_tensor = QCheckBox(LEAVE_OUTPUT_TENSOR)
|
||||
self.pure = QCheckBox(PURE)
|
||||
quant_options_layout.addRow(self.create_label("", ALLOWS_REQUANTIZING), self.allow_requantize)
|
||||
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), self.pure)
|
||||
|
||||
self.imatrix = QLineEdit()
|
||||
self.imatrix_button = QPushButton("Browse")
|
||||
self.imatrix_button = QPushButton(BROWSE)
|
||||
self.imatrix_button.clicked.connect(self.browse_imatrix)
|
||||
imatrix_layout = QHBoxLayout()
|
||||
imatrix_layout.addWidget(self.imatrix)
|
||||
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.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("Exclude Weights:", "Don't use importance matrix for these tensors"), self.exclude_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, 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.addItems(["F32", "F16", "Q4_0", "Q4_1", "Q5_0", "Q5_1", "Q8_0"])
|
||||
self.output_tensor_type.setEnabled(False)
|
||||
|
@ -164,9 +169,9 @@ def __init__(self):
|
|||
output_tensor_layout = QHBoxLayout()
|
||||
output_tensor_layout.addWidget(self.use_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.addItems(["F32", "F16", "Q4_0", "Q4_1", "Q5_0", "Q5_1", "Q8_0"])
|
||||
self.token_embedding_type.setEnabled(False)
|
||||
|
@ -174,17 +179,17 @@ def __init__(self):
|
|||
token_embedding_layout = QHBoxLayout()
|
||||
token_embedding_layout.addWidget(self.use_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()
|
||||
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
|
||||
self.kv_override_widget = QWidget()
|
||||
self.kv_override_layout = QVBoxLayout(self.kv_override_widget)
|
||||
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)
|
||||
|
||||
kv_override_scroll = QScrollArea()
|
||||
|
@ -196,55 +201,63 @@ def __init__(self):
|
|||
kv_override_main_layout.addWidget(kv_override_scroll)
|
||||
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_scroll.setWidget(quant_options_widget)
|
||||
quant_options_scroll.setWidgetResizable(True)
|
||||
left_layout.addWidget(quant_options_scroll)
|
||||
|
||||
# Quantize button
|
||||
quantize_button = QPushButton("Quantize Selected Model")
|
||||
# Quantize button layout
|
||||
quantize_layout = QHBoxLayout()
|
||||
quantize_button = QPushButton(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
|
||||
self.task_list = QListWidget()
|
||||
self.task_list.setSelectionMode(QListWidget.SelectionMode.NoSelection)
|
||||
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)
|
||||
|
||||
# IMatrix section
|
||||
imatrix_group = QGroupBox("IMatrix Generation")
|
||||
imatrix_group = QGroupBox(IMATRIX_GENERATION)
|
||||
imatrix_layout = QFormLayout()
|
||||
|
||||
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)
|
||||
imatrix_datafile_layout = QHBoxLayout()
|
||||
imatrix_datafile_layout.addWidget(self.imatrix_datafile)
|
||||
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_button = QPushButton("Browse")
|
||||
self.imatrix_model_button = QPushButton(BROWSE)
|
||||
self.imatrix_model_button.clicked.connect(self.browse_imatrix_model)
|
||||
imatrix_model_layout = QHBoxLayout()
|
||||
imatrix_model_layout.addWidget(self.imatrix_model)
|
||||
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_button = QPushButton("Browse")
|
||||
self.imatrix_output_button = QPushButton(BROWSE)
|
||||
self.imatrix_output_button.clicked.connect(self.browse_imatrix_output)
|
||||
imatrix_output_layout = QHBoxLayout()
|
||||
imatrix_output_layout.addWidget(self.imatrix_output)
|
||||
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()
|
||||
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_layout = QHBoxLayout()
|
||||
|
@ -257,15 +270,15 @@ def __init__(self):
|
|||
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_auto = QCheckBox("Auto")
|
||||
self.gpu_offload_auto = QCheckBox(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_spinbox)
|
||||
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_layout.addRow(imatrix_generate_button)
|
||||
|
||||
|
@ -288,9 +301,12 @@ def __init__(self):
|
|||
self.timer.start(200)
|
||||
|
||||
# Initialize threads
|
||||
self.quant_threads = []
|
||||
self.quant_threads = []
|
||||
|
||||
self.logger.info(AUTOGGUF_INITIALIZATION_COMPLETE)
|
||||
|
||||
def refresh_backends(self):
|
||||
self.logger.info(REFRESHING_BACKENDS)
|
||||
llama_bin = os.path.abspath("llama_bin")
|
||||
if not os.path.exists(llama_bin):
|
||||
os.makedirs(llama_bin)
|
||||
|
@ -307,22 +323,124 @@ def refresh_backends(self):
|
|||
self.backend_combo.addItem(name, userData=path)
|
||||
self.backend_combo.setEnabled(True) # Enable the combo box if there are valid backends
|
||||
else:
|
||||
self.backend_combo.addItem("No backends available")
|
||||
self.backend_combo.addItem(NO_BACKENDS_AVAILABLE)
|
||||
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):
|
||||
self.logger.info(DOWNLOAD_FINISHED_EXTRACTED_TO.format(extract_dir))
|
||||
self.download_button.setEnabled(True)
|
||||
self.download_progress.setValue(100)
|
||||
|
||||
if self.cuda_extract_checkbox.isChecked() and self.cuda_extract_checkbox.isVisible():
|
||||
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)
|
||||
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:
|
||||
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:
|
||||
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.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)
|
||||
|
||||
def refresh_releases(self):
|
||||
self.logger.info(REFRESHING_LLAMACPP_RELEASES)
|
||||
try:
|
||||
response = requests.get("https://api.github.com/repos/ggerganov/llama.cpp/releases")
|
||||
releases = response.json()
|
||||
|
@ -343,9 +462,10 @@ def refresh_releases(self):
|
|||
self.release_combo.currentIndexChanged.connect(self.update_assets)
|
||||
self.update_assets()
|
||||
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):
|
||||
self.logger.debug(UPDATING_ASSET_LIST)
|
||||
self.asset_combo.clear()
|
||||
release = self.release_combo.currentData()
|
||||
if release:
|
||||
|
@ -354,6 +474,7 @@ def update_assets(self):
|
|||
self.update_cuda_option()
|
||||
|
||||
def update_cuda_option(self):
|
||||
self.logger.debug(UPDATING_CUDA_OPTIONS)
|
||||
asset = self.asset_combo.currentData()
|
||||
is_cuda = asset and "cudart" in asset['name'].lower()
|
||||
self.cuda_extract_checkbox.setVisible(is_cuda)
|
||||
|
@ -363,9 +484,10 @@ def update_cuda_option(self):
|
|||
self.update_cuda_backends()
|
||||
|
||||
def download_llama_cpp(self):
|
||||
self.logger.info(STARTING_LLAMACPP_DOWNLOAD)
|
||||
asset = self.asset_combo.currentData()
|
||||
if not asset:
|
||||
self.show_error("No asset selected")
|
||||
self.show_error(NO_ASSET_SELECTED)
|
||||
return
|
||||
|
||||
llama_bin = os.path.abspath("llama_bin")
|
||||
|
@ -384,6 +506,7 @@ def download_llama_cpp(self):
|
|||
self.download_progress.setValue(0)
|
||||
|
||||
def update_cuda_backends(self):
|
||||
self.logger.debug(UPDATING_CUDA_BACKENDS)
|
||||
self.backend_combo_cuda.clear()
|
||||
llama_bin = os.path.abspath("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)
|
||||
|
||||
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)
|
||||
else:
|
||||
self.backend_combo_cuda.setEnabled(True)
|
||||
|
@ -410,15 +533,16 @@ def download_finished(self, extract_dir):
|
|||
cuda_backend = self.backend_combo_cuda.currentData()
|
||||
if 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:
|
||||
QMessageBox.warning(self, "CUDA Extraction Failed", "No CUDA backend selected for extraction")
|
||||
QMessageBox.warning(self, CUDA_EXTRACTION_FAILED, NO_CUDA_BACKEND_SELECTED)
|
||||
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()
|
||||
|
||||
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 file in files:
|
||||
if file.lower().endswith('.dll'):
|
||||
|
@ -428,9 +552,10 @@ def extract_cuda_files(self, extract_dir, destination):
|
|||
|
||||
|
||||
def download_error(self, error_message):
|
||||
self.logger.error(DOWNLOAD_ERROR.format(error_message))
|
||||
self.download_button.setEnabled(True)
|
||||
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
|
||||
asset = self.asset_combo.currentData()
|
||||
|
@ -440,31 +565,38 @@ def download_error(self, error_message):
|
|||
os.remove(partial_file)
|
||||
|
||||
def show_task_context_menu(self, position):
|
||||
self.logger.debug(SHOWING_TASK_CONTEXT_MENU)
|
||||
item = self.task_list.itemAt(position)
|
||||
if item is not None:
|
||||
context_menu = QMenu(self)
|
||||
|
||||
properties_action = QAction("Properties", self)
|
||||
properties_action = QAction(PROPERTIES, self)
|
||||
properties_action.triggered.connect(lambda: self.show_task_properties(item))
|
||||
context_menu.addAction(properties_action)
|
||||
|
||||
if self.task_list.itemWidget(item).status != "Completed":
|
||||
cancel_action = QAction("Cancel", self)
|
||||
task_item = self.task_list.itemWidget(item)
|
||||
if task_item.status != COMPLETED:
|
||||
cancel_action = QAction(CANCEL, self)
|
||||
cancel_action.triggered.connect(lambda: self.cancel_task(item))
|
||||
context_menu.addAction(cancel_action)
|
||||
|
||||
if self.task_list.itemWidget(item).status == "Canceled":
|
||||
retry_action = QAction("Retry", self)
|
||||
retry_action.triggered.connect(lambda: self.retry_task(item))
|
||||
context_menu.addAction(retry_action)
|
||||
if task_item.status == CANCELED:
|
||||
restart_action = QAction(RESTART, self)
|
||||
restart_action.triggered.connect(lambda: self.restart_task(task_item))
|
||||
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))
|
||||
context_menu.addAction(delete_action)
|
||||
|
||||
context_menu.exec(self.task_list.viewport().mapToGlobal(position))
|
||||
|
||||
def show_task_properties(self, item):
|
||||
self.logger.debug(SHOWING_PROPERTIES_FOR_TASK.format(item.text()))
|
||||
task_item = self.task_list.itemWidget(item)
|
||||
for thread in self.quant_threads:
|
||||
if thread.log_file == task_item.log_file:
|
||||
|
@ -473,11 +605,12 @@ def show_task_properties(self, item):
|
|||
break
|
||||
|
||||
def cancel_task(self, item):
|
||||
self.logger.info(CANCELLING_TASK.format(item.text()))
|
||||
task_item = self.task_list.itemWidget(item)
|
||||
for thread in self.quant_threads:
|
||||
if thread.log_file == task_item.log_file:
|
||||
thread.terminate()
|
||||
task_item.update_status("Canceled")
|
||||
task_item.update_status(CANCELED)
|
||||
break
|
||||
|
||||
def retry_task(self, item):
|
||||
|
@ -486,8 +619,9 @@ def retry_task(self, item):
|
|||
pass
|
||||
|
||||
def delete_task(self, item):
|
||||
reply = QMessageBox.question(self, 'Confirm Deletion',
|
||||
"Are you sure you want to delete this task?",
|
||||
self.logger.info(DELETING_TASK.format(item.text()))
|
||||
reply = QMessageBox.question(self, CONFIRM_DELETION_TITLE,
|
||||
CONFIRM_DELETION,
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No)
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
|
@ -507,35 +641,41 @@ def create_label(self, text, tooltip):
|
|||
return label
|
||||
|
||||
def load_models(self):
|
||||
self.logger.info(LOADING_MODELS)
|
||||
models_dir = self.models_input.text()
|
||||
ensure_directory(models_dir)
|
||||
self.model_list.clear()
|
||||
for file in os.listdir(models_dir):
|
||||
if file.endswith(".gguf"):
|
||||
self.model_list.addItem(file)
|
||||
self.logger.info(LOADED_MODELS.format(self.model_list.count()))
|
||||
|
||||
|
||||
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:
|
||||
self.models_input.setText(os.path.abspath(models_path))
|
||||
ensure_directory(models_path)
|
||||
self.load_models()
|
||||
|
||||
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:
|
||||
self.output_input.setText(os.path.abspath(output_path))
|
||||
ensure_directory(output_path)
|
||||
|
||||
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:
|
||||
self.logs_input.setText(os.path.abspath(logs_path))
|
||||
ensure_directory(logs_path)
|
||||
|
||||
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:
|
||||
self.imatrix.setText(os.path.abspath(imatrix_file))
|
||||
|
||||
|
@ -543,18 +683,25 @@ def update_system_info(self):
|
|||
ram = psutil.virtual_memory()
|
||||
cpu = psutil.cpu_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.cpu_label.setText(f"CPU Usage: {cpu:.1f}%")
|
||||
self.ram_bar.setFormat(RAM_USAGE_FORMAT.format(ram.percent, ram.used // 1024 // 1024, ram.total // 1024 // 1024))
|
||||
self.cpu_label.setText(CPU_USAGE_FORMAT.format(cpu))
|
||||
|
||||
def validate_quantization_inputs(self):
|
||||
self.logger.debug(VALIDATING_QUANTIZATION_INPUTS)
|
||||
errors = []
|
||||
if not self.backend_combo.currentData():
|
||||
raise ValueError("No backend selected")
|
||||
errors.append(NO_BACKEND_SELECTED)
|
||||
if not self.models_input.text():
|
||||
raise ValueError("Models path is required")
|
||||
errors.append(MODELS_PATH_REQUIRED)
|
||||
if not self.output_input.text():
|
||||
raise ValueError("Output path is required")
|
||||
errors.append(OUTPUT_PATH_REQUIRED)
|
||||
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):
|
||||
entry = KVOverrideEntry()
|
||||
|
@ -568,16 +715,17 @@ def remove_kv_override(self, entry):
|
|||
entry.deleteLater()
|
||||
|
||||
def quantize_model(self):
|
||||
self.logger.info(STARTING_MODEL_QUANTIZATION)
|
||||
try:
|
||||
self.validate_quantization_inputs()
|
||||
selected_model = self.model_list.currentItem()
|
||||
if not selected_model:
|
||||
raise ValueError("No model selected")
|
||||
raise ValueError(NO_MODEL_SELECTED)
|
||||
|
||||
model_name = selected_model.text()
|
||||
backend_path = self.backend_combo.currentData()
|
||||
if not backend_path:
|
||||
raise ValueError("No backend selected")
|
||||
raise ValueError(NO_BACKEND_SELECTED)
|
||||
quant_type = self.quant_type.currentText()
|
||||
|
||||
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)
|
||||
|
||||
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")]
|
||||
|
||||
|
@ -624,7 +772,7 @@ def quantize_model(self):
|
|||
thread = QuantizationThread(command, backend_path, log_file)
|
||||
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.setSizeHint(task_item.sizeHint())
|
||||
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.model_info_signal.connect(self.update_model_info)
|
||||
thread.start()
|
||||
self.logger.info(QUANTIZATION_TASK_STARTED.format(model_name))
|
||||
except ValueError as e:
|
||||
self.show_error(str(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):
|
||||
self.logger.debug(UPDATING_MODEL_INFO.format(model_info))
|
||||
# TODO: Do something with this
|
||||
pass
|
||||
|
||||
def task_finished(self, thread):
|
||||
self.logger.info(TASK_FINISHED.format(thread.log_file))
|
||||
if thread in self.quant_threads:
|
||||
self.quant_threads.remove(thread)
|
||||
|
||||
def show_task_details(self, item):
|
||||
self.logger.debug(SHOWING_TASK_DETAILS_FOR.format(item.text()))
|
||||
task_item = self.task_list.itemWidget(item)
|
||||
if task_item:
|
||||
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_text = QPlainTextEdit()
|
||||
|
@ -674,17 +828,20 @@ def show_task_details(self, item):
|
|||
log_dialog.exec()
|
||||
|
||||
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:
|
||||
self.imatrix_datafile.setText(os.path.abspath(datafile))
|
||||
|
||||
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:
|
||||
self.imatrix_model.setText(os.path.abspath(model_file))
|
||||
|
||||
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:
|
||||
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)
|
||||
|
||||
def generate_imatrix(self):
|
||||
self.logger.info(STARTING_IMATRIX_GENERATION)
|
||||
try:
|
||||
backend_path = self.backend_combo.currentData()
|
||||
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 = [
|
||||
os.path.join(backend_path, "llama-imatrix"),
|
||||
|
@ -724,7 +882,7 @@ def generate_imatrix(self):
|
|||
thread = QuantizationThread(command, backend_path, log_file)
|
||||
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.setSizeHint(task_item.sizeHint())
|
||||
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.start()
|
||||
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):
|
||||
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):
|
||||
self.logger.error(TASK_ERROR.format(error_message))
|
||||
self.show_error(error_message)
|
||||
task_item.set_error()
|
||||
|
||||
def closeEvent(self, event: QCloseEvent):
|
||||
self.logger.info(APPLICATION_CLOSING)
|
||||
if self.quant_threads:
|
||||
reply = QMessageBox.question(self, 'Warning',
|
||||
"Some tasks are still running. Are you sure you want to quit?",
|
||||
reply = QMessageBox.question(self, WARNING,
|
||||
TASK_RUNNING_WARNING,
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No)
|
||||
|
||||
|
@ -759,9 +921,10 @@ def closeEvent(self, event: QCloseEvent):
|
|||
event.ignore()
|
||||
else:
|
||||
event.accept()
|
||||
self.logger.info(APPLICATION_CLOSED)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
window = AutoGGUF()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
sys.exit(app.exec())
|
||||
|
|
|
@ -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)
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue