Skip to content

Repository files navigation

MSstatsConvertLLM

Local LLMs for automated schema identification in MSstatsConvert.

MSstatsConvertLLM uses locally-hosted large language models (LLMs) to automatically map the columns of arbitrary mass-spectrometry proteomics tool output onto the standardized MSstats analysis schema. Instead of relying on hand-written, tool-specific converters, the package prompts a local LLM to infer column mappings (and quality-control filters) and then feeds that mapping into the existing MSstatsConvert pipeline, reducing the manual overhead required to onboard new input formats.

The package provides two things:

  1. A generic converterLLMtoMSstatsFormat() takes raw tool output plus an LLM-produced mapping and returns a data.table in MSstats format, ready for MSstats::dataProcess().
  2. A benchmarking harness — tooling to evaluate how well different local models, prompt strategies, and tools recover the correct schema mapping, scored against curated ground truth.

This work was presented as a poster at ASMS 2026.

Installation

# install.packages("remotes")
remotes::install_github("Vitek-Lab/MSstatsConvertLLM")

Or, from a local clone:

# install.packages("devtools")
devtools::install(".")

Requirements

  • R >= 4.0
  • MSstatsConvert (Bioconductor) — installed automatically as a dependency.
  • ellmer — the LLM chat interface, installed automatically as a dependency.
  • For live inference: Ollama running locally with at least one model pulled.
  • (Optional) an ANTHROPIC_API_KEY environment variable to benchmark against Claude models via the API.
  • (Optional) MSstats (Bioconductor) for the downstream comparison scripts.

Quick start

1. Convert a dataset with an LLM-inferred mapping

library(MSstatsConvertLLM)

# Load a raw tool export (any proteomics tool: Spectronaut, DIA-NN, PD, ...)
input <- load_test_dataset("spectronaut")   # or data.table::fread("your_file.csv")

# Ask a local model to infer the column mapping + filters
trial <- run_trial(
  model_key   = "llama3.1-8b",
  tool_name   = "spectronaut",
  prompt_key  = "constrained_filter",
  acquisition = "DIA"
)

# Feed the LLM mapping into the MSstats converter
msstats_input <- LLMtoMSstatsFormat(
  input       = input,
  annotation  = "path/to/annotation.csv",   # Run / Condition / BioReplicate
  llm_mapping = trial$mapping
)

# Continue with the standard MSstats workflow
# processed <- MSstats::dataProcess(msstats_input)

LLMtoMSstatsFormat() applies the LLM's discovered filters, renames and fills columns to the MSstats schema, coerces types, merges the run annotation, removes shared peptides, summarizes duplicate PSMs, and records diagnostics (e.g. hallucinated columns, skipped filters) in attr(result, "diagnostics").

2. Run the benchmark

# Interactive single-trial test (edit the model/tool/prompt at the top)
Rscript run_one.R

# Full benchmark matrix (all models x tools x prompts x reps)
Rscript run_all.R

# Run a subset
Rscript run_all.R --models llama3.1-8b,deepseek-r1-14b --tools spectronaut --reps 1

The runner scripts call library(MSstatsConvertLLM), so install the package (or devtools::load_all(".")) before running them.

Scoring

Each predicted field is scored against ground truth:

  • exact_match — predicted column == primary expected column (case/punctuation normalized)
  • 🟢 acceptable_match — predicted column matches the primary or an accepted alternate
  • 🟡 candidate_hit — wrong primary pick, but the correct column appears in the candidates list
  • miss — neither the primary pick nor the candidates contain the correct mapping

Project structure

MSstatsConvertLLM/
├── DESCRIPTION            # Package metadata and dependencies
├── NAMESPACE              # Exports / imports (generated by roxygen2)
├── R/
│   ├── MSstatsConvertLLM-package.R  # Package doc, imports, globalVariables
│   ├── config.R           # Model registry, ground truth, MSstats schema, helpers
│   ├── prompts.R          # Lean / constrained / filter-aware prompt templates
│   ├── benchmark.R        # create_chat(), run_trial(), score_trial(), print_scorecard()
│   └── LLMtoMSstatsFormat.R  # Generic LLM-driven converter
├── man/                   # Generated function documentation
├── run_one.R              # Interactive single-trial driver
├── run_all.R              # Full benchmark-matrix driver
├── analysis.R             # Plots/summaries of benchmark results
├── compare_downstream.R   # LLM vs. native converter -> dataProcess -> groupComparison
├── compare_navarro.R      # Downstream comparison on the Navarro 2016 DIA benchmark
├── data/                  # Local benchmark inputs (not shipped with the package)
└── results/               # Benchmark outputs (CSV / RDS / plots; not shipped)

Extending the benchmark

The benchmark registries live in R/config.R. After editing them, reinstall the package or re-run devtools::load_all(".") to pick up the changes.

Add a new local model

In MODEL_REGISTRY:

MODEL_REGISTRY[["phi3-3.8b"]] <- list(
  provider = "ollama",
  model    = "phi3:3.8b",
  label    = "Phi-3 3.8B"
)

Then pull it in Ollama (ollama pull phi3:3.8b) and run:

Rscript run_all.R --models phi3-3.8b

Add a new proteomics tool / dataset

  1. Make the test file accessible (or use MSstatsConvert test data).
  2. In R/config.R, register the dataset path in load_test_dataset() and add its ground-truth mapping to GROUND_TRUTH:
# In load_test_dataset() paths list:
diann_v18 = "/path/to/diann_v18_report.tsv"

# In GROUND_TRUTH (a character vector lists acceptable alternates):
GROUND_TRUTH[["diann_v18"]] <- list(
  ProteinName     = "Protein.Group",
  PeptideSequence = "Modified.Sequence",
  PrecursorCharge = "Precursor.Charge",
  FragmentIon     = NULL,
  ProductCharge   = NULL,
  Run             = "Run",
  Intensity       = "Fragment.Quant.Raw",
  Qvalue          = "Q.Value"
)

Optionally set the acquisition type (TOOL_ACQUISITION) and whether to enable packed-column transform detection (TOOL_TRANSFORMS) for the new tool.

Add a new prompt strategy

In R/prompts.R, define your prompt string and register it:

PROMPT_FEWSHOT <- "..."
PROMPT_VERSIONS[["fewshot"]] <- PROMPT_FEWSHOT

Downstream validation

The comparison scripts run both the LLM converter and the native MSstatsConvert converter through the full MSstats workflow (dataProcess()groupComparison()) and compare summarized intensities, log2 fold-changes, adjusted p-values, and empirical FDR:

Data

The data/ directory (Spectronaut input + annotation for the Navarro 2016 DIA benchmark) is not versioned — the input file is large and lives on shared storage. The next developer can obtain it from the Vitek Lab share:

/projects/VitekLab/Data/MS/Benchmarking/DIA_Navarro2016

Copy the Spectronaut input and annotation into a local data/ directory (or update the RAW_PATH / ANNOT_PATH paths at the top of compare_navarro.R to point at the share directly).

Dependencies

Imports (installed automatically): data.table, ellmer (LLM chat interface), jsonlite, MSstatsConvert.

Suggests (optional; used by the downstream comparison scripts): MSstats, ggplot2, stringr, testthat.

External: Ollama running locally with the desired models pulled (required for live inference).

Citation

If you use this work, please cite the associated ASMS 2026 poster (citation details TBD).

About

In development package to replace the converters in MSstatsConvert with (local) LLMs. This work was present at ASMS 2026.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages