From 896c96257bd990d9f07de5f8ed9945d9d1ee1762 Mon Sep 17 00:00:00 2001 From: chenjw28 <792430652@qq.com> Date: Tue, 23 Jun 2026 13:58:15 +0800 Subject: [PATCH] x --- docker/ISSUES.md | 146 +++++++++++++++++++++++++- docker/scripts/patch_vllm_platform.py | 51 +++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/docker/ISSUES.md b/docker/ISSUES.md index 3d9c782..dad5601 100644 --- a/docker/ISSUES.md +++ b/docker/ISSUES.md @@ -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* diff --git a/docker/scripts/patch_vllm_platform.py b/docker/scripts/patch_vllm_platform.py index d047ed0..ecf98f1 100644 --- a/docker/scripts/patch_vllm_platform.py +++ b/docker/scripts/patch_vllm_platform.py @@ -101,6 +101,56 @@ def patch7_rocm_break_import_cycle(): 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(): """为 /opt/vllm 源码导入创建最小 dist-info。 @@ -233,6 +283,7 @@ def install_sitecustomize_force_rocm(): def main(): patch6_init_platform_fallback() patch7_rocm_break_import_cycle() + patch9_registry_model_impl_compat() install_sitecustomize_force_rocm() try: ensure_vllm_installed()