# CLI guide [简体中文](zh/cli.md) · [README](../README.md) For the React/Vite + FastAPI stack, see [Web deployment](web.md). ## Installation and first run Running from source requires Python 3.10+ and [uv](https://docs.astral.sh/uv/). ```bash uv sync export DEEPSEEK_API_KEY=sk-... uv run wenyi --version uv run wenyi translate book.epub ``` From the repository root, `uv sync` installs the local CLI (`wenyi-cli`) and translation engine (`wenyi-core`). The command is `wenyi`; `python -m wenyi_cli` is the module entry point. To develop the complete Python workspace, including the Web API, use `uv sync --locked --all-packages --group dev`. When upgrading from a checkout that used `trans-novel`, run `uv sync --locked` to refresh the installed command, then use `uv run wenyi ...`. The old command is no longer installed. The displayed version is generated from the repository's Git tags. Tagged builds show the release version; development builds include their commit distance and hash. CLI, Core and API packages use this same version source; OpenAPI reads the installed API package version. The virtual workspace root and private frontend packages do not maintain separate release versions. Whenever the program starts, it checks for `config.yaml` in the current directory and creates a documented default file when it is missing. Review the model settings before starting a real translation. ## Inspect model routing ```bash uv run wenyi models list uv run wenyi models explain --operation review.fix uv run wenyi models check --for translate ``` These commands preview routes and check credentials locally without requests. Keep the three default tiers or select models independently through `llm.routes`. See [configuration](configuration.md#models-and-operation-routing) for explicit config/usage conversion and budgets. ## Multilingual translation (experimental) Run `uv run wenyi languages` to list built-in languages. Use this fragment in your configuration, retaining your existing model settings, for direct Chinese-to-English translation: ```yaml language: source: zh target: en ``` ```bash uv run wenyi --config config.yaml translate book.epub --bilingual ``` Outputs are `output/book.en.epub` and `output/book.en-bi.epub`. For Japanese-to-English use `source: ja`, `target: en`; for English-to-Japanese use `source: en`, `target: ja`. A reverse run takes a file in the corresponding source language; each run selects one direction. Explicit `--out` names and their `-bi` derivatives retain their existing behavior. The `.zh.*` examples below describe the default Simplified Chinese target. All book targets use `state//targets//`; subtitles use `state/srt//targets//`. Resume and standalone stages use the same target configuration; switching back does not retranslate completed work. The EPUB about page is Chinese for Simplified Chinese targets and otherwise temporarily falls back to an English page tagged as English; disable it with `about_page: false`. The CLI uses English for help, progress, tables, and errors. Translation and generated descriptive metadata follow `language.target`; original names in `source` and `aliases` remain available for matching. Existing analysis and glossary notes are reused when resuming and are not automatically translated by this update. This version does not support RTL targets such as Arabic or Hebrew. PDF fonts and external bridge language capabilities require separate validation. Real-model long-form before/after quality evaluation remains outstanding. ## Windows CLI release filenames include the Git-tag version, component, platform and architecture. The examples below use tag `v1.2.3`; replace `1.2.3` with the version you download. Desktop installers are separate `wenyi-desktop-*` assets; see [Desktop](desktop.md). Windows releases provide `wenyi-cli-1.2.3-windows-x64.zip`. Verify the archive against `wenyi-cli-SHA256SUMS.txt`, then extract `wenyi.exe` into a directory before running it. When using a packaged `wenyi.exe`, set the API key in PowerShell: ```powershell # Current PowerShell session only $env:DEEPSEEK_API_KEY = "sk-..." .\wenyi.exe translate .\book.epub ``` To save the environment variable permanently, run the following command and then open a new PowerShell window: ```powershell setx DEEPSEEK_API_KEY "sk-..." ``` You may also set `language.source` to a known ISO language code to avoid an additional model call for language detection. ## Linux Releases provide `wenyi-cli-1.2.3-linux-x64.zip` and `wenyi-cli-1.2.3-linux-arm64.zip`. Download the archive matching your processor, verify it against `wenyi-cli-SHA256SUMS.txt`, and run: ```bash unzip wenyi-cli-1.2.3-linux-arm64.zip # use linux-x64 on x64 systems chmod +x wenyi export DEEPSEEK_API_KEY=sk-... ./wenyi translate book.epub ``` ## macOS Releases provide separate terminal executables for Apple Silicon (`wenyi-cli-1.2.3-macos-arm64.zip`) and Intel (`wenyi-cli-1.2.3-macos-x64.zip`) Macs. Download the archive matching your processor, verify it against `wenyi-cli-SHA256SUMS.txt`, and run: ```bash unzip wenyi-cli-1.2.3-macos-arm64.zip # use macos-x64 on Intel Macs chmod +x wenyi export DEEPSEEK_API_KEY=sk-... ./wenyi translate book.epub ``` These command-line executables are ad-hoc signed by PyInstaller but are not notarized with an Apple Developer certificate. macOS may quarantine a downloaded build; after verifying the checksum, approve it in **System Settings → Privacy & Security** if prompted. ## Input and output - Input formats: EPUB, FB2, TXT, Markdown, HTML, PDF, DOCX, and SRT. - For Markdown input, headings starting with `#` through `######`, followed by whitespace and title text, start new chapters. Their original levels are recorded during import; plain-text contents lists with page numbers remain body text. - Markdown heading levels are preserved in EPUB body headings, the generated EPUB navigation tree, and DOCX outline styles. Navigation nests each heading under the nearest preceding heading with a smaller level number; skipped levels do not create synthetic entries. Existing state with correct chapter divisions and stored heading levels only needs a fresh export, not retranslation. - Default book output: a monolingual `.zh.epub` under the source file's `output/` directory (`.docx` inputs default to `.zh.docx`, and BabelDOC PDF state defaults to `.zh.pdf`). The bilingual `*.zh-bi.*` edition is optional. - `--format epub|txt|html|markdown|pdf|docx`: export the selected format for book inputs. When omitted, BabelDOC PDF state → `pdf`, `.docx` → `docx`, and other books (including MinerU PDF state) → `epub`. An explicit format always takes precedence; PDF defaults follow the saved backend, even if the current `pdf_backend` setting has changed. This flag does not apply to SRT. - For EPUB input, Wenyi attempts to write translated text back into the original XHTML templates while preserving styles, images, the table of contents, and anchors. - The bilingual edition displays the translation and source text together. The source is visually subdued by default; set `output.bilingual_preserve_source_style: true` to inherit the book's normal text style. Their order is controlled by `output.bilingual_order`. - Untranslated EPUB paragraphs appear once as the original text, retaining ruby readings and links. This also applies to Web exports and partially translated chapters; bilingual ordering and source styling do not add a second copy. - EPUB output includes an “About this translation” page by default. Set `output.about_page: false` to disable it. - Book runtime data is stored under `state//targets//`, including chapter intermediates, the SQLite glossary, usage data, and reports. Subtitle runs use a separate tree under `state/srt/` (see [SRT subtitles](#srt-subtitles)). ### Experimental PDF support PDF input and PDF output are both experimental. #### PDF input Default backend is MinerU. For layout-preserving export, use the external **BabelDOC bridge** (AGPL, separate repo/process, HTTP only): 1. Install/start `wenyi-babeldoc-bridge` (default `http://127.0.0.1:8765`) 2. In `config.yaml`: ```yaml pipeline: pdf_backend: babeldoc babeldoc_bridge_url: http://127.0.0.1:8765 # babeldoc_pages: "15" # optional, 1-based ``` 3. `translate book.pdf` automatically exports PDF through bridge `/fillback`. Later, `assemble book.pdf` also defaults to PDF for that saved BabelDOC state; `--format pdf` is optional. Use an explicit `--format` to select another format. The fillback PDF omits BabelDOC layout overlay boxes and role labels such as ``plain text`` / ``title`` by default. The bridge freezes the post-extraction IL as a durable session snapshot. It can restart and lazily restore the same session without rerunning layout analysis, provided its session directory and exact Python/BabelDOC versions are retained. Set `WENYI_BABELDOC_STATE_DIR` to a persistent directory for long translations; the default system temporary directory may be cleaned after a reboot. Wenyi never imports babeldoc. Chapters are split from the PDF outline (bookmarks); segments keep `meta.babeldoc_id` for fillback. Books without outlines fall back to one chapter. BabelDOC is intended for PDFs with an extractable text layer. Before contacting the bridge, Wenyi checks the selected pages and stops with a suggestion to use the default MinerU backend or run OCR first when it finds only scanned images and no text layer. The first MinerU PDF import requires `MINERU_API_KEY`: ```bash export MINERU_API_KEY=... uv run wenyi translate book.pdf ``` MinerU's converted HTML is saved at `state//targets//source//converted.html`. The content-addressed directory prevents an interrupted run from reusing another PDF's conversion. Later runs reuse this file, and you may correct it manually before resuming. #### PDF output WeasyPrint is the default PDF engine. Install its optional dependency and omit `--pdf-engine`: ```bash uv sync --extra pdf-output uv run wenyi assemble book.html --format pdf ``` For a lightweight cross-platform engine without system rendering libraries, use `fpdf2`: ```bash uv sync --extra pdf-output-lite uv run wenyi assemble book.html --format pdf --pdf-engine fpdf2 ``` `fpdf2` supports basic layout and images, but only a limited HTML/CSS subset. Images mixed with text are placed as separate blocks. It uses a discoverable CJK system font; if none is found, set `TRANS_NOVEL_PDF_FONT` to a TTF, OTF, or TTC font file. This option also works on Windows. ## DOCX (Word) `translate book.docx` uses the full book Orchestrator (glossary, polish, review, resume under `state//targets//`). **Structure** - Paragraphs and heading styles (`Heading 1`–`9` / outline levels); level-1 headings start chapters. - Simple tables are rebuilt cell-by-cell (no merged cells / nested tables in v1). - Word automatic lists (`numPr`) become List Number / List Bullet groups (restart per source list id). - Documents without the optional numbering part are also accepted; ordinary paragraphs do not require list definitions. - Contents-style lines that already include a visible prefix such as `1. Title` are **not** auto-numbered again (avoids double numbering). **Styles** - Keeps bold / italic / underline / color / size, paragraph alignment, and shading. - Uniform runs: apply on export with **no** extra model call. - Mixed runs: after translate, each meaningful span is positioned alone (EPUB-annotation-style markers); bold/color and other attrs are **inherited from the source item**. Failed spans fall back proportionally without discarding the whole paragraph. - Font/size-only run splits are ignored for alignment (noise). - Translated Chinese uses **Song (宋体)**; untranslated source text and bilingual source lines do **not** force Song. - Default Heading theme blue is neutralized unless the source set an explicit color. **Output** - Default: `output/.zh.docx` (Navigation pane via heading outline). Override with `--format epub` (etc.). ```bash uv run wenyi translate book.docx uv run wenyi translate book.docx --bilingual uv run wenyi translate book.docx --format epub ``` ## SRT subtitles `translate` routes `.srt` files automatically. The subtitle path is intentionally lighter than the book pipeline: - sliding windows of 20 cues with overlap 10, up to 100 concurrent strong-tier calls; - no glossary, polishing, or whole-book review; - `--chapter`, `--polish`, `--review`, and `--format` are ignored or rejected where they do not apply; - monolingual `output/.zh.srt` by default; add `--bilingual` for `.zh-bi.srt`. ```bash uv run wenyi translate movie.srt uv run wenyi translate movie.srt --bilingual uv run wenyi translate movie.srt --no-mono --bilingual ``` Resume by running the same source file again. Cached batches under `state/srt//targets//batches/` are skipped. Layout: ```text state/srt//targets// manifest.json # source identity, cue counts, window settings cues.jsonl # one cue per line: index, timestamp, source, target, status batches/ # raw model results for resume usage.json # cumulative token usage across resumes timing.json # cumulative execution time and individual invocation durations events.jsonl # run events and LLM retry observations ``` There is no `glossary.db` or `reviews/` tree for subtitles. Package code lives in `trans_novel.srt` (store + translate), with I/O in `ingest.srt_reader` and `assemble.srt_writer`. ## Usage and event logs Each target directory stores cumulative token usage in `usage.json` and appends stage events and retries to `events.jsonl`. Review directories also record session usage; each increment is merged into the cumulative ledger once. The progress-bar clock measures the entire current workflow, including parsing, model waits, translation, polishing, review and export. Switching stages, chapters or review rounds does not reset it; completing one stage does not stop it while subsequent work is pending. Concurrent model requests contribute wall time, not the sum of request durations. After `prepare`, `translate` (including `--chapter` and SRT), `review` or `assemble`, the CLI prints the last run's duration and cumulative execution time. Each target's `timing.json` stores `total_seconds` and a `runs` list with invocation IDs, operations, timestamps, durations and completion statuses. Repeating a command adds only that invocation's execution time, excluding downtime between runs; nested pipeline stages are counted once. Separately launched commands contribute their own durations, even when they overlap. Book timing can also be inspected with `wenyi status book.epub`; inspection and report regeneration do not add time. Once book state has been initialized or validated, failures and normal Ctrl+C exits also save the invocation's elapsed time. Timing is committed atomically under its own lock, independently of token usage. Older runs have no timing history to recover; time is accumulated from this version onward. A forced kill or a failure before state initialization cannot save the current invocation's duration. Manifests bind input content with `source_sha256`. A different file with the same name, or state without a valid hash, cannot resume; create new translation state. ## Common commands ```bash # Parse and list the source chapters without model calls uv run wenyi parse book.epub # Run the complete workflow, translate one chapter, or prepare without translating uv run wenyi translate book.epub uv run wenyi translate book.epub --chapter 3 uv run wenyi translate book.epub --format txt uv run wenyi prepare book.epub uv run wenyi translate book.pdf uv run wenyi translate movie.srt # Override polishing and final review settings uv run wenyi translate book.epub --polish --review uv run wenyi translate book.epub --no-polish --no-review # Produce both editions, or only the bilingual edition uv run wenyi translate book.epub --bilingual uv run wenyi translate book.epub --no-mono --bilingual ``` `parse` lists zero-based chapter indices, source titles and paragraph counts, and saves the source document in `parsed_document.json` under the printed target-language state directory. It does not require LLM credentials, detect language with a model, generate digests/style/terms, or mark translation state initialized. `language.source: auto` remains unresolved until preparation. PDF parsing still needs the configured MinerU/BabelDOC service. SRT uses the independent subtitle workflow, not this book command. Use `prepare` or `translate` afterward with the same source and configuration. Matching cached parsing is reused, including PDF conversion; changed content or parsing options invalidate it. An existing initialized run rejects different source content and keeps saved translations, analysis and usage unchanged. `prepare` parses the book, detects its language, generates chapter digests when enabled, builds the style guide and initial glossary, and then generates the whole-book synopsis when enabled, without translating any body text. Run `translate` with the same source file to continue from the saved state. ## Best-of-three precision translation Select **Three-draft precision** when creating a Web book project. The choice is fixed for that project and does not appear as an option in global or project settings. Create another project to use a different mode. For CLI, use: ```bash uv run wenyi translate book.epub --translation-mode best_of_three --polish ``` The equivalent configuration is `pipeline.translation_mode: best_of_three` with `pipeline.polish: true`. `--translation-mode standard` restores the default workflow for pending batches. Precision with `--no-polish` is rejected; the option does not apply to SRT. When resuming, keep the same mode/configuration to reuse precision checkpoints. Existing translations are not automatically reprocessed. See [precision workflow](pipeline.md#best-of-three-precision-translation) for three independent drafts followed by one source-first synthesis, resume and cost/quality limitations. A normal batch uses four model calls versus two for standard translation with polishing, with the same routes and output limits. The final synthesis is structurally validated, not independently accuracy-judged. In comparisons, `target_before_polish` is the first initial draft, not a selected accurate draft. Quality improvement is not guaranteed. ## Interrupting and resuming Every completed batch is written to the state directory. To resume after an interruption, run the same source file again: ```bash uv run wenyi translate book.epub uv run wenyi status book.epub ``` Changing polishing settings does not automatically rerun translation batches that are already complete. Review is different: every `review` invocation rechecks the complete translated book and creates a new timestamped Review run. Runs are read-only by default; `--autofix` may publish their final revisions. Use a new state directory or remove the corresponding state only when you intentionally want a fresh translation. Language policies are built into the source code. When they change or saved state lacks a policy identity, `prepare`/`translate` automatically rebuilds affected analysis before pending work resumes; this may call models. Completed translations remain saved. Font and other export-only changes need a fresh export only. See [built-in language policies](configuration.md#built-in-language-policies) for book/SRT behavior and developer diagnostics. ## Independent stages and glossary management ```bash uv run wenyi review book.epub uv run wenyi review book.epub --autofix uv run wenyi glossary list book.epub uv run wenyi glossary conflicts book.epub uv run wenyi glossary resolve book.epub "source term" "chosen translation" uv run wenyi report book.epub uv run wenyi assemble book.epub ``` `review` checks the complete translated book using the final glossary. Its unchanged initial Reviewer runs over contiguous chunks concurrently; candidates can then enter a bounded evidence loop, and contradictory cross-chunk consistency suggestions can receive a final recommendation. Confirmed issues may generate provisional full-segment replacements in a run-local shadow translation. Every Fixer in a round reads the same immutable snapshot; the next whole-book pass blindly reviews the resulting shadow text without receiving prior issue explanations. By default these replacements remain read-only. With `--autofix` (or `pipeline.review_autofix: true`), folded `changes` are applied first; remaining issues then reuse the same bounded Review Agent Loop against that updated text, and confirmed issues reuse the same Review Fixer. The final complete segments are written only to chapter `target`; no Review history fields are added to chapter JSON, and the manifest and glossary are unchanged. Each run writes one user-facing `result.json`, its model-usage delta, an event stream, and internal round traces to `state//targets//reviews/review-/`. The same usage delta is also added once to the book's cumulative `usage.json`. Autofix keeps its full before/after chain, issue decisions, failures, and idempotent write journal in `autofix/index.json`; annotation and DOCX style offsets are refreshed after publishing. `report.json` contains a compact Review and Autofix summary. `report` summarizes the current translation and latest Review result without modifying translated text. `assemble` rebuilds output from existing state without calling the model again. If another terminal is still translating, export uses a consistent snapshot of the batches already persisted when the command starts; run it again to include batches completed afterward.