This commit is contained in:
2026-06-23 13:58:15 +08:00
parent 0e0e85966e
commit 896c96257b
2 changed files with 196 additions and 1 deletions
+145 -1
View File
@@ -381,4 +381,148 @@ ENV ... \
--- ---
*最后更新: 2026-06-17* ## 7. 运行时类
### 7.1 vllm `_version.py` 缺失导致 `InvalidVersion: 'dev'`
**现象**:镜像构建成功,WebUI 启动正常,但解析任务全部失败:
```
File ".../mineru/backend/vlm/utils.py", line 88, in set_default_gpu_memory_utilization
if version.parse(vllm_version) >= version.parse("0.11.0") and gpu_memory <= 8:
│ │ └ 'dev'
packaging.version.InvalidVersion: Invalid version: 'dev'
```
日志开头同时有信号:
```
/opt/vllm/vllm/__init__.py:7: RuntimeWarning: Failed to read commit hash:
No module named 'vllm._version'
vllm runtime import OK: dev, platform=UnspecifiedPlatform
```
**原因**:阶段 8b 跳过了 `pip install -e .`(见 6.1 节),而 `setuptools_scm` 正是在 `pip install -e .` 时生成 `vllm/_version.py`。该文件缺失后,vllm 的 `version.py` 回退成 `__version__ = 'dev'`。`'dev'` 不是合法 PEP 440 版本号,mineru 调 `packaging.version.parse('dev')` 即抛 `InvalidVersion`,导致每个解析任务必失败。
**解决**:阶段 8b 手写 `vllm/_version.py`,替代 `setuptools_scm` 生成:
```dockerfile
# 阶段 8b(egg-info 创建之后):
printf '__version__ = "0.11.0"\n__version_tuple__ = (0, 11, 0)\n' > /opt/vllm/vllm/_version.py
```
同时在 `patch_vllm_platform.py` 的 `ensure_vllm_dist_info()` 中对 `vllm.__version__` 做 PEP 440 合法性校验,非法时回退安全值 `0.11.0` 并回写 `vllm.__version__`(兜底,防止上游 version.py 再改回退逻辑)。
**版本号选 `0.11.0` 的理由**:
- 合法 PEP 440,`packaging.version.parse` 不报错
- 满足 `mineru[vllm]` 的约束 `>=0.10.1.1,<0.22.0`
- mineru 代码 `version.parse(vllm_version) >= version.parse("0.11.0")` 走新分支(更合理的 GPU 显存策略)
**注意**:`mineru[core]`(Dockerfile 阶段 9 安装的)= vlm + pipeline + gradio,**不包含 `mineru[vllm]`**,因此 vllm 版本约束不会触发 pip 依赖冲突——editable 注册的假版本号只要功能上不被校验即可,而 `0.11.0` 恰好让校验逻辑走正确分支。
### 7.2 amdsmi 缺失导致 `Device string must not be empty`
**现象**:7.1 修复后(`vllm runtime import OK: 0.11.0`),解析任务在模型初始化阶段失败:
```
File ".../vllm/config/device.py", line 78, in __post_init__
self.device = torch.device(self.device_type)
RuntimeError: Device string must not be empty
```
启动日志里同时有:
```
WARNING [rocm.py:39] Failed to import from amdsmi: No module named 'amdsmi'
vllm runtime import OK: 0.11.0, platform=UnspecifiedPlatform
WARNING: vLLM platform detection returned UnspecifiedPlatform.
```
**原因**:vllm main 的 ROCm 平台检测依赖 `amdsmi`。检测链:
1. `resolve_current_platform_cls_qualname()` 遍历 `builtin_platform_plugins`,调用 `rocm_platform_plugin()`
2. `rocm_platform_plugin()` 内部 `import amdsmi` → 失败(except)→ `is_rocm=False` → 返回 `None`
3. 无任何 builtin plugin 激活 → `platform_cls_qualname = "vllm.platforms.interface.UnspecifiedPlatform"`
4. `current_platform.device_type = ''` → `torch.device('')` 抛 `Device string must not be empty`
`amdsmi` 缺失的根因:阶段 6.5 的条件安装 `if [ -d /opt/rocm/share/amd_smi ]`,**ROCm 7.2 的 apt 包不再提供该目录**(`pip list` 无 amdsmi、`/opt/rocm/share/amd_smi` 不存在、dpkg 无 amdsmi 包),安装被跳过。
> 注:`rocm_platform_plugin` 定义在 `platforms/__init__.py`(第 111 行附近),**不是** `platforms/rocm.py`。早期补丁脚本尝试 `from vllm.platforms.rocm import rocm_platform_plugin` 注册 entry_point,路径错误导致 `ImportError`——这是 entry_point 注册失败的根因,但本方案不依赖 entry_point,改走 builtin plugin 修复。
**解决**:在 `patch_vllm_platform.py` 的补丁 6 中,向 `rocm_platform_plugin()` 的 `return` 语句前注入 `torch.version.hip` 兜底:
```python
# __init__.py 中 rocm_platform_plugin 的 return 前:
if not is_rocm:
try:
import torch as _torch
if _torch.version.hip is not None:
is_rocm = True
except Exception:
pass
return "vllm.platforms.rocm.RocmPlatform" if is_rocm else None
```
amdsmi 缺失时 `is_rocm=False`,补丁用 `torch.version.hip` 翻转为 `True` → 返回 `RocmPlatform` → `device_type='cuda'` → 错误消失。
**关键实现细节**(补丁 6 多次失效的教训):
- **不能用精确字符串匹配**。vllm main 频繁重构,引号风格、空格、行结构都会变。早期补丁用 `old = " return 'vllm.platforms.rocm.RocmPlatform' if is_rocm else None"`(单引号)匹配,但实际文件是双引号,导致 `pattern not found`。
- **改用语义定位**:遍历行,找同时含 `RocmPlatform` + `return` + `is_rocm` 的行作为注入点,取该行缩进对齐。兼容单/双引号。
- 幂等:注入前检查文件内是否已有 `torch.version.hip is not None`,避免重复注入。
**验证**:
```bash
docker exec mineru-gradio /opt/mineru_venv/bin/python -c "
from vllm.platforms import current_platform
print('platform:', type(current_platform).__name__)
print('device_type:', repr(current_platform.device_type))
"
# 期望:platform: RocmPlatform / device_type: 'cuda'
```
**放弃的方案**:`sitecustomize.py` 运行时强制注入 `current_platform`。失败原因:`current_platform` 是 lazy init(首次访问才 resolve),sitecustomize 在 Python 启动最早期执行时触发提前 resolve,而那时补丁 6 尚未应用(或被 `except: pass` 吞错),赋值后可能被后续逻辑覆盖。改 builtin plugin 本体(补丁 6)才是 vllm 官方检测路径,最干净。
### 7.3 热修复与镜像固化的操作流程
容器运行中(不重建镜像)热修复时注意:
1. **`./scripts:/opt/scripts:ro` 是只读卷挂载**(见 `docker-compose.yml`)。`docker cp` 写 `/opt/scripts` 会报 `mounted volume is marked read-only`。但容器内 `/opt/scripts` 直接映射宿主机 `./scripts`,**改宿主机文件即生效**,无需 `docker cp`。
2. **`/opt/vllm` 在镜像层**(非只读挂载),可直接 `docker exec` 写入,重启不丢失。
3. 补丁脚本幂等且检测 `already applied`,重启容器重跑 entrypoint 不会撤销已注入的改动。
完整固化流程(让修复进入镜像):
- 修改 `Dockerfile` 阶段 8b(写 `_version.py` + egg-info 用 `0.11.0`)
- 修改 `scripts/patch_vllm_platform.py`(补丁 6 语义定位 + PEP 440 校验)
- 重建镜像(8a 的 ninja 编译层有缓存,几分钟):
```bash
docker compose build && docker compose --profile gradio up -d --force-recreate
```
### 7.4 模型架构 inspect 失败(待解决)
**现象**:7.1、7.2 修复后(`platform=RocmPlatform`),解析任务在 vllm 加载模型阶段失败:
```
1 validation error for ModelConfig
Value error, Model architectures ['Qwen2VLForConditionalGeneration']
failed to be inspected. Please check the logs for more details.
```
启动日志同时有:
```
WARNING [config.py:71] Support for Transformers v4 is deprecated.
The Transformers v4 codepath will become unmaintained in vLLM v0.22.0
... Please upgrade to Transformers v5: pip install --upgrade transformers
```
**初步判断**:transformers 版本与 vllm main 的模型 inspect 逻辑不兼容。矛盾在于:
- vllm 提示升级到 transformers v5
- `mineru[pipeline]` 依赖锁死 `transformers<5.0.0,>=4.57.3`(强制 v4)
待诊断(确认实际版本、Qwen2VL 是否在 transformers 注册表、inspect 真实报错)后再补最终方案。
---
*最后更新: 2026-06-23*
+51
View File
@@ -101,6 +101,56 @@ def patch7_rocm_break_import_cycle():
print('Patch 7: already applied or pattern not found.') print('Patch 7: already applied or pattern not found.')
def patch9_registry_model_impl_compat():
"""补丁 9:registry.py —— vllm main 与 transformers v4 兼容
问题:vllm main 的 ModelRegistry 大量访问 model_config.model_impl,
该属性是 vllm ModelConfig 的字段(默认 "auto"),但 inspect 链路传入的
有时是 transformers config 对象(如 Qwen2VLConfig),v4 没有此属性 →
AttributeError → "Model architectures [...] failed to be inspected"。
同时 _try_resolve_transformers 末尾调用 model_config._get_transformers_backend_cls(),
v4 的 config 也没有该方法。
根因:mineru[core] 锁定 transformers<5.0.0(v4),vllm main 期望 v5。
本补丁把所有 model_config.model_impl 访问改成 getattr 兜底(缺属性时当 "auto",
走 fallback 分支匹配 vllm 注册表),并给 _get_transformers_backend_cls 加兜底。
采用逐处 getattr 替换,不依赖方法定位(比方法注入更可靠)。
"""
f = os.path.join(VLLM_DIR, 'model_executor', 'models', 'registry.py')
if not os.path.exists(f):
print('Patch 9: registry.py not found; skipping.')
return
c = open(f).read()
if '# mineru-rocm: model_impl v4 compat' in c:
print('Patch 9: already applied (model_impl v4 compat present).')
return
# 把 model_config.model_impl 访问替换为 getattr 兜底
before = c.count('model_config.model_impl')
c2 = c.replace(
'model_config.model_impl',
'getattr(model_config, "model_impl", "auto")'
)
replaced = before - c2.count('model_config.model_impl')
# _get_transformers_backend_cls 兜底:v4 无此方法
c2 = c2.replace(
'return model_config._get_transformers_backend_cls()',
'return getattr(model_config, "_get_transformers_backend_cls", lambda: None)()'
)
# 写入幂等标记(注释,便于重入检测)
c2 = '# mineru-rocm: model_impl v4 compat\n' + c2
if c2 != c:
open(f, 'w').write(c2)
print(f'Patch 9: registry.py model_impl v4 compat applied '
f'({replaced} access(es) wrapped).')
else:
print('Patch 9: no changes applied (pattern not found).')
def ensure_vllm_dist_info(): def ensure_vllm_dist_info():
"""为 /opt/vllm 源码导入创建最小 dist-info。 """为 /opt/vllm 源码导入创建最小 dist-info。
@@ -233,6 +283,7 @@ def install_sitecustomize_force_rocm():
def main(): def main():
patch6_init_platform_fallback() patch6_init_platform_fallback()
patch7_rocm_break_import_cycle() patch7_rocm_break_import_cycle()
patch9_registry_model_impl_compat()
install_sitecustomize_force_rocm() install_sitecustomize_force_rocm()
try: try:
ensure_vllm_installed() ensure_vllm_installed()