File size: 9,857 Bytes
179eccc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
> [!NOTE]
> Esta documentación puede contener contenido generado por IA. Aunque nos esforzamos por la precisión, pueden existir inexactitudes. Por favor, reporte cualquier problema a través de:
>
> - [GitHub Issues](https://github.com/PDFMathTranslate-next/PDFMathTranslate-next/issues)
> - Contribución de la comunidad (¡PRs son bienvenidas!)

## Python API: do_translate_async_stream

### Resumen
- do_translate_async_stream es el punto de entrada asíncrono de bajo nivel que traduce un único PDF y produce un flujo de eventos (progreso/error/finalización).
- Es adecuado para construir tu propia interfaz de usuario o CLI donde desees progreso en tiempo real y control total sobre los resultados.
- Acepta un SettingsModel validado y una ruta de archivo, y devuelve un generador asíncrono de eventos dict.

### Firma
- Importar: `from pdf2zh_next.high_level import do_translate_async_stream`
- Llamar: `async for event in do_translate_async_stream(settings, file): ...`
- Parámetros:
  - settings: SettingsModel. Debe ser válido; la función llamará a `settings.validate_settings()`.
  - file: str | pathlib.Path. El único PDF a traducir. Debe existir.

Nota:

- `settings.basic.input_files` es ignorado por esta función; solo se traduce el `file` proporcionado.
- Si `settings.basic.debug` es True, la traducción se ejecuta en el proceso principal; de lo contrario, se ejecuta en un subproceso. El esquema de eventos es idéntico en ambos casos.

### Contrato de flujo de eventos
El generador asíncrono produce eventos de tipo dict similares a JSON con los siguientes tipos:

- Evento de resumen de etapa: `stage_summary` (opcional, puede aparecer primero)
  - Campos
    - `type`: "stage_summary"
    - `stages`: lista de objetos `{ "name": str, "percent": float }` que describe la distribución estimada del trabajo
    - `part_index`: puede ser 0 para este evento de resumen
    - `total_parts`: número total de partes (>= 1)

- Eventos de progreso: `progress_start`, `progress_update`, `progress_end`
  - Campos comunes
    - `type`: uno de los anteriores
    - `stage`: nombre de la etapa legible por humanos (por ejemplo, "Analizar PDF y Crear Representación Intermedia", "Traducir Párrafos", "Guardar PDF")
    - `stage_progress`: flotante en [0, 100] que indica el progreso dentro de la etapa actual
    - `overall_progress`: flotante en [0, 100] que indica el progreso general
    - `part_index`: índice de la parte actual (típicamente basado en 1 para eventos de progreso)
    - `total_parts`: número total de partes (>= 1). Los documentos grandes pueden dividirse automáticamente.
    - `stage_current`: paso actual dentro de la etapa
    - `stage_total`: pasos totales dentro de la etapa

- Evento de finalización: `finish`
  - Campos
    - `type`: "finish"
    - `translate_result`: un **objeto** que proporciona las salidas finales (NOTA: no es un diccionario, sino una instancia de clase)
      - `original_pdf_path`: Ruta al PDF de entrada
      - `mono_pdf_path`: Ruta al PDF traducido monolingüe (o None)
      - `dual_pdf_path`: Ruta al PDF traducido bilingüe (o None)
      - `no_watermark_mono_pdf_path`: Ruta a la salida monolingüe sin marca de agua (si se produjo), de lo contrario None
      - `no_watermark_dual_pdf_path`: Ruta a la salida bilingüe sin marca de agua (si se produjo), de lo contrario None
      - `auto_extracted_glossary_path`: Ruta al glosario CSV extraído automáticamente (o None)
      - `total_seconds`: segundos transcurridos (flotante)
      - `peak_memory_usage`: uso máximo aproximado de memoria durante la traducción (flotante; unidades dependientes de la implementación)

- Evento de error: `error`
  - Campos
    - `type`: "error"
    - `error`: mensaje de error legible por humanos
    - `error_type`: uno de `BabeldocError`, `SubprocessError`, `IPCError`, `SubprocessCrashError`, etc.
    - `details`: detalles opcionales (por ejemplo, error original o traza de ejecución)

Comportamiento importante:
- Se puede emitir un `stage_summary` opcional antes de que comience el progreso.
- En ciertos fallos, el generador primero producirá un evento `error` y luego lanzará una excepción derivada de `TranslationError`. Debes verificar tanto los eventos de error como estar preparado para capturar excepciones.
- Los eventos `progress_update` pueden repetirse con valores idénticos; los consumidores deben aplicar debounce si es necesario.
- Deja de consumir el flujo cuando recibas un evento `finish`.

### Ejemplo de uso mínimo (Asíncrono)
```python
import asyncio
from pathlib import Path
from pdf2zh_next.high_level import do_translate_async_stream

# Assume you already have a valid SettingsModel instance named `settings`
# and a PDF file path

async def translate_one(settings, pdf_path: str | Path):
    try:
        async for event in do_translate_async_stream(settings, pdf_path):
            etype = event.get("type")

            if etype == "stage_summary":
                # Optional pre-flight summary of stages
                stages = event.get("stages", [])
                print("Stage summary:", ", ".join(f"{s['name']}:{s['percent']:.2f}" for s in stages))

            elif etype in {"progress_start", "progress_update", "progress_end"}:
                stage = event.get("stage")
                stage_prog = event.get("stage_progress")  # 0..100
                overall = event.get("overall_progress")  # 0..100
                part_i = event.get("part_index")
                part_n = event.get("total_parts")
                print(f"[{etype}] {stage} | stage {stage_prog:.1f}% | overall {overall:.1f}% (part {part_i}/{part_n})")

            elif etype == "error":
                # You will also get a raised exception after this yield
                print("[error]", event.get("error"), event.get("error_type"))

            elif etype == "finish":
                result = event["translate_result"]
                print("Done in", getattr(result, "total_seconds", None), "s")
                print("Mono:", getattr(result, "mono_pdf_path", None))
                print("Dual:", getattr(result, "dual_pdf_path", None))
                print("No-watermark Mono:", getattr(result, "no_watermark_mono_pdf_path", None))
                print("No-watermark Dual:", getattr(result, "no_watermark_dual_pdf_path", None))
                print("Glossary:", getattr(result, "auto_extracted_glossary_path", None))
                print("Peak memory:", getattr(result, "peak_memory_usage", None))
                break

    except Exception as exc:
        # Catch exceptions raised by the stream after an error event
        print("Translation failed:", exc)

# asyncio.run(translate_one(settings, "/path/to/file.pdf"))
```

### Cancelación
Puedes cancelar la tarea consumiendo el flujo. La cancelación se propaga al proceso de traducción subyacente.

```python
import asyncio
from pdf2zh_next.high_level import do_translate_async_stream

async def cancellable(settings, pdf):
    task = asyncio.create_task(_consume(settings, pdf))
    await asyncio.sleep(1.0)  # let it start
    task.cancel()
    try:
        await task
    except asyncio.CancelledError:
        print("Cancelled")

async def _consume(settings, pdf):
    async for event in do_translate_async_stream(settings, pdf):
        if event["type"] == "finish":
            break
```

### Formas de eventos de ejemplo
Evento de resumen de etapa (ejemplo):
```json
{
  "type": "stage_summary",
  "stages": [
    {"name": "Parse PDF and Create Intermediate Representation", "percent": 0.1086},
    {"name": "DetectScannedFile", "percent": 0.0188},
    {"name": "Parse Page Layout", "percent": 0.1079}
    // ... more stages ...
  ],
  "part_index": 0,
  "total_parts": 1
}
```

Evento de progreso (ejemplo):
```json
{
  "type": "progress_update",
  "stage": "Translate Paragraphs",
  "stage_progress": 2.04,
  "stage_current": 1,
  "stage_total": 49,
  "overall_progress": 53.44,
  "part_index": 1,
  "total_parts": 1
}
```

Evento de finalización (ejemplo):
```json
{
  "type": "finish",
  "translate_result": {
    "original_pdf_path": "pdf2zh_files/<session>/table.pdf",
    "mono_pdf_path": "pdf2zh_files/<session>/table.zh-CN.mono.pdf",
    "dual_pdf_path": "pdf2zh_files/<session>/table.zh-CN.dual.pdf",
    "no_watermark_mono_pdf_path": "pdf2zh_files/<session>/table.no_watermark.zh-CN.mono.pdf",
    "no_watermark_dual_pdf_path": "pdf2zh_files/<session>/table.no_watermark.zh-CN.dual.pdf",
    "auto_extracted_glossary_path": "pdf2zh_files/<session>/table.zh-CN.glossary.csv",
    "total_seconds": 42.83,
    "peak_memory_usage": 4651.55
  }
}
```

Evento de error (ejemplo):
```json
{
  "type": "error",
  "error": "Babeldoc translation error: <message>",
  "error_type": "BabeldocError",
  "details": "<optional original error or traceback>"
}
```

### Notas y mejores prácticas
-   Maneja siempre tanto los eventos de error como las excepciones del generador.
-   Rompe el bucle en `finish` para evitar trabajo innecesario.
-   Asegúrate de que el `file` exista y que `settings.validate_settings()` pase antes de llamar.
-   Los documentos grandes pueden dividirse; usa `part_index/total_parts` y `overall_progress` para impulsar tu interfaz de usuario.
-   Debounce `progress_update` si tu interfaz de usuario es sensible a actualizaciones repetidas e idénticas.
-   `report_interval` (SettingsModel): controla solo la tasa de emisión de eventos `progress_update`. No afecta a `stage_summary`, `progress_start`, `progress_end` o `finish`. El valor predeterminado es 0.1s y el mínimo permitido es 0.05s. Según la lógica del monitor de progreso, cuando `stage_total <= 3`, las actualizaciones no se limitan por `report_interval`.

<div align="right"> 
<h6><small>Parte del contenido de esta página ha sido traducido por GPT y puede contener errores.</small></h6>