1 Commits
v0.1.0 ... main

Author SHA1 Message Date
Victor Cao
4c271270d4 fix: FRAMEWORK vllm_bi150_r2r4 + image-verify CI (I8–I12)
All checks were successful
Docker Build / docker (push) Successful in 20m36s
2026-10-13 15:15:16 +08:00
12 changed files with 249 additions and 45 deletions

View File

@@ -24,7 +24,7 @@ jobs:
echo "GPU_TYPE=${GPU_TYPE:-}" echo "GPU_TYPE=${GPU_TYPE:-}"
echo "TASK_TYPE=${TASK_TYPE:-}" echo "TASK_TYPE=${TASK_TYPE:-}"
if [ -z "${FRAMEWORK:-}" ] || [ "${FRAMEWORK}" = "vllm_fix_tokenizer" ]; then if [ -z "${FRAMEWORK:-}" ] || [ "${FRAMEWORK}" = "vllm_fix_tokenizer" ]; then
echo "FRAMEWORK 无效。本仓使用 vllm_0_17_0_corex_4_4_0。" echo "FRAMEWORK 无效。本仓使用 vllm_bi150_r2r4。"
exit 1 exit 1
fi fi
if [ "${GPU_TYPE:-}" != "Iluvatar_bi-150" ]; then if [ "${GPU_TYPE:-}" != "Iluvatar_bi-150" ]; then
@@ -32,8 +32,52 @@ jobs:
exit 1 exit 1
fi fi
IMAGE_NAME="$(echo "${{ gitea.repository }}" | tr '[:upper:]' '[:lower:]' | tr '_' '-')" IMAGE_NAME="$(echo "${{ gitea.repository }}" | tr '[:upper:]' '[:lower:]' | tr '_' '-')"
IMAGE_TAG="${{ gitea.ref_name }}"
echo "FRAMEWORK=${FRAMEWORK}" >> "$GITEA_ENV"
echo "GPU_TYPE=${GPU_TYPE}" >> "$GITEA_ENV"
echo "TASK_TYPE=${TASK_TYPE:-}" >> "$GITEA_ENV"
echo "IMAGE_NAME=${IMAGE_NAME}" >> "$GITEA_ENV" echo "IMAGE_NAME=${IMAGE_NAME}" >> "$GITEA_ENV"
echo "IMAGE_TAG=${{ gitea.ref_name }}" >> "$GITEA_ENV" echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITEA_ENV"
if [ -n "${DOCKER_REGISTRY:-}" ] && [ -n "${DOCKER_USERNAME:-}" ]; then
echo "IMAGE=${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${IMAGE_TAG}" >> "$GITEA_ENV"
fi
# FIXED_TOKEN 由 Runner 注入,不写进仓库,也不打印。
- name: Validate Image Verify Metadata
run: |
if [ -z "${FIXED_TOKEN:-}" ]; then
echo "FIXED_TOKEN is not configured on runner"
exit 1
fi
if ! response="$(curl --silent --show-error --location --get \
'https://modelhub.org.cn/adminApi/image-verify/validate' \
--header "Xc-Token: ${FIXED_TOKEN}" \
--data-urlencode "gpuType=${GPU_TYPE:-}" \
--data-urlencode "taskType=${TASK_TYPE:-}")"; then
echo "调用 image-verify validate 失败"
exit 1
fi
VALIDATE_RESPONSE="$response" python3 - <<'PY'
import json
import os
import sys
raw = os.environ.get("VALIDATE_RESPONSE", "")
try:
body = json.loads(raw)
except json.JSONDecodeError:
print("image-verify validate 返回的不是合法 JSON")
print(raw)
sys.exit(1)
if body.get("code") == 0 and body.get("data") is True:
print("image-verify 元数据校验通过")
sys.exit(0)
print(f"image-verify 元数据校验失败: {body.get('message') or '未知错误'}")
print(raw)
sys.exit(1)
PY
- name: Build image - name: Build image
run: | run: |
@@ -45,12 +89,40 @@ jobs:
run: | run: |
if [ -z "${DOCKER_REGISTRY:-}" ] || [ -z "${DOCKER_USERNAME:-}" ] || [ -z "${DOCKER_PASSWORD:-}" ]; then if [ -z "${DOCKER_REGISTRY:-}" ] || [ -z "${DOCKER_USERNAME:-}" ] || [ -z "${DOCKER_PASSWORD:-}" ]; then
echo "未配置 DOCKER_REGISTRY、DOCKER_USERNAME 或 DOCKER_PASSWORD。" echo "未配置 DOCKER_REGISTRY、DOCKER_USERNAME 或 DOCKER_PASSWORD。"
echo "本次为 build-only,不登录仓库,不推送镜像。" echo "本次为 build-only,不登录仓库,不推送镜像,随后也不注册 image-verify。"
echo "PUSHED=0" >> "$GITEA_ENV"
exit 0 exit 0
fi fi
remote="${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${IMAGE_TAG}" echo "推送目标: ${IMAGE}"
echo "推送目标: ${remote}"
echo "$DOCKER_PASSWORD" | docker login "$DOCKER_REGISTRY" -u "$DOCKER_USERNAME" --password-stdin echo "$DOCKER_PASSWORD" | docker login "$DOCKER_REGISTRY" -u "$DOCKER_USERNAME" --password-stdin
docker tag "${IMAGE_NAME}:${IMAGE_TAG}" "$remote" docker tag "${IMAGE_NAME}:${IMAGE_TAG}" "$IMAGE"
docker push "$remote" docker push "$IMAGE"
echo "推送完成: ${remote}" echo "PUSHED=1" >> "$GITEA_ENV"
echo "推送完成: ${IMAGE}"
# 只有真的推成功才注册,build-only 没有可用的 imageUrl。
- name: Notify Image Verify
run: |
if [ "${PUSHED:-0}" != "1" ]; then
echo "镜像未推送,跳过 image-verify 注册。"
exit 0
fi
if [ -z "${FIXED_TOKEN:-}" ]; then
echo "FIXED_TOKEN is not configured on runner"
exit 1
fi
echo "注册 image-verify: ${IMAGE}"
curl --silent --show-error --fail-with-body --location --request POST \
'https://modelhub.org.cn//adminApi/image-verify' \
--header "Xc-Token: ${FIXED_TOKEN}" \
--header 'Content-Type: application/json' \
--data-raw "{
\"framework\": \"${FRAMEWORK}\",
\"gpuType\": \"${GPU_TYPE}\",
\"imageUrl\": \"${IMAGE}\",
\"taskType\": \"${TASK_TYPE}\",
\"createBy\": \"${{ gitea.actor }}\",
\"repoUrl\": \"${{ gitea.server_url }}/${{ gitea.repository }}\",
\"tag\": \"${{ gitea.ref_name }}\"
}"
echo "image-verify 注册请求已完成。适配下拉是否出现本引擎由人核对。"

