feat: add hybrid-flash processing functions for synchronous and asynchronous analyze stages

This commit is contained in:
myhloli
2026-06-05 19:34:19 +08:00
parent 742ac25101
commit d4e4ad67d5
2 changed files with 162 additions and 30 deletions
@@ -297,6 +297,8 @@ def doc_analyze(
**kwargs,
):
"""hybrid-flash第一阶段analyze:返回pipeline形态model_list和backend元信息。"""
# hybrid-flash 当前只执行 analyze,客户端输出开关属于最终输出阶段,不能下传到模型初始化。
kwargs.pop("client_side_output_generation", None)
if predictor is None:
predictor = ModelSingleton().get_model(backend, model_path, server_url, **kwargs)
predictor = _maybe_enable_serial_execution(predictor, backend)
@@ -425,6 +427,8 @@ async def aio_doc_analyze(
**kwargs,
):
"""异步hybrid-flash analyze入口,返回pipeline形态model_list和backend元信息。"""
# hybrid-flash 当前只执行 analyze,客户端输出开关属于最终输出阶段,不能下传到模型初始化。
kwargs.pop("client_side_output_generation", None)
if predictor is None:
predictor = await _get_model_async(backend, model_path, server_url, **kwargs)
predictor = _maybe_enable_serial_execution(predictor, backend)
+158 -30
View File
@@ -10,6 +10,7 @@ from typing import Sequence
from loguru import logger
from mineru.cli.backend_options import normalize_backend
from mineru.data.data_reader_writer import FileBasedDataWriter
from mineru.utils.draw_bbox import draw_layout_bbox, draw_span_bbox
from mineru.utils.engine_utils import get_vlm_engine
@@ -69,9 +70,15 @@ def ensure_backend_dependencies(backend: str) -> None:
def _load_hybrid_analyze_entrypoint(entrypoint_name: str, backend: str):
"""按 hybrid 后端家族加载普通 hybrid 或 hybrid-flash 的 analyze 入口。"""
ensure_backend_dependencies(backend)
module_name = (
"mineru.backend.hybrid_flash.hybrid_flash_analyze"
if backend.startswith("hybrid-flash-")
else "mineru.backend.hybrid.hybrid_analyze"
)
try:
hybrid_analyze = importlib.import_module("mineru.backend.hybrid.hybrid_analyze")
hybrid_analyze = importlib.import_module(module_name)
except (ImportError, ModuleNotFoundError) as exc:
raise HybridDependencyError(
build_hybrid_dependency_error_message(backend)
@@ -498,6 +505,112 @@ def _process_vlm(
)
def _process_hybrid_flash(
output_dir,
pdf_file_names,
pdf_bytes_list,
h_lang_list,
parse_method,
inline_formula_enable,
backend,
f_draw_layout_bbox,
f_draw_span_bbox,
f_dump_md,
f_dump_middle_json,
f_dump_model_output,
f_dump_orig_pdf,
f_dump_content_list,
f_make_md_mode,
table_enable,
server_url=None,
image_analysis=True,
**kwargs,
):
"""同步运行 hybrid-flash analyze 阶段,当前只产出 analyze 调试结果。"""
hybrid_flash_doc_analyze = _load_hybrid_analyze_entrypoint(
"doc_analyze",
f"hybrid-flash-{backend}",
)
if not backend.endswith("client"):
server_url = None
for idx, (pdf_bytes, lang) in enumerate(zip(pdf_bytes_list, h_lang_list)):
pdf_file_name = pdf_file_names[idx]
local_image_dir, _local_md_dir = prepare_env(
output_dir,
pdf_file_name,
f"hybrid_{parse_method}",
)
image_writer = FileBasedDataWriter(local_image_dir)
hybrid_flash_doc_analyze(
pdf_bytes=pdf_bytes,
image_writer=image_writer,
backend=backend,
parse_method=parse_method,
language=lang,
inline_formula_enable=inline_formula_enable,
table_enable=table_enable,
server_url=server_url,
image_analysis=image_analysis,
**kwargs,
)
async def _async_process_hybrid_flash(
output_dir,
pdf_file_names,
pdf_bytes_list,
h_lang_list,
parse_method,
inline_formula_enable,
backend,
f_draw_layout_bbox,
f_draw_span_bbox,
f_dump_md,
f_dump_middle_json,
f_dump_model_output,
f_dump_orig_pdf,
f_dump_content_list,
f_make_md_mode,
table_enable,
server_url=None,
image_analysis=True,
**kwargs,
):
"""异步运行 hybrid-flash analyze 阶段,当前只产出 analyze 调试结果。"""
aio_hybrid_flash_doc_analyze = _load_hybrid_analyze_entrypoint(
"aio_doc_analyze",
f"hybrid-flash-{backend}",
)
if not backend.endswith("client"):
server_url = None
for idx, (pdf_bytes, lang) in enumerate(zip(pdf_bytes_list, h_lang_list)):
pdf_file_name = pdf_file_names[idx]
local_image_dir, _local_md_dir = prepare_env(
output_dir,
pdf_file_name,
f"hybrid_{parse_method}",
)
image_writer = FileBasedDataWriter(local_image_dir)
await aio_hybrid_flash_doc_analyze(
pdf_bytes=pdf_bytes,
image_writer=image_writer,
backend=backend,
parse_method=parse_method,
language=lang,
inline_formula_enable=inline_formula_enable,
table_enable=table_enable,
server_url=server_url,
image_analysis=image_analysis,
**kwargs,
)
def _process_hybrid(
output_dir,
pdf_file_names,
@@ -684,6 +797,7 @@ def do_parse(
client_side_output_generation=False,
**kwargs,
):
backend = normalize_backend(backend)
need_remove_index = _process_office_doc(
output_dir,
pdf_file_names=pdf_file_names,
@@ -718,10 +832,7 @@ def do_parse(
if backend.startswith("vlm-"):
backend = backend[4:]
if backend == "vllm-async-engine":
raise Exception("vlm-vllm-async-engine backend is not supported in sync mode, please use vlm-vllm-engine backend")
if backend == "auto-engine":
if backend == "engine":
backend = get_vlm_engine(inference_engine='auto', is_async=False)
os.environ['MINERU_VLM_FORMULA_ENABLE'] = str(formula_enable)
@@ -737,24 +848,33 @@ def do_parse(
elif backend.startswith("hybrid-"):
ensure_backend_dependencies(backend)
backend = backend[7:]
is_flash = backend.startswith("flash-")
if backend == "vllm-async-engine":
raise Exception(
"hybrid-vllm-async-engine backend is not supported in sync mode, please use hybrid-vllm-engine backend")
if is_flash:
backend = backend[6:]
if backend == "auto-engine":
if backend == "engine":
backend = get_vlm_engine(inference_engine='auto', is_async=False)
os.environ['MINERU_VLM_TABLE_ENABLE'] = str(table_enable)
os.environ['MINERU_VLM_FORMULA_ENABLE'] = "true"
_process_hybrid(
output_dir, pdf_file_names, pdf_bytes_list, p_lang_list, parse_method, formula_enable, backend,
f_draw_layout_bbox, f_draw_span_bbox, f_dump_md, f_dump_middle_json,
f_dump_model_output, f_dump_orig_pdf, f_dump_content_list, f_make_md_mode,
server_url, image_analysis=image_analysis,
client_side_output_generation=client_side_output_generation, **kwargs,
)
if is_flash:
_process_hybrid_flash(
output_dir, pdf_file_names, pdf_bytes_list, p_lang_list, parse_method, formula_enable, backend,
f_draw_layout_bbox, f_draw_span_bbox, f_dump_md, f_dump_middle_json,
f_dump_model_output, f_dump_orig_pdf, f_dump_content_list, f_make_md_mode,
table_enable, server_url, image_analysis=image_analysis,
client_side_output_generation=client_side_output_generation, **kwargs,
)
else:
_process_hybrid(
output_dir, pdf_file_names, pdf_bytes_list, p_lang_list, parse_method, formula_enable, backend,
f_draw_layout_bbox, f_draw_span_bbox, f_dump_md, f_dump_middle_json,
f_dump_model_output, f_dump_orig_pdf, f_dump_content_list, f_make_md_mode,
server_url, image_analysis=image_analysis,
client_side_output_generation=client_side_output_generation, **kwargs,
)
async def aio_do_parse(
@@ -781,6 +901,7 @@ async def aio_do_parse(
client_side_output_generation=False,
**kwargs,
):
backend = normalize_backend(backend)
# Office 解析是同步且可能耗时的操作,异步入口需要放到线程中避免阻塞事件循环。
need_remove_index = await asyncio.to_thread(
_process_office_doc,
@@ -818,10 +939,7 @@ async def aio_do_parse(
if backend.startswith("vlm-"):
backend = backend[4:]
if backend == "vllm-engine":
raise Exception("vlm-vllm-engine backend is not supported in async mode, please use vlm-vllm-async-engine backend")
if backend == "auto-engine":
if backend == "engine":
backend = get_vlm_engine(inference_engine='auto', is_async=True)
os.environ['MINERU_VLM_FORMULA_ENABLE'] = str(formula_enable)
@@ -837,23 +955,33 @@ async def aio_do_parse(
elif backend.startswith("hybrid-"):
ensure_backend_dependencies(backend)
backend = backend[7:]
is_flash = backend.startswith("flash-")
if backend == "vllm-engine":
raise Exception("hybrid-vllm-engine backend is not supported in async mode, please use hybrid-vllm-async-engine backend")
if is_flash:
backend = backend[6:]
if backend == "auto-engine":
if backend == "engine":
backend = get_vlm_engine(inference_engine='auto', is_async=True)
os.environ['MINERU_VLM_TABLE_ENABLE'] = str(table_enable)
os.environ['MINERU_VLM_FORMULA_ENABLE'] = "true"
await _async_process_hybrid(
output_dir, pdf_file_names, pdf_bytes_list, p_lang_list, parse_method, formula_enable, backend,
f_draw_layout_bbox, f_draw_span_bbox, f_dump_md, f_dump_middle_json,
f_dump_model_output, f_dump_orig_pdf, f_dump_content_list, f_make_md_mode,
server_url, image_analysis=image_analysis,
client_side_output_generation=client_side_output_generation, **kwargs,
)
if is_flash:
await _async_process_hybrid_flash(
output_dir, pdf_file_names, pdf_bytes_list, p_lang_list, parse_method, formula_enable, backend,
f_draw_layout_bbox, f_draw_span_bbox, f_dump_md, f_dump_middle_json,
f_dump_model_output, f_dump_orig_pdf, f_dump_content_list, f_make_md_mode,
table_enable, server_url, image_analysis=image_analysis,
client_side_output_generation=client_side_output_generation, **kwargs,
)
else:
await _async_process_hybrid(
output_dir, pdf_file_names, pdf_bytes_list, p_lang_list, parse_method, formula_enable, backend,
f_draw_layout_bbox, f_draw_span_bbox, f_dump_md, f_dump_middle_json,
f_dump_model_output, f_dump_orig_pdf, f_dump_content_list, f_make_md_mode,
server_url, image_analysis=image_analysis,
client_side_output_generation=client_side_output_generation, **kwargs,
)
if __name__ == "__main__":