@erosolaraijs/cure 1.0.1 → 1.0.3

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.
Files changed (87) hide show
  1. package/dist/bin/cure.d.ts +2 -5
  2. package/dist/bin/cure.d.ts.map +1 -1
  3. package/dist/bin/cure.js +360 -129
  4. package/dist/bin/cure.js.map +1 -1
  5. package/dist/clinician/decisionSupport.d.ts +325 -0
  6. package/dist/clinician/decisionSupport.d.ts.map +1 -0
  7. package/dist/clinician/decisionSupport.js +604 -0
  8. package/dist/clinician/decisionSupport.js.map +1 -0
  9. package/dist/clinician/index.d.ts +5 -0
  10. package/dist/clinician/index.d.ts.map +1 -0
  11. package/dist/clinician/index.js +5 -0
  12. package/dist/clinician/index.js.map +1 -0
  13. package/dist/compliance/index.d.ts +5 -0
  14. package/dist/compliance/index.d.ts.map +1 -0
  15. package/dist/compliance/index.js +5 -0
  16. package/dist/compliance/index.js.map +1 -0
  17. package/dist/index.d.ts +65 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +102 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/integrations/clinicalTrials/index.d.ts +5 -0
  22. package/dist/integrations/clinicalTrials/index.d.ts.map +1 -0
  23. package/dist/integrations/clinicalTrials/index.js +5 -0
  24. package/dist/integrations/clinicalTrials/index.js.map +1 -0
  25. package/dist/integrations/ehr/index.d.ts +5 -0
  26. package/dist/integrations/ehr/index.d.ts.map +1 -0
  27. package/dist/integrations/ehr/index.js +5 -0
  28. package/dist/integrations/ehr/index.js.map +1 -0
  29. package/dist/integrations/genomics/index.d.ts +5 -0
  30. package/dist/integrations/genomics/index.d.ts.map +1 -0
  31. package/dist/integrations/genomics/index.js +5 -0
  32. package/dist/integrations/genomics/index.js.map +1 -0
  33. package/dist/ml/index.d.ts +5 -0
  34. package/dist/ml/index.d.ts.map +1 -0
  35. package/dist/ml/index.js +5 -0
  36. package/dist/ml/index.js.map +1 -0
  37. package/dist/ml/outcomePredictor.d.ts +297 -0
  38. package/dist/ml/outcomePredictor.d.ts.map +1 -0
  39. package/dist/ml/outcomePredictor.js +823 -0
  40. package/dist/ml/outcomePredictor.js.map +1 -0
  41. package/dist/orchestrator/index.d.ts +5 -0
  42. package/dist/orchestrator/index.d.ts.map +1 -0
  43. package/dist/orchestrator/index.js +5 -0
  44. package/dist/orchestrator/index.js.map +1 -0
  45. package/dist/orchestrator/realWorldOncology.d.ts +351 -0
  46. package/dist/orchestrator/realWorldOncology.d.ts.map +1 -0
  47. package/dist/orchestrator/realWorldOncology.js +425 -0
  48. package/dist/orchestrator/realWorldOncology.js.map +1 -0
  49. package/dist/patient/index.d.ts +5 -0
  50. package/dist/patient/index.d.ts.map +1 -0
  51. package/dist/patient/index.js +5 -0
  52. package/dist/patient/index.js.map +1 -0
  53. package/dist/patient/patientPortal.d.ts +337 -0
  54. package/dist/patient/patientPortal.d.ts.map +1 -0
  55. package/dist/patient/patientPortal.js +667 -0
  56. package/dist/patient/patientPortal.js.map +1 -0
  57. package/dist/safety/drugInteractions.d.ts +230 -0
  58. package/dist/safety/drugInteractions.d.ts.map +1 -0
  59. package/dist/safety/drugInteractions.js +697 -0
  60. package/dist/safety/drugInteractions.js.map +1 -0
  61. package/dist/safety/index.d.ts +5 -0
  62. package/dist/safety/index.d.ts.map +1 -0
  63. package/dist/safety/index.js +5 -0
  64. package/dist/safety/index.js.map +1 -0
  65. package/dist/validation/index.d.ts +5 -0
  66. package/dist/validation/index.d.ts.map +1 -0
  67. package/dist/validation/index.js +5 -0
  68. package/dist/validation/index.js.map +1 -0
  69. package/dist/validation/retrospectiveValidator.d.ts +246 -0
  70. package/dist/validation/retrospectiveValidator.d.ts.map +1 -0
  71. package/dist/validation/retrospectiveValidator.js +602 -0
  72. package/dist/validation/retrospectiveValidator.js.map +1 -0
  73. package/package.json +1 -1
  74. package/src/bin/cure.ts +412 -143
  75. package/src/clinician/decisionSupport.ts +949 -0
  76. package/src/clinician/index.ts +11 -0
  77. package/src/compliance/index.ts +19 -0
  78. package/src/integrations/clinicalTrials/index.ts +21 -0
  79. package/src/integrations/ehr/index.ts +32 -0
  80. package/src/integrations/genomics/index.ts +23 -0
  81. package/src/ml/index.ts +15 -0
  82. package/src/orchestrator/index.ts +11 -0
  83. package/src/orchestrator/realWorldOncology.ts +803 -0
  84. package/src/patient/index.ts +14 -0
  85. package/src/patient/patientPortal.ts +1039 -0
  86. package/src/safety/index.ts +14 -0
  87. package/src/validation/index.ts +10 -0
