Free, open-source AI academic paper reviewer. BYOK — bring your own key.
Project description
coarse
Free, open-source AI academic paper reviewer that outperforms popular paid AI reviewers.
You provide your own API key and pay the LLM provider directly — typically under $2 per review.
Don't want to run it locally? Use the web interface instead.
Quickstart
Get an API key from OpenRouter (free to sign up), then:
uvx coarse-ink review paper.pdf --api-key sk-or-v1-YOUR_KEY
That's it. The review is written to paper_review.md in the current directory.
Prerequisites
coarse requires Python 3.12+. If you don't have uvx, install uv first:
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
uvx runs coarse in a temporary environment with no permanent install. To install permanently:
uv tool install coarse-ink # or: pip install coarse-ink
Why
coarse-inkand notcoarse? The barecoarsename on PyPI is held by an unrelated package, so we ship undercoarse-ink. The Python import name (import coarse) and thecoarseCLI command are unchanged — installingcoarse-inkputs bothcoarseandcoarse-inkon your PATH.
Save your API key
To avoid passing --api-key every time, create a .env file in your working directory:
OPENROUTER_API_KEY=sk-or-v1-YOUR_KEY
Or run coarse setup to store keys in ~/.coarse/config.toml.
Supported formats
PDF, TXT, Markdown, LaTeX, DOCX, HTML, and EPUB. PDFs use Mistral OCR; other formats use Docling (if installed) with lightweight fallbacks. Install optional format support:
pip install coarse-ink[formats] # DOCX, HTML, EPUB fallbacks
pip install coarse-ink[docling] # Docling for PDF/DOCX/HTML/LaTeX
How it works
paper.pdf (or .txt, .md, .tex, .docx, .html, .epub)
-> Mistral OCR (Docling fallback) Extract text as markdown
-> Vision LLM spot-check Optional QA (auto-triggers on garbled text)
-> Structure analysis Parse sections, detect math content, classify domain
-> Domain calibration + lit search Parallel: domain-specific criteria + Perplexity Sonar Pro
-> 3-judge overview panel Three personas review full paper, then synthesize
-> Section agents + proof verification Parallel: 15-25 detailed comments; math sections get adversarial proof check
-> Cross-reference agent Deduplicate, validate consistency
-> Quote verification Fuzzy-match quotes against paper text (stricter for math)
-> Self-critique agent Quality gate, revise weak comments
-> Quote re-verification Fix any quotes garbled during critique
-> Synthesis Render final paper_review.md
The pipeline extracts text, classifies the paper's domain and structure, then generates domain-specific review criteria and searches for related literature (via Perplexity Sonar Pro, with arXiv fallback). A 3-judge overview panel produces macro-level feedback from different perspectives, which is then synthesized into a unified assessment. Section agents run in parallel, with an adversarial proof verification pass for math-heavy sections. A cross-reference pass deduplicates comments and a self-critique agent acts as a quality gate. All quotes are programmatically verified against the source text, with stricter thresholds for math content.
Model selection
Pass any litellm-compatible model string with --model:
coarse review paper.pdf --model openai/gpt-4o
coarse review paper.pdf --model anthropic/claude-sonnet-4-6
coarse review paper.pdf --model gemini/gemini-3-flash-preview
The default model is qwen/qwen3.5-plus-02-15 routed via OpenRouter.
Any model supported by litellm works.
With only OPENROUTER_API_KEY set, all models (including vision QA) route through OpenRouter automatically.
Use --cheap to automatically select the cheapest model for which you have an API key.
API keys
Only OPENROUTER_API_KEY is needed. This covers everything: review agents,
literature search, and PDF extraction (Mistral OCR is always routed through
OpenRouter's file-parser plugin, so there's no separate key for it). For
step-by-step setup instructions, see the API key guide.
Set your OpenRouter per-key spend limit to at least $10 (ideally matching the
max_cost_usddefault of$10). If the limit is hit mid-review the run will fail and you'll need to raise the limit and resubmit. Cost estimates shown before each review are approximate (~15% buffer) — they're a guide, not a hard ceiling, so leave yourself headroom.
For direct provider access to chat models (lower latency, separate billing), you can set the provider-specific key instead:
| Provider | Environment variable |
|---|---|
| OpenRouter | OPENROUTER_API_KEY |
| OpenAI | OPENAI_API_KEY |
| Anthropic | ANTHROPIC_API_KEY |
GEMINI_API_KEY |
|
| Mistral | MISTRAL_API_KEY |
| Groq | GROQ_API_KEY |
| Together | TOGETHER_API_KEY |
| Cohere | COHERE_API_KEY |
| Azure | AZURE_API_KEY |
Cost
coarse estimates cost before running and asks for confirmation. The estimate includes a 15% buffer to account for variance.
| Paper length | Typical cost |
|---|---|
| Short (< 20pp) | $0.25 - $0.50 |
| Long (30+ pp) | $0.50 - $1 |
Actual costs can run up to ~2× the estimate on complex papers depending on model reasoning depth, critique agent rewrites, and proof-verification chains for math-heavy sections. The 15% buffer is a first approximation, not a ceiling. Make sure your OpenRouter per-key spend limit has headroom above the estimate.
The default spending cap is $10 per review (max_cost_usd in config). Use --yes to skip the
confirmation prompt. Use --no-qa to skip the post-extraction quality check (vision LLM).
Scanned PDFs are supported via Docling's built-in OCR (pip install coarse-ink[docling]).
You can also load API keys from any .env file with --env-file path/to/.env.
Output format
The review is written as a structured markdown file:
# Paper Title
**Date**: MM/DD/YYYY
**Domain**: social_sciences/economics
**Taxonomy**: academic/research_paper
**Filter**: Active comments
---
## Overall Feedback
4-6 macro issues with titles and body paragraphs.
---
## Detailed Comments (N)
20+ numbered comments, each with a verbatim quote from the paper and
actionable feedback.
Python API
from coarse import review_paper
from pathlib import Path
review, markdown, paper_text = review_paper(
pdf_path=Path("paper.pdf"), # accepts any supported format
model="openai/gpt-4o", # optional; uses config default if omitted
)
print(markdown) # full review as markdown string
print(review.detailed_comments[0].feedback) # access structured fields
review_paper returns a (Review, str, PaperText) tuple: the structured Review model,
rendered markdown, and the extracted paper text. The pdf_path parameter accepts any
supported file format (PDF, TXT, MD, TeX, DOCX, HTML, EPUB).
Configuration
Settings are stored in ~/.coarse/config.toml:
default_model = "qwen/qwen3.5-plus-02-15"
vision_model = "gemini/gemini-3-flash-preview"
extraction_qa = true
max_cost_usd = 10.0
[api_keys]
openai = "sk-..."
anthropic = "sk-ant-..."
Run coarse setup for an interactive prompt that writes this file.
Development
git clone https://github.com/Davidvandijcke/coarse.git
cd coarse
uv sync --extra dev
uv run pytest tests/ -v
Contributing
See CONTRIBUTING.md for development setup, project structure, and guidelines.
Version
1.2.2
License
MIT
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file coarse_ink-1.2.2.tar.gz.
File metadata
- Download URL: coarse_ink-1.2.2.tar.gz
- Upload date:
- Size: 36.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e6d44ba1b6db0007adb5f0fd6563a39efc40a709c567d2e761318e148fb5e5f
|
|
| MD5 |
f2c6901d28a2dfde954d8283a56b9960
|
|
| BLAKE2b-256 |
b4379c5a6fe257063a54012df3e02cb537f8a6e602d5287b4f0460c97dec6fc9
|
Provenance
The following attestation bundles were made for coarse_ink-1.2.2.tar.gz:
Publisher:
release.yml on Davidvandijcke/coarse
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coarse_ink-1.2.2.tar.gz -
Subject digest:
7e6d44ba1b6db0007adb5f0fd6563a39efc40a709c567d2e761318e148fb5e5f - Sigstore transparency entry: 1282591492
- Sigstore integration time:
-
Permalink:
Davidvandijcke/coarse@648960be21053db6d80e73bd64a11b260ca399ca -
Branch / Tag:
refs/tags/v1.2.2 - Owner: https://github.com/Davidvandijcke
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@648960be21053db6d80e73bd64a11b260ca399ca -
Trigger Event:
push
-
Statement type:
File details
Details for the file coarse_ink-1.2.2-py3-none-any.whl.
File metadata
- Download URL: coarse_ink-1.2.2-py3-none-any.whl
- Upload date:
- Size: 117.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
530adcedc2f8b965c94783fc45837eb22f2d5181f32d85a890eecf5549ace87d
|
|
| MD5 |
794ac8822c0be5ada2a92078c6915568
|
|
| BLAKE2b-256 |
78108865ea09d770ec37a5ba06781fa41003ec0e6521168a6bc9e80a761d9ee7
|
Provenance
The following attestation bundles were made for coarse_ink-1.2.2-py3-none-any.whl:
Publisher:
release.yml on Davidvandijcke/coarse
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coarse_ink-1.2.2-py3-none-any.whl -
Subject digest:
530adcedc2f8b965c94783fc45837eb22f2d5181f32d85a890eecf5549ace87d - Sigstore transparency entry: 1282591631
- Sigstore integration time:
-
Permalink:
Davidvandijcke/coarse@648960be21053db6d80e73bd64a11b260ca399ca -
Branch / Tag:
refs/tags/v1.2.2 - Owner: https://github.com/Davidvandijcke
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@648960be21053db6d80e73bd64a11b260ca399ca -
Trigger Event:
push
-
Statement type: