chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
@@ -0,0 +1,65 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
import pytest
|
||||
import transformers.image_utils
|
||||
from PIL import Image
|
||||
|
||||
from vllm.transformers_utils.processors.pixtral import MistralCommonImageProcessor
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def image_processor() -> MistralCommonImageProcessor:
|
||||
return MistralCommonImageProcessor(mm_encoder=None)
|
||||
|
||||
|
||||
def test_fetch_images_passes_through_decoded_image(
|
||||
image_processor: MistralCommonImageProcessor,
|
||||
):
|
||||
image = Image.new("RGB", (4, 4))
|
||||
result = image_processor.fetch_images(image)
|
||||
assert result is image
|
||||
|
||||
|
||||
def test_fetch_images_recurses_over_list(
|
||||
image_processor: MistralCommonImageProcessor,
|
||||
):
|
||||
a = Image.new("RGB", (4, 4))
|
||||
b = Image.new("RGB", (8, 8))
|
||||
result = image_processor.fetch_images([a, b])
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 2
|
||||
assert result[0] is a
|
||||
assert result[1] is b
|
||||
|
||||
|
||||
def test_fetch_images_recurses_over_nested_list(
|
||||
image_processor: MistralCommonImageProcessor,
|
||||
):
|
||||
a = Image.new("RGB", (4, 4))
|
||||
b = Image.new("RGB", (8, 8))
|
||||
result = image_processor.fetch_images([[a], [b]])
|
||||
assert result == [[a], [b]]
|
||||
|
||||
|
||||
def test_fetch_images_str_delegates_to_load_image(
|
||||
monkeypatch, image_processor: MistralCommonImageProcessor
|
||||
):
|
||||
sentinel = Image.new("RGB", (2, 2))
|
||||
received: dict[str, object] = {}
|
||||
|
||||
def fake_load_image(path):
|
||||
received["path"] = path
|
||||
return sentinel
|
||||
|
||||
monkeypatch.setattr(transformers.image_utils, "load_image", fake_load_image)
|
||||
|
||||
result = image_processor.fetch_images("/tmp/fake.png")
|
||||
assert result is sentinel
|
||||
assert received["path"] == "/tmp/fake.png"
|
||||
|
||||
|
||||
def test_fetch_images_rejects_unsupported_type(
|
||||
image_processor: MistralCommonImageProcessor,
|
||||
):
|
||||
with pytest.raises(TypeError, match="only a single or a list"):
|
||||
image_processor.fetch_images(42)
|
||||
@@ -0,0 +1,127 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""Unit tests for ``MistralCommonFeatureExtractor.fetch_audio``.
|
||||
|
||||
``transformers>=5.10`` adds a ``ProcessorMixin.prepare_inputs_layout`` helper
|
||||
that calls ``self.feature_extractor.fetch_audio(...)`` unconditionally. The
|
||||
duck-typed :class:`MistralCommonFeatureExtractor` previously did not implement
|
||||
that method, so loading any voxtral model under transformers 5.10.x raised
|
||||
``AttributeError: 'MistralCommonFeatureExtractor' object has no attribute
|
||||
'fetch_audio'``. These tests pin the new ``fetch_audio`` method to the same
|
||||
contract as ``transformers.SequenceFeatureExtractor.fetch_audio``.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from vllm.tokenizers.mistral import MistralTokenizer
|
||||
from vllm.transformers_utils.processors.voxtral import (
|
||||
MistralCommonFeatureExtractor,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def feature_extractor() -> MistralCommonFeatureExtractor:
|
||||
tokenizer = MistralTokenizer.from_pretrained("mistralai/Voxtral-Mini-3B-2507")
|
||||
return MistralCommonFeatureExtractor(tokenizer.instruct.audio_encoder)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"audio",
|
||||
[
|
||||
np.zeros(1024, dtype=np.float32),
|
||||
torch.zeros(1024),
|
||||
[0.0, 1.0, 2.0],
|
||||
],
|
||||
ids=["numpy_array", "torch_tensor", "list_of_floats"],
|
||||
)
|
||||
def test_fetch_audio_passes_through(
|
||||
feature_extractor: MistralCommonFeatureExtractor, audio
|
||||
):
|
||||
result = feature_extractor.fetch_audio(audio)
|
||||
assert result is audio
|
||||
|
||||
|
||||
def test_fetch_audio_recurses_over_list_of_arrays(
|
||||
feature_extractor: MistralCommonFeatureExtractor,
|
||||
):
|
||||
a = np.zeros(8, dtype=np.float32)
|
||||
b = np.ones(8, dtype=np.float32)
|
||||
result = feature_extractor.fetch_audio([a, b])
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 2
|
||||
assert result[0] is a
|
||||
assert result[1] is b
|
||||
|
||||
|
||||
def test_fetch_audio_uses_self_sampling_rate_when_none(
|
||||
monkeypatch, feature_extractor: MistralCommonFeatureExtractor
|
||||
):
|
||||
"""If ``sampling_rate`` is None, ``self.sampling_rate`` must be used.
|
||||
|
||||
Verified indirectly via the recursion path: when we pass a list of arrays
|
||||
without sampling_rate, recursive calls receive the resolved rate.
|
||||
"""
|
||||
captured: list[int | None] = []
|
||||
original = feature_extractor.fetch_audio
|
||||
|
||||
def spy(audio, sampling_rate=None):
|
||||
captured.append(sampling_rate)
|
||||
return original(audio, sampling_rate=sampling_rate)
|
||||
|
||||
monkeypatch.setattr(feature_extractor, "fetch_audio", spy)
|
||||
feature_extractor.fetch_audio([np.zeros(4, dtype=np.float32)])
|
||||
# Top-level call has sampling_rate=None; inner recursive call sees the
|
||||
# resolved rate from self.sampling_rate.
|
||||
assert captured[0] is None
|
||||
assert captured[1] == 16000
|
||||
|
||||
|
||||
def test_fetch_audio_explicit_sampling_rate_propagates(
|
||||
monkeypatch, feature_extractor: MistralCommonFeatureExtractor
|
||||
):
|
||||
captured: list[int | None] = []
|
||||
original = feature_extractor.fetch_audio
|
||||
|
||||
def spy(audio, sampling_rate=None):
|
||||
captured.append(sampling_rate)
|
||||
return original(audio, sampling_rate=sampling_rate)
|
||||
|
||||
monkeypatch.setattr(feature_extractor, "fetch_audio", spy)
|
||||
feature_extractor.fetch_audio([np.zeros(4, dtype=np.float32)], sampling_rate=8000)
|
||||
assert captured[0] == 8000
|
||||
assert captured[1] == 8000
|
||||
|
||||
|
||||
def test_fetch_audio_rejects_unsupported_type(
|
||||
feature_extractor: MistralCommonFeatureExtractor,
|
||||
):
|
||||
with pytest.raises(TypeError, match="only a numpy array"):
|
||||
feature_extractor.fetch_audio(42) # type: ignore[arg-type]
|
||||
|
||||
|
||||
def test_fetch_audio_str_delegates_to_load_audio(
|
||||
monkeypatch, feature_extractor: MistralCommonFeatureExtractor
|
||||
):
|
||||
"""A str input must round-trip through ``transformers.audio_utils.load_audio``.
|
||||
|
||||
We monkey-patch ``load_audio`` so the test stays offline (no real URL/path
|
||||
fetched) and still asserts the delegation contract.
|
||||
"""
|
||||
sentinel = np.array([0.5, -0.5], dtype=np.float32)
|
||||
received: dict[str, object] = {}
|
||||
|
||||
def fake_load_audio(path, sampling_rate=None):
|
||||
received["path"] = path
|
||||
received["sampling_rate"] = sampling_rate
|
||||
return sentinel
|
||||
|
||||
import transformers.audio_utils
|
||||
|
||||
monkeypatch.setattr(transformers.audio_utils, "load_audio", fake_load_audio)
|
||||
|
||||
result = feature_extractor.fetch_audio("/tmp/fake.wav")
|
||||
assert result is sentinel
|
||||
assert received["path"] == "/tmp/fake.wav"
|
||||
assert received["sampling_rate"] == 16000
|
||||
@@ -0,0 +1,32 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""
|
||||
This test file includes some cases where it is inappropriate to
|
||||
only get the `eos_token_id` from the tokenizer as defined by
|
||||
`BaseRenderer.get_eos_token_id`.
|
||||
"""
|
||||
|
||||
from vllm.tokenizers import get_tokenizer
|
||||
from vllm.transformers_utils.config import try_get_generation_config
|
||||
|
||||
|
||||
def test_get_llama3_eos_token():
|
||||
model_name = "meta-llama/Llama-3.2-1B-Instruct"
|
||||
|
||||
tokenizer = get_tokenizer(model_name)
|
||||
assert tokenizer.eos_token_id == 128009
|
||||
|
||||
generation_config = try_get_generation_config(model_name, trust_remote_code=False)
|
||||
assert generation_config is not None
|
||||
assert generation_config.eos_token_id == [128001, 128008, 128009]
|
||||
|
||||
|
||||
def test_get_blip2_eos_token():
|
||||
model_name = "Salesforce/blip2-opt-2.7b"
|
||||
|
||||
tokenizer = get_tokenizer(model_name)
|
||||
assert tokenizer.eos_token_id == 2
|
||||
|
||||
generation_config = try_get_generation_config(model_name, trust_remote_code=False)
|
||||
assert generation_config is not None
|
||||
assert generation_config.eos_token_id == 50118
|
||||
@@ -0,0 +1,35 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from transformers import PretrainedConfig
|
||||
|
||||
from vllm.transformers_utils.config import get_config_parser, register_config_parser
|
||||
from vllm.transformers_utils.config_parser_base import ConfigParserBase
|
||||
|
||||
|
||||
@register_config_parser("custom_config_parser")
|
||||
class CustomConfigParser(ConfigParserBase):
|
||||
def parse(
|
||||
self,
|
||||
model: str | Path,
|
||||
trust_remote_code: bool,
|
||||
revision: str | None = None,
|
||||
code_revision: str | None = None,
|
||||
**kwargs,
|
||||
) -> tuple[dict, PretrainedConfig]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def test_register_config_parser():
|
||||
assert isinstance(get_config_parser("custom_config_parser"), CustomConfigParser)
|
||||
|
||||
|
||||
def test_invalid_config_parser():
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
@register_config_parser("invalid_config_parser")
|
||||
class InvalidConfigParser:
|
||||
pass
|
||||
@@ -0,0 +1,81 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""Test that hf_overrides model_type returns the correct config class."""
|
||||
|
||||
import json
|
||||
import tempfile
|
||||
|
||||
from transformers import PretrainedConfig
|
||||
|
||||
from vllm.transformers_utils.config import _CONFIG_REGISTRY, get_config
|
||||
|
||||
|
||||
class _TestCustomConfig(PretrainedConfig):
|
||||
model_type = "test_custom_model"
|
||||
|
||||
def __init__(self, custom_attr=42, **kw):
|
||||
super().__init__(**kw)
|
||||
self.custom_attr = custom_attr
|
||||
|
||||
|
||||
def test_hf_overrides_model_type_returns_correct_config_class():
|
||||
"""When hf_overrides sets model_type to a registered custom type whose
|
||||
checkpoint has a *different* model_type on disk, get_config() must return
|
||||
an instance of the registered config class — not the class that matches
|
||||
the on-disk model_type."""
|
||||
|
||||
# Register the custom config
|
||||
_CONFIG_REGISTRY["test_custom_model"] = _TestCustomConfig
|
||||
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Checkpoint says model_type="mixtral" on disk
|
||||
cfg = {
|
||||
"model_type": "mixtral",
|
||||
"hidden_size": 128,
|
||||
"num_hidden_layers": 2,
|
||||
"num_attention_heads": 4,
|
||||
"num_key_value_heads": 4,
|
||||
"intermediate_size": 256,
|
||||
"num_local_experts": 4,
|
||||
"num_experts_per_tok": 2,
|
||||
}
|
||||
with open(f"{tmpdir}/config.json", "w") as f:
|
||||
json.dump(cfg, f)
|
||||
|
||||
config = get_config(
|
||||
tmpdir,
|
||||
trust_remote_code=False,
|
||||
hf_overrides_kw={
|
||||
"model_type": "test_custom_model",
|
||||
},
|
||||
)
|
||||
|
||||
from transformers import AutoConfig
|
||||
from transformers.models.auto.configuration_auto import CONFIG_MAPPING
|
||||
|
||||
# get_config() returns the registered custom class
|
||||
assert isinstance(config, _TestCustomConfig), (
|
||||
f"Expected _TestCustomConfig, got {type(config).__name__}"
|
||||
)
|
||||
|
||||
# AutoConfig has _TestCustomConfig registered under both
|
||||
# the overridden model_type and the on-disk model_type
|
||||
assert CONFIG_MAPPING["test_custom_model"] is _TestCustomConfig
|
||||
assert CONFIG_MAPPING["mixtral"] is _TestCustomConfig
|
||||
|
||||
# AutoConfig.from_pretrained now returns _TestCustomConfig
|
||||
# for this checkpoint (even though its on-disk model_type
|
||||
# is "mixtral")
|
||||
auto_config = AutoConfig.from_pretrained(tmpdir)
|
||||
assert isinstance(auto_config, _TestCustomConfig), (
|
||||
f"Expected _TestCustomConfig from AutoConfig, got "
|
||||
f"{type(auto_config).__name__}"
|
||||
)
|
||||
finally:
|
||||
_CONFIG_REGISTRY.pop("test_custom_model", None)
|
||||
# Restore the original mixtral AutoConfig mapping to avoid
|
||||
# side effects on other tests in the same process
|
||||
from transformers import AutoConfig, MixtralConfig
|
||||
|
||||
AutoConfig.register("mixtral", MixtralConfig, exist_ok=True)
|
||||
@@ -0,0 +1,67 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
import importlib
|
||||
|
||||
from transformers.processing_utils import ProcessingKwargs
|
||||
from typing_extensions import Unpack
|
||||
|
||||
from vllm.transformers_utils.processor import (
|
||||
get_processor_kwargs_keys,
|
||||
get_processor_kwargs_type,
|
||||
)
|
||||
|
||||
|
||||
class _FakeProcessorKwargs(ProcessingKwargs, total=False): # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
def _assert_has_all_expected(keys: set[str]) -> None:
|
||||
# text
|
||||
for k in ("text_pair", "text_target", "text_pair_target"):
|
||||
assert k in keys
|
||||
# image
|
||||
for k in ("do_convert_rgb", "do_resize"):
|
||||
assert k in keys
|
||||
# audio
|
||||
for k in (
|
||||
"fps",
|
||||
"do_sample_frames",
|
||||
"input_data_format",
|
||||
"default_to_square",
|
||||
):
|
||||
assert k in keys
|
||||
# audio
|
||||
for k in ("padding", "return_attention_mask"):
|
||||
assert k in keys
|
||||
|
||||
|
||||
# Path 1: __call__ method has kwargs: Unpack[*ProcessorKwargs]
|
||||
class _ProcWithUnpack:
|
||||
def __call__(self, *args, **kwargs: Unpack[_FakeProcessorKwargs]): # type: ignore
|
||||
return None
|
||||
|
||||
|
||||
def test_get_processor_kwargs_from_processor_unpack_path_returns_full_union():
|
||||
proc = _ProcWithUnpack()
|
||||
keys = get_processor_kwargs_keys(get_processor_kwargs_type(proc))
|
||||
_assert_has_all_expected(keys)
|
||||
|
||||
|
||||
# ---- Path 2: No Unpack, fallback to scanning *ProcessorKwargs in module ----
|
||||
|
||||
|
||||
class _ProcWithoutUnpack:
|
||||
def __call__(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
|
||||
def test_get_processor_kwargs_from_processor_module_scan_returns_full_union():
|
||||
# ensure the module scanned by fallback is this test module
|
||||
module_name = _ProcWithoutUnpack.__module__
|
||||
mod = importlib.import_module(module_name)
|
||||
assert hasattr(mod, "_FakeProcessorKwargs")
|
||||
|
||||
proc = _ProcWithoutUnpack()
|
||||
keys = get_processor_kwargs_keys(get_processor_kwargs_type(proc))
|
||||
_assert_has_all_expected(keys)
|
||||
@@ -0,0 +1,187 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
import pytest
|
||||
from huggingface_hub import _CACHED_NO_EXIST
|
||||
|
||||
from vllm.transformers_utils.repo_utils import (
|
||||
any_pattern_in_repo_files,
|
||||
get_hf_file_to_dict,
|
||||
is_mistral_model_repo,
|
||||
list_filtered_repo_files,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"allow_patterns,expected_relative_files",
|
||||
[
|
||||
(
|
||||
["*.json", "correct*.txt"],
|
||||
["json_file.json", "subfolder/correct.txt", "correct_2.txt"],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_list_filtered_repo_files(
|
||||
allow_patterns: list[str], expected_relative_files: list[str]
|
||||
):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# Prep folder and files
|
||||
path_tmp_dir = Path(tmp_dir)
|
||||
subfolder = path_tmp_dir / "subfolder"
|
||||
subfolder.mkdir()
|
||||
(path_tmp_dir / "json_file.json").touch()
|
||||
(path_tmp_dir / "correct_2.txt").touch()
|
||||
(path_tmp_dir / "incorrect.txt").touch()
|
||||
(path_tmp_dir / "incorrect.jpeg").touch()
|
||||
(subfolder / "correct.txt").touch()
|
||||
(subfolder / "incorrect_sub.txt").touch()
|
||||
|
||||
def _glob_path() -> list[str]:
|
||||
return [
|
||||
str(file.relative_to(path_tmp_dir))
|
||||
for file in path_tmp_dir.glob("**/*")
|
||||
if file.is_file()
|
||||
]
|
||||
|
||||
# Patch list_repo_files called by fn
|
||||
with patch(
|
||||
"vllm.transformers_utils.repo_utils.list_repo_files",
|
||||
MagicMock(return_value=_glob_path()),
|
||||
) as mock_list_repo_files:
|
||||
out_files = sorted(
|
||||
list_filtered_repo_files(
|
||||
tmp_dir, allow_patterns, "revision", "model", "token"
|
||||
)
|
||||
)
|
||||
assert out_files == sorted(expected_relative_files)
|
||||
assert mock_list_repo_files.call_count == 1
|
||||
assert mock_list_repo_files.call_args_list[0] == call(
|
||||
repo_id=tmp_dir,
|
||||
revision="revision",
|
||||
repo_type="model",
|
||||
token="token",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("allow_patterns", "expected_bool"),
|
||||
[
|
||||
(["*.json", "correct*.txt"], True),
|
||||
(
|
||||
["*.jpeg"],
|
||||
True,
|
||||
),
|
||||
(
|
||||
["not_found.jpeg"],
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_one_filtered_repo_files(allow_patterns: list[str], expected_bool: bool):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# Prep folder and files
|
||||
path_tmp_dir = Path(tmp_dir)
|
||||
subfolder = path_tmp_dir / "subfolder"
|
||||
subfolder.mkdir()
|
||||
(path_tmp_dir / "incorrect.jpeg").touch()
|
||||
(subfolder / "correct.txt").touch()
|
||||
|
||||
def _glob_path() -> list[str]:
|
||||
return [
|
||||
str(file.relative_to(path_tmp_dir))
|
||||
for file in path_tmp_dir.glob("**/*")
|
||||
if file.is_file()
|
||||
]
|
||||
|
||||
# Patch list_repo_files called by fn
|
||||
with patch(
|
||||
"vllm.transformers_utils.repo_utils.list_repo_files",
|
||||
MagicMock(return_value=_glob_path()),
|
||||
) as mock_list_repo_files:
|
||||
assert (
|
||||
any_pattern_in_repo_files(
|
||||
tmp_dir, allow_patterns, "revision", "model", "token"
|
||||
)
|
||||
) is expected_bool
|
||||
assert mock_list_repo_files.call_count == 1
|
||||
assert mock_list_repo_files.call_args_list[0] == call(
|
||||
repo_id=tmp_dir,
|
||||
revision="revision",
|
||||
repo_type="model",
|
||||
token="token",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("cache_result", "should_download"),
|
||||
[
|
||||
# HF Hub recorded a prior 404: don't re-probe the Hub.
|
||||
(_CACHED_NO_EXIST, False),
|
||||
# File not in cache and existence unknown: preserve download behavior.
|
||||
(None, True),
|
||||
],
|
||||
)
|
||||
def test_get_hf_file_to_dict_honors_no_exist_marker(
|
||||
cache_result: object, should_download: bool
|
||||
):
|
||||
with (
|
||||
patch(
|
||||
"vllm.transformers_utils.repo_utils.try_to_load_from_cache",
|
||||
MagicMock(return_value=cache_result),
|
||||
),
|
||||
patch(
|
||||
"vllm.transformers_utils.repo_utils._try_download_from_hf_hub",
|
||||
MagicMock(return_value=None),
|
||||
) as mock_download,
|
||||
):
|
||||
result = get_hf_file_to_dict("processor_config.json", "some/repo")
|
||||
assert result is None
|
||||
assert mock_download.call_count == int(should_download)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("files", "expected_bool"),
|
||||
[
|
||||
(["consolidated.safetensors", "incorrect.txt"], True),
|
||||
(["consolidated-1.safetensors", "incorrect.txt"], True),
|
||||
(
|
||||
["consolidated-1.json"],
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_is_mistral_model_repo(files: list[str], expected_bool: bool):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# Prep folder and files
|
||||
path_tmp_dir = Path(tmp_dir)
|
||||
for file in files:
|
||||
(path_tmp_dir / file).touch()
|
||||
|
||||
def _glob_path() -> list[str]:
|
||||
return [
|
||||
str(file.relative_to(path_tmp_dir))
|
||||
for file in path_tmp_dir.glob("**/*")
|
||||
if file.is_file()
|
||||
]
|
||||
|
||||
# Patch list_repo_files called by fn
|
||||
with patch(
|
||||
"vllm.transformers_utils.repo_utils.list_repo_files",
|
||||
MagicMock(return_value=_glob_path()),
|
||||
) as mock_list_repo_files:
|
||||
assert (
|
||||
is_mistral_model_repo(tmp_dir, "revision", "model", "token")
|
||||
is expected_bool
|
||||
)
|
||||
assert mock_list_repo_files.call_count == 1
|
||||
assert mock_list_repo_files.call_args_list[0] == call(
|
||||
repo_id=tmp_dir,
|
||||
revision="revision",
|
||||
repo_type="model",
|
||||
token="token",
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
from vllm.transformers_utils.utils import (
|
||||
is_azure,
|
||||
is_cloud_storage,
|
||||
is_gcs,
|
||||
is_s3,
|
||||
)
|
||||
|
||||
|
||||
def test_is_gcs():
|
||||
assert is_gcs("gs://model-path")
|
||||
assert not is_gcs("s3://model-path/path-to-model")
|
||||
assert not is_gcs("/unix/local/path")
|
||||
assert not is_gcs("nfs://nfs-fqdn.local")
|
||||
|
||||
|
||||
def test_is_s3():
|
||||
assert is_s3("s3://model-path/path-to-model")
|
||||
assert not is_s3("gs://model-path")
|
||||
assert not is_s3("/unix/local/path")
|
||||
assert not is_s3("nfs://nfs-fqdn.local")
|
||||
|
||||
|
||||
def test_is_azure():
|
||||
assert is_azure("az://model-container/path")
|
||||
assert not is_azure("s3://model-path/path-to-model")
|
||||
assert not is_azure("/unix/local/path")
|
||||
assert not is_azure("nfs://nfs-fqdn.local")
|
||||
|
||||
|
||||
def test_is_cloud_storage():
|
||||
assert is_cloud_storage("gs://model-path")
|
||||
assert is_cloud_storage("s3://model-path/path-to-model")
|
||||
assert is_cloud_storage("az://model-container/path")
|
||||
assert not is_cloud_storage("/unix/local/path")
|
||||
assert not is_cloud_storage("nfs://nfs-fqdn.local")
|
||||
Reference in New Issue
Block a user