Skip to content

Commit 33abe8f

Browse files
authored
Merge branch 'main' into support_kunlunxin_backend_2
2 parents 6ffa703 + 885aaef commit 33abe8f

15 files changed

Lines changed: 433 additions & 102 deletions

File tree

.github/configs/enflame.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2026 FlagOS Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Enflame S60 hardware configuration.
16+
platform: enflame
17+
18+
ci_image: harbor.baai.ac.cn/flagos-dev/vllm-plugin-fl:v0.20.2-enflame-ci
19+
20+
# Replace this with the exact label configured on the Enflame S60 runner.
21+
runner_labels:
22+
- enflame-s60
23+
24+
# Model files are persistent host data and use the same path in the container.
25+
container_volumes:
26+
- /data:/data
27+
28+
container_options: >-
29+
--hostname vllm-plugin-fl
30+
--ipc=host
31+
--privileged
32+
--env VLLM_PLUGINS=fl
33+
--env TOPS_VISIBLE_DEVICES=2,3
34+
--env VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=99999
35+
--env TORCH_GCU_ENABLE_INT64_AND_UINT64=1
36+
--env ENABLE_I64_CHECK=0
37+
--env TORCHGCU_INDUCTOR_ENABLE=1

.github/configs/platforms.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ platforms:
2323
cuda:
2424
enabled: true
2525
ascend:
26-
enabled: false
26+
enabled: true
2727
hygon:
2828
enabled: true
2929
metax:
3030
enabled: true
3131
musa:
3232
enabled: true
33+
enflame:
34+
enabled: true

.github/scripts/enflame/check.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
# Copyright (c) 2025 BAAI. All rights reserved.
3+
# Check Enflame S60 availability.
4+
set -euo pipefail
5+
6+
echo "Current time: $(date '+%Y-%m-%d %H:%M:%S')"
7+
echo "=== Checking Enflame S60 availability ==="
8+
9+
if ! command -v efsmi >/dev/null 2>&1; then
10+
echo "::error::efsmi is not available in the CI container."
11+
exit 1
12+
fi
13+
14+
efsmi
15+
16+
python - <<'PY'
17+
import torch
18+
import torch_gcu # noqa: F401
19+
20+
if not hasattr(torch, "gcu") or not torch.gcu.is_available():
21+
raise RuntimeError("Enflame GCU is not available")
22+
23+
count = torch.gcu.device_count()
24+
print(f"Enflame GCU count: {count}")
25+
if count < 2:
26+
raise RuntimeError(f"At least 2 GCUs are required, found {count}")
27+
PY

.github/scripts/enflame/setup.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/bash
2+
# Copyright (c) 2025 BAAI. All rights reserved.
3+
# Setup script for Enflame S60 CI.
4+
set -euo pipefail
5+
6+
: "${VLLM_PLUGINS:?VLLM_PLUGINS is not set}"
7+
: "${TOPS_VISIBLE_DEVICES:?TOPS_VISIBLE_DEVICES is not set}"
8+
9+
git config --global --add safe.directory "$(pwd)"
10+
11+
if [[ -n "${GITHUB_ENV:-}" ]]; then
12+
for name in \
13+
VLLM_PLUGINS \
14+
TOPS_VISIBLE_DEVICES \
15+
VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS \
16+
TORCH_GCU_ENABLE_INT64_AND_UINT64 \
17+
ENABLE_I64_CHECK \
18+
TORCHGCU_INDUCTOR_ENABLE; do
19+
echo "${name}=${!name}" >> "${GITHUB_ENV}"
20+
done
21+
fi
22+
23+
# The vendor runtime, vLLM, and FlagGems come from the pinned base image.
24+
python -m pip install --no-build-isolation --no-deps -e .
25+
26+
python - <<'PY'
27+
import flag_gems
28+
import torch
29+
import torch_gcu # noqa: F401
30+
import vllm
31+
import vllm_fl
32+
33+
print(f"vLLM import ok: {vllm.__version__}")
34+
print(f"vLLM-FL import ok: {vllm_fl.__file__}")
35+
print(f"FlagGems import ok: {getattr(flag_gems, '__version__', 'unknown')}")
36+
print(f"FlagGems vendor: {getattr(flag_gems, 'vendor_name', 'auto-detected')}")
37+
print(f"Torch import ok: {torch.__version__}")
38+
print(f"Accelerator available: {torch.gcu.is_available()}")
39+
print(f"Accelerator count: {torch.gcu.device_count()}")
40+
PY

