Metadata-Version: 2.4
Name: biosingularity
Version: 0.1.0
Summary: Deterministic, receipt-bearing verifier for biomedical claims — the trust layer beneath biomedical AI
Author: Mark Hahnel
License: MIT
Project-URL: Homepage, https://biosingularity.netlify.app
Project-URL: Repository, https://github.com/markhahnel/biosingularity
Project-URL: Documentation, https://github.com/markhahnel/biosingularity/tree/main/docs
Project-URL: Issues, https://github.com/markhahnel/biosingularity/issues
Keywords: biomedical,verification,provenance,retraction,clinvar,gnomad,acmg,variant,mcp,llm,hallucination,grounding,trust,claude
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Provides-Extra: store
Requires-Dist: psycopg[binary]>=3.1; extra == "store"
Provides-Extra: openalex
Requires-Dist: duckdb>=1.0; extra == "openalex"
Provides-Extra: llm
Requires-Dist: anthropic>=0.69; extra == "llm"
Provides-Extra: nlp
Requires-Dist: openmed[hf]>=1.5; extra == "nlp"
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; extra == "mcp"
Provides-Extra: crypto
Requires-Dist: cryptography>=42; extra == "crypto"
Dynamic: license-file

# biosingularity

> The substrate determines the ceiling. This is the trust layer beneath biomedical AI.

A provenance & groundedness verifier for biomedical evidence chains. Give it a
claim, a DOI, or a reference list, and it returns a verdict — **grounded /
unsupported / contradicted / built-on-retracted** — with the receipt every time.

It does **not** try to be another evidence-aggregation platform (Causaly, the free
Open Targets) or another benchmark (CZI's Open Problems, Arc's Virtual Cell
Challenge). It is the connective trust tissue those tools lack: *is this statement
true, and where exactly does it come from?*

## v1 (this repo) — the smallest useful slice

Audit a reference list for **retracted and unverifiable sources** against PubMed.
Crisp ground truth, real pain (systematic reviews routinely, unwittingly cite
retracted work), demonstrably correct.

