') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - KatherLab/MillerQA: Curated benchmark suite for multi-dimensional clinical LLM evaluation · GitHub
Skip to content

Repository files navigation

MillerQA

Benchmark data and zero-shot inference harness accompanying the paper.

This repository contains two things:

  1. The benchmark suite — 24 multiple-choice benchmarks, released in both their original (pre-QC) and curated (post-QC) form. 23 of the 24 are redistributed here; MedBullets is omitted for licensing reasons (see Licensing) and is linked instead.
  2. The zero-shot harness — the code used to run the models over the suite via OpenRouter.
benchmarks/
pre_qc/ 23 of 24 shipped, 9,712 items — the original suite, before QC
<source>/ one directory per upstream dataset
LICENSE that dataset's licence and attribution
*.json the benchmarks derived from it
curated/ 23 of 24 shipped, 9,406 items — the post-QC suite used in the paper
<source>/ same 15 source directories
LICENSES.csv benchmark → source → SPDX identifier, machine-readable
_manifest.csv per-benchmark item counts, pre-QC vs curated
_dropped_items.csv every item removed by QC, with the reason
harness/ zero-shot inference code
docs/ figures used in this README
LICENSE licensing for the collection — read this first
requirements.txt
.env.example

The 24 benchmarks come from 15 upstream sources under several different licences, so they are grouped by source and each group carries its own LICENSE. See Licensingtwo subsets are non-commercial only, and one is not redistributed at all.

The paper analyses the full suite of 24 benchmarks (10,020 pre-QC / 9,594 curated). This repository ships 23 of them (9,712 / 9,406); MedBullets is excluded, with its item ids provided so the full suite can be reconstructed.

The benchmark suite

The 24 benchmarks, grouped into five competency dimensions

The suite spans five competency dimensions. Counts below are per benchmark, in both releases; the source dataset is given as its Hugging Face identifier where the set was pulled from the Hub.

Clinical Judgment

BenchmarkFilePre-QCCuratedSource datasetLicenceDescription
CUPCasecupcase700700CUPCaseApache-2.0Diagnosis from complex real-world clinical case reports
HLEhle146128cais/hleMITHumanity's Last Exam — expert-level frontier questions; biology/medicine subset, text-only
MHQAmhqa624616MHQACC BY-NC 2.0Mental-health research literature QA
MedCalcmedcalc340340ncbi/MedCalc-Bench-v1.0CC BY-SA 4.0Medical calculation from clinical vignettes; integer/decimal outputs
MetaMedQAmetamedqa676658Maximegmd/MetaMedQACC BY 4.0USMLE-style QA with unknown / unanswerable options (metacognition)
Triagetriage8684NLie2/TRIAGECC BY 4.0Mass-casualty triage, recast as MCQ over a fixed 4-tier scale

Robustness and Fairness

BenchmarkFilePre-QCCuratedSource datasetLicenceDescription
FairMedQAfairmedqa960960FairMedQACC BY 4.0Demographic bias in clinical QA via counterfactual vignettes (race / income / gender)
Global MMLUglobal_mmlu750705Global MMLUApache-2.0Multilingual professional-medicine exam questions across 15 languages
TruthfulQAtruthfulqa790790TruthfulQAApache-2.0Truthfulness and resistance to common misconceptions

Ethical and Professional Reasoning

BenchmarkFilePre-QCCuratedSource datasetLicenceDescription
MMLU-Pro (Phil)mmlupro_phil237200TIGER-Lab/MMLU-ProMITPhilosophy, formal logic and moral-disputes categories
MedEthicsQAmedethicsqa1,000928MedEthicsQACC BY-NC 4.0Medical ethics, stratified across the four core principles
TridentBenchtridentbench854842TridentBenchMITIdentifying which AMA Principle of Medical Ethics a scenario violates

Medical Knowledge

BenchmarkFilePre-QCCuratedSource datasetLicenceDescription
MMLU-Pro (Med)mmlupro_med326252TIGER-Lab/MMLU-ProMITAdvanced clinical-knowledge and professional-medicine exam questions
MedBullets⚠️medbullets308188LangAGI-Lab/medbullets_op5not redistributedUSMLE Step 2/3-style clinical vignettes — data file not included, see Licensing
PubMedQApubmedqa1,000998qiaojin/PubMedQAMITYes / no / maybe QA over biomedical research abstracts