@@ -0,0 +1,823 @@
1
+ /**
2
+ * Machine Learning Outcome Prediction Infrastructure
3
+ *
4
+ * Provides ML-based prediction models for:
5
+ * - Treatment response prediction
6
+ * - Survival probability estimation
7
+ * - Toxicity risk assessment
8
+ * - Resistance prediction
9
+ * - Optimal therapy selection
10
+ *
11
+ * This module provides the infrastructure for training and deploying
12
+ * ML models. In production, models would be trained on real patient cohorts
13
+ * with proper validation and FDA clearance.
14
+ */
15
+ import { EventEmitter } from 'events';
16
+ import { createHash } from 'crypto';
17
+ // ═══════════════════════════════════════════════════════════════════════════════
18
+ // OUTCOME PREDICTOR SERVICE
19
+ // ═══════════════════════════════════════════════════════════════════════════════
20
+ export class OutcomePredictorService extends EventEmitter {
21
+ modelRegistry = new Map();
22
+ featureEncoders = new Map();
23
+ predictionCache = new Map();
24
+ cacheDuration = 3600000; // 1 hour
25
+ constructor() {
26
+ super();
27
+ this.initializeDefaultModels();
28
+ this.initializeFeatureEncoders();
29
+ }
30
+ /**
31
+ * Predict treatment response
32
+ */
33
+ async predictResponse(patient, treatment) {
34
+ const cacheKey = this.getCacheKey('response', patient, treatment);
35
+ const cached = this.getFromCache(cacheKey);
36
+ if (cached)
37
+ return cached;
38
+ // Get appropriate model
39
+ const model = this.getBestModel('response', patient.cancerType);
40
+ // Encode features
41
+ const features = this.encodeFeatures(patient, treatment);
42
+ // Make prediction (in production, this would call a trained model)
43
+ const prediction = this.computeResponsePrediction(features, patient, treatment, model);
44
+ this.setCache(cacheKey, prediction);
45
+ this.emit('prediction-made', { type: 'response', patient, treatment, prediction });
46
+ return prediction;
47
+ }
48
+ /**
49
+ * Predict survival outcomes
50
+ */
51
+ async predictSurvival(patient, treatment) {
52
+ const cacheKey = this.getCacheKey('survival', patient, treatment);
53
+ const cached = this.getFromCache(cacheKey);
54
+ if (cached)
55
+ return cached;
56
+ const model = this.getBestModel('pfs', patient.cancerType);
57
+ const features = this.encodeFeatures(patient, treatment);
58
+ const prediction = this.computeSurvivalPrediction(features, patient, treatment, model);
59
+ this.setCache(cacheKey, prediction);
60
+ this.emit('prediction-made', { type: 'survival', patient, treatment, prediction });
61
+ return prediction;
62
+ }
63
+ /**
64
+ * Predict toxicity risk
65
+ */
66
+ async predictToxicity(patient, treatment) {
67
+ const cacheKey = this.getCacheKey('toxicity', patient, treatment);
68
+ const cached = this.getFromCache(cacheKey);
69
+ if (cached)
70
+ return cached;
71
+ const model = this.getBestModel('toxicity', patient.cancerType);
72
+ const features = this.encodeFeatures(patient, treatment);
73
+ const prediction = this.computeToxicityPrediction(features, patient, treatment, model);
74
+ this.setCache(cacheKey, prediction);
75
+ this.emit('prediction-made', { type: 'toxicity', patient, treatment, prediction });
76
+ return prediction;
77
+ }
78
+ /**
79
+ * Predict resistance development
80
+ */
81
+ async predictResistance(patient, treatment) {
82
+ const cacheKey = this.getCacheKey('resistance', patient, treatment);
83
+ const cached = this.getFromCache(cacheKey);
84
+ if (cached)
85
+ return cached;
86
+ const model = this.getBestModel('resistance', patient.cancerType);
87
+ const features = this.encodeFeatures(patient, treatment);
88
+ const prediction = this.computeResistancePrediction(features, patient, treatment, model);
89
+ this.setCache(cacheKey, prediction);
90
+ this.emit('prediction-made', { type: 'resistance', patient, treatment, prediction });
91
+ return prediction;
92
+ }
93
+ /**
94
+ * Rank treatment options
95
+ */
96
+ async rankTherapies(patient, treatmentOptions, preferences) {
97
+ const rankings = [];
98
+ // Get predictions for each treatment option
99
+ for (const treatment of treatmentOptions) {
100
+ const [response, survival, toxicity] = await Promise.all([
101
+ this.predictResponse(patient, treatment),
102
+ this.predictSurvival(patient, treatment),
103
+ this.predictToxicity(patient, treatment)
104
+ ]);
105
+ // Calculate overall score based on preferences
106
+ const weights = {
107
+ efficacy: preferences?.prioritizeEfficacy ? 0.5 : 0.35,
108
+ toxicity: preferences?.prioritizeToxicity ? 0.3 : 0.25,
109
+ qol: preferences?.prioritizeQoL ? 0.3 : 0.2,
110
+ survival: 0.2
111
+ };
112
+ const efficacyScore = response.objectiveResponseRate;
113
+ const toxicityScore = 1 - toxicity.grade3PlusRisk;
114
+ const survivalScore = survival.pfs.twelveMonth;
115
+ const qolScore = this.estimateQoLScore(toxicity);
116
+ const overallScore = efficacyScore * weights.efficacy +
117
+ toxicityScore * weights.toxicity +
118
+ survivalScore * weights.survival +
119
+ qolScore * weights.qol;
120
+ rankings.push({
121
+ regimen: treatment.regimen,
122
+ drugs: treatment.drugs,
123
+ rank: 0, // Will be set after sorting
124
+ predictions: {
125
+ responseRate: response.objectiveResponseRate,
126
+ pfsSurvival: survival.pfs.median,
127
+ osSurvival: survival.os.median,
128
+ toxicityRisk: toxicity.grade3PlusRisk,
129
+ qualityOfLifeScore: qolScore
130
+ },
131
+ overallScore,
132
+ confidence: (response.confidence + survival.pfs.confidence) / 2,
133
+ matchingBiomarkers: this.getMatchingBiomarkers(patient, treatment),
134
+ fdaApproved: this.checkFDAApproval(treatment, patient.cancerType),
135
+ nccnRecommended: this.checkNCCNRecommendation(treatment, patient.cancerType, patient.stage),
136
+ considerations: this.getConsiderations(patient, treatment)
137
+ });
138
+ }
139
+ // Sort by overall score
140
+ rankings.sort((a, b) => b.overallScore - a.overallScore);
141
+ // Assign ranks
142
+ rankings.forEach((r, i) => r.rank = i + 1);
143
+ const bestChoice = rankings[0];
144
+ return {
145
+ therapies: rankings,
146
+ bestChoice: {
147
+ regimen: bestChoice.regimen,
148
+ rationale: [
149
+ `Highest overall score (${(bestChoice.overallScore * 100).toFixed(1)}%)`,
150
+ `Expected response rate: ${(bestChoice.predictions.responseRate * 100).toFixed(1)}%`,
151
+ `Expected median PFS: ${bestChoice.predictions.pfsSurvival.toFixed(1)} months`,
152
+ bestChoice.fdaApproved ? 'FDA approved for this indication' : '',
153
+ bestChoice.nccnRecommended ? 'NCCN recommended' : '',
154
+ ...bestChoice.matchingBiomarkers.map(b => `Matches biomarker: ${b}`)
155
+ ].filter(Boolean)
156
+ }
157
+ };
158
+ }
159
+ /**
160
+ * Get comprehensive prediction report
161
+ */
162
+ async getComprehensivePrediction(patient, treatment) {
163
+ const [response, survival, toxicity, resistance] = await Promise.all([
164
+ this.predictResponse(patient, treatment),
165
+ this.predictSurvival(patient, treatment),
166
+ this.predictToxicity(patient, treatment),
167
+ this.predictResistance(patient, treatment)
168
+ ]);
169
+ // Generate overall assessment
170
+ const overallAssessment = this.generateOverallAssessment(patient, treatment, response, survival, toxicity, resistance);
171
+ return {
172
+ response,
173
+ survival,
174
+ toxicity,
175
+ resistance,
176
+ overallAssessment
177
+ };
178
+ }
179
+ // ═══════════════════════════════════════════════════════════════════════════════
180
+ // PREDICTION COMPUTATION (Placeholder implementations)
181
+ // In production, these would use trained ML models
182
+ // ═══════════════════════════════════════════════════════════════════════════════
183
+ computeResponsePrediction(features, patient, treatment, model) {
184
+ // Base probabilities based on treatment type and cancer
185
+ let baseCR = 0.15;
186
+ let basePR = 0.30;
187
+ let baseSD = 0.30;
188
+ // Adjust based on biomarkers
189
+ if (this.hasBiomarkerMatch(patient, treatment)) {
190
+ baseCR += 0.15;
191
+ basePR += 0.15;
192
+ }
193
+ // Adjust based on stage
194
+ const stageModifiers = {
195
+ 'I': 1.3, 'IA': 1.3, 'IB': 1.25,
196
+ 'II': 1.15, 'IIA': 1.15, 'IIB': 1.1,
197
+ 'III': 0.9, 'IIIA': 0.95, 'IIIB': 0.85, 'IIIC': 0.8,
198
+ 'IV': 0.7, 'IVA': 0.75, 'IVB': 0.65
199
+ };
200
+ const stageMod = stageModifiers[patient.stage] || 1.0;
201
+ // Adjust based on ECOG
202
+ const ecogModifiers = [1.0, 0.9, 0.75, 0.5, 0.25];
203
+ const ecogMod = ecogModifiers[patient.ecogStatus];
204
+ // Adjust based on treatment setting
205
+ const settingModifiers = {
206
+ 'first-line': 1.0,
207
+ 'second-line': 0.75,
208
+ 'third-line-plus': 0.5,
209
+ 'neoadjuvant': 1.1,
210
+ 'adjuvant': 1.05,
211
+ 'maintenance': 0.9
212
+ };
213
+ const settingMod = settingModifiers[treatment.setting] || 1.0;
214
+ // Apply modifiers
215
+ const modifier = stageMod * ecogMod * settingMod;
216
+ const crProb = Math.min(baseCR * modifier, 0.6);
217
+ const prProb = Math.min(basePR * modifier, 0.5);
218
+ const sdProb = Math.min(baseSD * modifier, 0.4);
219
+ const pdProb = Math.max(1 - crProb - prProb - sdProb, 0.05);
220
+ // Normalize
221
+ const total = crProb + prProb + sdProb + pdProb;
222
+ const probabilities = {
223
+ completeResponse: crProb / total,
224
+ partialResponse: prProb / total,
225
+ stableDisease: sdProb / total,
226
+ progressiveDisease: pdProb / total
227
+ };
228
+ // Determine best response
229
+ const maxProb = Math.max(...Object.values(probabilities));
230
+ let predictedResponse = 'SD';
231
+ if (probabilities.completeResponse === maxProb)
232
+ predictedResponse = 'CR';
233
+ else if (probabilities.partialResponse === maxProb)
234
+ predictedResponse = 'PR';
235
+ else if (probabilities.progressiveDisease === maxProb)
236
+ predictedResponse = 'PD';
237
+ return {
238
+ predictedResponse,
239
+ probabilities,
240
+ confidence: model ? model.performance.auc || 0.75 : 0.70,
241
+ objectiveResponseRate: probabilities.completeResponse + probabilities.partialResponse,
242
+ diseaseControlRate: probabilities.completeResponse + probabilities.partialResponse + probabilities.stableDisease,
243
+ timeToResponse: {
244
+ median: 8,
245
+ range: [4, 16]
246
+ }
247
+ };
248
+ }
249
+ computeSurvivalPrediction(features, patient, treatment, model) {
250
+ // Base survival estimates (in months)
251
+ let basePFS = 12;
252
+ let baseOS = 24;
253
+ // Cancer type adjustments
254
+ const cancerPFSModifiers = {
255
+ 'NSCLC': 1.0, 'SCLC': 0.5, 'Breast': 1.5, 'Colorectal': 1.0,
256
+ 'Melanoma': 1.2, 'RCC': 1.3, 'Ovarian': 0.8, 'Pancreatic': 0.4,
257
+ 'Glioblastoma': 0.3, 'AML': 0.6, 'Multiple Myeloma': 1.5
258
+ };
259
+ const cancerMod = cancerPFSModifiers[patient.cancerType] || 1.0;
260
+ basePFS *= cancerMod;
261
+ baseOS *= cancerMod;
262
+ // Stage adjustments
263
+ const stageModifiers = {
264
+ 'I': 3.0, 'IA': 3.5, 'IB': 2.8,
265
+ 'II': 2.0, 'IIA': 2.2, 'IIB': 1.8,
266
+ 'III': 1.0, 'IIIA': 1.2, 'IIIB': 0.9, 'IIIC': 0.7,
267
+ 'IV': 0.5, 'IVA': 0.55, 'IVB': 0.4
268
+ };
269
+ const stageMod = stageModifiers[patient.stage] || 1.0;
270
+ basePFS *= stageMod;
271
+ baseOS *= stageMod;
272
+ // Biomarker-driven therapy boost
273
+ if (this.hasBiomarkerMatch(patient, treatment)) {
274
+ basePFS *= 1.5;
275
+ baseOS *= 1.3;
276
+ }
277
+ // ECOG adjustment
278
+ const ecogMultipliers = [1.0, 0.85, 0.65, 0.4, 0.2];
279
+ basePFS *= ecogMultipliers[patient.ecogStatus];
280
+ baseOS *= ecogMultipliers[patient.ecogStatus];
281
+ // Calculate probabilities using exponential survival model
282
+ const lambda_pfs = 1 / basePFS;
283
+ const lambda_os = 1 / baseOS;
284
+ const pfs = {
285
+ median: basePFS,
286
+ sixMonth: Math.exp(-lambda_pfs * 6),
287
+ twelveMonth: Math.exp(-lambda_pfs * 12),
288
+ twentyFourMonth: Math.exp(-lambda_pfs * 24),
289
+ confidence: model ? model.performance.cIndex || 0.72 : 0.68
290
+ };
291
+ const os = {
292
+ median: baseOS,
293
+ twelveMonth: Math.exp(-lambda_os * 12),
294
+ twentyFourMonth: Math.exp(-lambda_os * 24),
295
+ fiveYear: Math.exp(-lambda_os * 60),
296
+ confidence: model ? model.performance.cIndex || 0.72 : 0.68
297
+ };
298
+ // Calculate risk score (0-100)
299
+ const riskFactors = [
300
+ patient.stage.startsWith('IV') ? 25 : patient.stage.startsWith('III') ? 15 : 5,
301
+ patient.ecogStatus >= 2 ? 20 : patient.ecogStatus === 1 ? 10 : 0,
302
+ (patient.priorLines || 0) >= 2 ? 15 : (patient.priorLines || 0) >= 1 ? 8 : 0,
303
+ patient.ldh && patient.ldh > 250 ? 10 : 0,
304
+ patient.metastaticSites && patient.metastaticSites.length > 2 ? 15 : 0,
305
+ patient.age > 75 ? 10 : patient.age > 65 ? 5 : 0
306
+ ];
307
+ const riskScore = Math.min(riskFactors.reduce((a, b) => a + b, 0), 100);
308
+ let riskGroup;
309
+ if (riskScore < 25)
310
+ riskGroup = 'low';
311
+ else if (riskScore < 50)
312
+ riskGroup = 'intermediate-low';
313
+ else if (riskScore < 75)
314
+ riskGroup = 'intermediate-high';
315
+ else
316
+ riskGroup = 'high';
317
+ return { pfs, os, riskGroup, riskScore };
318
+ }
319
+ computeToxicityPrediction(features, patient, treatment, model) {
320
+ const specificRisks = [];
321
+ let grade3PlusRisk = 0;
322
+ // Define toxicity profiles by drug class
323
+ const drugToxicities = this.getDrugToxicityProfiles(treatment.drugs);
324
+ for (const tox of drugToxicities) {
325
+ // Adjust risk based on patient factors
326
+ let adjustedRisk = tox.baseRisk;
327
+ // Age adjustment
328
+ if (patient.age > 70)
329
+ adjustedRisk *= 1.2;
330
+ if (patient.age > 80)
331
+ adjustedRisk *= 1.4;
332
+ // Organ function adjustment
333
+ if (tox.affectedOrgan === 'hepatic' && patient.organFunction?.hepatic === 'impaired') {
334
+ adjustedRisk *= 1.5;
335
+ }
336
+ if (tox.affectedOrgan === 'renal' && patient.organFunction?.renal === 'impaired') {
337
+ adjustedRisk *= 1.5;
338
+ }
339
+ if (tox.affectedOrgan === 'cardiac' && patient.organFunction?.cardiac === 'impaired') {
340
+ adjustedRisk *= 1.5;
341
+ }
342
+ // ECOG adjustment
343
+ adjustedRisk *= (1 + patient.ecogStatus * 0.1);
344
+ adjustedRisk = Math.min(adjustedRisk, 0.95);
345
+ specificRisks.push({
346
+ toxicity: tox.name,
347
+ grade: tox.typicalGrade,
348
+ probability: adjustedRisk,
349
+ timeToOnset: tox.timeToOnset,
350
+ reversible: tox.reversible,
351
+ management: tox.management
352
+ });
353
+ if (tox.typicalGrade >= 3) {
354
+ grade3PlusRisk += adjustedRisk * 0.3; // Weighted contribution
355
+ }
356
+ }
357
+ grade3PlusRisk = Math.min(grade3PlusRisk, 0.9);
358
+ // Immunotherapy-specific irAE prediction
359
+ let iraeRisk;
360
+ if (treatment.treatmentType === 'immunotherapy' || this.hasImmunotherapyDrug(treatment.drugs)) {
361
+ iraeRisk = {
362
+ any: 0.60,
363
+ grade3Plus: 0.15,
364
+ specificOrgans: [
365
+ { organ: 'skin', risk: 0.35 },
366
+ { organ: 'GI', risk: 0.20 },
367
+ { organ: 'endocrine', risk: 0.15 },
368
+ { organ: 'hepatic', risk: 0.10 },
369
+ { organ: 'pulmonary', risk: 0.08 }
370
+ ]
371
+ };
372
+ }
373
+ // Dose modification recommendation
374
+ let doseModification;
375
+ if (patient.age > 75 || patient.ecogStatus >= 2 || patient.organFunction?.renal === 'impaired') {
376
+ doseModification = {
377
+ recommended: true,
378
+ reduction: 20,
379
+ reason: patient.age > 75 ? 'Advanced age' :
380
+ patient.ecogStatus >= 2 ? 'Poor performance status' :
381
+ 'Impaired organ function'
382
+ };
383
+ }
384
+ return {
385
+ overallRisk: grade3PlusRisk > 0.4 ? 'high' : grade3PlusRisk > 0.2 ? 'moderate' : 'low',
386
+ grade3PlusRisk,
387
+ specificRisks,
388
+ iraeRisk,
389
+ doseModification
390
+ };
391
+ }
392
+ computeResistancePrediction(features, patient, treatment, model) {
393
+ let intrinsicRisk = 0.2;
394
+ let acquiredRisk = 0.6;
395
+ // Adjust based on prior treatments
396
+ if (patient.priorLines && patient.priorLines > 0) {
397
+ intrinsicRisk += 0.1 * patient.priorLines;
398
+ acquiredRisk += 0.05 * patient.priorLines;
399
+ }
400
+ // Adjust based on prior response
401
+ if (patient.priorResponse === 'PD') {
402
+ intrinsicRisk += 0.2;
403
+ }
404
+ else if (patient.priorResponse === 'CR') {
405
+ intrinsicRisk -= 0.1;
406
+ }
407
+ // Get resistance mechanisms based on treatment and mutations
408
+ const mechanisms = this.getResistanceMechanisms(patient, treatment);
409
+ // Calculate time to resistance
410
+ const baseTimeToResistance = this.hasBiomarkerMatch(patient, treatment) ? 18 : 9;
411
+ const timeModifier = patient.priorLines ? 1 - (patient.priorLines * 0.15) : 1;
412
+ // Get next line options
413
+ const nextLineOptions = this.getNextLineOptions(patient, treatment, mechanisms);
414
+ return {
415
+ intrinsicResistanceRisk: Math.min(intrinsicRisk, 0.9),
416
+ acquiredResistanceRisk: Math.min(acquiredRisk, 0.95),
417
+ predictedMechanisms: mechanisms,
418
+ timeToResistance: {
419
+ median: baseTimeToResistance * timeModifier,
420
+ range: [baseTimeToResistance * timeModifier * 0.5, baseTimeToResistance * timeModifier * 2]
421
+ },
422
+ nextLineOptions
423
+ };
424
+ }
425
+ // ═══════════════════════════════════════════════════════════════════════════════
426
+ // HELPER METHODS
427
+ // ═══════════════════════════════════════════════════════════════════════════════
428
+ initializeDefaultModels() {
429
+ // Register placeholder models
430
+ const defaultModels = [
431
+ {
432
+ id: 'response-nsclc-v1',
433
+ name: 'NSCLC Response Predictor',
434
+ version: '1.0',
435
+ type: 'classification',
436
+ target: 'response',
437
+ cancerTypes: ['NSCLC'],
438
+ performance: { auc: 0.78, accuracy: 0.72 },
439
+ validation: { method: 'cross-validation', cohortSize: 1500, testSetSize: 300, validationDate: new Date() },
440
+ features: ['age', 'stage', 'ecog', 'pdl1', 'tmb', 'egfr', 'alk', 'kras'],
441
+ importantFeatures: [
442
+ { feature: 'pdl1', importance: 0.25 },
443
+ { feature: 'tmb', importance: 0.20 },
444
+ { feature: 'egfr', importance: 0.18 }
445
+ ],
446
+ status: 'validation'
447
+ },
448
+ {
449
+ id: 'survival-pan-cancer-v1',
450
+ name: 'Pan-Cancer Survival Model',
451
+ version: '1.0',
452
+ type: 'survival',
453
+ target: 'os',
454
+ cancerTypes: ['all'],
455
+ performance: { cIndex: 0.72 },
456
+ validation: { method: 'cross-validation', cohortSize: 10000, testSetSize: 2000, validationDate: new Date() },
457
+ features: ['age', 'stage', 'ecog', 'cancerType', 'priorLines', 'ldh'],
458
+ importantFeatures: [
459
+ { feature: 'stage', importance: 0.30 },
460
+ { feature: 'ecog', importance: 0.25 },
461
+ { feature: 'priorLines', importance: 0.15 }
462
+ ],
463
+ status: 'validation'
464
+ }
465
+ ];
466
+ for (const model of defaultModels) {
467
+ this.modelRegistry.set(model.id, model);
468
+ }
469
+ }
470
+ initializeFeatureEncoders() {
471
+ // Initialize encoders for categorical variables
472
+ this.featureEncoders.set('stage', new FeatureEncoder({
473
+ 'I': 1, 'IA': 1, 'IB': 1.5,
474
+ 'II': 2, 'IIA': 2, 'IIB': 2.5,
475
+ 'III': 3, 'IIIA': 3, 'IIIB': 3.5, 'IIIC': 3.8,
476
+ 'IV': 4, 'IVA': 4, 'IVB': 4.5
477
+ }));
478
+ this.featureEncoders.set('cancerType', new FeatureEncoder({
479
+ 'NSCLC': 1, 'SCLC': 2, 'Breast': 3, 'Colorectal': 4,
480
+ 'Melanoma': 5, 'RCC': 6, 'Ovarian': 7, 'Pancreatic': 8
481
+ }));
482
+ }
483
+ encodeFeatures(patient, treatment) {
484
+ const features = [];
485
+ // Numeric features (normalized)
486
+ features.push(patient.age / 100);
487
+ features.push(patient.ecogStatus / 4);
488
+ features.push(patient.gender === 'male' ? 1 : 0);
489
+ // Encoded categorical features
490
+ const stageEncoder = this.featureEncoders.get('stage');
491
+ features.push((stageEncoder?.encode(patient.stage) || 2) / 5);
492
+ // Biomarker features
493
+ features.push((patient.pdl1Score || 0) / 100);
494
+ features.push((patient.tmbValue || 0) / 50);
495
+ features.push(patient.msiStatus === 'MSI-H' ? 1 : 0);
496
+ features.push(patient.hrdStatus === 'positive' ? 1 : 0);
497
+ // Prior treatment features
498
+ features.push((patient.priorLines || 0) / 5);
499
+ // Treatment features
500
+ features.push(treatment.treatmentType === 'immunotherapy' ? 1 : 0);
501
+ features.push(treatment.treatmentType === 'targeted' ? 1 : 0);
502
+ return features;
503
+ }
504
+ getBestModel(target, cancerType) {
505
+ const models = Array.from(this.modelRegistry.values())
506
+ .filter(m => m.target === target &&
507
+ (m.cancerTypes.includes(cancerType) || m.cancerTypes.includes('all')) &&
508
+ m.status !== 'deprecated')
509
+ .sort((a, b) => (b.performance.auc || b.performance.cIndex || 0) - (a.performance.auc || a.performance.cIndex || 0));
510
+ return models[0];
511
+ }
512
+ hasBiomarkerMatch(patient, treatment) {
513
+ // Check for biomarker-drug matches
514
+ const matches = [
515
+ { biomarker: 'EGFR', drugs: ['osimertinib', 'erlotinib', 'gefitinib', 'afatinib'] },
516
+ { biomarker: 'ALK', drugs: ['alectinib', 'crizotinib', 'brigatinib', 'lorlatinib'] },
517
+ { biomarker: 'BRAF V600', drugs: ['dabrafenib', 'vemurafenib', 'encorafenib'] },
518
+ { biomarker: 'HER2', drugs: ['trastuzumab', 'pertuzumab', 't-dxd'] },
519
+ { biomarker: 'BRCA', drugs: ['olaparib', 'rucaparib', 'niraparib', 'talazoparib'] },
520
+ { biomarker: 'KRAS G12C', drugs: ['sotorasib', 'adagrasib'] }
521
+ ];
522
+ for (const match of matches) {
523
+ const hasBiomarker = patient.genomicAlterations.some(g => g.gene.toUpperCase().includes(match.biomarker) ||
524
+ g.alteration.toUpperCase().includes(match.biomarker));
525
+ const hasDrug = treatment.drugs.some(d => match.drugs.some(md => d.toLowerCase().includes(md)));
526
+ if (hasBiomarker && hasDrug)
527
+ return true;
528
+ }
529
+ // Check immunotherapy eligibility
530
+ if (treatment.treatmentType === 'immunotherapy' || this.hasImmunotherapyDrug(treatment.drugs)) {
531
+ if (patient.msiStatus === 'MSI-H')
532
+ return true;
533
+ if (patient.tmbStatus === 'high')
534
+ return true;
535
+ if (patient.pdl1Score && patient.pdl1Score >= 50)
536
+ return true;
537
+ }
538
+ return false;
539
+ }
540
+ hasImmunotherapyDrug(drugs) {
541
+ const immunoDrugs = ['pembrolizumab', 'nivolumab', 'ipilimumab', 'atezolizumab', 'durvalumab', 'avelumab'];
542
+ return drugs.some(d => immunoDrugs.some(id => d.toLowerCase().includes(id)));
543
+ }
544
+ getDrugToxicityProfiles(drugs) {
545
+ const profiles = [];
546
+ // Check for common drug classes and their toxicities
547
+ for (const drug of drugs) {
548
+ const lower = drug.toLowerCase();
549
+ // Checkpoint inhibitors
550
+ if (['pembrolizumab', 'nivolumab', 'ipilimumab', 'atezolizumab'].some(d => lower.includes(d))) {
551
+ profiles.push({ name: 'Immune-related dermatitis', baseRisk: 0.35, typicalGrade: 2, affectedOrgan: 'skin', reversible: true }, { name: 'Immune-related colitis', baseRisk: 0.15, typicalGrade: 3, affectedOrgan: 'GI', reversible: true, management: 'Corticosteroids' }, { name: 'Immune-related pneumonitis', baseRisk: 0.05, typicalGrade: 3, affectedOrgan: 'pulmonary', reversible: true, management: 'Hold treatment, corticosteroids' }, { name: 'Immune-related hepatitis', baseRisk: 0.08, typicalGrade: 3, affectedOrgan: 'hepatic', reversible: true }, { name: 'Immune-related thyroiditis', baseRisk: 0.15, typicalGrade: 2, affectedOrgan: 'endocrine', reversible: false });
552
+ }
553
+ // Platinum agents
554
+ if (['carboplatin', 'cisplatin', 'oxaliplatin'].some(d => lower.includes(d))) {
555
+ profiles.push({ name: 'Nausea/Vomiting', baseRisk: 0.60, typicalGrade: 2, affectedOrgan: 'GI', reversible: true }, { name: 'Nephrotoxicity', baseRisk: lower.includes('cisplatin') ? 0.25 : 0.08, typicalGrade: 2, affectedOrgan: 'renal', reversible: true }, { name: 'Myelosuppression', baseRisk: 0.40, typicalGrade: 3, affectedOrgan: 'hematologic', reversible: true }, { name: 'Peripheral neuropathy', baseRisk: 0.30, typicalGrade: 2, affectedOrgan: 'neurologic', reversible: false });
556
+ }
557
+ // EGFR TKIs
558
+ if (['osimertinib', 'erlotinib', 'gefitinib', 'afatinib'].some(d => lower.includes(d))) {
559
+ profiles.push({ name: 'Rash', baseRisk: 0.45, typicalGrade: 2, affectedOrgan: 'skin', reversible: true }, { name: 'Diarrhea', baseRisk: 0.50, typicalGrade: 2, affectedOrgan: 'GI', reversible: true }, { name: 'Interstitial lung disease', baseRisk: 0.03, typicalGrade: 4, affectedOrgan: 'pulmonary', reversible: false });
560
+ }
561
+ // CDK4/6 inhibitors
562
+ if (['palbociclib', 'ribociclib', 'abemaciclib'].some(d => lower.includes(d))) {
563
+ profiles.push({ name: 'Neutropenia', baseRisk: 0.70, typicalGrade: 3, affectedOrgan: 'hematologic', reversible: true }, { name: 'Fatigue', baseRisk: 0.40, typicalGrade: 2, reversible: true }, { name: 'Diarrhea', baseRisk: lower.includes('abemaciclib') ? 0.80 : 0.20, typicalGrade: 2, affectedOrgan: 'GI', reversible: true });
564
+ }
565
+ }
566
+ return profiles;
567
+ }
568
+ getResistanceMechanisms(patient, treatment) {
569
+ const mechanisms = [];
570
+ // EGFR TKI resistance mechanisms
571
+ if (treatment.drugs.some(d => ['osimertinib', 'erlotinib', 'gefitinib'].some(e => d.toLowerCase().includes(e)))) {
572
+ mechanisms.push({ mechanism: 'MET amplification', probability: 0.20, monitoringBiomarker: 'MET FISH/NGS' }, { mechanism: 'EGFR C797S mutation', probability: 0.15, monitoringBiomarker: 'ctDNA EGFR' }, { mechanism: 'Histologic transformation (SCLC)', probability: 0.05, monitoringBiomarker: 'Tissue biopsy' }, { mechanism: 'HER2 amplification', probability: 0.10, monitoringBiomarker: 'HER2 NGS/FISH' });
573
+ }
574
+ // Immunotherapy resistance
575
+ if (this.hasImmunotherapyDrug(treatment.drugs)) {
576
+ mechanisms.push({ mechanism: 'Beta-2 microglobulin loss', probability: 0.10, monitoringBiomarker: 'B2M NGS' }, { mechanism: 'JAK1/2 mutations', probability: 0.08, monitoringBiomarker: 'JAK1/2 NGS' }, { mechanism: 'Immunosuppressive TME', probability: 0.25, monitoringBiomarker: 'Tissue biopsy + IHC' }, { mechanism: 'Antigen loss', probability: 0.15 });
577
+ }
578
+ // BRAF inhibitor resistance
579
+ if (treatment.drugs.some(d => ['dabrafenib', 'vemurafenib', 'encorafenib'].some(b => d.toLowerCase().includes(b)))) {
580
+ mechanisms.push({ mechanism: 'MAPK reactivation', probability: 0.30, monitoringBiomarker: 'NRAS/MEK NGS' }, { mechanism: 'BRAF amplification', probability: 0.15, monitoringBiomarker: 'BRAF CNV' }, { mechanism: 'RTK bypass (EGFR, MET)', probability: 0.20, monitoringBiomarker: 'Comprehensive NGS' });
581
+ }
582
+ return mechanisms;
583
+ }
584
+ getNextLineOptions(patient, treatment, mechanisms) {
585
+ const options = [];
586
+ // Based on predicted resistance mechanisms
587
+ for (const mech of mechanisms.slice(0, 3)) {
588
+ if (mech.mechanism === 'MET amplification') {
589
+ options.push({
590
+ therapy: 'Osimertinib + Savolitinib',
591
+ rationale: 'Targets MET bypass pathway',
592
+ expectedBenefit: 8
593
+ });
594
+ }
595
+ if (mech.mechanism === 'EGFR C797S mutation') {
596
+ options.push({
597
+ therapy: 'First-generation EGFR TKI + Third-generation EGFR TKI',
598
+ rationale: 'C797S may restore sensitivity to 1st-gen TKI',
599
+ expectedBenefit: 6
600
+ });
601
+ }
602
+ if (mech.mechanism === 'Histologic transformation (SCLC)') {
603
+ options.push({
604
+ therapy: 'Platinum-etoposide chemotherapy',
605
+ rationale: 'Standard SCLC treatment',
606
+ expectedBenefit: 4
607
+ });
608
+ }
609
+ }
610
+ // Add general next-line options
611
+ if (treatment.treatmentType !== 'immunotherapy' && !this.hasImmunotherapyDrug(treatment.drugs)) {
612
+ options.push({
613
+ therapy: 'Immunotherapy (if PD-L1+/TMB-H)',
614
+ rationale: 'Different mechanism of action',
615
+ expectedBenefit: 10
616
+ });
617
+ }
618
+ // Clinical trial option
619
+ options.push({
620
+ therapy: 'Clinical trial enrollment',
621
+ rationale: 'Access to novel agents',
622
+ expectedBenefit: 8
623
+ });
624
+ return options;
625
+ }
626
+ estimateQoLScore(toxicity) {
627
+ let score = 0.85; // Base quality of life
628
+ // Reduce based on toxicity severity
629
+ score -= toxicity.grade3PlusRisk * 0.3;
630
+ // Specific high-impact toxicities
631
+ for (const tox of toxicity.specificRisks) {
632
+ if (tox.toxicity.toLowerCase().includes('neuropathy') && tox.grade >= 2) {
633
+ score -= 0.1;
634
+ }
635
+ if (tox.toxicity.toLowerCase().includes('fatigue') && tox.grade >= 2) {
636
+ score -= 0.08;
637
+ }
638
+ if (tox.toxicity.toLowerCase().includes('nausea') && tox.grade >= 2) {
639
+ score -= 0.05;
640
+ }
641
+ }
642
+ return Math.max(score, 0.3);
643
+ }
644
+ getMatchingBiomarkers(patient, treatment) {
645
+ const matches = [];
646
+ for (const alt of patient.genomicAlterations) {
647
+ if (this.isBiomarkerRelevant(alt.gene, alt.alteration, treatment)) {
648
+ matches.push(`${alt.gene} ${alt.alteration}`);
649
+ }
650
+ }
651
+ if (patient.msiStatus === 'MSI-H' && this.hasImmunotherapyDrug(treatment.drugs)) {
652
+ matches.push('MSI-H');
653
+ }
654
+ if (patient.tmbStatus === 'high' && this.hasImmunotherapyDrug(treatment.drugs)) {
655
+ matches.push('TMB-High');
656
+ }
657
+ if (patient.hrdStatus === 'positive' && treatment.drugs.some(d => ['olaparib', 'rucaparib', 'niraparib', 'talazoparib'].some(p => d.toLowerCase().includes(p)))) {
658
+ matches.push('HRD-positive');
659
+ }
660
+ return matches;
661
+ }
662
+ isBiomarkerRelevant(gene, alteration, treatment) {
663
+ const relevantPairs = {
664
+ 'EGFR': ['osimertinib', 'erlotinib', 'gefitinib', 'afatinib'],
665
+ 'ALK': ['alectinib', 'crizotinib', 'brigatinib', 'lorlatinib'],
666
+ 'ROS1': ['crizotinib', 'entrectinib'],
667
+ 'BRAF': ['dabrafenib', 'vemurafenib', 'encorafenib'],
668
+ 'HER2': ['trastuzumab', 'pertuzumab', 't-dxd'],
669
+ 'BRCA': ['olaparib', 'rucaparib', 'niraparib', 'talazoparib'],
670
+ 'KRAS': ['sotorasib', 'adagrasib'],
671
+ 'NTRK': ['larotrectinib', 'entrectinib'],
672
+ 'RET': ['selpercatinib', 'pralsetinib'],
673
+ 'MET': ['capmatinib', 'tepotinib']
674
+ };
675
+ const drugs = relevantPairs[gene.toUpperCase()];
676
+ if (!drugs)
677
+ return false;
678
+ return treatment.drugs.some(d => drugs.some(rd => d.toLowerCase().includes(rd)));
679
+ }
680
+ checkFDAApproval(treatment, cancerType) {
681
+ // Simplified FDA approval check - would reference actual database in production
682
+ const approvedCombinations = {
683
+ 'NSCLC': ['osimertinib', 'pembrolizumab', 'alectinib', 'sotorasib'],
684
+ 'Breast': ['palbociclib', 'trastuzumab', 'olaparib', 'sacituzumab'],
685
+ 'Melanoma': ['pembrolizumab', 'nivolumab', 'ipilimumab', 'dabrafenib'],
686
+ 'Colorectal': ['pembrolizumab', 'cetuximab', 'bevacizumab'],
687
+ 'RCC': ['pembrolizumab', 'nivolumab', 'cabozantinib']
688
+ };
689
+ const approvedDrugs = approvedCombinations[cancerType] || [];
690
+ return treatment.drugs.some(d => approvedDrugs.some(ad => d.toLowerCase().includes(ad)));
691
+ }
692
+ checkNCCNRecommendation(treatment, cancerType, stage) {
693
+ // Simplified NCCN check - would reference actual guidelines in production
694
+ return this.checkFDAApproval(treatment, cancerType);
695
+ }
696
+ getConsiderations(patient, treatment) {
697
+ const considerations = [];
698
+ if (patient.age > 75) {
699
+ considerations.push('Consider dose reduction for elderly patient');
700
+ }
701
+ if (patient.ecogStatus >= 2) {
702
+ considerations.push('Poor performance status may limit tolerability');
703
+ }
704
+ if (patient.organFunction?.renal === 'impaired') {
705
+ considerations.push('Dose adjustment may be needed for renal impairment');
706
+ }
707
+ if (patient.priorLines && patient.priorLines >= 2) {
708
+ considerations.push('Heavily pretreated - consider clinical trial');
709
+ }
710
+ return considerations;
711
+ }
712
+ generateOverallAssessment(patient, treatment, response, survival, toxicity, resistance) {
713
+ const rationale = [];
714
+ const caveats = [];
715
+ const alternativeOptions = [];
716
+ // Calculate recommendation score
717
+ let score = 50;
718
+ // Response contribution
719
+ if (response.objectiveResponseRate >= 0.6) {
720
+ score += 20;
721
+ rationale.push('High expected response rate');
722
+ }
723
+ else if (response.objectiveResponseRate >= 0.4) {
724
+ score += 10;
725
+ }
726
+ else if (response.objectiveResponseRate < 0.2) {
727
+ score -= 20;
728
+ caveats.push('Low expected response rate');
729
+ }
730
+ // Survival contribution
731
+ if (survival.pfs.twelveMonth >= 0.6) {
732
+ score += 15;
733
+ rationale.push('Favorable survival outlook');
734
+ }
735
+ else if (survival.pfs.twelveMonth < 0.3) {
736
+ score -= 15;
737
+ caveats.push('Limited expected duration of benefit');
738
+ }
739
+ // Toxicity contribution
740
+ if (toxicity.grade3PlusRisk < 0.2) {
741
+ score += 10;
742
+ rationale.push('Favorable toxicity profile');
743
+ }
744
+ else if (toxicity.grade3PlusRisk > 0.5) {
745
+ score -= 20;
746
+ caveats.push('High risk of severe toxicity');
747
+ }
748
+ // Biomarker match
749
+ if (this.hasBiomarkerMatch(patient, treatment)) {
750
+ score += 15;
751
+ rationale.push('Biomarker-matched therapy');
752
+ }
753
+ // FDA approval and guidelines
754
+ if (this.checkFDAApproval(treatment, patient.cancerType)) {
755
+ score += 10;
756
+ rationale.push('FDA approved for indication');
757
+ }
758
+ // Add alternatives
759
+ if (resistance.nextLineOptions.length > 0) {
760
+ alternativeOptions.push(...resistance.nextLineOptions.slice(0, 2).map(o => o.therapy));
761
+ }
762
+ // Determine recommendation
763
+ let recommendation;
764
+ if (score >= 80)
765
+ recommendation = 'strongly-recommended';
766
+ else if (score >= 60)
767
+ recommendation = 'recommended';
768
+ else if (score >= 40)
769
+ recommendation = 'consider';
770
+ else if (score >= 20)
771
+ recommendation = 'caution';
772
+ else
773
+ recommendation = 'not-recommended';
774
+ return { recommendation, rationale, caveats, alternativeOptions };
775
+ }
776
+ getCacheKey(type, patient, treatment) {
777
+ const data = JSON.stringify({ type, patient, treatment });
778
+ return createHash('sha256').update(data).digest('hex').substring(0, 16);
779
+ }
780
+ getFromCache(key) {
781
+ const cached = this.predictionCache.get(key);
782
+ if (cached && Date.now() - cached.timestamp.getTime() < this.cacheDuration) {
783
+ return cached.prediction;
784
+ }
785
+ return null;
786
+ }
787
+ setCache(key, prediction) {
788
+ this.predictionCache.set(key, { prediction, timestamp: new Date() });
789
+ }
790
+ /**
791
+ * Register a new model
792
+ */
793
+ registerModel(model) {
794
+ this.modelRegistry.set(model.id, model);
795
+ this.emit('model-registered', model);
796
+ }
797
+ /**
798
+ * Get model performance metrics
799
+ */
800
+ getModelMetrics(modelId) {
801
+ return this.modelRegistry.get(modelId)?.performance;
802
+ }
803
+ /**
804
+ * List all available models
805
+ */
806
+ listModels() {
807
+ return Array.from(this.modelRegistry.values());
808
+ }
809
+ }
810
+ // ═══════════════════════════════════════════════════════════════════════════════
811
+ // FEATURE ENCODER
812
+ // ═══════════════════════════════════════════════════════════════════════════════
813
+ class FeatureEncoder {
814
+ mapping;
815
+ constructor(mapping) {
816
+ this.mapping = mapping;
817
+ }
818
+ encode(value) {
819
+ return this.mapping[value] ?? 0;
820
+ }
821
+ }
822
+ export default OutcomePredictorService;
823
+ //# sourceMappingURL=outcomePredictor.js.map