Files
unslothai--unsloth/tests/saving/test_fix_sentencepiece_gguf_robustness.py
T
wehub-resource-sync e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:59:56 +08:00

128 lines
4.3 KiB
Python

import ast
import json
import os
os.environ.setdefault("PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION", "python")
from transformers.utils import sentencepiece_model_pb2
from unsloth.tokenizer_utils import fix_sentencepiece_gguf
NORMAL, CONTROL, USER_DEFINED = 1, 3, 4
_SAVE_PY = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..", "unsloth", "save.py")
)
_TOK_PY = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..", "unsloth", "tokenizer_utils.py")
)
def _build(pieces):
m = sentencepiece_model_pb2.ModelProto()
for piece, score, typ in pieces:
p = m.pieces.add()
p.piece = piece
p.score = score
p.type = typ
return m.SerializeToString()
def _read(path):
m = sentencepiece_model_pb2.ModelProto()
with open(path, "rb") as f:
m.ParseFromString(f.read())
return [(p.piece, p.type) for p in m.pieces]
def test_user_defined_special_piece_is_not_retyped(tmp_path):
pieces = [
("<s>", 0.0, CONTROL),
("a", -1.0, NORMAL),
("<ud_special>", -1.0, USER_DEFINED),
]
(tmp_path / "tokenizer.model").write_bytes(_build(pieces))
(tmp_path / "tokenizer.json").write_text(
json.dumps({"added_tokens": [{"id": 2, "content": "<ud_special>", "special": True}]})
)
fix_sentencepiece_gguf(str(tmp_path))
got = dict(_read(str(tmp_path / "tokenizer.model")))
assert got["<ud_special>"] == USER_DEFINED
def test_malformed_entry_missing_id_does_not_raise(tmp_path):
pieces = [("<s>", 0.0, CONTROL), ("a", -1.0, NORMAL), ("<sot>", -1.0, NORMAL)]
(tmp_path / "tokenizer.model").write_bytes(_build(pieces))
(tmp_path / "tokenizer.json").write_text(
json.dumps(
{
"added_tokens": [
{"content": "no_id_entry", "special": True},
{"id": 2, "content": "<sot>", "special": True},
]
}
)
)
fix_sentencepiece_gguf(str(tmp_path))
got = dict(_read(str(tmp_path / "tokenizer.model")))
assert got["<sot>"] == CONTROL
def test_entry_with_non_int_id_is_skipped(tmp_path):
pieces = [("<s>", 0.0, CONTROL), ("a", -1.0, NORMAL)]
(tmp_path / "tokenizer.model").write_bytes(_build(pieces))
(tmp_path / "tokenizer.json").write_text(
json.dumps({"added_tokens": [{"id": "oops", "content": "x", "special": True}]})
)
before = (tmp_path / "tokenizer.model").read_bytes()
fix_sentencepiece_gguf(str(tmp_path))
after = (tmp_path / "tokenizer.model").read_bytes()
assert before == after
def test_save_py_except_clause_is_broad_exception():
with open(_SAVE_PY) as f:
tree = ast.parse(f.read())
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and node.name == "unsloth_save_pretrained_gguf":
for subnode in ast.walk(node):
if isinstance(subnode, ast.Try):
body_src = "\n".join(ast.unparse(s) for s in subnode.body)
if "fix_sentencepiece_gguf(" not in body_src:
continue
handler = subnode.handlers[0]
assert handler.type is not None
assert isinstance(handler.type, ast.Name)
assert handler.type.id == "Exception"
return
raise AssertionError(
"fix_sentencepiece_gguf try block not found in unsloth_save_pretrained_gguf"
)
def test_tokenizer_utils_uses_import_protobuf_fallback_pattern():
with open(_TOK_PY) as f:
src = f.read()
tree = ast.parse(src)
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and node.name == "fix_sentencepiece_gguf":
fn_src = ast.unparse(node)
assert "import_protobuf" in fn_src
return
raise AssertionError("fix_sentencepiece_gguf not found in tokenizer_utils.py")
def test_all_special_tokens_are_gated_by_tokenizer_json_not_by_type(tmp_path):
pieces = [
("<s>", 0.0, CONTROL),
("a", -1.0, NORMAL),
("<n1>", -1.0, NORMAL),
("<u1>", -1.0, USER_DEFINED),
]
(tmp_path / "tokenizer.model").write_bytes(_build(pieces))
fix_sentencepiece_gguf(str(tmp_path))
got = dict(_read(str(tmp_path / "tokenizer.model")))
assert got["<n1>"] == NORMAL
assert got["<u1>"] == USER_DEFINED