ci: enable /rerun-test for multimodal gen PR tests (#22828)
This commit is contained in:
@@ -38,6 +38,11 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: "false"
|
default: "false"
|
||||||
|
install_diffusion:
|
||||||
|
description: "Install diffusion dependencies (for multimodal gen tests)"
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: "false"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SGLANG_IS_IN_CI: true
|
SGLANG_IS_IN_CI: true
|
||||||
@@ -73,6 +78,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
if [[ "${{ inputs.use_deepep }}" == "true" ]]; then
|
if [[ "${{ inputs.use_deepep }}" == "true" ]]; then
|
||||||
bash scripts/ci/cuda/ci_install_deepep.sh
|
bash scripts/ci/cuda/ci_install_deepep.sh
|
||||||
|
elif [[ "${{ inputs.install_diffusion }}" == "true" ]]; then
|
||||||
|
bash scripts/ci/cuda/ci_install_dependency.sh diffusion
|
||||||
else
|
else
|
||||||
bash scripts/ci/cuda/ci_install_dependency.sh
|
bash scripts/ci/cuda/ci_install_dependency.sh
|
||||||
fi
|
fi
|
||||||
@@ -83,7 +90,6 @@ jobs:
|
|||||||
if [[ "${{ inputs.runner_label }}" == "1-gpu-5090" ]]; then
|
if [[ "${{ inputs.runner_label }}" == "1-gpu-5090" ]]; then
|
||||||
source /etc/profile.d/sglang-ci.sh
|
source /etc/profile.d/sglang-ci.sh
|
||||||
fi
|
fi
|
||||||
cd test/
|
|
||||||
# Collect non-empty commands into an array for counting.
|
# Collect non-empty commands into an array for counting.
|
||||||
cmds=()
|
cmds=()
|
||||||
while IFS= read -r cmd; do
|
while IFS= read -r cmd; do
|
||||||
@@ -97,10 +103,17 @@ jobs:
|
|||||||
cmd="${cmds[$idx]}"
|
cmd="${cmds[$idx]}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "."
|
echo "."
|
||||||
|
if [[ "${{ inputs.install_diffusion }}" == "true" ]]; then
|
||||||
|
echo "Begin ($i/$total): python3 -m pytest $cmd -x"
|
||||||
|
echo "."
|
||||||
|
file_start=$SECONDS
|
||||||
|
python3 -m pytest $cmd -x || exit 1
|
||||||
|
else
|
||||||
echo "Begin ($i/$total): python3 $cmd"
|
echo "Begin ($i/$total): python3 $cmd"
|
||||||
echo "."
|
echo "."
|
||||||
file_start=$SECONDS
|
file_start=$SECONDS
|
||||||
python3 $cmd -f || exit 1
|
(cd test/ && python3 $cmd -f) || exit 1
|
||||||
|
fi
|
||||||
elapsed=$(( SECONDS - file_start ))
|
elapsed=$(( SECONDS - file_start ))
|
||||||
echo "."
|
echo "."
|
||||||
echo "End ($i/$total): elapsed=${elapsed}s"
|
echo "End ($i/$total): elapsed=${elapsed}s"
|
||||||
|
|||||||
@@ -489,40 +489,104 @@ DEEPEP_SUITES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MULTIMODAL_TEST_DIR = "python/sglang/multimodal_gen/test"
|
||||||
|
|
||||||
|
MULTIMODAL_PATH_TO_RUNNER = {
|
||||||
|
"2_gpu": "2-gpu-h100",
|
||||||
|
"2-gpu": "2-gpu-h100",
|
||||||
|
}
|
||||||
|
MULTIMODAL_DEFAULT_RUNNER = "1-gpu-h100"
|
||||||
|
|
||||||
|
|
||||||
def resolve_test_file(file_part):
|
def resolve_test_file(file_part):
|
||||||
"""
|
"""
|
||||||
Resolve a user-provided file path to a path relative to test/.
|
Resolve a user-provided file path to a path relative to test/ or full path for multimodal.
|
||||||
|
|
||||||
Supports:
|
Supports:
|
||||||
- Full path: test/registered/core/test_srt_endpoint.py
|
- Full path: test/registered/core/test_srt_endpoint.py
|
||||||
- Relative to test/: registered/core/test_srt_endpoint.py
|
- Relative to test/: registered/core/test_srt_endpoint.py
|
||||||
- Bare filename: test_srt_endpoint.py (glob-matched, must be unique)
|
- Bare filename: test_srt_endpoint.py (glob-matched, must be unique)
|
||||||
|
- Multimodal paths: python/sglang/multimodal_gen/test/server/test_server_a.py
|
||||||
|
|
||||||
Returns (resolved_path, error_message). On success error_message is None.
|
Returns (resolved_path, is_multimodal, error_message). On success error_message is None.
|
||||||
"""
|
"""
|
||||||
|
# Check if it's explicitly a multimodal path
|
||||||
|
multimodal_prefixes = [
|
||||||
|
"python/sglang/multimodal_gen/test/",
|
||||||
|
"sglang/multimodal_gen/test/",
|
||||||
|
"multimodal_gen/test/",
|
||||||
|
]
|
||||||
|
for prefix in multimodal_prefixes:
|
||||||
|
if file_part.startswith(prefix):
|
||||||
|
full_path = (
|
||||||
|
file_part
|
||||||
|
if file_part.startswith("python/")
|
||||||
|
else f"python/sglang/multimodal_gen/test/{file_part[len(prefix):]}"
|
||||||
|
)
|
||||||
|
if not os.path.isfile(full_path):
|
||||||
|
return None, False, f"File not found: `{full_path}`"
|
||||||
|
return full_path, True, None
|
||||||
|
|
||||||
|
# Existing logic for test/registered/ paths
|
||||||
if file_part.startswith("test/"):
|
if file_part.startswith("test/"):
|
||||||
file_part = file_part[len("test/") :]
|
file_part = file_part[len("test/") :]
|
||||||
|
|
||||||
if "/" not in file_part:
|
if "/" not in file_part:
|
||||||
|
# Try test/registered/ first
|
||||||
matches = glob.glob(f"test/registered/**/{file_part}", recursive=True)
|
matches = glob.glob(f"test/registered/**/{file_part}", recursive=True)
|
||||||
if len(matches) == 0:
|
|
||||||
|
# Try multimodal test directory
|
||||||
|
mm_matches = glob.glob(f"{MULTIMODAL_TEST_DIR}/**/{file_part}", recursive=True)
|
||||||
|
# Filter to only test files
|
||||||
|
mm_matches = [m for m in mm_matches if os.path.basename(m).startswith("test_")]
|
||||||
|
|
||||||
|
if len(matches) == 1 and len(mm_matches) == 0:
|
||||||
|
return matches[0][len("test/") :], False, None
|
||||||
|
if len(matches) == 0 and len(mm_matches) == 1:
|
||||||
|
return mm_matches[0], True, None
|
||||||
|
|
||||||
|
all_matches = matches + mm_matches
|
||||||
|
if len(all_matches) == 0:
|
||||||
return (
|
return (
|
||||||
None,
|
None,
|
||||||
f"No test file found matching `{file_part}` under `test/registered/`.",
|
False,
|
||||||
|
f"No test file found matching `{file_part}` under `test/registered/` or `{MULTIMODAL_TEST_DIR}/`.",
|
||||||
)
|
)
|
||||||
if len(matches) > 1:
|
if len(all_matches) > 1:
|
||||||
match_list = "\n".join(f"- `{m}`" for m in sorted(matches))
|
match_list = "\n".join(f"- `{m}`" for m in sorted(all_matches))
|
||||||
return None, (
|
return (
|
||||||
f"Ambiguous filename `{file_part}` — matched {len(matches)} files:\n\n"
|
None,
|
||||||
|
False,
|
||||||
|
(
|
||||||
|
f"Ambiguous filename `{file_part}` — matched {len(all_matches)} files:\n\n"
|
||||||
f"{match_list}\n\n"
|
f"{match_list}\n\n"
|
||||||
f"Please provide the full path, e.g. `/rerun-test {matches[0]}`"
|
f"Please provide the full path, e.g. `/rerun-test {all_matches[0]}`"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return matches[0][len("test/") :], None
|
# Shouldn't reach here, but handle gracefully
|
||||||
|
if mm_matches:
|
||||||
|
return mm_matches[0], True, None
|
||||||
|
return matches[0][len("test/") :], False, None
|
||||||
|
|
||||||
|
# Path with directory - check test/ location
|
||||||
full_path = f"test/{file_part}"
|
full_path = f"test/{file_part}"
|
||||||
if not os.path.isfile(full_path):
|
if os.path.isfile(full_path):
|
||||||
return None, f"File not found: `{full_path}`"
|
return file_part, False, None
|
||||||
return file_part, None
|
|
||||||
|
return None, False, f"File not found: `{full_path}`"
|
||||||
|
|
||||||
|
|
||||||
|
def detect_multimodal_suite(file_path):
|
||||||
|
"""
|
||||||
|
Determine runner for a multimodal gen test file based on its path.
|
||||||
|
|
||||||
|
Returns (runner_label, error_message).
|
||||||
|
"""
|
||||||
|
# Check path components and basename for GPU count hints
|
||||||
|
for pattern, runner in MULTIMODAL_PATH_TO_RUNNER.items():
|
||||||
|
if pattern in file_path:
|
||||||
|
return runner, None
|
||||||
|
return MULTIMODAL_DEFAULT_RUNNER, None
|
||||||
|
|
||||||
|
|
||||||
def detect_suite(file_path_from_test):
|
def detect_suite(file_path_from_test):
|
||||||
@@ -598,10 +662,35 @@ def _resolve_test_spec(test_spec):
|
|||||||
if test_selector:
|
if test_selector:
|
||||||
test_selector = test_selector.strip()
|
test_selector = test_selector.strip()
|
||||||
|
|
||||||
resolved_path, err = resolve_test_file(file_part)
|
resolved_path, is_multimodal, err = resolve_test_file(file_part)
|
||||||
if err:
|
if err:
|
||||||
return {"spec": test_spec, "error": err}
|
return {"spec": test_spec, "error": err}
|
||||||
|
|
||||||
|
if is_multimodal:
|
||||||
|
runner_label, err = detect_multimodal_suite(resolved_path)
|
||||||
|
if err:
|
||||||
|
return {"spec": test_spec, "error": err}
|
||||||
|
|
||||||
|
# For multimodal pytest tests, use :: separator for test selection
|
||||||
|
test_command = resolved_path
|
||||||
|
if test_selector:
|
||||||
|
test_command = f"{resolved_path}::{test_selector}"
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Resolved (multimodal): file={resolved_path}, selector={test_selector}, "
|
||||||
|
f"runner={runner_label}, command='{test_command}'"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"spec": test_spec,
|
||||||
|
"test_command": test_command,
|
||||||
|
"suite": "multimodal",
|
||||||
|
"runner_label": runner_label,
|
||||||
|
"use_deepep": False,
|
||||||
|
"is_cpu": False,
|
||||||
|
"install_diffusion": True,
|
||||||
|
"error": None,
|
||||||
|
}
|
||||||
|
|
||||||
suite, runner_label, use_deepep, is_cpu, err = detect_suite(resolved_path)
|
suite, runner_label, use_deepep, is_cpu, err = detect_suite(resolved_path)
|
||||||
if err:
|
if err:
|
||||||
return {"spec": test_spec, "error": err}
|
return {"spec": test_spec, "error": err}
|
||||||
@@ -622,6 +711,7 @@ def _resolve_test_spec(test_spec):
|
|||||||
"runner_label": runner_label,
|
"runner_label": runner_label,
|
||||||
"use_deepep": use_deepep,
|
"use_deepep": use_deepep,
|
||||||
"is_cpu": is_cpu,
|
"is_cpu": is_cpu,
|
||||||
|
"install_diffusion": False,
|
||||||
"error": None,
|
"error": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,6 +727,7 @@ def _dispatch_batch(gh_repo, pr, batch, token):
|
|||||||
runner_label = batch[0]["runner_label"]
|
runner_label = batch[0]["runner_label"]
|
||||||
use_deepep = batch[0]["use_deepep"]
|
use_deepep = batch[0]["use_deepep"]
|
||||||
is_cpu = batch[0]["is_cpu"]
|
is_cpu = batch[0]["is_cpu"]
|
||||||
|
install_diffusion = batch[0].get("install_diffusion", False)
|
||||||
|
|
||||||
# Join multiple commands with newlines for the workflow to iterate over
|
# Join multiple commands with newlines for the workflow to iterate over
|
||||||
combined_command = "\n".join(test_commands)
|
combined_command = "\n".join(test_commands)
|
||||||
@@ -667,6 +758,7 @@ def _dispatch_batch(gh_repo, pr, batch, token):
|
|||||||
"runner_label": runner_label,
|
"runner_label": runner_label,
|
||||||
"use_deepep": str(use_deepep).lower(),
|
"use_deepep": str(use_deepep).lower(),
|
||||||
"is_cpu": str(is_cpu).lower(),
|
"is_cpu": str(is_cpu).lower(),
|
||||||
|
"install_diffusion": str(install_diffusion).lower(),
|
||||||
}
|
}
|
||||||
if is_fork:
|
if is_fork:
|
||||||
ref = "main"
|
ref = "main"
|
||||||
@@ -777,10 +869,15 @@ def handle_rerun_test(gh_repo, pr, comment, user_perms, test_specs, token):
|
|||||||
else:
|
else:
|
||||||
resolved.append(r)
|
resolved.append(r)
|
||||||
|
|
||||||
# Phase 2: Group by (runner_label, use_deepep, is_cpu)
|
# Phase 2: Group by (runner_label, use_deepep, is_cpu, install_diffusion)
|
||||||
groups = {}
|
groups = {}
|
||||||
for r in resolved:
|
for r in resolved:
|
||||||
key = (r["runner_label"], r["use_deepep"], r["is_cpu"])
|
key = (
|
||||||
|
r["runner_label"],
|
||||||
|
r["use_deepep"],
|
||||||
|
r["is_cpu"],
|
||||||
|
r.get("install_diffusion", False),
|
||||||
|
)
|
||||||
groups.setdefault(key, []).append(r)
|
groups.setdefault(key, []).append(r)
|
||||||
|
|
||||||
# Phase 3: Dispatch one workflow per group
|
# Phase 3: Dispatch one workflow per group
|
||||||
@@ -792,6 +889,16 @@ def handle_rerun_test(gh_repo, pr, comment, user_perms, test_specs, token):
|
|||||||
lines = []
|
lines = []
|
||||||
for dr in dispatch_results:
|
for dr in dispatch_results:
|
||||||
if dr["success"]:
|
if dr["success"]:
|
||||||
|
install_diff = any(
|
||||||
|
r.get("install_diffusion", False)
|
||||||
|
for r in resolved
|
||||||
|
if r["spec"] in dr["specs"]
|
||||||
|
)
|
||||||
|
if install_diff:
|
||||||
|
cmds = "\n".join(
|
||||||
|
f"python3 -m pytest {cmd} -x" for cmd in dr["test_commands"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
cmds = "\n".join(
|
cmds = "\n".join(
|
||||||
f"cd test/ && python3 {cmd}" for cmd in dr["test_commands"]
|
f"cd test/ && python3 {cmd}" for cmd in dr["test_commands"]
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user