From c02df3005ef29f02aa53993ca174a2877b8d2962 Mon Sep 17 00:00:00 2001 From: BuildTools Date: Fri, 16 Aug 2024 19:43:48 -0700 Subject: [PATCH] docs: update docstrings and small code fixes - update docstrings for AutoGGUF.py and add for lora_conversion.py and Logger.py - fix IDE detected code typos and errors --- .github/workflows/codeql.yml | 2 +- CONTRIBUTING.md | 2 +- README.md | 2 +- SECURITY.md | 6 +- docs/AutoGGUF.py | 353 ++++++++++++++++++++++++++--------- docs/Logger.py | 56 ++++++ docs/lora_conversion.py | 17 ++ src/AutoGGUF.py | 51 ----- src/convert_hf_to_gguf.py | 2 +- src/gguf-py/gguf/lazy.py | 2 +- src/gguf-py/gguf/metadata.py | 4 +- src/ui_update.py | 1 + 12 files changed, 345 insertions(+), 153 deletions(-) create mode 100644 docs/Logger.py create mode 100644 docs/lora_conversion.py diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c4fd444..00058bf 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -77,7 +77,7 @@ jobs: # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - # If the analyze step fails for one of the languages you are analyzing with + # If the analysis step fails for one of the languages you are analyzing with # "We were unable to automatically build your code", modify the matrix above # to set the build mode to "manual" for that language. Then modify this step # to build your code. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ea1dbe6..1b09444 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ You can find issues labeled with "good first issue" in the Issues tab as a start - Use the present tense ("Add feature" not "Added feature") - Use the imperative mood ("Move cursor to..." not "Moves cursor to...") -- Limit the first line to 72 characters or less +- Limit the first line to 72 characters or fewer ### Commit Types: diff --git a/README.md b/README.md index 6747547..13080e3 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Find them in `requirements.txt`. View the list of supported languages at [AutoGGUF/wiki/Installation#configuration](https://github.com/leafspark/AutoGGUF/wiki/Installation#configuration) (LLM translated, except for English). -To use a specific language, set the `AUTOGGUF_LANGUAGE` environment variable to one of the listed language codes (note: some languages may not be fully supported yet, those will fallback to English). +To use a specific language, set the `AUTOGGUF_LANGUAGE` environment variable to one of the listed language codes (note: some languages may not be fully supported yet, those will fall back to English). ## Known Issues diff --git a/SECURITY.md b/SECURITY.md index ea0e18d..9589256 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Supported Versions -| Version | Supported | -| ----------------- | ------------------ | -| stable (v1.6.2) | :white_check_mark: | +| Version | Supported | +|-----------------|--------------------| +| stable (v1.6.2) | :white_check_mark: | ## Reporting a Vulnerability diff --git a/docs/AutoGGUF.py b/docs/AutoGGUF.py index 37a1c6a..8bdc2be 100644 --- a/docs/AutoGGUF.py +++ b/docs/AutoGGUF.py @@ -1,145 +1,314 @@ +import json +import re +import shutil +import os + +from functools import partial +from PySide6.QtCore import * +from PySide6.QtGui import * +from PySide6.QtWidgets import * + +from GPUMonitor import GPUMonitor +from KVOverrideEntry import KVOverrideEntry +from Logger import Logger +from ModelInfoDialog import ModelInfoDialog +from error_handling import show_error, handle_error +from imports_and_globals import ( + 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): + """ + Custom title bar for the main window, providing drag-and-drop functionality + and minimize/close buttons. + """ + + def __init__(self, parent=None): + """ + Initializes the custom title bar. + + Args: + parent (QWidget, optional): The parent widget. Defaults to None. + """ + + class AutoGGUF(QMainWindow): """ - AutoGGUF is a PySide6-based graphical user interface for managing and quantizing large language models. - - This class provides functionality for: - - Loading and displaying models (including sharded models) - - Quantizing models with various options - - Downloading llama.cpp releases - - Generating importance matrices - - Converting and exporting LoRA models - - Managing quantization tasks - - Converting Hugging Face models to GGUF format - - The GUI allows users to interact with these features in an intuitive way, providing - options for model selection, quantization parameters, and task management. - - Attributes: - logger (Logger): Instance of the Logger class for logging operations. - ram_bar (QProgressBar): Progress bar for displaying RAM usage. - cpu_label (QLabel): Label for displaying CPU usage. - gpu_monitor (GPUMonitor): Widget for monitoring GPU usage. - backend_combo (QComboBox): Dropdown for selecting the backend. - model_tree (QTreeWidget): Tree widget for displaying available models. - task_list (QListWidget): List widget for displaying ongoing tasks. - quant_threads (list): List to store active quantization threads. - - The class also contains numerous UI elements for user input and interaction, - including text inputs, checkboxes, and buttons for various operations. + Main application window for AutoGGUF, providing a user interface for + quantizing and converting large language models. """ def __init__(self): """ - Initialize the AutoGGUF application window. + Initializes the main window, setting up the UI, logger, and other + necessary components. + """ + + def keyPressEvent(self, event): + """ + Handles key press events for window resizing. + + Args: + event (QKeyEvent): The key press event. + """ + + def resize_window(self, larger): + """ + Resizes the window by a specified factor. + + Args: + larger (bool): Whether to make the window larger or smaller. + """ + + def reset_size(self): + """Resets the window to its default size.""" + + def parse_resolution(self): + """ + Parses the resolution from the AUTOGGUF_RESOLUTION environment variable. + + Returns: + tuple: The width and height of the window. + """ - This method sets up the main window, initializes the UI components, - sets up layouts, and connects various signals to their respective slots. - It also initializes the logger, sets up the system info update timer, - and prepares the application for model management and quantization tasks. + def resizeEvent(self, event): + """ + Handles resize events to maintain rounded corners. - The initialization process includes: - - Setting up the main window properties (title, icon, size) - - Creating and arranging UI components for different functionalities - - Initializing backend and release information - - Setting up file browsers for various inputs - - Preparing quantization options and task management interface - - Initializing iMatrix generation interface - - Setting up LoRA conversion and export interfaces - - Preparing Hugging Face to GGUF conversion interface + Args: + event (QResizeEvent): The resize event. """ def refresh_backends(self): + """Refreshes the list of available backends.""" + + def save_task_preset(self, task_item): + """ + Saves the preset for a specific task. + + Args: + task_item (TaskListItem): The task item to save the preset for. """ - Refresh the list of available backends. - This method scans the 'llama_bin' directory for valid backends, - updates the backend selection combo box, and enables/disables - it based on the availability of backends. + def browse_export_lora_model(self): + """Opens a file dialog to browse for the export LORA model file.""" + + def browse_export_lora_output(self): + """Opens a file dialog to browse for the export LORA output file.""" + + def add_lora_adapter(self): + """Adds a LORA adapter to the export LORA list.""" - The method logs the refresh operation and the number of valid - backends found. + def browse_base_model(self): + """Opens a file dialog to browse for the base model folder.""" + + def delete_lora_adapter_item(self, adapter_widget): """ + Deletes a LORA adapter item from the export LORA list. - def update_assets(self): + Args: + adapter_widget (QWidget): The widget containing the adapter information. """ - Update the list of assets for the selected llama.cpp release. - This method clears the current asset list and populates it with - the assets of the selected release. It also updates the CUDA - option visibility based on the selected asset. + def browse_hf_model_input(self): + """Opens a file dialog to browse for the HuggingFace model directory.""" + + def browse_hf_outfile(self): + """Opens a file dialog to browse for the HuggingFace to GGUF output file.""" + + def convert_hf_to_gguf(self): + """Converts a HuggingFace model to GGUF format.""" + + def export_lora(self): + """Exports a LORA from a GGML model.""" + + def restart_task(self, task_item): """ + Restarts a specific task. - def download_llama_cpp(self): + Args: + task_item (TaskListItem): The task item to restart. """ - Initiate the download of the selected llama.cpp release asset. - This method starts a download thread for the selected asset, - updates the UI to show download progress, and sets up signal - connections for download completion and error handling. + def lora_conversion_finished(self, thread, input_path, output_path): """ + Handles the completion of a LORA conversion task. - def load_models(self): + Args: + thread (QuantizationThread): The thread that handled the conversion. + input_path (str): The path to the input LORA file. + output_path (str): The path to the output GGML file. """ - Load and display the list of available models. - This method scans the specified models directory for .gguf files, - organizes them into sharded and single models, and populates - the model tree widget with this information. + def download_finished(self, extract_dir): """ + Handles the completion of a download, extracting files and updating the UI. - def quantize_model(self): + Args: + extract_dir (str): The directory where the downloaded files were extracted. """ - Start the quantization process for the selected model. - This method prepares the quantization command based on user-selected - options, creates a new quantization thread, and sets up a task item - in the task list to track the quantization progress. + def extract_cuda_files(self, extract_dir, destination): """ + Extracts CUDA files from a downloaded archive. - def generate_imatrix(self): + Args: + extract_dir (str): The directory where the downloaded files were extracted. + destination (str): The destination directory for the CUDA files. """ - Start the importance matrix generation process. - This method prepares the iMatrix generation command based on user inputs, - creates a new thread for the operation, and sets up a task item - in the task list to track the generation progress. + def download_error(self, error_message): """ + Handles download errors, displaying an error message and cleaning up. - def convert_lora(self): + Args: + error_message (str): The error message. """ - Start the LoRA conversion process. - This method prepares the LoRA conversion command based on user inputs, - creates a new thread for the conversion, and sets up a task item - in the task list to track the conversion progress. + def show_task_context_menu(self, position): """ + Shows the context menu for a task item in the task list. - def export_lora(self): + Args: + position (QPoint): The position of the context menu. """ - Start the LoRA export process. - This method prepares the LoRA export command based on user inputs, - creates a new thread for the export operation, and sets up a task item - in the task list to track the export progress. + def show_task_properties(self, item): """ + Shows the properties dialog for a specific task. - def convert_hf_to_gguf(self): + Args: + item (QListWidgetItem): The task item. """ - Start the process of converting a Hugging Face model to GGUF format. - This method prepares the conversion command based on user inputs, - creates a new thread for the conversion, and sets up a task item - in the task list to track the conversion progress. + def toggle_gpu_offload_auto(self, state): """ + Toggles the automatic GPU offload option. - def closeEvent(self, event: QCloseEvent): + Args: + state (Qt.CheckState): The state of the checkbox. + """ + + def cancel_task_by_item(self, item): + """ + Cancels a task by its item in the task list. + + Args: + item (QListWidgetItem): The task item. + """ + + def cancel_task(self, item): """ - Handle the window close event. + Cancels a specific task. + + Args: + item (QListWidgetItem): The task item. + """ + + def delete_task(self, item): + """ + Deletes a specific task. + + Args: + item (QListWidgetItem): The task item. + """ + + def create_label(self, text, tooltip): + """ + Creates a QLabel with a tooltip. + + Args: + text (str): The text for the label. + tooltip (str): The tooltip for the label. + + Returns: + QLabel: The created label. + """ + + def load_models(self): + """Loads the available models and displays them in the model tree.""" + + def browse_models(self): + """Opens a file dialog to browse for the models directory.""" + + def browse_output(self): + """Opens a file dialog to browse for the output directory.""" + + def browse_logs(self): + """Opens a file dialog to browse for the logs directory.""" + + def browse_imatrix(self): + """Opens a file dialog to browse for the imatrix file.""" + + def validate_quantization_inputs(self): + """Validates the inputs for quantization.""" - This method is called when the user attempts to close the application. - It checks for any running tasks and prompts the user for confirmation - before closing if tasks are still in progress. + def add_kv_override(self, override_string=None): + """Adds a KV override entry to the list.""" + + def remove_kv_override(self, entry): + """Removes a KV override entry from the list.""" + + def quantize_model(self): + """Quantizes the selected model.""" + + def parse_progress(self, line, task_item): + """ + Parses the progress from the output line and updates the task item. + + Args: + line (str): The output line. + task_item (TaskListItem): The task item. + """ + + def task_finished(self, thread, task_item): + """ + Handles the completion of a task. + + Args: + thread (QuantizationThread): The thread that handled the task. + task_item (TaskListItem): The task item. + """ + + def show_task_details(self, item): + """ + Shows the details of a specific task. + + Args: + item (QListWidgetItem): The task item. + """ + + def browse_imatrix_datafile(self): + """Opens a file dialog to browse for the imatrix data file.""" + + def browse_imatrix_model(self): + """Opens a file dialog to browse for the imatrix model file.""" + + def browse_imatrix_output(self): + """Opens a file dialog to browse for the imatrix output file.""" + + def get_models_data(self): + """Retrieves data for all loaded models.""" + + def get_tasks_data(self): + """Retrieves data for all tasks in the task list.""" + + def generate_imatrix(self): + """Generates an imatrix file.""" + + def closeEvent(self, event: QCloseEvent): + """ + Handles close events, prompting the user if there are running tasks. Args: - event (QCloseEvent): The close event object. + event (QCloseEvent): The close event. """ diff --git a/docs/Logger.py b/docs/Logger.py new file mode 100644 index 0000000..e5c12ea --- /dev/null +++ b/docs/Logger.py @@ -0,0 +1,56 @@ +class Logger: + """ + This module provides a custom logger class for logging messages to both the console and a rotating log file. + + The log file will be created in the specified `log_dir` with a timestamp in the filename. + The file will rotate when it reaches 10MB, keeping a maximum of 5 backup files. + """ + + def __init__(self, name, log_dir): + """ + Initializes the logger with a specified name and log directory. + + Args: + name (str): The name of the logger. + log_dir (str): The directory where log files will be stored. + """ + + def debug(self, message): + """ + Logs a message with the DEBUG level. + + Args: + message (str): The message to log. + """ + + def info(self, message): + """ + Logs a message with the INFO level. + + Args: + message (str): The message to log. + """ + + def warning(self, message): + """ + Logs a message with the WARNING level. + + Args: + message (str): The message to log. + """ + + def error(self, message): + """ + Logs a message with the ERROR level. + + Args: + message (str): The message to log. + """ + + def critical(self, message): + """ + Logs a message with the CRITICAL level. + + Args: + message (str): The message to log. + """ diff --git a/docs/lora_conversion.py b/docs/lora_conversion.py new file mode 100644 index 0000000..296c077 --- /dev/null +++ b/docs/lora_conversion.py @@ -0,0 +1,17 @@ +def convert_lora(self): + """Converts a LORA file to either GGML or GGUF format. + + This function initiates the conversion process based on user input, + utilizing a separate thread for the actual conversion and providing + progress updates in the UI. + + It validates input paths, constructs the conversion command, creates + a log file, manages the conversion thread, and handles errors. + + Args: + self: The object instance. + + Raises: + ValueError: If required input paths are missing. + + """ diff --git a/src/AutoGGUF.py b/src/AutoGGUF.py index 422a789..055d89d 100644 --- a/src/AutoGGUF.py +++ b/src/AutoGGUF.py @@ -1,7 +1,6 @@ import json import re import shutil -import os from functools import partial from PySide6.QtCore import * @@ -841,27 +840,6 @@ def resizeEvent(self, event): mask = QRegion(path.toFillPolygon().toPolygon()) self.setMask(mask) - def closeEvent(self, event: QCloseEvent): - self.logger.info(APPLICATION_CLOSING) - if self.quant_threads: - reply = QMessageBox.question( - self, - WARNING, - TASK_RUNNING_WARNING, - QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, - QMessageBox.StandardButton.No, - ) - - if reply == QMessageBox.StandardButton.Yes: - for thread in self.quant_threads: - thread.terminate() - event.accept() - else: - event.ignore() - else: - event.accept() - self.logger.info(APPLICATION_CLOSED) - def refresh_backends(self): self.logger.info(REFRESHING_BACKENDS) llama_bin = os.path.abspath("llama_bin") @@ -1206,35 +1184,6 @@ def download_finished(self, extract_dir): if index >= 0: self.backend_combo.setCurrentIndex(index) - def download_finished(self, 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: - 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_CUDA_BACKEND_SELECTED - ) - else: - 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): diff --git a/src/convert_hf_to_gguf.py b/src/convert_hf_to_gguf.py index 7c844c3..9f8ada0 100644 --- a/src/convert_hf_to_gguf.py +++ b/src/convert_hf_to_gguf.py @@ -2952,7 +2952,7 @@ def modify_tensors( ) -> Iterable[tuple[str, Tensor]]: del bid # unused - # we are only using BERT for embeddings so we don't need the pooling layer + # we are only using BERT for embeddings, so we don't need the pooling layer if name in ( "embeddings.position_ids", "pooler.dense.weight", diff --git a/src/gguf-py/gguf/lazy.py b/src/gguf-py/gguf/lazy.py index a8aaefe..831e3dc 100644 --- a/src/gguf-py/gguf/lazy.py +++ b/src/gguf-py/gguf/lazy.py @@ -34,7 +34,7 @@ def __getattr__(self, name: str) -> Any: # need to make a builder for the wrapped wrapper to copy the name, # or else it fails with very cryptic error messages, - # because somehow the same string would end up in every closures + # because somehow the same string would end up in every closure def mk_wrap(op_name: str, *, meta_noop: bool = False): # need to wrap the wrapper to get self def wrapped_special_op(self, *args, **kwargs): diff --git a/src/gguf-py/gguf/metadata.py b/src/gguf-py/gguf/metadata.py index f775dc5..7092683 100644 --- a/src/gguf-py/gguf/metadata.py +++ b/src/gguf-py/gguf/metadata.py @@ -50,7 +50,7 @@ def load( model_name: Optional[str] = None, total_params: int = 0, ) -> Metadata: - # This grabs as many contextual authorship metadata as possible from the model repository + # This grabs as much contextual authorship metadata as possible from the model repository # making any conversion as required to match the gguf kv store metadata format # as well as giving users the ability to override any authorship metadata that may be incorrect @@ -508,7 +508,7 @@ def apply_metadata_heuristic( hf_name_or_path = hf_params.get("_name_or_path") if hf_name_or_path is not None and hf_name_or_path.count("/") <= 1: - # Use _name_or_path only if its actually a model name and not some computer path + # Use _name_or_path only if it's actually a model name and not some computer path # e.g. 'meta-llama/Llama-2-7b-hf' model_id = hf_name_or_path ( diff --git a/src/ui_update.py b/src/ui_update.py index 48c3a8c..99c1dff 100644 --- a/src/ui_update.py +++ b/src/ui_update.py @@ -1,5 +1,6 @@ from Localizations import * import psutil +from error_handling import show_error def update_model_info(logger, self, model_info):