View File

@@ -1,3 +1,3 @@
GPU_TYPE=Iluvatar_bi-150 GPU_TYPE=Iluvatar_bi-150
FRAMEWORK=vllm_0_17_0_corex_4_4_0 FRAMEWORK=vllm_bi150_r2r4
TASK_TYPE=text-generation TASK_TYPE=text-generation

View File

@@ -3,7 +3,7 @@
ARG BASE_IMAGE=registry.iluvatar.com.cn:10443/customer/sz/vllm0.17.0-4.4.0-x86:v4.1 ARG BASE_IMAGE=registry.iluvatar.com.cn:10443/customer/sz/vllm0.17.0-4.4.0-x86:v4.1
FROM ${BASE_IMAGE} FROM ${BASE_IMAGE}
ENV FRAMEWORK=vllm_0_17_0_corex_4_4_0 \ ENV FRAMEWORK=vllm_bi150_r2r4 \
GPU_TYPE=Iluvatar_bi-150 \ GPU_TYPE=Iluvatar_bi-150 \
TASK_TYPE=text-generation \ TASK_TYPE=text-generation \
MODEL_DIR=/model \ MODEL_DIR=/model \

View File

@@ -10,7 +10,7 @@
- Track B(竞赛)为 **NO-GO**。没有 `contest/`,没有竞赛构建。 - Track B(竞赛)为 **NO-GO**。没有 `contest/`,没有竞赛构建。
- 没有 ZIP 打包,没有上传驱动的 Makefile 目标。 - 没有 ZIP 打包,没有上传驱动的 Makefile 目标。
- `FRAMEWORK` 使用 `vllm_0_17_0_corex_4_4_0`,不使用 `vllm_fix_tokenizer`。 - `FRAMEWORK` 使用 `vllm_bi150_r2r4`,不使用 `vllm_fix_tokenizer`。
- tokenizer 修补(I5)未开启。现有 bi150 引擎已覆盖 tokenizer;本仓补的是 chat_template(R2)和 architecture 白名单(R4)。 - tokenizer 修补(I5)未开启。现有 bi150 引擎已覆盖 tokenizer;本仓补的是 chat_template(R2)和 architecture 白名单(R4)。
## 基线镜像 ## 基线镜像
@@ -40,21 +40,37 @@ make dry-run
`make dry-run` 只打印启动计划与模板预览,不启动 vLLM,不访问平台。 `make dry-run` 只打印启动计划与模板预览,不启动 vLLM,不访问平台。
## 改 FRAMEWORK ## FRAMEWORK
默认值写在 [task_info.env.example](task_info.env.example)。镜像内和 CI 使用同一组值: 默认值写在 [task_info.env.example](task_info.env.example)。镜像内和 CI 使用同一组值:
- `GPU_TYPE=Iluvatar_bi-150` - `GPU_TYPE=Iluvatar_bi-150`
- `FRAMEWORK=vllm_0_17_0_corex_4_4_0` - `FRAMEWORK=vllm_bi150_r2r4`
- `TASK_TYPE=text-generation` - `TASK_TYPE=text-generation`
### 为什么不用 `vllm_0_17_0_corex_4_4_0`
适配页按 FRAMEWORK 聚合。该名字已被 **i-liangzhuobang** 占用展示,本仓 v0.1.0 用它注册时,引擎下拉里看到的是对方的开发者身份。`vllm_bi150_r2r4` 是本仓独有的名字,用来让适配页正确归属。
若平台给出新的 FRAMEWORK 名,同时改这三处,并让 `make test` 保持通过: 若平台给出新的 FRAMEWORK 名,同时改这三处,并让 `make test` 保持通过:
- `task_info.env.example` - `task_info.env.example`
- `.gitea/workflows/task_info.env` - `.gitea/workflows/task_info.env`
- `Dockerfile` 的 `ENV` - `Dockerfile` 的 `ENV`
不要改回 `vllm_fix_tokenizer`。 不要改回 `vllm_fix_tokenizer`,也不要改回撞名的旧值。
## CI 与 image-verify
`v*` 标签触发 [.gitea/workflows/build.yml](.gitea/workflows/build.yml):
```text
Clone → Load task info → Validate Image Verify Metadata → Build image → Push → Notify Image Verify
```
Validate 在构建前校验 `gpuType` 与 `taskType`;Notify 只在镜像真的推送成功后注册 `imageUrl`。两步都要 Runner 注入 `FIXED_TOKEN`,仓库里不存任何 Token。
CI 只调用 `adminApi/image-verify` 和它的 `validate`。不代领、也不代提交适配任务。
## 建仓与选用 ## 建仓与选用

