Skip to content

About

A package to automate extracting ecological data from papers using AI

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

EcoExtract

Structured ecological data extraction and refinement from scientific literature.

EcoExtract automates the extraction of structured data from PDFs using OCR and LLMs. It's domain-agnostic and works with any JSON schema you define.

Pipeline

graph LR
    A[PDF Papers] -->|ohseer| B[OCR]
    B -->|ecoextract| C[Metadata Extraction]
    B -->|ecoextract| D[Data Extraction]
    C --> E[SQLite Database]
    D --> E
    E -.->|optional| F[Refinement]
    F -.-> E
    E -->|ecoreview| G[Human Review]
    G --> H[Validated Data]

    style A fill:#e1f5ff
    style H fill:#c8e6c9
    style G fill:#fff9c4
    style E fill:#f0f0f0
Loading

Package Purpose Links
ohseer OCR processing (Mistral, Tensorlake, Claude) GitHub
ecoextract AI-powered extraction pipeline Docs | GitHub
ecoreview Interactive Shiny review app GitHub

Quick Links:

Installation

Recommended: renv (reproducible, locks dependency versions)

renv::init()
renv::install("n8layman/ohseer")      # OCR processing
renv::install("n8layman/ecoextract")  # Data extraction
renv::install("n8layman/ecoreview")   # Review app (optional)
renv::snapshot()

Alternative: pak

pak::pak("n8layman/ohseer")
pak::pak("n8layman/ecoextract")
pak::pak("n8layman/ecoreview")   # optional

See the Complete Guide for troubleshooting.

API Key Setup

EcoExtract uses ellmer for LLM interactions and ohseer for OCR.

Required API keys:

Optional providers:

