Skip to main content

Scan a Zotero collection, summarize each paper's PDF, and write the summary back as a note (LangGraph + DeepSeek).

Project description

zotery

PyPI Python License: MIT

Scan a Zotero collection, read each paper's attached PDF, generate a structured summary with an LLM (DeepSeek, Google Gemini, or a local Ollama model), and write that summary back into Zotero as a child note on the paper.

Install it as zotery, run it as zotery. (The Python module is zotero_summarizer.)

Every summary contains four sections:

  • Motivation & Main Problem
  • Key Findings
  • Methodology
  • Future Work

The pipeline is orchestrated with LangGraph:

START → load_items → process_paper → summarize → write_note → END
                          ↑__________________________|   (loops per paper)

load_items scans the collection · process_paper finds + downloads + extracts the PDF · summarize calls the LLM for a structured PaperSummary · write_note renders it to HTML and pushes it to Zotero.

How it connects to Zotero

It uses pyzotero as the connector, which speaks to both Zotero APIs:

  • Web API (ZOTERO_LOCAL=false) — the Zotero cloud library, via an API key. Required to write notes back, because Zotero's local API is read-only. Needs Zotero Sync turned on (so the library exists on zotero.org) and a write-enabled key. PDFs are still read locally from disk (see ZOTERO_STORAGE_DIR), so you do not need Zotero file sync.
  • Local API (ZOTERO_LOCAL=true) — the running Zotero 7 desktop app. No API key, reads PDFs straight off disk. Good for read-only previews (--dry-run), but cannot write notes (the local API rejects writes).

Prefer an MCP server? The summarization core (summarizer.py + graph.py) is independent of how items are fetched, so you can swap zotero_client.py for a Zotero MCP client. pyzotero is the default because it needs no extra service and reads local PDFs directly.

Install

Requires Python 3.10+ (the LangChain stack no longer supports 3.9).

From PyPI (current version 0.0.2):

pip install zotery
# or, with uv:
uv tool install zotery      # installs the `zotery` command globally

This puts the zotery command on your PATH. Then create a config file from the template and edit it (see below):

curl -O https://raw.githubusercontent.com/mkassaf/zotero-summarizer/main/.env.example
mv .env.example .env
# edit .env, or export the variables in your shell instead

.env is optional — every setting can also come from real environment variables or CLI flags. See Configuration below.

Install from source (for development)
git clone https://github.com/mkassaf/zotero-summarizer.git
cd zotero-summarizer

python3 -m venv .venv
source .venv/bin/activate
pip install -e .            # or: uv sync

cp .env.example .env
# then edit .env  (see below)

Configure Zotero (.env)

To write notes you need the Web API:

  1. Turn on sync: Zotero → Settings → Sync → log in. This puts your library metadata on zotero.org so the API can see it. (File sync is optional — PDFs are read locally.)
  2. Create a write-enabled key: https://www.zotero.org/settings/keys/new — check "Allow library access" and "Allow write access".
ZOTERO_LOCAL=false
ZOTERO_LIBRARY_TYPE=user
ZOTERO_LIBRARY_ID=your-username      # username OR numeric userID both work
ZOTERO_API_KEY=your-write-key

# Optional: where PDFs live on disk. Auto-detected to ~/Zotero/storage if unset.
# ZOTERO_STORAGE_DIR=/Users/you/Zotero/storage

ZOTERO_LIBRARY_ID accepts your username — it's resolved to the numeric id the Web API requires, using your API key. The numeric id works too.

Configure the LLM

Pick one provider:

Provider Settings Standard key env var Notes
DeepSeek (default) LLM_PROVIDER=deepseek
LLM_MODEL=deepseek-chat
DEEPSEEK_API_KEY Key from https://platform.deepseek.com.
Google Gemini LLM_PROVIDER=google
LLM_MODEL=gemini-2.5-flash
GOOGLE_API_KEY Fast, recommended for big runs.
OpenAI-compatible LLM_PROVIDER=openai
LLM_MODEL=gpt-4o-mini
LLM_BASE_URL=...
OPENAI_API_KEY OpenAI, Together, vLLM, etc.
Ollama (local, free) LLM_PROVIDER=ollama
LLM_MODEL=qwen3:8b
(none) Needs Ollama running + ollama pull qwen3:8b. Native JSON-schema output. Slower per paper.

Where the API key comes from

The LLM key is resolved in this order — first match wins:

  1. CLI flag--llm-api-key sk-... (highest precedence; never written to disk).
  2. Generic overrideLLM_API_KEY (works for any provider).
  3. Provider's standard env varDEEPSEEK_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY (see the table). Use these if you already export your keys globally in your shell — nothing extra to configure here.

The Zotero key works the same way: --zotero-api-key overrides ZOTERO_API_KEY.

# Example: provider + key entirely from the command line, no .env needed
zotery "Literature Review" \
  --llm-api-key "$MY_KEY" --zotero-api-key "$ZKEY"

# Example: rely on a globally-exported key (e.g. in ~/.zshrc)
export OPENAI_API_KEY=sk-...
LLM_PROVIDER=openai LLM_MODEL=gpt-4o-mini zotery "Literature Review"