View File

@@ -2,13 +2,33 @@
时区:**Asia/Shanghai**。本模板由人工填写。仓库不自动采集审核状态。 时区:**Asia/Shanghai**。本模板由人工填写。仓库不自动采集审核状态。
- 镜像 tag:待填写 ## 镜像
- CI 地址:待填写
- 镜像 tag:待填写(例:v0.1.1)
- 完整 imageUrl:待填写(例:harbor.4pd.io/modelhubxc/vc-nosugar/modelhub-engine-bi150-compat:v0.1.1)
- 镜像 digest:待填写
- CI 地址:待填写(例:https://dev.modelhub.org.cn/VC_noSugar/modelhub-engine-bi150-compat/actions/runs/N)
## image-verify
- FRAMEWORK 展示名:vllm_bi150_r2r4
- Validate 通过时间(Asia/Shanghai):待填写
- Notify HTTP 结果:待填写
- createBy / 适配 UI 开发者:应为 VC_noSugar(或页面等价展示)
- 适配下拉是否可见 vllm_bi150_r2r4:待填写
- 抽检模型(只看下拉可见,不提交任务):Qwen/Qwen2.5-0.5B-Instruct
## 平台
- 平台状态:未推送
- 审核:未开始 - 审核:未开始
- 审核通过时间(Asia/Shanghai):待填写 - 审核通过时间(Asia/Shanghai):待填写
- 首次被适配任务选用的任务 ID:待填写 - 首次被适配任务选用的任务 ID:待填写
- 选用时间(Asia/Shanghai):待填写 - 选用时间(Asia/Shanghai):待填写
- 平台状态:未推送
- 积分:待平台核实 - 积分:待平台核实
## 说明
v0.1.0 用的是撞名的 `vllm_0_17_0_corex_4_4_0`,适配页把它聚合到了别人名下,且流水线没有 image-verify 注册。可见性要看含 Validate + Notify 的新 Tag。
无卡本地测试只说明 R2/R4 修补逻辑能跑,不说明天垓 150 上的吞吐。 无卡本地测试只说明 R2/R4 修补逻辑能跑,不说明天垓 150 上的吞吐。

View File

@@ -2,13 +2,24 @@
Track B 不在本仓实现,状态保持 NO-GO。 Track B 不在本仓实现,状态保持 NO-GO。
## 首版(v0.1.0)
- [x] I0 仓骨架 + README + 非目标声明 - [x] I0 仓骨架 + README + 非目标声明
- [x] I1 Dockerfile + task_info.env.example - [x] I1 Dockerfile + task_info.env.example
- [x] I2 wrapper + dry-run - [x] I2 wrapper + dry-run
- [x] I3 R2 chat_template 修补 - [x] I3 R2 chat_template 修补
- [x] I4 R4 architecture 白名单 - [x] I4 R4 architecture 白名单
- [ ] I5 tokenizer 修补(未开启;等 R2/R4 确认盖不住目标模型族再做) - [ ] I5 tokenizer 修补(未开启;等 R2/R4 确认盖不住目标模型族再做)
- [x] I6 CI build(Tag 触发;无凭据时 build-only;不提交平台任务) - [x] I6 CI build(Tag 触发;无凭据时 build-only)
- [x] I7 人工 runbook + evidence 模板 - [x] I7 人工 runbook + evidence 模板
本地 `make test` 已覆盖 I3/I4 与 dry-run。镜像是否能在 Runner 上拉到基线 tag,要等第一次 `v*` 流水线。 ## image-verify 注册(待发 v0.1.1)
- [x] I8 FRAMEWORK 改为 `vllm_bi150_r2r4`,全仓同步,避开与 i-liangzhuobang 撞名
- [x] I9 CI 在 build 前 Validate Image Verify Metadata,缺 `FIXED_TOKEN` 失败且不打印 Token
- [x] I10 CI 在推送成功后 Notify Image Verify,body 含 framework/gpuType/imageUrl/taskType/createBy/repoUrl/tag
- [x] I11 单测契约翻转:要求 CI 含 image-verify,仍禁 ZIP/contest/代提交,仍拒 `vllm_fix_tokenizer`
- [x] I12 runbook + evidence 写明注册步骤与下拉归属核对
- [ ] I13 发版:推 main + tag v0.1.1;Actions 出现 Validate + Notify;人工抽检下拉
本地 `make test` 20 项覆盖 I3/I4、dry-run 与 CI 契约。镜像能否拉到基线 tag、适配下拉是否归属 `VC_noSugar`,都要等 v0.1.1 流水线和人工核对。

View File

@@ -1,6 +1,6 @@
# 建仓、打 Tag、选用引擎(人工) # 建仓、打 Tag、选用引擎(人工)
本仓没有自动提交命令。CI 只构建镜像;仓库凭据齐全时才推送。CI 不调用 modelhub.org.cn,不代领适配任务。 本仓没有自动提交命令。CI 构建镜像、在凭据齐全时推送,并把镜像注册到 `image-verify`。注册只是让适配页能看到这个引擎,不等于代领或代提交适配任务。
积分:待平台核实。 积分:待平台核实。
@@ -23,17 +23,24 @@ git push origin v0.1.0
## 3. 看 CI ## 3. 看 CI
流水线顺序:`Clone → Load task info → Validate Image Verify Metadata → Build image → Push → Notify Image Verify`。
1. 打开该 Tag 的 Actions / 流水线。 1. 打开该 Tag 的 Actions / 流水线。
2. `Load task info` 应打印 `FRAMEWORK=vllm_0_17_0_corex_4_4_0` 和 `GPU_TYPE=Iluvatar_bi-150`。 2. `Load task info` 应打印 `FRAMEWORK=vllm_bi150_r2r4` 和 `GPU_TYPE=Iluvatar_bi-150`。
3. `Build image` 执行 `docker build`。基线镜像拉取失败时,日志会停在构建步骤;无卡不能代替真机验证。 3. `Validate Image Verify Metadata` 在构建前 GET `adminApi/image-verify/validate`,校验 `gpuType` 与 `taskType`。缺 `FIXED_TOKEN` 这步直接失败。
4. 若 Runner 没有 `DOCKER_REGISTRY`、`DOCKER_USERNAME`、`DOCKER_PASSWORD`,流水线打印 build-only 并成功结束,不推送。 4. `Build image` 执行 `docker build`。基线镜像拉取失败时,日志会停在构建步骤;无卡不能代替真机验证。
5. 三份凭据都由 Runner 环境提供,不写进仓库。有凭据时才会 `docker login` 并推送。 5. `Push` 需要 `DOCKER_REGISTRY`、`DOCKER_USERNAME`、`DOCKER_PASSWORD`。缺任意一项就打印 build-only 并成功结束,不推送。
6. `Notify Image Verify` 只在推送成功后 POST `adminApi/image-verify`,把 `framework`、`gpuType`、`imageUrl`、`taskType`、`createBy`、`repoUrl`、`tag` 注册上去。build-only 时跳过,因为没有真实 `imageUrl`。
7. `FIXED_TOKEN` 与 Harbor 密码都由 Runner 注入,不写进仓库,也不在日志里打印。打新 Tag 前先确认旧 Harbor 密码已轮换。
旧 Tag 不会自动补注册。`v0.1.0` 没有 Notify 步骤,必须打新 Tag 重跑。
## 4. 在模盒选用引擎 ## 4. 在模盒选用引擎
1. 用浏览器登录模盒。 1. 用浏览器登录模盒。
2. 等镜像审核通过。审核状态由人在页面上确认。 2. 在适配任务的引擎下拉里确认能看到 `vllm_bi150_r2r4`,且开发者 / `createBy` 是 `VC_noSugar`(或页面等价展示)。如果看到的是别人的名字,说明 FRAMEWORK 又撞名了。
3. 在适配任务里选用该引擎。本仓不代为创建或提交任务。 3. 等镜像审核通过。审核状态由人在页面上确认。
4. 把审核通过时间、镜像 tag、以及首次被选用的适配任务 ID 写入 `docs/evidence.md`。积分栏保持「待平台核实」。 4. 在适配任务里选用该引擎。本仓不代为创建或提交任务。可用 `Qwen/Qwen2.5-0.5B-Instruct`(`Qwen2ForCausalLM`,在 R4 白名单内)只做下拉可见性抽检。
5. 把审核通过时间、镜像 tag、`imageUrl`、以及首次被选用的适配任务 ID 写入 `docs/evidence.md`。积分栏保持「待平台核实」。
字段以登录后的控制台为准。 字段以登录后的控制台为准。

View File

@@ -1,7 +1,7 @@
【引擎 bi150-compat】2026-10-13 【引擎 bi150-compat · image-verify】2026-10-13
- 本周:I0–I4、I6、I7 完成;I5 未开启。阻塞:尚未推送 Gitea,也还没有真机流水线。 - 本周:I8–I12 补注册 CI;FRAMEWORK 改为 vllm_bi150_r2r4(避开与 i-liangzhuobang 撞名)
- 镜像 tag:未构建。默认基线 registry.iluvatar.com.cn:10443/customer/sz/vllm0.17.0-4.4.0-x86:v4.1(公开仓反推) - 镜像:v0.1.0 已推;待发 v0.1.1(含 validate + notify)
- 本地 test:见 make test - 本地 test:make test 20 项通过
- 平台:未推送 - 平台:Harbor 有镜像但适配下拉尚无本仓身份;根因缺 image-verify 注册
- 积分:待核实 - 积分:待核实
- 下周:人工建仓、打 v* Tag、看 CI;审核通过或被选用后再填 docs/evidence.md - 下周:打 v0.1.1 → 确认下拉可见 vllm_bi150_r2r4 且归属 VC_noSugar → 证据写入 docs/evidence.md(不代提交适配)

View File

@@ -12,6 +12,7 @@ import sys
from pathlib import Path from pathlib import Path
FORBIDDEN_FRAMEWORK = "vllm_fix_tokenizer" FORBIDDEN_FRAMEWORK = "vllm_fix_tokenizer"
DEFAULT_FRAMEWORK = "vllm_bi150_r2r4"
TOKENIZER_PATCH_KEY = "modelhub_tokenizer_patch" TOKENIZER_PATCH_KEY = "modelhub_tokenizer_patch"
@@ -26,7 +27,7 @@ def engine_root() -> Path:
def assert_framework(framework: str) -> None: def assert_framework(framework: str) -> None:
if not framework or framework == FORBIDDEN_FRAMEWORK: if not framework or framework == FORBIDDEN_FRAMEWORK:
raise PatchError( raise PatchError(
"FRAMEWORK 必须是 vllm_0_17_0_corex_4_4_0," f"FRAMEWORK 必须是 {DEFAULT_FRAMEWORK},"
"不能使用 vllm_fix_tokenizer" "不能使用 vllm_fix_tokenizer"
) )
@@ -157,7 +158,7 @@ def main(argv: list[str] | None = None) -> int:
root = engine_root() root = engine_root()
parser = argparse.ArgumentParser(description="应用 R2/R4 模型目录修补") parser = argparse.ArgumentParser(description="应用 R2/R4 模型目录修补")
parser.add_argument("--model-dir", required=True, type=Path) parser.add_argument("--model-dir", required=True, type=Path)
parser.add_argument("--framework", default="vllm_0_17_0_corex_4_4_0") parser.add_argument("--framework", default=DEFAULT_FRAMEWORK)
parser.add_argument("--unknown-policy", default="fail", choices=("fail", "skip")) parser.add_argument("--unknown-policy", default="fail", choices=("fail", "skip"))
parser.add_argument("--force-chat-template", action="store_true") parser.add_argument("--force-chat-template", action="store_true")
parser.add_argument("--tokenizer-patch", action="store_true") parser.add_argument("--tokenizer-patch", action="store_true")

View File

@@ -61,7 +61,7 @@ APPLY_TOKENIZER_PATCH="${APPLY_TOKENIZER_PATCH:-0}"
FORCE_CHAT_TEMPLATE="${FORCE_CHAT_TEMPLATE:-0}" FORCE_CHAT_TEMPLATE="${FORCE_CHAT_TEMPLATE:-0}"
if [[ -z "$FRAMEWORK" || "$FRAMEWORK" == "vllm_fix_tokenizer" ]]; then if [[ -z "$FRAMEWORK" || "$FRAMEWORK" == "vllm_fix_tokenizer" ]]; then
echo "拒绝 FRAMEWORK=${FRAMEWORK:-空}。本仓使用 vllm_0_17_0_corex_4_4_0。" >&2 echo "拒绝 FRAMEWORK=${FRAMEWORK:-空}。本仓使用 vllm_bi150_r2r4。" >&2
exit 1 exit 1
fi fi
if [[ -z "$GPU_TYPE" ]]; then if [[ -z "$GPU_TYPE" ]]; then

View File

@@ -1,6 +1,7 @@
# 本地与镜像内的默认任务信息。平台若给出不同 FRAMEWORK,只改这一处,并保持 Dockerfile 的 ENV 与 CI 文件一致。 # 本地与镜像内的默认任务信息。平台若给出不同 FRAMEWORK,只改这一处,并保持 Dockerfile 的 ENV 与 CI 文件一致。
# FRAMEWORK 必须是本仓独有的名字,否则适配页会把镜像聚合到同名的别人名下。
GPU_TYPE=Iluvatar_bi-150 GPU_TYPE=Iluvatar_bi-150
FRAMEWORK=vllm_0_17_0_corex_4_4_0 FRAMEWORK=vllm_bi150_r2r4
TASK_TYPE=text-generation TASK_TYPE=text-generation
MODEL_DIR=/model MODEL_DIR=/model
SERVED_MODEL_NAME=llm SERVED_MODEL_NAME=llm

View File

@@ -1,9 +1,12 @@
import os import os
import re
import subprocess import subprocess
import unittest import unittest
from pathlib import Path from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
FRAMEWORK = "vllm_bi150_r2r4"
RETIRED_FRAMEWORK = "vllm_0_17_0_corex_4_4_0"
class EngineContractTest(unittest.TestCase): class EngineContractTest(unittest.TestCase):
@@ -11,11 +14,11 @@ class EngineContractTest(unittest.TestCase):
example = (ROOT / "task_info.env.example").read_text(encoding="utf-8") example = (ROOT / "task_info.env.example").read_text(encoding="utf-8")
workflow_env = (ROOT / ".gitea/workflows/task_info.env").read_text(encoding="utf-8") workflow_env = (ROOT / ".gitea/workflows/task_info.env").read_text(encoding="utf-8")
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8") dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
self.assertIn("FRAMEWORK=vllm_0_17_0_corex_4_4_0", example) self.assertIn(f"FRAMEWORK={FRAMEWORK}", example)
self.assertIn("GPU_TYPE=Iluvatar_bi-150", example) self.assertIn("GPU_TYPE=Iluvatar_bi-150", example)
self.assertIn("FRAMEWORK=vllm_0_17_0_corex_4_4_0", workflow_env) self.assertIn(f"FRAMEWORK={FRAMEWORK}", workflow_env)
self.assertIn("GPU_TYPE=Iluvatar_bi-150", workflow_env) self.assertIn("GPU_TYPE=Iluvatar_bi-150", workflow_env)
self.assertIn("FRAMEWORK=vllm_0_17_0_corex_4_4_0", dockerfile) self.assertIn(f"FRAMEWORK={FRAMEWORK}", dockerfile)
self.assertIn("GPU_TYPE=Iluvatar_bi-150", dockerfile) self.assertIn("GPU_TYPE=Iluvatar_bi-150", dockerfile)
self.assertIn( self.assertIn(
"registry.iluvatar.com.cn:10443/customer/sz/vllm0.17.0-4.4.0-x86:v4.1", "registry.iluvatar.com.cn:10443/customer/sz/vllm0.17.0-4.4.0-x86:v4.1",
@@ -24,13 +27,83 @@ class EngineContractTest(unittest.TestCase):
self.assertNotIn("FRAMEWORK=vllm_fix_tokenizer", example) self.assertNotIn("FRAMEWORK=vllm_fix_tokenizer", example)
self.assertNotIn("FRAMEWORK=vllm_fix_tokenizer", workflow_env) self.assertNotIn("FRAMEWORK=vllm_fix_tokenizer", workflow_env)
self.assertNotIn("FRAMEWORK=vllm_fix_tokenizer", dockerfile) self.assertNotIn("FRAMEWORK=vllm_fix_tokenizer", dockerfile)
self.assertNotIn(RETIRED_FRAMEWORK, example)
self.assertNotIn(RETIRED_FRAMEWORK, workflow_env)
self.assertNotIn(RETIRED_FRAMEWORK, dockerfile)
def test_ci_does_not_submit_platform_task(self) -> None: def test_ci_registers_image_verify(self) -> None:
workflow = (ROOT / ".gitea/workflows/build.yml").read_text(encoding="utf-8") workflow = (ROOT / ".gitea/workflows/build.yml").read_text(encoding="utf-8")
self.assertNotIn("adminApi", workflow) self.assertIn("Validate Image Verify Metadata", workflow)
self.assertNotIn("modelhub.org.cn", workflow) self.assertIn("https://modelhub.org.cn/adminApi/image-verify/validate", workflow)
self.assertIn("Notify Image Verify", workflow)
self.assertIn("https://modelhub.org.cn//adminApi/image-verify", workflow)
self.assertIn("Xc-Token: ${FIXED_TOKEN}", workflow)
self.assertIn("FIXED_TOKEN is not configured on runner", workflow)
for field in ("framework", "gpuType", "imageUrl", "taskType", "createBy", "repoUrl", "tag"):
self.assertIn(f'\\"{field}\\"', workflow)
self.assertIn("build-only", workflow) self.assertIn("build-only", workflow)
self.assertIn('tags:', workflow) self.assertIn("tags:", workflow)
self.assertIn(f"本仓使用 {FRAMEWORK}", workflow)
self.assertNotIn(RETIRED_FRAMEWORK, workflow)
def test_ci_notifies_only_after_push(self) -> None:
workflow = (ROOT / ".gitea/workflows/build.yml").read_text(encoding="utf-8")
self.assertIn('PUSHED=1', workflow)
self.assertIn('if [ "${PUSHED:-0}" != "1" ]; then', workflow)
notify_at = workflow.index("Notify Image Verify")
push_at = workflow.index("Push when registry credentials exist")
validate_at = workflow.index("Validate Image Verify Metadata")
build_at = workflow.index("Build image")
self.assertLess(validate_at, build_at)
self.assertLess(build_at, push_at)
self.assertLess(push_at, notify_at)
def test_ci_does_not_submit_adaptation_task(self) -> None:
workflow = (ROOT / ".gitea/workflows/build.yml").read_text(encoding="utf-8")
allowed = {
"https://modelhub.org.cn/adminApi/image-verify/validate",
"https://modelhub.org.cn//adminApi/image-verify",
}
for url in re.findall(r"https?://[^\s'\"]+", workflow):
if "modelhub.org.cn" not in url:
continue
self.assertIn(url, allowed, f"CI 只允许调用 image-verify,发现 {url}")
def test_no_secret_literals_in_repo(self) -> None:
tracked = subprocess.run(
["git", "ls-files"],
cwd=ROOT,
text=True,
capture_output=True,
check=True,
).stdout.split()
pattern = re.compile(r"(FIXED_TOKEN|DOCKER_PASSWORD)\s*=\s*\S")
for name in tracked:
path = ROOT / name
if not path.is_file():
continue
try:
text = path.read_text(encoding="utf-8")
except UnicodeDecodeError:
continue
for line in text.splitlines():
self.assertIsNone(pattern.search(line), f"{name} 可能写入了明文密钥: {line}")
def test_ci_does_not_log_secrets(self) -> None:
workflow = (ROOT / ".gitea/workflows/build.yml").read_text(encoding="utf-8")
for line in workflow.splitlines():
stripped = line.strip()
if "FIXED_TOKEN" in stripped:
self.assertFalse(
stripped.startswith("echo") and "${FIXED_TOKEN" in stripped,
f"不要把 FIXED_TOKEN 打进日志: {stripped}",
)
if stripped.startswith("echo") and "$DOCKER_PASSWORD" in stripped:
self.assertIn(
"docker login",
stripped,
f"DOCKER_PASSWORD 只能管道给 docker login: {stripped}",
)
def test_no_zip_or_contest_targets(self) -> None: def test_no_zip_or_contest_targets(self) -> None:
makefile = (ROOT / "Makefile").read_text(encoding="utf-8") makefile = (ROOT / "Makefile").read_text(encoding="utf-8")
@@ -53,11 +126,12 @@ class EngineContractTest(unittest.TestCase):
check=False, check=False,
) )
self.assertEqual(completed.returncode, 0, completed.stderr) self.assertEqual(completed.returncode, 0, completed.stderr)
self.assertIn("FRAMEWORK=vllm_0_17_0_corex_4_4_0", completed.stdout) self.assertIn(f"FRAMEWORK={FRAMEWORK}", completed.stdout)
self.assertIn("GPU_TYPE=Iluvatar_bi-150", completed.stdout) self.assertIn("GPU_TYPE=Iluvatar_bi-150", completed.stdout)
self.assertIn("python3 -m vllm.entrypoints.openai.api_server", completed.stdout) self.assertIn("python3 -m vllm.entrypoints.openai.api_server", completed.stdout)
self.assertIn("dry-run: 不启动进程,不访问平台", completed.stdout) self.assertIn("dry-run: 不启动进程,不访问平台", completed.stdout)
self.assertNotIn("modelhub.org.cn", completed.stdout) self.assertNotIn("modelhub.org.cn", completed.stdout)
self.assertNotIn(RETIRED_FRAMEWORK, completed.stdout)
self.assertIn("<|user|>", completed.stdout) self.assertIn("<|user|>", completed.stdout)
def test_dry_run_rejects_forbidden_framework(self) -> None: def test_dry_run_rejects_forbidden_framework(self) -> None:
@@ -73,6 +147,7 @@ class EngineContractTest(unittest.TestCase):
) )
self.assertNotEqual(completed.returncode, 0) self.assertNotEqual(completed.returncode, 0)
self.assertIn("vllm_fix_tokenizer", completed.stderr) self.assertIn("vllm_fix_tokenizer", completed.stderr)
self.assertIn(FRAMEWORK, completed.stderr)
def test_tokenizer_patch_stays_off(self) -> None: def test_tokenizer_patch_stays_off(self) -> None:
import shutil import shutil
@@ -80,6 +155,7 @@ class EngineContractTest(unittest.TestCase):
import apply_patches import apply_patches
self.assertEqual(apply_patches.DEFAULT_FRAMEWORK, FRAMEWORK)
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
model = Path(tmp) / "model" model = Path(tmp) / "model"
shutil.copytree(ROOT / "tests" / "fixtures" / "fake-model", model) shutil.copytree(ROOT / "tests" / "fixtures" / "fake-model", model)