epub-translator 0.1.4__py3-none-any.whl → 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epub_translator/xml_translator/submitter.py +28 -10
- {epub_translator-0.1.4.dist-info → epub_translator-0.1.5.dist-info}/METADATA +30 -7
- {epub_translator-0.1.4.dist-info → epub_translator-0.1.5.dist-info}/RECORD +5 -5
- {epub_translator-0.1.4.dist-info → epub_translator-0.1.5.dist-info}/LICENSE +0 -0
- {epub_translator-0.1.4.dist-info → epub_translator-0.1.5.dist-info}/WHEEL +0 -0
|
@@ -122,11 +122,17 @@ class _Submitter:
|
|
|
122
122
|
last_tail_element = child_element
|
|
123
123
|
|
|
124
124
|
for text_segments, child_node in node.items:
|
|
125
|
-
|
|
125
|
+
anchor_element = _find_anchor_in_parent(node.raw_element, child_node.raw_element)
|
|
126
|
+
if anchor_element is None:
|
|
127
|
+
# 防御性编程:理论上 anchor_element 不应该为 None,
|
|
128
|
+
# 因为 _nest_nodes 已经通过 _check_includes 验证了包含关系。
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
tail_element = tail_elements.get(id(anchor_element), None)
|
|
126
132
|
items_preserved_elements: list[Element] = []
|
|
127
133
|
|
|
128
134
|
if self._action == SubmitKind.REPLACE:
|
|
129
|
-
end_index = index_of_parent(node.raw_element,
|
|
135
|
+
end_index = index_of_parent(node.raw_element, anchor_element)
|
|
130
136
|
items_preserved_elements = self._remove_elements_after_tail(
|
|
131
137
|
node_element=node.raw_element,
|
|
132
138
|
tail_element=tail_element,
|
|
@@ -137,11 +143,11 @@ class _Submitter:
|
|
|
137
143
|
node_element=node.raw_element,
|
|
138
144
|
text_segments=text_segments,
|
|
139
145
|
tail_element=tail_element,
|
|
146
|
+
anchor_element=anchor_element,
|
|
140
147
|
append_to_end=False,
|
|
141
|
-
ref_element=child_node.raw_element,
|
|
142
148
|
)
|
|
143
149
|
if items_preserved_elements:
|
|
144
|
-
insert_position = index_of_parent(node.raw_element,
|
|
150
|
+
insert_position = index_of_parent(node.raw_element, anchor_element)
|
|
145
151
|
for i, elem in enumerate(items_preserved_elements):
|
|
146
152
|
node.raw_element.insert(insert_position + i, elem)
|
|
147
153
|
|
|
@@ -166,7 +172,7 @@ class _Submitter:
|
|
|
166
172
|
node_element=node.raw_element,
|
|
167
173
|
text_segments=node.tail_text_segments,
|
|
168
174
|
tail_element=last_tail_element,
|
|
169
|
-
|
|
175
|
+
anchor_element=None,
|
|
170
176
|
append_to_end=True,
|
|
171
177
|
)
|
|
172
178
|
if tail_preserved_elements:
|
|
@@ -208,7 +214,7 @@ class _Submitter:
|
|
|
208
214
|
node_element: Element,
|
|
209
215
|
text_segments: list[TextSegment],
|
|
210
216
|
tail_element: Element | None,
|
|
211
|
-
|
|
217
|
+
anchor_element: Element | None,
|
|
212
218
|
append_to_end: bool,
|
|
213
219
|
) -> None:
|
|
214
220
|
combined = self._combine_text_segments(text_segments)
|
|
@@ -225,14 +231,14 @@ class _Submitter:
|
|
|
225
231
|
append_text=combined.text,
|
|
226
232
|
will_inject_space=will_inject_space,
|
|
227
233
|
)
|
|
228
|
-
elif
|
|
234
|
+
elif anchor_element is None:
|
|
229
235
|
node_element.text = self._append_text_in_element(
|
|
230
236
|
origin_text=node_element.text,
|
|
231
237
|
append_text=combined.text,
|
|
232
238
|
will_inject_space=will_inject_space,
|
|
233
239
|
)
|
|
234
240
|
else:
|
|
235
|
-
ref_index = index_of_parent(node_element,
|
|
241
|
+
ref_index = index_of_parent(node_element, anchor_element)
|
|
236
242
|
if ref_index > 0:
|
|
237
243
|
# 添加到前一个元素的 tail
|
|
238
244
|
prev_element = node_element[ref_index - 1]
|
|
@@ -253,10 +259,10 @@ class _Submitter:
|
|
|
253
259
|
insert_position = index_of_parent(node_element, tail_element) + 1
|
|
254
260
|
elif append_to_end:
|
|
255
261
|
insert_position = len(node_element)
|
|
256
|
-
elif
|
|
262
|
+
elif anchor_element is not None:
|
|
257
263
|
# 使用 ref_element 来定位插入位置
|
|
258
264
|
# 如果文本被添加到前一个元素的 tail,则在前一个元素之后插入
|
|
259
|
-
ref_index = index_of_parent(node_element,
|
|
265
|
+
ref_index = index_of_parent(node_element, anchor_element)
|
|
260
266
|
if ref_index > 0:
|
|
261
267
|
# 在前一个元素之后插入
|
|
262
268
|
insert_position = ref_index
|
|
@@ -346,6 +352,18 @@ def _nest_nodes(mappings: list[InlineSegmentMapping]) -> Generator[_Node, None,
|
|
|
346
352
|
yield child_node
|
|
347
353
|
|
|
348
354
|
|
|
355
|
+
def _find_anchor_in_parent(parent: Element, descendant: Element) -> Element | None:
|
|
356
|
+
for child in parent:
|
|
357
|
+
if child is descendant:
|
|
358
|
+
return descendant
|
|
359
|
+
|
|
360
|
+
for child in parent:
|
|
361
|
+
if _check_includes(child, descendant):
|
|
362
|
+
return child
|
|
363
|
+
|
|
364
|
+
return None
|
|
365
|
+
|
|
366
|
+
|
|
349
367
|
def _fold_top_of_stack(stack: list[_Node]):
|
|
350
368
|
child_node = stack.pop()
|
|
351
369
|
if not stack:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: epub-translator
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Translate the epub book using LLM. The translated book will retain the original text and list the translated text side by side with the original text.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: epub,llm,translation,translator
|
|
@@ -238,16 +238,17 @@ translate(
|
|
|
238
238
|
|
|
239
239
|
### Error Handling with `on_fill_failed`
|
|
240
240
|
|
|
241
|
-
Monitor
|
|
241
|
+
Monitor translation errors using the `on_fill_failed` callback. The system automatically retries failed translations up to `max_retries` times (default: 5). Most errors are recovered during retries and don't affect the final output.
|
|
242
242
|
|
|
243
243
|
```python
|
|
244
244
|
from epub_translator import FillFailedEvent
|
|
245
245
|
|
|
246
246
|
def handle_fill_error(event: FillFailedEvent):
|
|
247
|
-
|
|
248
|
-
print(f" {event.error_message}")
|
|
247
|
+
# Only log critical errors that will affect the final EPUB
|
|
249
248
|
if event.over_maximum_retries:
|
|
250
|
-
print("
|
|
249
|
+
print(f"Critical error after {event.retried_count} attempts:")
|
|
250
|
+
print(f" {event.error_message}")
|
|
251
|
+
print(" This error will be present in the final EPUB file!")
|
|
251
252
|
|
|
252
253
|
translate(
|
|
253
254
|
source_path="source.epub",
|
|
@@ -259,10 +260,32 @@ translate(
|
|
|
259
260
|
)
|
|
260
261
|
```
|
|
261
262
|
|
|
263
|
+
**Understanding Error Severity:**
|
|
264
|
+
|
|
262
265
|
The `FillFailedEvent` contains:
|
|
263
266
|
- `error_message: str` - Description of the error
|
|
264
|
-
- `retried_count: int` - Current retry attempt number
|
|
265
|
-
- `over_maximum_retries: bool` - Whether
|
|
267
|
+
- `retried_count: int` - Current retry attempt number (1 to max_retries)
|
|
268
|
+
- `over_maximum_retries: bool` - Whether the error is critical
|
|
269
|
+
|
|
270
|
+
**Error Categories:**
|
|
271
|
+
|
|
272
|
+
- **Recoverable errors** (`over_maximum_retries=False`): Errors during retry attempts. The system will continue retrying and may resolve these automatically. Safe to ignore in most cases.
|
|
273
|
+
|
|
274
|
+
- **Critical errors** (`over_maximum_retries=True`): Errors that persist after all retry attempts. These will appear in the final EPUB file and should be investigated.
|
|
275
|
+
|
|
276
|
+
**Advanced Usage:**
|
|
277
|
+
|
|
278
|
+
For verbose logging during translation debugging:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
def handle_fill_error(event: FillFailedEvent):
|
|
282
|
+
if event.over_maximum_retries:
|
|
283
|
+
# Critical: affects final output
|
|
284
|
+
print(f"❌ CRITICAL: {event.error_message}")
|
|
285
|
+
else:
|
|
286
|
+
# Informational: system is retrying
|
|
287
|
+
print(f"⚠️ Retry {event.retried_count}: {event.error_message}")
|
|
288
|
+
```
|
|
266
289
|
|
|
267
290
|
### Dual-LLM Architecture
|
|
268
291
|
|
|
@@ -59,10 +59,10 @@ epub_translator/xml_translator/callbacks.py,sha256=IoZrsaivd2W76cHFupwv6auVxgEWH
|
|
|
59
59
|
epub_translator/xml_translator/common.py,sha256=hSPptgPp7j6dm47imELB5DgmEbzTEyJD6WEeELOOc50,38
|
|
60
60
|
epub_translator/xml_translator/hill_climbing.py,sha256=1jvilOkTLzwljJA4Nrel8yU2XGvOXpueUJTK7RAp-XY,4272
|
|
61
61
|
epub_translator/xml_translator/stream_mapper.py,sha256=tbMc2vyPUn9zEkJZ7-OVYuKaYyn2pPPwjcAdQ8HLzNs,10179
|
|
62
|
-
epub_translator/xml_translator/submitter.py,sha256=
|
|
62
|
+
epub_translator/xml_translator/submitter.py,sha256=6PGQTnEcOgL3zseDpSzDmU5d9Eg3eO5OfPIGmQp2DVY,14155
|
|
63
63
|
epub_translator/xml_translator/translator.py,sha256=eIvniqKtNoqFFvfvxK4oA-W02y5ZTpmPQ8wFAJlvOUU,9752
|
|
64
64
|
epub_translator/xml_translator/validation.py,sha256=-OKlSZuD__sjAiEpGAO93YQme4ZDSPmoPjRsAMOCEjc,16668
|
|
65
|
-
epub_translator-0.1.
|
|
66
|
-
epub_translator-0.1.
|
|
67
|
-
epub_translator-0.1.
|
|
68
|
-
epub_translator-0.1.
|
|
65
|
+
epub_translator-0.1.5.dist-info/LICENSE,sha256=5RF32sL3LtMOJIErdDKp1ZEYPGXS8WPpsiSz_jMBnGI,1066
|
|
66
|
+
epub_translator-0.1.5.dist-info/METADATA,sha256=IT5MBdl68pICDYmk5tn3CwvdnZ5QxlVoaSzw-VhKf3c,14603
|
|
67
|
+
epub_translator-0.1.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
68
|
+
epub_translator-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|