Skip to content

Repository files navigation

BibSummary

Summarize the linked PDF of the selected publication(s) in BibDesk and attach the summary back to the entry as a linked Markdown file. Works with Anthropic, OpenAI, or a local Ollama model — chosen in a small config file.

The AppleScript is a thin wrapper: it grabs the selected entry's cite key, title, and linked PDF, hands them to a shell helper that does the text extraction and LLM call, then links the resulting .md back onto the entry.

See CHANGELOG.md for release history.

Requirements

  • macOS with BibDesk
  • curl and jq — install jq with brew install jq
  • A PDF text extractor, either:
    • pdftotext (recommended): brew install poppler, or
    • Xcode command line tools (provides swift): xcode-select --install
  • For Anthropic/OpenAI: an API key. For Ollama: a running local instance.

Install

Quick install (recommended)

From the project folder:

chmod +x install.sh   # only needed once (downloads don't preserve the execute bit)
./install.sh

This compiles the AppleScript into BibDesk's Scripts folder, places the helper in a sibling support folder, creates your config (without overwriting an existing one), and checks dependencies. Then edit the config it prints and you're set.

Manual install

The helper and the compiled script go in two different places — this matters, because BibDesk lists every file in its Scripts folder as a menu entry, so only the compiled .scpt belongs there.

  1. Compile the AppleScript into BibDesk's Scripts folder:

    mkdir -p ~/Library/Application\ Support/BibDesk/Scripts
    osacompile -o ~/Library/Application\ Support/BibDesk/Scripts/BibSummary.scpt bibsummary.applescript

    (Or open bibsummary.applescript in Script Editor and save it as a Script into that folder.)

  2. Put the helper in the sibling support folder (NOT the Scripts folder):

    mkdir -p ~/Library/Application\ Support/BibSummary
    cp bibsummary.sh ~/Library/Application\ Support/BibSummary/
    chmod +x ~/Library/Application\ Support/BibSummary/bibsummary.sh
  3. Create your config:

    mkdir -p ~/.config/bibsummary
    cp bibsummary.conf.example ~/.config/bibsummary/config.conf
    chmod 600 ~/.config/bibsummary/config.conf

    Then edit ~/.config/bibsummary/config.conf — set PROVIDER, the matching model, and your API key.

Usage

  1. Select one or more publications in BibDesk (each must have a linked PDF).
  2. Run BibSummary from BibDesk's Scripts menu (the page-shaped icon). You can assign it a keyboard shortcut there too.
  3. A start dialog appears: Run uses your default config, Config… lets you pick an alternate profile (see Config profiles). Run is the default button, so pressing Return just goes.
  4. A notification reports progress. Each summary is written next to its PDF as <citekey>-summary.md and linked onto the entry, so it shows up in the entry's file list.

To skip the start dialog entirely and always use the default config, set property promptForProfile : false near the top of bibsummary.applescript and re-run ./install.sh.

Choosing a provider

Set PROVIDER in the config to one of:

Provider Needs Notes
anthropic ANTHROPIC_API_KEY Cloud. Set ANTHROPIC_MODEL (e.g. claude-opus-4-8).
openai OPENAI_API_KEY Cloud. Set OPENAI_MODEL to a model your key can access.
ollama Ollama running locally Fully local & private — no key, no data leaves your machine.

For Ollama: install from ollama.com, pull a model (ollama pull llama3.1), and make sure it's running. Point OLLAMA_HOST at it if it's not on the default http://localhost:11434.

Config profiles

You can keep more than one config and pick between them at run time — handy for, say, a fast/cheap default and a separate profile that sends the whole paper, or one that runs locally on Ollama.

Each profile is just another *.conf file in ~/.config/bibsummary/. Copy your config and edit the bits that differ:

cd ~/.config/bibsummary
cp config.conf config-fullpaper.conf   # e.g. MAX_CHARS=450000, HTTP_TIMEOUT=300
cp config.conf config-ollama.conf      # PROVIDER="ollama", a local model, no key
chmod 600 *.conf                        # they hold API keys — keep them private

When you run BibSummary, choose Config… in the start dialog and pick the profile from the list. Every *.conf in that folder (including the default config.conf) shows up automatically; the one you pick is used for that run only via the BIBSUMMARY_CONF environment variable. Run always uses config.conf.

A profile is a complete config, so each one carries its own provider, model, keys, and limits — there's nothing to keep in sync between them.

Customizing the output

  • Summary style/length — edit PROMPT in the config. The sections, tone, and length are all driven from there.
  • How much text is sentMAX_CHARS (default 50000). Lower it to cut cost; raise it for long papers (see Long papers).
  • Summary lengthMAX_OUTPUT_TOKENS (default 4096). Raise it if summaries come out cut off mid-sentence. Applies to anthropic and ollama; OpenAI uses its model default.
  • Network timeoutHTTP_TIMEOUT (seconds). Commented out by default, which uses the built-in defaults (180 for anthropic/openai, 300 for ollama). Raise it if you push MAX_CHARS up a lot and requests start timing out.
  • Where files landOUTPUT_DIR (alongside the PDF, or a fixed folder path).

