From bfb140303277bafe5ba1fade10586be8e55dd1ec Mon Sep 17 00:00:00 2001 From: Roland Tannous <115670425+rolandtannous@users.noreply.github.com> Date: Wed, 25 Feb 2026 18:54:39 +0400 Subject: [PATCH] Relocate GGUF exports into exports/ directory --- studio/backend/core/export/export.py | 33 +++++++++++++++++-- .../src/features/export/export-page.tsx | 7 +++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/studio/backend/core/export/export.py b/studio/backend/core/export/export.py index cb2cb01c22..079ea0277d 100644 --- a/studio/backend/core/export/export.py +++ b/studio/backend/core/export/export.py @@ -2,9 +2,11 @@ """ Export backend - handles model exporting in various formats """ +import glob import json import logging import os +import shutil from pathlib import Path from typing import Optional, Tuple, List from peft import PeftModel, PeftModelForCausalLM @@ -409,9 +411,15 @@ class ExportBackend: # On WSL, patch out sudo check before llama.cpp build _apply_wsl_sudo_patch() + # Snapshot existing .gguf files in cwd before conversion. + # unsloth's convert_to_gguf writes output files relative to + # cwd (repo root), so we diff afterwards and relocate them. + cwd = os.getcwd() + pre_existing_ggufs = set(glob.glob(os.path.join(cwd, "*.gguf"))) + # Pass absolute path — no os.chdir needed. - # unsloth saves model files into this directory, while - # check_llama_cpp("llama.cpp") resolves against cwd (repo root) + # unsloth saves intermediate HF model files into model_save_path, + # while check_llama_cpp("llama.cpp") resolves against cwd (repo root) # where setup.sh already built llama.cpp with quantizer. model_save_path = os.path.join(abs_save_dir, "model") self.current_model.save_pretrained_gguf( @@ -420,6 +428,27 @@ class ExportBackend: quantization_method=quant_method ) + # Relocate GGUF artifacts into the export directory. + # convert_to_gguf writes .gguf files to cwd (repo root) + # because --outfile is a relative path like "model.Q4_K_M.gguf". + new_ggufs = set(glob.glob(os.path.join(cwd, "*.gguf"))) - pre_existing_ggufs + for src in sorted(new_ggufs): + dest = os.path.join(abs_save_dir, os.path.basename(src)) + shutil.move(src, dest) + logger.info(f"Relocated GGUF: {os.path.basename(src)} → {abs_save_dir}/") + + # Also check model_save_path for any .gguf files + if os.path.isdir(model_save_path): + for src in glob.glob(os.path.join(model_save_path, "*.gguf")): + dest = os.path.join(abs_save_dir, os.path.basename(src)) + shutil.move(src, dest) + logger.info(f"Relocated GGUF: {os.path.basename(src)} → {abs_save_dir}/") + + # Clean up intermediate HF model files (safetensors, config, etc.) + # since we only need the final .gguf output + shutil.rmtree(model_save_path, ignore_errors=True) + logger.info("Cleaned up intermediate HF model files") + logger.info(f"GGUF model saved successfully in {abs_save_dir}") # Push to hub if requested diff --git a/studio/frontend/src/features/export/export-page.tsx b/studio/frontend/src/features/export/export-page.tsx index 9d3e5053de..fa43c5eb98 100644 --- a/studio/frontend/src/features/export/export-page.tsx +++ b/studio/frontend/src/features/export/export-page.tsx @@ -155,7 +155,12 @@ export function ExportPage() { setExportError(null); setExportSuccess(false); - const saveDir = `./exports/${selectedModelIdx ?? "model"}/${checkpoint}`; + // For GGUF, use a flat folder like "exports/gemma-3-4b-it-finetune-gguf" + // For other formats, nest under training-run/checkpoint + const saveDir = + exportMethod === "gguf" + ? `./exports/${(baseModelName.split("/").pop() ?? selectedModelIdx ?? "model")}-finetune-gguf` + : `./exports/${selectedModelIdx ?? "model"}/${checkpoint}`; const pushToHub = destination === "hub"; const repoId = pushToHub && hfUsername && modelName ? `${hfUsername}/${modelName}`