chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import json
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from mlc_llm.support import logging
|
||||
from mlc_llm.support.auto_config import detect_config
|
||||
|
||||
logging.enable_logging()
|
||||
|
||||
# test category "unittest"
|
||||
pytestmark = [pytest.mark.unittest]
|
||||
|
||||
|
||||
def _create_json_file(json_path, data):
|
||||
with open(json_path, "w", encoding="utf-8") as i_f:
|
||||
json.dump(data, i_f)
|
||||
|
||||
|
||||
def test_detect_config():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
base_path = Path(tmpdir)
|
||||
config_json_path = base_path / "config.json"
|
||||
_create_json_file(config_json_path, {})
|
||||
|
||||
assert detect_config(base_path) == config_json_path
|
||||
assert detect_config(config_json_path) == config_json_path
|
||||
|
||||
|
||||
def test_detect_config_fail():
|
||||
with pytest.raises(ValueError):
|
||||
detect_config(Path("do/not/exist"))
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
base_path = Path(tmpdir)
|
||||
with pytest.raises(ValueError):
|
||||
assert detect_config(base_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_detect_config()
|
||||
test_detect_config_fail()
|
||||
@@ -0,0 +1,37 @@
|
||||
import pytest
|
||||
from tvm.target import Target
|
||||
|
||||
from mlc_llm.support.auto_target import _apply_webgpu_subgroups
|
||||
|
||||
# test category "unittest"
|
||||
pytestmark = [pytest.mark.unittest]
|
||||
|
||||
|
||||
def test_apply_webgpu_subgroups_enables_webgpu_target():
|
||||
target = Target("webgpu")
|
||||
|
||||
updated = _apply_webgpu_subgroups(target, True)
|
||||
|
||||
assert updated is not target
|
||||
assert dict(target.export())["supports_subgroups"] is False
|
||||
assert dict(updated.export())["supports_subgroups"] is True
|
||||
|
||||
|
||||
def test_apply_webgpu_subgroups_non_webgpu_target_is_unchanged():
|
||||
target = Target("llvm")
|
||||
|
||||
updated = _apply_webgpu_subgroups(target, True)
|
||||
|
||||
assert updated is target
|
||||
assert dict(updated.export()) == dict(target.export())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("target_kind", ["webgpu", "llvm"])
|
||||
@pytest.mark.parametrize("enable_subgroups", [False, None])
|
||||
def test_apply_webgpu_subgroups_disabled_is_unchanged(target_kind, enable_subgroups):
|
||||
target = Target(target_kind)
|
||||
|
||||
updated = _apply_webgpu_subgroups(target, enable_subgroups)
|
||||
|
||||
assert updated is target
|
||||
assert dict(updated.export()) == dict(target.export())
|
||||
@@ -0,0 +1,150 @@
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from mlc_llm.support import logging
|
||||
from mlc_llm.support.auto_weight import detect_weight
|
||||
|
||||
logging.enable_logging()
|
||||
|
||||
# test category "unittest"
|
||||
pytestmark = [pytest.mark.unittest]
|
||||
|
||||
|
||||
def _create_json_file(json_path, data):
|
||||
with open(json_path, "w", encoding="utf-8") as i_f:
|
||||
json.dump(data, i_f)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"weight_format, index_filename, result",
|
||||
[
|
||||
("huggingface-torch", "pytorch_model.bin.index.json", "huggingface-torch"),
|
||||
(
|
||||
"huggingface-safetensor",
|
||||
"model.safetensors.index.json",
|
||||
"huggingface-safetensor",
|
||||
),
|
||||
("auto", "pytorch_model.bin.index.json", "huggingface-torch"),
|
||||
("auto", "model.safetensors.index.json", "huggingface-safetensor"),
|
||||
],
|
||||
)
|
||||
def test_detect_weight(weight_format, index_filename, result):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
base_path = Path(tmpdir)
|
||||
if index_filename is not None:
|
||||
weight_index_file = base_path / index_filename
|
||||
_create_json_file(weight_index_file, {})
|
||||
assert detect_weight(base_path, None, weight_format) == (
|
||||
weight_index_file,
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"weight_format, index_filename, result",
|
||||
[
|
||||
("huggingface-torch", "pytorch_model.bin.index.json", "huggingface-torch"),
|
||||
(
|
||||
"huggingface-safetensor",
|
||||
"model.safetensors.index.json",
|
||||
"huggingface-safetensor",
|
||||
),
|
||||
("auto", "pytorch_model.bin.index.json", "huggingface-torch"),
|
||||
("auto", "model.safetensors.index.json", "huggingface-safetensor"),
|
||||
],
|
||||
)
|
||||
def test_detect_weight_in_config_json(weight_format, index_filename, result):
|
||||
with (
|
||||
tempfile.TemporaryDirectory() as config_dir,
|
||||
tempfile.TemporaryDirectory() as weight_dir,
|
||||
):
|
||||
config_path = Path(config_dir)
|
||||
weight_path = Path(weight_dir)
|
||||
config_json_path = config_path / "config.json"
|
||||
_create_json_file(config_json_path, {"weight_path": weight_dir})
|
||||
if index_filename is not None:
|
||||
weight_index_file = weight_path / index_filename
|
||||
_create_json_file(weight_index_file, {})
|
||||
|
||||
assert detect_weight(None, config_json_path, weight_format) == (
|
||||
weight_index_file,
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"weight_format, index_filename, result",
|
||||
[
|
||||
("huggingface-torch", "pytorch_model.bin.index.json", "huggingface-torch"),
|
||||
(
|
||||
"huggingface-safetensor",
|
||||
"model.safetensors.index.json",
|
||||
"huggingface-safetensor",
|
||||
),
|
||||
("auto", "pytorch_model.bin.index.json", "huggingface-torch"),
|
||||
("auto", "model.safetensors.index.json", "huggingface-safetensor"),
|
||||
],
|
||||
)
|
||||
def test_detect_weight_same_dir_config_json(weight_format, index_filename, result):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
base_path = Path(tmpdir)
|
||||
config_json_path = base_path / "config.json"
|
||||
_create_json_file(config_json_path, {})
|
||||
if index_filename is not None:
|
||||
weight_index_file = Path(os.path.join(tmpdir, index_filename))
|
||||
_create_json_file(weight_index_file, {})
|
||||
assert detect_weight(None, config_json_path, weight_format) == (
|
||||
weight_index_file,
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
def test_find_weight_fail():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
base_path = Path(tmpdir)
|
||||
with pytest.raises(ValueError):
|
||||
detect_weight(Path("do/not/exist"), base_path, "awq")
|
||||
with pytest.raises(AssertionError):
|
||||
detect_weight(None, Path("do/not/exist"), "awq")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_detect_weight("huggingface-torch", "pytorch_model.bin.index.json", "huggingface-torch")
|
||||
test_detect_weight(
|
||||
"huggingface-safetensor",
|
||||
"model.safetensors.index.json",
|
||||
"huggingface-safetensor",
|
||||
)
|
||||
test_detect_weight("auto", "pytorch_model.bin.index.json", "huggingface-torch")
|
||||
test_detect_weight("auto", "model.safetensors.index.json", "huggingface-safetensor")
|
||||
test_detect_weight_in_config_json(
|
||||
"huggingface-torch", "pytorch_model.bin.index.json", "huggingface-torch"
|
||||
)
|
||||
test_detect_weight_in_config_json(
|
||||
"huggingface-safetensor",
|
||||
"model.safetensors.index.json",
|
||||
"huggingface-safetensor",
|
||||
)
|
||||
test_detect_weight_in_config_json("auto", "pytorch_model.bin.index.json", "huggingface-torch")
|
||||
test_detect_weight_in_config_json(
|
||||
"auto", "model.safetensors.index.json", "huggingface-safetensor"
|
||||
)
|
||||
test_detect_weight_same_dir_config_json(
|
||||
"huggingface-torch", "pytorch_model.bin.index.json", "huggingface-torch"
|
||||
)
|
||||
test_detect_weight_same_dir_config_json(
|
||||
"huggingface-safetensor",
|
||||
"model.safetensors.index.json",
|
||||
"huggingface-safetensor",
|
||||
)
|
||||
test_detect_weight_same_dir_config_json(
|
||||
"auto", "pytorch_model.bin.index.json", "huggingface-torch"
|
||||
)
|
||||
test_detect_weight_same_dir_config_json(
|
||||
"auto", "model.safetensors.index.json", "huggingface-safetensor"
|
||||
)
|
||||
test_find_weight_fail()
|
||||
@@ -0,0 +1,69 @@
|
||||
import json
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from mlc_llm.cli import convert_weight as convert_weight_cli
|
||||
|
||||
pytestmark = [pytest.mark.unittest]
|
||||
|
||||
|
||||
def test_convert_weight_cli_passes_lora_adapter(monkeypatch):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
temp_path = Path(tmp_dir)
|
||||
config_path = temp_path / "config.json"
|
||||
source_dir = temp_path / "source"
|
||||
source_dir.mkdir(parents=True, exist_ok=True)
|
||||
source_index = source_dir / "pytorch_model.bin.index.json"
|
||||
adapter_dir = temp_path / "adapter"
|
||||
adapter_dir.mkdir(parents=True, exist_ok=True)
|
||||
output_dir = temp_path / "output"
|
||||
|
||||
config_path.write_text(json.dumps({}), encoding="utf-8")
|
||||
source_index.write_text(json.dumps({"weight_map": {}}), encoding="utf-8")
|
||||
|
||||
def _fake_detect_device(device):
|
||||
return device
|
||||
|
||||
def _fake_detect_weight(_weight_path, _config_json_path, _weight_format):
|
||||
return source_index, "huggingface-torch"
|
||||
|
||||
def _fake_detect_model_type(_model_type, _config):
|
||||
return "dummy"
|
||||
|
||||
monkeypatch.setattr(convert_weight_cli, "detect_config", Path)
|
||||
monkeypatch.setattr(convert_weight_cli, "detect_device", _fake_detect_device)
|
||||
monkeypatch.setattr(convert_weight_cli, "detect_weight", _fake_detect_weight)
|
||||
monkeypatch.setattr(convert_weight_cli, "detect_model_type", _fake_detect_model_type)
|
||||
monkeypatch.setattr(convert_weight_cli, "MODELS", {"dummy": object()})
|
||||
monkeypatch.setattr(convert_weight_cli, "QUANTIZATION", {"q0f16": object()})
|
||||
|
||||
call_args = {}
|
||||
|
||||
def _fake_convert_weight(**kwargs):
|
||||
call_args.update(kwargs)
|
||||
|
||||
monkeypatch.setattr(convert_weight_cli, "convert_weight", _fake_convert_weight)
|
||||
|
||||
convert_weight_cli.main(
|
||||
[
|
||||
str(config_path),
|
||||
"--quantization",
|
||||
"q0f16",
|
||||
"--model-type",
|
||||
"dummy",
|
||||
"--source",
|
||||
str(source_dir),
|
||||
"--source-format",
|
||||
"auto",
|
||||
"--output",
|
||||
str(output_dir),
|
||||
"--lora-adapter",
|
||||
str(adapter_dir),
|
||||
]
|
||||
)
|
||||
|
||||
assert call_args["lora_adapter"] == adapter_dir
|
||||
assert call_args["source"] == source_index
|
||||
assert call_args["source_format"] == "huggingface-torch"
|
||||
@@ -0,0 +1,108 @@
|
||||
import contextlib
|
||||
import json
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from mlc_llm.interface import convert_weight as convert_weight_interface
|
||||
|
||||
pytestmark = [pytest.mark.unittest]
|
||||
|
||||
|
||||
def test_resolve_base_model_dir():
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
temp_path = Path(tmp_dir)
|
||||
model_dir = temp_path / "model"
|
||||
model_dir.mkdir(parents=True, exist_ok=True)
|
||||
source_file = model_dir / "pytorch_model.bin.index.json"
|
||||
source_file.write_text(json.dumps({"weight_map": {}}), encoding="utf-8")
|
||||
|
||||
assert convert_weight_interface._resolve_base_model_dir(model_dir) == model_dir
|
||||
assert convert_weight_interface._resolve_base_model_dir(source_file) == model_dir
|
||||
|
||||
|
||||
def test_convert_weight_with_lora_uses_merged_source(monkeypatch):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
temp_path = Path(tmp_dir)
|
||||
config_path = temp_path / "config.json"
|
||||
config_path.write_text(json.dumps({}), encoding="utf-8")
|
||||
|
||||
source_dir = temp_path / "source"
|
||||
source_dir.mkdir(parents=True, exist_ok=True)
|
||||
source_file = source_dir / "pytorch_model.bin.index.json"
|
||||
source_file.write_text(json.dumps({"weight_map": {}}), encoding="utf-8")
|
||||
|
||||
adapter_dir = temp_path / "adapter"
|
||||
adapter_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
merged_dir = temp_path / "merged"
|
||||
merged_dir.mkdir(parents=True, exist_ok=True)
|
||||
merged_file = merged_dir / "pytorch_model.bin"
|
||||
merged_file.write_bytes(b"")
|
||||
|
||||
captured = {}
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _fake_merge(base_source: Path, lora_adapter: Path):
|
||||
captured["merge_base_source"] = base_source
|
||||
captured["merge_lora_adapter"] = lora_adapter
|
||||
yield merged_dir
|
||||
|
||||
def _fake_detect_weight(weight_path: Path, config_json_path: Path, weight_format: str):
|
||||
captured["detect_weight_path"] = weight_path
|
||||
captured["detect_weight_config"] = config_json_path
|
||||
captured["detect_weight_format"] = weight_format
|
||||
return merged_file, "huggingface-torch"
|
||||
|
||||
def _fake_convert_args(args):
|
||||
captured["converted_args"] = args
|
||||
|
||||
monkeypatch.setattr(
|
||||
convert_weight_interface, "_merge_lora_adapter_with_base_model", _fake_merge
|
||||
)
|
||||
monkeypatch.setattr(convert_weight_interface, "detect_weight", _fake_detect_weight)
|
||||
monkeypatch.setattr(convert_weight_interface, "_convert_args", _fake_convert_args)
|
||||
monkeypatch.setattr(convert_weight_interface.ConversionArgs, "display", lambda self: None)
|
||||
|
||||
convert_weight_interface.convert_weight(
|
||||
config=config_path,
|
||||
quantization=object(),
|
||||
model=type("DummyModel", (), {"name": "dummy"})(),
|
||||
device=object(),
|
||||
source=source_file,
|
||||
source_format="huggingface-safetensor",
|
||||
output=temp_path / "output",
|
||||
lora_adapter=adapter_dir,
|
||||
)
|
||||
|
||||
converted_args = captured["converted_args"]
|
||||
assert captured["merge_base_source"] == source_file
|
||||
assert captured["merge_lora_adapter"] == adapter_dir
|
||||
assert captured["detect_weight_path"] == merged_dir
|
||||
assert captured["detect_weight_config"] == config_path
|
||||
assert captured["detect_weight_format"] == "auto"
|
||||
assert converted_args.source == merged_file
|
||||
assert converted_args.source_format == "huggingface-torch"
|
||||
assert converted_args.lora_adapter == adapter_dir
|
||||
|
||||
|
||||
def test_convert_weight_with_lora_rejects_awq():
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
temp_path = Path(tmp_dir)
|
||||
config_path = temp_path / "config.json"
|
||||
config_path.write_text(json.dumps({}), encoding="utf-8")
|
||||
adapter_dir = temp_path / "adapter"
|
||||
adapter_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with pytest.raises(ValueError, match="only supports source formats"):
|
||||
convert_weight_interface.convert_weight(
|
||||
config=config_path,
|
||||
quantization=object(),
|
||||
model=type("DummyModel", (), {"name": "dummy"})(),
|
||||
device=object(),
|
||||
source=temp_path / "source",
|
||||
source_format="awq",
|
||||
output=temp_path / "output",
|
||||
lora_adapter=adapter_dir,
|
||||
)
|
||||
Reference in New Issue
Block a user