@inflexa-ai/harness 0.35.7 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/types.d.ts +5 -14
- package/dist/auth/types.js +3 -10
- package/dist/billing/usage-recorder.d.ts +1 -1
- package/dist/contracts/analogy-report.d.ts +1 -1
- package/dist/contracts/analogy-report.js +1 -1
- package/dist/contracts/chat-parts.d.ts +2 -3
- package/dist/contracts/index.d.ts +0 -4
- package/dist/contracts/index.js +0 -2
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -21
- package/dist/lib/metrics.d.ts +1 -1
- package/dist/lib/otel-spans.d.ts +0 -2
- package/dist/lib/otel-spans.js +0 -2
- package/dist/loop/genai-spans.d.ts +20 -0
- package/dist/loop/genai-spans.js +53 -0
- package/dist/loop/run-agent.d.ts +1 -1
- package/dist/loop/run-agent.js +9 -4
- package/dist/providers/ai-sdk.js +8 -0
- package/dist/runtime/assemble.d.ts +0 -3
- package/dist/runtime/assemble.js +0 -3
- package/dist/state/index.d.ts +0 -2
- package/dist/state/index.js +0 -1
- package/dist/state/init.js +1 -60
- package/dist/state/purge-analysis.d.ts +1 -2
- package/dist/state/purge-analysis.js +1 -2
- package/dist/tasks/regulatory-corpus-boot-refresh.js +1 -1
- package/dist/tools/bio/gene-preclinical-profile.d.ts +1 -3
- package/dist/tools/bio/gene-preclinical-profile.js +1 -3
- package/dist/tools/lib/chembl-client.d.ts +1 -4
- package/dist/tools/lib/chembl-client.js +1 -4
- package/dist/tools/lib/clinical-benchmarks-client.d.ts +1 -1
- package/dist/tools/lib/clinical-benchmarks-client.js +1 -1
- package/dist/tools/lib/clinical-trials-client.d.ts +0 -3
- package/dist/tools/lib/clinical-trials-client.js +0 -3
- package/dist/tools/lib/identifier-resolver.d.ts +5 -7
- package/dist/tools/lib/identifier-resolver.js +5 -7
- package/dist/tools/lib/iuphar-client.d.ts +1 -3
- package/dist/tools/lib/iuphar-client.js +1 -3
- package/dist/tools/lib/opentargets-client.d.ts +1 -1
- package/dist/tools/lib/opentargets-client.js +1 -1
- package/dist/tools/lib/protein-family-normalize.d.ts +5 -7
- package/dist/tools/lib/protein-family-normalize.js +5 -7
- package/dist/tools/lib/uniprot-client.d.ts +0 -7
- package/dist/tools/lib/uniprot-client.js +2 -9
- package/package.json +9 -8
- package/dist/contracts/target-assessment-row.d.ts +0 -85
- package/dist/contracts/target-assessment-row.js +0 -37
- package/dist/contracts/target-dossier.d.ts +0 -12423
- package/dist/contracts/target-dossier.js +0 -1282
- package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +0 -1
- package/dist/prompts/target-assessment/briefs/executive-recommendation.js +0 -85
- package/dist/prompts/target-assessment/briefs/index.d.ts +0 -4
- package/dist/prompts/target-assessment/briefs/index.js +0 -4
- package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +0 -1
- package/dist/prompts/target-assessment/briefs/liability-bullets.js +0 -114
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +0 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +0 -37
- package/dist/prompts/target-assessment/briefs/translational-commentary.d.ts +0 -1
- package/dist/prompts/target-assessment/briefs/translational-commentary.js +0 -23
- package/dist/prompts/target-assessment/drugs-in-class.d.ts +0 -1
- package/dist/prompts/target-assessment/drugs-in-class.js +0 -29
- package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +0 -1
- package/dist/prompts/target-assessment/investigation/adversarial-critique.js +0 -67
- package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +0 -1
- package/dist/prompts/target-assessment/investigation/claim-reverification.js +0 -49
- package/dist/prompts/target-assessment/investigation/index.d.ts +0 -3
- package/dist/prompts/target-assessment/investigation/index.js +0 -3
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +0 -1
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +0 -49
- package/dist/prompts/target-assessment/modulator-triage.d.ts +0 -1
- package/dist/prompts/target-assessment/modulator-triage.js +0 -31
- package/dist/prompts/target-assessment/off-target-annotator.d.ts +0 -1
- package/dist/prompts/target-assessment/off-target-annotator.js +0 -28
- package/dist/prompts/target-assessment/tox-voice/core.d.ts +0 -1
- package/dist/prompts/target-assessment/tox-voice/core.js +0 -74
- package/dist/prompts/target-assessment/tox-voice/critique.d.ts +0 -7
- package/dist/prompts/target-assessment/tox-voice/critique.js +0 -109
- package/dist/prompts/target-assessment/tox-voice/exemplars.d.ts +0 -8
- package/dist/prompts/target-assessment/tox-voice/exemplars.js +0 -24
- package/dist/prompts/target-assessment/tox-voice/index.d.ts +0 -4
- package/dist/prompts/target-assessment/tox-voice/index.js +0 -4
- package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +0 -18
- package/dist/prompts/target-assessment/tox-voice/vocabulary.js +0 -61
- package/dist/state/target-assessments.d.ts +0 -68
- package/dist/state/target-assessments.js +0 -222
- package/dist/workflows/execute-target-assessment.d.ts +0 -118
- package/dist/workflows/execute-target-assessment.js +0 -631
- package/dist/workflows/target-assessment/assemblers/index.d.ts +0 -5
- package/dist/workflows/target-assessment/assemblers/index.js +0 -4
- package/dist/workflows/target-assessment/assemblers/literature.d.ts +0 -281
- package/dist/workflows/target-assessment/assemblers/literature.js +0 -971
- package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +0 -118
- package/dist/workflows/target-assessment/assemblers/orchestrator.js +0 -945
- package/dist/workflows/target-assessment/assemblers/safety.d.ts +0 -374
- package/dist/workflows/target-assessment/assemblers/safety.js +0 -1068
- package/dist/workflows/target-assessment/assemblers/trials.d.ts +0 -30
- package/dist/workflows/target-assessment/assemblers/trials.js +0 -75
- package/dist/workflows/target-assessment/collectors/index.d.ts +0 -114
- package/dist/workflows/target-assessment/collectors/index.js +0 -538
- package/dist/workflows/target-assessment/coverage.d.ts +0 -97
- package/dist/workflows/target-assessment/coverage.js +0 -86
- package/dist/workflows/target-assessment/decisions/index.d.ts +0 -86
- package/dist/workflows/target-assessment/decisions/index.js +0 -127
- package/dist/workflows/target-assessment/fanout/index.d.ts +0 -32
- package/dist/workflows/target-assessment/fanout/index.js +0 -156
- package/dist/workflows/target-assessment/investigation/index.d.ts +0 -97
- package/dist/workflows/target-assessment/investigation/index.js +0 -419
- package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +0 -73
- package/dist/workflows/target-assessment/lib/approval-precedents.js +0 -185
- package/dist/workflows/target-assessment/lib/citation-validator.d.ts +0 -46
- package/dist/workflows/target-assessment/lib/citation-validator.js +0 -316
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +0 -65
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +0 -174
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +0 -44
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -152
- package/dist/workflows/target-assessment/lib/compute-derived.d.ts +0 -20
- package/dist/workflows/target-assessment/lib/compute-derived.js +0 -261
- package/dist/workflows/target-assessment/lib/compute-selectivity.d.ts +0 -15
- package/dist/workflows/target-assessment/lib/compute-selectivity.js +0 -18
- package/dist/workflows/target-assessment/lib/dedup-modulators.d.ts +0 -37
- package/dist/workflows/target-assessment/lib/dedup-modulators.js +0 -157
- package/dist/workflows/target-assessment/lib/evidence-conflict-detector.d.ts +0 -17
- package/dist/workflows/target-assessment/lib/evidence-conflict-detector.js +0 -100
- package/dist/workflows/target-assessment/lib/expression-constants.d.ts +0 -21
- package/dist/workflows/target-assessment/lib/expression-constants.js +0 -21
- package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +0 -20
- package/dist/workflows/target-assessment/lib/family-complex-supplement.js +0 -36
- package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +0 -72
- package/dist/workflows/target-assessment/lib/fda-label-safety.js +0 -166
- package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -30
- package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -44
- package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +0 -34
- package/dist/workflows/target-assessment/lib/hpo-organ-map.js +0 -63
- package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +0 -19
- package/dist/workflows/target-assessment/lib/impc-organ-map.js +0 -39
- package/dist/workflows/target-assessment/lib/intended-polypharm-filter.d.ts +0 -4
- package/dist/workflows/target-assessment/lib/intended-polypharm-filter.js +0 -24
- package/dist/workflows/target-assessment/lib/llm-step.d.ts +0 -80
- package/dist/workflows/target-assessment/lib/llm-step.js +0 -101
- package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +0 -40
- package/dist/workflows/target-assessment/lib/meddra-organ-map.js +0 -138
- package/dist/workflows/target-assessment/lib/recommendation-quality-gates.d.ts +0 -17
- package/dist/workflows/target-assessment/lib/recommendation-quality-gates.js +0 -99
- package/dist/workflows/target-assessment/lib/recommendation-sanitizer.d.ts +0 -2
- package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +0 -51
- package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +0 -25
- package/dist/workflows/target-assessment/lib/regulatory-actions.js +0 -176
- package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +0 -44
- package/dist/workflows/target-assessment/lib/safety-corroboration.js +0 -273
- package/dist/workflows/target-assessment/lib/structured-llm.d.ts +0 -61
- package/dist/workflows/target-assessment/lib/structured-llm.js +0 -102
- package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +0 -59
- package/dist/workflows/target-assessment/lib/target-identity-filter.js +0 -95
- package/dist/workflows/target-assessment/lib/therapeutic-programs.d.ts +0 -20
- package/dist/workflows/target-assessment/lib/therapeutic-programs.js +0 -68
- package/dist/workflows/target-assessment/metrics.d.ts +0 -16
- package/dist/workflows/target-assessment/metrics.js +0 -30
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +0 -3182
- package/dist/workflows/target-assessment/phase4-assemble.js +0 -39
- package/dist/workflows/target-assessment/phase5-persist.d.ts +0 -59
- package/dist/workflows/target-assessment/phase5-persist.js +0 -349
- package/dist/workflows/target-assessment/progress-stream.d.ts +0 -75
- package/dist/workflows/target-assessment/progress-stream.js +0 -110
- package/dist/workflows/target-assessment/progress.d.ts +0 -39
- package/dist/workflows/target-assessment/progress.js +0 -77
- package/dist/workflows/target-assessment/schemas.d.ts +0 -970
- package/dist/workflows/target-assessment/schemas.js +0 -318
- package/dist/workflows/target-assessment/steps/collectors/pathways.d.ts +0 -15
- package/dist/workflows/target-assessment/steps/collectors/pathways.js +0 -58
- package/dist/workflows/target-assessment/steps/fanout/aes-for-one-class-drug-step.d.ts +0 -34
- package/dist/workflows/target-assessment/steps/fanout/aes-for-one-class-drug-step.js +0 -31
- package/dist/workflows/target-assessment/steps/fanout/aes-for-one-trial-step.d.ts +0 -57
- package/dist/workflows/target-assessment/steps/fanout/aes-for-one-trial-step.js +0 -54
- package/dist/workflows/target-assessment/steps/fanout/faers-for-one-modulator-step.d.ts +0 -35
- package/dist/workflows/target-assessment/steps/fanout/faers-for-one-modulator-step.js +0 -32
- package/dist/workflows/target-assessment/steps/fanout/index.d.ts +0 -14
- package/dist/workflows/target-assessment/steps/fanout/index.js +0 -10
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +0 -29
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +0 -29
- package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +0 -657
- package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -34
- package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +0 -806
- package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -16
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -28
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -27
- package/dist/workflows/target-assessment/sub-workflows/index.d.ts +0 -15
- package/dist/workflows/target-assessment/sub-workflows/index.js +0 -11
- package/dist/workflows/target-assessment/sub-workflows/per-class-drug-aes.d.ts +0 -41
- package/dist/workflows/target-assessment/sub-workflows/per-class-drug-aes.js +0 -12
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-faers.d.ts +0 -41
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-faers.js +0 -12
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +0 -37
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.js +0 -12
- package/dist/workflows/target-assessment/sub-workflows/per-trial-aes.d.ts +0 -66
- package/dist/workflows/target-assessment/sub-workflows/per-trial-aes.js +0 -12
- package/dist/workflows/target-assessment/synthesis/index.d.ts +0 -607
- package/dist/workflows/target-assessment/synthesis/index.js +0 -354
- package/dist/workflows/target-assessment/synthesis-probes.d.ts +0 -56
- package/dist/workflows/target-assessment/synthesis-probes.js +0 -320
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic structural conflict detector.
|
|
3
|
-
*
|
|
4
|
-
* Each rule is a pure function over the dossier body. A row is emitted
|
|
5
|
-
* when the body internally contradicts itself in a way that should be
|
|
6
|
-
* impossible post-pipeline-fix. The post-fix pipeline should produce an
|
|
7
|
-
* empty array for any well-formed body; the rules exist to catch
|
|
8
|
-
* regressions and to surface upstream pipeline bugs.
|
|
9
|
-
*/
|
|
10
|
-
import { HIGH_EXPRESSION_TPM_THRESHOLD } from "./expression-constants.js";
|
|
11
|
-
function checkFaersCoverage(body) {
|
|
12
|
-
const faers = body?.safety_profile?.faers;
|
|
13
|
-
const cp = body?.safety_profile?.class_precedent;
|
|
14
|
-
if (faers?.coverage !== "queried_no_data")
|
|
15
|
-
return null;
|
|
16
|
-
if (cp?.coverage !== "available")
|
|
17
|
-
return null;
|
|
18
|
-
let total = 0;
|
|
19
|
-
for (const o of cp.data?.per_organ ?? []) {
|
|
20
|
-
for (const ae of o.top_aes ?? [])
|
|
21
|
-
total += ae.report_count ?? 0;
|
|
22
|
-
}
|
|
23
|
-
if (total === 0)
|
|
24
|
-
return null;
|
|
25
|
-
return {
|
|
26
|
-
evidence_item_id: "structural:faers_coverage",
|
|
27
|
-
predicate: "safety_profile.faers.coverage=queried_no_data",
|
|
28
|
-
contradicting_predicates: [`safety_profile.class_precedent has ${total} FAERS-derived reports across per_organ.top_aes`],
|
|
29
|
-
surfaced_in_section: "safety_profile.faers",
|
|
30
|
-
evidence: [],
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
function checkFatalSignal(body) {
|
|
34
|
-
const signal = body?.derived?.risk_summary?.any_fatal_signal;
|
|
35
|
-
if (signal !== "unknown")
|
|
36
|
-
return null;
|
|
37
|
-
const bullets = body?.liability_summary?.liability_bullets ?? [];
|
|
38
|
-
const fatal = bullets.find((b) => b?.category === "fatal_post_market");
|
|
39
|
-
if (!fatal)
|
|
40
|
-
return null;
|
|
41
|
-
return {
|
|
42
|
-
evidence_item_id: "structural:fatal_signal",
|
|
43
|
-
predicate: "derived.risk_summary.any_fatal_signal=unknown",
|
|
44
|
-
contradicting_predicates: ["liability_summary.liability_bullets contains a fatal_post_market entry"],
|
|
45
|
-
surfaced_in_section: "derived.risk_summary",
|
|
46
|
-
evidence: [],
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function checkTrialCount(body) {
|
|
50
|
-
const trials = body?.clinical_development?.trials;
|
|
51
|
-
const outcomes = body?.clinical_development?.outcomes;
|
|
52
|
-
if (trials?.coverage !== "queried_no_data")
|
|
53
|
-
return null;
|
|
54
|
-
const outcomeRows = outcomes?.coverage === "available" ? (outcomes.data?.rows ?? []) : [];
|
|
55
|
-
if (outcomeRows.length === 0)
|
|
56
|
-
return null;
|
|
57
|
-
return {
|
|
58
|
-
evidence_item_id: "structural:trial_count",
|
|
59
|
-
predicate: "clinical_development.trials.coverage=queried_no_data",
|
|
60
|
-
contradicting_predicates: [`clinical_development.outcomes.data.rows has ${outcomeRows.length} row(s)`],
|
|
61
|
-
surfaced_in_section: "clinical_development.outcomes",
|
|
62
|
-
evidence: [],
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
function checkHighExpressionCount(body) {
|
|
66
|
-
const reported = body?.derived?.liability_summary?.expression_breadth?.high_expression_tissue_count;
|
|
67
|
-
if (typeof reported !== "number")
|
|
68
|
-
return null;
|
|
69
|
-
const set = new Set();
|
|
70
|
-
const consensusRows = body?.reference_biology?.normal_tissue_expression?.data?.rows ?? [];
|
|
71
|
-
for (const r of consensusRows) {
|
|
72
|
-
if ((r.value ?? 0) >= HIGH_EXPRESSION_TPM_THRESHOLD && r.tissue) {
|
|
73
|
-
set.add(`${r.tissue}::human`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const cells = body?.reference_biology?.preclinical?.expression_heatmap?.data?.cells ?? [];
|
|
77
|
-
for (const c of cells) {
|
|
78
|
-
if (c.rank === "high" && c.tissue)
|
|
79
|
-
set.add(`${c.tissue}::${c.species ?? "unknown"}`);
|
|
80
|
-
}
|
|
81
|
-
if (set.size === reported)
|
|
82
|
-
return null;
|
|
83
|
-
return {
|
|
84
|
-
evidence_item_id: "structural:high_expression_count",
|
|
85
|
-
predicate: `derived.liability_summary.expression_breadth.high_expression_tissue_count=${reported}`,
|
|
86
|
-
contradicting_predicates: [`body-recomputed count is ${set.size}`],
|
|
87
|
-
surfaced_in_section: "derived.liability_summary.expression_breadth",
|
|
88
|
-
evidence: [],
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
export function detectStructuralEvidenceConflicts(bodyInput) {
|
|
92
|
-
const body = bodyInput;
|
|
93
|
-
const out = [];
|
|
94
|
-
for (const fn of [checkFaersCoverage, checkFatalSignal, checkTrialCount, checkHighExpressionCount]) {
|
|
95
|
-
const row = fn(body);
|
|
96
|
-
if (row)
|
|
97
|
-
out.push(row);
|
|
98
|
-
}
|
|
99
|
-
return out;
|
|
100
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TPM threshold for "high expression" classification, aligned with the
|
|
3
|
-
* Human Protein Atlas tissue-expression categorisation. Tissues with
|
|
4
|
-
* TPM >= this threshold count toward
|
|
5
|
-
* liability_summary.expression_breadth.high_expression_tissue_count
|
|
6
|
-
* and inform organ-rollup signal scoring.
|
|
7
|
-
*/
|
|
8
|
-
export declare const HIGH_EXPRESSION_TPM_THRESHOLD = 50;
|
|
9
|
-
/**
|
|
10
|
-
* Off-tissue floor for CNS regions. Brain subregions (hypothalamus excepted)
|
|
11
|
-
* carry biologically meaningful CALCR/peptide-GPCR expression at single-digit
|
|
12
|
-
* nTPM; the default 100 floor produced organ_claim_without_probe_pass audit
|
|
13
|
-
* failures for CNS bullets that the recommendation makes.
|
|
14
|
-
*/
|
|
15
|
-
export declare const CNS_REGION_TPM_FLOOR = 5;
|
|
16
|
-
/**
|
|
17
|
-
* Off-tissue floor for musculoskeletal tissues. Skeletal muscle and bone
|
|
18
|
-
* surfaces routinely show 20-80 nTPM for GPCRs whose ligands affect
|
|
19
|
-
* mineral and muscle metabolism.
|
|
20
|
-
*/
|
|
21
|
-
export declare const MUSCULOSKELETAL_TPM_FLOOR = 20;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TPM threshold for "high expression" classification, aligned with the
|
|
3
|
-
* Human Protein Atlas tissue-expression categorisation. Tissues with
|
|
4
|
-
* TPM >= this threshold count toward
|
|
5
|
-
* liability_summary.expression_breadth.high_expression_tissue_count
|
|
6
|
-
* and inform organ-rollup signal scoring.
|
|
7
|
-
*/
|
|
8
|
-
export const HIGH_EXPRESSION_TPM_THRESHOLD = 50;
|
|
9
|
-
/**
|
|
10
|
-
* Off-tissue floor for CNS regions. Brain subregions (hypothalamus excepted)
|
|
11
|
-
* carry biologically meaningful CALCR/peptide-GPCR expression at single-digit
|
|
12
|
-
* nTPM; the default 100 floor produced organ_claim_without_probe_pass audit
|
|
13
|
-
* failures for CNS bullets that the recommendation makes.
|
|
14
|
-
*/
|
|
15
|
-
export const CNS_REGION_TPM_FLOOR = 5;
|
|
16
|
-
/**
|
|
17
|
-
* Off-tissue floor for musculoskeletal tissues. Skeletal muscle and bone
|
|
18
|
-
* surfaces routinely show 20-80 nTPM for GPCRs whose ligands affect
|
|
19
|
-
* mineral and muscle metabolism.
|
|
20
|
-
*/
|
|
21
|
-
export const MUSCULOSKELETAL_TPM_FLOOR = 20;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build the obligate-cofactor supplement rows shown in
|
|
3
|
-
* OffTargetPanel.excluded_rows from the IUPHAR family-complexes collector
|
|
4
|
-
* bundle.
|
|
5
|
-
*
|
|
6
|
-
* pchembl is set to 0 because IUPHAR exposes complex structure, not
|
|
7
|
-
* ligand affinity for the complex; the row is marked selectivity_unknown
|
|
8
|
-
* with an explicit reason so downstream readers see the obligate-cofactor
|
|
9
|
-
* surface rather than a developable potency claim. The per-row LLM
|
|
10
|
-
* clinical_consequence annotator populates `clinical_consequence` once
|
|
11
|
-
* the per-row annotation step runs.
|
|
12
|
-
*/
|
|
13
|
-
import type { ExcludedOffTargetRowSchema } from "@inflexa-ai/harness/contracts/target-dossier.js";
|
|
14
|
-
import type { z } from "zod";
|
|
15
|
-
import type { FamilyComplexesBundle } from "../schemas.js";
|
|
16
|
-
type ExcludedOffTargetRow = z.infer<typeof ExcludedOffTargetRowSchema>;
|
|
17
|
-
export declare function buildFamilyComplexSupplement(bundle: FamilyComplexesBundle): (Omit<ExcludedOffTargetRow, "evidence"> & {
|
|
18
|
-
evidence: [];
|
|
19
|
-
})[];
|
|
20
|
-
export {};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build the obligate-cofactor supplement rows shown in
|
|
3
|
-
* OffTargetPanel.excluded_rows from the IUPHAR family-complexes collector
|
|
4
|
-
* bundle.
|
|
5
|
-
*
|
|
6
|
-
* pchembl is set to 0 because IUPHAR exposes complex structure, not
|
|
7
|
-
* ligand affinity for the complex; the row is marked selectivity_unknown
|
|
8
|
-
* with an explicit reason so downstream readers see the obligate-cofactor
|
|
9
|
-
* surface rather than a developable potency claim. The per-row LLM
|
|
10
|
-
* clinical_consequence annotator populates `clinical_consequence` once
|
|
11
|
-
* the per-row annotation step runs.
|
|
12
|
-
*/
|
|
13
|
-
export function buildFamilyComplexSupplement(bundle) {
|
|
14
|
-
return bundle.complexes.map((c) => {
|
|
15
|
-
const accDesc = c.accessoryNames.length > 0 ? c.accessoryNames.join("+") : "an accessory protein";
|
|
16
|
-
return {
|
|
17
|
-
off_target_id: null,
|
|
18
|
-
off_target_name: c.complexName,
|
|
19
|
-
target_class: "GPCR receptor-accessory complex",
|
|
20
|
-
pchembl: 0,
|
|
21
|
-
is_safety_panel_target: false,
|
|
22
|
-
organ_system: null,
|
|
23
|
-
clinical_consequence: null,
|
|
24
|
-
selectivity: {
|
|
25
|
-
selectivity_unknown: true,
|
|
26
|
-
reason: "obligate cofactor (same primary protein, different accessory)",
|
|
27
|
-
},
|
|
28
|
-
selectivity_window_below_threshold: false,
|
|
29
|
-
relationship: "obligate_cofactor",
|
|
30
|
-
reason: `${c.complexName} is ${bundle.primaryTargetGene} heterodimerised with ${accDesc}; ` +
|
|
31
|
-
`selectivity is not pharmacologically attainable. Surfaced as an intrinsic-pharmacology ` +
|
|
32
|
-
`liability rather than a developable off-target.`,
|
|
33
|
-
evidence: [],
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The typed form of an FDA Structured Product Label's safety prose, and the
|
|
3
|
-
* per-organ projection over it.
|
|
4
|
-
*
|
|
5
|
-
* A label arrives as prose in a handful of named sections. Ingesting it once
|
|
6
|
-
* into this shape is what lets the same warning reach two places without being
|
|
7
|
-
* fetched twice: the synthesis markdown renders the sections, and the dossier's
|
|
8
|
-
* per-organ regulatory signals are segmented out of them. Everything a reader
|
|
9
|
-
* needs to find a signal again — the application number and the label section —
|
|
10
|
-
* rides on the signal, so it satisfies the claim-evidence locator requirement.
|
|
11
|
-
*/
|
|
12
|
-
import type { OrganSystem } from "../../../contracts/organ-system.js";
|
|
13
|
-
import type { EvidenceItem } from "../../../contracts/target-dossier.js";
|
|
14
|
-
/** The label sections whose prose carries a safety finding. */
|
|
15
|
-
export declare const LABEL_SAFETY_SECTIONS: readonly ["boxed_warning", "warnings_and_precautions", "warnings", "contraindications"];
|
|
16
|
-
export type LabelSafetySection = (typeof LABEL_SAFETY_SECTIONS)[number];
|
|
17
|
-
/** One block of prose, keyed to the label section it was published under. */
|
|
18
|
-
export interface LabelSafetyText {
|
|
19
|
-
readonly section: LabelSafetySection;
|
|
20
|
-
readonly text: string;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* One label's identity plus its safety prose.
|
|
24
|
-
*
|
|
25
|
-
* `application_number` is required rather than nullable because it is half of
|
|
26
|
-
* what makes a signal resolvable; a producer holding a label without one has
|
|
27
|
-
* nothing to cite and does not construct this shape.
|
|
28
|
-
*/
|
|
29
|
-
export interface FdaLabelSafety {
|
|
30
|
-
readonly application_number: string;
|
|
31
|
-
readonly drug_name: string;
|
|
32
|
-
readonly effective_time?: string;
|
|
33
|
-
readonly source_url?: string;
|
|
34
|
-
readonly sections: readonly LabelSafetyText[];
|
|
35
|
-
}
|
|
36
|
-
/** A warning fragment attributed to one canonical organ, with its locator. */
|
|
37
|
-
export interface RegulatoryOrganSignal {
|
|
38
|
-
readonly organ: OrganSystem;
|
|
39
|
-
readonly drug_name: string;
|
|
40
|
-
readonly application_number: string;
|
|
41
|
-
/** The label section the prose was published under. */
|
|
42
|
-
readonly source_section: LabelSafetySection;
|
|
43
|
-
/** What a reader opens to find the excerpt: a numbered marker when the prose carried one. */
|
|
44
|
-
readonly label_section: string;
|
|
45
|
-
readonly excerpt: string;
|
|
46
|
-
readonly evidence: EvidenceItem;
|
|
47
|
-
}
|
|
48
|
-
export interface OrganSignalProjection {
|
|
49
|
-
readonly signals: readonly RegulatoryOrganSignal[];
|
|
50
|
-
/** Fragments whose organ did not resolve and were therefore not emitted. */
|
|
51
|
-
readonly dropped_count: number;
|
|
52
|
-
}
|
|
53
|
-
/** What the section reports as having emptied it when nothing resolved. */
|
|
54
|
-
export declare const ORGAN_RESOLUTION_FILTER = "label warning fragments with no resolvable organ";
|
|
55
|
-
interface WarningFragment {
|
|
56
|
-
readonly marker: string | null;
|
|
57
|
-
readonly text: string;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Split one section's prose into the fragments an organ can be attributed to:
|
|
61
|
-
* numbered label-section markers first, then bullets inside each marked block.
|
|
62
|
-
*/
|
|
63
|
-
export declare function splitWarningProse(text: string): WarningFragment[];
|
|
64
|
-
/**
|
|
65
|
-
* Project a set of ingested labels onto per-organ regulatory signals.
|
|
66
|
-
*
|
|
67
|
-
* A fragment that resolves to no canonical organ is dropped and counted rather
|
|
68
|
-
* than filed under a neighbouring organ. A fragment the highlights section
|
|
69
|
-
* repeats from the full section is emitted once.
|
|
70
|
-
*/
|
|
71
|
-
export declare function segmentLabelSafety(labels: readonly FdaLabelSafety[]): OrganSignalProjection;
|
|
72
|
-
export {};
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The typed form of an FDA Structured Product Label's safety prose, and the
|
|
3
|
-
* per-organ projection over it.
|
|
4
|
-
*
|
|
5
|
-
* A label arrives as prose in a handful of named sections. Ingesting it once
|
|
6
|
-
* into this shape is what lets the same warning reach two places without being
|
|
7
|
-
* fetched twice: the synthesis markdown renders the sections, and the dossier's
|
|
8
|
-
* per-organ regulatory signals are segmented out of them. Everything a reader
|
|
9
|
-
* needs to find a signal again — the application number and the label section —
|
|
10
|
-
* rides on the signal, so it satisfies the claim-evidence locator requirement.
|
|
11
|
-
*/
|
|
12
|
-
import { classifyOrgan } from "./meddra-organ-map.js";
|
|
13
|
-
/** The label sections whose prose carries a safety finding. */
|
|
14
|
-
export const LABEL_SAFETY_SECTIONS = ["boxed_warning", "warnings_and_precautions", "warnings", "contraindications"];
|
|
15
|
-
/** What the section reports as having emptied it when nothing resolved. */
|
|
16
|
-
export const ORGAN_RESOLUTION_FILTER = "label warning fragments with no resolvable organ";
|
|
17
|
-
/**
|
|
18
|
-
* Numbered label-section markers ("5.1 Hepatotoxicity"). Split as a lookahead
|
|
19
|
-
* so the marker stays with the prose it heads. The trailing uppercase-or-paren
|
|
20
|
-
* requirement is what keeps a dose ("2.5 mg") from reading as a marker.
|
|
21
|
-
*/
|
|
22
|
-
const SECTION_MARKER_SPLIT = /(?=\b\d{1,2}\.\d{1,2}\s+[A-Z(])/;
|
|
23
|
-
const SECTION_MARKER_HEAD = /^(\d{1,2}\.\d{1,2})\s+/;
|
|
24
|
-
/**
|
|
25
|
-
* Bullet boundaries. Single newlines are NOT boundaries — labels wrap prose
|
|
26
|
-
* mid-sentence, and splitting on the wrap would sever a finding from the organ
|
|
27
|
-
* named in its own heading.
|
|
28
|
-
*/
|
|
29
|
-
const FRAGMENT_SPLIT = /[•·▪‣]+|\r?\n\s*\r?\n+|\r?\n[ \t]*[-*]\s+/;
|
|
30
|
-
/** Below this a fragment is a stray heading remnant, not a finding. */
|
|
31
|
-
const MIN_FRAGMENT_CHARS = 16;
|
|
32
|
-
/** Excerpt budget per signal — enough to read the finding, bounded for storage. */
|
|
33
|
-
const MAX_EXCERPT_CHARS = 400;
|
|
34
|
-
function clipExcerpt(text) {
|
|
35
|
-
if (text.length <= MAX_EXCERPT_CHARS)
|
|
36
|
-
return text;
|
|
37
|
-
return `${text.slice(0, MAX_EXCERPT_CHARS - 1).trimEnd()}…`;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Split one section's prose into the fragments an organ can be attributed to:
|
|
41
|
-
* numbered label-section markers first, then bullets inside each marked block.
|
|
42
|
-
*/
|
|
43
|
-
export function splitWarningProse(text) {
|
|
44
|
-
const fragments = [];
|
|
45
|
-
for (const block of text.split(SECTION_MARKER_SPLIT)) {
|
|
46
|
-
const head = SECTION_MARKER_HEAD.exec(block);
|
|
47
|
-
const marker = head?.[1] ?? null;
|
|
48
|
-
const body = head ? block.slice(head[0].length) : block;
|
|
49
|
-
for (const piece of body.split(FRAGMENT_SPLIT)) {
|
|
50
|
-
const collapsed = piece.replace(/\s+/g, " ").trim();
|
|
51
|
-
if (collapsed.length < MIN_FRAGMENT_CHARS)
|
|
52
|
-
continue;
|
|
53
|
-
fragments.push({ marker, text: collapsed });
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return fragments;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Resolve a fragment onto the canonical organ vocabulary at its own boundary.
|
|
60
|
-
* The heading — the prose up to its first colon — is tried first, because a
|
|
61
|
-
* label states the organ there and then discusses it; scanning the whole
|
|
62
|
-
* fragment first would let an incidental mention outrank the heading.
|
|
63
|
-
*/
|
|
64
|
-
function resolveFragmentOrgan(fragment) {
|
|
65
|
-
const colon = fragment.indexOf(":");
|
|
66
|
-
const heading = colon > 0 ? fragment.slice(0, colon) : "";
|
|
67
|
-
return (heading ? classifyOrgan(heading) : null) ?? classifyOrgan(fragment);
|
|
68
|
-
}
|
|
69
|
-
function buildEvidence(label, labelSection, excerpt, organ) {
|
|
70
|
-
return {
|
|
71
|
-
source: "openfda:label",
|
|
72
|
-
predicate: "label_warning",
|
|
73
|
-
excerpt,
|
|
74
|
-
regulatory_reference: {
|
|
75
|
-
document: `FDA prescribing information — ${label.drug_name}`,
|
|
76
|
-
section: labelSection,
|
|
77
|
-
doc_id: label.application_number,
|
|
78
|
-
...(label.source_url ? { doc_url: label.source_url } : {}),
|
|
79
|
-
},
|
|
80
|
-
metadata: {
|
|
81
|
-
organ,
|
|
82
|
-
drug_name: label.drug_name,
|
|
83
|
-
...(label.effective_time ? { effective_time: label.effective_time } : {}),
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* A short fragment can be a substring of a longer unrelated one, so containment
|
|
89
|
-
* only settles a repeat once the contained text is long enough to be prose
|
|
90
|
-
* rather than a phrase.
|
|
91
|
-
*/
|
|
92
|
-
const MIN_CONTAINMENT_CHARS = 60;
|
|
93
|
-
function isRepeatOf(seen, candidate) {
|
|
94
|
-
if (seen === candidate)
|
|
95
|
-
return true;
|
|
96
|
-
if (seen.includes(candidate))
|
|
97
|
-
return candidate.length >= MIN_CONTAINMENT_CHARS;
|
|
98
|
-
if (candidate.includes(seen))
|
|
99
|
-
return seen.length >= MIN_CONTAINMENT_CHARS;
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Order a label's sections for segmentation: published section order, then
|
|
104
|
-
* longest prose first within a section. openFDA returns the highlights summary
|
|
105
|
-
* and the full section under the same key, so reading the longer one first is
|
|
106
|
-
* what makes the shorter repeat recognisable as a repeat rather than the other
|
|
107
|
-
* way round.
|
|
108
|
-
*/
|
|
109
|
-
function segmentationOrder(sections) {
|
|
110
|
-
const firstSeenAt = new Map();
|
|
111
|
-
for (const [index, s] of sections.entries()) {
|
|
112
|
-
if (!firstSeenAt.has(s.section))
|
|
113
|
-
firstSeenAt.set(s.section, index);
|
|
114
|
-
}
|
|
115
|
-
return [...sections].sort((a, b) => firstSeenAt.get(a.section) - firstSeenAt.get(b.section) || b.text.length - a.text.length);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Project a set of ingested labels onto per-organ regulatory signals.
|
|
119
|
-
*
|
|
120
|
-
* A fragment that resolves to no canonical organ is dropped and counted rather
|
|
121
|
-
* than filed under a neighbouring organ. A fragment the highlights section
|
|
122
|
-
* repeats from the full section is emitted once.
|
|
123
|
-
*/
|
|
124
|
-
export function segmentLabelSafety(labels) {
|
|
125
|
-
const signals = [];
|
|
126
|
-
let droppedCount = 0;
|
|
127
|
-
for (const label of labels) {
|
|
128
|
-
const seenMarkers = new Set();
|
|
129
|
-
const seenText = [];
|
|
130
|
-
for (const section of segmentationOrder(label.sections)) {
|
|
131
|
-
// A numbered marker settles a repeat only across blocks: one block
|
|
132
|
-
// legitimately carries several fragments under its own `5.1`, while
|
|
133
|
-
// a second block reprising `5.1` is the highlights summary of it.
|
|
134
|
-
const blockMarkers = new Set();
|
|
135
|
-
for (const fragment of splitWarningProse(section.text)) {
|
|
136
|
-
const normalized = fragment.text.toLowerCase();
|
|
137
|
-
const markerKey = fragment.marker ? `${section.section}:${fragment.marker}` : null;
|
|
138
|
-
const repeated = (markerKey !== null && seenMarkers.has(markerKey)) || seenText.some((seen) => isRepeatOf(seen, normalized));
|
|
139
|
-
if (repeated)
|
|
140
|
-
continue;
|
|
141
|
-
if (markerKey !== null)
|
|
142
|
-
blockMarkers.add(markerKey);
|
|
143
|
-
seenText.push(normalized);
|
|
144
|
-
const organ = resolveFragmentOrgan(fragment.text);
|
|
145
|
-
if (organ === null) {
|
|
146
|
-
droppedCount += 1;
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
const labelSection = fragment.marker ?? section.section;
|
|
150
|
-
const excerpt = clipExcerpt(fragment.text);
|
|
151
|
-
signals.push({
|
|
152
|
-
organ,
|
|
153
|
-
drug_name: label.drug_name,
|
|
154
|
-
application_number: label.application_number,
|
|
155
|
-
source_section: section.section,
|
|
156
|
-
label_section: labelSection,
|
|
157
|
-
excerpt,
|
|
158
|
-
evidence: buildEvidence(label, labelSection, excerpt, organ),
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
for (const marker of blockMarkers)
|
|
162
|
-
seenMarkers.add(marker);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return { signals, dropped_count: droppedCount };
|
|
166
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detect receptor-accessory heterodimer entries that share the assessment
|
|
3
|
-
* target's gene product. These are NOT off-targets in the pharmacological
|
|
4
|
-
* sense: the primary protein is the same, only the obligate accessory
|
|
5
|
-
* cofactor differs. Selectivity windows between such partner complexes are
|
|
6
|
-
* not pharmacologically attainable and should not be reported as off-target
|
|
7
|
-
* liabilities.
|
|
8
|
-
*
|
|
9
|
-
* The accessory list is supplied by the IUPHAR family-complexes collector
|
|
10
|
-
* at runtime — there is no hardcoded RAMP/MRAP/RGS pattern. For a CALCR
|
|
11
|
-
* assessment the collector returns ["RAMP1","RAMP2","RAMP3"], so the
|
|
12
|
-
* regex matches "CALCR/RAMP1", "CALCR/RAMP2", "CALCR/RAMP3" only.
|
|
13
|
-
*/
|
|
14
|
-
export interface HeterodimerFilterInput {
|
|
15
|
-
assessmentGeneSymbol: string;
|
|
16
|
-
accessoryProteinNames: string[];
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Build a predicate that returns true when an off-target name describes a
|
|
20
|
-
* heterodimer of the assessment target's primary protein with one of the
|
|
21
|
-
* supplied accessory gene symbols. Returns a constant-false predicate when
|
|
22
|
-
* the inputs cannot produce a meaningful match (missing gene symbol or
|
|
23
|
-
* empty accessory list).
|
|
24
|
-
*
|
|
25
|
-
* Matched substrings:
|
|
26
|
-
* "<primary>/<accessory>" (e.g., "CALCR/RAMP3")
|
|
27
|
-
* "<primary> + <accessory>"
|
|
28
|
-
* "<primary>-<accessory>" (some sources use a hyphen)
|
|
29
|
-
*/
|
|
30
|
-
export declare function makeHeterodimerOfAssessmentFilter(input: HeterodimerFilterInput): (offTargetName: string) => boolean;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detect receptor-accessory heterodimer entries that share the assessment
|
|
3
|
-
* target's gene product. These are NOT off-targets in the pharmacological
|
|
4
|
-
* sense: the primary protein is the same, only the obligate accessory
|
|
5
|
-
* cofactor differs. Selectivity windows between such partner complexes are
|
|
6
|
-
* not pharmacologically attainable and should not be reported as off-target
|
|
7
|
-
* liabilities.
|
|
8
|
-
*
|
|
9
|
-
* The accessory list is supplied by the IUPHAR family-complexes collector
|
|
10
|
-
* at runtime — there is no hardcoded RAMP/MRAP/RGS pattern. For a CALCR
|
|
11
|
-
* assessment the collector returns ["RAMP1","RAMP2","RAMP3"], so the
|
|
12
|
-
* regex matches "CALCR/RAMP1", "CALCR/RAMP2", "CALCR/RAMP3" only.
|
|
13
|
-
*/
|
|
14
|
-
const ACC_ESCAPE_RE = /[.*+?^${}()|[\]\\]/g;
|
|
15
|
-
function escapeForRegex(token) {
|
|
16
|
-
return token.replace(ACC_ESCAPE_RE, "\\$&");
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Build a predicate that returns true when an off-target name describes a
|
|
20
|
-
* heterodimer of the assessment target's primary protein with one of the
|
|
21
|
-
* supplied accessory gene symbols. Returns a constant-false predicate when
|
|
22
|
-
* the inputs cannot produce a meaningful match (missing gene symbol or
|
|
23
|
-
* empty accessory list).
|
|
24
|
-
*
|
|
25
|
-
* Matched substrings:
|
|
26
|
-
* "<primary>/<accessory>" (e.g., "CALCR/RAMP3")
|
|
27
|
-
* "<primary> + <accessory>"
|
|
28
|
-
* "<primary>-<accessory>" (some sources use a hyphen)
|
|
29
|
-
*/
|
|
30
|
-
export function makeHeterodimerOfAssessmentFilter(input) {
|
|
31
|
-
const target = input.assessmentGeneSymbol.trim().toUpperCase();
|
|
32
|
-
const accessories = input.accessoryProteinNames.map((n) => n.trim()).filter((n) => n.length > 0);
|
|
33
|
-
if (!target || accessories.length === 0) {
|
|
34
|
-
return () => false;
|
|
35
|
-
}
|
|
36
|
-
const alternation = accessories.map(escapeForRegex).join("|");
|
|
37
|
-
const re = new RegExp(`\\b([A-Z][A-Z0-9]{1,9})\\s*[\\/+\\-]\\s*(${alternation})\\b`, "i");
|
|
38
|
-
return (offTargetName) => {
|
|
39
|
-
const m = re.exec(offTargetName);
|
|
40
|
-
if (!m)
|
|
41
|
-
return false;
|
|
42
|
-
return m[1].toUpperCase() === target;
|
|
43
|
-
};
|
|
44
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Human Phenotype Ontology terms onto the canonical organ vocabulary.
|
|
3
|
-
*
|
|
4
|
-
* HPO organises every phenotypic abnormality under organ-system roots, and a
|
|
5
|
-
* phenotype association carries its full ancestor closure. Resolution is
|
|
6
|
-
* therefore membership of an ancestor id in this table — equality on
|
|
7
|
-
* identifiers, not matching on prose, so a renamed term keeps resolving and an
|
|
8
|
-
* incidental word in a label never decides an organ.
|
|
9
|
-
*
|
|
10
|
-
* The table is ordered most specific first, because a term sits under several
|
|
11
|
-
* roots at once: a liver phenotype's closure also contains the digestive-system
|
|
12
|
-
* root, and reading the broad root first would file every hepatic finding under
|
|
13
|
-
* `gi`. Roots with no canonical organ to land in (ear, head and neck, growth,
|
|
14
|
-
* prenatal) are absent — a phenotype is reported under the organ it affects or
|
|
15
|
-
* not at all.
|
|
16
|
-
*
|
|
17
|
-
* A root broader than any single token is absent for that same reason, even
|
|
18
|
-
* where a token's name is close to it: the endocrine-system root spans every
|
|
19
|
-
* endocrine organ while the only endocrine token names the thyroid, so an
|
|
20
|
-
* adrenal phenotype resolves through no root and is dropped. The neoplasm root
|
|
21
|
-
* says a phenotype is malignant, not where it sits, so a neoplasm resolves
|
|
22
|
-
* through the organ root it also carries — a lung neoplasm is respiratory, and
|
|
23
|
-
* a neoplasm with no organ root at all resolves to nothing.
|
|
24
|
-
*
|
|
25
|
-
* The values are the canonical tokens, so this module declares no organ
|
|
26
|
-
* enumeration of its own.
|
|
27
|
-
*/
|
|
28
|
-
import type { OrganSystem } from "../../../contracts/organ-system.js";
|
|
29
|
-
/**
|
|
30
|
-
* Resolve an HPO term onto a canonical organ from its own id plus its ancestor
|
|
31
|
-
* closure. Returns null when nothing in the ancestry denotes a canonical organ
|
|
32
|
-
* system — the caller drops the phenotype and counts it.
|
|
33
|
-
*/
|
|
34
|
-
export declare function resolveHpoOrgan(termId: string, ancestorIds: readonly string[]): OrganSystem | null;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Human Phenotype Ontology terms onto the canonical organ vocabulary.
|
|
3
|
-
*
|
|
4
|
-
* HPO organises every phenotypic abnormality under organ-system roots, and a
|
|
5
|
-
* phenotype association carries its full ancestor closure. Resolution is
|
|
6
|
-
* therefore membership of an ancestor id in this table — equality on
|
|
7
|
-
* identifiers, not matching on prose, so a renamed term keeps resolving and an
|
|
8
|
-
* incidental word in a label never decides an organ.
|
|
9
|
-
*
|
|
10
|
-
* The table is ordered most specific first, because a term sits under several
|
|
11
|
-
* roots at once: a liver phenotype's closure also contains the digestive-system
|
|
12
|
-
* root, and reading the broad root first would file every hepatic finding under
|
|
13
|
-
* `gi`. Roots with no canonical organ to land in (ear, head and neck, growth,
|
|
14
|
-
* prenatal) are absent — a phenotype is reported under the organ it affects or
|
|
15
|
-
* not at all.
|
|
16
|
-
*
|
|
17
|
-
* A root broader than any single token is absent for that same reason, even
|
|
18
|
-
* where a token's name is close to it: the endocrine-system root spans every
|
|
19
|
-
* endocrine organ while the only endocrine token names the thyroid, so an
|
|
20
|
-
* adrenal phenotype resolves through no root and is dropped. The neoplasm root
|
|
21
|
-
* says a phenotype is malignant, not where it sits, so a neoplasm resolves
|
|
22
|
-
* through the organ root it also carries — a lung neoplasm is respiratory, and
|
|
23
|
-
* a neoplasm with no organ root at all resolves to nothing.
|
|
24
|
-
*
|
|
25
|
-
* The values are the canonical tokens, so this module declares no organ
|
|
26
|
-
* enumeration of its own.
|
|
27
|
-
*/
|
|
28
|
-
/** HPO ancestor id → canonical organ, most specific ancestor first. */
|
|
29
|
-
const HPO_ORGAN_ANCESTORS = [
|
|
30
|
-
["HP:0001392", "hepatic"], // Abnormality of the liver
|
|
31
|
-
["HP:0001733", "pancreas"], // Abnormality of the pancreas
|
|
32
|
-
["HP:0000820", "endocrine_thyroid"], // Abnormality of the thyroid gland
|
|
33
|
-
["HP:0000079", "renal"], // Abnormality of the urinary system
|
|
34
|
-
["HP:0000078", "reproductive"], // Abnormality of the genital system
|
|
35
|
-
["HP:0002597", "vascular"], // Abnormality of the vasculature
|
|
36
|
-
["HP:0001627", "cardiac"], // Abnormal heart morphology
|
|
37
|
-
["HP:0001626", "cardiac"], // Abnormality of the cardiovascular system
|
|
38
|
-
["HP:0000759", "pns"], // Abnormal peripheral nervous system morphology
|
|
39
|
-
["HP:0012639", "cns"], // Abnormal nervous system morphology
|
|
40
|
-
["HP:0000707", "cns"], // Abnormality of the nervous system
|
|
41
|
-
["HP:0002715", "immune"], // Abnormality of the immune system
|
|
42
|
-
["HP:0001871", "hematologic"], // Abnormality of blood and blood-forming tissues
|
|
43
|
-
["HP:0001939", "metabolic"], // Abnormality of metabolism/homeostasis
|
|
44
|
-
["HP:0002086", "respiratory"], // Abnormality of the respiratory system
|
|
45
|
-
["HP:0025031", "gi"], // Abnormality of the digestive system
|
|
46
|
-
["HP:0000478", "ocular"], // Abnormality of the eye
|
|
47
|
-
["HP:0001574", "dermatologic"], // Abnormality of the integument
|
|
48
|
-
["HP:0003011", "musculoskeletal"], // Abnormality of the musculature
|
|
49
|
-
["HP:0000924", "musculoskeletal"], // Abnormality of the skeletal system
|
|
50
|
-
];
|
|
51
|
-
/**
|
|
52
|
-
* Resolve an HPO term onto a canonical organ from its own id plus its ancestor
|
|
53
|
-
* closure. Returns null when nothing in the ancestry denotes a canonical organ
|
|
54
|
-
* system — the caller drops the phenotype and counts it.
|
|
55
|
-
*/
|
|
56
|
-
export function resolveHpoOrgan(termId, ancestorIds) {
|
|
57
|
-
const ancestry = new Set([termId, ...ancestorIds]);
|
|
58
|
-
for (const [id, organ] of HPO_ORGAN_ANCESTORS) {
|
|
59
|
-
if (ancestry.has(id))
|
|
60
|
-
return organ;
|
|
61
|
-
}
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The IMPC top-level-phenotype vocabulary onto the canonical organ vocabulary.
|
|
3
|
-
*
|
|
4
|
-
* Buckets with no organ system to land in (hearing, growth, mortality,
|
|
5
|
-
* craniofacial) are absent: a phenotype is reported under the organ it affects
|
|
6
|
-
* or not at all, never under an approximate neighbour. The map's values are the
|
|
7
|
-
* canonical tokens, so it declares no enumeration of its own.
|
|
8
|
-
*
|
|
9
|
-
* The endocrine bucket is absent for the same reason: IMPC's
|
|
10
|
-
* "endocrine/exocrine gland phenotype" spans every endocrine organ and the
|
|
11
|
-
* exocrine glands besides, while the only endocrine token names the thyroid.
|
|
12
|
-
* A knockout in that bucket names no canonical organ, so it is dropped and
|
|
13
|
-
* counted rather than read as a thyroid finding.
|
|
14
|
-
*/
|
|
15
|
-
import type { OrganSystem } from "../../../contracts/organ-system.js";
|
|
16
|
-
/** Resolve one IMPC bucket, or null when it denotes no canonical organ. */
|
|
17
|
-
export declare function resolveImpcOrgan(bucket: string): OrganSystem | null;
|
|
18
|
-
/** Resolve a set of IMPC buckets, dropping those with no canonical organ. */
|
|
19
|
-
export declare function toOrganSystems(impcOrganSystems: readonly string[]): OrganSystem[];
|