Long papers

By default only the first MAX_CHARS (50000) characters of extracted text are sent — roughly the first ~12k tokens, so a long paper is summarized from its opening pages only. To summarize the whole thing, raise MAX_CHARS. Check how much text a PDF actually has:

pdftotext -enc UTF-8 paper.pdf - | wc -m

If that count is well above 50000, the paper is being truncated. A typical full paper is a few hundred thousand characters (~100k tokens) — comfortably within the context window of large cloud models like claude-opus-4-8, but likely too big for small or local models, which have much smaller context windows and will error or silently truncate. Match MAX_CHARS to the model you're using.

You don't have to guess whether truncation happened: every summary's header records how much text was used (e.g. "sent 50,000 of 402,342 characters — truncated"), and when a paper is truncated the BibDesk dialog adds a note at the end of the run telling you which entries were affected.

Because raising MAX_CHARS increases both cost and request time, a dedicated config profile (e.g. config-fullpaper.conf with a high MAX_CHARS and a larger HTTP_TIMEOUT) is a convenient way to opt in per-run rather than changing your default.

How linking and auto-filing work

The summary .md is always written next to the PDF and linked to the entry explicitly (add … to linked files). This step does not depend on any BibDesk auto-file setting — the file is created in a permanent, valid location and linked there regardless.

After linking, the script calls BibDesk's auto file on the entry (controlled by the autoFileAfterLinking property at the top of the AppleScript, default true). That's what renames the summary to match your library's local-file format — which is why the .md ends up named like the PDF.

What happens in different setups:

  • You use auto-file (papers folder configured): the summary is renamed/placed per your format, alongside the PDF. This is the typical experience.
  • You don't use auto-file, or no papers folder is set: the auto file call is wrapped in a try, so it's harmlessly skipped. The summary just stays as <citekey>-summary.md next to the PDF, still linked. Nothing fails.
  • Heads-up: auto file files all of an entry's linked files, not only the new summary. So if your PDFs are not already auto-filed, leaving this on can move the original PDF into your papers folder too. If you don't want anything moved (and prefer the explicit -summary filename), set autoFileAfterLinking to false in bibsummary.applescript and re-run ./install.sh.

Troubleshooting

  • "No PDF text extractor found" — install poppler (brew install poppler) or the Xcode command line tools.
  • "Extracted no text" — the PDF is likely a scan with no text layer. OCR it first (e.g. with ocrmypdf) and retry.
  • "Provider returned: …" / "HTTP 4xx from …" — the API rejected the request; the message comes straight from the provider (bad key, unknown model, rate limit, prompt too long, etc.). For example, a too-large MAX_CHARS on a small-context model surfaces here as a "prompt is too long" error.
  • "could not reach … network/DNS/TLS failure or timed out" — the request never completed: no connectivity, wrong OLLAMA_HOST, or the model took longer than HTTP_TIMEOUT. For big inputs, raise HTTP_TIMEOUT (see Long papers).
  • A large paper fails — the request body is sent over stdin, so there's no command-line size limit; a failure on a big paper is almost always a provider error (prompt too long for the model's context) or a timeout. The two messages above tell you which. Run with debugging on (below) to see the exact HTTP status.
  • Summary is cut off mid-sentence — the model hit its output limit. Raise MAX_OUTPUT_TOKENS in the config (default 4096). The helper also prints a truncation warning to stderr when this happens.
  • Nothing happens / wrong tool path — BibDesk launches scripts with a minimal PATH; the helper adds /opt/homebrew/bin and /usr/local/bin. If your tools live elsewhere, adjust the export PATH= line near the top of bibsummary.sh.
  • Long PDFs feel slow — the run blocks BibDesk's UI until the model responds. That's expected for v1; the notification fires when done.

Debugging

BibDesk hides the helper's stderr unless the whole run fails, so the quickest way to see what's happening is to turn on the log:

  • Set BIBSUMMARY_DEBUG="1" in your config (or pass it inline when running from a terminal). The helper then appends progress, request/response sizes, and the HTTP status to a logfile — /tmp/bibsummary.log by default, or wherever BIBSUMMARY_LOG points.

    tail -f /tmp/bibsummary.log
  • To reproduce a failure outside BibDesk, run the helper directly — the real error goes to stderr:

    BIBSUMMARY_DEBUG=1 bash ~/Library/Application\ Support/BibSummary/bibsummary.sh /path/to/paper.pdf testkey 2>&1 | tail -20

Privacy

API keys live only in your local config file (keep it chmod 600). With the ollama provider, the PDF text and summary never leave your machine.

Notes

  • Tested against the request/response shapes of the Anthropic Messages API, OpenAI Chat Completions, and the Ollama /api/chat endpoint. Provider APIs change; if a call starts failing, check the provider's current docs and the model name in your config.
  • This uses extracted text for all providers (uniform behavior, and required for local models). If you only ever use Anthropic, you could adapt the helper to send the PDF natively for better handling of figures and tables.

License

MIT — see LICENSE. Use it, fork it, ship it.

About

Summarize a BibDesk entry's linked PDF with your LLM of choice, attached as a linked Markdown note

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages