Explorar el Código

pdf_converter_v2: 同步设备环境识别(nvi/npu)、mineru-api.service、config/utils;mineru: models_download_utils local配置None检查

何文松 hace 3 semanas
padre
commit
206bdccbb4

+ 6 - 0
mineru/utils/models_download_utils.py

@@ -18,6 +18,12 @@ def auto_download_and_get_model_root_path(relative_path: str, repo_mode='pipelin
 
     if model_source == 'local':
         local_models_config = get_local_models_dir()
+        if local_models_config is None:
+            raise ValueError(
+                "MINERU_MODEL_SOURCE=local but local models config is not set. "
+                "Either set MINERU_MODEL_SOURCE to 'modelscope' or 'huggingface', "
+                "or configure 'models-dir' (with 'pipeline' and 'vlm' keys) in MinerU config."
+            )
         root_path = local_models_config.get(repo_mode, None)
         if not root_path:
             raise ValueError(f"Local path for repo_mode '{repo_mode}' is not configured.")

+ 5 - 0
pdf_converter_v2/config.py

@@ -6,6 +6,11 @@
 
 import os
 
+# 设备环境:nvi(NVIDIA GPU)/ npu(华为昇腾 NPU)/ cpu,用于按环境选择 VLLM_PLUGINS、PADDLE_OCR_DEVICE 等
+from .utils.device_env import detect_device_kind
+
+DEVICE_KIND = os.getenv("PDF_CONVERTER_DEVICE_KIND") or detect_device_kind()
+
 # 默认模型配置(与 v1 保持一致)
 DEFAULT_MODEL_NAME = "OpenDataLab/MinerU2.5-2509-1.2B"
 DEFAULT_GPU_MEMORY_UTILIZATION = 0.9

+ 11 - 18
pdf_converter_v2/scripts/mineru-api.service

@@ -1,27 +1,20 @@
-# MinerU file_parse API - systemd 服务
-# 仅适用于宿主机(已用 systemd 作为 init)。Docker 容器内无 systemd,请改用 scripts/start_mineru_in_container.sh
-# 工作目录:/root/work/Clerk2.5
-#
-# 安装步骤(在宿主机上,且以 systemd 启动):
-#   sudo cp mineru-api.service /etc/systemd/system/
-#   sudo systemctl daemon-reload
-#   sudo systemctl enable mineru-api
-#   sudo systemctl start mineru-api
-#   sudo systemctl status mineru-api
-#
-# 若本机 simsimd/scikit_learn 的 libgomp 路径不同,请修改 Environment=LD_PRELOAD
+# MinerU file_parse API - 最简 systemd 服务(NVIDIA GPU)
+# 安装:sudo cp mineru-api.service /etc/systemd/system/
+#       sudo systemctl daemon-reload && sudo systemctl enable --now mineru-api
 
 [Unit]
-Description=MinerU file_parse API (uvicorn)
+Description=MinerU file_parse API
 After=network.target
 
 [Service]
 Type=simple
-WorkingDirectory=/root/work/Clerk2.5
-# NPU/容器内需预加载 libgomp,避免 static TLS 报错
-Environment=LD_PRELOAD=/usr/local/lib/python3.10/dist-packages/simsimd.libs/libgomp-a49a47f9.so.1.0.0:/usr/local/lib/python3.10/dist-packages/scikit_learn.libs/libgomp-d22c30c5.so.1.0.0
-Environment=PYTHONPATH=/root/work/Clerk2.5
-ExecStart=/usr/bin/python3 -m uvicorn mineru.cli.fast_api:app --host 0.0.0.0 --port 5282
+WorkingDirectory=/root/workspeac/Clerk2.5
+Environment=MINERU_MODEL_SOURCE=modelscope
+# 不加载 vLLM 插件,避免 PaddleX GenAI 插件在纯 NVIDIA 环境下报错
+Environment=VLLM_PLUGINS=
+# 跳过模型源连通性检查,加快启动(可选)
+Environment=PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK=True
+ExecStart=/root/workspeac/Clerk2.5/.venv_paddleocr/bin/mineru-api --host 0.0.0.0 --port 5282
 Restart=on-failure
 RestartSec=5
 User=root

+ 15 - 0
pdf_converter_v2/utils/__init__.py

@@ -2,3 +2,18 @@
 
 """工具函数模块"""
 
+from .device_env import (
+    DeviceKind,
+    detect_device_kind,
+    is_nvidia,
+    is_npu,
+    is_cpu_only,
+)
+
+__all__ = [
+    "DeviceKind",
+    "detect_device_kind",
+    "is_nvidia",
+    "is_npu",
+    "is_cpu_only",
+]

+ 94 - 0
pdf_converter_v2/utils/device_env.py

@@ -0,0 +1,94 @@
+# Copyright (c) Opendatalab. All rights reserved.
+
+"""
+设备环境识别:区分本地是 NVIDIA GPU (nvi) 还是华为昇腾 NPU (npu)。
+用于在代码中按环境设置 VLLM_PLUGINS、LD_PRELOAD、PADDLE_OCR_DEVICE 等。
+"""
+
+import os
+import subprocess
+from typing import Literal
+
+DeviceKind = Literal["nvi", "npu", "cpu"]
+
+# 环境变量显式指定时优先使用(nvi / npu / cpu)
+ENV_DEVICE_KIND = "PDF_CONVERTER_DEVICE_KIND"
+
+
+def _nvidia_available() -> bool:
+    """检测是否有可用 NVIDIA 环境(CUDA / nvidia-smi)。"""
+    if os.getenv("CUDA_VISIBLE_DEVICES") is not None:
+        # 若显式设为空字符串表示隐藏 GPU,不视为 nvi
+        if os.getenv("CUDA_VISIBLE_DEVICES", "").strip() == "":
+            return False
+        return True
+    try:
+        r = subprocess.run(
+            ["nvidia-smi"],
+            capture_output=True,
+            timeout=5,
+            check=False,
+        )
+        return r.returncode == 0
+    except (FileNotFoundError, subprocess.TimeoutExpired):
+        return False
+
+
+def _npu_available() -> bool:
+    """检测是否有华为昇腾 NPU 环境。"""
+    if os.getenv("ASCEND_HOME"):
+        return True
+    if os.getenv("ASCEND_RT_VISIBLE_DEVICES") is not None:
+        return True
+    if os.getenv("MINERU_DEVICE_MODE", "").lower().startswith("npu"):
+        return True
+    try:
+        r = subprocess.run(
+            ["npu-smi", "info"],
+            capture_output=True,
+            timeout=5,
+            check=False,
+        )
+        return r.returncode == 0
+    except (FileNotFoundError, subprocess.TimeoutExpired):
+        pass
+    return False
+
+
+def detect_device_kind() -> DeviceKind:
+    """
+    识别当前运行环境为 nvi(NVIDIA GPU)、npu(华为昇腾 NPU)或 cpu。
+
+    优先级:
+    1. 环境变量 PDF_CONVERTER_DEVICE_KIND(nvi / npu / cpu)
+    2. NPU 相关环境或 npu-smi 可用 -> npu
+    3. NVIDIA 相关环境或 nvidia-smi 可用 -> nvi
+    4. 否则 -> cpu
+
+    Returns:
+        "nvi" | "npu" | "cpu"
+    """
+    raw = os.getenv(ENV_DEVICE_KIND, "").strip().lower()
+    if raw in ("nvi", "npu", "cpu"):
+        return raw  # type: ignore[return-value]
+
+    if _npu_available():
+        return "npu"
+    if _nvidia_available():
+        return "nvi"
+    return "cpu"
+
+
+def is_nvidia() -> bool:
+    """当前是否为 NVIDIA GPU 环境。"""
+    return detect_device_kind() == "nvi"
+
+
+def is_npu() -> bool:
+    """当前是否为华为昇腾 NPU 环境。"""
+    return detect_device_kind() == "npu"
+
+
+def is_cpu_only() -> bool:
+    """当前是否仅为 CPU 环境(无 nvi/npu)。"""
+    return detect_device_kind() == "cpu"

+ 5 - 1
pdf_converter_v2/utils/paddleocr_fallback.py

@@ -45,10 +45,14 @@ MINERU_LOCK_FILE = "/tmp/mineru_service_lock"
 MINERU_COUNT_FILE = "/tmp/mineru_service_count"
 
 # PaddleOCR 推理设备:NPU 环境下需设为 npu 或 npu:0,否则会走 CPU 并可能段错误
-# 通过环境变量 PADDLE_OCR_DEVICE 指定,例如:export PADDLE_OCR_DEVICE=npu:0
+# 通过环境变量 PADDLE_OCR_DEVICE 指定;未设置时根据设备环境自动选择(NPU 下默认 npu:0)
 def _paddle_ocr_device_args() -> list:
     """返回 PaddleOCR 命令的 --device 参数列表(若未设置则返回空列表)"""
     device = os.getenv("PADDLE_OCR_DEVICE", "").strip()
+    if not device:
+        from .device_env import is_npu
+        if is_npu():
+            device = "npu:0"
     if device:
         return ["--device", device]
     return []