Metadata-Version: 2.4
Name: breviabook
Version: 0.3.0
Summary: Condense large technical ebooks (EPUB/PDF) preserving code and diagrams, optionally translating in the same pass.
Project-URL: Homepage, https://github.com/willywg/breviabook
Project-URL: Repository, https://github.com/willywg/breviabook
Author-email: William Wong Garay <willywg@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: ebook,epub,llm,ollama,pdf,summarization,translation
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Text Processing
Requires-Python: >=3.11
Requires-Dist: beautifulsoup4>=4.12
Requires-Dist: httpx>=0.27
Requires-Dist: litellm>=1.40
Requires-Dist: lxml>=5.2
Requires-Dist: pdfplumber>=0.11
Requires-Dist: pydantic-settings>=2.2
Requires-Dist: pydantic>=2.6
Requires-Dist: pypdf>=4.2
Requires-Dist: python-dotenv>=1.0
Requires-Dist: rich>=13.7
Requires-Dist: tiktoken>=0.7
Requires-Dist: typer>=0.12
Provides-Extra: pdf
Requires-Dist: weasyprint>=62; extra == 'pdf'
Description-Content-Type: text/markdown

# BreviaBook

> Condense large technical ebooks (EPUB/PDF) into a fast, filler-free version — **preserving
> code, formulas, and the important diagrams** — and optionally **translate** them in the same
> pass. Outputs **EPUB, PDF, and Markdown**.

Multi-provider LLM from day one: **Ollama** (local), **OpenAI**, **Gemini**, **OpenRouter**,
and any **OpenAI-compatible** endpoint (vLLM, LM Studio, LocalAI). Runs fully local on a laptop
with Ollama, or via a paid API when it makes sense.

[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)

## Why

Technical books are long and padded. BreviaBook reads dense books fast without losing the parts
that matter — **code examples, tables, and meaningful figures survive; filler doesn't** — and
can deliver the result in your language (e.g. English → Spanish) in one go.

## Features

- **Structure-aware condensation** — chapter-aware chunking; **code blocks are never
  summarized or split**.
- **Hierarchical summarization** — per-chunk condense + per-chapter synthesis with active
  length control toward a `--target-ratio`.
- **Image preservation (the differentiator)** — keeps images whose section survives
  (Strategy A); optional **vision ranking** (`--rank-images`) drops decorative images and
  improves captions.
- **Integrated translation** — condense-and-translate in one pass, or translate the **full** book
  without condensing (`breviabook translate`). Optional glossary for consistent terminology; code
  stays untranslated. Resilient batches: a malformed model response retries, then **bisects** to
  isolate the offending segment so its neighbours still translate, and falls back to the source
  text rather than crashing.
- **Inline formatting preserved** — bold, italic, links, inline code, **color** (including
  CSS-class-styled headings), and images embedded mid-text survive parse → translate → render.
  Untrusted source markup is sanitized to a strict allowlist.
- **Three outputs** — EPUB (our own builder), PDF (weasyprint), Markdown.
- **Live TUI** — a banner plus per-phase progress bars (parse → condense → synthesize →
  translate → render) and a usage panel that ticks token/cost totals in real time. Degrades to
  plain text when output is piped.
