mirror of
https://github.com/mims-harvard/ToolUniverse.git
synced 2026-09-19 07:31:47 +08:00
Restructure the case-studies README as input, process, output (#541)
* Rewrite the case-studies README around the science, not the verification The README read like a test report. Its first substantive sentence was about matching published values, it carried three Published-vs-Observed tables, and the longest section was forensics on a ChEMBL outage. Someone arriving wants to run a scientific investigation, not audit a regression run. Restructure around what each case actually does: the question, where the evidence turns, and what the workflow concludes when two valid results point opposite ways. Each case now shows the pivotal moment as real output — the DepMap dependency that reframes BLM, the mechanism check that disqualifies nolasiban, the adjusted odds ratio and what it does not support. The verification material stays, but as a short "Reading the output" section: how the published: annotations work, which values are expected to drift and which are not, and the ChEMBL outage in three lines instead of thirty. 233 lines to 151. Every number shown is copied from an actual run. * Restructure the case-studies README as input, process, output The previous rewrite told each case as a narrative, which read better than the test report it replaced but still left the mechanics implicit: what you hand the script, what it does with it, what comes back. Give every case the same three headings. Input states the identifier or the data files and the question. Process is a table of steps with the tools each one calls and what it establishes, with the pivotal step marked. Output is the verdict, with the actual printed result above it. The header carries a diagram of that shape and a summary table, so the pattern is visible before reading any single case. Step and tool counts in the tables are extracted from the scripts rather than written by hand: 7 steps/15 tools, 6 steps/15 tools, 3 steps/1 tool in three modes. Every value quoted comes from a recorded run. * Stop pinning Case 2 to an unstable affinity value ChEMBL came back up, so the two steps that had never run against the live service finally did. Case 1's analog comparison reproduced exactly (DILI 0.987-0.992). Case 2's affinity lookup did not: it reported 0.5 nM against a published "Ki as low as 3.2 nM". The cause is not the data. ChEMBL returns activities unsorted, so the minimum over one page depends on which page you fetch: 0.50 nM at offset 0, 1.0 at offset 100, 0.09 at offset 200. Annotating that number against one published value makes the case appear to fail at random. Report what actually reproduces instead — that the receptor has nanomolar ligands — and label the sampled minimum as page-dependent rather than as a value being matched. The README's ChEMBL note is updated too: it claimed both steps were unconfirmed, which is no longer true. * Find the real lowest Ki for OXTR instead of sampling one page Case 2 reported 0.5 nM where the write-up says "Ki as low as 3.2 nM". Neither is the lowest measured affinity. ChEMBL returns activities unsorted and paginated, so the minimum over any single page is an artefact of which page was fetched: 0.50 nM at offset 0, 1.0 at offset 100, 0.09 at offset 200. ChEMBL holds 812 Ki values in nM for CHEMBL2049. The actual minimum is 0.025 nM (CHEMBL2037514, no validity flag, not a duplicate), and 182 of the 812 sit below the published 3.2 nM. The published figure is understated by roughly 130x because it too came from a partial sample. Page through the whole set rather than one page, restrict to Ki in nM, drop rows carrying a data_validity_comment, and report the count alongside the minimum so the figure is interpretable. The case's conclusion is unchanged, and better supported: OXTR has well-characterised high-affinity ligands.
This commit is contained in:
+164
-182
@@ -1,28 +1,25 @@
|
||||
# Case studies: setup and reproduction
|
||||
# Three research questions, answered end to end
|
||||
|
||||
Runnable versions of the three case studies in
|
||||
[Use Cases: Target Assessment, Druggability, and a Clinical Trial Reanalysis](https://aiscientist.tools/posts/tooluniverse-case-studies).
|
||||
Each script takes a research question, chains tools across databases,
|
||||
structures, screens and predictive models to answer it, and prints the evidence
|
||||
as it arrives. This is the same sequence the AI scientist ran in
|
||||
[the write-up](https://aiscientist.tools/posts/tooluniverse-case-studies).
|
||||
|
||||
Each script makes the same tool calls, in the same order, that the AI scientist
|
||||
made in the post, and prints the published value next to the one it just
|
||||
retrieved, so a run tells you either "this still reproduces" or exactly which
|
||||
number moved.
|
||||
Every case has the same shape:
|
||||
|
||||
The post reports headline numbers; the supplementary note it condenses
|
||||
("Step-by-step real-world case studies", in the ToolUniverse manuscript,
|
||||
[arXiv:2509.23426](https://arxiv.org/abs/2509.23426)) reports the intermediate
|
||||
ones. Both are annotated as `published:` here, so several values these scripts
|
||||
check — ACMG scores, PDB accessions, per-context DepMap effects, PubChem CIDs —
|
||||
are found in the supplementary note rather than in the post itself.
|
||||
```
|
||||
input process output
|
||||
───────────────────── ────────────────────────────────── ──────────────────
|
||||
a question + an N steps, each one or more tool a verdict, with
|
||||
identifier or a file calls, each step's result feeding every number
|
||||
the next traceable to a call
|
||||
```
|
||||
|
||||
| Case | Question | Script |
|
||||
|---|---|---|
|
||||
| 1 | Is *BLM* a context-selective anticancer target, and is ML216 a developable lead? | [`case1_blm_target_assessment.py`](case1_blm_target_assessment.py) |
|
||||
| 2 | Is *OXTR* druggable, and does its chemistry fit the autism hypothesis? | [`case2_oxtr_druggability.py`](case2_oxtr_druggability.py) |
|
||||
| 3 | Is BCG vaccination associated with higher adverse-event severity? | [`case3_bcg_ae_severity.py`](case3_bcg_ae_severity.py) |
|
||||
|
||||
Cases 1 and 2 read live databases, so their numbers drift as those databases are
|
||||
re-released. Case 3 runs on a frozen public dataset and reproduces exactly.
|
||||
| | Input | Process | Output |
|
||||
|---|---|---|---|
|
||||
| **[1](case1_blm_target_assessment.py)** | gene `BLM` | 7 steps, 15 tools | is it a target, and is ML216 developable? |
|
||||
| **[2](case2_oxtr_druggability.py)** | gene `OXTR` | 6 steps, 15 tools | is the chemistry right for the autism hypothesis? |
|
||||
| **[3](case3_bcg_ae_severity.py)** | 2 trial tables (CSV) | 3 steps, 1 tool in 3 modes | does BCG associate with worse adverse events? |
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -35,195 +32,180 @@ cd ToolUniverse/examples/case_studies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
No API keys are needed. Every database used here (Open Targets, ClinVar, GeneBe,
|
||||
AlphaFold, PDBe, Europe PMC, PubChem, ChEMBL) is open, and ADMET-AI runs
|
||||
locally.
|
||||
|
||||
Two things worth knowing:
|
||||
|
||||
- Without the **`ml` extra**, the ADMET-AI steps in Cases 1 and 2 report
|
||||
`ADMETModel requires 'admet-ai' package`. The rest of both cases still runs.
|
||||
- Case 3 gives identical results on pandas 2 and pandas 3.
|
||||
|
||||
## Running
|
||||
No API keys. Every database used — Open Targets, ClinVar, GeneBe, AlphaFold,
|
||||
PDBe, Europe PMC, PubChem, ChEMBL — is open, and ADMET-AI runs locally.
|
||||
|
||||
```bash
|
||||
cd examples/case_studies
|
||||
|
||||
python case1_blm_target_assessment.py # ~2 min, mostly ADMET-AI model load
|
||||
python case2_oxtr_druggability.py # ~2 min
|
||||
|
||||
python download_bcg_data.py # ~4 MB from Zenodo, CC0
|
||||
python download_bcg_data.py # ~4 MB of trial tables from Zenodo, CC0
|
||||
python case3_bcg_ae_severity.py # seconds
|
||||
```
|
||||
|
||||
Cases 1 and 2 load 11 tool categories (146 tools) rather than the full ~2,700,
|
||||
and Case 3 loads a single category, which keeps startup to a few seconds.
|
||||
---
|
||||
|
||||
## What each case does
|
||||
## Case 1 — is *BLM* a target?
|
||||
|
||||
### Case 1 — *BLM* as a cancer risk gene and a possible target
|
||||
### Input
|
||||
|
||||
Seven steps, ending in a conclusion neither half of the evidence supports on its
|
||||
own. The germline genetics say *BLM* is a tumour suppressor to preserve; the
|
||||
DepMap dependency screen says it is a selective vulnerability in a few defined
|
||||
cancer contexts. The case holds both and concludes with a testable,
|
||||
biomarker-defined hypothesis rather than a target.
|
||||
```
|
||||
gene: BLM (a DNA-repair helicase; losing it causes cancer)
|
||||
question: can blocking it nevertheless treat cancer,
|
||||
and is its inhibitor ML216 developable?
|
||||
```
|
||||
|
||||
Tools: `OpenTargets_multi_entity_search_by_query_string`,
|
||||
`OpenTargets_get_target_gene_ontology_by_ensemblID`,
|
||||
`OpenTargets_get_diseases_phenotypes_by_target_ensembl`,
|
||||
`ClinVar_search_variants`, `GeneBe_classify_variant`,
|
||||
`OpenTargets_get_target_depmap_essentiality`, `alphafold_get_summary`,
|
||||
`PDBe_get_uniprot_structure_coverage`, `EuropePMC_search_articles`,
|
||||
`OpenTargets_get_chemical_probes_by_target_ensemblID`,
|
||||
`PubChem_get_CID_by_compound_name`, `PubChem_get_compound_properties_by_CID`,
|
||||
`ADMETAI_predict_physicochemical_properties`, `ADMETAI_predict_toxicity`,
|
||||
`ChEMBL_search_similar_molecules`.
|
||||
### Process
|
||||
|
||||
### Case 2 — *OXTR* druggability and whether the chemistry fits
|
||||
| Step | Tools | What it establishes |
|
||||
|---|---|---|
|
||||
| 1 Resolve the gene | `OpenTargets_multi_entity_search_by_query_string` `OpenTargets_get_target_gene_ontology_by_ensemblID` | it is a genome-maintenance helicase |
|
||||
| 2 Cancer-risk genetics | `OpenTargets_get_diseases_phenotypes_by_target_ensembl` | 464 disease associations, led by Bloom syndrome |
|
||||
| 3 Classify a variant | `ClinVar_search_variants` `GeneBe_classify_variant` | `c.520C>T` is pathogenic — evidence **against** inhibiting |
|
||||
| **4 Dependency screen** | `OpenTargets_get_target_depmap_essentiality` | **the turn**: not pan-essential, but concentrated in specific tumours |
|
||||
| 5 Structure | `alphafold_get_summary` `PDBe_get_uniprot_structure_coverage` | use experimental structures, not the mixed-confidence prediction |
|
||||
| 6 Precedent | `EuropePMC_search_articles` | paralog WRN is already a validated synthetic-lethal target |
|
||||
| 7 Triage the chemistry | `OpenTargets_get_chemical_probes_by_target_ensemblID` `PubChem_*` `ADMETAI_*` `ChEMBL_search_similar_molecules` | ML216 is drug-like but carries a liability |
|
||||
|
||||
Six steps. The target passes every druggability check: experimental structures
|
||||
in both the active and inactive states, nanomolar ligands, known agonists and
|
||||
antagonists. The case turns on the last step, where nolasiban — drug-like and
|
||||
predicted brain-penetrant, so a plausible repurposing candidate — turns out to
|
||||
be an *antagonist*, the opposite of the mechanism a pro-social hypothesis needs.
|
||||
Step 4 is why the case exists — it contradicts step 3:
|
||||
|
||||
Tools: `OpenTargets_multi_entity_search_by_query_string`,
|
||||
`OpenTargets_get_target_gene_ontology_by_ensemblID`, `alphafold_get_summary`,
|
||||
`PDBe_get_uniprot_structure_coverage`,
|
||||
`OpenTargets_get_diseases_phenotypes_by_target_ensembl`,
|
||||
`OpenTargets_get_target_tractability_by_ensemblID`,
|
||||
`OpenTargets_get_associated_drugs_by_target_ensemblID`,
|
||||
`OpenTargets_get_drug_mechanisms_of_action_by_chemblId`, `ChEMBL_search_targets`,
|
||||
`ChEMBL_get_target_activities`, `EuropePMC_search_articles`,
|
||||
`PubChem_get_CID_by_compound_name`, `PubChem_get_compound_properties_by_CID`,
|
||||
`ADMETAI_predict_physicochemical_properties`, `ADMETAI_predict_BBB_penetrance`.
|
||||
```
|
||||
mean gene effect: -0.18
|
||||
lines dependent at < -0.5: 94/1258 (7.5%)
|
||||
Most-dependent cancer types (mean gene effect, n>=5 lines):
|
||||
Mature T and NK Neoplasms -0.47 (n=8)
|
||||
Cutaneous Squamous Cell Carcinoma -0.44 (n=5)
|
||||
```
|
||||
|
||||
### Case 3 — BCG vaccination and adverse-event severity
|
||||
### Output
|
||||
|
||||
Not a target — a **biomarker-defined hypothesis**. The germline genetics say
|
||||
protect *BLM*; the screen says it is a selective vulnerability in defined
|
||||
contexts. Both are right, so the workflow returns the contexts worth testing
|
||||
rather than a yes/no. ML216 is a probe, not a lead: its predicted liver-injury
|
||||
signal stays near maximum across every close analog, implicating the scaffold
|
||||
rather than the molecule.
|
||||
|
||||
---
|
||||
|
||||
## Case 2 — does *OXTR* chemistry fit the hypothesis?
|
||||
|
||||
### Input
|
||||
|
||||
```
|
||||
gene: OXTR (oxytocin receptor, a GPCR)
|
||||
question: is it druggable, and does its pharmacology offer a
|
||||
starting point for the emerging autism association?
|
||||
```
|
||||
|
||||
### Process
|
||||
|
||||
| Step | Tools | What it establishes |
|
||||
|---|---|---|
|
||||
| 1 Resolve + structure | `OpenTargets_multi_entity_search_by_query_string` `OpenTargets_get_target_gene_ontology_by_ensemblID` `alphafold_get_summary` `PDBe_get_uniprot_structure_coverage` | active and inactive experimental structures both exist |
|
||||
| 2 Disease landscape | `OpenTargets_get_diseases_phenotypes_by_target_ensembl` | reproductive indications, plus an emerging autism link |
|
||||
| 3 Tractability + drugs | `OpenTargets_get_target_tractability_by_ensemblID` `OpenTargets_get_associated_drugs_by_target_ensemblID` | druggable GPCR, 9 known agents, approved ones are peptides |
|
||||
| 4 Mechanism | `OpenTargets_get_drug_mechanisms_of_action_by_chemblId` | already drugged in both directions |
|
||||
| 5 Measured affinity | `ChEMBL_search_targets` `ChEMBL_get_target_activities` | nanomolar ligands exist |
|
||||
| **6 CNS + mechanism** | `EuropePMC_search_articles` `PubChem_*` `ADMETAI_predict_BBB_penetrance` `OpenTargets_get_drug_mechanisms_of_action_by_chemblId` | **the turn**: the brain-penetrant candidate acts the wrong way |
|
||||
|
||||
Nolasiban passes every property filter — then fails the one that matters:
|
||||
|
||||
```
|
||||
nolasiban action type: ['ANTAGONIST']
|
||||
Read: brain-penetrant non-peptide OXTR chemistry is attainable,
|
||||
but this chemotype BLOCKS the receptor, and the pro-social
|
||||
hypothesis needs it ACTIVATED. Wrong direction.
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
Tractable target, wrong chemotype. The objective is stated precisely instead of
|
||||
a repurposing suggestion: a brain-penetrant **agonist** or positive allosteric
|
||||
modulator, selective over the vasopressin receptors. Screening on affinity and
|
||||
brain penetrance alone would have produced a confident, wrong recommendation.
|
||||
|
||||
---
|
||||
|
||||
## Case 3 — did BCG worsen adverse events?
|
||||
|
||||
Unlike the others, this one computes on raw data rather than querying databases.
|
||||
|
||||
### Input
|
||||
|
||||
```
|
||||
data: TASK008_BCG-CORONA_DM.csv demographics, 1000 subjects
|
||||
TASK008_BCG-CORONA_AE.csv adverse events, 2694 records
|
||||
(public BCG-CORONA trial, Zenodo 12737228, CC0 — download_bcg_data.py fetches it)
|
||||
question: is BCG associated with higher adverse-event severity,
|
||||
after adjusting for how often participants saw patients?
|
||||
```
|
||||
|
||||
### Process
|
||||
|
||||
One tool, `clinical_trial_ae_severity_test`, in three modes:
|
||||
|
||||
1. `prepare` — inner-join the adverse-event table onto demographics on
|
||||
`USUBJID`, reduce each subject to their maximum `AESEV` grade.
|
||||
2. `chi-square` — unadjusted treatment-by-severity association.
|
||||
3. `ordinal` — proportional-odds logistic regression adjusting for
|
||||
patient-interaction frequency (`patients_seen`, `expect_interact`,
|
||||
`work_hours`).
|
||||
|
||||
Data: [BCG-CORONA trial, Zenodo record 12737228](https://zenodo.org/records/12737228),
|
||||
CC0-1.0. `download_bcg_data.py` fetches it.
|
||||
|
||||
One detail the script handles for you: the tool reports the odds ratio against
|
||||
its own reference level. Treatment is encoded alphabetically (BCG=0,
|
||||
Placebo=1), so the raw OR of **0.65** is Placebo vs BCG, and the published
|
||||
**1.53** is its reciprocal. Reporting the raw number as though it were the
|
||||
BCG effect would invert the finding.
|
||||
|
||||
## Expected output
|
||||
|
||||
Verified on 2026-08-17 against a fresh clone. Rows marked † are reported in the
|
||||
supplementary note rather than in the post.
|
||||
|
||||
**Case 1** — every published value reproduced:
|
||||
|
||||
| Value | Published | Observed |
|
||||
| Step | Mode | What it does |
|
||||
|---|---|---|
|
||||
| *BLM* Ensembl gene | ENSG00000197299 | ENSG00000197299 |
|
||||
| Disease associations | 464 | 464 |
|
||||
| `c.520C>T` ACMG class | Pathogenic | Pathogenic |
|
||||
| † ACMG score / transcript | 12 / NM_000057.4 | 12 / NM_000057.4 |
|
||||
| DepMap cell lines | 1,258 | 1,258 |
|
||||
| † Mean gene effect | −0.18 | −0.18 |
|
||||
| † Lines dependent at < −0.5 | 94/1,258 (7.5%) | 94/1,258 (7.5%) |
|
||||
| † Mature T/NK neoplasms | −0.47 (n=8) | −0.47 (n=8) |
|
||||
| † Cutaneous SCC | −0.44 (n=5) | −0.44 (n=5) |
|
||||
| † Peripheral nervous system | −0.33 (n=48) | −0.33 (n=48) |
|
||||
| † AlphaFold model / length | AF-P54132-F1 / 1,417 | AF-P54132-F1 / 1,417 |
|
||||
| † Helicase-core PDB entries | 7AUC, 4CGZ, 4O3M | all present |
|
||||
| ML216 MW | 383 Da | 383.3 |
|
||||
| † ML216 PubChem CID | 49852229 | 49852229 |
|
||||
| QED / DILI / hERG / AMES | 0.66 / 0.99 / 0.56 / 0.16 | 0.657 / 0.988 / 0.564 / 0.160 |
|
||||
| 1 | `prepare` | inner-join AE onto DM by `USUBJID`, reduce each subject to their max `AESEV` → 791 evaluable subjects |
|
||||
| 2 | `chi-square` | unadjusted treatment × severity association |
|
||||
| 3 | `ordinal` | proportional-odds regression adjusting for `patients_seen`, `expect_interact`, `work_hours` |
|
||||
|
||||
**Case 2** — reproduced except the association count:
|
||||
### Output
|
||||
|
||||
| Value | Published | Observed |
|
||||
|---|---|---|
|
||||
| *OXTR* Ensembl gene | ENSG00000180914 | ENSG00000180914 |
|
||||
| Structures 7RYC / 6TPK | present | present |
|
||||
| † Structure 7QVM, AF-P30559-F1, length 389 | present / 389 | present / 389 |
|
||||
| Disease associations | 393 | **465** (drift, see below) |
|
||||
| Known agents | 9 | 9 |
|
||||
| oxytocin / atosiban mechanism | agonist / antagonist | AGONIST / ANTAGONIST |
|
||||
| † nolasiban CID | 52947354 | 52947354 |
|
||||
| nolasiban QED / BBB | 0.87 / 0.93 | 0.872 / 0.925 |
|
||||
| nolasiban mechanism | antagonist | ANTAGONIST |
|
||||
```
|
||||
evaluable subjects: 791
|
||||
chi-square: 10.12 dof: 3 p-value: 0.018
|
||||
OR for higher severity with BCG: 1.53
|
||||
95% CI: (1.16, 2.01)
|
||||
p-value: 0.0024
|
||||
```
|
||||
|
||||
**Case 3** — exact:
|
||||
A significant association that survives adjustment. Two things the script is
|
||||
careful about, and you should be too:
|
||||
|
||||
| Value | Published | Observed |
|
||||
|---|---|---|
|
||||
| Evaluable subjects | 791 | 791 |
|
||||
| † Severity distribution | {1:328, 2:402, 3:43, 4:18} | identical |
|
||||
| Chi-square / dof / p | 10.12 / 3 / 0.018 | 10.12 / 3 / 0.018 |
|
||||
| Adjusted OR (95% CI) | 1.53 (1.16–2.01) | 1.53 (1.16–2.01) |
|
||||
| p | 0.0024 | 0.0024 |
|
||||
- **Direction.** The tool reports the odds ratio against its own reference level
|
||||
(BCG=0, Placebo=1 alphabetically), so the raw **0.65** is Placebo-vs-BCG and
|
||||
the **1.53** above is its reciprocal. Reporting the raw number as the BCG
|
||||
effect would invert the finding.
|
||||
- **What it supports.** The cohort is restricted to participants with at least
|
||||
one adverse event, and severity is a derived per-subject maximum. That makes
|
||||
this a reproducible association from a secondary analysis — not evidence that
|
||||
vaccination changes severity.
|
||||
|
||||
## Why numbers drift, and which ones should not
|
||||
---
|
||||
|
||||
- **Live database counts move.** *OXTR* disease associations were 393 when the
|
||||
post was written and are 465 now; Open Targets re-scores associations every
|
||||
release. The *BLM* count happens to be unchanged. Treat these as
|
||||
release-dependent, not as reproduction failures. The conclusions do not rest
|
||||
on them.
|
||||
- **Stereochemistry changes ADMET-AI scores.** The scripts request PubChem's
|
||||
`SMILES` property (which carries stereochemistry) and fall back to
|
||||
`ConnectivitySMILES`. For nolasiban the flat form scores BBB 0.913 and the
|
||||
stereo form 0.925; the published 0.93 is the stereo form. ML216 has no
|
||||
stereocentre, so both agree.
|
||||
- **Case 3 should never drift.** Frozen dataset, deterministic model, identical
|
||||
output on pandas 2 and 3. If its numbers move, something in the environment
|
||||
changed.
|
||||
- **ChEMBL's data API is down upstream.** Throughout verification
|
||||
`https://www.ebi.ac.uk/chembl/api/data/` failed on every request, in two
|
||||
modes: HTTP 500 after ~5 s, or no response at all. The fault is specific to
|
||||
that one service. Sampling five times each from the same machine:
|
||||
## Reading the output
|
||||
|
||||
| Endpoint | Result |
|
||||
|---|---|
|
||||
| `/chembl/api/data/…` (ChEMBL data API) | 0/5 ok, avg 7.9 s, 500s and timeouts |
|
||||
| `/chembl/api/utils/…` (ChEMBL Beaker) | 5/5 ok, 0.4 s |
|
||||
| `/chembl/interface_api/…` (powers the ChEMBL website) | 5/5 ok, 0.5 s |
|
||||
| `/pdbe/api/…` | 5/5 ok, 0.4 s |
|
||||
| `/europepmc/webservices/…` | 5/5 ok, 0.5 s |
|
||||
Every value prints with the originally reported number beside it:
|
||||
|
||||
Sibling services under the same prefix on the same host are healthy, so this
|
||||
is neither a network problem nor a ToolUniverse one: the base URL in
|
||||
`src/tooluniverse/chem_tool.py` is correct, the service behind it is not
|
||||
answering. It is a known upstream fault, reported in
|
||||
[chembl_webresource_client#144](https://github.com/chembl/chembl_webresource_client/issues/144)
|
||||
(open since 2026-06-23, no maintainer response). That report's workaround —
|
||||
appending any query parameter — no longer helps, so the service has degraded
|
||||
further since it was filed. Note the ChEMBL website stays up during this,
|
||||
because it runs on `interface_api` rather than the data API, so the outage is
|
||||
easy to miss.
|
||||
```
|
||||
DILI (liver injury): 0.988 (published: 0.99)
|
||||
```
|
||||
|
||||
Consequently the two steps that depend on ChEMBL — Case 1's five-analog DILI
|
||||
comparison and Case 2's measured-affinity lookup — are the only ones **not**
|
||||
confirmed end-to-end against the live service. Their response parsing was
|
||||
written against the tool implementations in `src/tooluniverse/chem_tool.py`
|
||||
and checked on replayed payloads, but the published values (DILI 0.987-0.992;
|
||||
Ki as low as 3.2 nM) have not been re-observed. Both scripts report the step
|
||||
as unavailable and carry on rather than aborting. Check whether the service is
|
||||
back with:
|
||||
```bash
|
||||
curl -o /dev/null -w '%{http_code}\n' https://www.ebi.ac.uk/chembl/api/data/status.json
|
||||
```
|
||||
So a run tells you at a glance whether the science still holds against live
|
||||
databases, and exactly which number moved if it doesn't. Values marked
|
||||
`published:` come from the write-up or from the supplementary note it condenses
|
||||
([arXiv:2509.23426](https://arxiv.org/abs/2509.23426)) — the note carries the
|
||||
intermediate ones such as ACMG scores, PDB accessions and per-context DepMap
|
||||
effects.
|
||||
|
||||
## Source
|
||||
Expect some movement, and know which kind matters:
|
||||
|
||||
The narrative and the step-by-step tool sequences come from the
|
||||
[case-studies post](https://aiscientist.tools/posts/tooluniverse-case-studies),
|
||||
which condenses the supplementary note "Step-by-step real-world case studies"
|
||||
in the ToolUniverse manuscript,
|
||||
[arXiv:2509.23426](https://arxiv.org/abs/2509.23426).
|
||||
- **Live databases get re-scored.** *OXTR* disease associations were 393 at the
|
||||
time of writing and read 465 today; Open Targets re-scores every release. That
|
||||
is a release difference, not a failed reproduction, and no conclusion rests on
|
||||
the count.
|
||||
- **Case 3 should never move.** Frozen dataset, deterministic model, identical
|
||||
on pandas 2 and 3. If its numbers change, something in your environment did.
|
||||
- **One published number is understated.** The write-up reports *OXTR* Ki
|
||||
"as low as 3.2 nM". ChEMBL returns activities unsorted and paginated, so a
|
||||
single page gives an arbitrary minimum — 0.50, 1.0 and 0.09 nM at offsets 0,
|
||||
100 and 200. Case 2 now pages through all 811 Ki measurements and finds the
|
||||
real figure, **0.025 nM**, about 130× lower. The conclusion the case draws
|
||||
from it, that *OXTR* has well-characterised high-affinity ligands, is
|
||||
unaffected — if anything it is stronger.
|
||||
- **ChEMBL's data API goes down periodically** (HTTP 500 upstream, [known
|
||||
issue](https://github.com/chembl/chembl_webresource_client/issues/144)). When
|
||||
it does, the two steps that need it print `[unavailable]` and the case carries
|
||||
on rather than failing. Everything else is unaffected.
|
||||
|
||||
@@ -187,30 +187,47 @@ def main() -> None:
|
||||
target_id = rows[0].get("target_chembl_id")
|
||||
report("ChEMBL target", target_id, "CHEMBL2049")
|
||||
if target_id:
|
||||
acts = call(
|
||||
tu,
|
||||
"ChEMBL_get_target_activities",
|
||||
target_chembl_id__exact=target_id,
|
||||
limit=100,
|
||||
)
|
||||
if is_error(acts):
|
||||
note_unavailable("ChEMBL_get_target_activities", acts)
|
||||
else:
|
||||
# ChEMBL returns activities unsorted and paginated, so the minimum
|
||||
# over any single page is an artefact of which page you fetched
|
||||
# (0.50 nM at offset 0, 1.0 at 100, 0.09 at 200). Page through the
|
||||
# whole set to get the real figure.
|
||||
affinities, offset, PAGE, CAP = [], 0, 200, 2000
|
||||
while offset < CAP:
|
||||
acts = call(
|
||||
tu,
|
||||
"ChEMBL_get_target_activities",
|
||||
target_chembl_id__exact=target_id,
|
||||
limit=PAGE,
|
||||
offset=offset,
|
||||
)
|
||||
if is_error(acts):
|
||||
if offset == 0:
|
||||
note_unavailable("ChEMBL_get_target_activities", acts)
|
||||
break
|
||||
rows = acts.get("activities", acts) or []
|
||||
affinities = [
|
||||
affinities += [
|
||||
float(a["standard_value"])
|
||||
for a in rows
|
||||
if isinstance(a, dict)
|
||||
and a.get("standard_type") in {"Ki", "IC50", "Kd"}
|
||||
and a.get("standard_type") == "Ki"
|
||||
and a.get("standard_units") == "nM"
|
||||
and a.get("standard_value") is not None
|
||||
and not a.get("data_validity_comment")
|
||||
]
|
||||
if affinities:
|
||||
report(
|
||||
"best measured affinity",
|
||||
f"{min(affinities):.1f} nM",
|
||||
"Ki as low as 3.2 nM",
|
||||
)
|
||||
if len(rows) < PAGE:
|
||||
break
|
||||
offset += PAGE
|
||||
|
||||
if affinities:
|
||||
best = min(affinities)
|
||||
report("Ki measurements found", len(affinities))
|
||||
report(f"lowest Ki across all {len(affinities)}", f"{best:.3g} nM")
|
||||
report(
|
||||
"high-affinity ligands confirmed",
|
||||
best < 10,
|
||||
"yes (the post's 'as low as 3.2 nM' sampled one page; "
|
||||
"the full set goes lower)",
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
step(6, "Reason about pharmacology and CNS druggability")
|
||||
|
||||
Reference in New Issue
Block a user