```bash
# zero pip installs needed — the audit runtime is stdlib-only
PYTHONPATH=src python -m biosingularity audit examples/sample.bib

# GROUND a whole free-text answer in ONE call — decompose -> verify every part -> one verdict + pass/review/reject
PYTHONPATH=src python -m biosingularity ground \
    "APP is linked to Alzheimer's disease (10.1038/nature04533), and warfarin with ibuprofen is safe."
    # -> 🔴 REJECT: retracted citation + a MAJOR interaction caught; the immunity-system call for biomedical AI

# audit specific DOIs, drugs (ChEMBL), and trials (ClinicalTrials.gov)
PYTHONPATH=src python -m biosingularity audit --doi 10.1038/nature04533
PYTHONPATH=src python -m biosingularity audit examples/sample.bib \
    --drug aducanumab --drug cerivastatin --nct NCT02477800
PYTHONPATH=src python -m biosingularity audit examples/sample.bib --json

# persist findings into the substrate (needs psycopg + a Postgres at BIOSINGULARITY_DATABASE_URL)
PYTHONPATH=src python -m biosingularity audit examples/sample.bib --persist

# resolve offline against a local OpenAlex snapshot (no API; set the path)
export BIOSINGULARITY_OPENALEX_DUCKDB=/path/to/openalex.duckdb
PYTHONPATH=src python -m biosingularity audit examples/sample.bib --source openalex

# verify a claim against a paper's PMC full text (needs the crosswalk loaded — see below)
PYTHONPATH=src python -m biosingularity verify-claim --doi 10.1186/s40478-014-0135-5 \
    "the amyloid hypothesis has driven drug development strategies for Alzheimer's disease"

# add an LLM entailment judgment over the retrieved passages (opt-in, paid API call)
export ANTHROPIC_API_KEY=sk-ant-...          # judge model: BIOSINGULARITY_JUDGE_MODEL (default claude-opus-4-8)
PYTHONPATH=src python -m biosingularity verify-claim --judge --doi 10.1186/s40478-014-0135-5 \
    "the amyloid hypothesis has driven drug development strategies for Alzheimer's disease"

# serve the web report page
PYTHONPATH=src python -m biosingularity.server --port 8765

# MCP server — let ANY agent (Claude Desktop, Claude Science, …) ground its claims by calling the verifiers
pip install 'biosingularity[mcp,store]'
biosingularity-mcp        # stdio; tools: audit_references, verify_variant, corroborate_gene_disease, check_drug_interaction, provenance, scan_folder, …
# Claude Desktop — add to claude_desktop_config.json:
#   { "mcpServers": { "biosingularity": { "command": "biosingularity-mcp" } } }
# Connector setup (Desktop / Code / Science + one-click .mcpb bundle): docs/CONNECTOR.md

# DEMO — ground a fluent (but wrong) AI answer: 1 true claim affirmed, 3 errors caught (retracted/contradicted/MAJOR)
PYTHONPATH=src python examples/ground_your_output.py

# trace a paper's funding (which grants funded it): PubMed GrantList (all funders),
# enriched with NIH RePORTER, EU/EC (OpenAIRE) and UKRI (Gateway to Research) detail
PYTHONPATH=src python -m biosingularity grants --pmid 28766406 --persist
PYTHONPATH=src python -m biosingularity grants --doi 10.1038/s41586-019-1373-2   # EU/H2020 grants
PYTHONPATH=src python -m biosingularity grants --pmid 38381819                    # UKRI council grants
PYTHONPATH=src python -m biosingularity grants --grant R01AI104987               # a grant -> what it produced

# verify a gene/target against genetic + functional evidence (local Open Targets datalake)
PYTHONPATH=src python -m biosingularity audit --target APP --disease "Alzheimer disease"

# verify a genetic variant against ClinVar — and CONTRADICT a false claim
PYTHONPATH=src python -m biosingularity audit --variant "CFTR p.Phe508del"
PYTHONPATH=src python -m biosingularity audit --variant "CFTR p.Phe508del" --asserts benign   # -> red, contradicted

# check a drug-drug interaction (safety) against DDInter
PYTHONPATH=src python -m biosingularity audit --interaction "warfarin + ibuprofen"   # -> red, MAJOR

# check a gene cancer-dependency claim against DepMap
PYTHONPATH=src python -m biosingularity audit --essential KRAS   # -> selective dependency

# verify a gene-disease association (MONDO-normalized) against DisGeNET
PYTHONPATH=src python -m biosingularity audit --gene-disease "APP : Alzheimer disease"   # -> supported

# CORROBORATE a gene-disease claim across DisGeNET + Open Targets + GWAS at once (consensus + confidence)
PYTHONPATH=src python -m biosingularity audit --corroborate "APP : Alzheimer disease"    # -> corroborated by 3 sources, 100%
PYTHONPATH=src python -m biosingularity audit --corroborate "ERBB2 : breast carcinoma"   # -> single-source (Open Targets), corroborate first

# verify a variant/gene-trait association against the GWAS Catalog (genome-wide significance)
PYTHONPATH=src python -m biosingularity audit --variant-trait "rs7903146 : type 2 diabetes"  # -> significant
PYTHONPATH=src python -m biosingularity audit --variant-trait "APOE : Alzheimer disease"     # -> rs429358, p<1e-300

# verify a gene-phenotype claim against HPO (Human Phenotype Ontology)
PYTHONPATH=src python -m biosingularity audit --phenotype "FBN1 : ectopia lentis"   # -> supported (Marfan)

# resolve drugs offline against a local ChEMBL mirror (no EBI dependency, fast & reliable)
PYTHONPATH=src python -m biosingularity audit --drug cerivastatin --drug-source local

# decompose a free-text claim into entities and verify each (genes -> Open Targets, drugs -> ChEMBL)
pip install 'biosingularity[nlp]'   # local OpenMed NER models (optional extra)
PYTHONPATH=src python -m biosingularity decompose \
    "Aducanumab targets APP to treat Alzheimer's disease" --audit

# score the substrate's OWN accuracy against a labelled claim set (precision/recall/F1, confusion matrix)
PYTHONPATH=src python -m biosingularity eval                                 # runs the full pipeline on data/eval_claims.json
# HONEST miss-rate: of N random known-true DisGeNET associations, how many does the pipeline confirm / cross-corroborate?
PYTHONPATH=src python -m biosingularity eval --recall 100                    # -> round-trip recall + cross-source agreement %

# scan a folder (thesis / manuscript / reading list) -> trust-metadata layer + a visual HTML dashboard
PYTHONPATH=src python -m biosingularity scan ~/thesis --html report.html --sidecars   # flags any retracted refs you cite

# durable provenance: everything the substrate knows about a reference (every flag, receipt, and when)
PYTHONPATH=src python -m biosingularity provenance 10.1038/nature04533       # -> retracted, with receipts + dates

# DISCLOSE data freshness — how old is the ground truth behind each datalake verdict? (every verdict now stamps its data age)
PYTHONPATH=src python -m biosingularity freshness            # -> ClinVar/GWAS undated, Open Targets 486d old, ...

# freshness: re-audit stored papers/drugs and flag any status DRIFT (cron-friendly; exit 2 on drift)
PYTHONPATH=src python -m biosingularity refresh --kind paper --limit 50

# evidence-handling micro-bench: does substrate-grounding lift a model's answers?
PYTHONPATH=src python -m biosingularity microbench --demo                    # deterministic, no API spend
PYTHONPATH=src python -m biosingularity microbench --model claude-opus-4-8   # live model (key-gated, paid)

# funder coverage: which of the top funders do we cover at depth vs breadth?
PYTHONPATH=src python -m biosingularity funders --gaps 10
PYTHONPATH=src python -m biosingularity funders --lookup "Wellcome Trust"
```

