feat: support GGUF export for non-PEFT models + fix venv_t5 switching for local checkpoints (#4455)
* feat: support full model GGUF export, disable incompatible methods in UI * fix: resolve base model from config.json for venv_t5 export switching * feat: detect BNB-quantized models and disable all export methods for quantized non-PEFT checkpoints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: relocate Ollama Modelfile alongside GGUFs during non-PEFT export cleanup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
be901ecdea
commit
ebe45981dd
10 changed files with 362 additions and 14 deletions
|
|
@ -76,6 +76,18 @@ def scan_checkpoints(
|
|||
elif config_file.exists():
|
||||
cfg = json.loads(config_file.read_text())
|
||||
metadata["base_model"] = cfg.get("_name_or_path")
|
||||
|
||||
# Detect BNB quantization from config.json (present in both cases)
|
||||
if config_file.exists():
|
||||
if "cfg" not in dir():
|
||||
cfg = json.loads(config_file.read_text())
|
||||
quant_cfg = cfg.get("quantization_config")
|
||||
if (
|
||||
isinstance(quant_cfg, dict)
|
||||
and quant_cfg.get("quant_method") == "bitsandbytes"
|
||||
):
|
||||
metadata["is_quantized"] = True
|
||||
logger.info("Detected BNB-quantized model: %s", item.name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,24 @@ def _resolve_base_model(model_name: str) -> str:
|
|||
except Exception as exc:
|
||||
logger.debug("Could not read %s: %s", adapter_cfg_path, exc)
|
||||
|
||||
# --- config.json fallback (works for both LoRA and full fine-tune) ------
|
||||
config_json_path = local_path / "config.json"
|
||||
if config_json_path.is_file():
|
||||
try:
|
||||
with open(config_json_path) as f:
|
||||
cfg = json.load(f)
|
||||
# Unsloth writes "model_name"; HF writes "_name_or_path"
|
||||
base = cfg.get("model_name") or cfg.get("_name_or_path")
|
||||
if base and base != str(local_path):
|
||||
logger.info(
|
||||
"Resolved checkpoint '%s' → base model '%s' (via config.json)",
|
||||
model_name,
|
||||
base,
|
||||
)
|
||||
return base
|
||||
except Exception as exc:
|
||||
logger.debug("Could not read %s: %s", config_json_path, exc)
|
||||
|
||||
# --- Only try the heavier fallback for local directories ----------------
|
||||
if local_path.is_dir():
|
||||
try:
|
||||
|
|
@ -126,6 +144,27 @@ def _check_tokenizer_config_needs_v5(model_name: str) -> bool:
|
|||
if model_name in _tokenizer_class_cache:
|
||||
return _tokenizer_class_cache[model_name]
|
||||
|
||||
# --- Check local tokenizer_config.json first ---------------------------
|
||||
local_path = Path(model_name)
|
||||
local_tc = local_path / "tokenizer_config.json"
|
||||
if local_tc.is_file():
|
||||
try:
|
||||
with open(local_tc) as f:
|
||||
data = json.load(f)
|
||||
tokenizer_class = data.get("tokenizer_class", "")
|
||||
result = tokenizer_class in _TRANSFORMERS_5_TOKENIZER_CLASSES
|
||||
if result:
|
||||
logger.info(
|
||||
"Local check: %s uses tokenizer_class=%s (requires transformers 5.x)",
|
||||
model_name,
|
||||
tokenizer_class,
|
||||
)
|
||||
_tokenizer_class_cache[model_name] = result
|
||||
return result
|
||||
except Exception as exc:
|
||||
logger.debug("Could not read %s: %s", local_tc, exc)
|
||||
|
||||
# --- Fall back to fetching from HuggingFace ----------------------------
|
||||
import urllib.request
|
||||
|
||||
url = f"https://huggingface.co/{model_name}/raw/main/tokenizer_config.json"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue