Config-driven pipeline that turns a research goal into a structured literature database.
Docs: Documentation
Flow: search → filter → download PDFs → convert to markdown → extract JSON → export database.json
- Python 3.10+
- API keys depending on your config (OpenAlex, Anthropic, and/or local Ollama)
- Optional: GPU for faster PDF→markdown conversion (Marker)
git clone https://github.com/MineralsCloud/litcurate.git
cd litcurateOr, if you already have this folder:
cd litcuratepython3 -m venv .venv
source .venv/bin/activateWindows:
python -m venv .venv
.venv\Scripts\activateInstall LitCurate (editable) with OpenAlex support and test tools:
pip install -U pip
pip install -e ".[dev,openalex]"Check that the CLI works:
litcurate --help
litcurate stagesSmoke test (no API keys — uses dry-run sample data):
litcurate run configs/config.yamlCopy the example env file and edit it:
cp .env.example .envFill in at least:
| Variable | Needed for |
|---|---|
OPENALEX_API_KEY and/or OPENALEX_EMAIL |
Literature search |
UNPAYWALL_EMAIL |
Open-access PDF download |
ANTHROPIC_API_KEY |
Claude stages (if your config uses Anthropic) |
OPENAI_API_KEY |
Ollama / OpenAI-compatible APIs (any non-empty value for local Ollama) |
LitCurate loads .env automatically. Keys are not stored in YAML configs.
configs/config.yaml is the shipped template and dry-run smoke test. Copy it for your domain and point extraction.schemas at your own files under schemas/ and prompts/.
Set the config once per shell session:
export CONFIG=configs/config.yamlOptional: screen abstracts/full text with a local OpenAI-compatible server (e.g. Ollama). Uncomment the provider / base_url / model fields under abstract_filter or fulltext_filter in your YAML.
# Install from https://ollama.com if needed
ollama pull qwen3:14b
ollama serveLeave that running. In another terminal, keep the venv activated.
Remote GPU Ollama: set in YAML:
base_url: http://GPU_HOST:11434/v1In your config, set Marker’s device:
conversion:
engine: marker
device: cpu # use cuda:0 on NVIDIA, or mps on Apple SiliconAlways activate the venv first:
cd litcurate
source .venv/bin/activate
export CONFIG=configs/config.yamllitcurate run-stage query_generation --config $CONFIGCopy the printed run id, then:
RUN_ID=<your_run_id>
# or: litcurate list-runsUse the same RUN_ID for every later stage.
litcurate run-stage openalex_search --run-id $RUN_ID
litcurate show openalex_search $RUN_IDlitcurate run-stage merge_rank --run-id $RUN_IDlitcurate run-stage filter_abstracts --run-id $RUN_ID --config $CONFIGlitcurate run-stage download_pdfs --run-id $RUN_IDThis stage resolves PDF URLs through metadata services and downloads them over HTTP. It does not install, open, or automate a browser.
litcurate run-stage convert_marker --run-id $RUN_IDLong server job:
mkdir -p runs/$RUN_ID/logs
nohup litcurate run-stage convert_marker --run-id $RUN_ID \
> runs/$RUN_ID/logs/convert_marker.nohup.log 2>&1 &
tail -f runs/$RUN_ID/logs/convert_marker.nohup.loglitcurate run-stage clean_markdown --run-id $RUN_IDlitcurate run-stage pre_extract --run-id $RUN_IDlitcurate run-stage filter_fulltext --run-id $RUN_ID --config $CONFIGlitcurate run-stage extract_schema --run-id $RUN_ID --config $CONFIGlitcurate run-stage export --run-id $RUN_IDOutput:
runs/<RUN_ID>/artifacts/output/database.json
This file merges successful per-paper extractions (full envelopes) ready for ingest.
litcurate list-runs
litcurate status $RUN_ID
litcurate show <stage_name> $RUN_ID
litcurate resume $RUN_IDRe-run one stage after a config change:
litcurate run-stage filter_abstracts --run-id $RUN_ID --config $CONFIG --forcePass --config $CONFIG on LLM stages so provider/model settings are applied (avoids a stale snapshot).
If download fails (paywalled), name files by DOI with / → _:
| DOI | Filename |
|---|---|
10.1029/2011jb008988 |
10.1029_2011jb008988.pdf |
litcurate ingest-pdfs --run-id $RUN_ID --from-dir ~/manual_pdfs/ --dry-run
litcurate ingest-pdfs --run-id $RUN_ID --from-dir ~/manual_pdfs/
litcurate run-stage convert_marker --run-id $RUN_ID --force| # | Stage | Typical needs |
|---|---|---|
| 1 | query_generation |
LLM (e.g. Anthropic) |
| 2 | openalex_search |
OpenAlex |
| 3 | merge_rank |
— |
| 4 | filter_abstracts |
Ollama / LLM |
| 5 | download_pdfs |
Unpaywall email |
| 6 | convert_marker |
Marker (GPU optional) |
| 7 | clean_markdown |
— |
| 8 | pre_extract |
— |
| 9 | filter_fulltext |
Ollama / LLM |
| 10 | extract_schema |
LLM |
| 11 | export |
— |
runs/<RUN_ID>/
run.db # SQLite status / resume
config.snapshot.yaml # frozen config for this run
artifacts/ # queries, PDFs, markdown, extractions, output/
logs/
Per-stage override:
extraction:
provider: openai_compatible
base_url: http://localhost:11434/v1
model: qwen3:14bGlobal default (required — no implicit Anthropic fallback):
llm:
provider: openai_compatible
base_url: http://localhost:11434/v1marker-pdf is included in the base install.
| Problem | Fix |
|---|---|
litcurate: command not found |
Activate .venv, then pip install -e ".[dev,openalex]" |
| OpenAlex auth error | Set OPENALEX_API_KEY and/or OPENALEX_EMAIL in .env |
| Ollama connection error | Run ollama serve and ollama pull qwen3:14b |
| Marker / CUDA crash | Set conversion.device: cpu and re-run convert |
| Wrong model on a stage | Pass --config $CONFIG on that stage |
| Continue after a crash | litcurate resume $RUN_ID |
LitCurate uses Semantic Versioning. Git tags of the form vX.Y.Z publish a GitHub Release with source and wheel artifacts. See CONTRIBUTING.md for the full process.
If you use LitCurate, please cite it via CITATION.cff (GitHub Cite this repository).
MIT — see LICENSE.