Create a .env file in your project root (make sure it's in .gitignore first!):

ANTHROPIC_API_KEY=your_anthropic_api_key_here
MISTRAL_API_KEY=your_mistral_api_key_here
# Optional providers
TENSORLAKE_API_KEY=your_tensorlake_api_key_here
GOOGLE_API_KEY=your_google_api_key_here
OPENAI_API_KEY=your_openai_api_key_here

The .env file is automatically loaded when R starts in the project directory. See the Complete Guide for detailed setup instructions.

By default, ecoextract uses anthropic/claude-sonnet-5 for extraction and mistral for OCR. To use different providers, pass the model or ocr_provider parameters to process_documents(). Supported LLM providers: Anthropic (anthropic/), Google Gemini (google_gemini/), OpenAI (openai/), Mistral (mistral/), Groq (groq/).

Note: OpenAI models require schemas to follow OpenAI's structured output rules -- specifically "additionalProperties": false on every object and all properties listed in required. ecoextract sets "additionalProperties": false on every object before sending a schema (except to Gemini, which rejects it); listing properties in required is up to the schema. Schemas generated by init_ecoextract() follow these rules by default. See the Configuration Guide for details.

Quick Start

library(ecoextract)

# Process all PDFs in a folder through the 4-step pipeline:
# OCR -> Metadata -> Extraction -> Refinement (optional)
results <- process_documents(
  pdf_path = "path/to/pdfs/",
  db_conn = "ecoextract_records.db"
)

# Retrieve your data
records <- get_records()
export_db(filename = "extracted_data.csv")

Model Fallback

EcoExtract supports tiered model fallback to handle content refusals (e.g., Claude refusing disease/biosecurity papers). Provide a vector of models to try sequentially:

# Single model (default)
process_documents(
  pdf_path = "papers/",
  model = "anthropic/claude-sonnet-5"
)

# Tiered fallback: try Claude, then Gemini (1M context), then Mistral
process_documents(
  pdf_path = "papers/",
  model = c(
    "anthropic/claude-sonnet-5",
    "google_gemini/gemini-2.5-flash",
    "mistral/mistral-large-latest"
  )
)

Audit logging: The database tracks which model succeeded for each step (metadata, extraction, refinement) in *_llm_model columns. All failed attempts with error messages and timestamps are logged in *_log columns for debugging.

API keys: Add keys for fallback providers to your .env:

ANTHROPIC_API_KEY=your_anthropic_key
GOOGLE_API_KEY=your_google_key
OPENAI_API_KEY=your_openai_key
MISTRAL_API_KEY=your_mistral_key

OCR Provider Selection

EcoExtract supports multiple OCR providers through ohseer. By default it uses Mistral, but you can switch to Tensorlake or Claude:

# Use default provider (Mistral)
process_documents("papers/")

# Use Tensorlake OCR
process_documents(
  pdf_path = "papers/",
  ocr_provider = "tensorlake"
)

# Use Claude OCR
process_documents(
  pdf_path = "papers/",
  ocr_provider = "claude"
)

# Use Claude OCR
process_documents(
  pdf_path = "papers/",
  ocr_provider = "claude"
)

# OCR fallback: try Mistral, then Tensorlake if Mistral fails
process_documents(
  pdf_path = "papers/",
  ocr_provider = c("mistral", "tensorlake")
)

# Increase OCR timeout for large documents
process_documents(
  pdf_path = "papers/",
  ocr_timeout = 300  # 5 minutes
)

Provider fallback: When multiple providers are specified, ohseer automatically tries each in order until one succeeds. The OCR provider used for each document is tracked in the ocr_provider column of the documents table.

Key Features

  • Smart skip logic -- Re-running process_documents() skips completed steps. Forced re-runs automatically invalidate downstream steps.
  • Parallel processing -- Process multiple documents simultaneously with workers = 4 (requires crew package).
  • Deduplication -- Three methods: "llm" (default), "embedding", or "jaccard".
  • Human review -- Edit, add, and delete records in the ecoreview Shiny app with full audit trail.
  • Accuracy metrics -- Calculate detection recall, field precision, F1, and edit severity after review.

See the Complete Guide for details on all features.

Custom Schemas

EcoExtract is domain-agnostic and works with any JSON schema:

# Create custom config directory with templates
init_ecoextract()

# Edit the generated files:
# - ecoextract/schema.json          # Define your data structure
# - ecoextract/extraction_prompt.md # Describe what to extract

# The package automatically uses these files
process_documents("pdfs/", "records.db")

Schema requirements: top-level records property (array of objects), each field with type and description, JSON Schema draft-07 format.

Custom metadata: The metadata step extracts bibliographic fields (title, authors, year, DOI, ...) by default. For corpora that are not journal articles, add ecoextract/metadata_schema.json and ecoextract/metadata_prompt.md (or pass metadata_schema_file / metadata_prompt_file to process_documents()) to extract your own document-level fields, or set run_metadata = FALSE to skip the step. New fields become columns in the documents table. The schema's x-record-id-fields names the fields that record IDs are built from: metadata fields, document_id, or file_name (default: first_author_lastname and publication_year, giving Smith_2020_1_r1). List every metadata field in required with nullable types, and keep nullable fields to 16 or fewer so the schema works on older models such as Claude Sonnet 4.6.

See the Configuration Guide for complete details and examples.

Troubleshooting

When every model in model fails for a step, the step's status (e.g. metadata_status in the results and in the documents table) lists each model's error.

Unexplained schema errors? Try a newer model before changing your schema. Older models enforce much tighter structured-output limits than current ones, and report them as Schema is too complex or another HTTP 400 that reads like a schema problem. The same schema can fail on anthropic/claude-sonnet-4-6 or anthropic/claude-haiku-4-5 and pass on anthropic/claude-sonnet-5. If every model in your fallback list fails with the same schema error, check whether they are all older models. See Unexplained Schema Errors for the measured limits.

Content refusals: Some models refuse papers on sensitive topics (e.g. select agents). Add a model from another provider to model as a fallback (see Model Fallback).

See the Complete Guide for more.

Package Functions

Workflow

  • process_documents() - Complete 4-step workflow (OCR -> Metadata -> Extract -> Refine)

Database Setup

  • init_ecoextract_database() - Initialize database with schema
  • init_ecoextract() - Create project config directory with template schema and prompts

Data Access

  • get_documents() - Query documents and their metadata from database
  • get_records() - Query extracted records from database
  • get_ocr_markdown() - Get OCR markdown text for a document
  • get_ocr_pages() - Get OCR markdown for each page, with stored images embedded
  • get_ocr_html_preview() - Render OCR output with embedded images as HTML
  • get_db_stats() - Get document and record counts from database
  • export_db() - Export records with metadata to tibble or CSV file
  • export_bibtex() - Export document bibliography to BibTeX format
  • calculate_accuracy() - Calculate detection recall, field precision, F1, and edit severity after human review

Contributing

The main branch is protected. All changes must go through a pull request. PRs must pass the full CI suite before merging, which includes:

  • R CMD CHECK (no errors, no warnings)
  • Unit and integration tests (devtools::test()) — integration tests require live API keys and perform real extractions

See CONTRIBUTING.md for development setup and guidelines.

Testing

devtools::test()   # Run all tests
devtools::check()  # Run package checks

Integration tests require API keys in a .env file and perform live extractions against real APIs — they verify API response structure, not content accuracy.

The main branch is protected. All changes must be submitted as a pull request. PRs must pass the full CI suite before merging:

  • R CMD CHECK with no errors or warnings
  • Full test suite including integration tests (requires live API keys configured in CI)

This ensures every merge has been validated against real API behavior, not just mocks.

File Structure

ecoextract/
├── R/
│   ├── workflow.R          # Main process_documents() workflow + skip/cascade logic
│   ├── ocr.R               # OCR processing
│   ├── metadata.R          # Publication metadata extraction
│   ├── extraction.R        # Data extraction functions
│   ├── refinement.R        # Data refinement functions
│   ├── deduplication.R     # Record deduplication (LLM, embedding, Jaccard)
│   ├── database.R          # Database operations
│   ├── bibtex.R            # Bibliography export (BibTeX format)
│   ├── getters.R           # Data access functions (get_*, export_db, export_bibtex)
│   ├── config_loader.R     # Configuration file loading + init_ecoextract()
│   ├── prompts.R           # Prompt loading
│   ├── utils.R             # Utilities
│   ├── config.R            # Package configuration
│   └── ecoextract-package.R # Package metadata
├── inst/
│   ├── extdata/            # Schema files
│   │   ├── schema.json
│   │   └── metadata_schema.json
│   └── prompts/            # System prompts
│       ├── extraction_prompt.md
│       ├── extraction_context.md
│       ├── metadata_prompt.md
│       ├── metadata_context.md
│       ├── refinement_prompt.md
│       ├── refinement_context.md
│       └── deduplication_prompt.md
├── tests/testthat/         # Tests
├── vignettes/              # Package vignettes
├── DESCRIPTION
├── NAMESPACE
├── CONTRIBUTING.md         # Development guide
└── README.md

Tech Stack

R Packages

  • ellmer - Structured LLM outputs
  • ohseer - OCR processing
  • dplyr - Data manipulation
  • DBI & RSQLite - Database operations
  • jsonlite - JSON handling
  • glue - String interpolation
  • stringr & stringi - String manipulation
  • digest - Hashing
  • tidyllm - LLM deduplication

External APIs

  • Tensorlake - OCR processing (via ohseer)
  • Anthropic Claude / OpenAI / other LLM providers - Data extraction and refinement (via ellmer)

License

GPL (>= 3)

About

A package to automate extracting ecological data from papers using AI

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages