feat: enhance local API launch mode handling and update Ascend.md documentation

This commit is contained in:
myhloli
2026-04-20 23:37:34 +08:00
parent 247be09c23
commit 23b3e5d30f
2 changed files with 63 additions and 47 deletions
+3 -7
View File
@@ -73,7 +73,6 @@ docker run -u root --name mineru_docker --privileged=true \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
-e VLLM_WORKER_MULTIPROC_METHOD=spawn \
-e MINERU_LOCAL_API_LAUNCH_MODE=spawn \
-e MINERU_MODEL_SOURCE=local \
-e MINERU_LMDEPLOY_DEVICE=ascend \
-it mineru:npu-vllm-latest \
@@ -81,7 +80,8 @@ docker run -u root --name mineru_docker --privileged=true \
```
>[!TIP]
> 请根据实际情况选择使用`vllm`或`lmdeploy`版本的镜像,如需使用lmdeploy,替换上述命令中的`mineru:npu-vllm-latest`为`mineru:npu-lmdeploy-latest`即可。
> - 请根据实际情况选择使用`vllm`或`lmdeploy`版本的镜像,如需使用lmdeploy,替换上述命令中的`mineru:npu-vllm-latest`为`mineru:npu-lmdeploy-latest`即可。
> - 在 Ascend NPU 环境下,请务必保留环境变量`MINERU_LMDEPLOY_DEVICE=ascend`,即使您是使用`vllm`后端,也需要保留该环境变量。
执行该命令后,您将进入到Docker容器的交互式终端,您可以直接在容器内运行MinerU相关命令来使用MinerU的功能。
您也可以直接通过替换`/bin/bash`为服务启动命令来启动MinerU服务,详细说明请参考[通过命令启动服务](https://opendatalab.github.io/MinerU/zh/usage/quick_usage/#apiwebuihttp-clientserver)。
@@ -95,10 +95,6 @@ docker run -u root --name mineru_docker --privileged=true \
## 4. 注意事项
>[!WARNING]
> - 由于 MinerU 3 及以上版本将 `client` 与 `gradio` 改为通过本地 API 编排;在 `NPU(Ascend)`环境下运行时,必须设置环境变量 `MINERU_LOCAL_API_LAUNCH_MODE=spawn`
> - 环境变量`MINERU_LOCAL_API_LAUNCH_MODE`自`3.1.1`版本后引入
不同环境下,MinerU对Ascend加速卡的支持情况如下表所示:
<table border="1">
@@ -176,4 +172,4 @@ docker run -u root --name mineru_docker --privileged=true \
>[!TIP]
> - NPU加速卡指定可用加速卡的方式与NVIDIA GPU类似,请参考[ASCEND_RT_VISIBLE_DEVICES](https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/850alpha001/maintenref/envvar/envref_07_0028.html)
> - 在Ascend平台可以通过`npu-smi info`命令查看加速卡的使用情况,并根据需要指定空闲的加速卡ID以避免资源冲突。
> - 在Ascend平台可以通过`npu-smi info`命令查看加速卡的使用情况,并根据需要指定空闲的加速卡ID以避免资源冲突。
+60 -40
View File
@@ -5,6 +5,7 @@ import json
import mimetypes
import multiprocessing
import os
import platform
import signal
import socket
import subprocess
@@ -29,6 +30,7 @@ from mineru.cli.api_protocol import (
from mineru.utils.config_reader import (
get_max_concurrent_requests as read_max_concurrent_requests,
)
from mineru.utils.check_sys_env import is_linux_environment
HEALTH_ENDPOINT = "/health"
TASKS_ENDPOINT = "/tasks"
@@ -39,8 +41,10 @@ LOCAL_API_CLEANUP_RETRIES = 8
LOCAL_API_CLEANUP_RETRY_INTERVAL_SECONDS = 0.25
PROCESS_TREE_CLEANUP_GRACE_SECONDS = 0.1
MINERU_LOCAL_API_LAUNCH_MODE_ENV = "MINERU_LOCAL_API_LAUNCH_MODE"
MINERU_LMDEPLOY_DEVICE_ENV = "MINERU_LMDEPLOY_DEVICE"
LOCAL_API_LAUNCH_MODE_SUBPROCESS = "subprocess"
LOCAL_API_LAUNCH_MODE_SPAWN = "spawn"
MINERU_SPAWN_DEVICE_LIST = ["ascend"]
ManagedProcess = subprocess.Popen[bytes] | multiprocessing.process.BaseProcess
@@ -105,6 +109,19 @@ def get_local_api_launch_mode(default: str = LOCAL_API_LAUNCH_MODE_SUBPROCESS) -
return default
def get_effective_local_api_launch_mode(
default: str = LOCAL_API_LAUNCH_MODE_SUBPROCESS,
) -> str:
if os.getenv(MINERU_LOCAL_API_LAUNCH_MODE_ENV) is not None:
return get_local_api_launch_mode(default=default)
device_type = os.getenv(MINERU_LMDEPLOY_DEVICE_ENV, "")
if device_type.strip().lower() in MINERU_SPAWN_DEVICE_LIST:
return LOCAL_API_LAUNCH_MODE_SPAWN
return default
def build_managed_process_popen_kwargs() -> dict[str, object]:
if os.name == "nt":
creationflags = getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
@@ -248,6 +265,19 @@ def _managed_process_is_running(process: ManagedProcess | None) -> bool:
return process is not None and _managed_process_exit_code(process) is None
def _validate_local_api_launch_mode_platform(launch_mode: str) -> None:
if launch_mode != LOCAL_API_LAUNCH_MODE_SPAWN or is_linux_environment():
return
current_platform = platform.system() or os.name
raise click.ClickException(
f"{MINERU_LOCAL_API_LAUNCH_MODE_ENV}=spawn is supported only on Linux. "
f"Current platform: {current_platform}. Unset "
f"{MINERU_LOCAL_API_LAUNCH_MODE_ENV} or set "
f"{MINERU_LOCAL_API_LAUNCH_MODE_ENV}={LOCAL_API_LAUNCH_MODE_SUBPROCESS}."
)
def _stop_spawn_managed_process(
process: multiprocessing.process.BaseProcess | None,
*,
@@ -257,36 +287,6 @@ def _stop_spawn_managed_process(
if process is None and process_group_id is None:
return
if os.name == "nt":
if process is None or process.exitcode is not None:
return
cleanup_target_process_group_id = (
process_group_id if process_group_id is not None else process.pid
)
process.terminate()
process.join(timeout=shutdown_timeout_seconds)
if process.exitcode is not None:
return
_signal_process_tree_pid(cleanup_target_process_group_id, force=False)
process.join(timeout=shutdown_timeout_seconds)
if process.exitcode is not None:
return
_signal_process_tree_pid(cleanup_target_process_group_id, force=True)
process.join(timeout=shutdown_timeout_seconds)
if process.exitcode is None:
logger.warning(
"Managed MinerU spawn process {} did not exit after forceful stop.",
process.pid,
)
return
if process_group_id is None:
if process is None or process.exitcode is not None:
return
@@ -336,6 +336,24 @@ def _build_local_api_server_cli_args(
)
def _cli_args_include_flag(cli_args: Sequence[str], flag: str) -> bool:
return flag in cli_args or any(arg.startswith(f"{flag}=") for arg in cli_args)
def _validate_local_api_launch_mode_cli_args(
launch_mode: str,
cli_args: Sequence[str],
) -> None:
if (
launch_mode == LOCAL_API_LAUNCH_MODE_SPAWN
and _cli_args_include_flag(cli_args, "--reload")
):
raise click.ClickException(
"Local mineru-api spawn launch mode does not support --reload. "
"Remove --reload or set MINERU_LOCAL_API_LAUNCH_MODE=subprocess."
)
def _build_local_api_server_env(
output_root: Path,
*,
@@ -371,14 +389,13 @@ def _run_local_api_via_spawn(
os.chdir(cwd)
sys.argv = ["mineru-api", *cli_args]
if os.name != "nt":
try:
os.setsid()
except OSError as exc:
logger.warning(
"Failed to create a dedicated process group for spawned mineru-api: {}",
exc,
)
try:
os.setsid()
except OSError as exc:
logger.warning(
"Failed to create a dedicated process group for spawned mineru-api: {}",
exc,
)
from mineru.cli.fast_api import main as fast_api_main
@@ -436,10 +453,12 @@ class LocalAPIServer:
resolved_port = find_free_port()
self.base_url = f"http://127.0.0.1:{resolved_port}"
self._launch_mode = get_local_api_launch_mode()
self._launch_mode = get_effective_local_api_launch_mode()
_validate_local_api_launch_mode_platform(self._launch_mode)
# On Windows, the temporary FastAPI child process can stall during
# parsing startup when launched with stdin=PIPE and an EOF-based
# shutdown watcher. Spawn mode also uses explicit process termination.
# shutdown watcher, so we only enable that path on non-Windows
# subprocess launches.
self._use_stdin_shutdown_watcher = (
self._launch_mode == LOCAL_API_LAUNCH_MODE_SUBPROCESS and os.name != "nt"
)
@@ -457,6 +476,7 @@ class LocalAPIServer:
resolved_port,
self.extra_cli_args,
)
_validate_local_api_launch_mode_cli_args(self._launch_mode, cli_args)
if self._launch_mode == LOCAL_API_LAUNCH_MODE_SUBPROCESS:
command = [
sys.executable,