95 lines
3.5 KiB
Python
95 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Direct SafeTensors to GGUF converter for unsupported architectures.
|
|
|
|
This script attempts to convert SafeTensors models to GGUF format directly,
|
|
without relying on llama.cpp's architecture-specific conversion logic.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import traceback
|
|
from argparse import ArgumentParser
|
|
from pathlib import Path
|
|
|
|
from helpers.logger import logger
|
|
from helpers.services.gguf import GGUFConverter
|
|
from helpers.utils.config_parser import ConfigParser
|
|
from helpers.utils.tensor_mapping import TensorMapper
|
|
|
|
|
|
def convert_safetensors_to_gguf(
|
|
model_path: Path, output_path: Path, force_architecture: str | None = None
|
|
) -> bool:
|
|
"""Convert SafeTensors model to GGUF format with comprehensive metadata handling.
|
|
|
|
Orchestrates the complete conversion workflow: loads configuration, maps
|
|
architecture to known GGUF types, creates writer with proper metadata,
|
|
processes all tensor files with name mapping, and adds tokeniser data.
|
|
Handles BFloat16 conversion and provides fallback architecture mapping
|
|
for unsupported model types to ensure maximum compatibility.
|
|
|
|
Returns:
|
|
True if conversion was successful, False otherwise.
|
|
"""
|
|
# Use ConfigParser to load configuration
|
|
config_parser = ConfigParser()
|
|
model_config = config_parser.load_model_config(model_path)
|
|
|
|
arch_name = model_config.architectures[0]
|
|
model_type = model_config.model_type
|
|
|
|
logger.info(f"Architecture: {arch_name}")
|
|
logger.info(f"Model type: {model_type}")
|
|
|
|
# Use forced architecture or try to map to a known one
|
|
if force_architecture:
|
|
arch = force_architecture
|
|
logger.warning(f"Using forced architecture: {arch}")
|
|
else:
|
|
# Use ConfigParser's architecture mapping
|
|
arch = config_parser.get_architecture_mapping(arch_name)
|
|
if arch != arch_name:
|
|
logger.warning(f"Unknown architecture {arch_name}, using {arch} as fallback")
|
|
|
|
# Use the new GGUFConverter for the conversion
|
|
tensor_mapper = TensorMapper()
|
|
return GGUFConverter.convert_safetensors(
|
|
model_path, output_path, model_config, arch, tensor_mapper
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
"""Main entry point for SafeTensors to GGUF conversion command-line interface.
|
|
|
|
Parses command-line arguments, validates input paths, and orchestrates the
|
|
conversion process with proper error handling. Supports forced architecture
|
|
mapping and flexible output path specification. Provides comprehensive
|
|
error reporting and exit codes for integration with automated workflows.
|
|
"""
|
|
parser = ArgumentParser(description="Convert SafeTensors to GGUF directly")
|
|
parser.add_argument("model_path", help="Path to SafeTensors model directory")
|
|
parser.add_argument("-o", "--output", help="Output GGUF file path")
|
|
parser.add_argument("--force-arch", help="Force a specific architecture mapping")
|
|
|
|
args = parser.parse_args()
|
|
|
|
model_path = Path(args.model_path)
|
|
if not model_path.exists():
|
|
logger.error(f"Model path not found: {model_path}")
|
|
sys.exit(1)
|
|
|
|
output_path = Path(args.output) if args.output else model_path / f"{model_path.name}-f32.gguf"
|
|
|
|
try:
|
|
success = convert_safetensors_to_gguf(model_path, output_path, args.force_arch)
|
|
sys.exit(0 if success else 1)
|
|
except Exception as e:
|
|
logger.error(f"Conversion failed: {e}")
|
|
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|