Resolve bioinformatics identifiers into confidence-scored knowledge graphs.
Given a gene identifier (any species — NCBI gene ID, symbol, VEuPathDB
locus tag, UniProt accession, Ensembl ID, ...), BioNym gathers evidence
from public APIs (NCBI, VEuPathDB, OMA, UniProt, GEO, Expression Atlas,
...), asks JEV (TypeSafe systemone) an ordered
workflow of typed questions, and emits a knowledge graph where every claim
carries a confidence score and a list of supporting evidence.
All pipeline stages implemented: S0 (ID classification), S1 (entity
resolution → organism + assembly), S2 (related & newer assemblies with
species-rank lineage normalization), S3 (orthologs via OMA), S4 (UniProt
annotation: GO evidence-code confidence, KEGG, InterPro/Pfam), S5
(expression: GEO + Expression Atlas, JEV-scored), S6 (cross-assembly
presence: annotated_in + JEV likely_present).
On top of the staged pipeline, two optional LLM passes propose claims that
JEV then verifies: text claims over publication/dataset metadata, and data
claims over per-gene measurements (VEuPathDB ExpressionGraphs, GEO GDS SOFT
files, GXA baseline/differential TSVs). A third pass can synthesize a
JEV-verified gene summary into metadata.summary.
Interfaces: CLI (bionym resolve, bionym summarize, bionym ask), self-contained HTML report (--report), FastAPI backend (backend/), static web frontend (web/, D3 multi-partite network). The pip/conda package ships the CLI + library + report; the backend and frontend are in this repo only.
ask answers a free-form question over a resolved graph: JEV first
classifies the question into graph categories, the matching subgraph
is sliced out, the LLM answers over that slice (citing node ids), and
JEV scores accuracy/completeness — deterministic citation fidelity
checked against the full graph on top.
pip install bionymFrom source (editable, with test/dev dependencies):
conda create -n bionym python=3.11 -y
conda activate bionym
pip install -e '.[dev]'cp .env.example .env # then editTYPESAFE_API_KEY— required for real JEV calls.NCBI_API_KEY— optional, raises NCBI rate limit 3/s → 10/s.VEUPATHDB_API_KEY— optional, enables VEuPathDB lookups (release 71+).BIONYM_CACHE_DIR— optional response/JEV cache.LLM_API_KEY+LLM_MODEL— optional, enables the proposal passes (claims, summary). Any OpenAI-compatible endpoint;LLM_BASE_URLdefaults to OpenRouter. Disabled when unset — the resolver runs fine without them, you just get no proposed claims or summary.BIONYM_PROPOSAL_MIN_SCORE(default 0.5) — JEV triage score a dataset/publication needs before the LLM spends a call on it.BIONYM_PROPOSAL_CONCURRENCY(default 8) — max simultaneous LLM calls.JEV_BASE_URL— optional; repoint the systemone endpoint at any compatible backend (Laya, Kev, CLM all speakPOST /v1/systemone).JEV_MODEL— optional (defaultjev-latest); model string sent in the request — set it when pointing at a non-jev backend.LLM_TIMEOUT— optional, seconds (default 180); whole-graph asks can exceed the old 60s on slow models.BIONYM_ALLOW_MOCK— server only;=1permitsmock_jev=trueon API requests (offline dev/evals). Off by default.
bionym resolve PF3D7_0710100 -o graph.json -v
bionym resolve 672 --depth 0 # classify only
bionym resolve 672 --report # + self-contained HTML report
bionym resolve 672 --min-confidence 0.7 # drop low-confidence edges
bionym resolve 672 --no-propose # quick scan: skip LLM claim passes
bionym resolve 672 --summarize # + JEV-verified summary in metadata
bionym resolve 672 --mock-jev # offline dev, no API key
bionym summarize graph.json # summary pass on an existing graph
bionym ask graph.json "what datasets are associated?"
# classify → slice → LLM answer → JEV scores
--mock-jevis for development only. It returns deterministic placeholder answers (first option / midpoint score / 0.5) instead of real JEV judgments, so the resulting graph will not be biologically meaningful — edges and confidences are arbitrary. It's a free way to exercise the full pipeline and surface technical bugs (crashes, parsing, wiring) without an API key or cost. Never interpret mock output as a result.
Output: a JSON knowledge graph — nodes (typed: Gene, Organism, Assembly,
IdType, ...; each with an external url), edges (claims with
confidence, probabilities, jev_question_id, evidence[]), and
metadata (outcome: resolved|refused, stages + stage_times,
jev_usage per-stage token counts, optional summary: JEV-verified
claim list with claim/quote/confidence).
# backend (needs the package installed + .env)
cd backend && uvicorn main:app --port 8001
# frontend (static; also works opened directly or from GH Pages)
cd web && python -m http.server 8080
# open http://localhost:8080 — backend URL is set in web/index.html (BACKEND const)The UI offers Quick scan (propose=false — deterministic stages + JEV
edge scoring only, seconds) vs Full analysis (propose+summarize,
minutes). The verified summary renders in a collapsible section above the
network. API: GET /api/resolve?identifier=…&depth=…&propose=…&summarize=…,
POST /api/summarize (summary pass over a previously returned graph),
and POST /api/ask (ask-the-graph over a previously returned graph —
powers the collapsible Q&A panel in the UI).
- JEV questions are Python-templated only — no LLM generates questions or
candidate options.
criteriaalways come from API result sets; every Choice question has anone/otherescape. JEV judges, never enumerates. - The one exception: claims. Free-text biological claims can't be enumerated from an API result set, so the proposal passes (text claims, data claims, summary) let the LLM propose candidates — the only place it generates content. The asymmetry holds: every proposal is verified by JEV against the serialized source (dataset metadata, per-sample values, or the graph itself) before it lands. The LLM proposes, JEV disposes — it never scores its own output.
- Every edge must carry ≥1
Evidencerecord (source, endpoint, timestamp, payload) so "why" is always answerable. - Unbounded API result sets are capped before JEV/LLM stages so a single
state can't overflow the model context or spend unbounded calls:
ortholog candidates keep the top 100 by OMA score (S3), and
datasets/publications keep the top 100 per kind by triage score
(
proposals.MAX_ITEMS_PER_KIND). These are hard-coded for now — if a use case needs deeper coverage they should become configurable.
src/bionym/ core library + CLI (no web deps)
jev.py systemone client: batch questions, token logging,
mock mode, JEV_BASE_URL for Laya/Kev/CLM backends
llm.py proposal/summary/ask LLM client (mock mode for dev)
ask.py ask-the-graph orchestration (classify→slice→answer→score)
evidence.py Evidence records
graph.py KnowledgeGraph: nodes/edges/confidence, url + filtering
proposals.py LLM propose-then-JEV-verify: prompts, serializers, parsers
questions/ staged JEV question builders (s0–s6) + ask
classify/slice builders
clients/ one thin client per data source
resolver.py stage orchestration + proposal/summary passes
report.py self-contained HTML report (D3 network + tables)
backend/ FastAPI deployable
web/ static frontend (D3 multi-partite network)
evals/ corpus + runner + differ for real-ID eval runs
(see evals/README.md; results committed as JSONL)
tests/ pytest; JEV/LLM mocked or faked, backend via
TestClient