BabelDOC 异步翻译 API 完全指南:async_translate 的事件流、进度上报与取消机制
2026/9/16 8:27:37 网站建设 项目流程

BabelDOC 异步翻译 API 完全指南:async_translate 的事件流、进度上报与取消机制

【免费下载链接】BabelDOCYet Another Document Translator项目地址: https://gitcode.com/GitHub_Trending/ba/BabelDOC

导读

yadt.high_level.async_translate(当前仓库中为babeldoc.format.pdf.high_level.async_translate)为 PDF 翻译提供了一个基于 Pythonasyncio的异步接口,以**事件流(event stream)**的方式实时上报翻译进度,方便上层 UI(进度条、任务面板)与 CLI 消费。读完本文你将掌握:如何用async for消费翻译过程中的全部事件类型、五种事件的数据结构与语义、八个核心翻译阶段与真实权重、三种取消方式(CancelledErrorKeyboardInterruptcancel_translation()),以及底层ProgressMonitorAsyncCallback的实现原理。本文所有结论均可对应到 async_translate 实现 与 ProgressMonitor 实现 的源码。

一、异步翻译 API 概览

1.1 函数签名与设计定位

async_translate是一个异步生成器(async generator),定义在 babeldoc/format/pdf/high_level.py。它接收一个TranslationConfig对象,并在翻译过程中持续yield描述进度的字典事件:

async def async_translate(translation_config: TranslationConfig): """Asynchronously translate a PDF file with real-time progress reporting."""

同步版本translate()与异步版本共用同一套底层流程:两者最终都会进入do_translate()(high_level.py),区别在于异步版本把进度回调包装成跨线程的 asyncio 事件队列,从而让调用方能够以async for逐条接收进度事件,而不是阻塞等待一个最终结果。

1.2 与同步翻译的区别

维度同步translate(config)异步async_translate(config)
返回值直接返回TranslateResult异步生成器,yield 事件字典
进度获取通过ProgressMonitor回调/进度条事件流中的progress_start/progress_update/progress_end
取消依赖配置与回调支持asyncio原生取消与cancel_translation()
典型场景CLI、脚本批量处理GUI/Web 任务面板、需要实时进度反馈的应用

注意:仓库中实际的导入路径为babeldoc.format.pdf.high_level.async_translate(见 CLI 调用处 与 executor 适配器),文档中的yadt.high_level为历史包名。

二、快速上手:消费事件流

文档给出的基本用法可以直接照搬,完整可运行的形态如下:

import asyncio async def translate_with_progress(): config = TranslationConfig( input_file="example.pdf", translator=your_translator, # 一个 BaseTranslator 实例,如 OpenAITranslator lang_in="en", lang_out="zh", doc_layout_model=your_layout_model, # DocLayoutModel,用于版面分析 # ... 其他配置项,见下文参数小节 ) try: async for event in async_translate(config): if event["type"] == "progress_update": print(f"Progress: {event['overall_progress']}%") elif event["type"] == "finish": result = event["translate_result"] print(f"Translation completed: {result.original_pdf_path}") elif event["type"] == "error": print(f"Error occurred: {event['error']}") break except asyncio.CancelledError: print("Translation was cancelled") except KeyboardInterrupt: print("Translation was interrupted")

从源码看,事件流在收到error事件后就会停止迭代(high_level.py),因此在error分支break是安全且必要的;而finish事件则由ProgressMonitor.translate_done()触发(progress_monitor.py)。

2.1 构造 TranslationConfig 的关键参数

TranslationConfig的完整签名见 translation_config.py,以下是构造异步翻译配置时的常用参数:

参数类型默认值说明
translatorBaseTranslator必填翻译引擎,实现do_translate/do_llm_translate
input_filestr \| Path必填输入 PDF 路径
lang_in/lang_outstr必填源语言 / 目标语言代码
doc_layout_modelDocLayoutModel自动加载版面分析模型;不传时调用DocLayoutModel.load_available()
pagesstrNone页码范围,形如"1-,2,-3,4",由parse_pages()解析为(start, end)列表
report_intervalfloat0.1进度上报最小时间间隔(秒),控制progress_update的发送频率
output_dirstr \| Path当前目录输出 PDF 存放目录
working_dirstr \| Path临时目录中间文件工作目录;debug 模式固定为缓存目录下working/<输入文件名>
qpsint1翻译请求速率限制(每秒请求数),同时作为线程池pool_max_workers的默认值
no_dual/no_monoboolFalse是否不生成双语 / 单语 PDF
watermark_output_modeWatermarkOutputModeWatermarkedwatermarked/no_watermark/both三选一
debugboolFalse开启后保留中间 IL JSON、输出更详细日志
skip_scanned_detectionboolFalse跳过扫描件检测阶段
auto_extract_glossaryboolTrue是否启用术语自动抽取
skip_translationboolFalse跳过翻译(仅排版)
split_strategyBaseSplitStrategyNone大文件分片策略,可用create_max_pages_per_part_split_strategy()创建

依赖说明:translate_with_progress运行在asyncio事件循环中,底层翻译工作通过loop.run_in_executor(None, do_translate, pm, config)投递到线程池执行(high_level.py),所以即使翻译引擎是同步实现,也不会阻塞事件循环。

三、事件类型详解

async_translate一共产出五种事件,所有事件都以"type"字段区分。以下字段定义与 high_level.py 的 docstring 以及 ProgressMonitor 的发送点 完全一致。

3.1 progress_start —— 阶段开始事件

某个翻译阶段开始时发出,stage_progress恒为0.0stage_current恒为0

{ "type": "progress_start", "stage": str, # 当前阶段名称 "stage_progress": float, # 恒为 0.0 "stage_current": int, # 当前进度计数(0) "stage_total": int # 本阶段待处理的总条目数 }

源码出处:ProgressMonitor.stage_start()(progress_monitor.py),其中stage_total由各阶段在启动时上报,例如"翻译段落"阶段的total是待翻译段落数。

3.2 progress_update —— 进度更新事件

翻译过程中周期性发出,发送频率由TranslationConfig.report_interval控制(默认0.1秒)。这是消费端最常用的事件,用于驱动进度条:

{ "type": "progress_update", "stage": str, # 当前阶段名称 "stage_progress": float, # 当前阶段进度百分比(0-100) "stage_current": int, # 本阶段已处理的条目数 "stage_total": int, # 本阶段总条目数 "overall_progress": float # 整体翻译进度(0-100) }

源码出处:ProgressMonitor.stage_update()(progress_monitor.py)。注意两点实现细节:

  • 节流逻辑:当time.time() - last_report_time < report_intervalstage.total > 3时直接跳过上报,避免高频小步进刷屏;阶段总条目数不大于 3 时则每次都上报。
  • 整体进度overall_progresscalculate_current_progress()(progress_monitor.py)基于阶段权重加权计算,而不是简单平均——权重见下一节。

3.3 progress_end —— 阶段结束事件

阶段完成时发出,stage_progress恒为100.0stage_current等于stage_total

{ "type": "progress_end", "stage": str, # 已完成的阶段名称 "stage_progress": float, # 恒为 100.0 "stage_current": int, # 等于 stage_total "stage_total": int, # 本阶段处理的总条目数 "overall_progress": float # 整体翻译进度(0-100) }

源码出处:ProgressMonitor.stage_done()(progress_monitor.py)。它由TranslationStage.__exit__在阶段上下文管理器退出时触发,如果阶段提前结束(current != total且未取消),会先记录一条 warning 日志再上报结束事件。

3.4 finish —— 完成事件

整份文档翻译成功时发出,携带TranslateResult

{ "type": "finish", "translate_result": TranslateResult # 包含产物文件路径与耗时等信息 }

TranslateResult定义在 translation_config.py,主要字段包括:

字段含义
original_pdf_path原始输入 PDF 路径
mono_pdf_path/dual_pdf_path单语 / 双语 PDF 产物路径(含水印)
no_watermark_mono_pdf_path/no_watermark_dual_pdf_path无水印产物路径
total_seconds翻译总耗时(秒)
peak_memory_usage峰值内存占用(MB,含子进程)
total_valid_character_count全文件有效字符数统计
total_valid_text_token_count全文件有效文本 token 数统计(按 gpt-4o 口径)
auto_extracted_glossary_path自动抽取术语表导出路径(可选)

3.5 error —— 错误事件

翻译过程中任何异常都会先被记录日志,再以错误事件上报:

{ "type": "error", "error": str # 错误信息 }

源码出处:ProgressMonitor.translate_error()(progress_monitor.py),由do_translate()的异常分支调用(high_level.py)。错误事件之后事件流随即终止。error字段在取消场景下可能携带asyncio.CancelledError对象(见第五节)。

四、翻译阶段与整体进度权重

4.1 八个核心阶段(含子阶段)

文档列出的 8 个阶段对应真实流水线。在 high_level.py 的 TRANSLATE_STAGES 中,阶段被展开为 14 项并配有经验权重(权重之和为 100,用于计算整体进度):

阶段(文档名 → 真实 stage 名)权重对应实现
ILCreater →Parse PDF and Create Intermediate Representation14.12il_creater.py / il_creater_active.py
(扫描件检测)→DetectScannedFile2.45detect_scanned_file.py
LayoutParser →Parse Page Layout14.03layout_parser.py
(表格解析)→Parse Table1.0table_parser.py
ParagraphFinder →Parse Paragraphs6.26paragraph_finder.py
StylesAndFormulas →Parse Formulas and Styles1.66styles_and_formulas.py
(术语抽取)→Automatic Term Extraction30.0automatic_term_extractor.py
ILTranslator →Translate Paragraphs46.96il_translator.py / il_translator_llm_only.py
Typesetting →Typesetting4.71typesetting.py
FontMapper →Add Fonts0.61fontmap.py
PDFCreater →Generate drawing instructions1.96pdf_creater.py
(子集化字体)→Subset font0.92pdf_creater.py
(保存 PDF)→Save PDF6.34pdf_creater.py

每个阶段都会按上文三种progress_*事件上报自己的进度;overall_progressProgressMonitor将各阶段权重归一化后累加得到。

4.2 阶段裁剪:按配置动态调整流水线

get_translation_stage()(high_level.py)会根据配置裁剪阶段,进而影响事件流中出现的stage名称:

  • only_parse_generate_pdf=True:跳过检测、版面、表格、段落、公式、术语抽取、翻译、排版等全部中间阶段,只保留解析与生成;
  • skip_scanned_detection=True:移除DetectScannedFile
  • table_model为空:移除Parse Table(注意当前版本table_model已弃用并强制置None,见 translation_config.py);
  • auto_extract_glossary=False:移除Automatic Term Extraction
  • skip_translation=True:移除Translate Paragraphs

理解这一点对消费端很重要:不要硬编码假设事件流一定包含某个阶段,应始终以事件中的stage字段为准。

五、取消机制

文档列出了三种取消途径,源码全部支持:

5.1 方式一:抛出CancelledErrorasyncio.Task.cancel()

async_translate内部,async for event in callback循环若收到CancelledError,会立刻cancel_event.set()(high_level.py),随后future.cancel()尝试终止线程池中的翻译任务,并等待finish_event确认收尾(high_level.py)。

5.2 方式二:KeyboardInterrupt(Ctrl+C)

事件循环捕获KeyboardInterrupt时同样会设置取消事件并记录日志"Translation cancelled by user through keyboard interrupt"(high_level.py)。

5.3 方式三:TranslationConfig.cancel_translation()

这是推荐的程序化取消方式cancel_translation()会调用progress_monitor.cancel(),最终cancel_event.set()(translation_config.py、progress_monitor.py)。各翻译阶段内部通过raise_if_cancelled()(translation_config.py)在关键点检查取消标记并抛出asyncio.CancelledError,从而优雅中断。

文档中的完整示例(单独任务 + 延时取消):

async def translate_with_cancellation(): config = TranslationConfig( input_file="example.pdf", translator=your_translator, # ... 其他配置 ) try: # 在另一个任务中启动翻译 translation_task = asyncio.create_task(process_translation(config)) # 模拟某个需要取消的条件 await asyncio.sleep(5) config.cancel_translation() # 触发取消 await translation_task # 等待任务结束 except asyncio.CancelledError: print("Translation was cancelled") async def process_translation(config): async for event in async_translate(config): if event["type"] == "error": if isinstance(event["error"], asyncio.CancelledError): print("Translation was cancelled") break print(f"Error occurred: {event['error']}") break # ... 处理其他事件 ...

提示:取消时error事件的error字段可能不是字符串而是asyncio.CancelledError对象,示例中isinstance判断正是为此设计(对应 progress_monitor.py 中on_finish()的行为)。

5.4 取消后的行为保证

对照源码,取消(或任何终止)发生后:

  • 取消原因会被记录到日志;
  • do_translate()finally分支会调用pm.on_finish()并执行translation_config.cleanup_temp_files()清理临时文件(high_level.py);
  • 正在进行的翻译任务通过future.cancel()与各阶段raise_if_cancelled()停止;
  • 若取消标记已设置,会补发一个携带CancelledErrorerror事件(progress_monitor.py);
  • async_translate等待finish_event后优雅退出(high_level.py)。

六、错误处理

文档要求与源码行为一致:

  1. 记录日志:异常在do_translate()中被捕获,debug=True时记录完整 traceback(logger.exception),否则仅记录错误摘要(high_level.py);
  2. 上报错误事件pm.translate_error(e)发送{"type": "error", "error": e}
  3. 终止事件流async_translate收到error事件后break,事件流停止(high_level.py);
  4. 清理资源finally中执行on_finish()cleanup_temp_files()

此外do_translate()在进入正式流程前还会做一次元数据校验:若输入 PDF 的 producer 字段含BabelDOCTranslation_generated_by_AI,please_carefully_discern,会直接抛出InputFileGeneratedByBabelDOCError,拒绝翻译已被 BabelDOC 处理过的文件(high_level.py)。

七、底层实现原理

7.1 事件流的跨线程桥梁:AsyncCallback

翻译工作运行在线程池中,而事件循环在另一个线程。两者通过AsyncCallback(babeldoc/asynchronize/init.py)桥接:

  • step_callback():每个进度回调通过loop.call_soon_threadsafe(self.queue.put_nowait, args)将事件安全地投递进asyncio.Queue,并time.sleep(0.01)让出 GIL,保证事件循环有机会消费消息;
  • finished_callback():投递最终事件后置finished = True
  • __aiter__/__anext__:实现异步迭代器语义——只要finished为假或队列非空就持续产出事件,队列清空且finished为真时抛出StopAsyncIteration结束迭代。

这解释了为什么async for event in callback能够一边等待一边消费线程池里的进度更新。

7.2 进度计算的权重模型:ProgressMonitor

ProgressMonitor(babeldoc/progress_monitor.py)将阶段列表按权重归一化(weight / total_weight),overall_progress由已完成阶段的权重百分比累加、加上当前阶段stage.current / stage.total的加权值得到;全部阶段完成时精确返回100。它还支持分片翻译(split_strategy):每个分片通过create_part_monitor()创建子监视器,calculate_current_progress()再按part_index / total_parts折算整体进度,事件中也会附带part_indextotal_parts字段。

7.3 生产环境中的消费范式

仓库自身提供了两个可直接参考的消费端:

  • CLI:babeldoc/main.py 中async for event in async_translate(config)finish时打印str(result)并退出;
  • Executor 服务:babeldoc/tools/executor/babeldoc_adapter.py 的_run_async_translate()asyncio.run(run())包装,将finish事件中的TranslateResult转为返回值、将error事件包装为BabelDocReportedError,其余事件原样透传给调用方(如 IDE 插件的任务面板)。

如果你的应用需要把进度同步到 WebSocket/IPC,直接参照 executor 的做法:收到progress_update时转发overall_progress,收到finish时交付产物路径,收到error时上报错误信息即可。

八、最佳实践清单

  1. 始终按event["type"]分派,不要假设事件顺序与阶段集合固定(阶段可被配置裁剪);
  2. 使用overall_progress驱动全局进度条stage_progress只用于阶段内细节展示;
  3. error事件后必须break,事件流不会自行继续;
  4. 优先用config.cancel_translation()做程序化取消,并兼容error事件中error字段为CancelledError对象的情况;
  5. 取消/异常后无需手动清理cleanup_temp_files()会自动回收临时目录(除非设置了working_dir且非 debug 模式);
  6. 需要阶段明细时开启debug=True,会额外在working_dir中输出各阶段的 IL JSON(如layout_generator.jsonil_translated.json),便于排查排版与翻译问题。

相关源码索引

  • 异步翻译入口与事件定义:babeldoc/format/pdf/high_level.py
  • 阶段流水线与权重表:babeldoc/format/pdf/high_level.py
  • 配置与结果对象:babeldoc/format/pdf/translation_config.py
  • 进度监视器(阶段/权重/分片/取消):babeldoc/progress_monitor.py
  • 跨线程事件桥接:babeldoc/asynchronize/init.py
  • CLI 消费示例:babeldoc/main.py
  • Executor 消费示例:babeldoc/tools/executor/babeldoc_adapter.py

【免费下载链接】BabelDOCYet Another Document Translator项目地址: https://gitcode.com/GitHub_Trending/ba/BabelDOC

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询