The datalake-backed commands read local Postgres mirrors — point them with
`BIOSINGULARITY_CHEMBL_DSN` (local ChEMBL) and `BIOSINGULARITY_OPENSCIENCE_DSN`
(Open Targets); both default to `postgresql://localhost:5432/{chembl_temp,openscience_local}`.

Pre-populate the DOI→{OpenAlex id, PMC id} crosswalk (one-time, from a local snapshot):
```bash
psql -d biosingularity -f db/migrations/0004_doi_crosswalk.sql
BIOSINGULARITY_CROSSWALK_DUCKDB=/path/to/crosswalk.duckdb scripts/load_crosswalk.sh
```

Exit code reflects the worst finding: `0` clean, `1` amber (unresolved / corrected
/ stopped-early / black-box), `2` red (retracted / withdrawn drug). Set
`BIOSINGULARITY_NCBI_API_KEY` / `BIOSINGULARITY_NCBI_EMAIL` to raise NCBI rate limits.

## Architecture

```
db/migrations/0001_init.sql   the provenance substrate (entities, aliases, edges, flags, claims, logs)
src/biosingularity/
  bibtex.py    DOI extraction (regex — exact, offline, free)
  pubmed.py    NCBI E-utilities client: resolve DOI -> PMID, fetch publication types + retraction links
  classify.py  pure rules: ArticleMeta / drug / trial / target -> flags  (never assert grounded without a receipt)
  audit.py     orchestration: DOIs -> resolve -> fetch -> classify -> AuditReport
  nlp.py       claim decomposition (OpenMed NER) -> disease/drug/gene entities  [nlp extra]
  funding.py   funding provenance: PubMed GrantList + NIH RePORTER -> FundingReport
  verify.py    PMC full-text claim retrieval;  judge.py  LLM entailment judge  [llm extra]
  models.py    frozen domain values
  report.py    red/amber/green rendering (text + JSON)
  cli.py       `biosingularity {audit,verify-claim,grants,decompose,microbench,funders}`
  sources/     pluggable resolvers: chembl(+_local), clinicaltrials, opentargets_local, clinvar_local, ddinter_local, depmap_local, ontology_local, disgenet_local, gwas_local, hpo_local,
               openalex_local, crosswalk, pmc_fulltext, nih_reporter, openaire, ukri, crossref, nsf, usaspending, europepmc, funder_registry
  microbench/  evidence-handling benchmark: baseline vs substrate-grounded, rubric grader
  store.py     persist audits/funding into the Postgres substrate  [store extra]
tests/         offline unit tests (no network) — 101 and counting
```

