File size: 9,890 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]
> Questa documentazione potrebbe contenere contenuti generati dall'IA. Sebbene ci impegniamo per l'accuratezza, potrebbero esserci delle imprecisioni. Si prega di segnalare eventuali problemi tramite:
>
> - [GitHub Issues](https://github.com/PDFMathTranslate-next/PDFMathTranslate-next/issues)
> - Contributo della comunità (PR sono benvenuti!)

## Python API: do_translate_async_stream

### Panoramica
- do_translate_async_stream è il punto di ingresso asincrono di basso livello che traduce un singolo PDF e restituisce un flusso di eventi (progresso/errore/fine).
- È adatto per costruire la propria UI o CLI in cui si desidera un progresso in tempo reale e il controllo completo sui risultati.
- Accetta un SettingsModel validato e un percorso del file e restituisce un generatore asincrono di eventi dict.

### Firma
- Import: `from pdf2zh_next.high_level import do_translate_async_stream`
- Chiamata: `async for event in do_translate_async_stream(settings, file): ...`
- Parametri:
  - settings: SettingsModel. Deve essere valido; la funzione chiamerà `settings.validate_settings()`.
  - file: str | pathlib.Path. Il singolo PDF da tradurre. Deve esistere.

NOTE

- `settings.basic.input_files` viene ignorato da questa funzione; viene tradotto solo il `file` specificato.
- Se `settings.basic.debug` è True, la traduzione viene eseguita nel processo principale; altrimenti viene eseguita in un sottoprocesso. Lo schema degli eventi è identico in entrambi i casi.

### Contratto del flusso di eventi
Il generatore asincrono produce eventi di tipo dict simili a JSON con i seguenti tipi:

- Evento di riepilogo fase: `stage_summary` (opzionale, può apparire per primo)
  - Campi
    - `type`: "stage_summary"
    - `stages`: lista di oggetti `{ "name": str, "percent": float }` che descrivono la distribuzione stimata del lavoro
    - `part_index`: può essere 0 per questo evento di riepilogo
    - `total_parts`: numero totale di parti (>= 1)

- Eventi di avanzamento: `progress_start`, `progress_update`, `progress_end`
  - Campi comuni
    - `type`: uno dei precedenti
    - `stage`: nome della fase leggibile dall'uomo (ad es. "Analizza PDF e Crea Rappresentazione Intermedia", "Traduci Paragrafi", "Salva PDF")
    - `stage_progress`: float in [0, 100] che indica l'avanzamento all'interno della fase corrente
    - `overall_progress`: float in [0, 100] che indica l'avanzamento complessivo
    - `part_index`: indice della parte corrente (tipicamente basato su 1 per gli eventi di avanzamento)
    - `total_parts`: numero totale di parti (>= 1). Documenti di grandi dimensioni possono essere suddivisi automaticamente.
    - `stage_current`: passo corrente all'interno della fase
    - `stage_total`: passi totali all'interno della fase

- Evento di completamento: `finish`
  - Campi
    - `type`: "finish"
    - `translate_result`: un **oggetto** che fornisce gli output finali (NOTA: non un dizionario, ma un'istanza di classe)
      - `original_pdf_path`: Percorso del PDF di input
      - `mono_pdf_path`: Percorso del PDF tradotto monolingue (o None)
      - `dual_pdf_path`: Percorso del PDF tradotto bilingue (o None)
      - `no_watermark_mono_pdf_path`: Percorso dell'output monolingue senza filigrana (se prodotto), altrimenti None
      - `no_watermark_dual_pdf_path`: Percorso dell'output bilingue senza filigrana (se prodotto), altrimenti None
      - `auto_extracted_glossary_path`: Percorso del glossario CSV estratto automaticamente (o None)
      - `total_seconds`: secondi trascorsi (float)
      - `peak_memory_usage`: utilizzo approssimativo della memoria di picco durante la traduzione (float; unità dipendenti dall'implementazione)

- Evento di errore: `error`
  - Campi
    - `type`: "error"
    - `error`: messaggio di errore leggibile dall'uomo
    - `error_type`: uno tra `BabeldocError`, `SubprocessError`, `IPCError`, `SubprocessCrashError`, ecc.
    - `details`: dettagli opzionali (ad es. errore originale o traceback)

Comportamento importante:
- Un `stage_summary` opzionale può essere emesso prima che il progresso inizi.
- In caso di determinati fallimenti, il generatore emetterà prima un evento `error` e poi solleverà un'eccezione derivata da `TranslationError`. Dovresti sia controllare la presenza di eventi di errore che essere pronto a catturare le eccezioni.
- Gli eventi `progress_update` possono ripetersi con valori identici; i consumatori dovrebbero eseguire il debounce se necessario.
- Interrompi il consumo del flusso quando ricevi un evento `finish`.

### Esempio di utilizzo minimo (Async)
```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"))
```

### Annullamento
Puoi annullare l'attività consumando il flusso. L'annullamento viene propagato al processo di traduzione sottostante.

```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
```

### Forme di Evento di Esempio
Riepilogo dello stage evento (esempio):
```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
}
```

Progress event (esempio):
```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
}
```

Finish event (esempio):
```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 di errore (esempio):
```json
{
  "type": "error",
  "error": "Babeldoc translation error: <message>",
  "error_type": "BabeldocError",
  "details": "<optional original error or traceback>"
}
```

### Note e migliori pratiche
- Gestire sempre sia gli eventi di errore che le eccezioni dal generatore.
- Interrompere il ciclo su `finish` per evitare lavoro non necessario.
- Assicurarsi che il `file` esista e che `settings.validate_settings()` passi prima della chiamata.
- I documenti di grandi dimensioni potrebbero essere divisi; utilizzare `part_index/total_parts` e `overall_progress` per guidare la tua UI.
- Debounce `progress_update` se la tua UI è sensibile a ripetuti aggiornamenti identici.
- `report_interval` (SettingsModel): controlla solo la frequenza di emissione degli eventi `progress_update`. Non influisce su `stage_summary`, `progress_start`, `progress_end` o `finish`. Il valore predefinito è 0.1s e il minimo consentito è 0.05s. Come da logica del monitor di avanzamento, quando `stage_total <= 3`, gli aggiornamenti non sono limitati da `report_interval`.

<div align="right"> 
<h6><small>Parte del contenuto di questa pagina è stata tradotta da GPT e potrebbe contenere errori.</small></h6>