.github/scripts/generate_matrix.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
REPO_ROOT = Path(__file__).resolve().parents[2]
3232
PLATFORMS_DIR = REPO_ROOT / "tests" / "platforms"
33+
sys.path.insert(0, str(REPO_ROOT))
3334

3435
# YAML task key → test directory name
3536
TASK_DIR_MAP: dict[str, str] = {
@@ -130,6 +131,70 @@ def build_e2e_matrix(
130131
return entries
131132

132133

134+
def check_model_paths(
135+
platform: str,
136+
device: str | None = None,
137+
cases: str | None = None,
138+
) -> int:
139+
"""Validate that the selected CI matrix model directories exist."""
140+
from tests.utils.model_config import ModelConfig
141+
142+
if not cases:
143+
print("[check-models] No E2E model cases selected.")
144+
return 0
145+
146+
selected_cases = json.loads(cases)
147+
if not isinstance(selected_cases, list):
148+
print(
149+
"::error title=Invalid cases::--cases must be a JSON array", file=sys.stderr
150+
)
151+
return 1
152+
153+
missing = 0
154+
print(f"[check-models] Platform: {platform}")
155+
print(f"[check-models] Device: {device or ''}")
156+
print(f"[check-models] Cases: {len(selected_cases)}")
157+
158+
for case_entry in selected_cases:
159+
model = str(case_entry["model"])
160+
case = str(case_entry["case"])
161+
try:
162+
model_cfg = ModelConfig.load(
163+
model,
164+
case,
165+
platform=platform,
166+
device=device or None,
167+
)
168+
except Exception as exc:
169+
message = f"{model}/{case}: {exc}"
170+
print(
171+
f"::error title=Invalid model config::{message}",
172+
file=sys.stderr,
173+
)
174+
missing += 1
175+
continue
176+
177+
model_path = model_cfg.model
178+
exists = bool(model_path) and Path(model_path).exists()
179+
status = "OK" if exists else "MISSING"
180+
print(f"[check-models] {status}: {model}/{case} -> {model_path}")
181+
if not exists:
182+
message = f"{model}/{case} requires model path: {model_path}"
183+
print(
184+
f"::error file=tests/models/{model}/{case}.yaml,"
185+
f"title=Missing test model::{message}",
186+
file=sys.stderr,
187+
)
188+
missing += 1
189+
190+
if missing:
191+
print(f"[check-models] Missing or invalid model cases: {missing}")
192+
return 1
193+
194+
print("[check-models] All selected model paths exist.")
195+
return 0
196+
197+
133198
# ---------------------------------------------------------------------------
134199
# Changed-file filtering for PR-only smart skip
135200
# ---------------------------------------------------------------------------
@@ -244,8 +309,35 @@ def main(argv: list[str] | None = None) -> int:
244309
"When provided and non-empty, the e2e matrix is filtered to only "
245310
"include tests affected by those changes (PR smart-skip).",
246311
)
312+
parser.add_argument(
313+
"--check-models",
314+
action="store_true",
315+
help=(
316+
"Validate that selected E2E model paths exist instead of "
317+
"generating matrices."
318+
),
319+
)
320+
parser.add_argument(
321+
"--device",
322+
default=None,
323+
help="Device name to check when --check-models is set.",
324+
)
325+
parser.add_argument(
326+
"--cases",
327+
default=None,
328+
help=(
329+
"JSON array of {model, case} entries to check when --check-models is set."
330+
),
331+
)
247332
args = parser.parse_args(argv)
248333

334+
if args.check_models:
335+
return check_model_paths(
336+
args.platform,
337+
device=args.device,
338+
cases=args.cases,
339+
)
340+
249341
config = load_platform(args.platform)
250342
devices = get_device_sections(config)
251343
unsupported = config.get("unsupported_features", [])

.github/scripts/hygon/setup.sh

Lines changed: 4 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -15,92 +15,22 @@ git config --global --add safe.directory "$(pwd)"
1515
: "${DEVICE_LIB_PATH:?DEVICE_LIB_PATH is not set}"
1616
: "${LD_LIBRARY_PATH:?LD_LIBRARY_PATH is not set}"
1717

18-
USE_IMAGE_PLUGIN="${HYGON_USE_IMAGE_PLUGIN:-1}"
19-
20-
if [[ "${USE_IMAGE_PLUGIN}" == "1" ]]; then
21-
IMAGE_PLUGIN_ROOT="${VLLM_FL_IMAGE_PLUGIN_ROOT:-/opt/vllm-src/vllm-plugin-FL}"
22-
test -d "${IMAGE_PLUGIN_ROOT}/vllm_fl"
23-
24-
# The Hygon CI image already contains the validated plugin commit. Keep the
25-
# checkout available for tests and configs, but load vllm_fl from the image.
26-
HYGON_SITE_DIR="${RUNNER_TEMP:-/tmp}/hygon-python-site"
27-
mkdir -p "${HYGON_SITE_DIR}"
28-
cat > "${HYGON_SITE_DIR}/sitecustomize.py" <<'PY'
29-
import importlib.abc
30-
import importlib.util
31-
import os
32-
import sys
33-
from pathlib import Path
34-
35-
36-
class _ImageVllmFLFinder(importlib.abc.MetaPathFinder):
37-
def __init__(self, root):
38-
self.package_dir = Path(root) / "vllm_fl"
39-
40-
def find_spec(self, fullname, path=None, target=None):
41-
if fullname != "vllm_fl":
42-
return None
43-
init_file = self.package_dir / "__init__.py"
44-
if not init_file.exists():
45-
return None
46-
return importlib.util.spec_from_file_location(
47-
fullname,
48-
init_file,
49-
submodule_search_locations=[str(self.package_dir)],
50-
)
51-
52-
53-
_root = os.environ.get("VLLM_FL_IMAGE_PLUGIN_ROOT")
54-
if _root:
55-
sys.meta_path.insert(0, _ImageVllmFLFinder(_root))
56-
PY
57-
58-
export VLLM_FL_IMAGE_PLUGIN_ROOT="${IMAGE_PLUGIN_ROOT}"
59-
export PYTHONPATH="${HYGON_SITE_DIR}${PYTHONPATH:+:${PYTHONPATH}}"
60-
61-
if [[ -n "${GITHUB_ENV:-}" ]]; then
62-
{
63-
echo "VLLM_FL_IMAGE_PLUGIN_ROOT=${VLLM_FL_IMAGE_PLUGIN_ROOT}"
64-
echo "PYTHONPATH=${PYTHONPATH}"
65-
} >> "${GITHUB_ENV}"
66-
fi
67-
else
68-
unset VLLM_FL_IMAGE_PLUGIN_ROOT
69-
fi
70-
71-
export HYGON_USE_IMAGE_PLUGIN="${USE_IMAGE_PLUGIN}"
18+
unset VLLM_FL_IMAGE_PLUGIN_ROOT
19+
unset HYGON_USE_IMAGE_PLUGIN
7220

7321
echo "DTK_HOME=${DTK_HOME}"
7422
echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
75-
echo "HYGON_USE_IMAGE_PLUGIN=${HYGON_USE_IMAGE_PLUGIN}"
76-
if [[ "${USE_IMAGE_PLUGIN}" == "1" ]]; then
77-
echo "VLLM_FL_IMAGE_PLUGIN_ROOT=${VLLM_FL_IMAGE_PLUGIN_ROOT}"
78-
echo "PYTHONPATH=${PYTHONPATH}"
79-
fi
8023
test -e "${HIP_PATH}/lib/libgalaxyhip.so.5"
8124
test -e "${DTK_HOME}/llvm/lib/libomp.so"
8225

83-
python - <<'PY'
84-
import os
85-
from pathlib import Path
26+
python -m pip install --no-build-isolation --no-deps -e .
8627

28+
python - <<'PY'
8729
import flag_gems
8830
import torch
8931
import vllm
9032
import vllm_fl
9133
92-
if os.environ.get("HYGON_USE_IMAGE_PLUGIN", "1") == "1":
93-
image_plugin_root = Path(os.environ["VLLM_FL_IMAGE_PLUGIN_ROOT"]).resolve()
94-
expected_package = image_plugin_root / "vllm_fl"
95-
plugin_file = Path(vllm_fl.__file__).resolve()
96-
if (
97-
plugin_file != expected_package / "__init__.py"
98-
and expected_package not in plugin_file.parents
99-
):
100-
raise RuntimeError(
101-
f"Unexpected vllm_fl path: {plugin_file}; expected under {expected_package}"
102-
)
103-
10434
print(f"vLLM import ok: {vllm.__version__}")
10535
print(f"vLLM-FL import ok: {vllm_fl.__file__}")
10636
print(f"FlagGems import ok: {getattr(flag_gems, '__version__', 'unknown')}")

.github/workflows/_e2e_test.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,17 @@ jobs:
9393
- name: Check device availability
9494
run: bash .github/scripts/${{ inputs.platform }}/check.sh
9595

96-
- name: Prepare host models
97-
run: |
98-
model_script=".github/scripts/${{ inputs.platform }}/download_models.sh"
99-
if [ -f "${model_script}" ]; then
100-
bash "${model_script}"
101-
fi
102-
10396
- name: Install project
10497
run: bash .github/scripts/${{ inputs.platform }}/setup.sh
10598

99+
- name: Check test models
100+
run: |
101+
python .github/scripts/generate_matrix.py \
102+
--platform ${{ inputs.platform }} \
103+
--device "${{ inputs.device }}" \
104+
--cases '${{ inputs.cases }}' \
105+
--check-models
106+
106107
- name: Run E2E test
107108
run: |
108109
python tests/run.py \

.github/workflows/ci.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,14 @@ jobs:
155155
with:
156156
platform: musa
157157
secrets: inherit
158+
159+
# ============================================================
160+
# Job 4f: Enflame platform testing
161+
# ============================================================
162+
test-enflame:
163+
needs: discover
164+
if: contains(fromJson(needs.discover.outputs.platforms), 'enflame')
165+
uses: ./.github/workflows/_platform_test.yml
166+
with:
167+
platform: enflame
168+
secrets: inherit

docker/ascend/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ The common convention is:
2727
└── Qwen3-0.6B/
2828
```
2929

30-
The E2E workflow runs `.github/scripts/ascend/download_models.sh`
31-
idempotently. It downloads `Qwen/Qwen3-0.6B` through
32-
`https://hf-mirror.com` only when the model is absent.
30+
E2E jobs expect these model directories to be provisioned before CI starts.
31+
The workflow validates the selected model paths with the shared
32+
`.github/scripts/generate_matrix.py --check-models` check before running
33+
`tests/run.py`.
3334

3435
To avoid occupying a scarce NPU runner during development, validate changes
3536
on the host with the same image, setup script, and `tests/run.py` command

0 commit comments

Comments
 (0)