local-terminal-summarizer 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- local_terminal_summarizer-0.7.0/CONTRIBUTING.md +84 -0
- local_terminal_summarizer-0.7.0/LICENSE +21 -0
- local_terminal_summarizer-0.7.0/MANIFEST.in +11 -0
- local_terminal_summarizer-0.7.0/PKG-INFO +424 -0
- local_terminal_summarizer-0.7.0/README.md +390 -0
- local_terminal_summarizer-0.7.0/ROADMAP.md +77 -0
- local_terminal_summarizer-0.7.0/SECURITY.md +104 -0
- local_terminal_summarizer-0.7.0/config.example.toml +149 -0
- local_terminal_summarizer-0.7.0/pyproject.toml +62 -0
- local_terminal_summarizer-0.7.0/setup.cfg +4 -0
- local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/PKG-INFO +424 -0
- local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/SOURCES.txt +116 -0
- local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/dependency_links.txt +1 -0
- local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/entry_points.txt +2 -0
- local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/requires.txt +14 -0
- local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/top_level.txt +1 -0
- local_terminal_summarizer-0.7.0/src/summarizer/__init__.py +15 -0
- local_terminal_summarizer-0.7.0/src/summarizer/__main__.py +8 -0
- local_terminal_summarizer-0.7.0/src/summarizer/aggregation/__init__.py +18 -0
- local_terminal_summarizer-0.7.0/src/summarizer/aggregation/mapreduce.py +379 -0
- local_terminal_summarizer-0.7.0/src/summarizer/aggregation/refine.py +14 -0
- local_terminal_summarizer-0.7.0/src/summarizer/aggregation/strategies.py +34 -0
- local_terminal_summarizer-0.7.0/src/summarizer/cache/__init__.py +19 -0
- local_terminal_summarizer-0.7.0/src/summarizer/cache/cli.py +55 -0
- local_terminal_summarizer-0.7.0/src/summarizer/cache/store.py +376 -0
- local_terminal_summarizer-0.7.0/src/summarizer/cancel.py +49 -0
- local_terminal_summarizer-0.7.0/src/summarizer/chunking/__init__.py +46 -0
- local_terminal_summarizer-0.7.0/src/summarizer/chunking/budget.py +136 -0
- local_terminal_summarizer-0.7.0/src/summarizer/chunking/splitter.py +361 -0
- local_terminal_summarizer-0.7.0/src/summarizer/chunking/strategies.py +284 -0
- local_terminal_summarizer-0.7.0/src/summarizer/chunking/token.py +24 -0
- local_terminal_summarizer-0.7.0/src/summarizer/cli.py +660 -0
- local_terminal_summarizer-0.7.0/src/summarizer/config.py +792 -0
- local_terminal_summarizer-0.7.0/src/summarizer/doctor.py +548 -0
- local_terminal_summarizer-0.7.0/src/summarizer/document/__init__.py +16 -0
- local_terminal_summarizer-0.7.0/src/summarizer/document/metadata.py +32 -0
- local_terminal_summarizer-0.7.0/src/summarizer/document/model.py +107 -0
- local_terminal_summarizer-0.7.0/src/summarizer/document/reader.py +183 -0
- local_terminal_summarizer-0.7.0/src/summarizer/document/tables.py +87 -0
- local_terminal_summarizer-0.7.0/src/summarizer/engine/__init__.py +33 -0
- local_terminal_summarizer-0.7.0/src/summarizer/engine/backends.py +158 -0
- local_terminal_summarizer-0.7.0/src/summarizer/engine/base.py +111 -0
- local_terminal_summarizer-0.7.0/src/summarizer/engine/capabilities.py +155 -0
- local_terminal_summarizer-0.7.0/src/summarizer/engine/client.py +465 -0
- local_terminal_summarizer-0.7.0/src/summarizer/engine/factory.py +64 -0
- local_terminal_summarizer-0.7.0/src/summarizer/engine/ollama.py +291 -0
- local_terminal_summarizer-0.7.0/src/summarizer/engine/openai.py +426 -0
- local_terminal_summarizer-0.7.0/src/summarizer/errors.py +130 -0
- local_terminal_summarizer-0.7.0/src/summarizer/evaluation/__init__.py +47 -0
- local_terminal_summarizer-0.7.0/src/summarizer/evaluation/cases.py +235 -0
- local_terminal_summarizer-0.7.0/src/summarizer/evaluation/cli.py +90 -0
- local_terminal_summarizer-0.7.0/src/summarizer/evaluation/report.py +146 -0
- local_terminal_summarizer-0.7.0/src/summarizer/evaluation/runner.py +238 -0
- local_terminal_summarizer-0.7.0/src/summarizer/introspect.py +341 -0
- local_terminal_summarizer-0.7.0/src/summarizer/log.py +92 -0
- local_terminal_summarizer-0.7.0/src/summarizer/net.py +51 -0
- local_terminal_summarizer-0.7.0/src/summarizer/output/__init__.py +24 -0
- local_terminal_summarizer-0.7.0/src/summarizer/output/atomic.py +53 -0
- local_terminal_summarizer-0.7.0/src/summarizer/output/json.py +188 -0
- local_terminal_summarizer-0.7.0/src/summarizer/output/stream.py +41 -0
- local_terminal_summarizer-0.7.0/src/summarizer/pipeline.py +959 -0
- local_terminal_summarizer-0.7.0/src/summarizer/profiles/__init__.py +21 -0
- local_terminal_summarizer-0.7.0/src/summarizer/profiles/loader.py +138 -0
- local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/__reduce__.md +20 -0
- local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/academic.md +16 -0
- local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/code.md +22 -0
- local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/meeting.md +22 -0
- local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/plain.md +19 -0
- local_terminal_summarizer-0.7.0/src/summarizer/progress.py +165 -0
- local_terminal_summarizer-0.7.0/src/summarizer/prompt/__init__.py +16 -0
- local_terminal_summarizer-0.7.0/src/summarizer/prompt/builder.py +269 -0
- local_terminal_summarizer-0.7.0/src/summarizer/prompt/identity.py +64 -0
- local_terminal_summarizer-0.7.0/src/summarizer/readers/__init__.py +13 -0
- local_terminal_summarizer-0.7.0/src/summarizer/readers/code.py +58 -0
- local_terminal_summarizer-0.7.0/src/summarizer/readers/markdown.py +36 -0
- local_terminal_summarizer-0.7.0/src/summarizer/readers/pdf.py +186 -0
- local_terminal_summarizer-0.7.0/src/summarizer/readers/stdin.py +100 -0
- local_terminal_summarizer-0.7.0/src/summarizer/readers/text.py +49 -0
- local_terminal_summarizer-0.7.0/src/summarizer/session/__init__.py +44 -0
- local_terminal_summarizer-0.7.0/src/summarizer/session/digest.py +155 -0
- local_terminal_summarizer-0.7.0/src/summarizer/session/lock.py +64 -0
- local_terminal_summarizer-0.7.0/src/summarizer/session/manager.py +448 -0
- local_terminal_summarizer-0.7.0/src/summarizer/session/run.py +135 -0
- local_terminal_summarizer-0.7.0/src/summarizer/update/__init__.py +19 -0
- local_terminal_summarizer-0.7.0/src/summarizer/update/install.py +62 -0
- local_terminal_summarizer-0.7.0/src/summarizer/update/manager.py +116 -0
- local_terminal_summarizer-0.7.0/src/summarizer/update/release.py +95 -0
- local_terminal_summarizer-0.7.0/src/summarizer/update/verify.py +60 -0
- local_terminal_summarizer-0.7.0/src/summarizer/verify.py +133 -0
- local_terminal_summarizer-0.7.0/tests/test_adversarial_corpus.py +336 -0
- local_terminal_summarizer-0.7.0/tests/test_backends.py +203 -0
- local_terminal_summarizer-0.7.0/tests/test_cache_checkpoints.py +279 -0
- local_terminal_summarizer-0.7.0/tests/test_cancellation.py +228 -0
- local_terminal_summarizer-0.7.0/tests/test_chunk_budget_limits.py +104 -0
- local_terminal_summarizer-0.7.0/tests/test_concurrency.py +170 -0
- local_terminal_summarizer-0.7.0/tests/test_config_layering.py +263 -0
- local_terminal_summarizer-0.7.0/tests/test_context_overflow.py +215 -0
- local_terminal_summarizer-0.7.0/tests/test_endpoint_scope.py +152 -0
- local_terminal_summarizer-0.7.0/tests/test_evaluation.py +181 -0
- local_terminal_summarizer-0.7.0/tests/test_introspection.py +143 -0
- local_terminal_summarizer-0.7.0/tests/test_long_documents.py +183 -0
- local_terminal_summarizer-0.7.0/tests/test_model_discovery.py +82 -0
- local_terminal_summarizer-0.7.0/tests/test_ollama_warmup.py +125 -0
- local_terminal_summarizer-0.7.0/tests/test_output_behavior.py +269 -0
- local_terminal_summarizer-0.7.0/tests/test_packaging.py +60 -0
- local_terminal_summarizer-0.7.0/tests/test_pipeline_and_cli.py +366 -0
- local_terminal_summarizer-0.7.0/tests/test_progress.py +111 -0
- local_terminal_summarizer-0.7.0/tests/test_prompt_identity.py +54 -0
- local_terminal_summarizer-0.7.0/tests/test_provenance.py +57 -0
- local_terminal_summarizer-0.7.0/tests/test_readers_v3.py +303 -0
- local_terminal_summarizer-0.7.0/tests/test_reduce_limits.py +110 -0
- local_terminal_summarizer-0.7.0/tests/test_repo_guard.py +195 -0
- local_terminal_summarizer-0.7.0/tests/test_response_caps.py +141 -0
- local_terminal_summarizer-0.7.0/tests/test_security.py +240 -0
- local_terminal_summarizer-0.7.0/tests/test_sessions.py +550 -0
- local_terminal_summarizer-0.7.0/tests/test_stream_timeouts.py +128 -0
- local_terminal_summarizer-0.7.0/tests/test_tables_and_verify.py +183 -0
- local_terminal_summarizer-0.7.0/tests/test_truncation.py +295 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping. This is a local-first, Unix-native tool: keep changes
|
|
4
|
+
small, explicit, and testable without a model server.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
python3 -m venv .venv
|
|
10
|
+
.venv/bin/python -m pip install -e '.[test]'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The base install has zero runtime dependencies; `[test]` adds pytest and
|
|
14
|
+
pypdf so the PDF reader tests can run. Nothing downloads a model.
|
|
15
|
+
|
|
16
|
+
## Before you push
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
.venv/bin/python -m pytest # full suite, no network, no Ollama
|
|
20
|
+
.venv/bin/python -m compileall -q src tests evaluation scripts
|
|
21
|
+
.venv/bin/python scripts/check_repo_safety.py
|
|
22
|
+
.venv/bin/python -m build --no-isolation # optional: sdist + wheel
|
|
23
|
+
git diff --check
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
CI runs pytest and `compileall` on Python 3.11 and 3.14, the repository
|
|
27
|
+
safety guard, local CLI smoke tests, and a packaging job that builds the
|
|
28
|
+
distributions. It never needs Ollama, a GPU, or model access. Tests must bind
|
|
29
|
+
and connect only to loopback (`tests/conftest.py` enforces this).
|
|
30
|
+
|
|
31
|
+
## Real-model validation (manual, optional)
|
|
32
|
+
|
|
33
|
+
When a local Ollama is available, sanity-check against a real model:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
.venv/bin/summarize doctor
|
|
37
|
+
.venv/bin/summarize models
|
|
38
|
+
.venv/bin/summarize evaluate --model qwen3:8b --tags core
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`evaluate --dry-run` is the CI-safe path; the rest is manual integration, not
|
|
42
|
+
required for a contribution to be accepted.
|
|
43
|
+
|
|
44
|
+
## Ground rules
|
|
45
|
+
|
|
46
|
+
- **Backend names stay out of the pipeline.** If behaviour differs between
|
|
47
|
+
runtimes, put it in an adapter or express it as a capability in
|
|
48
|
+
`engine/capabilities.py`; never add `if backend == ...` to the pipeline.
|
|
49
|
+
- **Adding a local backend** is a `BackendSpec` entry in
|
|
50
|
+
`engine/backends.py` plus, only when needed, one small adapter class. No
|
|
51
|
+
cloud providers.
|
|
52
|
+
- **Untrusted data stays untrusted.** Document text, reader metadata, and
|
|
53
|
+
map-stage interim summaries are always wrapped in a boundary that is
|
|
54
|
+
checked against the content. The reduce instructions and interim summaries
|
|
55
|
+
must remain structurally separate. `--context` is the only user-trusted
|
|
56
|
+
input and is still boundary-checked.
|
|
57
|
+
- **Bounded output.** Every generation request carries `max_tokens`; never
|
|
58
|
+
ship a path that can generate unbounded output.
|
|
59
|
+
- **Local only.** No telemetry, analytics, cloud endpoints, remote URLs in
|
|
60
|
+
normal runtime behaviour, remote log upload, or model downloads. Test
|
|
61
|
+
servers bind `127.0.0.1`.
|
|
62
|
+
- **No secrets in the repo.** Keep real config in
|
|
63
|
+
`~/.config/summarizer/config.toml`; `config.example.toml` is placeholders
|
|
64
|
+
only. The safety guard allows exceptions only with a written reason.
|
|
65
|
+
|
|
66
|
+
## Scope
|
|
67
|
+
|
|
68
|
+
Out of scope unless a concrete need appears: GUI, URL fetching, cloud models,
|
|
69
|
+
agents, tool execution, embeddings/vector databases, self-update, background
|
|
70
|
+
services, automatic model installation, multi-model ensemble/routing, and
|
|
71
|
+
refine-based aggregation. See `ROADMAP.md`.
|
|
72
|
+
|
|
73
|
+
## Tests
|
|
74
|
+
|
|
75
|
+
Prefer in-process test engines (see `tests/test_pipeline_and_cli.py`) over a
|
|
76
|
+
live server. Structural security guarantees belong in
|
|
77
|
+
`tests/test_adversarial_corpus.py`; evaluation behaviour in
|
|
78
|
+
`tests/test_evaluation.py`; configuration precedence in
|
|
79
|
+
`tests/test_config_layering.py`; cancellation in `tests/test_cancellation.py`;
|
|
80
|
+
concurrency in `tests/test_concurrency.py`; caching/checkpoints and prompt
|
|
81
|
+
identity in `tests/test_cache_checkpoints.py` and
|
|
82
|
+
`tests/test_prompt_identity.py`; progress in `tests/test_progress.py`; output
|
|
83
|
+
and stats in `tests/test_output_behavior.py`; packaging/version in
|
|
84
|
+
`tests/test_packaging.py`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 summarizer contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Source-distribution contents. The wheel already carries the package and its
|
|
2
|
+
# prompt data (see [tool.setuptools.package-data]); this keeps the docs and the
|
|
3
|
+
# committed example config in the sdist as well.
|
|
4
|
+
include README.md
|
|
5
|
+
include SECURITY.md
|
|
6
|
+
include ROADMAP.md
|
|
7
|
+
include CONTRIBUTING.md
|
|
8
|
+
include LICENSE
|
|
9
|
+
include config.example.toml
|
|
10
|
+
include pyproject.toml
|
|
11
|
+
recursive-include src/summarizer/profiles/prompts *.md
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: local-terminal-summarizer
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: A local-first, Unix-native terminal summarizer for local LLM servers.
|
|
5
|
+
Author: summarizer contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/adigaiki/local-terminal-summarizer
|
|
8
|
+
Project-URL: Repository, https://github.com/adigaiki/local-terminal-summarizer
|
|
9
|
+
Project-URL: Issues, https://github.com/adigaiki/local-terminal-summarizer/issues
|
|
10
|
+
Keywords: summarize,llm,openai-compatible,local-first,cli
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Topic :: Text Processing
|
|
18
|
+
Classifier: Topic :: Utilities
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Provides-Extra: pdf
|
|
23
|
+
Requires-Dist: pypdf>=4.0; extra == "pdf"
|
|
24
|
+
Requires-Dist: fonttools>=4.0; extra == "pdf"
|
|
25
|
+
Provides-Extra: ocr
|
|
26
|
+
Requires-Dist: pypdf>=4.0; extra == "ocr"
|
|
27
|
+
Requires-Dist: fonttools>=4.0; extra == "ocr"
|
|
28
|
+
Requires-Dist: pytesseract>=0.3.10; extra == "ocr"
|
|
29
|
+
Requires-Dist: pdf2image>=1.16; extra == "ocr"
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
32
|
+
Requires-Dist: pypdf>=4.0; extra == "test"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# summarizer
|
|
36
|
+
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://www.python.org/)
|
|
39
|
+
[](https://github.com/adigaiki/local-terminal-summarizer/actions/workflows/ci.yml)
|
|
40
|
+
|
|
41
|
+
`summarize` is a Linux-and-macOS command-line summarizer that turns a file or
|
|
42
|
+
stdin into a concise summary using a **local** LLM server you already run
|
|
43
|
+
(Ollama, llama.cpp, LM Studio, or any OpenAI-compatible server). It is a Unix
|
|
44
|
+
tool: pipe a document in, get a summary on stdout, with long-document chunking,
|
|
45
|
+
a trusted/untrusted input boundary, stable JSON output, and diagnostics.
|
|
46
|
+
|
|
47
|
+
Nothing leaves your machine by default: every request goes to a model server
|
|
48
|
+
over loopback (`http://localhost:11434`). Two opt-ins can produce other
|
|
49
|
+
traffic: `--check-update` (one request to a URL you configure in `[update]`),
|
|
50
|
+
and a non-loopback `endpoint`, which is refused unless you set
|
|
51
|
+
`[engine] allow_non_loopback`. See [docs/security.md](docs/security.md).
|
|
52
|
+
|
|
53
|
+
- MIT-licensed, Python 3.11+, zero runtime dependencies in the core install
|
|
54
|
+
(PDF and OCR are optional extras). No cloud, no telemetry, no model download,
|
|
55
|
+
no self-update.
|
|
56
|
+
|
|
57
|
+
## Why not just `ollama run`?
|
|
58
|
+
|
|
59
|
+
`ollama run` prints whatever the model says, with no structure around it.
|
|
60
|
+
`summarize` wraps the same local model with the things a summarizer needs:
|
|
61
|
+
documents longer than the context window are chunked and reduced; the document
|
|
62
|
+
is wrapped in a fresh random boundary and treated as untrusted data while
|
|
63
|
+
`--context` stays trusted; `--format json` gives a stable envelope with chunk
|
|
64
|
+
provenance; and `summarize doctor` tells you what is reachable, configured, and
|
|
65
|
+
missing. It is one small tool, not a chat client.
|
|
66
|
+
|
|
67
|
+
## Example
|
|
68
|
+
|
|
69
|
+
Input (`docs/examples/cache-incident.md`, an original sample), summarized with
|
|
70
|
+
the default `plain` profile on `qwen2.5-3b-16k` (Ollama 0.35.0):
|
|
71
|
+
|
|
72
|
+
```console
|
|
73
|
+
$ summarize docs/examples/cache-incident.md
|
|
74
|
+
# Cache incident review: Stale Prices During Node Replacement
|
|
75
|
+
|
|
76
|
+
On 14 March, at 02:40, the night-shift on-call engineer replaced a failing cache node in the `pricing` cluster. The node had been experiencing intermittent timeouts for about twenty minutes, and the replacement was treated as routine. During the swap, the pricing API served roughly four percent of requests from a replica that had not yet caught up. This resulted in some customers seeing prices that were up to an hour old for about three and a half minutes. No orders were placed at the stale prices, as the checkout service re-quotes against the primary node before authorization, and the seven orders that arrived during the window were priced from fresh data.
|
|
77
|
+
|
|
78
|
+
The on-call engineer noticed a rise in stale-read metrics at 02:52 and rolled the replica back at 03:05. Error rates were normal by 03:20, and the stale-read rate returned to its baseline shortly afterwards.
|
|
79
|
+
|
|
80
|
+
… (excerpt; the full output also covers impact, cause, and follow-up actions)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Output varies from run to run and between models. The `--format json` envelope
|
|
84
|
+
and the full schema are in
|
|
85
|
+
[docs/readers.md](docs/readers.md#provenance-and-the-json-schema).
|
|
86
|
+
|
|
87
|
+
## Table of contents
|
|
88
|
+
|
|
89
|
+
- [Why not just `ollama run`?](#why-not-just-ollama-run)
|
|
90
|
+
- [Example](#example)
|
|
91
|
+
- [Requirements](#requirements)
|
|
92
|
+
- [Install](#install)
|
|
93
|
+
- [Quick start (Ollama)](#quick-start-ollama)
|
|
94
|
+
- [Common usage](#common-usage)
|
|
95
|
+
- [Supported backends](#supported-backends)
|
|
96
|
+
- [Supported inputs](#supported-inputs)
|
|
97
|
+
- [Configuration](#configuration)
|
|
98
|
+
- [Profiles](#profiles)
|
|
99
|
+
- [Safety: trusted vs. untrusted input](#safety-trusted-vs-untrusted-input)
|
|
100
|
+
- [Verifying a summary](#verifying-a-summary)
|
|
101
|
+
- [Output and the JSON schema](#output-and-the-json-schema)
|
|
102
|
+
- [Local state](#local-state)
|
|
103
|
+
- [Commands](#commands)
|
|
104
|
+
- [Evaluation is mechanical](#evaluation-is-mechanical)
|
|
105
|
+
- [Speed and system load](#speed-and-system-load)
|
|
106
|
+
- [Troubleshooting](#troubleshooting)
|
|
107
|
+
- [When not to use this](#when-not-to-use-this)
|
|
108
|
+
- [How this differs from other tools](#how-this-differs-from-other-tools)
|
|
109
|
+
- [Documentation](#documentation)
|
|
110
|
+
- [Contributing, changelog, license](#contributing-changelog-license)
|
|
111
|
+
|
|
112
|
+
## Requirements
|
|
113
|
+
|
|
114
|
+
- **Python 3.11 or newer.**
|
|
115
|
+
- **A local model server** with at least one model pulled. The default backend
|
|
116
|
+
is [Ollama](https://ollama.com/); see [Quick start](#quick-start-ollama).
|
|
117
|
+
- **A model that fits your machine.** A 3B–8B instruct model is plenty. A Q4
|
|
118
|
+
model needs roughly its download size of RAM; if it fits in GPU memory it is
|
|
119
|
+
fast, and if it spills to CPU it can drop to ~1 token/second. `ollama ps`
|
|
120
|
+
shows the CPU/GPU split and the serving window.
|
|
121
|
+
- **Platforms:** tested in CI on **Linux** and **macOS** (both run the full
|
|
122
|
+
suite); WSL is expected to work. The code uses only POSIX APIs (`flock`,
|
|
123
|
+
signals). Native Windows is unsupported.
|
|
124
|
+
|
|
125
|
+
## Install
|
|
126
|
+
|
|
127
|
+
Not on PyPI yet (planned distribution name: `local-terminal-summarizer`).
|
|
128
|
+
Install from a checkout with
|
|
129
|
+
[pipx](https://pipx.pypa.io/) (isolated, puts `summarize` on your `PATH`):
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
git clone https://github.com/adigaiki/local-terminal-summarizer.git
|
|
133
|
+
cd local-terminal-summarizer
|
|
134
|
+
pipx install . # or: uv tool install .
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
From a virtualenv instead: `python3 -m venv .venv &&
|
|
138
|
+
.venv/bin/python -m pip install -e '.[test]'`. Optional extras (the base
|
|
139
|
+
install stays dependency-free): `pipx install '.[pdf]'` for PDF text and
|
|
140
|
+
`pipx install '.[ocr]'` for OCR (which also needs system `tesseract` and
|
|
141
|
+
Poppler). Uninstall with `pipx uninstall local-terminal-summarizer`.
|
|
142
|
+
|
|
143
|
+
Releases, packaging, and TestPyPI-first publishing are in
|
|
144
|
+
[docs/installation.md](docs/installation.md).
|
|
145
|
+
|
|
146
|
+
## Quick start (Ollama)
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
# 1. Install Ollama (Linux) and start it: https://ollama.com/download
|
|
150
|
+
curl -fsSL https://ollama.com/install.sh | sh
|
|
151
|
+
|
|
152
|
+
# 2. Pull a model that fits your machine.
|
|
153
|
+
ollama pull qwen2.5:3b
|
|
154
|
+
|
|
155
|
+
# 3. Check the whole setup before you trust it.
|
|
156
|
+
summarize doctor
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`summarize doctor` is the first thing to run when anything is wrong. It checks
|
|
160
|
+
configuration, whether the backend is reachable, whether the configured model
|
|
161
|
+
exists, the context window and where it came from, and which optional extras
|
|
162
|
+
are installed. A healthy run looks like this (with the custom 16k variant from
|
|
163
|
+
the note below configured):
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
✓ Backend reachable — http://localhost:11434
|
|
167
|
+
✓ Model available — configured model 'qwen2.5-3b-16k' is installed
|
|
168
|
+
✓ Context window known — 16384 tokens (server)
|
|
169
|
+
! OCR — optional, not installed
|
|
170
|
+
|
|
171
|
+
Everything looks good.
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The full output, and what a failure looks like, are in
|
|
175
|
+
[docs/troubleshooting.md](docs/troubleshooting.md#what-doctor-prints).
|
|
176
|
+
|
|
177
|
+
**The window `doctor` reports is state-dependent.** With the model *unloaded*,
|
|
178
|
+
Ollama answers with the model's **trained maximum** (`/api/show`); once it is
|
|
179
|
+
loaded it serves a smaller window, visible as the `CONTEXT` column of
|
|
180
|
+
`ollama ps`. A plain `ollama pull qwen2.5:3b` reports a trained maximum of
|
|
181
|
+
32768 while a 4 GiB GPU serves 4096. A normal run warms the model with one
|
|
182
|
+
tiny request and re-reads the *served* window before planning, so it does not
|
|
183
|
+
send an over-large prompt; `doctor` never warms anything and shows a `!`
|
|
184
|
+
"Serving context window" line when it only has the trained maximum.
|
|
185
|
+
|
|
186
|
+
To give a model a larger served window, create a derived model (verified):
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
cat > Modelfile <<'EOF'
|
|
190
|
+
FROM qwen2.5:3b
|
|
191
|
+
PARAMETER num_ctx 16384
|
|
192
|
+
EOF
|
|
193
|
+
ollama create qwen2.5-3b-16k -f Modelfile
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Tested against Ollama 0.35.0. The adapter uses `/api/tags` (model list),
|
|
197
|
+
`/api/show` (context), `/api/ps` (serving window of the loaded model), and the
|
|
198
|
+
OpenAI-compatible `/v1/chat/completions` endpoint; `doctor` reports whichever
|
|
199
|
+
of these it can reach.
|
|
200
|
+
|
|
201
|
+
## Common usage
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
summarize article.md
|
|
205
|
+
git diff | summarize --profile code
|
|
206
|
+
summarize report.pdf --profile academic
|
|
207
|
+
summarize scan.pdf --ocr # OCR a scanned PDF (optional extra)
|
|
208
|
+
summarize notes.txt --format json | jq
|
|
209
|
+
summarize notes.txt -o summary.md
|
|
210
|
+
summarize incident.txt --context glossary.md # glossary.md is trusted, incident.txt stays untrusted
|
|
211
|
+
summarize notes.txt --dry-run # plan only; contacts no backend
|
|
212
|
+
summarize --stats article.md # timing and token counts on stderr
|
|
213
|
+
summarize paper.pdf --verify # flag summary numbers/expansions absent from the source
|
|
214
|
+
summarize doctor # environment diagnostics
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`summarize -` reads stdin explicitly. With no input argument and a TTY stdin,
|
|
218
|
+
`summarize` reports an error instead of waiting forever. Omitting `--profile`
|
|
219
|
+
gives the default profile, **`plain`** (see [Profiles](#profiles)).
|
|
220
|
+
|
|
221
|
+
**One input per invocation.** `summarize a.md b.md` is a usage error; batch
|
|
222
|
+
with the shell:
|
|
223
|
+
|
|
224
|
+
```sh
|
|
225
|
+
for f in docs/*.md; do summarize "$f" -o "${f%.md}.summary.md"; done
|
|
226
|
+
# or: printf '%s\0' docs/*.md | xargs -0 -n1 summarize
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Supported backends
|
|
230
|
+
|
|
231
|
+
There are no cloud providers. A backend is a local adapter:
|
|
232
|
+
|
|
233
|
+
| `backend` | Default endpoint | Notes |
|
|
234
|
+
| --- | --- | --- |
|
|
235
|
+
| `ollama` | `http://localhost:11434` | native model/context discovery |
|
|
236
|
+
| `openai-compatible` (alias `openai`) | configured | any local OpenAI Chat Completions server — this covers vLLM, text-generation-webui, and other `/v1` servers |
|
|
237
|
+
| `llama.cpp` (alias `llama-cpp`) | `http://localhost:8080` | llama.cpp server |
|
|
238
|
+
| `lmstudio` (alias `lm-studio`) | `http://localhost:1234` | LM Studio |
|
|
239
|
+
|
|
240
|
+
Selecting a backend without setting an endpoint uses its loopback default.
|
|
241
|
+
Capability negotiation (streaming, structured JSON, reasoning control, context
|
|
242
|
+
length) is described in [docs/backends.md](docs/backends.md).
|
|
243
|
+
|
|
244
|
+
## Supported inputs
|
|
245
|
+
|
|
246
|
+
| Input | Reader | Notes |
|
|
247
|
+
| --- | --- | --- |
|
|
248
|
+
| `-` or no argument | stdin | bounded; a TTY with no input is an error |
|
|
249
|
+
| `.txt`, `.log`, unknown text extensions | text | any file whose content is valid text |
|
|
250
|
+
| `.md`, `.markdown`, `.mdown`, `.mkd` | markdown | preserved verbatim; never rendered or fetched |
|
|
251
|
+
| `.py`, `.rs`, `.c`, `.go`, `.toml`, … | code | passed through unmodified |
|
|
252
|
+
| `.pdf` | PDF (optional extra) | page-aware; scanned PDFs need `--ocr` |
|
|
253
|
+
|
|
254
|
+
There is **no DOCX/HTML/EPUB reader** — convert those to text first. NUL bytes
|
|
255
|
+
are rejected as binary. Details: [docs/readers.md](docs/readers.md).
|
|
256
|
+
|
|
257
|
+
## Configuration
|
|
258
|
+
|
|
259
|
+
Configuration is layered (later wins): CLI → `SUMMARIZER_*` environment →
|
|
260
|
+
project `./summarizer.toml` (opt-in) → user config → built-in defaults. The
|
|
261
|
+
user config lives at `$XDG_CONFIG_HOME/summarizer/config.toml` or
|
|
262
|
+
`~/.config/summarizer/config.toml`:
|
|
263
|
+
|
|
264
|
+
```toml
|
|
265
|
+
[engine]
|
|
266
|
+
backend = "ollama"
|
|
267
|
+
endpoint = "http://localhost:11434"
|
|
268
|
+
model = "qwen2.5:3b"
|
|
269
|
+
timeout_seconds = 60
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Or override for one run: `SUMMARIZER_MODEL=qwen3:8b summarize article.md`.
|
|
273
|
+
A project `./summarizer.toml` is read only when you opt in
|
|
274
|
+
(`SUMMARIZER_PROJECT_CONFIG=1`). Every key, the precedence rules, the
|
|
275
|
+
environment variables, and a full example are in
|
|
276
|
+
[docs/configuration.md](docs/configuration.md) and
|
|
277
|
+
[config.example.toml](config.example.toml).
|
|
278
|
+
|
|
279
|
+
## Profiles
|
|
280
|
+
|
|
281
|
+
Built-in profiles: **`plain` (the default)**, `code`, `academic`, and
|
|
282
|
+
`meeting`. Add or override one by dropping `NAME.md` into
|
|
283
|
+
`~/.config/summarizer/prompts/`. `summarize profiles` lists them. See
|
|
284
|
+
[docs/configuration.md](docs/configuration.md#profiles).
|
|
285
|
+
|
|
286
|
+
## Safety: trusted vs. untrusted input
|
|
287
|
+
|
|
288
|
+
Document text is untrusted: each request wraps it in a fresh random boundary
|
|
289
|
+
checked against the content first, and map-reduce interim summaries get the
|
|
290
|
+
same treatment. `--context FILE` is the one input **you** mark as trusted — it
|
|
291
|
+
is kept in a separate labelled section, never mixed with the document.
|
|
292
|
+
|
|
293
|
+
These are structural guarantees, not a solved problem. See
|
|
294
|
+
[SECURITY.md](SECURITY.md) (threat model and private vulnerability reporting —
|
|
295
|
+
hence two security docs), [docs/design.md](docs/design.md), and
|
|
296
|
+
[docs/security.md](docs/security.md).
|
|
297
|
+
|
|
298
|
+
## Verifying a summary
|
|
299
|
+
|
|
300
|
+
`--verify` reports numbers and `ACRONYM (expansion)` phrases in the summary
|
|
301
|
+
that do not appear in the source: grep, not a judge model, and advisory (exit
|
|
302
|
+
status unchanged). It does not catch a real number on the wrong label or
|
|
303
|
+
non-numeric fabrication. Details: [docs/verify.md](docs/verify.md).
|
|
304
|
+
|
|
305
|
+
## Output and the JSON schema
|
|
306
|
+
|
|
307
|
+
Default output is Markdown on stdout. `--format json` emits a stable envelope
|
|
308
|
+
(`document`, `profile`, `engine`, `strategy`, `chunks`, `summary`,
|
|
309
|
+
`chunk_provenance`, `duration_seconds`; `--verify` adds `verification`, and a
|
|
310
|
+
detected truncation adds `truncated`). Schema and example:
|
|
311
|
+
[docs/readers.md](docs/readers.md#provenance-and-the-json-schema). Provenance
|
|
312
|
+
never leaks into plain/Markdown output and never enters trusted prompt
|
|
313
|
+
instructions.
|
|
314
|
+
|
|
315
|
+
## Local state
|
|
316
|
+
|
|
317
|
+
`summarize` writes nothing by default. Two optional, plain-file stores:
|
|
318
|
+
the **cache** (`summarize cache status|path|clear`; off by default; derived
|
|
319
|
+
summaries only, keyed by content hash + model/profile, under
|
|
320
|
+
`~/.cache/summarizer`, never document text or paths) and **sessions**
|
|
321
|
+
(`summarize session start|end|status`; pointers only, recorded only when
|
|
322
|
+
explicitly selected). See [docs/performance.md](docs/performance.md) and
|
|
323
|
+
[docs/sessions.md](docs/sessions.md).
|
|
324
|
+
|
|
325
|
+
## Commands
|
|
326
|
+
|
|
327
|
+
| Command | Purpose |
|
|
328
|
+
| --- | --- |
|
|
329
|
+
| `summarize FILE` / `-` | summarize a file or stdin |
|
|
330
|
+
| `summarize doctor` | installation, configuration, backend, model, and cache diagnostics |
|
|
331
|
+
| `summarize models` | models the configured local backend reports |
|
|
332
|
+
| `summarize profiles` | installed profiles and their prompt identity |
|
|
333
|
+
| `summarize config show\|path\|validate` | resolved configuration and locations |
|
|
334
|
+
| `summarize cache status\|path\|clear` | inspect and clear the optional cache (off by default) |
|
|
335
|
+
| `summarize evaluate` | run the local model/backend evaluation corpus |
|
|
336
|
+
| `summarize session start\|end\|status` | group runs under a named local record |
|
|
337
|
+
| `summarize completions bash\|zsh\|fish` | shell completion script |
|
|
338
|
+
|
|
339
|
+
Every option (including `--verify`, `--context`, `--dry-run`, `--stats`,
|
|
340
|
+
`--check-update`, `-o`) and the exit status are in [docs/cli.md](docs/cli.md).
|
|
341
|
+
|
|
342
|
+
## Evaluation is mechanical
|
|
343
|
+
|
|
344
|
+
`summarize evaluate` applies **mechanical** checks (valid format/JSON, sentinel
|
|
345
|
+
retention, marker absence, strategy, bounds) — not objective quality scores,
|
|
346
|
+
and no judge model. Whether summaries are actually good is addressed, in part
|
|
347
|
+
and with caveats, in [docs/quality.md](docs/quality.md); the harness is
|
|
348
|
+
[docs/evaluation.md](docs/evaluation.md).
|
|
349
|
+
|
|
350
|
+
## Speed and system load
|
|
351
|
+
|
|
352
|
+
Latency is dominated by whether the model fits your GPU. Measured on a laptop
|
|
353
|
+
with a 4 GiB-VRAM GPU: `qwen2.5-3b-16k` (GPU-resident) summarized a
|
|
354
|
+
14,000-token paper directly in ~21 s; `qwen3:8b`, which does not fit and spills
|
|
355
|
+
to CPU, runs ~1.2 tok/s. Keeping a document in one direct call is usually
|
|
356
|
+
faster and more coherent than map-reduce. The CLI is GPU-agnostic; the numbers
|
|
357
|
+
and the full picture are in [docs/performance.md](docs/performance.md).
|
|
358
|
+
|
|
359
|
+
## Troubleshooting
|
|
360
|
+
|
|
361
|
+
- **`connection refused` / `Backend reachable ✗`** — start your model server
|
|
362
|
+
(`ollama serve`, or the llama.cpp/LM Studio server) and confirm the endpoint
|
|
363
|
+
in `summarize doctor`. URL, model, and backend come from config or
|
|
364
|
+
`SUMMARIZER_ENDPOINT` / `SUMMARIZER_MODEL` / `SUMMARIZER_BACKEND`.
|
|
365
|
+
- **`the model is not available on the endpoint`** — pull it (`ollama pull
|
|
366
|
+
qwen2.5:3b`) or set `[engine] model` to an installed name. `summarize models`
|
|
367
|
+
lists what the backend reports.
|
|
368
|
+
- **`the backend processed ... prompt tokens ... truncated`** (or
|
|
369
|
+
`"truncated": true`) — the prompt was likely truncated; see
|
|
370
|
+
[docs/troubleshooting.md](docs/troubleshooting.md) for the fix and the
|
|
371
|
+
streaming caveat.
|
|
372
|
+
|
|
373
|
+
Everything else — PDFs and OCR, timeouts, context overflow, binary files, the
|
|
374
|
+
no-input error — is in [docs/troubleshooting.md](docs/troubleshooting.md).
|
|
375
|
+
|
|
376
|
+
## When not to use this
|
|
377
|
+
|
|
378
|
+
`summarize` produces a probabilistic summary from a local model. It is the
|
|
379
|
+
wrong tool when being wrong is expensive:
|
|
380
|
+
|
|
381
|
+
- **Where a wrong number or claim matters** (legal, medical, financial,
|
|
382
|
+
safety-critical). `--verify` is a grep, not fact-checking, and a clean report
|
|
383
|
+
is not proof; treat `--format json` as untrusted at any automation boundary.
|
|
384
|
+
- **Wide tables on a small model.** Table structuring fixes column
|
|
385
|
+
*assignment*, not judgment; a 3B model can still garble an 8-column table.
|
|
386
|
+
- **Documents needing reasoning across the whole text.** Map-reduce summarizes
|
|
387
|
+
chunks independently, so a term defined in one part and used far away can be
|
|
388
|
+
lost; prefer a model whose context holds the document in one call
|
|
389
|
+
(`--dry-run` shows the plan, `--strict` refuses to chunk).
|
|
390
|
+
- **Content you would not send to a local model.** The tool does not redact;
|
|
391
|
+
the model sees the document.
|
|
392
|
+
|
|
393
|
+
The honest state of summary quality is in [docs/quality.md](docs/quality.md).
|
|
394
|
+
|
|
395
|
+
## How this differs from other tools
|
|
396
|
+
|
|
397
|
+
Not better — different. `summarize` is a summarization pipeline with an
|
|
398
|
+
explicit trust boundary, strict context budgeting, and validated output;
|
|
399
|
+
general-purpose tools may fit better if you need chat, plugins, or cloud
|
|
400
|
+
models. Claims below are from each project's own README.
|
|
401
|
+
|
|
402
|
+
| Tool | What it is | How `summarize` differs |
|
|
403
|
+
| --- | --- | --- |
|
|
404
|
+
| `ollama run` | One prompt against a local model | No chunking, budgeting, trust boundary, or output validation |
|
|
405
|
+
| [`llm`](https://github.com/simonw/llm) | CLI/Python for many models, remote and local (Ollama via a plugin), with SQLite logging, embeddings, schemas and tools | General-purpose; long-document summarization is on you |
|
|
406
|
+
| [`fabric`](https://github.com/danielmiessler/fabric) | A library of reusable prompts ("Patterns", including `summarize`) over many providers, with a REST API | Prompt-library-first; `summarize` is pipeline-first and local-only |
|
|
407
|
+
|
|
408
|
+
## Documentation
|
|
409
|
+
|
|
410
|
+
The README is the orientation; the detail lives in [docs/](docs/index.md):
|
|
411
|
+
[Installation](docs/installation.md) · [Configuration](docs/configuration.md) ·
|
|
412
|
+
[CLI](docs/cli.md) · [Backends](docs/backends.md) ·
|
|
413
|
+
[Performance and local state](docs/performance.md) · [Readers](docs/readers.md) ·
|
|
414
|
+
[Claim verification](docs/verify.md) · [Troubleshooting](docs/troubleshooting.md) ·
|
|
415
|
+
[Sessions](docs/sessions.md) · [Design and safety](docs/design.md) ·
|
|
416
|
+
[Security and privacy](docs/security.md) · [Evaluation](docs/evaluation.md) ·
|
|
417
|
+
[Summary quality](docs/quality.md).
|
|
418
|
+
|
|
419
|
+
## Contributing, changelog, license
|
|
420
|
+
|
|
421
|
+
- Contributing and the repository safety guard: [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
422
|
+
- Version history: [CHANGELOG.md](CHANGELOG.md).
|
|
423
|
+
- Design notes and deferred work: [ROADMAP.md](ROADMAP.md).
|
|
424
|
+
- License: MIT — see [LICENSE](LICENSE).
|