Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions src/srtctl/core/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:]}",
),
[],
)
Expand Down Expand Up @@ -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}",
),
[],
)
Expand Down
60 changes: 60 additions & 0 deletions tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
):
Expand Down