mirror of https://github.com/leafspark/AutoGGUF
feat(ui): update Japanese and German localizations
- update Japanese and German localizations - bump version to v1.9.0 - optimize imports in main.py - organize function definitions in AutoGGUF.py
This commit is contained in:
parent
1133422456
commit
c02a02fbc1
|
@ -0,0 +1,28 @@
|
||||||
|
class ModelInfoDialog(QDialog):
|
||||||
|
"""
|
||||||
|
A dialog window for displaying model information.
|
||||||
|
|
||||||
|
This class creates a dialog that shows detailed information about a machine learning model,
|
||||||
|
including its architecture, quantization type, and other relevant data.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
None
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model_info (dict): A dictionary containing the model's information.
|
||||||
|
parent (QWidget, optional): The parent widget of this dialog. Defaults to None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def format_model_info(self, model_info) -> str:
|
||||||
|
"""
|
||||||
|
Formats the model information into HTML for display.
|
||||||
|
|
||||||
|
This method takes the raw model information and converts it into a formatted HTML string,
|
||||||
|
which can be displayed in the dialog's QTextEdit widget.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model_info (dict): A dictionary containing the model's information.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Formatted HTML string containing the model information.
|
||||||
|
"""
|
350
src/AutoGGUF.py
350
src/AutoGGUF.py
|
@ -1237,6 +1237,181 @@ def save_task_preset(self, task_item) -> None:
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def download_finished(self, extract_dir) -> None:
|
||||||
|
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:
|
||||||
|
self.extract_cuda_files(extract_dir, 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
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
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
|
||||||
|
|
||||||
|
# Select the newly downloaded backend
|
||||||
|
new_backend_name = os.path.basename(extract_dir)
|
||||||
|
index = self.backend_combo.findText(new_backend_name)
|
||||||
|
if index >= 0:
|
||||||
|
self.backend_combo.setCurrentIndex(index)
|
||||||
|
|
||||||
|
def verify_gguf(self, file_path) -> bool:
|
||||||
|
try:
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
magic = f.read(4)
|
||||||
|
return magic == b"GGUF"
|
||||||
|
except (FileNotFoundError, IOError, OSError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def validate_quantization_inputs(self) -> None:
|
||||||
|
self.logger.debug(VALIDATING_QUANTIZATION_INPUTS)
|
||||||
|
errors = []
|
||||||
|
if not self.backend_combo.currentData():
|
||||||
|
errors.append(NO_BACKEND_SELECTED)
|
||||||
|
if not self.models_input.text():
|
||||||
|
errors.append(MODELS_PATH_REQUIRED)
|
||||||
|
if not self.output_input.text():
|
||||||
|
errors.append(OUTPUT_PATH_REQUIRED)
|
||||||
|
if not self.logs_input.text():
|
||||||
|
errors.append(LOGS_PATH_REQUIRED)
|
||||||
|
if not self.model_tree.currentItem():
|
||||||
|
errors.append(NO_MODEL_SELECTED)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ValueError("\n".join(errors))
|
||||||
|
|
||||||
|
def load_models(self) -> None:
|
||||||
|
self.logger.info(LOADING_MODELS)
|
||||||
|
models_dir = self.models_input.text()
|
||||||
|
ensure_directory(models_dir)
|
||||||
|
self.model_tree.clear()
|
||||||
|
|
||||||
|
sharded_models = {}
|
||||||
|
single_models = []
|
||||||
|
concatenated_models = []
|
||||||
|
|
||||||
|
shard_pattern = re.compile(r"(.*)-(\d+)-of-(\d+)\.gguf$")
|
||||||
|
concat_pattern = re.compile(r"(.*)\.gguf\.part(\d+)of(\d+)$")
|
||||||
|
|
||||||
|
for file in os.listdir(models_dir):
|
||||||
|
full_path = os.path.join(models_dir, file)
|
||||||
|
if file.endswith(".gguf"):
|
||||||
|
if not self.verify_gguf(full_path):
|
||||||
|
show_error(self.logger, INVALID_GGUF_FILE.format(file))
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = shard_pattern.match(file)
|
||||||
|
if match:
|
||||||
|
base_name, shard_num, total_shards = match.groups()
|
||||||
|
if base_name not in sharded_models:
|
||||||
|
sharded_models[base_name] = []
|
||||||
|
sharded_models[base_name].append((int(shard_num), file))
|
||||||
|
else:
|
||||||
|
single_models.append(file)
|
||||||
|
else:
|
||||||
|
match = concat_pattern.match(file)
|
||||||
|
if match:
|
||||||
|
concatenated_models.append(file)
|
||||||
|
|
||||||
|
if hasattr(self, "imported_models"):
|
||||||
|
for imported_model in self.imported_models:
|
||||||
|
file_name = os.path.basename(imported_model)
|
||||||
|
if (
|
||||||
|
file_name not in single_models
|
||||||
|
and file_name not in concatenated_models
|
||||||
|
):
|
||||||
|
if self.verify_gguf(imported_model):
|
||||||
|
single_models.append(file_name)
|
||||||
|
else:
|
||||||
|
show_error(
|
||||||
|
self.logger, INVALID_GGUF_FILE.format(imported_model)
|
||||||
|
)
|
||||||
|
|
||||||
|
for base_name, shards in sharded_models.items():
|
||||||
|
parent_item = QTreeWidgetItem(self.model_tree)
|
||||||
|
parent_item.setText(0, SHARDED_MODEL_NAME.format(base_name))
|
||||||
|
first_shard = sorted(shards, key=lambda x: x[0])[0][1]
|
||||||
|
parent_item.setData(0, Qt.ItemDataRole.UserRole, first_shard)
|
||||||
|
for _, shard_file in sorted(shards):
|
||||||
|
child_item = QTreeWidgetItem(parent_item)
|
||||||
|
child_item.setText(0, shard_file)
|
||||||
|
child_item.setData(0, Qt.ItemDataRole.UserRole, shard_file)
|
||||||
|
|
||||||
|
for model in sorted(single_models):
|
||||||
|
self.add_model_to_tree(model)
|
||||||
|
|
||||||
|
for model in sorted(concatenated_models):
|
||||||
|
item = self.add_model_to_tree(model)
|
||||||
|
item.setForeground(0, Qt.gray)
|
||||||
|
item.setToolTip(0, CONCATENATED_FILE_WARNING)
|
||||||
|
|
||||||
|
self.model_tree.expandAll()
|
||||||
|
self.logger.info(
|
||||||
|
LOADED_MODELS.format(
|
||||||
|
len(single_models) + len(sharded_models) + len(concatenated_models)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if concatenated_models:
|
||||||
|
self.logger.warning(
|
||||||
|
CONCATENATED_FILES_FOUND.format(len(concatenated_models))
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_model_to_tree(self, model) -> QTreeWidgetItem:
|
||||||
|
item = QTreeWidgetItem(self.model_tree)
|
||||||
|
item.setText(0, model)
|
||||||
|
if hasattr(self, "imported_models") and model in [
|
||||||
|
os.path.basename(m) for m in self.imported_models
|
||||||
|
]:
|
||||||
|
full_path = next(
|
||||||
|
m for m in self.imported_models if os.path.basename(m) == model
|
||||||
|
)
|
||||||
|
item.setData(0, Qt.ItemDataRole.UserRole, full_path)
|
||||||
|
item.setToolTip(0, IMPORTED_MODEL_TOOLTIP.format(full_path))
|
||||||
|
else:
|
||||||
|
item.setData(0, Qt.ItemDataRole.UserRole, model)
|
||||||
|
return item
|
||||||
|
|
||||||
|
def extract_cuda_files(self, extract_dir, destination) -> None:
|
||||||
|
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"):
|
||||||
|
source_path = os.path.join(root, file)
|
||||||
|
dest_path = os.path.join(destination, file)
|
||||||
|
shutil.copy2(source_path, dest_path)
|
||||||
|
|
||||||
|
def download_error(self, error_message) -> None:
|
||||||
|
self.logger.error(DOWNLOAD_ERROR.format(error_message))
|
||||||
|
self.download_button.setEnabled(True)
|
||||||
|
self.download_progress.setValue(0)
|
||||||
|
show_error(self.logger, DOWNLOAD_FAILED.format(error_message))
|
||||||
|
|
||||||
|
# Clean up any partially downloaded files
|
||||||
|
asset = self.asset_combo.currentData()
|
||||||
|
if asset:
|
||||||
|
partial_file = os.path.join(os.path.abspath("llama_bin"), asset["name"])
|
||||||
|
if os.path.exists(partial_file):
|
||||||
|
os.remove(partial_file)
|
||||||
|
|
||||||
def browse_local_path(self) -> None:
|
def browse_local_path(self) -> None:
|
||||||
if self.upload_type_file.isChecked():
|
if self.upload_type_file.isChecked():
|
||||||
file_path, _ = QFileDialog.getOpenFileName(self, SELECT_FILE)
|
file_path, _ = QFileDialog.getOpenFileName(self, SELECT_FILE)
|
||||||
|
@ -1444,65 +1619,6 @@ def convert_hf_to_gguf(self) -> None:
|
||||||
show_error(self.logger, ERROR_STARTING_HF_TO_GGUF_CONVERSION.format(str(e)))
|
show_error(self.logger, ERROR_STARTING_HF_TO_GGUF_CONVERSION.format(str(e)))
|
||||||
self.logger.info(HF_TO_GGUF_CONVERSION_TASK_STARTED)
|
self.logger.info(HF_TO_GGUF_CONVERSION_TASK_STARTED)
|
||||||
|
|
||||||
def download_finished(self, extract_dir) -> None:
|
|
||||||
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:
|
|
||||||
self.extract_cuda_files(extract_dir, 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
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
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
|
|
||||||
|
|
||||||
# Select the newly downloaded backend
|
|
||||||
new_backend_name = os.path.basename(extract_dir)
|
|
||||||
index = self.backend_combo.findText(new_backend_name)
|
|
||||||
if index >= 0:
|
|
||||||
self.backend_combo.setCurrentIndex(index)
|
|
||||||
|
|
||||||
def extract_cuda_files(self, extract_dir, destination) -> None:
|
|
||||||
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"):
|
|
||||||
source_path = os.path.join(root, file)
|
|
||||||
dest_path = os.path.join(destination, file)
|
|
||||||
shutil.copy2(source_path, dest_path)
|
|
||||||
|
|
||||||
def download_error(self, error_message) -> None:
|
|
||||||
self.logger.error(DOWNLOAD_ERROR.format(error_message))
|
|
||||||
self.download_button.setEnabled(True)
|
|
||||||
self.download_progress.setValue(0)
|
|
||||||
show_error(self.logger, DOWNLOAD_FAILED.format(error_message))
|
|
||||||
|
|
||||||
# Clean up any partially downloaded files
|
|
||||||
asset = self.asset_combo.currentData()
|
|
||||||
if asset:
|
|
||||||
partial_file = os.path.join(os.path.abspath("llama_bin"), asset["name"])
|
|
||||||
if os.path.exists(partial_file):
|
|
||||||
os.remove(partial_file)
|
|
||||||
|
|
||||||
def split_gguf(
|
def split_gguf(
|
||||||
self, model_dir: str, output_dir: str, max_size: str, max_tensors: str
|
self, model_dir: str, output_dir: str, max_size: str, max_tensors: str
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -1557,122 +1673,6 @@ def split_gguf(
|
||||||
show_error(self.logger, SPLIT_GGUF_ERROR.format(e))
|
show_error(self.logger, SPLIT_GGUF_ERROR.format(e))
|
||||||
self.logger.info(SPLIT_GGUF_TASK_FINISHED)
|
self.logger.info(SPLIT_GGUF_TASK_FINISHED)
|
||||||
|
|
||||||
def verify_gguf(self, file_path) -> bool:
|
|
||||||
try:
|
|
||||||
with open(file_path, "rb") as f:
|
|
||||||
magic = f.read(4)
|
|
||||||
return magic == b"GGUF"
|
|
||||||
except (FileNotFoundError, IOError, OSError):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def load_models(self) -> None:
|
|
||||||
self.logger.info(LOADING_MODELS)
|
|
||||||
models_dir = self.models_input.text()
|
|
||||||
ensure_directory(models_dir)
|
|
||||||
self.model_tree.clear()
|
|
||||||
|
|
||||||
sharded_models = {}
|
|
||||||
single_models = []
|
|
||||||
concatenated_models = []
|
|
||||||
|
|
||||||
shard_pattern = re.compile(r"(.*)-(\d+)-of-(\d+)\.gguf$")
|
|
||||||
concat_pattern = re.compile(r"(.*)\.gguf\.part(\d+)of(\d+)$")
|
|
||||||
|
|
||||||
for file in os.listdir(models_dir):
|
|
||||||
full_path = os.path.join(models_dir, file)
|
|
||||||
if file.endswith(".gguf"):
|
|
||||||
if not self.verify_gguf(full_path):
|
|
||||||
show_error(self.logger, INVALID_GGUF_FILE.format(file))
|
|
||||||
continue
|
|
||||||
|
|
||||||
match = shard_pattern.match(file)
|
|
||||||
if match:
|
|
||||||
base_name, shard_num, total_shards = match.groups()
|
|
||||||
if base_name not in sharded_models:
|
|
||||||
sharded_models[base_name] = []
|
|
||||||
sharded_models[base_name].append((int(shard_num), file))
|
|
||||||
else:
|
|
||||||
single_models.append(file)
|
|
||||||
else:
|
|
||||||
match = concat_pattern.match(file)
|
|
||||||
if match:
|
|
||||||
concatenated_models.append(file)
|
|
||||||
|
|
||||||
if hasattr(self, "imported_models"):
|
|
||||||
for imported_model in self.imported_models:
|
|
||||||
file_name = os.path.basename(imported_model)
|
|
||||||
if (
|
|
||||||
file_name not in single_models
|
|
||||||
and file_name not in concatenated_models
|
|
||||||
):
|
|
||||||
if self.verify_gguf(imported_model):
|
|
||||||
single_models.append(file_name)
|
|
||||||
else:
|
|
||||||
show_error(
|
|
||||||
self.logger, INVALID_GGUF_FILE.format(imported_model)
|
|
||||||
)
|
|
||||||
|
|
||||||
for base_name, shards in sharded_models.items():
|
|
||||||
parent_item = QTreeWidgetItem(self.model_tree)
|
|
||||||
parent_item.setText(0, SHARDED_MODEL_NAME.format(base_name))
|
|
||||||
first_shard = sorted(shards, key=lambda x: x[0])[0][1]
|
|
||||||
parent_item.setData(0, Qt.ItemDataRole.UserRole, first_shard)
|
|
||||||
for _, shard_file in sorted(shards):
|
|
||||||
child_item = QTreeWidgetItem(parent_item)
|
|
||||||
child_item.setText(0, shard_file)
|
|
||||||
child_item.setData(0, Qt.ItemDataRole.UserRole, shard_file)
|
|
||||||
|
|
||||||
for model in sorted(single_models):
|
|
||||||
self.add_model_to_tree(model)
|
|
||||||
|
|
||||||
for model in sorted(concatenated_models):
|
|
||||||
item = self.add_model_to_tree(model)
|
|
||||||
item.setForeground(0, Qt.gray)
|
|
||||||
item.setToolTip(0, CONCATENATED_FILE_WARNING)
|
|
||||||
|
|
||||||
self.model_tree.expandAll()
|
|
||||||
self.logger.info(
|
|
||||||
LOADED_MODELS.format(
|
|
||||||
len(single_models) + len(sharded_models) + len(concatenated_models)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if concatenated_models:
|
|
||||||
self.logger.warning(
|
|
||||||
CONCATENATED_FILES_FOUND.format(len(concatenated_models))
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_model_to_tree(self, model) -> QTreeWidgetItem:
|
|
||||||
item = QTreeWidgetItem(self.model_tree)
|
|
||||||
item.setText(0, model)
|
|
||||||
if hasattr(self, "imported_models") and model in [
|
|
||||||
os.path.basename(m) for m in self.imported_models
|
|
||||||
]:
|
|
||||||
full_path = next(
|
|
||||||
m for m in self.imported_models if os.path.basename(m) == model
|
|
||||||
)
|
|
||||||
item.setData(0, Qt.ItemDataRole.UserRole, full_path)
|
|
||||||
item.setToolTip(0, IMPORTED_MODEL_TOOLTIP.format(full_path))
|
|
||||||
else:
|
|
||||||
item.setData(0, Qt.ItemDataRole.UserRole, model)
|
|
||||||
return item
|
|
||||||
|
|
||||||
def validate_quantization_inputs(self) -> None:
|
|
||||||
self.logger.debug(VALIDATING_QUANTIZATION_INPUTS)
|
|
||||||
errors = []
|
|
||||||
if not self.backend_combo.currentData():
|
|
||||||
errors.append(NO_BACKEND_SELECTED)
|
|
||||||
if not self.models_input.text():
|
|
||||||
errors.append(MODELS_PATH_REQUIRED)
|
|
||||||
if not self.output_input.text():
|
|
||||||
errors.append(OUTPUT_PATH_REQUIRED)
|
|
||||||
if not self.logs_input.text():
|
|
||||||
errors.append(LOGS_PATH_REQUIRED)
|
|
||||||
if not self.model_tree.currentItem():
|
|
||||||
errors.append(NO_MODEL_SELECTED)
|
|
||||||
|
|
||||||
if errors:
|
|
||||||
raise ValueError("\n".join(errors))
|
|
||||||
|
|
||||||
def quantize_model(self) -> None:
|
def quantize_model(self) -> None:
|
||||||
self.logger.info(STARTING_MODEL_QUANTIZATION)
|
self.logger.info(STARTING_MODEL_QUANTIZATION)
|
||||||
try:
|
try:
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
||||||
from PySide6.QtCore import QTimer
|
from PySide6.QtCore import QTimer
|
||||||
from PySide6.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
from fastapi import FastAPI, Query, Depends, HTTPException, Security
|
from fastapi import FastAPI, Query, Depends, HTTPException, Security
|
||||||
from fastapi.security.api_key import APIKeyHeader, APIKey
|
from fastapi.security.api_key import APIKeyHeader
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from uvicorn import Config, Server
|
from uvicorn import Config, Server
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue