@erosolaraijs/cure 1.0.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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -0
  3. package/dist/bin/cure.d.ts +10 -0
  4. package/dist/bin/cure.d.ts.map +1 -0
  5. package/dist/bin/cure.js +169 -0
  6. package/dist/bin/cure.js.map +1 -0
  7. package/dist/capabilities/cancerTreatmentCapability.d.ts +167 -0
  8. package/dist/capabilities/cancerTreatmentCapability.d.ts.map +1 -0
  9. package/dist/capabilities/cancerTreatmentCapability.js +912 -0
  10. package/dist/capabilities/cancerTreatmentCapability.js.map +1 -0
  11. package/dist/capabilities/index.d.ts +2 -0
  12. package/dist/capabilities/index.d.ts.map +1 -0
  13. package/dist/capabilities/index.js +3 -0
  14. package/dist/capabilities/index.js.map +1 -0
  15. package/dist/compliance/hipaa.d.ts +337 -0
  16. package/dist/compliance/hipaa.d.ts.map +1 -0
  17. package/dist/compliance/hipaa.js +929 -0
  18. package/dist/compliance/hipaa.js.map +1 -0
  19. package/dist/examples/cancerTreatmentDemo.d.ts +21 -0
  20. package/dist/examples/cancerTreatmentDemo.d.ts.map +1 -0
  21. package/dist/examples/cancerTreatmentDemo.js +216 -0
  22. package/dist/examples/cancerTreatmentDemo.js.map +1 -0
  23. package/dist/integrations/clinicalTrials/clinicalTrialsGov.d.ts +265 -0
  24. package/dist/integrations/clinicalTrials/clinicalTrialsGov.d.ts.map +1 -0
  25. package/dist/integrations/clinicalTrials/clinicalTrialsGov.js +808 -0
  26. package/dist/integrations/clinicalTrials/clinicalTrialsGov.js.map +1 -0
  27. package/dist/integrations/ehr/fhir.d.ts +455 -0
  28. package/dist/integrations/ehr/fhir.d.ts.map +1 -0
  29. package/dist/integrations/ehr/fhir.js +859 -0
  30. package/dist/integrations/ehr/fhir.js.map +1 -0
  31. package/dist/integrations/genomics/genomicPlatforms.d.ts +362 -0
  32. package/dist/integrations/genomics/genomicPlatforms.d.ts.map +1 -0
  33. package/dist/integrations/genomics/genomicPlatforms.js +1079 -0
  34. package/dist/integrations/genomics/genomicPlatforms.js.map +1 -0
  35. package/package.json +52 -0
  36. package/src/bin/cure.ts +182 -0
  37. package/src/capabilities/cancerTreatmentCapability.ts +1161 -0
  38. package/src/capabilities/index.ts +2 -0
  39. package/src/compliance/hipaa.ts +1365 -0
  40. package/src/examples/cancerTreatmentDemo.ts +241 -0
  41. package/src/integrations/clinicalTrials/clinicalTrialsGov.ts +1143 -0
  42. package/src/integrations/ehr/fhir.ts +1304 -0
  43. package/src/integrations/genomics/genomicPlatforms.ts +1480 -0
  44. package/src/ml/outcomePredictor.ts +1301 -0
  45. package/src/safety/drugInteractions.ts +942 -0
  46. package/src/validation/retrospectiveValidator.ts +887 -0
@@ -0,0 +1,1301 @@
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
+
16
+ import { EventEmitter } from 'events';
17
+ import { createHash } from 'crypto';
18
+
19
+ // ═══════════════════════════════════════════════════════════════════════════════
20
+ // FEATURE TYPES
21
+ // ═══════════════════════════════════════════════════════════════════════════════
22
+
23
+ export interface PatientFeatures {
24
+ // Demographics
25
+ age: number;
26
+ gender: 'male' | 'female';
27
+ ethnicity?: string;
28
+ bmi?: number;
29
+
30
+ // Disease characteristics
31
+ cancerType: string;
32
+ histology?: string;
33
+ stage: 'I' | 'II' | 'III' | 'IV' | 'IA' | 'IB' | 'IIA' | 'IIB' | 'IIIA' | 'IIIB' | 'IIIC' | 'IVA' | 'IVB';
34
+ grade?: 1 | 2 | 3;
35
+ tumorSize?: number; // cm
36
+ lymphNodeInvolvement?: number;
37
+ metastaticSites?: string[];
38
+
39
+ // Performance status
40
+ ecogStatus: 0 | 1 | 2 | 3 | 4;
41
+
42
+ // Biomarkers
43
+ genomicAlterations: {
44
+ gene: string;
45
+ alteration: string;
46
+ type: 'mutation' | 'amplification' | 'deletion' | 'fusion';
47
+ vaf?: number;
48
+ }[];
49
+
50
+ // Immunotherapy markers
51
+ pdl1Score?: number;
52
+ pdl1ScoreType?: 'TPS' | 'CPS' | 'IC';
53
+ msiStatus?: 'MSI-H' | 'MSI-L' | 'MSS';
54
+ tmbValue?: number;
55
+ tmbStatus?: 'high' | 'low';
56
+
57
+ // HRD status
58
+ hrdScore?: number;
59
+ hrdStatus?: 'positive' | 'negative';
60
+ brcaStatus?: 'BRCA1' | 'BRCA2' | 'wild-type';
61
+
62
+ // Lab values
63
+ ldh?: number;
64
+ albumin?: number;
65
+ hemoglobin?: number;
66
+ neutrophils?: number;
67
+ lymphocytes?: number;
68
+ platelets?: number;
69
+ creatinine?: number;
70
+ bilirubin?: number;
71
+ alkalinePhosphatase?: number;
72
+
73
+ // Prior treatments
74
+ priorLines?: number;
75
+ priorTherapies?: string[];
76
+ priorResponse?: 'CR' | 'PR' | 'SD' | 'PD';
77
+ treatmentFreeInterval?: number; // months
78
+
79
+ // Comorbidities
80
+ comorbidityIndex?: number;
81
+ organFunction?: {
82
+ cardiac?: 'normal' | 'impaired';
83
+ hepatic?: 'normal' | 'impaired';
84
+ renal?: 'normal' | 'impaired';
85
+ pulmonary?: 'normal' | 'impaired';
86
+ };
87
+ }
88
+
89
+ export interface TreatmentFeatures {
90
+ regimen: string;
91
+ drugs: string[];
92
+ treatmentType: 'chemotherapy' | 'immunotherapy' | 'targeted' | 'combination' | 'radiation' | 'surgery' | 'car-t';
93
+ setting: 'neoadjuvant' | 'adjuvant' | 'first-line' | 'second-line' | 'third-line-plus' | 'maintenance';
94
+ dosing?: 'standard' | 'reduced' | 'dose-dense';
95
+ schedule?: string;
96
+ }
97
+
98
+ // ═══════════════════════════════════════════════════════════════════════════════
99
+ // PREDICTION TYPES
100
+ // ═══════════════════════════════════════════════════════════════════════════════
101
+
102
+ export interface ResponsePrediction {
103
+ predictedResponse: 'CR' | 'PR' | 'SD' | 'PD';
104
+ probabilities: {
105
+ completeResponse: number;
106
+ partialResponse: number;
107
+ stableDisease: number;
108
+ progressiveDisease: number;
109
+ };
110
+ confidence: number;
111
+ objectiveResponseRate: number; // CR + PR probability
112
+ diseaseControlRate: number; // CR + PR + SD probability
113
+ timeToResponse?: {
114
+ median: number; // weeks
115
+ range: [number, number];
116
+ };
117
+ }
118
+
119
+ export interface SurvivalPrediction {
120
+ // Progression-Free Survival
121
+ pfs: {
122
+ median: number; // months
123
+ sixMonth: number; // probability
124
+ twelveMonth: number;
125
+ twentyFourMonth: number;
126
+ confidence: number;
127
+ hazardRatio?: number;
128
+ };
129
+
130
+ // Overall Survival
131
+ os: {
132
+ median: number;
133
+ twelveMonth: number;
134
+ twentyFourMonth: number;
135
+ fiveYear: number;
136
+ confidence: number;
137
+ hazardRatio?: number;
138
+ };
139
+
140
+ // Risk stratification
141
+ riskGroup: 'low' | 'intermediate-low' | 'intermediate-high' | 'high';
142
+ riskScore: number; // 0-100
143
+ }
144
+
145
+ export interface ToxicityPrediction {
146
+ overallRisk: 'low' | 'moderate' | 'high';
147
+ grade3PlusRisk: number; // Probability of Grade 3+ toxicity
148
+
149
+ specificRisks: {
150
+ toxicity: string;
151
+ grade: 1 | 2 | 3 | 4 | 5;
152
+ probability: number;
153
+ timeToOnset?: { median: number; unit: 'days' | 'weeks' | 'cycles' };
154
+ reversible: boolean;
155
+ management?: string;
156
+ }[];
157
+
158
+ // Immune-related adverse events (for immunotherapy)
159
+ iraeRisk?: {
160
+ any: number;
161
+ grade3Plus: number;
162
+ specificOrgans: { organ: string; risk: number }[];
163
+ };
164
+
165
+ // Dose modification recommendation
166
+ doseModification?: {
167
+ recommended: boolean;
168
+ reduction: number; // percentage
169
+ reason: string;
170
+ };
171
+ }
172
+
173
+ export interface ResistancePrediction {
174
+ intrinsicResistanceRisk: number; // Probability of primary resistance
175
+ acquiredResistanceRisk: number; // Probability of developing resistance
176
+
177
+ predictedMechanisms: {
178
+ mechanism: string;
179
+ probability: number;
180
+ monitoringBiomarker?: string;
181
+ }[];
182
+
183
+ timeToResistance?: {
184
+ median: number; // months
185
+ range: [number, number];
186
+ };
187
+
188
+ nextLineOptions: {
189
+ therapy: string;
190
+ rationale: string;
191
+ expectedBenefit: number; // expected months of benefit
192
+ }[];
193
+ }
194
+
195
+ export interface TherapyRanking {
196
+ therapies: {
197
+ regimen: string;
198
+ drugs: string[];
199
+ rank: number;
200
+
201
+ predictions: {
202
+ responseRate: number;
203
+ pfsSurvival: number;
204
+ osSurvival: number;
205
+ toxicityRisk: number;
206
+ qualityOfLifeScore: number;
207
+ };
208
+
209
+ overallScore: number;
210
+ confidence: number;
211
+
212
+ matchingBiomarkers: string[];
213
+ fdaApproved: boolean;
214
+ nccnRecommended: boolean;
215
+
216
+ considerations: string[];
217
+ contraindications?: string[];
218
+ }[];
219
+
220
+ bestChoice: {
221
+ regimen: string;
222
+ rationale: string[];
223
+ };
224
+ }
225
+
226
+ // ═══════════════════════════════════════════════════════════════════════════════
227
+ // MODEL TYPES
228
+ // ═══════════════════════════════════════════════════════════════════════════════
229
+
230
+ export interface MLModel {
231
+ id: string;
232
+ name: string;
233
+ version: string;
234
+ type: 'classification' | 'regression' | 'survival' | 'ranking';
235
+ target: 'response' | 'pfs' | 'os' | 'toxicity' | 'resistance';
236
+ cancerTypes: string[];
237
+
238
+ performance: {
239
+ auc?: number;
240
+ accuracy?: number;
241
+ sensitivity?: number;
242
+ specificity?: number;
243
+ cIndex?: number; // For survival models
244
+ calibration?: number;
245
+ brier?: number;
246
+ };
247
+
248
+ validation: {
249
+ method: 'cross-validation' | 'temporal' | 'external' | 'prospective';
250
+ cohortSize: number;
251
+ testSetSize: number;
252
+ validationDate: Date;
253
+ };
254
+
255
+ features: string[];
256
+ importantFeatures: { feature: string; importance: number }[];
257
+
258
+ status: 'development' | 'validation' | 'clinical-use' | 'deprecated';
259
+ regulatoryStatus?: 'not-submitted' | 'pending' | 'cleared' | '510k' | 'de-novo';
260
+ }
261
+
262
+ export interface ModelRegistry {
263
+ models: MLModel[];
264
+ getModel(id: string): MLModel | undefined;
265
+ getBestModel(target: MLModel['target'], cancerType: string): MLModel | undefined;
266
+ registerModel(model: MLModel): void;
267
+ }
268
+
269
+ // ═══════════════════════════════════════════════════════════════════════════════
270
+ // OUTCOME PREDICTOR SERVICE
271
+ // ═══════════════════════════════════════════════════════════════════════════════
272
+
273
+ export class OutcomePredictorService extends EventEmitter {
274
+ private modelRegistry: Map<string, MLModel> = new Map();
275
+ private featureEncoders: Map<string, FeatureEncoder> = new Map();
276
+ private predictionCache: Map<string, { prediction: any; timestamp: Date }> = new Map();
277
+ private cacheDuration = 3600000; // 1 hour
278
+
279
+ constructor() {
280
+ super();
281
+ this.initializeDefaultModels();
282
+ this.initializeFeatureEncoders();
283
+ }
284
+
285
+ /**
286
+ * Predict treatment response
287
+ */
288
+ async predictResponse(
289
+ patient: PatientFeatures,
290
+ treatment: TreatmentFeatures
291
+ ): Promise<ResponsePrediction> {
292
+ const cacheKey = this.getCacheKey('response', patient, treatment);
293
+ const cached = this.getFromCache<ResponsePrediction>(cacheKey);
294
+ if (cached) return cached;
295
+
296
+ // Get appropriate model
297
+ const model = this.getBestModel('response', patient.cancerType);
298
+
299
+ // Encode features
300
+ const features = this.encodeFeatures(patient, treatment);
301
+
302
+ // Make prediction (in production, this would call a trained model)
303
+ const prediction = this.computeResponsePrediction(features, patient, treatment, model);
304
+
305
+ this.setCache(cacheKey, prediction);
306
+ this.emit('prediction-made', { type: 'response', patient, treatment, prediction });
307
+
308
+ return prediction;
309
+ }
310
+
311
+ /**
312
+ * Predict survival outcomes
313
+ */
314
+ async predictSurvival(
315
+ patient: PatientFeatures,
316
+ treatment: TreatmentFeatures
317
+ ): Promise<SurvivalPrediction> {
318
+ const cacheKey = this.getCacheKey('survival', patient, treatment);
319
+ const cached = this.getFromCache<SurvivalPrediction>(cacheKey);
320
+ if (cached) return cached;
321
+
322
+ const model = this.getBestModel('pfs', patient.cancerType);
323
+ const features = this.encodeFeatures(patient, treatment);
324
+ const prediction = this.computeSurvivalPrediction(features, patient, treatment, model);
325
+
326
+ this.setCache(cacheKey, prediction);
327
+ this.emit('prediction-made', { type: 'survival', patient, treatment, prediction });
328
+
329
+ return prediction;
330
+ }
331
+
332
+ /**
333
+ * Predict toxicity risk
334
+ */
335
+ async predictToxicity(
336
+ patient: PatientFeatures,
337
+ treatment: TreatmentFeatures
338
+ ): Promise<ToxicityPrediction> {
339
+ const cacheKey = this.getCacheKey('toxicity', patient, treatment);
340
+ const cached = this.getFromCache<ToxicityPrediction>(cacheKey);
341
+ if (cached) return cached;
342
+
343
+ const model = this.getBestModel('toxicity', patient.cancerType);
344
+ const features = this.encodeFeatures(patient, treatment);
345
+ const prediction = this.computeToxicityPrediction(features, patient, treatment, model);
346
+
347
+ this.setCache(cacheKey, prediction);
348
+ this.emit('prediction-made', { type: 'toxicity', patient, treatment, prediction });
349
+
350
+ return prediction;
351
+ }
352
+
353
+ /**
354
+ * Predict resistance development
355
+ */
356
+ async predictResistance(
357
+ patient: PatientFeatures,
358
+ treatment: TreatmentFeatures
359
+ ): Promise<ResistancePrediction> {
360
+ const cacheKey = this.getCacheKey('resistance', patient, treatment);
361
+ const cached = this.getFromCache<ResistancePrediction>(cacheKey);
362
+ if (cached) return cached;
363
+
364
+ const model = this.getBestModel('resistance', patient.cancerType);
365
+ const features = this.encodeFeatures(patient, treatment);
366
+ const prediction = this.computeResistancePrediction(features, patient, treatment, model);
367
+
368
+ this.setCache(cacheKey, prediction);
369
+ this.emit('prediction-made', { type: 'resistance', patient, treatment, prediction });
370
+
371
+ return prediction;
372
+ }
373
+
374
+ /**
375
+ * Rank treatment options
376
+ */
377
+ async rankTherapies(
378
+ patient: PatientFeatures,
379
+ treatmentOptions: TreatmentFeatures[],
380
+ preferences?: {
381
+ prioritizeEfficacy?: boolean;
382
+ prioritizeToxicity?: boolean;
383
+ prioritizeQoL?: boolean;
384
+ }
385
+ ): Promise<TherapyRanking> {
386
+ const rankings: TherapyRanking['therapies'] = [];
387
+
388
+ // Get predictions for each treatment option
389
+ for (const treatment of treatmentOptions) {
390
+ const [response, survival, toxicity] = await Promise.all([
391
+ this.predictResponse(patient, treatment),
392
+ this.predictSurvival(patient, treatment),
393
+ this.predictToxicity(patient, treatment)
394
+ ]);
395
+
396
+ // Calculate overall score based on preferences
397
+ const weights = {
398
+ efficacy: preferences?.prioritizeEfficacy ? 0.5 : 0.35,
399
+ toxicity: preferences?.prioritizeToxicity ? 0.3 : 0.25,
400
+ qol: preferences?.prioritizeQoL ? 0.3 : 0.2,
401
+ survival: 0.2
402
+ };
403
+
404
+ const efficacyScore = response.objectiveResponseRate;
405
+ const toxicityScore = 1 - toxicity.grade3PlusRisk;
406
+ const survivalScore = survival.pfs.twelveMonth;
407
+ const qolScore = this.estimateQoLScore(toxicity);
408
+
409
+ const overallScore =
410
+ efficacyScore * weights.efficacy +
411
+ toxicityScore * weights.toxicity +
412
+ survivalScore * weights.survival +
413
+ qolScore * weights.qol;
414
+
415
+ rankings.push({
416
+ regimen: treatment.regimen,
417
+ drugs: treatment.drugs,
418
+ rank: 0, // Will be set after sorting
419
+ predictions: {
420
+ responseRate: response.objectiveResponseRate,
421
+ pfsSurvival: survival.pfs.median,
422
+ osSurvival: survival.os.median,
423
+ toxicityRisk: toxicity.grade3PlusRisk,
424
+ qualityOfLifeScore: qolScore
425
+ },
426
+ overallScore,
427
+ confidence: (response.confidence + survival.pfs.confidence) / 2,
428
+ matchingBiomarkers: this.getMatchingBiomarkers(patient, treatment),
429
+ fdaApproved: this.checkFDAApproval(treatment, patient.cancerType),
430
+ nccnRecommended: this.checkNCCNRecommendation(treatment, patient.cancerType, patient.stage),
431
+ considerations: this.getConsiderations(patient, treatment)
432
+ });
433
+ }
434
+
435
+ // Sort by overall score
436
+ rankings.sort((a, b) => b.overallScore - a.overallScore);
437
+
438
+ // Assign ranks
439
+ rankings.forEach((r, i) => r.rank = i + 1);
440
+
441
+ const bestChoice = rankings[0];
442
+
443
+ return {
444
+ therapies: rankings,
445
+ bestChoice: {
446
+ regimen: bestChoice.regimen,
447
+ rationale: [
448
+ `Highest overall score (${(bestChoice.overallScore * 100).toFixed(1)}%)`,
449
+ `Expected response rate: ${(bestChoice.predictions.responseRate * 100).toFixed(1)}%`,
450
+ `Expected median PFS: ${bestChoice.predictions.pfsSurvival.toFixed(1)} months`,
451
+ bestChoice.fdaApproved ? 'FDA approved for this indication' : '',
452
+ bestChoice.nccnRecommended ? 'NCCN recommended' : '',
453
+ ...bestChoice.matchingBiomarkers.map(b => `Matches biomarker: ${b}`)
454
+ ].filter(Boolean)
455
+ }
456
+ };
457
+ }
458
+
459
+ /**
460
+ * Get comprehensive prediction report
461
+ */
462
+ async getComprehensivePrediction(
463
+ patient: PatientFeatures,
464
+ treatment: TreatmentFeatures
465
+ ): Promise<{
466
+ response: ResponsePrediction;
467
+ survival: SurvivalPrediction;
468
+ toxicity: ToxicityPrediction;
469
+ resistance: ResistancePrediction;
470
+ overallAssessment: {
471
+ recommendation: 'strongly-recommended' | 'recommended' | 'consider' | 'caution' | 'not-recommended';
472
+ rationale: string[];
473
+ caveats: string[];
474
+ alternativeOptions: string[];
475
+ };
476
+ }> {
477
+ const [response, survival, toxicity, resistance] = await Promise.all([
478
+ this.predictResponse(patient, treatment),
479
+ this.predictSurvival(patient, treatment),
480
+ this.predictToxicity(patient, treatment),
481
+ this.predictResistance(patient, treatment)
482
+ ]);
483
+
484
+ // Generate overall assessment
485
+ const overallAssessment = this.generateOverallAssessment(
486
+ patient, treatment, response, survival, toxicity, resistance
487
+ );
488
+
489
+ return {
490
+ response,
491
+ survival,
492
+ toxicity,
493
+ resistance,
494
+ overallAssessment
495
+ };
496
+ }
497
+
498
+ // ═══════════════════════════════════════════════════════════════════════════════
499
+ // PREDICTION COMPUTATION (Placeholder implementations)
500
+ // In production, these would use trained ML models
501
+ // ═══════════════════════════════════════════════════════════════════════════════
502
+
503
+ private computeResponsePrediction(
504
+ features: number[],
505
+ patient: PatientFeatures,
506
+ treatment: TreatmentFeatures,
507
+ model?: MLModel
508
+ ): ResponsePrediction {
509
+ // Base probabilities based on treatment type and cancer
510
+ let baseCR = 0.15;
511
+ let basePR = 0.30;
512
+ let baseSD = 0.30;
513
+
514
+ // Adjust based on biomarkers
515
+ if (this.hasBiomarkerMatch(patient, treatment)) {
516
+ baseCR += 0.15;
517
+ basePR += 0.15;
518
+ }
519
+
520
+ // Adjust based on stage
521
+ const stageModifiers: Record<string, number> = {
522
+ 'I': 1.3, 'IA': 1.3, 'IB': 1.25,
523
+ 'II': 1.15, 'IIA': 1.15, 'IIB': 1.1,
524
+ 'III': 0.9, 'IIIA': 0.95, 'IIIB': 0.85, 'IIIC': 0.8,
525
+ 'IV': 0.7, 'IVA': 0.75, 'IVB': 0.65
526
+ };
527
+ const stageMod = stageModifiers[patient.stage] || 1.0;
528
+
529
+ // Adjust based on ECOG
530
+ const ecogModifiers = [1.0, 0.9, 0.75, 0.5, 0.25];
531
+ const ecogMod = ecogModifiers[patient.ecogStatus];
532
+
533
+ // Adjust based on treatment setting
534
+ const settingModifiers: Record<string, number> = {
535
+ 'first-line': 1.0,
536
+ 'second-line': 0.75,
537
+ 'third-line-plus': 0.5,
538
+ 'neoadjuvant': 1.1,
539
+ 'adjuvant': 1.05,
540
+ 'maintenance': 0.9
541
+ };
542
+ const settingMod = settingModifiers[treatment.setting] || 1.0;
543
+
544
+ // Apply modifiers
545
+ const modifier = stageMod * ecogMod * settingMod;
546
+ const crProb = Math.min(baseCR * modifier, 0.6);
547
+ const prProb = Math.min(basePR * modifier, 0.5);
548
+ const sdProb = Math.min(baseSD * modifier, 0.4);
549
+ const pdProb = Math.max(1 - crProb - prProb - sdProb, 0.05);
550
+
551
+ // Normalize
552
+ const total = crProb + prProb + sdProb + pdProb;
553
+ const probabilities = {
554
+ completeResponse: crProb / total,
555
+ partialResponse: prProb / total,
556
+ stableDisease: sdProb / total,
557
+ progressiveDisease: pdProb / total
558
+ };
559
+
560
+ // Determine best response
561
+ const maxProb = Math.max(...Object.values(probabilities));
562
+ let predictedResponse: ResponsePrediction['predictedResponse'] = 'SD';
563
+ if (probabilities.completeResponse === maxProb) predictedResponse = 'CR';
564
+ else if (probabilities.partialResponse === maxProb) predictedResponse = 'PR';
565
+ else if (probabilities.progressiveDisease === maxProb) predictedResponse = 'PD';
566
+
567
+ return {
568
+ predictedResponse,
569
+ probabilities,
570
+ confidence: model ? model.performance.auc || 0.75 : 0.70,
571
+ objectiveResponseRate: probabilities.completeResponse + probabilities.partialResponse,
572
+ diseaseControlRate: probabilities.completeResponse + probabilities.partialResponse + probabilities.stableDisease,
573
+ timeToResponse: {
574
+ median: 8,
575
+ range: [4, 16]
576
+ }
577
+ };
578
+ }
579
+
580
+ private computeSurvivalPrediction(
581
+ features: number[],
582
+ patient: PatientFeatures,
583
+ treatment: TreatmentFeatures,
584
+ model?: MLModel
585
+ ): SurvivalPrediction {
586
+ // Base survival estimates (in months)
587
+ let basePFS = 12;
588
+ let baseOS = 24;
589
+
590
+ // Cancer type adjustments
591
+ const cancerPFSModifiers: Record<string, number> = {
592
+ 'NSCLC': 1.0, 'SCLC': 0.5, 'Breast': 1.5, 'Colorectal': 1.0,
593
+ 'Melanoma': 1.2, 'RCC': 1.3, 'Ovarian': 0.8, 'Pancreatic': 0.4,
594
+ 'Glioblastoma': 0.3, 'AML': 0.6, 'Multiple Myeloma': 1.5
595
+ };
596
+
597
+ const cancerMod = cancerPFSModifiers[patient.cancerType] || 1.0;
598
+ basePFS *= cancerMod;
599
+ baseOS *= cancerMod;
600
+
601
+ // Stage adjustments
602
+ const stageModifiers: Record<string, number> = {
603
+ 'I': 3.0, 'IA': 3.5, 'IB': 2.8,
604
+ 'II': 2.0, 'IIA': 2.2, 'IIB': 1.8,
605
+ 'III': 1.0, 'IIIA': 1.2, 'IIIB': 0.9, 'IIIC': 0.7,
606
+ 'IV': 0.5, 'IVA': 0.55, 'IVB': 0.4
607
+ };
608
+ const stageMod = stageModifiers[patient.stage] || 1.0;
609
+ basePFS *= stageMod;
610
+ baseOS *= stageMod;
611
+
612
+ // Biomarker-driven therapy boost
613
+ if (this.hasBiomarkerMatch(patient, treatment)) {
614
+ basePFS *= 1.5;
615
+ baseOS *= 1.3;
616
+ }
617
+
618
+ // ECOG adjustment
619
+ const ecogMultipliers = [1.0, 0.85, 0.65, 0.4, 0.2];
620
+ basePFS *= ecogMultipliers[patient.ecogStatus];
621
+ baseOS *= ecogMultipliers[patient.ecogStatus];
622
+
623
+ // Calculate probabilities using exponential survival model
624
+ const lambda_pfs = 1 / basePFS;
625
+ const lambda_os = 1 / baseOS;
626
+
627
+ const pfs = {
628
+ median: basePFS,
629
+ sixMonth: Math.exp(-lambda_pfs * 6),
630
+ twelveMonth: Math.exp(-lambda_pfs * 12),
631
+ twentyFourMonth: Math.exp(-lambda_pfs * 24),
632
+ confidence: model ? model.performance.cIndex || 0.72 : 0.68
633
+ };
634
+
635
+ const os = {
636
+ median: baseOS,
637
+ twelveMonth: Math.exp(-lambda_os * 12),
638
+ twentyFourMonth: Math.exp(-lambda_os * 24),
639
+ fiveYear: Math.exp(-lambda_os * 60),
640
+ confidence: model ? model.performance.cIndex || 0.72 : 0.68
641
+ };
642
+
643
+ // Calculate risk score (0-100)
644
+ const riskFactors = [
645
+ patient.stage.startsWith('IV') ? 25 : patient.stage.startsWith('III') ? 15 : 5,
646
+ patient.ecogStatus >= 2 ? 20 : patient.ecogStatus === 1 ? 10 : 0,
647
+ (patient.priorLines || 0) >= 2 ? 15 : (patient.priorLines || 0) >= 1 ? 8 : 0,
648
+ patient.ldh && patient.ldh > 250 ? 10 : 0,
649
+ patient.metastaticSites && patient.metastaticSites.length > 2 ? 15 : 0,
650
+ patient.age > 75 ? 10 : patient.age > 65 ? 5 : 0
651
+ ];
652
+ const riskScore = Math.min(riskFactors.reduce((a, b) => a + b, 0), 100);
653
+
654
+ let riskGroup: SurvivalPrediction['riskGroup'];
655
+ if (riskScore < 25) riskGroup = 'low';
656
+ else if (riskScore < 50) riskGroup = 'intermediate-low';
657
+ else if (riskScore < 75) riskGroup = 'intermediate-high';
658
+ else riskGroup = 'high';
659
+
660
+ return { pfs, os, riskGroup, riskScore };
661
+ }
662
+
663
+ private computeToxicityPrediction(
664
+ features: number[],
665
+ patient: PatientFeatures,
666
+ treatment: TreatmentFeatures,
667
+ model?: MLModel
668
+ ): ToxicityPrediction {
669
+ const specificRisks: ToxicityPrediction['specificRisks'] = [];
670
+ let grade3PlusRisk = 0;
671
+
672
+ // Define toxicity profiles by drug class
673
+ const drugToxicities = this.getDrugToxicityProfiles(treatment.drugs);
674
+
675
+ for (const tox of drugToxicities) {
676
+ // Adjust risk based on patient factors
677
+ let adjustedRisk = tox.baseRisk;
678
+
679
+ // Age adjustment
680
+ if (patient.age > 70) adjustedRisk *= 1.2;
681
+ if (patient.age > 80) adjustedRisk *= 1.4;
682
+
683
+ // Organ function adjustment
684
+ if (tox.affectedOrgan === 'hepatic' && patient.organFunction?.hepatic === 'impaired') {
685
+ adjustedRisk *= 1.5;
686
+ }
687
+ if (tox.affectedOrgan === 'renal' && patient.organFunction?.renal === 'impaired') {
688
+ adjustedRisk *= 1.5;
689
+ }
690
+ if (tox.affectedOrgan === 'cardiac' && patient.organFunction?.cardiac === 'impaired') {
691
+ adjustedRisk *= 1.5;
692
+ }
693
+
694
+ // ECOG adjustment
695
+ adjustedRisk *= (1 + patient.ecogStatus * 0.1);
696
+
697
+ adjustedRisk = Math.min(adjustedRisk, 0.95);
698
+
699
+ specificRisks.push({
700
+ toxicity: tox.name,
701
+ grade: tox.typicalGrade,
702
+ probability: adjustedRisk,
703
+ timeToOnset: tox.timeToOnset,
704
+ reversible: tox.reversible,
705
+ management: tox.management
706
+ });
707
+
708
+ if (tox.typicalGrade >= 3) {
709
+ grade3PlusRisk += adjustedRisk * 0.3; // Weighted contribution
710
+ }
711
+ }
712
+
713
+ grade3PlusRisk = Math.min(grade3PlusRisk, 0.9);
714
+
715
+ // Immunotherapy-specific irAE prediction
716
+ let iraeRisk: ToxicityPrediction['iraeRisk'];
717
+ if (treatment.treatmentType === 'immunotherapy' || this.hasImmunotherapyDrug(treatment.drugs)) {
718
+ iraeRisk = {
719
+ any: 0.60,
720
+ grade3Plus: 0.15,
721
+ specificOrgans: [
722
+ { organ: 'skin', risk: 0.35 },
723
+ { organ: 'GI', risk: 0.20 },
724
+ { organ: 'endocrine', risk: 0.15 },
725
+ { organ: 'hepatic', risk: 0.10 },
726
+ { organ: 'pulmonary', risk: 0.08 }
727
+ ]
728
+ };
729
+ }
730
+
731
+ // Dose modification recommendation
732
+ let doseModification: ToxicityPrediction['doseModification'];
733
+ if (patient.age > 75 || patient.ecogStatus >= 2 || patient.organFunction?.renal === 'impaired') {
734
+ doseModification = {
735
+ recommended: true,
736
+ reduction: 20,
737
+ reason: patient.age > 75 ? 'Advanced age' :
738
+ patient.ecogStatus >= 2 ? 'Poor performance status' :
739
+ 'Impaired organ function'
740
+ };
741
+ }
742
+
743
+ return {
744
+ overallRisk: grade3PlusRisk > 0.4 ? 'high' : grade3PlusRisk > 0.2 ? 'moderate' : 'low',
745
+ grade3PlusRisk,
746
+ specificRisks,
747
+ iraeRisk,
748
+ doseModification
749
+ };
750
+ }
751
+
752
+ private computeResistancePrediction(
753
+ features: number[],
754
+ patient: PatientFeatures,
755
+ treatment: TreatmentFeatures,
756
+ model?: MLModel
757
+ ): ResistancePrediction {
758
+ let intrinsicRisk = 0.2;
759
+ let acquiredRisk = 0.6;
760
+
761
+ // Adjust based on prior treatments
762
+ if (patient.priorLines && patient.priorLines > 0) {
763
+ intrinsicRisk += 0.1 * patient.priorLines;
764
+ acquiredRisk += 0.05 * patient.priorLines;
765
+ }
766
+
767
+ // Adjust based on prior response
768
+ if (patient.priorResponse === 'PD') {
769
+ intrinsicRisk += 0.2;
770
+ } else if (patient.priorResponse === 'CR') {
771
+ intrinsicRisk -= 0.1;
772
+ }
773
+
774
+ // Get resistance mechanisms based on treatment and mutations
775
+ const mechanisms = this.getResistanceMechanisms(patient, treatment);
776
+
777
+ // Calculate time to resistance
778
+ const baseTimeToResistance = this.hasBiomarkerMatch(patient, treatment) ? 18 : 9;
779
+ const timeModifier = patient.priorLines ? 1 - (patient.priorLines * 0.15) : 1;
780
+
781
+ // Get next line options
782
+ const nextLineOptions = this.getNextLineOptions(patient, treatment, mechanisms);
783
+
784
+ return {
785
+ intrinsicResistanceRisk: Math.min(intrinsicRisk, 0.9),
786
+ acquiredResistanceRisk: Math.min(acquiredRisk, 0.95),
787
+ predictedMechanisms: mechanisms,
788
+ timeToResistance: {
789
+ median: baseTimeToResistance * timeModifier,
790
+ range: [baseTimeToResistance * timeModifier * 0.5, baseTimeToResistance * timeModifier * 2]
791
+ },
792
+ nextLineOptions
793
+ };
794
+ }
795
+
796
+ // ═══════════════════════════════════════════════════════════════════════════════
797
+ // HELPER METHODS
798
+ // ═══════════════════════════════════════════════════════════════════════════════
799
+
800
+ private initializeDefaultModels(): void {
801
+ // Register placeholder models
802
+ const defaultModels: MLModel[] = [
803
+ {
804
+ id: 'response-nsclc-v1',
805
+ name: 'NSCLC Response Predictor',
806
+ version: '1.0',
807
+ type: 'classification',
808
+ target: 'response',
809
+ cancerTypes: ['NSCLC'],
810
+ performance: { auc: 0.78, accuracy: 0.72 },
811
+ validation: { method: 'cross-validation', cohortSize: 1500, testSetSize: 300, validationDate: new Date() },
812
+ features: ['age', 'stage', 'ecog', 'pdl1', 'tmb', 'egfr', 'alk', 'kras'],
813
+ importantFeatures: [
814
+ { feature: 'pdl1', importance: 0.25 },
815
+ { feature: 'tmb', importance: 0.20 },
816
+ { feature: 'egfr', importance: 0.18 }
817
+ ],
818
+ status: 'validation'
819
+ },
820
+ {
821
+ id: 'survival-pan-cancer-v1',
822
+ name: 'Pan-Cancer Survival Model',
823
+ version: '1.0',
824
+ type: 'survival',
825
+ target: 'os',
826
+ cancerTypes: ['all'],
827
+ performance: { cIndex: 0.72 },
828
+ validation: { method: 'cross-validation', cohortSize: 10000, testSetSize: 2000, validationDate: new Date() },
829
+ features: ['age', 'stage', 'ecog', 'cancerType', 'priorLines', 'ldh'],
830
+ importantFeatures: [
831
+ { feature: 'stage', importance: 0.30 },
832
+ { feature: 'ecog', importance: 0.25 },
833
+ { feature: 'priorLines', importance: 0.15 }
834
+ ],
835
+ status: 'validation'
836
+ }
837
+ ];
838
+
839
+ for (const model of defaultModels) {
840
+ this.modelRegistry.set(model.id, model);
841
+ }
842
+ }
843
+
844
+ private initializeFeatureEncoders(): void {
845
+ // Initialize encoders for categorical variables
846
+ this.featureEncoders.set('stage', new FeatureEncoder({
847
+ 'I': 1, 'IA': 1, 'IB': 1.5,
848
+ 'II': 2, 'IIA': 2, 'IIB': 2.5,
849
+ 'III': 3, 'IIIA': 3, 'IIIB': 3.5, 'IIIC': 3.8,
850
+ 'IV': 4, 'IVA': 4, 'IVB': 4.5
851
+ }));
852
+
853
+ this.featureEncoders.set('cancerType', new FeatureEncoder({
854
+ 'NSCLC': 1, 'SCLC': 2, 'Breast': 3, 'Colorectal': 4,
855
+ 'Melanoma': 5, 'RCC': 6, 'Ovarian': 7, 'Pancreatic': 8
856
+ }));
857
+ }
858
+
859
+ private encodeFeatures(patient: PatientFeatures, treatment: TreatmentFeatures): number[] {
860
+ const features: number[] = [];
861
+
862
+ // Numeric features (normalized)
863
+ features.push(patient.age / 100);
864
+ features.push(patient.ecogStatus / 4);
865
+ features.push(patient.gender === 'male' ? 1 : 0);
866
+
867
+ // Encoded categorical features
868
+ const stageEncoder = this.featureEncoders.get('stage');
869
+ features.push((stageEncoder?.encode(patient.stage) || 2) / 5);
870
+
871
+ // Biomarker features
872
+ features.push((patient.pdl1Score || 0) / 100);
873
+ features.push((patient.tmbValue || 0) / 50);
874
+ features.push(patient.msiStatus === 'MSI-H' ? 1 : 0);
875
+ features.push(patient.hrdStatus === 'positive' ? 1 : 0);
876
+
877
+ // Prior treatment features
878
+ features.push((patient.priorLines || 0) / 5);
879
+
880
+ // Treatment features
881
+ features.push(treatment.treatmentType === 'immunotherapy' ? 1 : 0);
882
+ features.push(treatment.treatmentType === 'targeted' ? 1 : 0);
883
+
884
+ return features;
885
+ }
886
+
887
+ private getBestModel(target: MLModel['target'], cancerType: string): MLModel | undefined {
888
+ const models = Array.from(this.modelRegistry.values())
889
+ .filter(m =>
890
+ m.target === target &&
891
+ (m.cancerTypes.includes(cancerType) || m.cancerTypes.includes('all')) &&
892
+ m.status !== 'deprecated'
893
+ )
894
+ .sort((a, b) => (b.performance.auc || b.performance.cIndex || 0) - (a.performance.auc || a.performance.cIndex || 0));
895
+
896
+ return models[0];
897
+ }
898
+
899
+ private hasBiomarkerMatch(patient: PatientFeatures, treatment: TreatmentFeatures): boolean {
900
+ // Check for biomarker-drug matches
901
+ const matches = [
902
+ { biomarker: 'EGFR', drugs: ['osimertinib', 'erlotinib', 'gefitinib', 'afatinib'] },
903
+ { biomarker: 'ALK', drugs: ['alectinib', 'crizotinib', 'brigatinib', 'lorlatinib'] },
904
+ { biomarker: 'BRAF V600', drugs: ['dabrafenib', 'vemurafenib', 'encorafenib'] },
905
+ { biomarker: 'HER2', drugs: ['trastuzumab', 'pertuzumab', 't-dxd'] },
906
+ { biomarker: 'BRCA', drugs: ['olaparib', 'rucaparib', 'niraparib', 'talazoparib'] },
907
+ { biomarker: 'KRAS G12C', drugs: ['sotorasib', 'adagrasib'] }
908
+ ];
909
+
910
+ for (const match of matches) {
911
+ const hasBiomarker = patient.genomicAlterations.some(g =>
912
+ g.gene.toUpperCase().includes(match.biomarker) ||
913
+ g.alteration.toUpperCase().includes(match.biomarker)
914
+ );
915
+ const hasDrug = treatment.drugs.some(d =>
916
+ match.drugs.some(md => d.toLowerCase().includes(md))
917
+ );
918
+ if (hasBiomarker && hasDrug) return true;
919
+ }
920
+
921
+ // Check immunotherapy eligibility
922
+ if (treatment.treatmentType === 'immunotherapy' || this.hasImmunotherapyDrug(treatment.drugs)) {
923
+ if (patient.msiStatus === 'MSI-H') return true;
924
+ if (patient.tmbStatus === 'high') return true;
925
+ if (patient.pdl1Score && patient.pdl1Score >= 50) return true;
926
+ }
927
+
928
+ return false;
929
+ }
930
+
931
+ private hasImmunotherapyDrug(drugs: string[]): boolean {
932
+ const immunoDrugs = ['pembrolizumab', 'nivolumab', 'ipilimumab', 'atezolizumab', 'durvalumab', 'avelumab'];
933
+ return drugs.some(d => immunoDrugs.some(id => d.toLowerCase().includes(id)));
934
+ }
935
+
936
+ private getDrugToxicityProfiles(drugs: string[]): {
937
+ name: string;
938
+ baseRisk: number;
939
+ typicalGrade: 1 | 2 | 3 | 4 | 5;
940
+ affectedOrgan?: string;
941
+ timeToOnset?: { median: number; unit: 'days' | 'weeks' | 'cycles' };
942
+ reversible: boolean;
943
+ management?: string;
944
+ }[] {
945
+ const profiles: any[] = [];
946
+
947
+ // Check for common drug classes and their toxicities
948
+ for (const drug of drugs) {
949
+ const lower = drug.toLowerCase();
950
+
951
+ // Checkpoint inhibitors
952
+ if (['pembrolizumab', 'nivolumab', 'ipilimumab', 'atezolizumab'].some(d => lower.includes(d))) {
953
+ profiles.push(
954
+ { name: 'Immune-related dermatitis', baseRisk: 0.35, typicalGrade: 2, affectedOrgan: 'skin', reversible: true },
955
+ { name: 'Immune-related colitis', baseRisk: 0.15, typicalGrade: 3, affectedOrgan: 'GI', reversible: true, management: 'Corticosteroids' },
956
+ { name: 'Immune-related pneumonitis', baseRisk: 0.05, typicalGrade: 3, affectedOrgan: 'pulmonary', reversible: true, management: 'Hold treatment, corticosteroids' },
957
+ { name: 'Immune-related hepatitis', baseRisk: 0.08, typicalGrade: 3, affectedOrgan: 'hepatic', reversible: true },
958
+ { name: 'Immune-related thyroiditis', baseRisk: 0.15, typicalGrade: 2, affectedOrgan: 'endocrine', reversible: false }
959
+ );
960
+ }
961
+
962
+ // Platinum agents
963
+ if (['carboplatin', 'cisplatin', 'oxaliplatin'].some(d => lower.includes(d))) {
964
+ profiles.push(
965
+ { name: 'Nausea/Vomiting', baseRisk: 0.60, typicalGrade: 2, affectedOrgan: 'GI', reversible: true },
966
+ { name: 'Nephrotoxicity', baseRisk: lower.includes('cisplatin') ? 0.25 : 0.08, typicalGrade: 2, affectedOrgan: 'renal', reversible: true },
967
+ { name: 'Myelosuppression', baseRisk: 0.40, typicalGrade: 3, affectedOrgan: 'hematologic', reversible: true },
968
+ { name: 'Peripheral neuropathy', baseRisk: 0.30, typicalGrade: 2, affectedOrgan: 'neurologic', reversible: false }
969
+ );
970
+ }
971
+
972
+ // EGFR TKIs
973
+ if (['osimertinib', 'erlotinib', 'gefitinib', 'afatinib'].some(d => lower.includes(d))) {
974
+ profiles.push(
975
+ { name: 'Rash', baseRisk: 0.45, typicalGrade: 2, affectedOrgan: 'skin', reversible: true },
976
+ { name: 'Diarrhea', baseRisk: 0.50, typicalGrade: 2, affectedOrgan: 'GI', reversible: true },
977
+ { name: 'Interstitial lung disease', baseRisk: 0.03, typicalGrade: 4, affectedOrgan: 'pulmonary', reversible: false }
978
+ );
979
+ }
980
+
981
+ // CDK4/6 inhibitors
982
+ if (['palbociclib', 'ribociclib', 'abemaciclib'].some(d => lower.includes(d))) {
983
+ profiles.push(
984
+ { name: 'Neutropenia', baseRisk: 0.70, typicalGrade: 3, affectedOrgan: 'hematologic', reversible: true },
985
+ { name: 'Fatigue', baseRisk: 0.40, typicalGrade: 2, reversible: true },
986
+ { name: 'Diarrhea', baseRisk: lower.includes('abemaciclib') ? 0.80 : 0.20, typicalGrade: 2, affectedOrgan: 'GI', reversible: true }
987
+ );
988
+ }
989
+ }
990
+
991
+ return profiles;
992
+ }
993
+
994
+ private getResistanceMechanisms(patient: PatientFeatures, treatment: TreatmentFeatures): ResistancePrediction['predictedMechanisms'] {
995
+ const mechanisms: ResistancePrediction['predictedMechanisms'] = [];
996
+
997
+ // EGFR TKI resistance mechanisms
998
+ if (treatment.drugs.some(d => ['osimertinib', 'erlotinib', 'gefitinib'].some(e => d.toLowerCase().includes(e)))) {
999
+ mechanisms.push(
1000
+ { mechanism: 'MET amplification', probability: 0.20, monitoringBiomarker: 'MET FISH/NGS' },
1001
+ { mechanism: 'EGFR C797S mutation', probability: 0.15, monitoringBiomarker: 'ctDNA EGFR' },
1002
+ { mechanism: 'Histologic transformation (SCLC)', probability: 0.05, monitoringBiomarker: 'Tissue biopsy' },
1003
+ { mechanism: 'HER2 amplification', probability: 0.10, monitoringBiomarker: 'HER2 NGS/FISH' }
1004
+ );
1005
+ }
1006
+
1007
+ // Immunotherapy resistance
1008
+ if (this.hasImmunotherapyDrug(treatment.drugs)) {
1009
+ mechanisms.push(
1010
+ { mechanism: 'Beta-2 microglobulin loss', probability: 0.10, monitoringBiomarker: 'B2M NGS' },
1011
+ { mechanism: 'JAK1/2 mutations', probability: 0.08, monitoringBiomarker: 'JAK1/2 NGS' },
1012
+ { mechanism: 'Immunosuppressive TME', probability: 0.25, monitoringBiomarker: 'Tissue biopsy + IHC' },
1013
+ { mechanism: 'Antigen loss', probability: 0.15 }
1014
+ );
1015
+ }
1016
+
1017
+ // BRAF inhibitor resistance
1018
+ if (treatment.drugs.some(d => ['dabrafenib', 'vemurafenib', 'encorafenib'].some(b => d.toLowerCase().includes(b)))) {
1019
+ mechanisms.push(
1020
+ { mechanism: 'MAPK reactivation', probability: 0.30, monitoringBiomarker: 'NRAS/MEK NGS' },
1021
+ { mechanism: 'BRAF amplification', probability: 0.15, monitoringBiomarker: 'BRAF CNV' },
1022
+ { mechanism: 'RTK bypass (EGFR, MET)', probability: 0.20, monitoringBiomarker: 'Comprehensive NGS' }
1023
+ );
1024
+ }
1025
+
1026
+ return mechanisms;
1027
+ }
1028
+
1029
+ private getNextLineOptions(
1030
+ patient: PatientFeatures,
1031
+ treatment: TreatmentFeatures,
1032
+ mechanisms: ResistancePrediction['predictedMechanisms']
1033
+ ): ResistancePrediction['nextLineOptions'] {
1034
+ const options: ResistancePrediction['nextLineOptions'] = [];
1035
+
1036
+ // Based on predicted resistance mechanisms
1037
+ for (const mech of mechanisms.slice(0, 3)) {
1038
+ if (mech.mechanism === 'MET amplification') {
1039
+ options.push({
1040
+ therapy: 'Osimertinib + Savolitinib',
1041
+ rationale: 'Targets MET bypass pathway',
1042
+ expectedBenefit: 8
1043
+ });
1044
+ }
1045
+ if (mech.mechanism === 'EGFR C797S mutation') {
1046
+ options.push({
1047
+ therapy: 'First-generation EGFR TKI + Third-generation EGFR TKI',
1048
+ rationale: 'C797S may restore sensitivity to 1st-gen TKI',
1049
+ expectedBenefit: 6
1050
+ });
1051
+ }
1052
+ if (mech.mechanism === 'Histologic transformation (SCLC)') {
1053
+ options.push({
1054
+ therapy: 'Platinum-etoposide chemotherapy',
1055
+ rationale: 'Standard SCLC treatment',
1056
+ expectedBenefit: 4
1057
+ });
1058
+ }
1059
+ }
1060
+
1061
+ // Add general next-line options
1062
+ if (treatment.treatmentType !== 'immunotherapy' && !this.hasImmunotherapyDrug(treatment.drugs)) {
1063
+ options.push({
1064
+ therapy: 'Immunotherapy (if PD-L1+/TMB-H)',
1065
+ rationale: 'Different mechanism of action',
1066
+ expectedBenefit: 10
1067
+ });
1068
+ }
1069
+
1070
+ // Clinical trial option
1071
+ options.push({
1072
+ therapy: 'Clinical trial enrollment',
1073
+ rationale: 'Access to novel agents',
1074
+ expectedBenefit: 8
1075
+ });
1076
+
1077
+ return options;
1078
+ }
1079
+
1080
+ private estimateQoLScore(toxicity: ToxicityPrediction): number {
1081
+ let score = 0.85; // Base quality of life
1082
+
1083
+ // Reduce based on toxicity severity
1084
+ score -= toxicity.grade3PlusRisk * 0.3;
1085
+
1086
+ // Specific high-impact toxicities
1087
+ for (const tox of toxicity.specificRisks) {
1088
+ if (tox.toxicity.toLowerCase().includes('neuropathy') && tox.grade >= 2) {
1089
+ score -= 0.1;
1090
+ }
1091
+ if (tox.toxicity.toLowerCase().includes('fatigue') && tox.grade >= 2) {
1092
+ score -= 0.08;
1093
+ }
1094
+ if (tox.toxicity.toLowerCase().includes('nausea') && tox.grade >= 2) {
1095
+ score -= 0.05;
1096
+ }
1097
+ }
1098
+
1099
+ return Math.max(score, 0.3);
1100
+ }
1101
+
1102
+ private getMatchingBiomarkers(patient: PatientFeatures, treatment: TreatmentFeatures): string[] {
1103
+ const matches: string[] = [];
1104
+
1105
+ for (const alt of patient.genomicAlterations) {
1106
+ if (this.isBiomarkerRelevant(alt.gene, alt.alteration, treatment)) {
1107
+ matches.push(`${alt.gene} ${alt.alteration}`);
1108
+ }
1109
+ }
1110
+
1111
+ if (patient.msiStatus === 'MSI-H' && this.hasImmunotherapyDrug(treatment.drugs)) {
1112
+ matches.push('MSI-H');
1113
+ }
1114
+ if (patient.tmbStatus === 'high' && this.hasImmunotherapyDrug(treatment.drugs)) {
1115
+ matches.push('TMB-High');
1116
+ }
1117
+ if (patient.hrdStatus === 'positive' && treatment.drugs.some(d =>
1118
+ ['olaparib', 'rucaparib', 'niraparib', 'talazoparib'].some(p => d.toLowerCase().includes(p))
1119
+ )) {
1120
+ matches.push('HRD-positive');
1121
+ }
1122
+
1123
+ return matches;
1124
+ }
1125
+
1126
+ private isBiomarkerRelevant(gene: string, alteration: string, treatment: TreatmentFeatures): boolean {
1127
+ const relevantPairs: Record<string, string[]> = {
1128
+ 'EGFR': ['osimertinib', 'erlotinib', 'gefitinib', 'afatinib'],
1129
+ 'ALK': ['alectinib', 'crizotinib', 'brigatinib', 'lorlatinib'],
1130
+ 'ROS1': ['crizotinib', 'entrectinib'],
1131
+ 'BRAF': ['dabrafenib', 'vemurafenib', 'encorafenib'],
1132
+ 'HER2': ['trastuzumab', 'pertuzumab', 't-dxd'],
1133
+ 'BRCA': ['olaparib', 'rucaparib', 'niraparib', 'talazoparib'],
1134
+ 'KRAS': ['sotorasib', 'adagrasib'],
1135
+ 'NTRK': ['larotrectinib', 'entrectinib'],
1136
+ 'RET': ['selpercatinib', 'pralsetinib'],
1137
+ 'MET': ['capmatinib', 'tepotinib']
1138
+ };
1139
+
1140
+ const drugs = relevantPairs[gene.toUpperCase()];
1141
+ if (!drugs) return false;
1142
+
1143
+ return treatment.drugs.some(d => drugs.some(rd => d.toLowerCase().includes(rd)));
1144
+ }
1145
+
1146
+ private checkFDAApproval(treatment: TreatmentFeatures, cancerType: string): boolean {
1147
+ // Simplified FDA approval check - would reference actual database in production
1148
+ const approvedCombinations: Record<string, string[]> = {
1149
+ 'NSCLC': ['osimertinib', 'pembrolizumab', 'alectinib', 'sotorasib'],
1150
+ 'Breast': ['palbociclib', 'trastuzumab', 'olaparib', 'sacituzumab'],
1151
+ 'Melanoma': ['pembrolizumab', 'nivolumab', 'ipilimumab', 'dabrafenib'],
1152
+ 'Colorectal': ['pembrolizumab', 'cetuximab', 'bevacizumab'],
1153
+ 'RCC': ['pembrolizumab', 'nivolumab', 'cabozantinib']
1154
+ };
1155
+
1156
+ const approvedDrugs = approvedCombinations[cancerType] || [];
1157
+ return treatment.drugs.some(d => approvedDrugs.some(ad => d.toLowerCase().includes(ad)));
1158
+ }
1159
+
1160
+ private checkNCCNRecommendation(treatment: TreatmentFeatures, cancerType: string, stage: string): boolean {
1161
+ // Simplified NCCN check - would reference actual guidelines in production
1162
+ return this.checkFDAApproval(treatment, cancerType);
1163
+ }
1164
+
1165
+ private getConsiderations(patient: PatientFeatures, treatment: TreatmentFeatures): string[] {
1166
+ const considerations: string[] = [];
1167
+
1168
+ if (patient.age > 75) {
1169
+ considerations.push('Consider dose reduction for elderly patient');
1170
+ }
1171
+ if (patient.ecogStatus >= 2) {
1172
+ considerations.push('Poor performance status may limit tolerability');
1173
+ }
1174
+ if (patient.organFunction?.renal === 'impaired') {
1175
+ considerations.push('Dose adjustment may be needed for renal impairment');
1176
+ }
1177
+ if (patient.priorLines && patient.priorLines >= 2) {
1178
+ considerations.push('Heavily pretreated - consider clinical trial');
1179
+ }
1180
+
1181
+ return considerations;
1182
+ }
1183
+
1184
+ private generateOverallAssessment(
1185
+ patient: PatientFeatures,
1186
+ treatment: TreatmentFeatures,
1187
+ response: ResponsePrediction,
1188
+ survival: SurvivalPrediction,
1189
+ toxicity: ToxicityPrediction,
1190
+ resistance: ResistancePrediction
1191
+ ): {
1192
+ recommendation: 'strongly-recommended' | 'recommended' | 'consider' | 'caution' | 'not-recommended';
1193
+ rationale: string[];
1194
+ caveats: string[];
1195
+ alternativeOptions: string[];
1196
+ } {
1197
+ const rationale: string[] = [];
1198
+ const caveats: string[] = [];
1199
+ const alternativeOptions: string[] = [];
1200
+
1201
+ // Calculate recommendation score
1202
+ let score = 50;
1203
+
1204
+ // Response contribution
1205
+ if (response.objectiveResponseRate >= 0.6) { score += 20; rationale.push('High expected response rate'); }
1206
+ else if (response.objectiveResponseRate >= 0.4) { score += 10; }
1207
+ else if (response.objectiveResponseRate < 0.2) { score -= 20; caveats.push('Low expected response rate'); }
1208
+
1209
+ // Survival contribution
1210
+ if (survival.pfs.twelveMonth >= 0.6) { score += 15; rationale.push('Favorable survival outlook'); }
1211
+ else if (survival.pfs.twelveMonth < 0.3) { score -= 15; caveats.push('Limited expected duration of benefit'); }
1212
+
1213
+ // Toxicity contribution
1214
+ if (toxicity.grade3PlusRisk < 0.2) { score += 10; rationale.push('Favorable toxicity profile'); }
1215
+ else if (toxicity.grade3PlusRisk > 0.5) { score -= 20; caveats.push('High risk of severe toxicity'); }
1216
+
1217
+ // Biomarker match
1218
+ if (this.hasBiomarkerMatch(patient, treatment)) {
1219
+ score += 15;
1220
+ rationale.push('Biomarker-matched therapy');
1221
+ }
1222
+
1223
+ // FDA approval and guidelines
1224
+ if (this.checkFDAApproval(treatment, patient.cancerType)) {
1225
+ score += 10;
1226
+ rationale.push('FDA approved for indication');
1227
+ }
1228
+
1229
+ // Add alternatives
1230
+ if (resistance.nextLineOptions.length > 0) {
1231
+ alternativeOptions.push(...resistance.nextLineOptions.slice(0, 2).map(o => o.therapy));
1232
+ }
1233
+
1234
+ // Determine recommendation
1235
+ let recommendation: 'strongly-recommended' | 'recommended' | 'consider' | 'caution' | 'not-recommended';
1236
+ if (score >= 80) recommendation = 'strongly-recommended';
1237
+ else if (score >= 60) recommendation = 'recommended';
1238
+ else if (score >= 40) recommendation = 'consider';
1239
+ else if (score >= 20) recommendation = 'caution';
1240
+ else recommendation = 'not-recommended';
1241
+
1242
+ return { recommendation, rationale, caveats, alternativeOptions };
1243
+ }
1244
+
1245
+ private getCacheKey(type: string, patient: PatientFeatures, treatment: TreatmentFeatures): string {
1246
+ const data = JSON.stringify({ type, patient, treatment });
1247
+ return createHash('sha256').update(data).digest('hex').substring(0, 16);
1248
+ }
1249
+
1250
+ private getFromCache<T>(key: string): T | null {
1251
+ const cached = this.predictionCache.get(key);
1252
+ if (cached && Date.now() - cached.timestamp.getTime() < this.cacheDuration) {
1253
+ return cached.prediction as T;
1254
+ }
1255
+ return null;
1256
+ }
1257
+
1258
+ private setCache(key: string, prediction: any): void {
1259
+ this.predictionCache.set(key, { prediction, timestamp: new Date() });
1260
+ }
1261
+
1262
+ /**
1263
+ * Register a new model
1264
+ */
1265
+ registerModel(model: MLModel): void {
1266
+ this.modelRegistry.set(model.id, model);
1267
+ this.emit('model-registered', model);
1268
+ }
1269
+
1270
+ /**
1271
+ * Get model performance metrics
1272
+ */
1273
+ getModelMetrics(modelId: string): MLModel['performance'] | undefined {
1274
+ return this.modelRegistry.get(modelId)?.performance;
1275
+ }
1276
+
1277
+ /**
1278
+ * List all available models
1279
+ */
1280
+ listModels(): MLModel[] {
1281
+ return Array.from(this.modelRegistry.values());
1282
+ }
1283
+ }
1284
+
1285
+ // ═══════════════════════════════════════════════════════════════════════════════
1286
+ // FEATURE ENCODER
1287
+ // ═══════════════════════════════════════════════════════════════════════════════
1288
+
1289
+ class FeatureEncoder {
1290
+ private mapping: Record<string, number>;
1291
+
1292
+ constructor(mapping: Record<string, number>) {
1293
+ this.mapping = mapping;
1294
+ }
1295
+
1296
+ encode(value: string): number {
1297
+ return this.mapping[value] ?? 0;
1298
+ }
1299
+ }
1300
+
1301
+ export default OutcomePredictorService;