From 49f2ff257aeb9874aa2cefe99aa4008ac7661ba8 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 6 Jun 2026 19:15:36 +0200 Subject: [PATCH] Fix preflight remote alias handling --- src/srtctl/core/validation.py | 18 ++++++----- tests/test_validation.py | 60 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/srtctl/core/validation.py b/src/srtctl/core/validation.py index 63deff81c..3c654e9a6 100644 --- a/src/srtctl/core/validation.py +++ b/src/srtctl/core/validation.py @@ -126,15 +126,16 @@ def _preflight_model( # framework downloads via HF_HOME at serve time. Preflight cannot # filesystem-check a remote ID, so accept and let runtime fail loudly # if the ID is bogus. - if isinstance(raw, str) and raw.startswith("hf:"): + if isinstance(resolved, str) and resolved.startswith("hf:"): + resolved_source = source if source != "literal" else "huggingface" return ( PreflightResolution( field="model.path", raw=raw, - resolved=raw, - source="huggingface", + resolved=resolved, + source=resolved_source, ok=True, - message=f"HuggingFace model ID: {raw[3:]}", + message=f"HuggingFace model ID: {resolved[3:]}", ), [], ) @@ -216,15 +217,16 @@ def _preflight_container( # --container-image``, which Pyxis/enroot pulls on first use. The # ":" guard distinguishes a URI (registry/...:tag or scheme://...) # from a typo'd local relative path. - if isinstance(raw, str) and not raw.startswith(("/", "./")) and ":" in raw: + if isinstance(resolved, str) and not resolved.startswith(("/", "./")) and ":" in resolved: + resolved_source = source if source != "literal" else "container-uri" return ( PreflightResolution( field="model.container", raw=raw, - resolved=raw, - source="container-uri", + resolved=resolved, + source=resolved_source, ok=True, - message=f"Container image URI: {raw}", + message=f"Container image URI: {resolved}", ), [], ) diff --git a/tests/test_validation.py b/tests/test_validation.py index df0c6201c..8bdd7da75 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -426,6 +426,66 @@ def test_preflight_accepts_hf_model_and_docker_container_together( assert results[0].container.source == "container-uri" assert results[0].errors == [] + def test_preflight_accepts_model_alias_resolving_to_hf_model(self, tmp_path): + container_file = tmp_path / "container.sqsh" + container_file.write_text("sqsh") + + results = preflight_config_variants( + { + "name": "hf-model-alias", + "model": { + "path": "qwen32b", + "container": str(container_file), + "precision": "bf16", + }, + "resources": { + "gpu_type": "gb200", + "gpus_per_node": 4, + "prefill_nodes": 1, + "decode_nodes": 1, + "prefill_workers": 1, + "decode_workers": 1, + }, + }, + cluster_config={"model_paths": {"qwen32b": "hf:Qwen/Qwen3-32B"}}, + ) + + assert results[0].ok is True + assert results[0].model.raw == "qwen32b" + assert results[0].model.resolved == "hf:Qwen/Qwen3-32B" + assert results[0].model.source == "srtslurm.yaml:model_paths" + assert results[0].errors == [] + + def test_preflight_accepts_container_alias_resolving_to_remote_image(self, tmp_path): + model_dir = tmp_path / "model" + model_dir.mkdir() + + results = preflight_config_variants( + { + "name": "remote-container-alias", + "model": { + "path": str(model_dir), + "container": "sglang-latest", + "precision": "bf16", + }, + "resources": { + "gpu_type": "gb200", + "gpus_per_node": 4, + "prefill_nodes": 1, + "decode_nodes": 1, + "prefill_workers": 1, + "decode_workers": 1, + }, + }, + cluster_config={"containers": {"sglang-latest": "nvcr.io/nvidia/sglang-runtime:latest"}}, + ) + + assert results[0].ok is True + assert results[0].container.raw == "sglang-latest" + assert results[0].container.resolved == "nvcr.io/nvidia/sglang-runtime:latest" + assert results[0].container.source == "srtslurm.yaml:containers" + assert results[0].errors == [] + def test_preflight_still_rejects_typo_local_path_without_colon( self, tmp_path ):