v0.9: parallelize phase4 and add model/search playbooks
This commit is contained in:
+75
-36
@@ -19,6 +19,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import concurrent.futures
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
@@ -141,6 +142,12 @@ def main() -> int:
|
||||
default=None,
|
||||
help="最多润色前 N 个未缓存的块(调试用)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--workers",
|
||||
type=int,
|
||||
default=4,
|
||||
help="并发润色 worker 数(默认 4;设为 1 回退串行)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
load_secrets()
|
||||
@@ -167,50 +174,82 @@ def main() -> int:
|
||||
total_cn = sum(count_chinese_chars(b.content) for b in blocks)
|
||||
print(f"Source: {src_path.relative_to(project_root)}")
|
||||
print(f"Blocks: {len(blocks)} | total Chinese chars: {total_cn:,}")
|
||||
print(f"Model: {args.model} | temperature: {args.temperature}")
|
||||
workers = max(1, args.workers)
|
||||
print(f"Model: {args.model} | temperature: {args.temperature} | workers: {workers}")
|
||||
print()
|
||||
|
||||
start = time.time()
|
||||
translated_this_run = 0
|
||||
todo: list[MarkdownBlock] = []
|
||||
cached_count = 0
|
||||
notes_records: list[dict] = []
|
||||
for b in blocks:
|
||||
chunk_path = chunks_dir / f"{b.order:03d}-{b.anchor}.md"
|
||||
if only_orders is not None and b.order not in only_orders:
|
||||
continue
|
||||
if chunk_path.exists() and not args.force:
|
||||
cached_count += 1
|
||||
print(f" [ok ] #{b.order:03d} {b.short_title} (cached)")
|
||||
continue
|
||||
if args.limit is not None and len(todo) >= args.limit:
|
||||
continue
|
||||
todo.append(b)
|
||||
|
||||
if todo:
|
||||
print(f"To polish this run: {len(todo)} blocks | cached: {cached_count}")
|
||||
print()
|
||||
|
||||
with ZenMuxClient(log_file=log_file) as client:
|
||||
for b in blocks:
|
||||
def run_one(b: MarkdownBlock) -> tuple[MarkdownBlock, str, str, float]:
|
||||
chunk_path = chunks_dir / f"{b.order:03d}-{b.anchor}.md"
|
||||
if only_orders is not None and b.order not in only_orders:
|
||||
continue
|
||||
if chunk_path.exists() and not args.force:
|
||||
print(f" [ok ] #{b.order:03d} {b.short_title} (cached)")
|
||||
continue
|
||||
if args.limit is not None and translated_this_run >= args.limit:
|
||||
continue
|
||||
|
||||
label = f"#{b.order:03d} L{b.level} {count_chinese_chars(b.content):>4}字 {b.short_title}"
|
||||
print(f" [... ] {label} ", end="", flush=True)
|
||||
t0 = time.time()
|
||||
try:
|
||||
polished, notes = polish_block(
|
||||
client,
|
||||
b,
|
||||
model=args.model,
|
||||
system_prompt=system_prompt,
|
||||
temperature=args.temperature,
|
||||
)
|
||||
except (ZenMuxError, RuntimeError) as e:
|
||||
print(f"\n [FAIL] {label}\n {e}")
|
||||
continue
|
||||
elapsed = time.time() - t0
|
||||
|
||||
polished, notes = polish_block(
|
||||
client,
|
||||
b,
|
||||
model=args.model,
|
||||
system_prompt=system_prompt,
|
||||
temperature=args.temperature,
|
||||
)
|
||||
chunk_path.write_text(polished + "\n", encoding="utf-8")
|
||||
cn = count_chinese_chars(polished)
|
||||
before_cn = count_chinese_chars(b.content)
|
||||
delta = cn - before_cn
|
||||
sign = "+" if delta >= 0 else ""
|
||||
translated_this_run += 1
|
||||
if notes:
|
||||
notes_records.append(
|
||||
{"order": b.order, "anchor": b.anchor, "title": b.short_title, "notes": notes}
|
||||
)
|
||||
print(f"\r [done] {label} → {cn}字 ({sign}{delta}, {elapsed:4.1f}s)")
|
||||
return b, polished, notes, time.time() - t0
|
||||
|
||||
if workers == 1:
|
||||
for b in todo:
|
||||
label = f"#{b.order:03d} L{b.level} {count_chinese_chars(b.content):>4}字 {b.short_title}"
|
||||
print(f" [... ] {label} ", end="", flush=True)
|
||||
try:
|
||||
block, polished, notes, elapsed = run_one(b)
|
||||
except (ZenMuxError, RuntimeError) as e:
|
||||
print(f"\n [FAIL] {label}\n {e}")
|
||||
continue
|
||||
cn = count_chinese_chars(polished)
|
||||
before_cn = count_chinese_chars(block.content)
|
||||
delta = cn - before_cn
|
||||
sign = "+" if delta >= 0 else ""
|
||||
if notes:
|
||||
notes_records.append(
|
||||
{"order": block.order, "anchor": block.anchor, "title": block.short_title, "notes": notes}
|
||||
)
|
||||
print(f"\r [done] {label} → {cn}字 ({sign}{delta}, {elapsed:4.1f}s)")
|
||||
else:
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool:
|
||||
futures = {pool.submit(run_one, b): b for b in todo}
|
||||
for fut in concurrent.futures.as_completed(futures):
|
||||
b = futures[fut]
|
||||
label = f"#{b.order:03d} L{b.level} {count_chinese_chars(b.content):>4}字 {b.short_title}"
|
||||
try:
|
||||
block, polished, notes, elapsed = fut.result()
|
||||
except (ZenMuxError, RuntimeError) as e:
|
||||
print(f" [FAIL] {label}\n {e}")
|
||||
continue
|
||||
cn = count_chinese_chars(polished)
|
||||
before_cn = count_chinese_chars(block.content)
|
||||
delta = cn - before_cn
|
||||
sign = "+" if delta >= 0 else ""
|
||||
if notes:
|
||||
notes_records.append(
|
||||
{"order": block.order, "anchor": block.anchor, "title": block.short_title, "notes": notes}
|
||||
)
|
||||
print(f" [done] {label} → {cn}字 ({sign}{delta}, {elapsed:4.1f}s)")
|
||||
|
||||
# 汇总
|
||||
merged: list[str] = []
|
||||
|
||||
Reference in New Issue
Block a user