|
| 1 | +export const meta = { |
| 2 | + name: 'dialect-lit-validation', |
| 3 | + description: 'Validate DIALECT ME/CO driver-interaction networks against the biological literature across 34 cancer-type groups (find -> adversarially verify -> synthesize)', |
| 4 | + phases: [ |
| 5 | + { title: 'Search', detail: 'one literature-search agent per cancer-type group' }, |
| 6 | + { title: 'Verify', detail: 'adversarial re-check of established/emerging pairs' }, |
| 7 | + { title: 'Synthesize', detail: 'method-recovery + novel-discovery + concordance report' }, |
| 8 | + ], |
| 9 | +} |
| 10 | + |
| 11 | +// ---- payload injected at generation time (per cancer-type group: cohorts + ME/CO pairs) ---- |
| 12 | +const GROUPS = /*__PAYLOAD__*/; |
| 13 | + |
| 14 | +// Background DIALECT framing handed to every agent so it judges direction correctly. |
| 15 | +const FRAMING = `DIALECT is an EM model that, after subtracting a per-gene/per-sample passenger |
| 16 | +background mutation rate (BMR), estimates latent DRIVER mutation status for each gene and then fits a |
| 17 | +bivariate-Bernoulli interaction (tau) between gene pairs. It reports: |
| 18 | +- ME (mutually exclusive): the two genes' DRIVER mutations co-occur in the same tumor far LESS than |
| 19 | + chance (negative correlation rho<0). Biologically this usually means functional redundancy / |
| 20 | + same-pathway epistasis (one hit suffices) or different molecular subtypes. |
| 21 | +- CO (co-occurring): the two genes' driver mutations co-occur MORE than chance (rho>0). Biologically |
| 22 | + this usually means cooperation/synergy, a shared subtype, or a defined genomic context. |
| 23 | +Gene symbols may carry _M (missense/in-frame) or _N (truncating/nonsense) effect suffixes; here they |
| 24 | +are collapsed to the base gene symbol. BMR support codes: c=CBaSE, d=DIG, m=per-sample MutSig2CV. |
| 25 | +A pair supported by 'm' is robust to per-sample tumor-burden confounding (the strongest evidence it is |
| 26 | +not a hypermutation artifact); a CO pair seen ONLY under 'c' in a high-burden cohort is the most |
| 27 | +suspect.`; |
| 28 | + |
| 29 | +const FINDER_SCHEMA = { |
| 30 | + type: 'object', |
| 31 | + additionalProperties: false, |
| 32 | + properties: { |
| 33 | + group: { type: 'string' }, |
| 34 | + findings: { |
| 35 | + type: 'array', |
| 36 | + items: { |
| 37 | + type: 'object', |
| 38 | + additionalProperties: false, |
| 39 | + properties: { |
| 40 | + pair: { type: 'string', description: 'GENE_A:GENE_B exactly as given' }, |
| 41 | + observed: { type: 'string', enum: ['ME', 'CO'], description: 'direction DIALECT called' }, |
| 42 | + status: { |
| 43 | + type: 'string', |
| 44 | + enum: ['established', 'emerging', 'novel', 'contradicted', 'artifact'], |
| 45 | + description: 'established=textbook/multiple studies; emerging=some evidence; novel=biologically plausible but little/no prior report; contradicted=literature shows the OPPOSITE direction; artifact=one or both genes are not credible drivers in this tumor (likely passenger/FLAGS gene)', |
| 46 | + }, |
| 47 | + lit_direction: { type: 'string', enum: ['ME', 'CO', 'both', 'none'] }, |
| 48 | + concordant: { type: 'boolean', description: 'does DIALECT observed direction match the literature direction?' }, |
| 49 | + mechanism: { type: 'string', description: 'one sentence (<=200 chars) on the biology' }, |
| 50 | + citations: { |
| 51 | + type: 'array', |
| 52 | + description: 'up to 3 real peer-reviewed sources; empty if novel/artifact', |
| 53 | + items: { |
| 54 | + type: 'object', |
| 55 | + additionalProperties: false, |
| 56 | + properties: { |
| 57 | + ref: { type: 'string', description: 'first-author + journal + year, <=120 chars' }, |
| 58 | + identifier: { type: 'string', description: 'PMID, DOI, or URL' }, |
| 59 | + year: { type: 'integer' }, |
| 60 | + }, |
| 61 | + required: ['ref'], |
| 62 | + }, |
| 63 | + }, |
| 64 | + confidence: { type: 'string', enum: ['high', 'medium', 'low'] }, |
| 65 | + }, |
| 66 | + required: ['pair', 'observed', 'status', 'lit_direction', 'concordant', 'mechanism', 'confidence'], |
| 67 | + }, |
| 68 | + }, |
| 69 | + }, |
| 70 | + required: ['group', 'findings'], |
| 71 | +} |
| 72 | + |
| 73 | +const VERIFIER_SCHEMA = { |
| 74 | + type: 'object', |
| 75 | + additionalProperties: false, |
| 76 | + properties: { |
| 77 | + group: { type: 'string' }, |
| 78 | + verdicts: { |
| 79 | + type: 'array', |
| 80 | + items: { |
| 81 | + type: 'object', |
| 82 | + additionalProperties: false, |
| 83 | + properties: { |
| 84 | + pair: { type: 'string' }, |
| 85 | + claim_holds: { type: 'boolean', description: 'does the cited evidence really support the claimed status+direction?' }, |
| 86 | + final_status: { type: 'string', enum: ['established', 'emerging', 'novel', 'contradicted', 'artifact', 'unverifiable'] }, |
| 87 | + final_direction: { type: 'string', enum: ['ME', 'CO', 'both', 'none'] }, |
| 88 | + best_citation: { type: 'string', description: 'the single strongest verified citation w/ identifier, or "" if none verifiable' }, |
| 89 | + reason: { type: 'string', description: 'why the claim held or failed (<=240 chars)' }, |
| 90 | + }, |
| 91 | + required: ['pair', 'claim_holds', 'final_status', 'final_direction', 'reason'], |
| 92 | + }, |
| 93 | + }, |
| 94 | + }, |
| 95 | + required: ['group', 'verdicts'], |
| 96 | +} |
| 97 | + |
| 98 | +function finderPrompt(g) { |
| 99 | + const cohorts = g.cohorts.map(c => `${c.c} (N=${c.n}, medianTMB=${c.tmb})`).join('; '); |
| 100 | + const fmt = arr => arr.map(p => `${p[0]} [${p[1]},bmr=${p[2]},nCohorts=${p[3]}]`).join('\n '); |
| 101 | + return `You are a cancer-genomics literature analyst. Validate DIALECT's predicted driver-gene |
| 102 | +interaction network for the cancer-type group "${g.group}" against the PEER-REVIEWED biological literature. |
| 103 | +
|
| 104 | +${FRAMING} |
| 105 | +
|
| 106 | +CONSTITUENT COHORTS: ${cohorts} |
| 107 | +
|
| 108 | +DIALECT predicted MUTUALLY-EXCLUSIVE (ME) pairs: |
| 109 | + ${fmt(g.ME) || '(none)'} |
| 110 | +
|
| 111 | +DIALECT predicted CO-OCCURRING (CO) pairs: |
| 112 | + ${fmt(g.CO) || '(none)'} |
| 113 | +
|
| 114 | +(Tag legend: class dd=both OncoKB cancer genes, dp=one non-OncoKB partner. bmr letters c/d/m as defined above. nCohorts = how many cohorts in this group recovered the pair.) |
| 115 | +
|
| 116 | +TASK: For EVERY pair listed, determine what the literature says about that gene pair IN THIS CANCER TYPE |
| 117 | +(or closely related context). Use web search to find real evidence — load it first if needed via |
| 118 | +ToolSearch query "select:WebSearch,WebFetch", then run multiple targeted WebSearch queries |
| 119 | +(e.g. "GENE_A GENE_B mutual exclusivity <cancer>", "GENE_A GENE_B co-occurrence co-mutation <cancer>", |
| 120 | +"GENE_A GENE_B <cancer> pathway"). Prefer TCGA marker papers, cBioPortal/MSK studies, COSMIC, |
| 121 | +pathway/epistasis papers, and reviews. Fetch a source to confirm when a snippet is ambiguous. |
| 122 | +
|
| 123 | +For each pair decide: |
| 124 | +- status (established / emerging / novel / contradicted / artifact), |
| 125 | +- lit_direction (what direction the literature supports: ME, CO, both, or none), |
| 126 | +- concordant (does DIALECT's observed direction match the literature?), |
| 127 | +- a one-sentence mechanism, and up to 3 REAL citations with PMIDs/DOIs (NEVER fabricate an identifier; |
| 128 | + if you cannot find a real source, leave citations empty and lower the status to novel or artifact). |
| 129 | +
|
| 130 | +Be skeptical and calibrated: only "established" if you can name concrete supporting literature. |
| 131 | +If a pair's biology is well known to go the OTHER way than DIALECT called it, mark contradicted |
| 132 | +(this is a valuable finding). Return ALL pairs. Your structured output IS the result.`; |
| 133 | +} |
| 134 | + |
| 135 | +function verifierPrompt(g, toCheck) { |
| 136 | + const lines = toCheck.map(f => |
| 137 | + `- ${f.pair} | DIALECT=${f.observed} | claimed status=${f.status}, lit_dir=${f.lit_direction}, concordant=${f.concordant} | mechanism="${f.mechanism}" | cites=${JSON.stringify(f.citations || [])}` |
| 138 | + ).join('\n'); |
| 139 | + return `You are an ADVERSARIAL fact-checker for a cancer-genomics rebuttal. Another analyst claimed the |
| 140 | +following gene-pair interactions in "${g.group}" are supported by the literature. Independently verify |
| 141 | +each one. Default to skepticism: if you cannot confirm a real source supports the claimed STATUS and |
| 142 | +DIRECTION, mark claim_holds=false and set final_status to "unverifiable" (or "artifact"/"novel"/"contradicted" |
| 143 | +as appropriate). |
| 144 | +
|
| 145 | +${FRAMING} |
| 146 | +
|
| 147 | +CLAIMS TO CHECK: |
| 148 | +${lines} |
| 149 | +
|
| 150 | +For EACH claim: load web search if needed (ToolSearch "select:WebSearch,WebFetch"), run your OWN searches, |
| 151 | +and (a) confirm the cited identifier (PMID/DOI) actually exists and is about this gene pair in this/related |
| 152 | +cancer, and (b) confirm the literature direction (ME vs CO) matches what was claimed. Watch specifically for: |
| 153 | +fabricated PMIDs/DOIs, citations that are about a different cancer, and direction errors (claimed CO but the |
| 154 | +genes are actually mutually exclusive, or vice versa). Output a verdict per pair with the single strongest |
| 155 | +VERIFIED citation (or "" if none). Your structured output IS the result.`; |
| 156 | +} |
| 157 | + |
| 158 | +// ---------- Phase 1+2: per-group find -> adversarially verify (pipeline, no barrier) ---------- |
| 159 | +const perGroup = await pipeline( |
| 160 | + GROUPS, |
| 161 | + g => agent(finderPrompt(g), { label: `find:${g.group}`, phase: 'Search', schema: FINDER_SCHEMA }), |
| 162 | + (found, g) => { |
| 163 | + if (!found) return { group: g.group, found: null, verdicts: [] }; |
| 164 | + const toCheck = found.findings.filter(f => f.status === 'established' || f.status === 'emerging' || f.status === 'contradicted'); |
| 165 | + if (toCheck.length === 0) return { group: g.group, found, verdicts: [] }; |
| 166 | + return agent(verifierPrompt(g, toCheck), { label: `verify:${g.group}`, phase: 'Verify', schema: VERIFIER_SCHEMA }) |
| 167 | + .then(v => ({ group: g.group, found, verdicts: (v && v.verdicts) || [] })); |
| 168 | + }, |
| 169 | +); |
| 170 | + |
| 171 | +// ---------- merge finder + verifier into one master table ---------- |
| 172 | +const master = []; |
| 173 | +for (const r of perGroup.filter(Boolean)) { |
| 174 | + if (!r.found) continue; |
| 175 | + const vmap = {}; |
| 176 | + for (const v of r.verdicts) vmap[v.pair] = v; |
| 177 | + for (const f of r.found.findings) { |
| 178 | + const v = vmap[f.pair]; |
| 179 | + master.push({ |
| 180 | + group: r.group, |
| 181 | + pair: f.pair, |
| 182 | + observed: f.observed, |
| 183 | + status: v ? v.final_status : f.status, |
| 184 | + lit_direction: v ? v.final_direction : f.lit_direction, |
| 185 | + concordant: f.concordant, |
| 186 | + verified: v ? v.claim_holds : (f.status === 'novel' || f.status === 'artifact'), |
| 187 | + confidence: f.confidence, |
| 188 | + mechanism: f.mechanism, |
| 189 | + citation: v && v.best_citation ? v.best_citation |
| 190 | + : (f.citations && f.citations[0] ? `${f.citations[0].ref} ${f.citations[0].identifier || ''}`.trim() : ''), |
| 191 | + }); |
| 192 | + } |
| 193 | +} |
| 194 | +log(`master table: ${master.length} validated pair-findings across ${perGroup.filter(Boolean).length} groups`); |
| 195 | + |
| 196 | +// compact views for the synthesis agents |
| 197 | +const established = master.filter(m => (m.status === 'established' || m.status === 'emerging') && m.verified); |
| 198 | +const novel = master.filter(m => m.status === 'novel' && (m.confidence === 'high' || m.confidence === 'medium')); |
| 199 | +const discordant = master.filter(m => m.status === 'contradicted' || m.concordant === false); |
| 200 | +const compact = arr => arr.map(m => `${m.group} | ${m.pair} | DIALECT=${m.observed} | status=${m.status} | litdir=${m.lit_direction} | conf=${m.confidence} | ${m.mechanism} | ${m.citation}`).join('\n'); |
| 201 | + |
| 202 | +// ---------- Phase 3: synthesis (3 independent sections, in parallel) ---------- |
| 203 | +phase('Synthesize'); |
| 204 | +const STORY_CONTEXT = `This is for a PLOS Comp Biol major-revision rebuttal. The central reviewer critique was that |
| 205 | +DIALECT's co-occurrence (CO) calls were inflated by background-mutation-rate (BMR) / hypermutator confounding. |
| 206 | +Key findings already established by the authors: (1) a proper per-(gene,sample,context) BMR extracted from a |
| 207 | +patched MutSig2CV collapses spurious CO in high-tumor-burden cohorts (e.g. UCEC CO 4850->~300); (2) BUT in |
| 208 | +LOW-burden cohorts that same per-sample BMR OVER-corrects and erases REAL biology — e.g. in AML it inflates the |
| 209 | +DNMT3A background so much (observed/expected ~1.35) that it deletes the canonical DNMT3A:FLT3 / DNMT3A:IDH1 |
| 210 | +co-occurrences that CBaSE and DIG both recover; (3) this motivates a burden-aware BMR choice: per-sample MutSig |
| 211 | +for high-TMB cohorts, per-gene CBaSE/DIG for low-TMB. The literature validation below tests whether DIALECT's |
| 212 | +ME/CO networks recover known cancer biology (method validation) and surface credible novel interactions.`; |
| 213 | + |
| 214 | +const synth = await parallel([ |
| 215 | + () => agent(`${STORY_CONTEXT} |
| 216 | +
|
| 217 | +You are writing the METHOD-RECOVERY section of the rebuttal's literature-validation appendix. Below are the |
| 218 | +gene-pair interactions DIALECT predicted that are SUPPORTED by the literature and survived adversarial |
| 219 | +verification. Write a tight, well-organized markdown section that demonstrates DIALECT recovers established |
| 220 | +cancer biology. Organize by cancer-type group; for each, give a one-line summary then a bullet list of the |
| 221 | +strongest recovered ME and CO pairs with their mechanism and a citation. Lead with the most famous textbook |
| 222 | +recoveries (e.g. lung KRAS/EGFR mutual exclusivity, PDAC KRAS:TP53:SMAD4:CDKN2A co-occurrence, glioma |
| 223 | +IDH1:TP53:ATRX, AML DNMT3A:FLT3:NPM1, colorectal APC:KRAS:TP53). Note where a CO pair is robust to per-sample |
| 224 | +MutSig (bmr code includes m) as the strongest anti-confounding evidence. Be precise and do not invent pairs |
| 225 | +not in the list. |
| 226 | +
|
| 227 | +VERIFIED ESTABLISHED/EMERGING PAIRS: |
| 228 | +${compact(established)}`, { label: 'synth:recovery', phase: 'Synthesize' }), |
| 229 | + |
| 230 | + () => agent(`${STORY_CONTEXT} |
| 231 | +
|
| 232 | +You are writing the NOVEL-CANDIDATES section. Below are biologically plausible gene-pair interactions DIALECT |
| 233 | +predicted for which there is little/no prior published report (status=novel) at medium/high confidence. Write a |
| 234 | +markdown section highlighting the most interesting candidates as discovery opportunities. Group by cancer type, |
| 235 | +prioritize driver-driver pairs and pairs recovered in multiple cohorts or robust to per-sample MutSig. For each, |
| 236 | +give the observed direction (ME/CO), a plausible mechanistic hypothesis, and why it is worth experimental or |
| 237 | +cohort follow-up. Be honest that these are hypotheses. Do not invent pairs not in the list. |
| 238 | +
|
| 239 | +NOVEL CANDIDATE PAIRS: |
| 240 | +${compact(novel)}`, { label: 'synth:novel', phase: 'Synthesize' }), |
| 241 | + |
| 242 | + () => agent(`${STORY_CONTEXT} |
| 243 | +
|
| 244 | +You are writing the DIRECTION-CONCORDANCE & CAVEATS section. Below are pairs where DIALECT's ME/CO call is |
| 245 | +DISCORDANT with the literature, or was flagged contradicted. Write a markdown section that (1) honestly catalogs |
| 246 | +the discordances by cancer type with the likely cause (residual BMR confounding, subtype mixing, or a genuine |
| 247 | +novel direction), (2) connects them to the burden-aware BMR story (which discordant CO calls appear only under |
| 248 | +CBaSE in high-TMB cohorts vs survive per-sample MutSig), and (3) gives a short, fair assessment of the overall |
| 249 | +concordance rate as evidence the method is well-calibrated when the right BMR is used. Do not invent pairs. |
| 250 | +
|
| 251 | +DISCORDANT / CONTRADICTED PAIRS: |
| 252 | +${compact(discordant)}`, { label: 'synth:concordance', phase: 'Synthesize' }), |
| 253 | +]); |
| 254 | + |
| 255 | +return { |
| 256 | + counts: { |
| 257 | + total: master.length, |
| 258 | + established_verified: established.length, |
| 259 | + novel: novel.length, |
| 260 | + discordant: discordant.length, |
| 261 | + groups: perGroup.filter(Boolean).length, |
| 262 | + }, |
| 263 | + master, |
| 264 | + sections: { |
| 265 | + recovery: synth[0], |
| 266 | + novel: synth[1], |
| 267 | + concordance: synth[2], |
| 268 | + }, |
| 269 | +}; |
0 commit comments