Skip to content

Commit 2d86230

Browse files
committed
chore: sanitize PR 174 and preserve XPU test support
1 parent d24daa1 commit 2d86230

67 files changed

Lines changed: 839 additions & 660 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ bash tests/embodied/run.sh --chip A800 --models fastwam_ddp fastwam_ddp_zero1
8989
# Collect baselines for the current chip
9090
bash tests/embodied/run.sh --chip A800 --auto_collect_baseline
9191

92-
# Prepare artifacts through BOS synchronization
93-
bash tests/embodied/run.sh --chip A800 --prepare
92+
# Artifacts are provisioned by the CI workflow/self-hosted runner before this step.
9493

9594
# Validate commands/configuration without training
9695
bash tests/embodied/run.sh --chip A800 --dry_run
@@ -99,7 +98,7 @@ bash tests/embodied/run.sh --chip A800 --dry_run
9998
Embodied test conventions:
10099

101100
- `tests/embodied/config/env.sh` centralizes `EMBODIED_CI_ROOT`,
102-
`LOCAL_VLA_ARTIFACTS_ROOT`, log, baseline, and BOS tool paths. Prefer environment
101+
`LOCAL_VLA_ARTIFACTS_ROOT`, log, and baseline paths. Prefer environment
103102
overrides or this file when moving the suite to another machine.
104103
- Add every new training script to `tests/embodied/config/scripts.yaml`; the manifest
105104
path is relative to `examples/embodied/`. Add a baseline under

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ See the full documentation for installation, tutorials, and advanced usage — [
7272

7373
**1. Install** — using **prebuilt Docker images** or **source build**:
7474
- **NVIDIA GPU**: [Installation Guide](https://loongforge.readthedocs.io/en/latest/get_started/installation.html)
75+
- **Kunlun XPU**: [Installation Guide](https://loongforge.readthedocs.io/en/latest/kunlun_tutorial/install_p800.html)
7576

7677
**2. Launch your first training run** — follow a tutorial for your target hardware and modality:
7778
- **NVIDIA GPU**: [LLM](https://loongforge.readthedocs.io/en/latest/llm_tutorial/quick_start_llm_pretrain.html) · [VLM](https://loongforge.readthedocs.io/en/latest/vlm_tutorial/quick_start_vlm_pretrain.html) · [VLA & WAM](https://loongforge.readthedocs.io/en/latest/embodied_tutorial/overview.html) · [Diffusion](https://loongforge.readthedocs.io/en/latest/wan_tutorial/quick_start_wan_training.html)
@@ -239,7 +240,7 @@ LoongForge/
239240
├── tools/ # Checkpoint conversion, data preprocessing
240241
├── ops/ # Custom fused operators (incl. open-sourced TileLang)
241242
├── patches/ # TransformerEngine patches
242-
├── docker/ # Dockerfile (GPU)
243+
├── docker/ # Dockerfiles (GPU & XPU)
243244
├── tests/ # E2E test suite (YAML-driven)
244245
└── docs/ # Documentation
245246
```

README_zh.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272

7373
**1. 安装** —— 可使用 **预构建镜像****源码构建**
7474
- **NVIDIA GPU**[安装指南](https://loongforge.readthedocs.io/zh-cn/latest/get_started/installation.html)
75+
- **昆仑芯 XPU**[安装指南](https://loongforge.readthedocs.io/zh-cn/latest/kunlun_tutorial/install_p800.html)
7576

7677
**2. 启动你的第一个训练任务** —— 根据目标硬件与模态选择教程:
7778
- **NVIDIA GPU**[LLM](https://loongforge.readthedocs.io/zh-cn/latest/llm_tutorial/quick_start_llm_pretrain.html) · [VLM](https://loongforge.readthedocs.io/zh-cn/latest/vlm_tutorial/quick_start_vlm_pretrain.html) · [VLA & WAM](https://loongforge.readthedocs.io/zh-cn/latest/embodied_tutorial/overview.html) · [Diffusion (WAN)](https://loongforge.readthedocs.io/zh-cn/latest/wan_tutorial/quick_start_wan_training.html)
@@ -238,7 +239,7 @@ LoongForge/
238239
├── tools/ # Checkpoint 转换、数据预处理
239240
├── ops/ # 自定义融合算子(含开源的 TileLang 版本)
240241
├── patches/ # TransformerEngine 补丁
241-
├── docker/ # Dockerfile(GPU)
242+
├── docker/ # Dockerfile(GPU & XPU
242243
├── tests/ # 端到端测试(YAML 驱动)
243244
└── docs/ # 文档
244245
```

ci/sensitive_rules.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,6 @@
207207
"as a path or symbol default, break external users.",
208208
"pattern": r"(?i)\bAIAK[-_](?:Training[-_](?:Omni|LLM)|Megatron)\b"
209209
r"|\bBaigeOmni\b"
210-
r"|\bLoongForge-VLA\b"
211210
r"|\baiak_training_omni\b"
212211
r"|\bset_aiak_\w+|\binitialize_baige_\w+|\bUSE_AIAK_\w+"
213212
r"|\baiak-ckpt\b",

docker/Dockerfile.xpu

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
ARG BASE_IMAGE="loongforge/loongforge_kunlun:py310_torch25"
2+
FROM ${BASE_IMAGE}
3+
4+
# =========================================================
5+
# Build Arguments
6+
# =========================================================
7+
ARG XPYTORCH_URL_ARG=""
8+
ARG KUNLUN_OPS_URL_ARG=""
9+
ARG COCOPOD_URL_ARG=""
10+
ARG XSPEEDGATE_URL_ARG=""
11+
12+
# =========================================================
13+
# Base System Configuration + APT Source Replacement
14+
# =========================================================
15+
COPY ./LoongForge /workspace/LoongForge
16+
SHELL ["/bin/bash", "-c"]
17+
ENV PATH=/opt/loongforge_kunlun/bin:${PATH}
18+
19+
RUN wget https://ai-data.bj.bcebos.com/open-source/xcudart.tar.gz && tar -xzvf xcudart.tar.gz -C /opt/loongforge_kunlun/ && rm -rf xcudart*
20+
ENV LD_LIBRARY_PATH=/usr/local/cuda-11.7/:/opt/loongforge_kunlun/xcudart/lib/:${LD_LIBRARY_PATH}
21+
22+
# ======================== Install XPU Requirements ========================
23+
RUN set -exuo pipefail && \
24+
download_xpytorch() { \
25+
local XPYTORCH_URL=$1; \
26+
local XPYTORCH_FILE=$(basename "${XPYTORCH_URL}"); \
27+
echo "download xpytorch package: ${XPYTORCH_URL}" && \
28+
wget "${XPYTORCH_URL}" && \
29+
bash "${XPYTORCH_FILE}" --noexec --target xpytorch_unpack && cd xpytorch_unpack && \
30+
sed -i 's/CONDA_PREFIX/VIRTUAL_ENV/g' setup.sh && \
31+
python -m ensurepip --upgrade && \
32+
python -m pip install --upgrade pip && \
33+
export VIRTUAL_ENV=/opt/loongforge_kunlun && bash setup.sh && \
34+
cd .. && rm -rf "${XPYTORCH_FILE}" xpytorch_unpack; \
35+
}; \
36+
source /opt/loongforge_kunlun/bin/activate && \
37+
if [ -n "${XPYTORCH_URL_ARG}" ]; then \
38+
XPYTORCH_URL="${XPYTORCH_URL_ARG}"; \
39+
echo "use specified xpytorch version: ${XPYTORCH_URL}"; \
40+
else \
41+
XPYTORCH_URL=$(python3 -c "import json; print(json.load(open('/workspace/LoongForge/docker/xpu_packages.json'))['torch25']['xpytorch']['url'])"); \
42+
echo "use default xpytorch version from json: ${XPYTORCH_URL}"; \
43+
fi && \
44+
download_xpytorch "${XPYTORCH_URL}"
45+
46+
# ======================== Fix xpytorch_import_hook ========================
47+
RUN source /opt/loongforge_kunlun/bin/activate && bash /workspace/LoongForge/docker/fix_xpytorch_hook.sh
48+
49+
# ======================== Install XPU Packages ========================
50+
RUN source /opt/loongforge_kunlun/bin/activate && \
51+
if [ -n "${COCOPOD_URL_ARG}" ]; then \
52+
COCOPOD_URL="${COCOPOD_URL_ARG}"; \
53+
else \
54+
COCOPOD_URL=$(python3 -c "import json; print(json.load(open('/workspace/LoongForge/docker/xpu_packages.json'))['torch25']['cocopod']['url'])"); \
55+
fi && \
56+
if [ -n "${XSPEEDGATE_URL_ARG}" ]; then \
57+
XSPEEDGATE_URL="${XSPEEDGATE_URL_ARG}"; \
58+
else \
59+
XSPEEDGATE_URL=$(python3 -c "import json; print(json.load(open('/workspace/LoongForge/docker/xpu_packages.json'))['torch25']['xspeedgate']['url'])"); \
60+
fi && \
61+
if [ -n "${KUNLUN_OPS_URL_ARG}" ]; then \
62+
KUNLUN_OPS_URL="${KUNLUN_OPS_URL_ARG}"; \
63+
else \
64+
KUNLUN_OPS_URL=$(python3 -c "import json; print(json.load(open('/workspace/LoongForge/docker/xpu_packages.json'))['torch25']['kunlun_ops']['url'])"); \
65+
fi && \
66+
if [ "${COCOPOD_URL}" = "__SET_WITH_COCOPOD_URL_ARG__" ] || [ "${XSPEEDGATE_URL}" = "__SET_WITH_XSPEEDGATE_URL_ARG__" ]; then \
67+
echo "Set COCOPOD_URL_ARG and XSPEEDGATE_URL_ARG when building the XPU image." >&2; \
68+
exit 1; \
69+
fi && \
70+
pip install "${COCOPOD_URL}" "${XSPEEDGATE_URL}" "${KUNLUN_OPS_URL}"
71+
72+
# ======================== Install Base Requirements ========================
73+
RUN set -exuo pipefail && \
74+
source /opt/loongforge_kunlun/bin/activate && \
75+
# for deepspeed cpu adam
76+
DS_BUILD_CPU_ADAM=1 BUILD_UTILS=1 pip install --no-build-isolation deepspeed==0.18.5 -U && \
77+
echo "alias ll='ls -alF'" > /etc/profile.d/alias.sh; \
78+
echo "source /etc/profile.d/alias.sh" >> ~/.bashrc && \
79+
apt-get update && \
80+
apt-get install -y ffmpeg=7:6.1.1-3ubuntu5 && \
81+
apt-get install -y libomp-dev && \
82+
cd /workspace/LoongForge/ && \
83+
pip install uv && \
84+
uv pip install --no-config --no-cache -e ".[xpu]"
85+
86+
# =========================================================
87+
# Lerobot
88+
# =========================================================
89+
RUN set -exuo pipefail && \
90+
install_lerobot() { \
91+
echo "Installing lerobot..." && \
92+
pip install "dmuon @ git+https://github.com/X-Square-Robot/dmuon.git" && \
93+
cd /workspace/LoongForge/ && \
94+
uv pip install --no-config --system --break-system-packages --no-cache -e ".[vla]" && \
95+
cd /workspace && \
96+
if [ -d "lerobot" ]; then \
97+
rm -rf lerobot; \
98+
fi && \
99+
git clone --no-checkout https://github.com/huggingface/lerobot.git && \
100+
cd lerobot && \
101+
git checkout tags/v0.5.0 && \
102+
if [ -f "requirements-ubuntu.txt" ]; then \
103+
sed -i '/evdev/d' requirements-ubuntu.txt; \
104+
fi && \
105+
pip install func_timeout pytest-xdist prettytable==3.11.0 huggingface-hub==0.34.0 && \
106+
sed -i 's/^class Backtrackable\[T\]:$/from typing import Iterable, TypeVar, Generic\nT = TypeVar('\''T'\'')\nclass Backtrackable(Generic[T]):/' ./src/lerobot/datasets/utils.py && \
107+
sed -i 's/^def deserialize_json_into_object\[T: JsonLike\](fpath: Path, obj: T) -> T:$/from typing import TypeVar\nT = TypeVar('\''T'\'', bound=JsonLike)\ndef deserialize_json_into_object(fpath: Path, obj: T) -> T:/' ./src/lerobot/utils/io_utils.py && \
108+
sed -i '/^@dataclass$/{N; s/^@dataclass\nclass DataProcessorPipeline\[TInput, TOutput\](HubMixin):$/from typing import TypeVar, Generic\nTInput = TypeVar('\''TInput'\'')\nTOutput = TypeVar('\''TOutput'\'')\n@dataclass\nclass DataProcessorPipeline(Generic[TInput, TOutput], HubMixin):/}' ./src/lerobot/processor/pipeline.py && \
109+
sed -i 's/^\([[:space:]]*\)from typing import TYPE_CHECKING, Literal, TypedDict, Unpack$/\1from typing import TYPE_CHECKING, Literal, TypedDict\n\1from typing_extensions import Unpack/' ./src/lerobot/policies/pi0_fast/modeling_pi0_fast.py && \
110+
sed -i 's/^\([[:space:]]*\)from typing import TYPE_CHECKING, Literal, TypedDict, Unpack$/\1from typing import TYPE_CHECKING, Literal, TypedDict\n\1from typing_extensions import Unpack/' ./src/lerobot/policies/pi05/modeling_pi05.py && \
111+
sed -i 's/^\([[:space:]]*\)from typing import TYPE_CHECKING, Literal, TypedDict, Unpack$/\1from typing import TYPE_CHECKING, Literal, TypedDict\n\1from typing_extensions import Unpack/' ./src/lerobot/policies/pi0/modeling_pi0.py && \
112+
sed -i 's/^\([[:space:]]*\)from typing import TypedDict, Unpack$/\1from typing import TypedDict\n\1from typing_extensions import Unpack/' ./src/lerobot/policies/smolvla/modeling_smolvla.py && \
113+
sed -i 's/^\([[:space:]]*\)from typing import TypedDict, TypeVar, Unpack$/\1from typing import TypedDict, TypeVar\n\1from typing_extensions import Unpack/' ./src/lerobot/policies/pretrained.py && \
114+
sed -i 's/^\([[:space:]]*\)from typing import Any, TypedDict, Unpack$/\1from typing import Any, TypedDict\n\1from typing_extensions import Unpack/' ./src/lerobot/policies/factory.py && \
115+
sed -i '/^[[:space:]]*type NameOrID = str | int$/{N; s/^\([[:space:]]*\)type NameOrID = str | int\n[[:space:]]*type Value = int | float$/from typing import TypeAlias, Union\n\1NameOrID: TypeAlias = Union[str, int]\n\1Value: TypeAlias = Union[int, float]/}' ./src/lerobot/motors/motors_bus.py && \
116+
sed -i -e '/"torch[>=<]/d' \
117+
-e 's/"torchvision[^"]*",/"torchvision==0.20.1",/' \
118+
-e '/python-can/d' \
119+
-e '/lerobot\[can-dep\]/d' \
120+
-e '/lerobot\[damiao\]/d' \
121+
-e '/unitree_g1 = \[/,/]/ { /matplotlib/d; /pin/d; /meshcat/d; /casadi/d; }' \
122+
-e '/lerobot-setup-can/d' \
123+
-e '/\[tool.setuptools.package-data\]/d' \
124+
-e '/lerobot = \["envs\/\*\.json"\]/d' \
125+
-e '/"OT_VALUE"/d' \
126+
-e 's/requires-python = ">=3\.12"/requires-python = ">=3\.10"/g' \
127+
-e '/huggingface-hub>=1\.0\.0,<2\.0\.0/d' \
128+
pyproject.toml && \
129+
pip install --no-build-isolation -e ".[pi,peft-dep,scipy-dep]" && \
130+
sed -i 's|tokenizer_name="google/paligemma-3b-pt-224",|tokenizer_name=config.tokenizer_name,|g' ./src/lerobot/policies/pi05/processor_pi05.py && \
131+
pip install "numpy==1.26.4" && \
132+
pip install "scipy==1.15.3" && \
133+
echo "Lerobot installation completed"; \
134+
}; \
135+
source /opt/loongforge_kunlun/bin/activate && \
136+
install_lerobot;
137+
138+
139+
# =========================================================
140+
# Megatron Symlink
141+
# Scripts reference /workspace/Loong-Megatron; the actual location is
142+
# /workspace/LoongForge/third_party/Loong-Megatron (git submodule).
143+
# =========================================================
144+
RUN ln -sf /workspace/LoongForge/third_party/Loong-Megatron /workspace/Loong-Megatron
145+
146+
# =========================================================
147+
# Jupyter Configuration
148+
# =========================================================
149+
RUN mkdir -p /root/.jupyter && \
150+
touch /root/.jupyter/enterpoint.sh && \
151+
chmod +x /root/.jupyter/enterpoint.sh
152+
153+
# =========================================================
154+
# Clean Up Unused Files (Consistent with install.sh)
155+
# =========================================================
156+
RUN rm -rf /tmp/* ~/.bash_history && \
157+
rm -rf /workspace/LoongForge/.git && \
158+
rm -rf /workspace/LoongForge/third_party/Loong-Megatron/.git && \
159+
rm -rf /workspace/LoongForge/build.sh && \
160+
rm -rf /workspace/LoongForge/ci.yml && \
161+
rm -rf /workspace/LoongForge/docker/ci && \
162+
rm -rf /workspace/*.tar.gz && \
163+
rm -f /workspace/README.md /workspace/license.txt && \
164+
rm -rf /workspace/docker-examples /workspace/tutorials /workspace/lerobot
165+
166+
WORKDIR /workspace

docker/fix_xpytorch_hook.sh

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/bin/bash
2+
# Copyright 2026 The LoongForge Authors.
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Fix xpytorch_import_hook.py and torch_xray_import_hook.py
6+
# Auto-detect pip isolated build environment and skip hook
7+
8+
set -e
9+
10+
echo "Patching xpytorch and xray import hooks..."
11+
12+
python3 << 'EOFPY'
13+
import os
14+
import re
15+
import site
16+
import sys
17+
18+
patched_count = 0
19+
20+
# ======================== Fix xpytorch_import_hook.py ========================
21+
hook_files = []
22+
for path in site.getsitepackages():
23+
candidate = os.path.join(path, "xpytorch_import_hook.py")
24+
if os.path.exists(candidate):
25+
hook_files.append(candidate)
26+
27+
if not hook_files:
28+
print("xpytorch_import_hook.py not found, skipping patch")
29+
else:
30+
for hook_file in hook_files:
31+
with open(hook_file, "r") as f:
32+
content = f.read()
33+
34+
if "# patched: auto-detect pip isolated env" in content:
35+
print(f"[xpytorch] Already patched: {hook_file}")
36+
patched_count += 1
37+
continue
38+
39+
# Fix 1: Add pip isolated environment detection at the beginning of _custom_import
40+
old_func_start = r"def _custom_import\(module_name, globals=None, locals=None, fromlist=\(\), level=0\):\n( *)global SYMBOL_REWRITE_REGISTER"
41+
42+
def make_func_patch(m):
43+
indent = m.group(1)
44+
detection_code = (
45+
f"{indent}# patched: auto-detect pip isolated build environment\n"
46+
f"{indent}if any('pip-' in p or 'pip_' in p for p in sys.path):\n"
47+
f"{indent} return builtins.__origin__import__(module_name, globals, locals, fromlist, level)\n"
48+
)
49+
return (
50+
"def _custom_import(module_name, globals=None, locals=None, fromlist=(), level=0):\n"
51+
+ detection_code +
52+
f"{indent}global SYMBOL_REWRITE_REGISTER"
53+
)
54+
55+
content, count1 = re.subn(old_func_start, make_func_patch, content)
56+
57+
# Fix 1 is critical, fail if not matched
58+
if count1 == 0:
59+
print(f"ERROR: Function pattern not found in {hook_file}", file=sys.stderr)
60+
print("This is a critical patch. Build cannot continue.", file=sys.stderr)
61+
sys.exit(1)
62+
63+
# Fix 2: torch_version = version('torch') with fallback
64+
old_pattern2 = r"^( *)torch_version = version\('torch'\)"
65+
66+
def make_patch2(m):
67+
indent = m.group(1)
68+
return (
69+
f"{indent}try:\n"
70+
f"{indent} torch_version = version('torch')\n"
71+
f"{indent}except Exception:\n"
72+
f"{indent} try:\n"
73+
f"{indent} torch_version = __import__('torch').__version__ # patched\n"
74+
f"{indent} except ImportError:\n"
75+
f"{indent} torch_version = \"0.0.0\" # patched fallback"
76+
)
77+
78+
content, count2 = re.subn(old_pattern2, make_patch2, content, flags=re.MULTILINE)
79+
80+
with open(hook_file, "w") as f:
81+
f.write(content)
82+
83+
patched_count += 1
84+
print(f"[xpytorch] Patched: {hook_file} (func={count1}, version={count2})")
85+
86+
# Check if at least one file was patched
87+
if patched_count == 0:
88+
print("ERROR: No xpytorch_import_hook.py files were successfully patched", file=sys.stderr)
89+
sys.exit(1)
90+
91+
# ======================== Fix torch_xray_import_hook.py ========================
92+
xray_files = []
93+
for path in site.getsitepackages():
94+
candidate = os.path.join(path, "torch_xray_import_hook.py")
95+
if os.path.exists(candidate):
96+
xray_files.append(candidate)
97+
98+
xray_patched_count = 0
99+
100+
if not xray_files:
101+
print("torch_xray_import_hook.py not found, skipping patch")
102+
else:
103+
for xray_file in xray_files:
104+
with open(xray_file, "r") as f:
105+
content = f.read()
106+
107+
if "if spec is not None and spec.loader is not None:" in content:
108+
print(f"[xray] Already patched: {xray_file}")
109+
xray_patched_count += 1
110+
continue
111+
112+
old_pattern = r"^( *)spec\.loader = XrayMetaPathLoader\(spec\.loader\)"
113+
114+
def make_xray_patch(m):
115+
indent = m.group(1)
116+
return (
117+
f"{indent}if spec is not None and spec.loader is not None:\n"
118+
f"{indent} spec.loader = XrayMetaPathLoader(spec.loader)"
119+
)
120+
121+
content, count = re.subn(old_pattern, make_xray_patch, content, flags=re.MULTILINE)
122+
123+
if count == 0:
124+
print(f"ERROR: Pattern not found in {xray_file}", file=sys.stderr)
125+
print("This is a critical patch. Build cannot continue.", file=sys.stderr)
126+
sys.exit(1)
127+
128+
with open(xray_file, "w") as f:
129+
f.write(content)
130+
131+
xray_patched_count += 1
132+
print(f"[xray] Patched: {xray_file}")
133+
134+
# Check if at least one xray file was patched
135+
if xray_patched_count == 0:
136+
print("ERROR: No torch_xray_import_hook.py files were successfully patched", file=sys.stderr)
137+
sys.exit(1)
138+
139+
print("All import hook fixes applied successfully!")
140+
EOFPY

0 commit comments

Comments
 (0)