chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:22:34 +08:00
commit 4b22cfda96
9037 changed files with 2363717 additions and 0 deletions
@@ -0,0 +1,723 @@
import importlib.util
import json
from pathlib import Path
import numpy as np
import pytest
import yaml
pytest.importorskip("diffusers")
pytest.importorskip("safetensors")
from unittest.mock import MagicMock, Mock, patch
from safetensors.numpy import save_file
import mlflow
import mlflow.diffusers
from mlflow.diffusers import FLAVOR_NAME, DiffusersAdapterModel
from mlflow.exceptions import MlflowException
from mlflow.models.model import MLMODEL_FILE_NAME
BASE_MODEL_ID = "stabilityai/stable-diffusion-xl-base-1.0"
def _create_fake_adapter(tmp_path, filename="adapter.safetensors"):
adapter_dir = tmp_path / "fake_adapter"
adapter_dir.mkdir(exist_ok=True)
tensors = {"lora_weight": np.random.randn(4, 4).astype(np.float32)}
save_file(tensors, str(adapter_dir / filename))
return adapter_dir
@pytest.fixture
def adapter_dir(tmp_path):
return _create_fake_adapter(tmp_path)
@pytest.fixture
def adapter_file(tmp_path):
adapter_dir = _create_fake_adapter(tmp_path)
return adapter_dir / "adapter.safetensors"
@pytest.fixture
def model_path(tmp_path):
return tmp_path / "model_output"
def test_save_model_from_directory(adapter_dir, model_path):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
mlmodel_path = model_path / MLMODEL_FILE_NAME
assert mlmodel_path.exists()
with open(mlmodel_path) as f:
mlmodel = yaml.safe_load(f)
assert FLAVOR_NAME in mlmodel["flavors"]
assert "python_function" in mlmodel["flavors"]
flavor_conf = mlmodel["flavors"][FLAVOR_NAME]
assert flavor_conf["base_model"] == BASE_MODEL_ID
assert flavor_conf["adapter_type"] == "lora"
assert flavor_conf["adapter_weights"] == "adapter_weights"
def test_save_model_normalizes_single_file_input(adapter_file, model_path):
mlflow.diffusers.save_model(
adapter_path=str(adapter_file),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
weights_dir = model_path / "adapter_weights"
assert weights_dir.exists()
assert (weights_dir / "pytorch_lora_weights.safetensors").exists()
def test_save_model_normalizes_single_file_directory(adapter_dir, model_path):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
weights_dir = model_path / "adapter_weights"
assert weights_dir.exists()
assert (weights_dir / "pytorch_lora_weights.safetensors").exists()
def test_save_model_writes_environment_files(adapter_dir, model_path):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
assert (model_path / "conda.yaml").exists()
assert (model_path / "requirements.txt").exists()
assert (model_path / "python_env.yaml").exists()
def test_save_model_default_signature(adapter_dir, model_path):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
with open(model_path / MLMODEL_FILE_NAME) as f:
mlmodel = yaml.safe_load(f)
assert "signature" in mlmodel
sig = mlmodel["signature"]
inputs = json.loads(sig["inputs"])
assert inputs[0]["name"] == "prompt"
assert inputs[0]["type"] == "string"
def test_save_model_custom_signature(adapter_dir, model_path):
from mlflow.types import DataType, Schema
from mlflow.types.schema import ColSpec
custom_sig = mlflow.models.ModelSignature(
inputs=Schema([ColSpec(type=DataType.string, name="text")]),
outputs=Schema([ColSpec(type=DataType.binary, name="img")]),
)
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
signature=custom_sig,
)
with open(model_path / MLMODEL_FILE_NAME) as f:
mlmodel = yaml.safe_load(f)
inputs = json.loads(mlmodel["signature"]["inputs"])
assert inputs[0]["name"] == "text"
def test_save_model_invalid_adapter_type(adapter_dir, model_path):
with pytest.raises(mlflow.exceptions.MlflowException, match="Unsupported adapter type"):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
adapter_type="invalid",
)
def test_save_model_nonexistent_path(model_path):
with pytest.raises(mlflow.exceptions.MlflowException, match="does not exist"):
mlflow.diffusers.save_model(
adapter_path="/nonexistent/path",
path=str(model_path),
base_model=BASE_MODEL_ID,
)
def test_save_model_with_metadata(adapter_dir, model_path):
test_metadata = {"training_dataset": "my-dataset", "lora_rank": 16}
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
metadata=test_metadata,
)
with open(model_path / MLMODEL_FILE_NAME) as f:
mlmodel = yaml.safe_load(f)
assert mlmodel["metadata"] == test_metadata
def test_log_model(adapter_dir):
with mlflow.start_run() as run:
model_info = mlflow.diffusers.log_model(
adapter_path=str(adapter_dir),
base_model=BASE_MODEL_ID,
name="test_adapter",
)
assert model_info is not None
client = mlflow.MlflowClient()
artifacts = [a.path for a in client.list_artifacts(run.info.run_id, "test_adapter")]
assert any("adapter_weights" in a for a in artifacts)
assert any(MLMODEL_FILE_NAME in a for a in artifacts)
def test_save_load_model_direct_roundtrip(adapter_dir, model_path):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
loaded = mlflow.diffusers.load_model(str(model_path))
assert isinstance(loaded, DiffusersAdapterModel)
assert loaded.base_model == BASE_MODEL_ID
assert loaded.adapter_type == "lora"
assert Path(loaded.adapter_path).exists()
def test_base_model_revision_roundtrip(adapter_dir, model_path):
fake_revision = "abc123def456"
with patch("mlflow.diffusers._resolve_base_model_revision", return_value=fake_revision):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
# Verify revision stored in flavor config
mlmodel_path = model_path / MLMODEL_FILE_NAME
with open(mlmodel_path) as f:
mlmodel = yaml.safe_load(f)
assert mlmodel["flavors"][FLAVOR_NAME]["base_model_revision"] == fake_revision
# Verify revision survives load_model roundtrip
loaded = mlflow.diffusers.load_model(str(model_path))
assert loaded.base_model_revision == fake_revision
def test_load_model_via_tracking_roundtrip(adapter_dir):
with mlflow.start_run() as run:
mlflow.diffusers.log_model(
adapter_path=str(adapter_dir),
base_model=BASE_MODEL_ID,
name="test_adapter",
)
model_uri = f"runs:/{run.info.run_id}/test_adapter"
loaded = mlflow.diffusers.load_model(model_uri)
assert isinstance(loaded, DiffusersAdapterModel)
assert loaded.base_model == BASE_MODEL_ID
assert loaded.adapter_type == "lora"
assert Path(loaded.adapter_path).exists()
assert (Path(loaded.adapter_path) / "pytorch_lora_weights.safetensors").exists()
def test_load_pyfunc_returns_wrapper(adapter_dir):
from mlflow.diffusers.wrapper import _DiffusersAdapterWrapper
with mlflow.start_run() as run:
mlflow.diffusers.log_model(
adapter_path=str(adapter_dir),
base_model=BASE_MODEL_ID,
name="test_adapter",
)
model_uri = f"runs:/{run.info.run_id}/test_adapter"
loaded_pyfunc = mlflow.pyfunc.load_model(model_uri)
assert hasattr(loaded_pyfunc, "predict")
wrapper = loaded_pyfunc._model_impl
assert isinstance(wrapper, _DiffusersAdapterWrapper)
assert wrapper._flavor_conf["base_model"] == BASE_MODEL_ID
def test_load_pipeline_base_model_override(adapter_dir, model_path):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
loaded = mlflow.diffusers.load_model(str(model_path))
override = "other-org/other-model"
with patch("diffusers.DiffusionPipeline.from_pretrained") as mock_fp:
mock_pipe = MagicMock()
mock_fp.return_value = mock_pipe
loaded.load_pipeline(base_model=override)
mock_fp.assert_called_once()
assert mock_fp.call_args[0][0] == override
def test_load_pipeline_wraps_oserror(adapter_dir, model_path):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
loaded = mlflow.diffusers.load_model(str(model_path))
with patch(
"diffusers.DiffusionPipeline.from_pretrained",
side_effect=OSError("is not a local folder"),
):
with pytest.raises(MlflowException, match="Failed to load base model"):
loaded.load_pipeline()
def test_wrapper_model_config_base_model_override():
from mlflow.diffusers.wrapper import _DiffusersAdapterWrapper
w = _DiffusersAdapterWrapper(
adapter_path="/fake",
flavor_conf={"base_model": "original/model", "adapter_type": "lora"},
model_config={"base_model": "override/model"},
)
with patch("diffusers.DiffusionPipeline.from_pretrained") as mock_fp:
mock_pipe = MagicMock()
mock_fp.return_value = mock_pipe
w._load_pipeline()
mock_fp.assert_called_once()
assert mock_fp.call_args[0][0] == "override/model"
@pytest.mark.parametrize("package", ["diffusers", "transformers", "torch", "peft", "safetensors"])
def test_default_pip_requirements_contains_core(package):
reqs = mlflow.diffusers.get_default_pip_requirements()
req_names = [r.split("==")[0] for r in reqs]
assert package in req_names
@pytest.mark.parametrize("package", ["accelerate"])
def test_default_pip_requirements_contains_optional_if_installed(package):
reqs = mlflow.diffusers.get_default_pip_requirements()
req_names = [r.split("==")[0] for r in reqs]
if importlib.util.find_spec(package):
assert package in req_names
else:
assert package not in req_names
def test_default_conda_env():
env = mlflow.diffusers.get_default_conda_env()
assert "dependencies" in env
# -- predict() tests (mock-based, no GPU) --
class _FakeImage:
def save(self, buf, format=None):
buf.write(b"\x89PNG\r\n\x1a\n" + b"\x00" * 100)
class _FakePipelineOutput:
def __init__(self, n=1):
self.images = [_FakeImage() for _ in range(n)]
@pytest.fixture
def wrapper():
from mlflow.diffusers.wrapper import _DiffusersAdapterWrapper
w = _DiffusersAdapterWrapper(
adapter_path="/fake",
flavor_conf={"base_model": "test/model", "adapter_type": "lora"},
)
# Inject a mock pipeline so _load_pipeline is never called
w._pipeline = lambda prompt, **kwargs: _FakePipelineOutput(len(prompt))
return w
def test_get_raw_model(wrapper):
model = wrapper.get_raw_model()
assert model is not None
assert callable(model)
def test_predict_dataframe(wrapper):
import pandas as pd
result = wrapper.predict(pd.DataFrame({"prompt": ["a cat", "a dog"]}))
assert len(result) == 2
assert all(isinstance(r, bytes) for r in result)
def test_predict_string(wrapper):
result = wrapper.predict("a cat")
assert len(result) == 1
assert result[0][:4] == b"\x89PNG"
def test_predict_dict(wrapper):
result = wrapper.predict({"prompt": "a cat"})
assert len(result) == 1
def test_predict_dict_list(wrapper):
result = wrapper.predict({"prompt": ["a cat", "a dog"]})
assert len(result) == 2
def test_predict_list(wrapper):
result = wrapper.predict(["a cat", "a dog"])
assert len(result) == 2
def test_predict_with_params(wrapper):
mock_pipeline = Mock(return_value=_FakePipelineOutput(1))
wrapper._pipeline = mock_pipeline
result = wrapper.predict(
"a cat",
params={"num_inference_steps": 10, "height": 256, "negative_prompt": "blurry"},
)
assert len(result) == 1
mock_pipeline.assert_called_once()
call_kwargs = mock_pipeline.call_args.kwargs
assert call_kwargs["num_inference_steps"] == 10
assert call_kwargs["height"] == 256
assert call_kwargs["negative_prompt"] == "blurry"
assert call_kwargs["prompt"] == ["a cat"]
def test_predict_dataframe_missing_prompt(wrapper):
import pandas as pd
with pytest.raises(MlflowException, match="prompt"):
wrapper.predict(pd.DataFrame({"text": ["a cat"], "other": ["extra"]}))
def test_predict_dict_missing_prompt(wrapper):
with pytest.raises(MlflowException, match="prompt"):
wrapper.predict({"text": "a cat"})
def test_predict_empty_prompts(wrapper):
with pytest.raises(MlflowException, match="No prompts"):
wrapper.predict([])
def test_predict_dict_invalid_prompt_type(wrapper):
with pytest.raises(MlflowException, match="must be a string or list of strings"):
wrapper.predict({"prompt": 42})
def test_predict_unsupported_type(wrapper):
with pytest.raises(MlflowException, match="Unsupported input type"):
wrapper.predict(12345)
def test_predict_no_images(wrapper):
class _NoImagesOutput:
images = None
wrapper._pipeline = lambda prompt, **kwargs: _NoImagesOutput()
with pytest.raises(MlflowException, match="Pipeline returned no images"):
wrapper.predict("a cat")
def test_predict_empty_images_list(wrapper):
class _EmptyImagesOutput:
images = []
wrapper._pipeline = lambda prompt, **kwargs: _EmptyImagesOutput()
with pytest.raises(MlflowException, match="Pipeline returned no images"):
wrapper.predict("a cat")
def test_predict_none_prompt_rejected(wrapper):
with pytest.raises(MlflowException, match="must be strings, not None"):
wrapper.predict([None])
def test_predict_none_in_dataframe_rejected(wrapper):
import pandas as pd
with pytest.raises(MlflowException, match="must be strings, not None"):
wrapper.predict(pd.DataFrame({"prompt": [None]}))
# -- _resolve_base_model_revision tests --
def test_resolve_revision_absolute_path_returns_none():
from mlflow.diffusers import _resolve_base_model_revision
assert _resolve_base_model_revision("/absolute/path/to/model") is None
def test_resolve_revision_dot_relative_path_returns_none():
from mlflow.diffusers import _resolve_base_model_revision
assert _resolve_base_model_revision("./local/model") is None
assert _resolve_base_model_revision("../parent/model") is None
def test_resolve_revision_hf_hub_success():
from mlflow.diffusers import _resolve_base_model_revision
fake_sha = "abc123"
with patch(
"mlflow.utils.huggingface_utils.get_latest_commit_for_repo",
return_value=fake_sha,
):
result = _resolve_base_model_revision("org/model-name")
assert result == fake_sha
def test_resolve_revision_hf_hub_failure_returns_none():
from mlflow.diffusers import _resolve_base_model_revision
with patch(
"mlflow.utils.huggingface_utils.get_latest_commit_for_repo",
side_effect=Exception("network error"),
):
result = _resolve_base_model_revision("org/model-name")
assert result is None
def test_resolve_revision_cwd_collision_still_resolves(tmp_path, monkeypatch):
from mlflow.diffusers import _resolve_base_model_revision
(tmp_path / "org" / "model").mkdir(parents=True)
monkeypatch.chdir(tmp_path)
fake_sha = "deadbeef"
with patch(
"mlflow.utils.huggingface_utils.get_latest_commit_for_repo",
return_value=fake_sha,
):
result = _resolve_base_model_revision("org/model")
assert result == fake_sha
def test_save_model_multi_file_directory_preserved(tmp_path, model_path):
adapter_dir = tmp_path / "multi_adapter"
adapter_dir.mkdir()
tensors = {"w": np.random.randn(4, 4).astype(np.float32)}
save_file(tensors, str(adapter_dir / "pytorch_lora_weights.safetensors"))
# Companion file (e.g., text encoder LoRA or adapter_config from PEFT)
(adapter_dir / "adapter_config.json").write_text('{"type": "lora"}')
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
weights_dir = model_path / "adapter_weights"
assert (weights_dir / "pytorch_lora_weights.safetensors").exists()
assert (weights_dir / "adapter_config.json").exists()
def test_save_model_records_weight_name_for_nonstandard_files(tmp_path, model_path):
adapter_dir = tmp_path / "multi_nonstandard"
adapter_dir.mkdir()
tensors = {"w": np.random.randn(4, 4).astype(np.float32)}
save_file(tensors, str(adapter_dir / "alpha_weights.safetensors"))
save_file(tensors, str(adapter_dir / "beta_weights.safetensors"))
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
mlmodel = yaml.safe_load((model_path / MLMODEL_FILE_NAME).read_text())
flavor_conf = mlmodel["flavors"]["diffusers"]
assert flavor_conf["weight_name"] == "alpha_weights.safetensors"
loaded = mlflow.diffusers.load_model(str(model_path))
assert loaded.weight_name == "alpha_weights.safetensors"
def test_save_model_records_weight_name_for_peft_adapter(tmp_path, model_path):
adapter_dir = tmp_path / "peft_adapter"
adapter_dir.mkdir()
tensors = {"w": np.random.randn(4, 4).astype(np.float32)}
save_file(tensors, str(adapter_dir / "adapter_model.safetensors"))
(adapter_dir / "adapter_config.json").write_text('{"type": "lora"}')
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
mlmodel = yaml.safe_load((model_path / MLMODEL_FILE_NAME).read_text())
flavor_conf = mlmodel["flavors"]["diffusers"]
assert flavor_conf["weight_name"] == "adapter_model.safetensors"
loaded = mlflow.diffusers.load_model(str(model_path))
assert loaded.weight_name == "adapter_model.safetensors"
def test_save_model_rejects_non_safetensors_file(tmp_path, model_path):
bad_file = tmp_path / "model.bin"
bad_file.write_bytes(b"\x00" * 100)
with pytest.raises(MlflowException, match=".safetensors"):
mlflow.diffusers.save_model(
adapter_path=str(bad_file),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
def test_save_model_rejects_invalid_safetensors_content(tmp_path, model_path):
fake_file = tmp_path / "bad.safetensors"
fake_file.write_bytes(b"this is not safetensors format")
with pytest.raises(MlflowException, match="not a valid safetensors"):
mlflow.diffusers.save_model(
adapter_path=str(fake_file),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
@pytest.mark.parametrize(
("base_model", "match"),
[
("", "non-empty"),
(" ", "non-empty"),
(None, "must be a"),
(123, "must be a"),
],
)
def test_save_model_rejects_invalid_base_model(adapter_dir, model_path, base_model, match):
with pytest.raises(MlflowException, match=match):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=base_model,
)
@pytest.mark.parametrize("adapter_type", [None, 123, []])
def test_save_model_rejects_non_string_adapter_type(adapter_dir, model_path, adapter_type):
with pytest.raises(MlflowException, match="adapter_type must be a string"):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
adapter_type=adapter_type,
)
# -- _detect_device tests --
def test_detect_device_explicit():
from mlflow.diffusers import _detect_device
assert _detect_device("cpu") == "cpu"
assert _detect_device("cuda:1") == "cuda:1"
def test_detect_device_env_var(monkeypatch):
from mlflow.diffusers import _detect_device
monkeypatch.setenv("MLFLOW_DEFAULT_PREDICTION_DEVICE", "cpu")
assert _detect_device() == "cpu"
def test_predict_single_column_dataframe(wrapper):
import pandas as pd
result = wrapper.predict(pd.DataFrame(["a cat", "a dog"]))
assert len(result) == 2
def test_save_model_rejects_empty_directory(tmp_path, model_path):
empty_dir = tmp_path / "empty_adapter"
empty_dir.mkdir()
(empty_dir / "readme.txt").write_text("no weights here")
with pytest.raises(MlflowException, match="no .safetensors"):
mlflow.diffusers.save_model(
adapter_path=str(empty_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
def test_save_model_ignores_hidden_files(tmp_path, model_path):
adapter_dir = tmp_path / "adapter_with_ds_store"
adapter_dir.mkdir()
tensors = {"w": np.random.randn(4, 4).astype(np.float32)}
save_file(tensors, str(adapter_dir / "my_lora.safetensors"))
(adapter_dir / ".DS_Store").write_bytes(b"\x00" * 10)
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)
weights_dir = model_path / "adapter_weights"
assert (weights_dir / "pytorch_lora_weights.safetensors").exists()
def test_save_model_validates_all_safetensors_in_multi_file_dir(tmp_path, model_path):
adapter_dir = tmp_path / "corrupt_multi"
adapter_dir.mkdir()
tensors = {"w": np.random.randn(4, 4).astype(np.float32)}
save_file(tensors, str(adapter_dir / "pytorch_lora_weights.safetensors"))
(adapter_dir / "corrupt.safetensors").write_bytes(b"not valid safetensors")
with pytest.raises(MlflowException, match="not a valid safetensors"):
mlflow.diffusers.save_model(
adapter_path=str(adapter_dir),
path=str(model_path),
base_model=BASE_MODEL_ID,
)