heor-agent-mcp 0.3.0 → 0.7.1
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/README.md +44 -0
- package/dist/models/evppi.d.ts +33 -0
- package/dist/models/evppi.d.ts.map +1 -0
- package/dist/models/evppi.js +75 -0
- package/dist/models/evppi.js.map +1 -0
- package/dist/models/modelUtils.d.ts +7 -2
- package/dist/models/modelUtils.d.ts.map +1 -1
- package/dist/models/modelUtils.js +49 -31
- package/dist/models/modelUtils.js.map +1 -1
- package/dist/models/psa.d.ts +4 -0
- package/dist/models/psa.d.ts.map +1 -1
- package/dist/models/psa.js +29 -2
- package/dist/models/psa.js.map +1 -1
- package/dist/models/survivalFitting.d.ts +47 -0
- package/dist/models/survivalFitting.d.ts.map +1 -0
- package/dist/models/survivalFitting.js +342 -0
- package/dist/models/survivalFitting.js.map +1 -0
- package/dist/providers/direct/index.d.ts.map +1 -1
- package/dist/providers/direct/index.js +43 -23
- package/dist/providers/direct/index.js.map +1 -1
- package/dist/providers/direct/iqwig.js +1 -1
- package/dist/providers/direct/iqwig.js.map +1 -1
- package/dist/providers/direct/niceTa.d.ts +4 -0
- package/dist/providers/direct/niceTa.d.ts.map +1 -1
- package/dist/providers/direct/niceTa.js +76 -25
- package/dist/providers/direct/niceTa.js.map +1 -1
- package/dist/providers/direct/tlv.js +1 -1
- package/dist/providers/direct/tlv.js.map +1 -1
- package/dist/providers/types.d.ts +5 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/server.js +84 -9
- package/dist/server.js.map +1 -1
- package/dist/tools/budgetImpactModel.d.ts +116 -0
- package/dist/tools/budgetImpactModel.d.ts.map +1 -0
- package/dist/tools/budgetImpactModel.js +257 -0
- package/dist/tools/budgetImpactModel.js.map +1 -0
- package/dist/tools/costEffectivenessModel.d.ts +18 -0
- package/dist/tools/costEffectivenessModel.d.ts.map +1 -1
- package/dist/tools/costEffectivenessModel.js +155 -27
- package/dist/tools/costEffectivenessModel.js.map +1 -1
- package/dist/tools/htaDossierPrep.d.ts.map +1 -1
- package/dist/tools/htaDossierPrep.js +119 -0
- package/dist/tools/htaDossierPrep.js.map +1 -1
- package/dist/tools/knowledgeWrite.d.ts.map +1 -1
- package/dist/tools/knowledgeWrite.js +17 -3
- package/dist/tools/knowledgeWrite.js.map +1 -1
- package/dist/tools/populationAdjustedComparison.d.ts +145 -0
- package/dist/tools/populationAdjustedComparison.d.ts.map +1 -0
- package/dist/tools/populationAdjustedComparison.js +431 -0
- package/dist/tools/populationAdjustedComparison.js.map +1 -0
- package/dist/tools/screenAbstracts.d.ts +99 -0
- package/dist/tools/screenAbstracts.d.ts.map +1 -0
- package/dist/tools/screenAbstracts.js +442 -0
- package/dist/tools/screenAbstracts.js.map +1 -0
- package/dist/tools/survivalFitting.d.ts +56 -0
- package/dist/tools/survivalFitting.d.ts.map +1 -0
- package/dist/tools/survivalFitting.js +147 -0
- package/dist/tools/survivalFitting.js.map +1 -0
- package/dist/tools/validateLinks.d.ts +24 -0
- package/dist/tools/validateLinks.d.ts.map +1 -0
- package/dist/tools/validateLinks.js +171 -0
- package/dist/tools/validateLinks.js.map +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -126,6 +126,50 @@ Pass `project: "project-id"` to any tool and results are saved automatically.
|
|
|
126
126
|
|
|
127
127
|
---
|
|
128
128
|
|
|
129
|
+
## Examples
|
|
130
|
+
|
|
131
|
+
Copy-paste prompts to try in Claude Code, Claude Desktop, or the [web UI](https://web-michael-ns-projects.vercel.app).
|
|
132
|
+
|
|
133
|
+
### Single-tool examples
|
|
134
|
+
|
|
135
|
+
**Literature search**
|
|
136
|
+
> Search the literature for tirzepatide cardiovascular outcomes in type 2 diabetes. Use PubMed, ClinicalTrials.gov, and NICE TAs.
|
|
137
|
+
|
|
138
|
+
**Survival curve fitting**
|
|
139
|
+
> Fit survival curves to this OS data from KEYNOTE-189: time 0 survival 1.0, time 6 survival 0.88, time 12 survival 0.72, time 18 survival 0.60, time 24 survival 0.51, time 36 survival 0.38. Use months.
|
|
140
|
+
|
|
141
|
+
**Budget impact**
|
|
142
|
+
> Estimate the 5-year NHS budget impact of semaglutide for obesity. 200,000 eligible patients, drug cost £1,200/year, comparator (orlistat) £250/year, uptake 15% year 1 to 40% year 5.
|
|
143
|
+
|
|
144
|
+
**Cost-effectiveness model**
|
|
145
|
+
> Build a CE model for semaglutide vs sitagliptin in T2D, NHS perspective, lifetime horizon, with PSA.
|
|
146
|
+
|
|
147
|
+
**Indirect comparison (Bucher)**
|
|
148
|
+
> I have two trials: SUSTAIN-1 showed semaglutide vs placebo HR 0.74 (0.58-0.95) for HbA1c, and AWARD-5 showed dulaglutide vs placebo HR 0.78 (0.65-0.93). Run a Bucher indirect comparison between semaglutide and dulaglutide.
|
|
149
|
+
|
|
150
|
+
**MAIC (population-adjusted comparison)**
|
|
151
|
+
> Run a MAIC between SUSTAIN-7 (N=300, semaglutide vs placebo, HR 0.74, CI 0.58-0.95, age 56±10, BMI 33±5) and AWARD-11 (N=600, dulaglutide vs placebo, HR 0.78, CI 0.65-0.93, age 58±9, BMI 35±6). Adjust for age and BMI.
|
|
152
|
+
|
|
153
|
+
### Multi-tool workflows
|
|
154
|
+
|
|
155
|
+
**Abstract screening workflow**
|
|
156
|
+
> Search PubMed for pembrolizumab in NSCLC, then screen the results with population adults with NSCLC, intervention pembrolizumab, comparator chemotherapy, outcomes overall survival and PFS.
|
|
157
|
+
|
|
158
|
+
**Evidence network + NMA feasibility**
|
|
159
|
+
> Search for GLP-1 receptor agonists in T2D using PubMed, build an evidence network from the results, and assess NMA feasibility.
|
|
160
|
+
|
|
161
|
+
**CE model with scenarios**
|
|
162
|
+
> Build a CE model for dapagliflozin vs placebo in heart failure, NHS perspective, lifetime horizon, with PSA. Add scenarios: "20% price reduction" with drug cost 400, "10-year horizon" with time_horizon 10yr.
|
|
163
|
+
|
|
164
|
+
### End-to-end HTA workflow
|
|
165
|
+
|
|
166
|
+
**Full dossier preparation**
|
|
167
|
+
> Create a project for semaglutide in obesity targeting NICE and ICER. Search literature for evidence, screen the results for adults with obesity comparing semaglutide to placebo for weight loss outcomes, then draft a NICE STA dossier using the screened results.
|
|
168
|
+
|
|
169
|
+
This single prompt exercises: `project_create` → `literature_search` → `screen_abstracts` → `hta_dossier_prep` (with auto-GRADE).
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
129
173
|
## Data Sources
|
|
130
174
|
|
|
131
175
|
**41 sources across 9 categories.** Every `literature_search` call includes a source selection table showing used/not-used status and reason for each.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expected Value of Partial Perfect Information (EVPPI)
|
|
3
|
+
*
|
|
4
|
+
* Per-parameter VOI analysis: shows which specific parameters are
|
|
5
|
+
* worth further research by computing how much value would be gained
|
|
6
|
+
* from resolving uncertainty in each parameter individually.
|
|
7
|
+
*
|
|
8
|
+
* Method: non-parametric regression (GAM approximation via binning)
|
|
9
|
+
* following Strong et al. (2014) "Estimating multiparameter partial
|
|
10
|
+
* expected value of perfect information from a probabilistic
|
|
11
|
+
* sensitivity analysis sample"
|
|
12
|
+
*/
|
|
13
|
+
export interface EVPPIResult {
|
|
14
|
+
parameter: string;
|
|
15
|
+
evppi: number;
|
|
16
|
+
evppi_proportion: number;
|
|
17
|
+
}
|
|
18
|
+
export interface PSAIterationData {
|
|
19
|
+
delta_cost: number;
|
|
20
|
+
delta_qaly: number;
|
|
21
|
+
params: Record<string, number>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Compute EVPPI for each parameter using the binning method.
|
|
25
|
+
*
|
|
26
|
+
* For each parameter theta_j:
|
|
27
|
+
* 1. Sort iterations by theta_j
|
|
28
|
+
* 2. Bin into K groups
|
|
29
|
+
* 3. Within each bin, compute E[max_arm(NMB)]
|
|
30
|
+
* 4. EVPPI(theta_j) = E_bins[max(NMB_bin)] - max(E[NMB])
|
|
31
|
+
*/
|
|
32
|
+
export declare function computeEVPPI(iterations: PSAIterationData[], lambda: number, parameterNames: string[]): EVPPIResult[];
|
|
33
|
+
//# sourceMappingURL=evppi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evppi.d.ts","sourceRoot":"","sources":["../../src/models/evppi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,gBAAgB,EAAE,EAC9B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EAAE,GACvB,WAAW,EAAE,CAsEf"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expected Value of Partial Perfect Information (EVPPI)
|
|
3
|
+
*
|
|
4
|
+
* Per-parameter VOI analysis: shows which specific parameters are
|
|
5
|
+
* worth further research by computing how much value would be gained
|
|
6
|
+
* from resolving uncertainty in each parameter individually.
|
|
7
|
+
*
|
|
8
|
+
* Method: non-parametric regression (GAM approximation via binning)
|
|
9
|
+
* following Strong et al. (2014) "Estimating multiparameter partial
|
|
10
|
+
* expected value of perfect information from a probabilistic
|
|
11
|
+
* sensitivity analysis sample"
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Compute EVPPI for each parameter using the binning method.
|
|
15
|
+
*
|
|
16
|
+
* For each parameter theta_j:
|
|
17
|
+
* 1. Sort iterations by theta_j
|
|
18
|
+
* 2. Bin into K groups
|
|
19
|
+
* 3. Within each bin, compute E[max_arm(NMB)]
|
|
20
|
+
* 4. EVPPI(theta_j) = E_bins[max(NMB_bin)] - max(E[NMB])
|
|
21
|
+
*/
|
|
22
|
+
export function computeEVPPI(iterations, lambda, parameterNames) {
|
|
23
|
+
const N = iterations.length;
|
|
24
|
+
if (N < 20)
|
|
25
|
+
return [];
|
|
26
|
+
// Overall expected NMB for each arm
|
|
27
|
+
const nmb_intervention_mean = iterations.reduce((sum, it) => sum + (lambda * it.delta_qaly - it.delta_cost), 0) / N;
|
|
28
|
+
const nmb_comparator_mean = 0;
|
|
29
|
+
const max_e_nmb = Math.max(nmb_intervention_mean, nmb_comparator_mean);
|
|
30
|
+
// Total EVPI for reference
|
|
31
|
+
const e_max_nmb = iterations.reduce((sum, it) => {
|
|
32
|
+
const nmb_int = lambda * it.delta_qaly - it.delta_cost;
|
|
33
|
+
return sum + Math.max(nmb_int, 0);
|
|
34
|
+
}, 0) / N;
|
|
35
|
+
const totalEVPI = Math.max(0, e_max_nmb - max_e_nmb);
|
|
36
|
+
// Number of bins (Sturges' rule)
|
|
37
|
+
const K = Math.max(5, Math.min(30, Math.ceil(1 + 3.322 * Math.log10(N))));
|
|
38
|
+
const binSize = Math.ceil(N / K);
|
|
39
|
+
const results = [];
|
|
40
|
+
for (const paramName of parameterNames) {
|
|
41
|
+
// Check if parameter exists in iterations
|
|
42
|
+
if (iterations[0]?.params[paramName] === undefined)
|
|
43
|
+
continue;
|
|
44
|
+
// Sort by this parameter
|
|
45
|
+
const sorted = [...iterations].sort((a, b) => (a.params[paramName] ?? 0) - (b.params[paramName] ?? 0));
|
|
46
|
+
// Bin and compute conditional expectations
|
|
47
|
+
let e_max_conditional = 0;
|
|
48
|
+
for (let bin = 0; bin < K; bin++) {
|
|
49
|
+
const start = bin * binSize;
|
|
50
|
+
const end = Math.min(start + binSize, N);
|
|
51
|
+
const binN = end - start;
|
|
52
|
+
if (binN === 0)
|
|
53
|
+
continue;
|
|
54
|
+
// Expected NMB within this bin for intervention
|
|
55
|
+
let sumNMBInt = 0;
|
|
56
|
+
for (let i = start; i < end; i++) {
|
|
57
|
+
const it = sorted[i];
|
|
58
|
+
sumNMBInt += lambda * it.delta_qaly - it.delta_cost;
|
|
59
|
+
}
|
|
60
|
+
const meanNMBInt = sumNMBInt / binN;
|
|
61
|
+
// Max of conditional expectations
|
|
62
|
+
e_max_conditional += Math.max(meanNMBInt, 0) * (binN / N);
|
|
63
|
+
}
|
|
64
|
+
const evppi = Math.max(0, e_max_conditional - max_e_nmb);
|
|
65
|
+
results.push({
|
|
66
|
+
parameter: paramName,
|
|
67
|
+
evppi,
|
|
68
|
+
evppi_proportion: totalEVPI > 0 ? evppi / totalEVPI : 0,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Sort by EVPPI descending
|
|
72
|
+
results.sort((a, b) => b.evppi - a.evppi);
|
|
73
|
+
return results;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=evppi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evppi.js","sourceRoot":"","sources":["../../src/models/evppi.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,9 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { CEModelParams } from "../providers/types.js";
|
|
6
6
|
import type { MarkovParams } from "./markov.js";
|
|
7
|
+
export declare function getTimeHorizonYears(horizon: CEModelParams["time_horizon"]): number;
|
|
7
8
|
/**
|
|
8
|
-
* Build MarkovParams from CEModelParams using a
|
|
9
|
-
* (On-Treatment, Off-Treatment).
|
|
9
|
+
* Build MarkovParams from CEModelParams using a 3-state model
|
|
10
|
+
* (On-Treatment, Off-Treatment, Dead).
|
|
11
|
+
*
|
|
12
|
+
* The Dead state is an absorbing state (utility=0, cost=0) — the cohort
|
|
13
|
+
* fraction that enters Dead never leaves, which correctly bounds life-year
|
|
14
|
+
* and QALY accumulation over the time horizon.
|
|
10
15
|
*/
|
|
11
16
|
export declare function buildMarkovParamsFromCE(params: CEModelParams): MarkovParams;
|
|
12
17
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelUtils.d.ts","sourceRoot":"","sources":["../../src/models/modelUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"modelUtils.d.ts","sourceRoot":"","sources":["../../src/models/modelUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,aAAa,CAAC;AAK/E,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,GACrC,MAAM,CAKR;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,CAgG3E;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAgEA"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { runMarkovModel } from "./markov.js";
|
|
6
6
|
const DISCOUNT_RATE = 0.035;
|
|
7
|
-
function getTimeHorizonYears(horizon) {
|
|
7
|
+
export function getTimeHorizonYears(horizon) {
|
|
8
8
|
if (horizon === "lifetime")
|
|
9
9
|
return 40;
|
|
10
10
|
if (horizon === "5yr")
|
|
@@ -14,8 +14,12 @@ function getTimeHorizonYears(horizon) {
|
|
|
14
14
|
return Number(horizon);
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Build MarkovParams from CEModelParams using a
|
|
18
|
-
* (On-Treatment, Off-Treatment).
|
|
17
|
+
* Build MarkovParams from CEModelParams using a 3-state model
|
|
18
|
+
* (On-Treatment, Off-Treatment, Dead).
|
|
19
|
+
*
|
|
20
|
+
* The Dead state is an absorbing state (utility=0, cost=0) — the cohort
|
|
21
|
+
* fraction that enters Dead never leaves, which correctly bounds life-year
|
|
22
|
+
* and QALY accumulation over the time horizon.
|
|
19
23
|
*/
|
|
20
24
|
export function buildMarkovParamsFromCE(params) {
|
|
21
25
|
const years = getTimeHorizonYears(params.time_horizon);
|
|
@@ -29,50 +33,62 @@ export function buildMarkovParamsFromCE(params) {
|
|
|
29
33
|
const costComparator = params.cost_inputs.comparator_cost_annual +
|
|
30
34
|
(params.cost_inputs.admin_cost ?? 0);
|
|
31
35
|
// Efficacy delta used to derive annual probability of staying on treatment
|
|
32
|
-
// Higher efficacy → more cycles on treatment → better health
|
|
33
36
|
const efficacyDelta = Math.max(0, Math.min(0.999, params.clinical_inputs.efficacy_delta));
|
|
34
37
|
const mortalityReduction = params.clinical_inputs.mortality_reduction ?? 0;
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const mortalityEffect = Math.max(0, Math.min(0.3, mortalityReduction * 0.3));
|
|
38
|
+
// Background annual mortality rate (~2% baseline, reduced by mortalityReduction)
|
|
39
|
+
const baseMortality = 0.02;
|
|
40
|
+
const interventionMortality = Math.max(0.005, baseMortality * (1 - mortalityReduction));
|
|
41
|
+
const comparatorMortality = baseMortality;
|
|
42
|
+
// Transition: prob of staying in On-Treatment state
|
|
43
|
+
const probStayOnIntervention = Math.max(0.05, Math.min(0.93, 0.5 + efficacyDelta * 0.5));
|
|
44
|
+
const baselineProbStayOn = Math.max(0.05, Math.min(0.88, probStayOnIntervention * 0.7));
|
|
43
45
|
const states = [
|
|
44
46
|
{ name: "On-Treatment", utility: utilityOn, cost_annual: costIntervention },
|
|
45
47
|
{ name: "Off-Treatment", utility: utilityOff, cost_annual: 0 },
|
|
48
|
+
{ name: "Dead", utility: 0, cost_annual: 0 },
|
|
46
49
|
];
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
// Helper: ensure row sums to 1.0
|
|
51
|
+
function normalizeRow(row) {
|
|
52
|
+
const sum = Object.values(row).reduce((a, b) => a + b, 0);
|
|
53
|
+
if (Math.abs(sum - 1.0) < 1e-10)
|
|
54
|
+
return row;
|
|
55
|
+
const result = {};
|
|
56
|
+
for (const [k, v] of Object.entries(row)) {
|
|
57
|
+
result[k] = v / sum;
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
51
61
|
const transition_matrix_intervention = {
|
|
52
|
-
"On-Treatment": {
|
|
53
|
-
"On-Treatment":
|
|
54
|
-
"Off-Treatment":
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
"On-Treatment": normalizeRow({
|
|
63
|
+
"On-Treatment": probStayOnIntervention,
|
|
64
|
+
"Off-Treatment": 1 - probStayOnIntervention - interventionMortality,
|
|
65
|
+
Dead: interventionMortality,
|
|
66
|
+
}),
|
|
67
|
+
"Off-Treatment": normalizeRow({
|
|
57
68
|
"On-Treatment": 0.05,
|
|
58
|
-
"Off-Treatment": 0.95,
|
|
59
|
-
|
|
69
|
+
"Off-Treatment": 0.95 - interventionMortality,
|
|
70
|
+
Dead: interventionMortality,
|
|
71
|
+
}),
|
|
72
|
+
Dead: { "On-Treatment": 0, "Off-Treatment": 0, Dead: 1 },
|
|
60
73
|
};
|
|
61
74
|
const transition_matrix_comparator = {
|
|
62
|
-
"On-Treatment": {
|
|
63
|
-
"On-Treatment":
|
|
64
|
-
"Off-Treatment":
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
"On-Treatment": normalizeRow({
|
|
76
|
+
"On-Treatment": baselineProbStayOn,
|
|
77
|
+
"Off-Treatment": 1 - baselineProbStayOn - comparatorMortality,
|
|
78
|
+
Dead: comparatorMortality,
|
|
79
|
+
}),
|
|
80
|
+
"Off-Treatment": normalizeRow({
|
|
67
81
|
"On-Treatment": 0.05,
|
|
68
|
-
"Off-Treatment": 0.95,
|
|
69
|
-
|
|
82
|
+
"Off-Treatment": 0.95 - comparatorMortality,
|
|
83
|
+
Dead: comparatorMortality,
|
|
84
|
+
}),
|
|
85
|
+
Dead: { "On-Treatment": 0, "Off-Treatment": 0, Dead: 1 },
|
|
70
86
|
};
|
|
71
87
|
return {
|
|
72
88
|
states,
|
|
73
89
|
transition_matrix_intervention,
|
|
74
90
|
transition_matrix_comparator,
|
|
75
|
-
initial_cohort: { "On-Treatment": 1.0, "Off-Treatment": 0.0 },
|
|
91
|
+
initial_cohort: { "On-Treatment": 1.0, "Off-Treatment": 0.0, Dead: 0.0 },
|
|
76
92
|
cycle_length_years,
|
|
77
93
|
n_cycles,
|
|
78
94
|
discount_rate_costs: DISCOUNT_RATE,
|
|
@@ -97,10 +113,12 @@ export function runMarkovAndComputeICER(params) {
|
|
|
97
113
|
const statesIntervention = [
|
|
98
114
|
{ name: "On-Treatment", utility: utilityOn, cost_annual: costIntervention },
|
|
99
115
|
{ name: "Off-Treatment", utility: utilityOff, cost_annual: 0 },
|
|
116
|
+
{ name: "Dead", utility: 0, cost_annual: 0 },
|
|
100
117
|
];
|
|
101
118
|
const statesComparator = [
|
|
102
119
|
{ name: "On-Treatment", utility: utilityOn, cost_annual: costComparator },
|
|
103
120
|
{ name: "Off-Treatment", utility: utilityOff, cost_annual: 0 },
|
|
121
|
+
{ name: "Dead", utility: 0, cost_annual: 0 },
|
|
104
122
|
];
|
|
105
123
|
// Run intervention arm: use intervention states + intervention transition matrix.
|
|
106
124
|
// We pass intervention states in both arms' params; we only use the intervention
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelUtils.js","sourceRoot":"","sources":["../../src/models/modelUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,aAAa,GAAG,KAAK,CAAC;AAE5B,
|
|
1
|
+
{"version":3,"file":"modelUtils.js","sourceRoot":"","sources":["../../src/models/modelUtils.ts"],"names":[],"mappings":"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"}
|
package/dist/models/psa.d.ts
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
* Probabilistic Sensitivity Analysis (PSA) using Monte Carlo simulation.
|
|
3
3
|
*/
|
|
4
4
|
import type { CEModelParams } from "../providers/types.js";
|
|
5
|
+
import { type EVPPIResult } from "./evppi.js";
|
|
5
6
|
export type CEModelInputs = CEModelParams;
|
|
6
7
|
export interface PSAParams {
|
|
7
8
|
base_params: CEModelInputs;
|
|
8
9
|
n_iterations: number;
|
|
9
10
|
seed?: number;
|
|
11
|
+
/** WTP threshold for EVPI calculation. Defaults to perspective-appropriate value. */
|
|
12
|
+
evpi_lambda?: number;
|
|
10
13
|
}
|
|
11
14
|
export interface PSAIteration {
|
|
12
15
|
delta_cost: number;
|
|
@@ -24,6 +27,7 @@ export interface PSAResult {
|
|
|
24
27
|
prob_ce: number;
|
|
25
28
|
}>;
|
|
26
29
|
evpi: number;
|
|
30
|
+
evppi: EVPPIResult[];
|
|
27
31
|
scatter_sample: PSAIteration[];
|
|
28
32
|
}
|
|
29
33
|
/**
|
package/dist/models/psa.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"psa.d.ts","sourceRoot":"","sources":["../../src/models/psa.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"psa.d.ts","sourceRoot":"","sources":["../../src/models/psa.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAM3D,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC;AAE1C,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,aAAa,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,cAAc,EAAE,YAAY,EAAE,CAAC;CAChC;AAiCD;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CA8KnD"}
|
package/dist/models/psa.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { betaSample, gammaSample, createSeededRng } from "./distributions.js";
|
|
5
5
|
import { runMarkovAndComputeICER, } from "./modelUtils.js";
|
|
6
|
+
import { computeEVPPI, } from "./evppi.js";
|
|
6
7
|
const WTP_THRESHOLDS_PSA = {
|
|
7
8
|
nhs_low: 25000,
|
|
8
9
|
nhs_high: 35000,
|
|
@@ -37,6 +38,7 @@ function perturbGamma(value, cv, rng) {
|
|
|
37
38
|
export function runPSA(params) {
|
|
38
39
|
const rng = createSeededRng(params.seed ?? 42);
|
|
39
40
|
const iterations = [];
|
|
41
|
+
const evppiData = [];
|
|
40
42
|
for (let i = 0; i < params.n_iterations; i++) {
|
|
41
43
|
// Sample perturbed parameters
|
|
42
44
|
const perturbedParams = {
|
|
@@ -67,6 +69,27 @@ export function runPSA(params) {
|
|
|
67
69
|
};
|
|
68
70
|
const { delta_cost, delta_qaly, icer } = runMarkovAndComputeICER(perturbedParams);
|
|
69
71
|
iterations.push({ delta_cost, delta_qaly, icer });
|
|
72
|
+
// Track parameter values for EVPPI
|
|
73
|
+
evppiData.push({
|
|
74
|
+
delta_cost,
|
|
75
|
+
delta_qaly,
|
|
76
|
+
params: {
|
|
77
|
+
efficacy_delta: perturbedParams.clinical_inputs.efficacy_delta,
|
|
78
|
+
drug_cost_annual: perturbedParams.cost_inputs.drug_cost_annual,
|
|
79
|
+
comparator_cost_annual: perturbedParams.cost_inputs.comparator_cost_annual,
|
|
80
|
+
...(perturbedParams.clinical_inputs.mortality_reduction !== undefined
|
|
81
|
+
? {
|
|
82
|
+
mortality_reduction: perturbedParams.clinical_inputs.mortality_reduction,
|
|
83
|
+
}
|
|
84
|
+
: {}),
|
|
85
|
+
...(perturbedParams.utility_inputs
|
|
86
|
+
? {
|
|
87
|
+
qaly_on_treatment: perturbedParams.utility_inputs.qaly_on_treatment,
|
|
88
|
+
qaly_comparator: perturbedParams.utility_inputs.qaly_comparator,
|
|
89
|
+
}
|
|
90
|
+
: {}),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
70
93
|
}
|
|
71
94
|
// Compute statistics
|
|
72
95
|
const finiteICERs = iterations
|
|
@@ -96,8 +119,8 @@ export function runPSA(params) {
|
|
|
96
119
|
ceac.push({ wtp, prob_ce: n_ce / iterations.length });
|
|
97
120
|
}
|
|
98
121
|
// EVPI: E[max_arm(NMB)] - max_arm(E[NMB])
|
|
99
|
-
//
|
|
100
|
-
const lambda = 50000;
|
|
122
|
+
// Use perspective-appropriate WTP threshold (passed via evpi_lambda)
|
|
123
|
+
const lambda = params.evpi_lambda ?? 50000;
|
|
101
124
|
const nmb_intervention_mean = iterations.reduce((sum, it) => sum + (lambda * it.delta_qaly - it.delta_cost), 0) / iterations.length;
|
|
102
125
|
const nmb_comparator_mean = 0; // comparator is reference (delta = 0)
|
|
103
126
|
const e_max_nmb = iterations.reduce((sum, it) => {
|
|
@@ -106,6 +129,9 @@ export function runPSA(params) {
|
|
|
106
129
|
}, 0) / iterations.length;
|
|
107
130
|
const max_e_nmb = Math.max(nmb_intervention_mean, nmb_comparator_mean);
|
|
108
131
|
const evpi = Math.max(0, e_max_nmb - max_e_nmb);
|
|
132
|
+
// EVPPI: per-parameter partial value of information
|
|
133
|
+
const paramNames = evppiData.length > 0 ? Object.keys(evppiData[0].params) : [];
|
|
134
|
+
const evppiResults = computeEVPPI(evppiData, lambda, paramNames);
|
|
109
135
|
const scatter_sample = iterations.slice(0, 500);
|
|
110
136
|
return {
|
|
111
137
|
iterations,
|
|
@@ -115,6 +141,7 @@ export function runPSA(params) {
|
|
|
115
141
|
prob_cost_effective,
|
|
116
142
|
ceac,
|
|
117
143
|
evpi,
|
|
144
|
+
evppi: evppiResults,
|
|
118
145
|
scatter_sample,
|
|
119
146
|
};
|
|
120
147
|
}
|
package/dist/models/psa.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"psa.js","sourceRoot":"","sources":["../../src/models/psa.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAEL,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"psa.js","sourceRoot":"","sources":["../../src/models/psa.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Survival Curve Fitting — fit parametric distributions to Kaplan-Meier data.
|
|
3
|
+
*
|
|
4
|
+
* Supports: Exponential, Weibull, Log-logistic, Log-normal, Gompertz.
|
|
5
|
+
* Uses maximum likelihood estimation via Newton-Raphson optimization.
|
|
6
|
+
* Model selection via AIC/BIC.
|
|
7
|
+
*
|
|
8
|
+
* References:
|
|
9
|
+
* - Latimer NR. NICE DSU TSD 14: Survival analysis (2013)
|
|
10
|
+
* - Collett D. Modelling Survival Data in Medical Research (2015)
|
|
11
|
+
*/
|
|
12
|
+
export interface KMDataPoint {
|
|
13
|
+
time: number;
|
|
14
|
+
survival: number;
|
|
15
|
+
n_at_risk?: number;
|
|
16
|
+
n_events?: number;
|
|
17
|
+
}
|
|
18
|
+
export type DistributionName = "exponential" | "weibull" | "log_logistic" | "log_normal" | "gompertz";
|
|
19
|
+
export interface FittedDistribution {
|
|
20
|
+
name: DistributionName;
|
|
21
|
+
params: Record<string, number>;
|
|
22
|
+
aic: number;
|
|
23
|
+
bic: number;
|
|
24
|
+
log_likelihood: number;
|
|
25
|
+
survival_at: (t: number) => number;
|
|
26
|
+
hazard_at: (t: number) => number;
|
|
27
|
+
median_survival: number;
|
|
28
|
+
mean_survival_restricted: number;
|
|
29
|
+
}
|
|
30
|
+
export interface SurvivalFitResult {
|
|
31
|
+
fits: FittedDistribution[];
|
|
32
|
+
best_aic: FittedDistribution;
|
|
33
|
+
best_bic: FittedDistribution;
|
|
34
|
+
km_data: KMDataPoint[];
|
|
35
|
+
time_unit: string;
|
|
36
|
+
extrapolations: Array<{
|
|
37
|
+
time: number;
|
|
38
|
+
km_observed?: number;
|
|
39
|
+
exponential: number;
|
|
40
|
+
weibull: number;
|
|
41
|
+
log_logistic: number;
|
|
42
|
+
log_normal: number;
|
|
43
|
+
gompertz: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export declare function fitSurvivalCurves(data: KMDataPoint[], timeUnit?: string): SurvivalFitResult;
|
|
47
|
+
//# sourceMappingURL=survivalFitting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"survivalFitting.d.ts","sourceRoot":"","sources":["../../src/models/survivalFitting.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,SAAS,GACT,cAAc,GACd,YAAY,GACZ,UAAU,CAAC;AAEf,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,KAAK,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;AAyXD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EAAE,EACnB,QAAQ,GAAE,MAAiB,GAC1B,iBAAiB,CAiDnB"}
|