"""Apply an agent-authored translation manifest for offline, reproducible QA.""" import argparse import asyncio import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from src.common.data_model import BookStructure, ManifestEntry from src.preprocessing.epub_cleaner import EpubCleaner from src.preprocessing.text_extractor import FineGrainedExtractor from src.preprocessing.navigation import prepare_navigation from src.assembly.restoration_engine import RestorationEngine from src.assembly.backfiller import BackfillEngine from src.assembly.builder import BilingualBuilder async def run(args): args.out.mkdir(parents=True,exist_ok=True) structure=BookStructure.load(EpubCleaner(args.source,args.out/'source').clean()) entries=FineGrainedExtractor().extract(structure) prepare_navigation(args.source,structure,entries) by_id={e.entry_id:e for e in entries} provided=[ManifestEntry.model_validate(e) for e in json.loads(args.manifest.read_text())] for sample in provided: current=by_id.get(sample.entry_id) if current is None or current.original_text!=sample.original_text or current.placeholders!=sample.placeholders: raise ValueError(f'Sample/source mismatch: {sample.entry_id}') current.translated_text=sample.translated_text count=await RestorationEngine().restore_entries(entries) if count!=len(provided): raise ValueError('Sample has missing or invalid translations') (args.out/'manifest.json').write_text(json.dumps([e.model_dump() for e in entries],ensure_ascii=False,indent=2)) for mode in ['bilingual','target_only']: copy=structure.model_copy(deep=True) await BackfillEngine().backfill(copy,entries,mode,allow_partial=True) BilingualBuilder(args.out/'source',args.source).build(copy,args.out/f'{mode}-sample.epub') print(f'Explicit partial sample: {count}/{len(entries)} translated units. Outputs: {args.out}') def main(): parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('source',type=Path) parser.add_argument('manifest',type=Path) parser.add_argument('--out',type=Path,default=Path('.work/offline-sample')) asyncio.run(run(parser.parse_args())) if __name__=='__main__': main()