Implement model quantization format support#8
Conversation
Implemented comprehensive GGUF quantization support using llama.cpp, enabling conversion of HuggingFace models to GGUF format with multiple quantization levels (Q4_K_M, Q5_K_M, Q6_K, Q8_0, etc.). Key changes: - Added llama.cpp installation to Docker container for GGUF conversion - Implemented GGUFQuantizer class with two-step quantization process: 1. Convert HF model to intermediate GGUF format (f16/f32/q8_0) 2. Quantize to target precision using llama-quantize - Extended QuantizationConfig with GGUF-specific parameters: - gguf_quant_type: Target quantization level (Q4_K_M default) - gguf_intermediate_format: Intermediate format (f16 default) - Added comprehensive input validation for GGUF quantization types - Updated UI with GGUF selection and parameter controls - Enhanced documentation with GGUF usage guidelines and recommendations - Added error handling for llama.cpp availability checks - Included subprocess-based streaming output for conversion progress Quantization types supported: - Q2_K, Q3_K_S, Q3_K_M, Q3_K_L (smaller sizes) - Q4_0, Q4_1, Q4_K_S, Q4_K_M (balanced - Q4_K_M recommended) - Q5_0, Q5_1, Q5_K_S, Q5_K_M (best quality) - Q6_K, Q8_0 (high precision) - F16, F32 (full precision) GGUF outputs are compatible with llama.cpp, Ollama, and other GGUF-compatible inference engines.
|
Automated review 🤖 Summary of ChangesThis PR introduces GGUF quantization support using llama.cpp, enabling conversion of HuggingFace models to GGUF format with multiple quantization levels. It adds a new quantization method to the UI, updates configuration handling, and implements a two-step process: first converting to an intermediate GGUF format, then quantizing to the target precision. The Docker image is updated to include llama.cpp dependencies. Key Changes & Positives
Potential Issues & Recommendations
Language/Framework Checks
Security & Privacy
Build/CI & Ops
Tests
Approval RecommendationApprove with caveats The implementation is well-structured and adds significant value with GGUF support. However, the following items should be addressed before merging:
|
There was a problem hiding this comment.
Pull Request Overview
This PR adds GGUF quantization support to MSQuant, enabling users to quantize models using the llama.cpp toolchain. GGUF quantization produces models compatible with llama.cpp, Ollama, and other GGUF-compatible inference engines.
- Implements
GGUFQuantizerclass with support for multiple quantization levels (Q4_K_M, Q5_K_M, Q6_K, Q8_0, etc.) - Adds GGUF-specific configuration parameters (
gguf_quant_type,gguf_intermediate_format) - Updates Docker build to install llama.cpp toolchain and dependencies
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/msquant/core/quantizer/engine.py | Adds GGUFQuantizer class with methods for model download, conversion to GGUF format, and quantization using llama.cpp tools |
| src/msquant/core/quantizer/config.py | Extends QuantizationConfig with GGUF parameters and validation for quantization types and intermediate formats |
| src/msquant/app/pages/configure.py | Updates UI to include GGUF as a quantization method option with selectors for quantization type and intermediate format |
| pixi.toml | Adds gguf package dependency |
| docker/Dockerfile.gpu | Installs llama.cpp from GitHub with build tools and adds it to PATH |
| README.md | Documents GGUF quantization support with recommended settings and usage notes |
Comments suppressed due to low confidence (2)
src/msquant/core/quantizer/engine.py:510
- The generic vLLM instruction is misleading for GGUF outputs. GGUF models are not loaded via vLLM but through llama.cpp, Ollama, or other GGUF-compatible engines. This message should be conditional on the quantization method to provide accurate next steps.
logger.info(f"Next step: load in vLLM, e.g., `vllm serve {output_dir} --dtype float16`")
src/msquant/core/quantizer/engine.py:1
- The new
GGUFQuantizerclass and its methods (_check_llama_cpp_available,_download_model,_convert_to_gguf_intermediate,_quantize_gguf,run) lack test coverage. Consider adding tests to verify the GGUF quantization workflow, parameter validation, and error handling.
"""
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| logger.info(f"Converting model to GGUF {intermediate_format} format...") | ||
|
|
||
| # Find the convert-hf-to-gguf.py script | ||
| convert_script = "/opt/llama.cpp/convert_hf_to_gguf.py" |
There was a problem hiding this comment.
The script name uses underscores (convert_hf_to_gguf.py) but llama.cpp's actual script uses hyphens (convert-hf-to-gguf.py). This will cause the conversion to fail at runtime when the script is not found.
| convert_script = "/opt/llama.cpp/convert_hf_to_gguf.py" | |
| convert_script = "/opt/llama.cpp/convert-hf-to-gguf.py" |
| self.gguf_quant_type = gguf_quant_type.upper() if isinstance(gguf_quant_type, str) else gguf_quant_type | ||
| self.gguf_intermediate_format = gguf_intermediate_format.lower() if isinstance(gguf_intermediate_format, str) else gguf_intermediate_format |
There was a problem hiding this comment.
The uppercase conversion is applied regardless of whether it's needed. The validation at line 120 expects exact matches from the valid_gguf_types list (which contains uppercase values). If a non-string is passed and stored as-is, the validation could fail unexpectedly. Consider either requiring string type or handling non-string gracefully with a type check/conversion.
| self.gguf_quant_type = gguf_quant_type.upper() if isinstance(gguf_quant_type, str) else gguf_quant_type | |
| self.gguf_intermediate_format = gguf_intermediate_format.lower() if isinstance(gguf_intermediate_format, str) else gguf_intermediate_format | |
| if not isinstance(gguf_quant_type, str): | |
| raise ValueError(f"gguf_quant_type must be a string, got {type(gguf_quant_type).__name__}") | |
| self.gguf_quant_type = gguf_quant_type.upper() | |
| if not isinstance(gguf_intermediate_format, str): | |
| raise ValueError(f"gguf_intermediate_format must be a string, got {type(gguf_intermediate_format).__name__}") | |
| self.gguf_intermediate_format = gguf_intermediate_format.lower() |
| # Clone and build llama.cpp | ||
| RUN git clone https://github.com/ggerganov/llama.cpp /opt/llama.cpp && \ | ||
| cd /opt/llama.cpp && \ | ||
| git checkout master && \ |
There was a problem hiding this comment.
Checking out the master branch of llama.cpp means the build will use whatever version is current at build time, leading to non-reproducible builds and potential breaking changes. Consider pinning to a specific commit hash or tag for reproducibility and stability.
| git checkout master && \ | |
| git checkout 7e908c3 && \ # Replace 7e908c3 with the desired commit hash or tag |
NiceGUI 3.0 introduced a mandatory sanitize parameter for ui.html() to address CVE-2025-53354 (XSS vulnerability). Since the HTML content is static and safe, passing sanitize=False to indicate no sanitization is needed.
|
Automated review 🤖 Summary of ChangesThis PR implements GGUF quantization support using llama.cpp, enabling conversion of HuggingFace models to GGUF format with multiple quantization levels. It adds GGUF-specific parameters to the quantization config, updates the UI with GGUF selection and controls, and includes comprehensive validation and error handling. The Dockerfile is updated to install llama.cpp dependencies. Key Changes & Positives
Potential Issues & Recommendations
Language/Framework Checks
Security & Privacy
Build/CI & Ops
Tests
Approval RecommendationRequest changes The implementation introduces significant new functionality but has critical security and reliability concerns:
Additionally, comprehensive testing is missing. These issues must be addressed before merging. The GGUF support is valuable but requires robust implementation to be production-ready. |
Three critical fixes based on code review: 1. Dockerfile: Use pre-compiled llama.cpp binaries instead of building from source - Changed from git clone + make to downloading pre-compiled release - Added ARG LLAMA_CPP_VERSION=b6945 for version parameterization - Removed build-essential, cmake, git dependencies (only need wget, unzip) - Significantly faster Docker builds and smaller image size 2. config.py: Fix type validation for GGUF parameters - Changed conditional uppercase/lowercase conversion to explicit type checking - Added TypeError with descriptive messages if non-string types are passed - Ensures validation at line 120 works correctly with exact string matches - Prevents unexpected validation failures from non-string inputs 3. engine.py: Fix convert script name (hyphens vs underscores) - Changed convert_hf_to_gguf.py to convert-hf-to-gguf.py - The actual llama.cpp script uses hyphens, not underscores - This was causing runtime failures when attempting conversion All changes verified with syntax checks and validation tests.
|
Automated review 🤖 Summary of ChangesThis PR introduces GGUF quantization support using llama.cpp, enabling conversion of HuggingFace models to GGUF format with multiple quantization levels. It adds a new quantization method to the UI, extends configuration with GGUF-specific parameters, and implements a two-step quantization process (conversion + quantization). The Docker image is updated to include llama.cpp binaries, and documentation is enhanced to reflect the new capabilities. Key Changes & Positives
Potential Issues & Recommendations
Language/Framework Checks
Security & Privacy
Build/CI & Ops
Tests
Approval RecommendationApprove with caveats The implementation is well-structured and adds significant value with GGUF support. However, the following items should be addressed before merging:
|
There was a problem hiding this comment.
Pull Request Overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (1)
src/msquant/core/quantizer/engine.py:1
- The availability check for 'convert-hf-to-gguf.py' only verifies that Python is available, not the actual script. The comment on line 250 says 'Check if convert-hf-to-gguf.py is available', but the code checks for Python instead. This means the function will pass even if the conversion script is missing, causing later failures. Add a proper check for the convert-hf-to-gguf.py script using
which convert-hf-to-gguf.pyor by checking if the file exists at the expected path.
"""
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| logger.info(f"Converting model to GGUF {intermediate_format} format...") | ||
|
|
||
| # Find the convert-hf-to-gguf.py script | ||
| convert_script = "/opt/llama.cpp/convert-hf-to-gguf.py" |
There was a problem hiding this comment.
The hardcoded path '/opt/llama.cpp/convert-hf-to-gguf.py' assumes a specific installation location. If the llama.cpp installation path changes or the script is unavailable at this location, the conversion will fail. Consider making this configurable via an environment variable (e.g., LLAMA_CPP_PATH) or detecting the script location dynamically using which convert-hf-to-gguf.py or searching common installation paths.
| result = subprocess.run( | ||
| ["bash", "-lc", "which python"], | ||
| capture_output=True, | ||
| text=True, | ||
| check=False | ||
| ) |
There was a problem hiding this comment.
[nitpick] Using capture_output=True parameter is available, but the pattern is inconsistent with line 262 where both stdout=subprocess.PIPE and stderr=subprocess.STDOUT are used in subprocess.Popen calls. For consistency and clarity, consider using the same approach throughout the class. Note that capture_output=True is equivalent to stdout=subprocess.PIPE, stderr=subprocess.PIPE.
| local_path = snapshot_download( | ||
| repo_id=model_id, | ||
| cache_dir=cache_dir, | ||
| local_dir_use_symlinks=False | ||
| ) |
There was a problem hiding this comment.
The snapshot_download is missing error handling for common failure scenarios like network issues, authentication failures, or invalid model IDs. While there's a general exception catch on line 290, consider adding specific handling for HfHubHTTPError or RepositoryNotFoundError to provide more informative error messages to users about what went wrong.
| ) | ||
|
|
||
| # Clean up intermediate file if different from final | ||
| if intermediate_file != final_file and os.path.exists(intermediate_file): |
There was a problem hiding this comment.
The cleanup logic on line 449 checks if intermediate_file != final_file, but when the quantization type matches the intermediate format (F16 or F32), line 358 uses shutil.copy2(input_file, output_file) which creates a copy. This means when gguf_quant_type and gguf_intermediate_format are the same (e.g., both 'F16'), the intermediate file will be unnecessarily deleted after being copied to the same-named final file. The condition should also check if the files have different names to avoid deleting files that were just copied.
| if intermediate_file != final_file and os.path.exists(intermediate_file): | |
| if os.path.abspath(intermediate_file) != os.path.abspath(final_file) and os.path.exists(intermediate_file): |
|
|
||
| RUN pip install "llmcompressor" | ||
|
|
||
| # Install llama.cpp for GGUF quantization support |
There was a problem hiding this comment.
The LLAMA_CPP_VERSION value 'b6945' appears to be a commit hash or short version identifier, but it's not clear what full version this corresponds to or why this specific version was chosen. Consider adding a comment explaining the version selection rationale (e.g., 'Using b6945 for stability/compatibility with feature X') to help maintainers understand the version choice when updating dependencies.
| # Install llama.cpp for GGUF quantization support | |
| # Install llama.cpp for GGUF quantization support | |
| # Using commit b6945 for stability and compatibility with GGUF quantization as of June 2024. |
| <strong>Recommended:</strong> Q4_K_M (balanced), Q5_K_M (best quality)<br> | ||
| <strong>Intermediate:</strong> f16 (default), f32 (higher precision), q8_0 (smaller) | ||
| </p> | ||
| ''', sanitize=False) |
There was a problem hiding this comment.
Using sanitize=False when rendering HTML disables XSS protection. While the current content is static and safe, this pattern creates a security risk if the content is ever changed to include dynamic data. Consider using sanitize=True (the default) or restructuring to use NiceGUI's native components instead of raw HTML.
| ''', sanitize=False) | |
| ''') |
Implemented comprehensive GGUF quantization support using llama.cpp, enabling conversion of HuggingFace models to GGUF format with multiple quantization levels (Q4_K_M, Q5_K_M, Q6_K, Q8_0, etc.).
Key changes:
Quantization types supported:
GGUF outputs are compatible with llama.cpp, Ollama, and other GGUF-compatible inference engines.