The schema deliberately mirrors a PID-first design: every entity carries
`external_ids` (DOI/PMID/PMCID/ChEMBL/NCT/ORCID/ROR), identity resolution lives in
`entity_aliases`, every edge and flag records its `source` and a receipt, and every
run is logged in `import_logs`.

## Tests

```bash
python -m unittest discover -s tests        # stdlib, zero deps
# or:  pip install -e ".[dev]" && pytest
```

## Roadmap

- [x] Persist audits into the substrate (`db/migrations/*.sql` + `store.py`, wired via `--persist`)
- [x] ChEMBL + ClinicalTrials checks (drug withdrawal, black-box, stopped-early trials) via `--drug` / `--nct`
- [x] Shareable report page (`server.py` + `web/`) — the free-academic adoption artifact
- [x] Offline resolution against a local OpenAlex snapshot (`--source openalex`)
- [x] DOI crosswalk — pre-populated PMC/OpenAlex ids stamped into `external_ids` on persist
- [x] PMC full-text **claim verification** (`verify-claim`) — locate where a paper states a claim
- [x] **LLM entailment judge** (`verify-claim --judge`) — supported / refuted / not-addressed, grounded in the retrieved passages
- [x] **Funding provenance** (`grants`) — PubMed GrantList (all funders) + NIH RePORTER enrichment, persisted as `funded_by` edges
- [x] **Genetic target-evidence** (`audit --target`) — verify a gene/target against the local Open Targets datalake
- [x] **Variant pathogenicity** (`audit --variant [--asserts]`) — verify a variant against ClinVar; first CONTRADICTED verdict (claim refuted by authoritative ground truth)
- [x] **Drug–drug interaction** (`audit --interaction`) — DDInter; a MAJOR interaction surfaces red
- [x] **Gene essentiality** (`audit --essential`) — DepMap cancer-dependency; a claimed dependency DepMap doesn't support is flagged
- [x] **Gene–disease association** (`audit --gene-disease`) — DisGeNET (publication-backed), MONDO-normalized; a claimed association absent from the authoritative set is flagged
- [x] **Variant–trait association** (`audit --variant-trait`) — GWAS Catalog; genome-wide significance (p<=5e-8) grounds the claim, a sub-threshold/absent association surfaces amber (L2G evaluated — unusable, rsid/gene keys empty — so GWAS Catalog is authoritative)
- [x] **Gene–phenotype corroboration** (`audit --phenotype`) — HPO; a free-text phenotype is resolved to its HP id (full-text), then the gene+phenotype annotation is the receipt; no annotation surfaces amber with data-richness context
- [x] **HGNC/MONDO entity normalization** (`ontology_local`) — gene aliases -> approved symbol, disease names -> MONDO id + xrefs; wired into the gene verifiers (HER2 -> ERBB2)
- [x] **Offline drug resolution** (`--drug-source local`) — local ChEMBL mirror; no EBI dependency
- [x] **Claim decomposition** (`decompose`) — OpenMed NER -> entities, multi-entity routing (gene+disease -> cross-source corroboration; gene-alone -> Open Targets; drug -> ChEMBL), a claim-level rollup verdict, and explicit 'unverifiable' (never a silent skip) when a datalake is down
- [x] **Evidence-handling micro-bench** (`microbench`) — measures the lift substrate-grounding gives a model
- [x] **Substrate accuracy eval** (`eval`) — labelled claim set run through the full pipeline; exact-severity accuracy per claim-kind + red/flagged precision/recall/F1 + confusion matrix; a regression gate (21-case v1, expandable)
- [x] **Web UI for claim decomposition + target evidence** (`server.py` tabs: Audit / Decompose / Verify a target)
- [x] **EU (OpenAIRE) + UKRI (Gateway to Research) funder enrichment** in `grants`, on top of NIH + the universal PubMed backbone
- [x] **Crossref long-tail funder breadth** — DOI -> all funders (Funder Registry `10.13039/*` ids + awards) across ~30k funders, persisted with canonical funder identity
- [x] **NSF depth enrichment** — Crossref's NSF award numbers -> NSF Awards API (title, PI, institution, $amount)
- [x] **Funder registry** (`funders`) — 740 top funders crosswalked to canonical Crossref Funder Registry ids; coverage scorecard (depth/breadth/attribution); cross-source funder dedup on persist
- [x] **USAspending.gov depth** — all US federal funders (DOE/CDC/SAMHSA/DoD/NASA/…, 71 in the registry) via federal award numbers Crossref surfaces; depth coverage 43%->70% of $
- [x] **Europe PMC** grantsList breadth — acknowledgement-mined / non-MEDLINE funder attribution (catches funders Crossref + PubMed GrantList miss)
- [ ] Evaluated, not cleanly integrable: OpenAlex grants (field empty), Gates/IATI (no award↔paper linkage), DFG/GEPRIS (HTML-only), KAKEN (key + JP), NHMRC/CIHR (brittle per-year CKAN), Europe PMC Grist depth (XML, fiddly grant-id linkage)
- [x] **Cross-source corroboration** (`audit --corroborate`) — one gene-disease claim fanned out to DisGeNET + Open Targets + GWAS at once; verdict is corroborated (≥2 agree, confidence via noisy-OR) / single-source / unsupported / **inconclusive** (a source was unreachable — never conflated with a real "no")
- [ ] `contradicts` / `supports` graph edges in the substrate (Consensus / scite enrichment)
- [x] Drug/trial auditing in the web UI (4th tab over /api/entities)
- [x] **Hosted datalake access** — `server.py` hardened for tunnel exposure (`BIOSINGULARITY_API_KEY` gate + CORS allow-list + `/api/health`); the SPA's datalake tabs point at a configurable `datalakeApiBase` (`web/config.js`). See [docs/DEPLOY.md](docs/DEPLOY.md) — run the local server behind a Cloudflare/Tailscale tunnel and every tab works live against the full 493GB.
- [x] **Durable provenance API** (`provenance` + `GET /api/provenance`) — any reference (DOI/PMID/drug/NCT/gene/pair) resolves through the alias graph to its full flag history with receipts + detection dates; the citable "audit_id → provenance record"
- [x] **Staleness / re-audit** (`refresh`) — re-audits stored papers/drugs stalest-first, persists fresh flags, and reports status DRIFT (e.g. a paper now retracted that was clean); exit 2 on drift so a cron can alert
- [x] **MCP server** (`biosingularity-mcp`) — exposes the verifiers as agent tools (audit_references, verify_variant [+contradiction], corroborate_gene_disease, check_drug_interaction, gene_essentiality, variant_trait, phenotype, check_drug/trial, provenance, scan_folder) so any MCP client can GROUND a claim before asserting it — the integration play: be the trust layer *beneath* generative biomedical AI, not a competitor to it
- [ ] Commercial slice: private audits + "substrate-verified" certification + metered API
