File size: 9,800 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 documentação pode conter conteúdo gerado por IA. Embora nos esforcemos pela precisão, podem ocorrer imprecisões. Por favor, relate quaisquer problemas através de:
>
> - [GitHub Issues](https://github.com/PDFMathTranslate-next/PDFMathTranslate-next/issues)
> - Contribuição da comunidade (PRs são bem-vindos!)

## Python API: do_translate_async_stream

### Visão geral
- do_translate_async_stream é o ponto de entrada assíncrono de baixo nível que traduz um único PDF e produz um fluxo de eventos (progresso/erro/conclusão).
- É adequado para construir sua própria UI ou CLI onde você deseja progresso em tempo real e controle total sobre os resultados.
- Ele aceita um SettingsModel validado e um caminho de arquivo e retorna um gerador assíncrono de eventos de dicionário.

### Assinatura
- Importar: `from pdf2zh_next.high_level import do_translate_async_stream`
- Chamar: `async for event in do_translate_async_stream(settings, file): ...`
- Parâmetros:
  - settings: SettingsModel. Deve ser válido; a função chamará `settings.validate_settings()`.
  - file: str | pathlib.Path. O único PDF a ser traduzido. Deve existir.

Observação:

- `settings.basic.input_files` é ignorado por esta função; apenas o `file` fornecido é traduzido.
- Se `settings.basic.debug` for True, a tradução é executada no processo principal; caso contrário, é executada em um subprocesso. O esquema de eventos é idêntico para ambos.

### Contrato de Fluxo de Eventos
O gerador assíncrono produz eventos de dicionário semelhantes a JSON com os seguintes tipos:

- Evento de resumo de estágio: `stage_summary` (opcional, pode aparecer primeiro)
  - Campos
    - `type`: "stage_summary"
    - `stages`: lista de objetos `{ "name": str, "percent": float }` descrevendo a distribuição estimada do trabalho
    - `part_index`: pode ser 0 para este evento de resumo
    - `total_parts`: número total de partes (>= 1)

- Eventos de progresso: `progress_start`, `progress_update`, `progress_end`
  - Campos comuns
    - `type`: um dos acima
    - `stage`: nome do estágio legível por humanos (por exemplo, "Analisar PDF e Criar Representação Intermediária", "Traduzir Parágrafos", "Salvar PDF")
    - `stage_progress`: float em [0, 100] indicando o progresso dentro do estágio atual
    - `overall_progress`: float em [0, 100] indicando o progresso geral
    - `part_index`: índice da parte atual (normalmente baseado em 1 para eventos de progresso)
    - `total_parts`: número total de partes (>= 1). Documentos grandes podem ser divididos automaticamente.
    - `stage_current`: etapa atual dentro do estágio
    - `stage_total`: total de etapas dentro do estágio

- Evento de conclusão: `finish`
  - Campos
    - `type`: "finish"
    - `translate_result`: um **objeto** fornecendo as saídas finais (NOTA: não um dicionário, mas uma instância de classe)
      - `original_pdf_path`: Caminho para o PDF de entrada
      - `mono_pdf_path`: Caminho para o PDF traduzido monolíngue (ou None)
      - `dual_pdf_path`: Caminho para o PDF traduzido bilíngue (ou None)
      - `no_watermark_mono_pdf_path`: Caminho para a saída monolíngue sem marca d'água (se produzida), caso contrário None
      - `no_watermark_dual_pdf_path`: Caminho para a saída bilíngue sem marca d'água (se produzida), caso contrário None
      - `auto_extracted_glossary_path`: Caminho para o CSV do glossário extraído automaticamente (ou None)
      - `total_seconds`: segundos decorridos (float)
      - `peak_memory_usage`: uso aproximado de memória de pico durante a tradução (float; unidades dependentes da implementação)

- Evento de erro: `error`
  - Campos
    - `type`: "error"
    - `error`: mensagem de erro legível por humanos
    - `error_type`: um de `BabeldocError`, `SubprocessError`, `IPCError`, `SubprocessCrashError`, etc.
    - `details`: detalhes opcionais (por exemplo, erro original ou traceback)

Comportamento importante:
- Um `stage_summary` opcional pode ser emitido antes do progresso começar.
- Em certas falhas, o gerador primeiro produzirá um evento `error` e, em seguida, levantará uma exceção derivada de `TranslationError`. Você deve verificar tanto os eventos de erro quanto estar preparado para capturar exceções.
- Eventos `progress_update` podem se repetir com valores idênticos; os consumidores devem fazer _debounce_ se necessário.
- Pare de consumir o fluxo quando receber um evento `finish`.

### Exemplo de Uso Mínimo (Assí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"))
```

### Cancelamento
Você pode cancelar a tarefa consumindo o fluxo. O cancelamento é propagado para o processo de tradução subjacente.

```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 Exemplo
Resumo do estágio (exemplo):
```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 (exemplo):
```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 (exemplo):
```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 erro (exemplo):
```json
{
  "type": "error",
  "error": "Babeldoc translation error: <message>",
  "error_type": "BabeldocError",
  "details": "<optional original error or traceback>"
}
```

### Notas e Melhores Práticas
- Sempre trate tanto os eventos de erro quanto as exceções do gerador.
- Interrompa o loop em `finish` para evitar trabalho desnecessário.
- Certifique-se de que o `file` existe e `settings.validate_settings()` passa antes de chamar.
- Documentos grandes podem ser divididos; use `part_index/total_parts` e `overall_progress` para orientar sua interface do usuário.
- Debounce `progress_update` se sua interface do usuário for sensível a atualizações repetidas e idênticas.
- `report_interval` (SettingsModel): controla apenas a taxa de emissão dos eventos `progress_update`. Não afeta `stage_summary`, `progress_start`, `progress_end` ou `finish`. O padrão é 0,1s e o mínimo permitido é 0,05s. Conforme a lógica do monitor de progresso, quando `stage_total <= 3`, as atualizações não são limitadas por `report_interval`.

<div align="right"> 
<h6><small>Parte do conteúdo desta página foi traduzida pelo GPT e pode conter erros.</small></h6>