Batch-summarize .txt books with Ollama and a local LLM, writing one row per book to an Excel spreadsheet (.xlsx).
The model reads the full text of every book — never a truncated excerpt. Books that fit the context window are summarized in one pass; bigger books are split into passages, each summarized separately, and the passage summaries are merged into the final summary.
All prompts are in French and the model is asked to produce a ~500-word French summary plus a score from 1 to 10 (e.g. Note : 8/10) for each book.
- No truncation — the whole book is always fed to the model:
- fits in context → single-shot summary;
- too big → chunk-compact: each passage summarized in 200–300 French words, then merged into the final summary.
- Plain-text replies — the model answers in free-form French prose (no JSON, no fragile structured parsing).
- Resumable — the output spreadsheet is parsed on startup; books already processed are skipped automatically, previously failed books are retried.
- Per-book robustness — each model call retries up to 3 times (empty or degenerate replies included), and results are saved to the spreadsheet after every book.
- Progress on screen —
File+ summary per book,[CHUNKED: N passages]marker, and a--verbosemode with timing and raw replies. - Stop anytime — press
ESCin the console to abort the run gracefully (works even mid-call;Ctrl-Calso works). The in-flight book is not recorded and is retried on the next run.
- Python 3.10+
- An Ollama server running locally (default
http://127.0.0.1:11434) - The Python client and the spreadsheet writer:
pip install ollama openpyxl - A model pulled, e.g.:
ollama pull mistral-nemo:12bThe default model is mistral-nemo:12b (12B, ~7 GB quantized, native 128k context). Any other model can be used with --model.
git clone <your-repo-url>
cd <your-repo>
pip install ollama openpyxlpython summarize_books.py <folder> [options]<folder> is scanned recursively for *.txt files.
Examples:
# Summarize every book in the folder
python summarize_books.py "books/Africa"
# Only the last 5 books (in sorted order)
python summarize_books.py "books/Africa" -n 5
# Show per-call timing and raw replies
python summarize_books.py "books/Africa" -n 5 --verboseRe-running the same command skips books already in the spreadsheet and picks up where the previous run stopped. Books that previously errored are attempted again.
review_summaries.py walks every book whose review column is still empty, one at a time, showing the file path and the stored summary:
python review_summaries.py # review book_reviews.xlsx
python review_summaries.py -o out.xlsx -e out_errors.csvFor each book press a single key (no Enter needed): 0–9 to rate it, e to mark it as a bad entry, or q to quit. The book title and summary are read aloud (Windows TTS, French voice when installed); pressing any key stops the reading — use --no-tts to disable. Ratings are stored in the review column (added to the spreadsheet on first use); e removes the row from the spreadsheet and appends it to the error file (book_reviews_errors.csv by default) with the date, so bad entries stay trackable. Rows whose reply starts with [ERROR] are moved to the error file automatically on startup. Progress is saved after every entry, so the next run resumes where you left off. summarize_books.py preserves the review column on every rewrite.
| Option | Default | Description |
|---|---|---|
folder |
— | Folder containing the books (.txt, scanned recursively) |
--model |
mistral-nemo:12b |
Ollama model to use |
--output |
book_reviews.xlsx |
Output spreadsheet path |
--num-ctx |
49152 |
Context window in tokens (0 = auto-detect from the model) |
--chunk-size |
45000 |
Max tokens per passage when a book is chunked (kept below --num-ctx automatically) |
-n, --limit |
0 |
Process at most N books, in sorted order (0 = all) |
--verbose |
off | Print per-call timing, token counts and raw replies |
--no-prompt-cache |
off | Disable ollama's prompt cache (avoids CPU churn from KV state saves between chunks) |
--timeout |
600 |
Per-call timeout in seconds |
Excel spreadsheet (.xlsx) with two columns:
file path |
llm output |
|---|---|
books/FR Nouvelles/Żywila Powiastka z Dziejów Litewskich.txt |
Le livre "Żywila" est une légende lithuanienne ... Note : 8/10 |
The llm output cell holds the model's raw reply (French summary + Note : X/10). On screen, each result is printed as File + Summary, prefixed with [CHUNKED: N passages] when the book was chunked. Failures are stored as [ERROR] <reason> and are retried on the next run. The workbook is rewritten (atomically) after every book, so progress survives a crash; --validate prunes bad rows from the file.
- Skip already-done books — the output spreadsheet is read first; rows with a real reply are skipped,
[ERROR]rows are retried. - Read the book (
*.txt, UTF-8 with fallback). - Single-shot or chunked:
- if the estimated token count fits in
--num-ctx(minus a 2500-token answer reserve), the whole text goes in one call; - otherwise it is split on paragraph boundaries into passages of up to
--chunk-sizetokens. Each passage is summarized (200–300 words, French,CHUNK_SYSTEM_PROMPT), then all passage summaries are merged into the final ~500-word summary + score (MERGE_SYSTEM_PROMPT).
- if the estimated token count fits in
- Length correction — if the final summary is under 350 words, one expansion pass asks for a longer one.
- Write — the row is added to the workbook and saved immediately.
Every model call retries up to 3 times with a French corrective prompt when the reply is empty or degenerate (e.g. shorter than 50 words for a passage), and replies are capped at 1200 tokens (num_predict) so a runaway generation can never hang a run.
The KV cache is the VRAM driver, not the chunk size:
- Default
--num-ctx 49152≈ 7.9 GB of f16 KV cache + ~7 GB weights ≈ fits 16 GB with headroom; at this setting the whole model stays on the GPU (40–41/41 layers, verified in the ollama logs). --chunk-sizecan go up to almost--num-ctx(≈46.6k tokens at the default) with no extra VRAM cost.- Raising
--num-ctxbeyond ~64k starts offloading layers to CPU (much slower) and, past ~96k, can crash the ollama runner — which looks like the program hanging.--timeoutbounds how long a hung call waits. - To halve the KV cache so more layers fit on the GPU, restart the ollama server with
OLLAMA_KV_CACHE_TYPE=q8_0(per-requestcache_typeis ignored by some ollama builds).
| Symptom | Fix |
|---|---|
ModelNotFoundError / model not found |
ollama pull mistral-nemo:12b (or your --model) |
| Very slow, or "stuck" on a large book | Lower --num-ctx (e.g. 49152) or restart ollama with OLLAMA_KV_CACHE_TYPE=q8_0; check ollama list / GPU usage |
| High CPU churn between passages | Add --no-prompt-cache (ollama saves/evicts GBs of KV state between chunk requests by default) |
| A call appears to generate forever | Fixed: replies are capped at 1200 tokens (num_predict); re-run and the spreadsheet resume skips finished books |
A book shows [ERROR] in the spreadsheet |
It will be retried automatically on the next run; use --verbose to see the raw failure |
| Re-run skips too many books | Rows starting with [ERROR] are never skipped — only successful replies are |
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for details.