Normalize Chinese Part ordinals and support resumable complete-book translation

This commit is contained in:
Kai Tan
2026-10-08 08:32:05 +08:00
parent 498b5d09ce
commit 644e93b874
12 changed files with 186 additions and 20 deletions
+38
View File
@@ -15,6 +15,7 @@ from src.assembly.backfiller import BackfillEngine
from src.assembly.builder import BilingualBuilder
from src.translation.translator_engine import Translator
from src.translation.glossary import BookGlossary, matches
from src.translation.glossary import prepare_glossary
from src.translation.manifest_manager import ManifestManager
from src.translation.llm_client import LLMClient
from src.qa.epub_audit import audit_epub, compare_audits
@@ -26,6 +27,43 @@ def entry(html='<p id="p1">Masayoshi Son <em>built</em> a company.</p>'):
original_text=text, placeholders=mapping)
def test_glossary_model_conflicts_are_recorded_and_scan_resumes(tmp_path):
class Scanner:
model = 'test-model'
def __init__(self, fail=False): self.calls = 0; self.fail = fail
async def raw_chat_completion(self, *args):
self.calls += 1
if self.fail and self.calls == 2: raise RuntimeError('network interrupted')
return json.dumps({'terms': [
{'source': 'Masayoshi Son', 'target': '孙正义', 'aliases': ['Masa']},
{'source': 'Masa', 'target': '马萨', 'aliases': []}]})
entries = [entry(), entry('<p id="p2">Masa continued.</p>')]
path = tmp_path / 'glossary.json'
with pytest.raises(RuntimeError):
asyncio.run(prepare_glossary(entries, Scanner(True), path, chunk_chars=1))
saved = json.loads(path.read_text())
assert saved['next_chunk'] == 1 and not saved['scan_complete']
assert saved['terms'][0]['target'] == '孙正义'
assert saved['conflicts']
resumed = Scanner()
glossary = asyncio.run(prepare_glossary(entries, resumed, path, chunk_chars=1))
assert resumed.calls == 1
assert len(glossary.terms) == 1
assert json.loads(path.read_text())['scan_complete']
asyncio.run(prepare_glossary(entries, resumed, path, chunk_chars=1))
assert resumed.calls == 1
def test_zenmux_credentials_override_unrelated_openai_key(tmp_path, monkeypatch):
from src.common.config import load_global_config
config = tmp_path / 'zenmux.yaml'
config.write_text('llm:\n base_url: https://zenmux.ai/api/v1\n')
monkeypatch.setenv('ZENMUX_API_KEY', 'test-zenmux-key')
monkeypatch.setenv('OPENAI_API_KEY', 'test-other-key')
monkeypatch.delenv('OPENAI_BASE_URL', raising=False)
assert load_global_config(config)['llm']['api_key'] == 'test-zenmux-key'
@pytest.mark.parametrize('html', [
'<p>Hello <em><b>world</b></em>.</p>',
'<p>The angle φ = 10, and φ1φ is literal mathematical text.</p>',