Ollama tip: the default base URL is http://127.0.0.1:11434. Use 127.0.0.1, not localhostlocalhost can resolve to IPv6/Docker and miss your models.

Usage

After pip install zotery, use the zotery command (or, from a source checkout, python -m zotero_summarizer):

# Summarize every paper in a collection (by name or 8-char key)
zotery "Literature Review"

# Preview first: generate + print summaries, write nothing
zotery "Literature Review" --dry-run --limit 3

# Re-summarize papers that already have an AI note
zotery ABCD1234 --force

Override the provider per-run without editing .env:

LLM_PROVIDER=google LLM_MODEL=gemini-2.5-flash zotery "Literature Review"

Flags:

flag meaning
--limit N only process the first N papers
--dry-run generate and print summaries, but don't write notes to Zotero
--force re-summarize even if an AI summary note already exists
--llm-api-key KEY LLM API key; overrides LLM_API_KEY and the provider env var
--zotero-api-key KEY Zotero Web API key; overrides ZOTERO_API_KEY

Re-runs are idempotent: papers that already have an AI summary note are skipped unless you pass --force.

Run locally & free with Ollama

Ollama runs an LLM on your own machine — no API key, no per-token cost, nothing leaves your computer. Good for private libraries or large runs you don't want to pay for. It's slower per paper and quality depends on the model you pick.

1. Install Ollama and pull a model (a ~5 GB instruct model is a good start):

# Install: https://ollama.com/download  (or `brew install ollama` on macOS)
ollama serve            # start the server (skip if the desktop app is running)
ollama pull qwen3:8b    # download the model

2. Point zotery at Ollama and run:

# One-off, all on the command line (no .env edits, no key needed):
LLM_PROVIDER=ollama LLM_MODEL=qwen3:8b zotery "Literature Review"

# Safe first run: print summaries without writing notes to Zotero
LLM_PROVIDER=ollama LLM_MODEL=qwen3:8b zotery "Literature Review" --dry-run --limit 3

Or set it once in .env and just run zotery "Literature Review":

LLM_PROVIDER=ollama
LLM_MODEL=qwen3:8b
# LLM_BASE_URL=http://127.0.0.1:11434   # optional; this is the default

Notes:

  • No --llm-api-key / LLM_API_KEY needed — Ollama is keyless. (You still need a Zotero Web API key to write notes; use --dry-run to skip that.)
  • Use any model you've pulled — e.g. LLM_MODEL=llama3.1:8b, LLM_MODEL=mistral. Bigger models give better summaries but run slower.
  • If Ollama runs on another host/port, set LLM_BASE_URL (e.g. http://192.168.1.10:11434). Use 127.0.0.1, not localhostlocalhost can resolve to IPv6/Docker and miss your local models.

How it works

file responsibility
config.py load .env; build the LLM (DeepSeek / Google / Ollama / OpenAI)
zotero_client.py list collection papers, find/download PDFs, write notes
pdf_utils.py extract text from PDF bytes
summarizer.py prompt + structured (PaperSummary) output + note HTML
graph.py the LangGraph pipeline
cli.py argument parsing and the run report

Notes & limits

  • Writing requires the Web API. The local API is read-only; use it only for reading/--dry-run.
  • Scanned/image-only PDFs yield no text and are skipped (no OCR).
  • Long PDFs are truncated to MAX_PDF_CHARS (default 48k chars) to stay within the model's context window.
  • PDFs are fetched via the API, falling back to ZOTERO_STORAGE_DIR (the local storage/ folder, auto-detected at ~/Zotero/storage). This means Web API mode works without Zotero file sync.
  • Never commit your .env — it holds your API keys (it's already in .gitignore).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zotery-0.0.2.tar.gz (19.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

zotery-0.0.2-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file zotery-0.0.2.tar.gz.

File metadata

  • Download URL: zotery-0.0.2.tar.gz
  • Upload date:
  • Size: 19.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for zotery-0.0.2.tar.gz
Algorithm Hash digest
SHA256 d51ef97f226960a724a1e3492b512aadb4f8e49fb090ade66cb78e0a95ab0d50
MD5 dc49ea1d6d9c84e6097697719bf85bab
BLAKE2b-256 ef56a744adbfa3b5607649c8b13018854956e40ab3444d64bbe36742d5c8ddc4

See more details on using hashes here.

Provenance

The following attestation bundles were made for zotery-0.0.2.tar.gz:

Publisher: publish.yml on mkassaf/zotero-summarizer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zotery-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: zotery-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 17.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for zotery-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 627645a8db090fdc5ea174cbd0a5f8292ff360fdae0b1303d8f88d311dab02e8
MD5 fe4fb4cd3cfa83a4d5f40f5b4554555f
BLAKE2b-256 00961826dfb556ddf685d990c0eb46b2dc9bd77e199bd48dbdaf7d4f13e01bb2

See more details on using hashes here.

Provenance

The following attestation bundles were made for zotery-0.0.2-py3-none-any.whl:

Publisher: publish.yml on mkassaf/zotero-summarizer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page