Local AI reading assistant for researchers. Zotero + arXiv → two-tier LLM analysis → Obsidian notes + research map — all from the command line.
rsp monitors your Zotero library (and optionally auto-fetches from arXiv), runs a two-tier relevance screen, calls an LLM to analyse papers against your personal question pack, and writes structured Obsidian Markdown notes organised by date — with a live research-map canvas on the side.
Zero manual copy-paste. One command per day.
flowchart TD
subgraph in["📥 Input"]
A["arXiv\nsources.yaml"] -->|"rsp fetch"| S
Z["Zotero library\nmanual additions"] -->|"rsp ingest"| S
end
S[("SQLite\nstate.sqlite\nstatus: pending")]
S -->|"rsp process"| KW
subgraph analysis["🤖 Two-tier LLM analysis"]
KW{{"Keyword screen\nvs research_profile"}}
KW -->|"< 2 matches"| SUM["SUMMARY tier\nabstract only\n~10× cheaper"]
KW -->|"≥ 2 matches"| FULL["FULL tier\nfull text · PDF fallback\nper-question analysis"]
end
SUM --> MD1["📄 summary note\npapers/YYYY-MM-DD/KEY.md"]
FULL --> MD2["📄 structured note\npapers/YYYY-MM-DD/KEY.md"]
FULL --> GR["📊 research_graph.json"]
FULL --> CV["🗺️ Research-Relevance.canvas"]
| Group | Commands | Purpose |
|---|---|---|
| Ingest | fetch, ingest |
Pull papers into state |
| Analyse | process |
Run LLM, write notes |
| Maintain | audit, duplicates, clean, organize-papers |
Keep vault tidy |
| Enrich | attach-pdfs, classify |
Add PDFs, tag Zotero collections |
| Explore | search, doctor |
Inspect state & debug |
- Two-tier processing — summary notes cost ~10× fewer tokens; full notes answer your custom question pack with evidence references
- arXiv auto-fetch — define keyword + category sources;
rsp fetchruns them and writes directly to Zotero - Incremental & idempotent — content-hash deduplication means re-running is always safe
- Duplicate detection — DOI / arXiv ID / title fingerprint;
rsp cleanmerges children and deletes duplicates interactively - Date-organised vault — notes land in
papers/YYYY-MM-DD/{zotero_key}.md;rsp organize-papersmigrates older flat files - Living research map — full-tier papers grow a JSON Canvas graph in Obsidian
- Pluggable LLM backends — local (Ollama, LM Studio, llama.cpp), cloud open-source (Groq, Together.ai, Fireworks, OpenRouter), or cloud proprietary (OpenAI, Anthropic); one config field to switch
- Python 3.9+
- Zotero account + Web API key
- Obsidian vault (any local path)
- An LLM backend: local (Ollama/LM Studio/llama.cpp — free, no key) or a cloud API key (Groq, Together.ai, Fireworks, OpenRouter, OpenAI, Anthropic)
git clone https://github.com/YOUR_USERNAME/read-summarize-papers
cd read-summarize-papers
pip install -e .cp config/config.example.yaml config/config.yaml
cp config/question_pack.example.yaml config/question_pack.yamlEdit config/config.yaml:
vault:
notes_dir: /path/to/your/obsidian/vault/notes
paper_notes_subdir: papers # notes_dir/papers/YYYY-MM-DD/{key}.md
date_subfolders: true # organise by processing date
canvas:
output_path: /path/to/your/obsidian/vault/Research-Relevance.canvas
llm:
provider: ollama # see provider table below
model: llama3.2 # leave blank to use provider default
temperature: 0.2
max_output_tokens: 4096LLM provider options — set provider: to any of the following:
provider |
Backend | Key needed? | Default base_url |
|---|---|---|---|
ollama |
Ollama (local) | No | http://localhost:11434/v1 |
lm_studio |
LM Studio (local) | No | http://localhost:1234/v1 |
llamacpp |
llama.cpp server (local) | No | http://localhost:8080/v1 |
groq |
Groq | GROQ_API_KEY |
auto |
together |
Together.ai | TOGETHER_API_KEY |
auto |
fireworks |
Fireworks.ai | FIREWORKS_API_KEY |
auto |
openrouter |
OpenRouter | OPENROUTER_API_KEY |
auto |
openai |
OpenAI | OPENAI_API_KEY |
auto |
anthropic |
Anthropic | ANTHROPIC_API_KEY |
auto |
openai_compatible |
Any custom endpoint | OPENAI_API_KEY or LLM_API_KEY |
https://api.openai.com/v1 |
Set LLM_API_KEY as a generic fallback when no provider-specific key is configured. Override base_url: in config to point any provider at a different endpoint.
Edit config/question_pack.yaml — set your research_profile and define the questions the LLM should answer for every full-tier paper.
Edit config/sources.yaml — define the arXiv keyword queries to auto-fetch from.
export ZOTERO_LIBRARY_ID="your_numeric_user_id" # zotero.org/settings/keys
export ZOTERO_API_KEY="your_zotero_api_key"
# Set the key for whichever provider you chose in config.yaml:
export GROQ_API_KEY="your_groq_key" # provider: groq
export TOGETHER_API_KEY="your_together_key" # provider: together
export OPENROUTER_API_KEY="your_or_key" # provider: openrouter
export ANTHROPIC_API_KEY="your_anthropic_key"# provider: anthropic
export OPENAI_API_KEY="your_openai_key" # provider: openai / openai_compatible
# Local providers (ollama, lm_studio, llamacpp) need no key.
# LLM_API_KEY works as a generic fallback for any provider.Verify everything is wired up:
rsp doctor --config config/config.yaml# Pull new papers from arXiv → Zotero → pending state
rsp fetch --config config/config.yaml
# (Or sync what you added manually in Zotero)
rsp ingest --config config/config.yaml
# Analyse all pending papers and write notes
rsp process --config config/config.yaml --all-pendingInterrupt with Ctrl+C at any time — completed papers are safe and the interrupted one retries next run.
Validates paths, Zotero API, LLM credentials, and pymupdf. Run first after any config change.
rsp doctor --config config/config.yamlQueries arXiv sources from config/sources.yaml, adds new papers to Zotero, and marks them pending.
rsp fetch --config config/config.yaml # all sources
rsp fetch --config config/config.yaml --source ebsd_kikuchi # one source
rsp fetch --config config/config.yaml --dry-run # preview onlyconfig/sources.yaml format:
sources:
- id: ebsd_kikuchi
label: "EBSD & Kikuchi patterns"
provider: arxiv
priority: 1 # 1=high, 2=medium, 3=low
query: "ti:EBSD OR ti:kikuchi"
categories: [cond-mat.mtrl-sci]
max_results: 15Incremental sync from Zotero (based on Last-Modified-Version). Marks new or changed items as pending.
rsp ingest --config config/config.yaml
rsp ingest --config config/config.yaml --no-dedupRuns the two-tier pipeline for pending or specified items.
rsp process --config config/config.yaml --all-pending
rsp process --config config/config.yaml --key ABC123XY
rsp process --config config/config.yaml --all-pending --force # re-run changed items
rsp process --config config/config.yaml --key ABC123XY --force-full # force full analysisDownloads PDFs from arXiv and attaches them to Zotero items so you can open them in the Zotero app.
rsp attach-pdfs --config config/config.yaml --dry-run # preview
rsp attach-pdfs --config config/config.yaml # attach all missing
rsp attach-pdfs --config config/config.yaml --source ebsd_kikuchi
rsp attach-pdfs --config config/config.yaml --warn-at 70Stops automatically if Zotero storage is full (free plan: 300 MB).
Finds Zotero items that are missing Obsidian notes and reports their state.
rsp audit --config config/config.yaml # report only
rsp audit --config config/config.yaml --mark-pending # queue gaps → then rsp process| Category | Meaning |
|---|---|
| Not ingested | In Zotero, never seen by rsp ingest |
| Pending | Queued but rsp process hasn't run yet |
| State=done, note missing | Note was moved or deleted |
| Error | Previous process run failed |
Scans for duplicate papers using DOI, arXiv ID, ISBN, and title/author/year fingerprint.
rsp duplicates --config config/config.yaml # report only
rsp duplicates --config config/config.yaml --mark # mark in state (skipped by process)Interactively deletes duplicate Zotero items — moves their children to the canonical, deletes the duplicate, removes the vault note.
rsp clean --config config/config.yaml # interactive: y / s / q per group
rsp clean --config config/config.yaml --dry-run # preview onlyAssigns Zotero items to collections based on keyword-match score against sources.yaml queries.
rsp classify --config config/config.yaml --dry-run # preview
rsp classify --config config/config.yaml
rsp classify --config config/config.yaml --verbose
rsp classify --config config/config.yaml --min-score 2Moves all .md files into papers/YYYY-MM-DD/{zotero_key}.md date subfolders. Useful after migrating from a flat note layout or changing the naming convention. Updates SQLite state with the new paths.
rsp organize-papers --config config/config.yaml --dry-run # preview
rsp organize-papers --config config/config.yaml # applySearches your Zotero library by title and shows current processing status.
rsp search "kikuchi patterns" --config config/config.yaml
rsp search "hydrogen reduction" -n 5ABC123XY [journalArticle 2024]
Learning metal microstructural heterogeneity through spatial mapping
status: done [full]
| File | Description |
|---|---|
<vault>/papers/YYYY-MM-DD/{zotero_key}.md |
One Obsidian note per paper, grouped by processing date |
<canvas.output_path> |
JSON Canvas research map (updated for every full-tier paper) |
data/research_graph.json |
Persistent graph of papers and question nodes |
data/items/<key>.analysis.json |
Raw LLM JSON response for full-tier papers |
data/state.sqlite |
Processing state, content hashes, tier, duplicate marks, run log |
Each Zotero key maps to exactly one note file (path stored in SQLite). Re-processing updates the same file in place.
Full-tier note:
---
title: ...
zotero_key: ABC123XY
doi: 10.xxxx/...
pack_version: "2026-01-01"
model: claude-sonnet-4-6
processed_at: "2026-07-17T..."
tier: full
---
## Overview
## Methods
## Variables
## Datasets / data
## Comparison axes
## Claims (paper-stated)
## Limitations
## Relevance to question pack
### `q_relevance`
### `q_transferable_method`
...
## Graph
Summary-tier note:
---
tier: summary
matched_topics: [topic_a]
---
> [!note] Low relevance — 1 topic match(es): topic_a
## Overview
## Methods
## Relevance note
config/question_pack.yaml defines your research profile (domain, subtopics, keywords) and the questions the LLM answers for every full-tier paper. Customise it to match your research focus — the questions drive the note structure and the research graph edges.
| Priority | Default question | What it extracts |
|---|---|---|
| 1 | q_problem_fit |
Alignment with your core research question |
| 2 | q_methods_transfer |
Transferable methods / experimental designs |
| 2 | q_variables_evidence |
Key variables, assumptions, evidence chains |
MIT — see LICENSE.