This is a small local web app for manual structural elucidation annotation.
The app can run in an assisted evidence mode where ICEBERG candidates and predicted spectra are shown as a starting point for manual review. Use this mode only when model evidence is allowed for the annotation task.
The assisted mode is organized like a lightweight project dashboard: it shows a progress snapshot, an RT vs precursor m/z feature map, the feature catalog, and per-case ICEBERG evidence before the annotator saves a final manual structure.
For each case, annotators can save:
- final structure SMILES
- formula, adduct, InChI, InChIKey
- confidence and reasoning notes
- per-peak assignments:
- precursor / isotope / fragment / neutral loss / adduct-related / noise / ambiguous
- fragment structure note or fragment SMILES
- fragment formula
- neutral loss
- theoretical m/z
- confidence
- evidence note
cd ms-ms-pred-annotations
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtUse this workflow when the project owner provides a zipped assisted-case bundle,
for example assisted_cases.zip. This bundle should already contain one folder
per test case, with raw spectra plus ICEBERG starting-point evidence.
First clone and install the app:
git clone https://github.com/Zi-hang-Zhou/ms-ms-pred-annotations.git
cd ms-ms-pred-annotations
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtPut the provided asset under data/ and unzip it:
mkdir -p data
unzip /path/to/assisted_cases.zip -d data/After unzipping, the case directory should look like this:
data/assisted_cases/
case_001/
metadata.json
experimental_spectra_long.csv
iceberg_candidates.csv
iceberg_predicted_spectra_long.csv
formula_predictions.csv
case_002/
...
Start the local annotation app:
ANNOTATION_CASE_DIR=data/assisted_cases \
ANNOTATION_OUTPUT_DIR=annotations_collaborator \
ANNOTATION_ENABLE_MODEL_EVIDENCE=1 \
PORT=7861 \
python app.pyOpen:
http://127.0.0.1:7861
The annotator should now see the raw MS/MS spectrum, ICEBERG candidate structures, candidate SMILES, formula predictions, and predicted peak evidence. Click Fill final structure to copy the selected ICEBERG candidate into the manual final-structure fields, then edit or replace it as needed.
Click Save after each case. Saved annotations are written locally to:
annotations_collaborator/<case_id>.json
Export final results while the app is still running:
http://127.0.0.1:7861/api/export/final_structures.csv
http://127.0.0.1:7861/api/export/annotations.json
python app.pyOpen:
http://127.0.0.1:7861
The default case directory is:
example_cases
The default annotation output directory is:
annotations
The included demo case also contains small synthetic ICEBERG-style evidence files, so you can preview assisted mode without any private dataset:
ANNOTATION_ENABLE_MODEL_EVIDENCE=1 python app.pyIn assisted demo mode, open http://127.0.0.1:7861 and select
case_demo_001. The Final Structure panel will show demo candidate structures,
candidate SMILES, formula predictions, and predicted peak evidence.
Point ANNOTATION_CASE_DIR to a directory containing one subdirectory per case:
ANNOTATION_CASE_DIR=/path/to/cases \
ANNOTATION_OUTPUT_DIR=/path/to/annotations \
PORT=7861 \
python app.pyExample case layout:
cases/
case_001/
metadata.json
experimental_spectra_long.csv
case_002/
metadata.json
experimental_spectra_long.csv
metadata.json should contain only public/blind metadata, for example:
{
"case_id": "case_001",
"feature": {
"precursor_mz": 181.0713,
"retention_time_min": 3.42,
"method": "LC-MS/MS",
"instrument": "QTOF",
"adduct": "[M+H]+",
"ion_mode": "positive"
}
}experimental_spectra_long.csv must contain at least:
case_id,spectrum_id,spectrum_type,collision_label,collision_value_nce,collision_energy_ev,peak_count,mz,intensity_raw,intensity_display
Rows are one peak per line. Multiple spectra for the same case are represented
with different spectrum_id values.
If your spectra are in MGF format, convert them into case directories first:
python scripts/convert_mgf_to_cases.py \
--mgf /path/to/spectra.mgf \
--out converted_cases \
--prefix msnlibThen run the app on the converted cases:
ANNOTATION_CASE_DIR=converted_cases python app.pyOr specify a custom annotation output directory:
ANNOTATION_CASE_DIR=converted_cases \
ANNOTATION_OUTPUT_DIR=annotations_msnlib \
python app.pyFor a quick test:
python scripts/convert_mgf_to_cases.py \
--mgf /path/to/spectra.mgf \
--out converted_cases_smoke \
--prefix msnlib \
--limit 5By default, the converter omits structure-like MGF fields such as SMILES,
INCHI, INCHIKEY, and INCHI_AUX. It also omits FORMULA unless you pass
--keep-formula, if FORMULA is present in the MGF metadata.
Use this workflow only if the provided asset is not already an assisted-case bundle. For example, the asset may contain:
spectra.mgf
cand_labels.tsv
iceberg_out/binned_preds.hdf5
Convert the raw spectra first:
python scripts/convert_mgf_to_cases.py \
--mgf data/raw_iceberg/spectra.mgf \
--out data/base_cases \
--prefix caseThen pack the ICEBERG output into annotation cases:
python scripts/pack_iceberg_outputs_to_cases.py \
--base-cases data/base_cases \
--iceberg-hdf5 data/raw_iceberg/iceberg_out/binned_preds.hdf5 \
--labels data/raw_iceberg/cand_labels.tsv \
--out data/assisted_cases \
--case-id-column case_id \
--top-n 20 \
--overwriteThen start the app with:
ANNOTATION_CASE_DIR=data/assisted_cases \
ANNOTATION_OUTPUT_DIR=annotations_collaborator \
ANNOTATION_ENABLE_MODEL_EVIDENCE=1 \
python app.pyIf you have raw ICEBERG prediction output, first make blind case directories from the experimental spectra, then merge the ICEBERG evidence into those cases.
The packer needs three inputs:
- base case directories containing
metadata.jsonandexperimental_spectra_long.csv - the ICEBERG prediction HDF5, usually
preds.hdf5orbinned_preds.hdf5 - the ICEBERG
dataset-labelsTSV/CSV that was used for prediction, containing candidate SMILES
The HDF5 alone is not enough because it usually contains predicted spectra but not the original experimental spectra or complete candidate metadata.
Single-case example:
python scripts/pack_iceberg_outputs_to_cases.py \
--base-cases converted_cases/case_001 \
--iceberg-hdf5 /path/to/iceberg_out/binned_preds.hdf5 \
--labels /path/to/cand_labels.tsv \
--out assisted_cases \
--case-id case_001 \
--top-n 20 \
--overwriteFor multi-case labels, include a case_id column in the labels file, or pass
the column name:
python scripts/pack_iceberg_outputs_to_cases.py \
--base-cases converted_cases \
--iceberg-hdf5 /path/to/iceberg_out/binned_preds.hdf5 \
--labels /path/to/cand_labels_with_case_id.tsv \
--case-id-column case_id \
--out assisted_cases \
--top-n 20The script writes:
iceberg_candidates.csv
iceberg_predicted_spectra_long.csv
iceberg_collision_matching.csv
formula_predictions.csv
Then run the app on the packed cases:
ANNOTATION_CASE_DIR=assisted_cases \
ANNOTATION_ENABLE_MODEL_EVIDENCE=1 \
python app.pyIf your case directories are ICEBERG bundles containing:
iceberg_candidates.csv
iceberg_predicted_spectra_long.csv
formula_predictions.csv
you can show ICEBERG top candidates and predicted peaks in the UI:
ANNOTATION_CASE_DIR=/home/zihang/ms-pred/pipeline/bundles/msnlib_strict_100_paper \
ANNOTATION_OUTPUT_DIR=annotations_msnlib_iceberg \
ANNOTATION_ENABLE_MODEL_EVIDENCE=1 \
python app.pyOptional limits:
ANNOTATION_MODEL_EVIDENCE_TOP_N=20
ANNOTATION_MODEL_EVIDENCE_PEAKS_PER_CANDIDATE=12In this mode, the Final Structure panel includes an "ICEBERG Starting Point" section. Each ICEBERG candidate is shown with a RDKit-rendered 2D structure, its SMILES, rank, entropy distance, and predicted peak evidence. Select a candidate and click "Fill final structure" to fill the manual final-structure fields with its SMILES, adduct, InChIKey, and a short note. This does not copy anything to the system clipboard.
The UI does not generate or upload these candidates. They are loaded from the
ICEBERG output files already present in ANNOTATION_CASE_DIR. A fresh GitHub
clone will not automatically show top-20 candidates unless you provide those
ICEBERG bundle files and run with ANNOTATION_ENABLE_MODEL_EVIDENCE=1.
The case catalog also shows the top formula and ICEBERG rank-1 structure summary for each feature, and the top overview panel tracks annotation progress and available model evidence.
When an annotator clicks Save, one JSON file is saved per case under the annotation output directory:
annotations/<case_id>.json
If you run with a custom output directory, for example:
ANNOTATION_CASE_DIR=converted_cases \
ANNOTATION_OUTPUT_DIR=annotations_msnlib \
python app.pythen saved annotations are written to:
annotations_msnlib/<case_id>.json
Export endpoints are available only while python app.py is still running:
http://127.0.0.1:7861/api/export/final_structures.csvhttp://127.0.0.1:7861/api/export/annotations.json
Open these URLs in a browser to view or download the exported CSV/JSON
These endpoints summarize the saved local annotation JSON files. They do not upload data anywhere.
In default blind mode, the /api/cases/<case_id> endpoint returns only:
- public feature metadata
- experimental spectra peak lists
- the current saved manual annotation, if any
It does not return candidate structures, ground truth structures, model scores,
or predicted spectra. Do not include those fields in metadata.json if the
deployment is intended to be blind.
When ANNOTATION_ENABLE_MODEL_EVIDENCE=1, the endpoint also returns
model_evidence parsed from ICEBERG files in each case directory. Ground-truth
structures are still not intentionally returned.