Legal and Regulatory

BenchmarkFilePre-QCCuratedSource datasetLicenceDescription
LB (ipi)ipi_legalbench133133nguha/legalbenchCC BY 4.0Insurance policy interpretation — is the claim covered?
LB (lhb)lhb_legalbench6665nguha/legalbenchCC BY 4.0Learned Hands: does the post concern public benefits or social services?
LB (lhh)lhh_legalbench226225nguha/legalbenchCC BY 4.0Learned Hands: does the post concern health care or medico-legal issues?
LB (oad)oad_legalbench10095nguha/legalbenchCC BY 4.0OPP-115: user access, edit and deletion clauses
LB (odr)odr_legalbench10094nguha/legalbenchCC BY 4.0OPP-115: data-retention clauses
LB (ods)ods_legalbench10097nguha/legalbenchCC BY 4.0OPP-115: data-security clauses
LB (ofp)ofp_legalbench100100nguha/legalbenchCC BY 4.0OPP-115: first-party collection and use clauses
LB (otp)otp_legalbench10098nguha/legalbenchCC BY 4.0OPP-115: third-party sharing and collection clauses
LB (tos)tos_legalbench298298nguha/legalbenchCC BY 4.0Classifying potentially unfair Terms-of-Service clauses

Full citations for each source dataset are given in the paper.

Licensing

This repository is a mixed-licence collection. There is no single licence covering all of it, and the top-level LICENSE does not grant rights over the benchmark data.

Per-source licences. Each of the 15 upstream datasets has its own directory inside both releases, carrying that dataset's LICENSE file — its SPDX identifier, a link to the full licence text, the upstream source, and a record of how we modified the data. benchmarks/LICENSES.csv gives the same mapping in machine-readable form.

benchmarks/curated/legalbench/LICENSE ← CC BY 4.0, covers the 9 LB sets
benchmarks/curated/legalbench/tos_legalbench.json

Non-commercial subsets. Two benchmarks may not be used commercially under any circumstances, and any commercial use of this repository must exclude them:

BenchmarkSourceLicence
mhqaMHQACC BY-NC 2.0
medethicsqaMedEthicsQACC BY-NC 4.0

medcalc is CC BY-SA 4.0, which additionally imposes share-alike on adapted versions of that subset.

Subset that is not redistributed.medbullets is part of the suite as analysed, but its data file is not included in this repository. No licence could be established — neither the Hugging Face mirror we drew from nor the originating repository declares one — and the underlying questions derive from a commercial question bank, so we do not redistribute them.

benchmarks/<release>/medbullets/ still contains a LICENSE explaining the omission and an item_ids.csv listing the exact ids in each release, so the full suite can be rebuilt from the upstream source. The harness runs the other 23 benchmarks unchanged when the file is absent.

Our contributions — the harness code, the QC layer, the harmonised schema and the option sets we wrote when recasting source tasks as multiple choice — are released under CC BY 4.0. The data items themselves are not ours to relicense.

The CC licences require attribution and an indication of changes: when reusing a subset, cite the original dataset alongside this work, and see that source directory's LICENSE for what we changed.

Benchmark format

Every file is a JSON array of items with a common schema:

{
"id": "professional_medicine/test/131/en",
"question": "A 62-year-old man ...",
"options": ["Begin ...", "Order ...", "Discharge ...", "Consult ..."],
"target": 2,
"kind": "professional_medicine"
}

target is the 0-based index into options of the correct answer, in the order given in the file. kind is an optional subcategory label and is absent in some benchmarks. Question text and options are kept fully separable, which is what makes the choice-only baseline possible.

pre-QC vs curated

pre_qc/ is the suite as originally assembled: 10,020 items, of which 9,712 are shipped here. curated/ is the same suite after the quality-control pass described in the paper: 9,594 items, of which 9,406 are shipped here, with defective items excluded and duplicates removed. Three benchmarks were rebuilt from source during QC rather than filtered — medcalc, triage and truthfulqa — and both directories carry the rebuilt versions, so the two releases hold the same 24 benchmark names and differ only in which items survive. The replaces_retired column of _manifest.csv records the original name in each case.

_manifest.csv gives the per-benchmark counts on both sides; _dropped_items.csv lists all 447 removed items with the QC check that caught each one and the decision taken. Results reported in the paper are computed on the curated set, which is also the harness default.

Running the zero-shot harness

1. Install

Python 3.10+ (the code uses X | Y type syntax).

python -m venv .venv &&source .venv/bin/activate
pip install -r requirements.txt

2. Configure

cp .env.example .env

Then edit .env and set OPENROUTER_API_KEY. Everything else is optional; the defaults shown in that file are the ones used for the paper — notably SHUFFLE_SEED=42, which controls answer-option shuffling and must be kept unchanged to reproduce the published runs.

3. Run

All commands are run from inside harness/.

cd harness
# Print the execution plan without spending anything — always start here
python run_experiment.py --dry-run
# One model, one benchmark
python run_experiment.py --models gpt-4o --benchmarks fairmedqa
# The full paper run: every model configuration × all 24 benchmarks
python run_experiment.py
# Run against the original pre-QC release instead
python run_experiment.py --data-dir ../benchmarks/pre_qc --experiment-id zeroshot_preqc

The full run is 26 model configurations × 9,406 shipped items ≈ 245k API calls (9,594 if you reconstruct MedBullets). Cost and time are substantial. Use --dry-run first and scope with --models / --benchmarks.

Useful flags:

FlagEffect
--models LABEL ...Restrict to model labels from harness/config.py (default: all 26)
--benchmarks NAME ...Restrict to benchmark file stems (default: all 24)
--data-dir PATHBenchmark directory to load (default: benchmarks/curated)
--experiment-id IDOutput folder name under results/ (default: zeroshot_v1)
--concurrency NMax in-flight API calls (default: 25)
--retry-failedRe-run only items that errored or failed to parse
--forceIgnore resume state and re-run everything
--dry-runPrint the plan and exit without calling the API
-vDebug logging

Runs are resumable: results are appended per model/benchmark, and re-running the same command picks up where it left off rather than repeating completed items. Use --force only when you intend to discard prior results.

4. Output

results/<experiment_id>/
<model_label>/<benchmark>.jsonl one JSON record per item
csv/master.csv all records flattened
csv/by_model/<model_label>.csv
csv/by_benchmark/<benchmark>.csv
run.log

Each record carries the prompt condition, the model's answer, and accounting: permutation and correct_letter (the shuffle actually shown to this model), selected_letter, selected_original_index, is_correct, confidence, explanation, raw_content, token counts, cost_usd, latency_ms, and error. Because options are shuffled per (item, model), correctness is scored by mapping the selected letter back through permutation to the original option index and comparing against target — never by comparing letters directly.

is_correct is null for items with no parseable answer (a refusal, a truncation, a malformed response). These are unscored, not wrong; the paper drops them from the denominator and reports the counts separately.

What is in harness/

FileRole
run_experiment.pyCLI entry point
config.pyPaths, API settings, and the 26-entry model registry
data_loader.pyLoads and normalises the benchmark JSON
prompt_builder.pyDeterministic per-(item, model) option shuffling and prompt assembly
api_client.pyOpenRouter calls, retries, reasoning-config handling
batch_runner.pyConcurrency, resume, per-model orchestration
output_parser.pyResponse parsing into the flat record schema
make_csv.pyJSONL → CSV

The model registry in config.py holds the 26 configurations that were dispatched. Four of them returned an API error on every request and produced no usable data (gpt-5, o3-mini, lfm2-8b, aion-2-0_base), so the paper reports 22 configurations across 19 models; they are left in the registry so the run is reproducible as executed. Models tested both with and without reasoning appear twice, with different labels and a reasoning_config that is passed through to the provider. Some entries carry model-specific max_tokens; the comments in that file record why.

Citation

Please cite the accompanying paper. Individual benchmarks are derived from previously published sources and remain subject to their original licences; see the paper for the full provenance of each of the 24 sets.

About

Curated benchmark suite for multi-dimensional clinical LLM evaluation

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages