Files
enginex-bi100-compat/README.md

175 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# enginex-bi100-compat
天数智芯 **天垓100(Iluvatar_bi-100)** · 文本生成 · vLLM **兼容增强引擎**。
> **一句话**:不改模型、不改卡,只把「引擎镜像层」的结构性不兼容在容器启动前修掉。
> 这类失败的特征是——**换任何模型、换任何卡都会遇到**,所以修在引擎层比修在提交层更普适。
---
## 1. 动机:失败不是均匀分布的
我在信创模盒累计 **37 个验证失败**。把 74 份失败日志
(`log-extract/<taskId>/pod_runtime_log.txt` / `error_report_raw.json`)聚合后发现,
失败高度集中在几类「模型没坏、卡也没坏、纯粹是引擎/配置层不匹配」的问题上。
按卡分布(我有日志的 42 例):
| 卡 | 失败数 | 主要缺陷 |
|---|---|---|
| **Iluvatar_bi-100** | **10** | R2×4、R3×2、R7×1、R4×1 |
| hygon_k100-ai | 5 | R4×2 |
| Iluvatar_bi-150 | 4 | 其他×3 |
| Iluvatar_mrv-100 | 4 | R1×2(GGUF 需 llamacpp,属提交侧)、R2×2 |
| Cambricon_mlu-370-x8 | 3 | R2×3、R4×1 |
选 **bi-100** 首发,三个理由:
1. 失败最集中(10 例,全平台第一);
2. 该卡社区通过率约 **51%**(15 张卡里最低),修好收益最大;
3. 它的原生 build-config 最简单(`vllm serve /model --port 80 -tp 1`),改动面小、风险低。
---
## 2. 修什么(五类,全部有日志实证)
| 编号 | 现象 | 根因 | 修法 |
|---|---|---|---|
| **R3** | 容器秒崩 `AttributeError: 'list' object has no attribute 'keys'` | `tokenizer_config.json` 里 `extra_special_tokens` 写成 **list**,而 transformers 的 `SpecialTokensMixin` 会执行 `... + list(special_tokens.keys())` | 生成 tokenizer 覆盖目录,把该字段规范成 dict,用 `--tokenizer` 指过去 |
| **R3b** | tokenizer 加载异常 | `tokenizer_class` 是 `TokenizersBackend` / `TiktokenTokenizer` 这类镜像不认的类 | 按 fast/sentencepiece/bpe 归一化成正常类,并清掉 `backend` |
| **R2** | `/v1/chat/completions` 返回 400 或输出为空;启动参数里 `chat_template=None` | 模型自身没带 chat_template | 注入最小可用 Jinja 模板,用 `--chat-template` 指过去 |
| **R4** | `KeyError: 'xxx'` / `MODEL_NOT_SUPPORTED`,配置加载阶段就挂 | `config.json` 的 `architectures` 声明了引擎镜像**没注册**的类 | 用 `--hf-overrides {"architectures":[...]}` 把**已证实**的未注册类名映射到已注册类名 |
| **R7** | `ModuleNotFoundError: No module named 'ixformer.contrib.vllm.layers'` | 引擎镜像缺 MoE 实现依赖 | `shims/` 提供**纯 PyTorch 回退**,经 `PYTHONPATH` 注入 |
### 证据片段(均来自真实失败日志)
**R3**
```
self.SPECIAL_TOKENS_ATTRIBUTES = self.SPECIAL_TOKENS_ATTRIBUTES + list(special_tokens.keys())
AttributeError: 'list' object has no attribute 'keys'
```
**R4**
```
File ".../transformers/models/auto/configuration_auto.py", line N, in __getitem__
raise KeyError(key)
KeyError: 'olmo...'
```
**R7**
```
File ".../vllm/model_executor/models/mixtral.py", line N, in forward
from ixformer.contrib.vllm.layers import mixtral_decoder_layer_forward
ModuleNotFoundError: No module named 'ixformer.contrib.vllm.layers'
```
**R2**
```
startup args: ..., chat_template=None, ...
后续 MCQ 评测全部 HTTP Error 400 / 输出为空
```
---
## 3. 实现:沿用社区已验证的引擎模式
R3 / R3b 两条修复**不是我发明的**——社区开发者 `sunruoxi` 已在
`EngineX-Sunrise/enginex-S2-vllm-fix-tokenizer`(2026-05-28 上线,已注册到曦望 S2)
做过并跑在生产上。本引擎:
- **完全沿用它的结构**:`Dockerfile` + `entrypoint.sh` + 修补脚本 + `detect_tokenizer.py` + README,
`entrypoint.sh` 也是 `detect → fix → exec vllm serve "$MODEL_DIR" $EXTRA "$@"` 这一套;
- **合并它那两条修复**(`extra_special_tokens` list→dict 用同形的 `token→token`,
`tokenizer_class` 归一化用同一份坏类清单),保证两套引擎口径一致、可交叉验证;
- 在其上补了 R2 / R4 / R7 三条,把「只修 tokenizer」升级成「修 tokenizer + 模板 + 架构 + 缺模块」。
`extra_special_tokens` dict 的构造方式与社区上线版一致:
```python
cfg["extra_special_tokens"] = {token: token for token in orig_list}
```
---
## 4. 三条设计红线
1. **绝不写模型目录**——平台挂载的 `/model` 可能只读。所有修补只通过
「临时覆盖目录 + 命令行参数」实现;软链优先,软链不可用时**只拷贝 tokenizer 白名单文件**,
权重文件(`.safetensors/.bin/.gguf/.pt/...`)永远不碰(自测专门盯这条)。
2. **修补失败绝不阻断启动**——`entrypoint.sh` 里 preflight 非 0 退出也只打日志然后按原命令跑;
preflight 内部每步都有 try/except。最坏情况=没修上,不会比不装本引擎更差。
3. **干净模型零改动**——模型自带 chat_template、架构已注册、无 list 字段时,
preflight 不产出任何额外参数(自测用例 5 专门盯这条)。
---
## 5. 自测(不需要 GPU / vLLM / Docker)
```bash
python3 test_engine.py
# 25 passed, 0 failed
```
覆盖:R3 list→dict 且原目录零写入、R3b 坏类名归一化、R2 模板注入与 `--api completion` 时跳过、
R4 别名映射与「已注册类名不动」、干净模型不误补、模型目录不存在/空目录/坏 JSON 都不崩、
R7 shim 可 import 且 MoE forward 2D/3D 形状与数值有限(torch 2.13 cpu 实测)、
`entrypoint.sh` bash 语法、覆盖目录**不含权重文件**。
---
## 6. 使用方式
`Dockerfile` 以 `ENTRYPOINT ["/opt/entrypoint.sh"]` 结尾,平台会把 GPU 数、端口、
`--max-model-len` 等参数作为 `"$@"` 传进来,原样透传给 `vllm serve`:
```bash
exec vllm serve "$MODEL_DIR" $EXTRA "$@"
```
修补结果打进容器日志,事后可归因:
```
[entrypoint] preflight extra args: --tokenizer /tmp/mhxc_compat_xxx/tokenizer --hf-overrides {...}
[entrypoint] preflight: [preflight] patches=3 R3: ... | R4: ... | R7: ...
```
---
## 7. 如何注册为平台可用框架(本轮挖出的完整链路)
平台侧的「上传驱动」按钮在当前 UI 里不存在,但**引擎注册的完整链路藏在社区引擎的 CI 里**。
照 `EngineX-Sunrise/enginex-S2-vllm-fix-tokenizer` 的 `.gitea/workflows/` 复刻即可:
```
.gitea/workflows/docker-build-push.yml 平台官方引擎 CI(原样复制,勿改)
.gitea/workflows/task_info.env 声明三要素:FRAMEWORK / GPU_TYPE / TASK_TYPE
```
推送一个 **`v*` 标签**即触发,CI 会依次:
1. 读 `task_info.env` 取 `FRAMEWORK`/`GPU_TYPE`/`TASK_TYPE`(FRAMEWORK 必填);
2. 调 `GET https://modelhub.org.cn/adminApi/image-verify/validate?gpuType=…&taskType=…` 校验元数据;
3. `docker build` + `docker push` 到平台 registry;
4. 调 `POST https://modelhub.org.cn//adminApi/image-verify` 回填 `{framework, gpuType, imageUrl, taskType, createBy, repoUrl, tag}` —— **这一步就是注册**。
本仓的取值:`FRAMEWORK=vllm_compat`、`GPU_TYPE=Iluvatar_bi-100`、`TASK_TYPE=text-generation`,
并已推送标签 **`v1.0.0`**(commit `d78a92b3`)。
CI 运行记录:`actions/runs/1`,9 个步骤(Clone / Set metadata / Load Task Info / Validate Metadata /
Login / Build / Push / Notify / Complete)**全部 success**,其中 Build 2s、Push 1m1s。
> 注册生效后,提交适配任务时的「框架」下拉里会出现 `vllm_compat`(只对 Iluvatar_bi-100 × 文本生成)。
## 8. 已验证 / 未验证(分清楚,不把做了当成了成了)
**已验证**
- `python3 test_engine.py` → **25 passed, 0 failed**(不需要 GPU / vLLM / Docker)
- CI 9 步全部 success;`image-verify/validate` 返回 `code:0, data:true`(平台认可元数据)
**尚未验证**
- **框架还没出现在平台侧**:`Iluvatar_bi-100 × text-generation` 目前仍只有
`transformers / vllm / vllm-patch-tokenizer(个人开发者 zhengzhongwei) / vllm_fix_tokenizer` 四个,
`vllm_compat` 未出现,「我的驱动列表」也仍显示「暂无驱动」。
→ 注册是异步或需平台审核,**尚未生效**,需要继续观察(不能声称已注册成功)。
- **没在真卡上跑过**:本机没有天垓100,也拉不到该基础镜像,Dockerfile 未经真机验证。
风险已压到最小:基线镜像与官方基线一致、结构照搬生产引擎、修补全失败安全。
## 9. 已知边界
* R4 别名表只收已证实的 4 条(Qwen3_5 系列 + Gemma3 早期类名),不靠猜扩表。
* R7 shim 是兜底不是优化:保证「能出结果」,不追求吞吐。
* 提交任务的「框架」下拉要等平台注册生效后才看得到 `vllm_compat`。