[Bugfix] Preserve object storage URIs during model resolution (#5036)
Signed-off-by: Ziyang Zhang <hafuhafu@qq.com>
This commit is contained in:
1 parent
35cacf020c
commit
6f36fd3b78
6 files changed
+267
-18
No files matched your search
@@ -10,8 +10,10 @@ ModelScope path.
|
||||
|
||||
import sys
|
||||
import types
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from pytest_mock import MockerFixture
|
||||
from vllm import envs
|
||||
|
||||
from vllm_omni.entrypoints import omni_base
|
||||
@@ -21,6 +23,64 @@ pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
MODEL_ID = "Qwen/Qwen3-Omni-30B-A3B-Instruct"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"model_uri",
|
||||
[
|
||||
"s3://bucket/model",
|
||||
"gs://bucket/model",
|
||||
"az://bucket/model",
|
||||
],
|
||||
)
|
||||
def test_omni_snapshot_download_preserves_object_storage_uri(
|
||||
model_uri: str,
|
||||
mocker: MockerFixture,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.delenv("VLLM_USE_MODELSCOPE", raising=False)
|
||||
hf_download = mocker.patch.object(omni_base, "download_weights_from_hf_specific")
|
||||
|
||||
assert omni_base.omni_snapshot_download(model_uri) == model_uri
|
||||
hf_download.assert_not_called()
|
||||
|
||||
|
||||
def test_omni_snapshot_download_preserves_existing_local_path(
|
||||
tmp_path: Path,
|
||||
mocker: MockerFixture,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.delenv("VLLM_USE_MODELSCOPE", raising=False)
|
||||
absolute_model_path = tmp_path / "absolute-model"
|
||||
absolute_model_path.mkdir()
|
||||
relative_model_path = "relative-model"
|
||||
(tmp_path / relative_model_path).mkdir()
|
||||
monkeypatch.chdir(tmp_path)
|
||||
hf_download = mocker.patch.object(omni_base, "download_weights_from_hf_specific")
|
||||
|
||||
assert omni_base.omni_snapshot_download(str(absolute_model_path)) == str(absolute_model_path)
|
||||
assert omni_base.omni_snapshot_download(relative_model_path) == relative_model_path
|
||||
hf_download.assert_not_called()
|
||||
|
||||
|
||||
def test_omni_snapshot_download_uses_hf_for_relative_repo_id(
|
||||
mocker: MockerFixture,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.delenv("VLLM_USE_MODELSCOPE", raising=False)
|
||||
model_id = "org/model"
|
||||
# Stub the modular-index probe so the test never reaches huggingface.co.
|
||||
hf_file_probe = mocker.patch.object(omni_base, "file_or_path_exists", return_value=False)
|
||||
hf_download = mocker.patch.object(omni_base, "download_weights_from_hf_specific")
|
||||
|
||||
assert omni_base.omni_snapshot_download(model_id) == model_id
|
||||
hf_file_probe.assert_called_once_with(model_id, "modular_model_index.json", revision=None)
|
||||
hf_download.assert_called_once_with(
|
||||
model_name_or_path=model_id,
|
||||
cache_dir=None,
|
||||
allow_patterns=["*"],
|
||||
require_all=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def download_backend(monkeypatch: pytest.MonkeyPatch):
|
||||
"""Run ``omni_snapshot_download`` and report which backend it selected.
|
||||
|
||||
@@ -22,6 +22,7 @@ from vllm_omni.engine.stage_init_utils import (
|
||||
from vllm_omni.entrypoints.utils import (
|
||||
_convert_dataclasses_to_dict,
|
||||
_filter_dict_like_object,
|
||||
_try_resolve_omni_model_type,
|
||||
coerce_param_message_types,
|
||||
filter_dataclass_kwargs,
|
||||
filter_stages,
|
||||
@@ -371,6 +372,48 @@ class TestResolveModelConfigPath:
|
||||
assert sender["extra"]["connector_get_max_wait_first_chunk"] == 3000
|
||||
assert sender["extra"]["connector_get_max_wait"] == 300
|
||||
|
||||
def test_object_storage_uri_resolves_via_materialized_configs(
|
||||
self,
|
||||
mocker: MockerFixture,
|
||||
tmp_path,
|
||||
):
|
||||
"""An object-storage URI must not reach HF helpers; resolution reads the
|
||||
locally materialized config files instead."""
|
||||
hf_config = SimpleNamespace(
|
||||
model_type="qwen3_omni_moe",
|
||||
architectures=["Qwen3OmniMoeForConditionalGeneration"],
|
||||
)
|
||||
uri = "s3://qwen3-tts-models/Qwen3-Omni-30B-A3B-Instruct"
|
||||
materialize = mocker.patch(
|
||||
"vllm_omni.entrypoints.utils._materialize_object_storage_configs",
|
||||
return_value=str(tmp_path),
|
||||
)
|
||||
get_config = mocker.patch(
|
||||
"vllm_omni.entrypoints.utils.get_config",
|
||||
return_value=hf_config,
|
||||
)
|
||||
|
||||
result = resolve_model_config_path(uri)
|
||||
|
||||
materialize.assert_called_once_with(uri)
|
||||
get_config.assert_called_once()
|
||||
assert get_config.call_args[0][0] == str(tmp_path)
|
||||
assert result is not None
|
||||
assert Path(result).as_posix().endswith("qwen3_omni_moe.yaml")
|
||||
|
||||
|
||||
class TestTryResolveOmniModelType:
|
||||
"""Name matching must only scan the last path component."""
|
||||
|
||||
def test_uri_bucket_name_cannot_hijack_pipeline(self):
|
||||
# "qwen3-tts-models" normalizes to "qwen3ttsmodels", which contains the
|
||||
# registered key "qwen3tts" — only the basename may participate, so a
|
||||
# misleadingly named bucket selects nothing.
|
||||
assert _try_resolve_omni_model_type("s3://qwen3-tts-models/plain-checkpoint") is None
|
||||
|
||||
def test_basename_still_matches_pipeline(self):
|
||||
assert _try_resolve_omni_model_type("gs://any-bucket/Fun-CosyVoice3-0.5B-2512") == "cosyvoice3"
|
||||
|
||||
|
||||
class TestLoadAndResolveStageConfigs:
|
||||
def test_load_and_resolve_with_kwargs(self, mocker: MockerFixture):
|
||||
|
||||
Reference in new issue
Block a user