- **Cost control for reasoning models** — `--reasoning-effort disable` turns off "thinking" on
  models like `gemini-3-flash-preview`, which otherwise spend most output tokens on discarded
  reasoning (~3.6× cheaper on a real run, same quality). See [Cost & reasoning models](#cost--reasoning-models).
- **Compression report** — every run prints how much smaller the result is and an approximate
  page count (e.g. `~479 → ~149 pages, 69% smaller`).
- **Resumable** — `--resume` continues an interrupted job from a checkpoint (already-condensed
  chunks are reused, not re-billed).
- **Dry-run + cost** — `--dry-run` estimates tokens, pages, compression, and approximate cost
  without calling the LLM.
- **Usage report** — every run prints prompt/completion/cached tokens and estimated cost.

## Install

Requires **Python 3.11+** and [uv](https://docs.astral.sh/uv/). EPUB and Markdown output need
no system libraries; PDF output adds the optional `[pdf]` extra (see
[PDF output requirements](#pdf-output-requirements)). Set credentials before running cloud
providers — see [Configuration](#configuration).

### Run with uvx (no install)

Run the latest straight from GitHub — no clone, no virtualenv:

```bash
# EPUB + Markdown (nothing else needed)
uvx --from "git+https://github.com/willywg/breviabook" breviabook condense book.epub --formats epub,md

# with PDF output
uvx --from "breviabook[pdf] @ git+https://github.com/willywg/breviabook" breviabook condense book.epub
```

Once published to PyPI this shortens to `uvx breviabook condense book.epub` (and
`uvx "breviabook[pdf]" …` for PDF output).

### Install the command

```bash
uv tool install "git+https://github.com/willywg/breviabook"   # append [pdf] for PDF output
breviabook condense book.epub --out ./out/                     # now on your PATH
```

(After PyPI: `uv tool install breviabook`.)

### From source (development)

```bash
git clone https://github.com/willywg/breviabook.git
cd breviabook
uv sync                # EPUB + Markdown
uv sync --extra pdf    # add PDF output
uv run breviabook --help
```

### PDF output requirements

PDF rendering uses [weasyprint](https://weasyprint.org/), which needs system libraries
(EPUB and Markdown need nothing extra):

- **macOS:** `brew install pango gdk-pixbuf libffi` — on Apple Silicon also
  `export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib`.
- **Debian/Ubuntu:** `libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0 libffi-dev`.

## Quickstart

```bash
# Local, all three formats, with Ollama
uv run breviabook condense book.epub --formats epub,pdf,md --out ./out/

# Estimate tokens + cost first, without calling the LLM
uv run breviabook condense book.epub --dry-run

# Condense + translate to Spanish with a cloud model (Gemini thinking is off by default)
uv run breviabook condense book.epub \
  --provider gemini --model gemini-3-flash-preview \
  --translate-to Spanish --source-lang English --glossary glossary.json \
  --formats epub,md --out ./out/

# Drop decorative images with a vision model, and resume if interrupted
uv run breviabook condense book.pdf --provider gemini --model gemini-3-flash-preview \
  --rank-images --resume --out ./out/
```

### Translate without condensing

The `translate` command translates the **full** book — every chapter, every paragraph — without
shortening it. It produces a same-length output in the target language, preserving code, tables,
and images. It is the mode with the most LLM calls, which is why it supports `--resume` with its
own translation checkpoint:

```bash
# Translate the full book to Spanish (no condensation)
uv run breviabook translate book.epub --to Spanish --formats epub,md --out ./out/

# Dry-run estimate first
uv run breviabook translate book.epub --to Spanish --dry-run

# Resume an interrupted translation
uv run breviabook translate book.epub --to Spanish --resume --formats epub,md --out ./out/
```

## CLI

```
breviabook condense INPUT.{epub,pdf} [options]

  --provider        ollama | openai | gemini | openrouter        (default: ollama)
  --model           model tag (default from .env)
  --api-endpoint    base URL for OpenAI-compatible servers (vLLM/LM Studio/LocalAI)
  --target-ratio    target size, e.g. 0.30 = ~30% of the original
  --formats         comma list of epub,pdf,md                     (default: epub,pdf,md)
  --translate-to    target language (omit = no translation)
  --source-lang     source language (optional hint)
  --glossary        glossary JSON {source_term: target_term}
  --rank-images     use a vision model to score/drop images
  --reasoning-effort  auto | disable | low | medium | high  (thinking budget; Gemini defaults
                      to "disable" to save cost — pass "auto" to keep native thinking)
  --manual-toc      manual TOC JSON for PDFs without an outline
  --out             output directory                              (default: ./output)
  --resume          resume from checkpoint
  --dry-run         estimate tokens/cost/pages only, no LLM call

breviabook translate INPUT.{epub,pdf} --to TARGET_LANG [options]

  --to              target language (required)
  --from            source language (optional hint)
  --glossary        glossary JSON {source_term: target_term}
  --formats         comma list of epub,pdf,md                     (default: epub,pdf,md)
  --out             output directory                              (default: ./output)
  --resume          resume from translation checkpoint
  --provider        ollama | openai | gemini | openrouter        (default: ollama)
  --model           model tag (default from .env)
  --api-endpoint    base URL for OpenAI-compatible endpoints
  --reasoning-effort  auto | disable | low | medium | high
  --rank-images     use a vision model to score/drop images
  --manual-toc      manual TOC JSON for PDFs without an outline
  --dry-run         estimate tokens/cost only, no LLM call
```

## Cost & reasoning models

Some cloud models "think" before answering. For condensation/translation — rewriting tasks,
not reasoning tasks — that thinking is pure waste: on a real run ~94% of output tokens were
discarded reasoning, billed as output. **BreviaBook therefore disables thinking by default** for
providers that have it on (Gemini). To restore a model's native thinking, pass
`--reasoning-effort auto` (or set `low`/`medium`/`high` explicitly).

| Run (Introducing Go, EPUB → Spanish) | Cost | Output tokens | Quality |
|---|---|---|---|
| thinking on (`--reasoning-effort auto`) | $0.78 | 243k | excellent |
| **default (thinking disabled)** | **$0.22** | 55k | **excellent (identical)** |

Estimate first with `--dry-run` (no LLM call). Note the dry-run assumes no reasoning tokens —
which matches the default; if you re-enable thinking with `--reasoning-effort auto`, the real
cost can be several times the estimate. Pricing for `gemini-3-flash-preview`: ~$0.50 / 1M
input, ~$3.00 / 1M output.

## Configuration

BreviaBook reads settings from **environment variables** or a **`.env` file in the directory you
run from**. Environment variables take precedence. Local Ollama needs no key, so the defaults
work out of the box; cloud providers need an API key. CLI flags (`--provider`, `--model`, …)
override both.

```bash
# Option A — environment variable (best for `uvx`; works from any directory)
export GEMINI_API_KEY="your-key"
uvx breviabook condense book.epub --provider gemini --model gemini-3-flash-preview --translate-to Spanish

# one-off, inline (no export)
GEMINI_API_KEY="your-key" uvx breviabook condense book.epub --provider gemini

# Option B — a .env file in the current directory (from source: `cp .env.example .env`)
printf 'GEMINI_API_KEY=your-key\n' > .env
```

All recognized variables (every one optional; names are case-insensitive):

```
LLM_PROVIDER=ollama                       # ollama | openai | gemini | openrouter
OLLAMA_ENDPOINT=http://localhost:11434
DEFAULT_MODEL=gemma4:e4b

OPENAI_API_KEY=         # comma-separated for key rotation
GEMINI_API_KEY=
OPENROUTER_API_KEY=

DEFAULT_TARGET_RATIO=0.30
DEFAULT_CHUNK_TOKENS=2000
IMAGE_STRATEGY=keep_referenced   # keep_referenced | vision_ranked
```

Keys are sent only to the selected provider's endpoint, never elsewhere. `.env` is gitignored —
don't commit credentials.

## How it works

Everything flows through a format-agnostic **Intermediate Representation (IR)**:

```
parse → chunk → condense → synthesize → (translate) → image-select → render
        EPUB/PDF → IR        per-chunk    per-chapter    glossary      Strategy A/B   EPUB/PDF/MD
```

Parsers turn EPUB/PDF into the IR; the condenser and translator transform its text blocks
(leaving code and images intact); renderers emit the final files. Adding an input or output
format means writing one parser or one renderer — the condensation logic doesn't change.

See the full design and build plan in **[docs/ROADMAP.md](docs/ROADMAP.md)**.

## Development

```bash
uv run ruff check . && uv run ruff format --check .   # lint + format
uv run mypy --strict breviabook                           # types
uv run pytest -q                                       # tests
uv run pip-licenses --partial-match --fail-on "General Public License;GPL" --ignore-packages pyphen   # license audit (blocks GPL/AGPL; pyphen ignored — MPL 1.1 elected from its tri-license)
```

## License

[Apache-2.0](LICENSE). BreviaBook is **inspired by** open-source work but contains no copied code
and depends on no copyleft (GPL/AGPL) libraries — see [docs/ROADMAP.md §14](docs/ROADMAP.md)
and [NOTICE](NOTICE).
