diff --git a/.env.example b/.env.example index 993b8a3..da700ce 100644 --- a/.env.example +++ b/.env.example @@ -3,3 +3,6 @@ AUTOGGUF_THEME= AUTOGGUF_CHECK_BACKEND=enabled AUTOGGUF_CHECK_UPDATE=enabled AUTOGGUF_SERVER_API_KEY= +AUTOGGUF_MODEL_DIR_NAME=models +AUTOGGUF_OUTPUT_DIR_NAME=quantized_models +AUTOGGUF_RESIZE_FACTOR=1.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 16cf044..54a2801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## [v1.8.1] - 2024-09-04 + +### Added +- AutoFP8 quantization classes and window (currently WIP) +- Minimize/maximize buttons to title bar +- API key authentication support for the local server +- HuggingFace upload/download class +- OpenAPI docs for endpoints + +### Changed +- Replaced Flask with FastAPI and Uvicorn for improved performance +- Moved functions out of AutoGGUF.py into utils.py and TaskListItem.py +- Updated llama.cpp convert scripts +- Improved LoRA conversion process: + - Allow specifying output path in arguments + - Removed shutil.move operation + - Increased max number of LoRA layers +- Changed default port to 7001 +- Now binding to localhost (127.0.0.1) instead of 0.0.0.0 +- Upadted Spanish localizations +- Updated setuptools requirement from ~=68.2.0 to ~=74.0.0 + +### Fixed +- Web page not found error +- Use of proper status in TaskListItem +- Passing of quant_threads and Logger to TaskListItem +- Improved window moving smoothness +- Prevention of moving window below taskbar +- Optimized imports in various files + ## [v1.8.0] - 2024-08-26 ### Added diff --git a/assets/icon.RES b/assets/icon.RES deleted file mode 100644 index 6627e35..0000000 Binary files a/assets/icon.RES and /dev/null differ diff --git a/assets/icon.rc b/assets/icon.rc deleted file mode 100644 index 1fa8fd7..0000000 --- a/assets/icon.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON "favicon.ico" \ No newline at end of file diff --git a/docs/AutoGGUF.py b/docs/AutoGGUF.py index 8bdc2be..4645430 100644 --- a/docs/AutoGGUF.py +++ b/docs/AutoGGUF.py @@ -1,29 +1,34 @@ +import importlib import json import re import shutil -import os - +from datetime import datetime from functools import partial +from typing import Any, Dict, List, Tuple + +import requests from PySide6.QtCore import * from PySide6.QtGui import * from PySide6.QtWidgets import * +from dotenv import load_dotenv +import lora_conversion +import presets +import ui_update +import utils +from CustomTitleBar import CustomTitleBar from GPUMonitor import GPUMonitor -from KVOverrideEntry import KVOverrideEntry +from Localizations import * from Logger import Logger -from ModelInfoDialog import ModelInfoDialog -from error_handling import show_error, handle_error +from QuantizationThread import QuantizationThread +from TaskListItem import TaskListItem +from error_handling import handle_error, show_error from imports_and_globals import ( + ensure_directory, open_file_safe, resource_path, show_about, - ensure_directory, ) -from Localizations import * -import presets -import ui_update -import lora_conversion -import utils class CustomTitleBar(QWidget): diff --git a/docs/DownloadThread.py b/docs/DownloadThread.py new file mode 100644 index 0000000..dbbd3c7 --- /dev/null +++ b/docs/DownloadThread.py @@ -0,0 +1,44 @@ +import os +import zipfile + +import requests +from PySide6.QtCore import QThread, Signal + + +class DownloadThread(QThread): + """ + A QThread subclass for downloading and extracting zip files. + + This thread downloads a file from a given URL, saves it to a specified path, + extracts its contents if it's a zip file, and then removes the original zip file. + + Signals: + progress_signal (int): Emits the download progress as a percentage. + finished_signal (str): Emits the path of the extracted directory upon successful completion. + error_signal (str): Emits an error message if an exception occurs during the process. + """ + + def __init__(self, url: str, save_path: str) -> None: + """ + Initialize the DownloadThread. + + Args: + url (str): The URL of the file to download. + save_path (str): The local path where the file will be saved. + """ + + def run(self) -> None: + """ + Execute the download, extraction, and cleanup process. + + This method performs the following steps: + 1. Downloads the file from the specified URL. + 2. Saves the file to the specified path. + 3. Extracts the contents if it's a zip file. + 4. Removes the original zip file after extraction. + 5. Emits signals for progress updates, completion, or errors. + + Raises: + Exception: Any exception that occurs during the process is caught + and emitted through the error_signal. + """ diff --git a/src/AutoGGUF.py b/src/AutoGGUF.py index 29429b6..e7ce8f4 100644 --- a/src/AutoGGUF.py +++ b/src/AutoGGUF.py @@ -1016,7 +1016,9 @@ def browse_hf_outfile(self) -> None: self.hf_outfile.setText(os.path.abspath(outfile)) def quantize_to_fp8_dynamic(self, model_dir: str, output_dir: str) -> None: - self.logger.info(f"Quantizing {os.path.basename(model_dir)} to {output_dir}") + self.logger.info( + QUANTIZING_TO_WITH_AUTOFP8.format(os.path.basename(model_dir), output_dir) + ) try: command = [ "python", @@ -1034,7 +1036,7 @@ def quantize_to_fp8_dynamic(self, model_dir: str, output_dir: str) -> None: thread = QuantizationThread(command, os.getcwd(), log_file) self.quant_threads.append(thread) - task_name = f"Quantizing {os.path.basename(model_dir)} with AutoFP8" + task_name = QUANTIZING_WITH_AUTOFP8.format(os.path.basename(model_dir)) task_item = TaskListItem( task_name, log_file, @@ -1057,12 +1059,12 @@ def quantize_to_fp8_dynamic(self, model_dir: str, output_dir: str) -> None: thread.start() except Exception as e: - show_error(self.logger, f"Error starting AutoFP8 quantization: {e}") - self.logger.info("AutoFP8 quantization task started") + show_error(self.logger, f"{ERROR_STARTING_AUTOFP8_QUANTIZATION}: {e}") + self.logger.info(AUTOFP8_QUANTIZATION_TASK_STARTED) def show_autofp8_window(self): dialog = QDialog(self) - dialog.setWindowTitle("Quantize to FP8 Dynamic") + dialog.setWindowTitle(QUANTIZE_TO_FP8_DYNAMIC) dialog.setFixedWidth(500) layout = QVBoxLayout() @@ -1072,10 +1074,10 @@ def show_autofp8_window(self): input_button = QPushButton(BROWSE) input_button.clicked.connect( lambda: self.fp8_input.setText( - QFileDialog.getExistingDirectory(self, "Open Model Folder") + QFileDialog.getExistingDirectory(self, OPEN_MODEL_FOLDER) ) ) - input_layout.addWidget(QLabel("Input Model:")) + input_layout.addWidget(QLabel(INPUT_MODEL)) input_layout.addWidget(self.fp8_input) input_layout.addWidget(input_button) layout.addLayout(input_layout) @@ -1086,16 +1088,16 @@ def show_autofp8_window(self): output_button = QPushButton(BROWSE) output_button.clicked.connect( lambda: self.fp8_output.setText( - QFileDialog.getExistingDirectory(self, "Open Model Folder") + QFileDialog.getExistingDirectory(self, OPEN_MODEL_FOLDER) ) ) - output_layout.addWidget(QLabel("Output Path:")) + output_layout.addWidget(QLabel(OUTPUT)) output_layout.addWidget(self.fp8_output) output_layout.addWidget(output_button) layout.addLayout(output_layout) # Quantize button - quantize_button = QPushButton("Quantize") + quantize_button = QPushButton(QUANTIZE) quantize_button.clicked.connect( lambda: self.quantize_to_fp8_dynamic( self.fp8_input.text(), self.fp8_output.text() diff --git a/src/Localizations.py b/src/Localizations.py index 2d460ba..9233061 100644 --- a/src/Localizations.py +++ b/src/Localizations.py @@ -1,6 +1,6 @@ import os -AUTOGGUF_VERSION = "v1.8.0" +AUTOGGUF_VERSION = "v1.8.1" class _Localization: @@ -34,6 +34,17 @@ def __init__(self): self.IMPORTING_MODEL = "Importing model" self.IMPORTED_MODEL_TOOLTIP = "Imported model: {}" + # AutoFP8 Quantization + self.AUTOFP8_QUANTIZATION_TASK_STARTED = "AutoFP8 quantization task started" + self.ERROR_STARTING_AUTOFP8_QUANTIZATION = "Error starting AutoFP8 quantization" + self.QUANTIZING_WITH_AUTOFP8 = "Quantizing {0} with AutoFP8" + self.QUANTIZING_TO_WITH_AUTOFP8 = "Quantizing {0} to {1}" + self.QUANTIZE_TO_FP8_DYNAMIC = "Quantize to FP8 Dynamic" + self.OPEN_MODEL_FOLDER = "Open Model Folder" + self.QUANTIZE = "Quantize" + self.OPEN_MODEL_FOLDER = "Open Model Folder" + self.INPUT_MODEL = "Input Model:" + # GGUF Verification self.INVALID_GGUF_FILE = "Invalid GGUF file: {}" self.SHARDED_MODEL_NAME = "{} (Sharded)"