Harrizi Saad commited on
Commit
01aa9f2
·
verified ·
1 Parent(s): f82e005

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +395 -639
app.py CHANGED
@@ -14,19 +14,19 @@ st.set_page_config(
14
  initial_sidebar_state="collapsed"
15
  )
16
 
17
- # Beautiful Professional CSS with animations
18
  st.markdown("""
19
  <style>
20
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&display=swap');
21
-
22
  * {
23
  font-family: 'Inter', sans-serif;
24
  }
25
-
26
  .main {
27
  background: linear-gradient(180deg, #0a0e27 0%, #1a1f3a 50%, #0a0e27 100%);
28
  }
29
-
30
  .main-header {
31
  font-size: 4rem;
32
  font-weight: 900;
@@ -37,21 +37,71 @@ st.markdown("""
37
  margin-bottom: 0.5rem;
38
  animation: glow 3s ease-in-out infinite alternate;
39
  }
40
-
41
  @keyframes glow {
42
  from { filter: drop-shadow(0 0 20px rgba(102, 126, 234, 0.3)); }
43
  to { filter: drop-shadow(0 0 40px rgba(118, 75, 162, 0.6)); }
44
  }
45
-
46
  .sub-header {
47
  font-size: 1.4rem;
48
  text-align: center;
49
  color: #a0a6b8;
50
  margin-bottom: 3rem;
51
  font-weight: 300;
52
- letter-spacing: 0.5px;
53
  }
54
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  .risk-card-low {
56
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
57
  padding: 3rem;
@@ -61,12 +111,12 @@ st.markdown("""
61
  box-shadow: 0 20px 60px rgba(102, 126, 234, 0.5);
62
  animation: pulse-low 2s ease-in-out infinite;
63
  }
64
-
65
  @keyframes pulse-low {
66
  0%, 100% { transform: scale(1); }
67
- 50% { transform: scale(1.02); }
68
  }
69
-
70
  .risk-card-moderate {
71
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
72
  padding: 3rem;
@@ -76,12 +126,12 @@ st.markdown("""
76
  box-shadow: 0 20px 60px rgba(240, 147, 251, 0.5);
77
  animation: pulse-moderate 1.5s ease-in-out infinite;
78
  }
79
-
80
  @keyframes pulse-moderate {
81
  0%, 100% { transform: scale(1); }
82
  50% { transform: scale(1.03); }
83
  }
84
-
85
  .risk-card-high {
86
  background: linear-gradient(135deg, #ff0844 0%, #ffb199 100%);
87
  padding: 3rem;
@@ -91,12 +141,12 @@ st.markdown("""
91
  box-shadow: 0 20px 60px rgba(255, 8, 68, 0.6);
92
  animation: pulse-high 1s ease-in-out infinite;
93
  }
94
-
95
  @keyframes pulse-high {
96
  0%, 100% { transform: scale(1); }
97
  50% { transform: scale(1.05); }
98
  }
99
-
100
  .property-card {
101
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
102
  border: 2px solid rgba(102, 126, 234, 0.3);
@@ -104,40 +154,14 @@ st.markdown("""
104
  border-radius: 20px;
105
  margin: 1rem 0;
106
  transition: all 0.3s ease;
107
- position: relative;
108
- overflow: hidden;
109
- }
110
-
111
- .property-card::before {
112
- content: '';
113
- position: absolute;
114
- top: 0;
115
- left: -100%;
116
- width: 100%;
117
- height: 100%;
118
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
119
- transition: left 0.5s;
120
- }
121
-
122
- .property-card:hover::before {
123
- left: 100%;
124
  }
125
-
126
  .property-card:hover {
127
  transform: translateY(-5px);
128
  border-color: rgba(102, 126, 234, 0.6);
129
  box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
130
  }
131
-
132
- .property-label {
133
- color: #a0a6b8;
134
- font-size: 0.85rem;
135
- font-weight: 600;
136
- text-transform: uppercase;
137
- letter-spacing: 1.5px;
138
- margin-bottom: 0.5rem;
139
- }
140
-
141
  .property-value {
142
  color: #ffffff;
143
  font-size: 2rem;
@@ -146,15 +170,7 @@ st.markdown("""
146
  -webkit-background-clip: text;
147
  -webkit-text-fill-color: transparent;
148
  }
149
-
150
- .property-interpretation {
151
- color: #8b92a8;
152
- font-size: 0.9rem;
153
- margin-top: 0.5rem;
154
- font-style: italic;
155
- line-height: 1.6;
156
- }
157
-
158
  .mechanism-card {
159
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
160
  border: 2px solid rgba(102, 126, 234, 0.2);
@@ -162,151 +178,208 @@ st.markdown("""
162
  border-radius: 20px;
163
  text-align: center;
164
  transition: all 0.4s ease;
165
- position: relative;
166
  }
167
-
168
  .mechanism-card:hover {
169
  transform: translateY(-10px) scale(1.05);
170
  box-shadow: 0 20px 50px rgba(102, 126, 234, 0.4);
171
- border-color: rgba(102, 126, 234, 0.8);
172
- }
173
-
174
- .mechanism-icon {
175
- font-size: 3rem;
176
- margin-bottom: 1rem;
177
- filter: drop-shadow(0 0 10px rgba(102, 126, 234, 0.5));
178
- }
179
-
180
- .mechanism-title {
181
- font-size: 1rem;
182
- color: #a0a6b8;
183
- font-weight: 600;
184
- text-transform: uppercase;
185
- letter-spacing: 1px;
186
- margin-bottom: 1rem;
187
  }
188
-
189
  .mechanism-value {
190
  font-size: 3rem;
191
  font-weight: 900;
192
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
193
  -webkit-background-clip: text;
194
  -webkit-text-fill-color: transparent;
195
- margin-bottom: 0.5rem;
196
  }
197
-
198
- .mechanism-bar {
199
- width: 100%;
200
- height: 8px;
201
- background: rgba(255, 255, 255, 0.1);
202
- border-radius: 10px;
203
- overflow: hidden;
204
- margin-top: 1rem;
205
- }
206
-
207
- .mechanism-bar-fill {
208
- height: 100%;
209
- background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
210
- border-radius: 10px;
211
- transition: width 1s ease;
212
- }
213
-
214
- .interpretation-box {
215
- background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
216
- border-left: 5px solid #667eea;
217
- padding: 2.5rem;
218
- border-radius: 20px;
219
- margin: 2rem 0;
220
- color: #e8eaf0;
221
- line-height: 2;
222
- font-size: 1.05rem;
223
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
224
- }
225
-
226
- .interpretation-section {
227
- margin: 1.5rem 0;
228
- padding-left: 1.5rem;
229
- border-left: 3px solid rgba(102, 126, 234, 0.3);
230
- }
231
-
232
- .interpretation-title {
233
- color: #667eea;
234
- font-size: 1.3rem;
235
- font-weight: 700;
236
- margin-bottom: 1rem;
237
- display: flex;
238
- align-items: center;
239
- }
240
-
241
- .interpretation-icon {
242
- margin-right: 0.5rem;
243
- font-size: 1.5rem;
244
- }
245
-
246
  .structure-container {
247
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
248
  border: 2px solid rgba(102, 126, 234, 0.3);
249
  padding: 2rem;
250
  border-radius: 25px;
251
  text-align: center;
252
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
253
  }
254
-
255
- .cascade-box {
256
- background: linear-gradient(135deg, rgba(255, 8, 68, 0.1) 0%, rgba(255, 177, 153, 0.1) 100%);
257
- border: 2px solid rgba(255, 8, 68, 0.3);
258
- padding: 2rem;
259
  border-radius: 20px;
260
  margin: 2rem 0;
261
- color: #ffb199;
262
- }
263
-
264
- .cascade-step {
265
- display: flex;
266
- align-items: center;
267
- margin: 1rem 0;
268
- font-size: 1.1rem;
269
- font-weight: 600;
270
- }
271
-
272
- .cascade-arrow {
273
- color: #ff0844;
274
- font-size: 2rem;
275
- margin: 0 1rem;
276
- }
277
-
278
- .insight-badge {
279
- display: inline-block;
280
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
281
- color: white;
282
- padding: 0.5rem 1rem;
283
- border-radius: 20px;
284
- font-size: 0.9rem;
285
- font-weight: 600;
286
- margin: 0.5rem 0.5rem 0.5rem 0;
287
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
288
- }
289
-
290
- .warning-box {
291
- background: linear-gradient(135deg, rgba(255, 177, 153, 0.15) 0%, rgba(255, 8, 68, 0.15) 100%);
292
- border: 2px solid rgba(255, 8, 68, 0.5);
293
- border-radius: 20px;
294
- padding: 2rem;
295
- margin: 1.5rem 0;
296
- color: #ffb199;
297
- }
298
-
299
- .safe-box {
300
- background: linear-gradient(135deg, rgba(102, 234, 170, 0.15) 0%, rgba(102, 126, 234, 0.15) 100%);
301
- border: 2px solid rgba(102, 234, 170, 0.5);
302
- border-radius: 20px;
303
- padding: 2rem;
304
- margin: 1.5rem 0;
305
- color: #66eaaa;
306
  }
307
  </style>
308
  """, unsafe_allow_html=True)
309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  @st.cache_resource
311
  def load_models():
312
  """Load models"""
@@ -324,243 +397,13 @@ def load_models():
324
  st.error(f"Error: {str(e)}")
325
  return None
326
 
327
- def get_property_interpretation(prop_name, value, mol):
328
- """Deep interpretation of each property"""
329
- interpretations = {
330
- 'MW': {
331
- 'value': value,
332
- 'status': '✅ Optimal' if 250 <= value <= 400 else ('⚠️ High' if value > 400 else '⚠️ Low'),
333
- 'meaning': f"""
334
- **What it means:** Molecular weight of {value:.1f} Da.
335
- {
336
- 'Perfect size for cellular uptake and distribution. Like a key fitting a lock.' if 250 <= value <= 400 else
337
- f'Large molecules (>400 Da) accumulate in cells like cargo ships too big for the harbor. They burden protein degradation systems, leading to cellular stress. Your compound is {value-400:.0f} Da above the safe threshold.' if value > 400 else
338
- 'Very small molecules may lack specificity and be rapidly cleared.'
339
- }
340
- """
341
- },
342
- 'LogP': {
343
- 'value': value,
344
- 'status': '✅ Optimal' if 0.5 <= value <= 2.5 else ('🔴 Very High' if value > 4 else ('⚠️ High' if value > 2.5 else '⚠️ Low')),
345
- 'meaning': f"""
346
- **What it means:** LogP of {value:.2f} (lipophilicity = fat-loving tendency).
347
- {
348
- 'Perfect balance! Can cross membranes but won't get trapped. Like a passport that works everywhere.' if 0.5 <= value <= 2.5 else
349
- f'🚨 CRITICAL: LogP 4-6 is the "danger zone"! Your compound ({value:.2f}) will accumulate 3-5x in mitochondria compared to cytoplasm. Think of it like oil droplets concentrating in the engine - they disrupt the machinery. This causes membrane potential collapse and triggers the entire toxicity cascade.' if 4 <= value <= 6 else
350
- f'High lipophilicity ({value:.2f}) means your compound loves fat more than water. It will get trapped in membranes and mitochondria, unable to escape. This is why many drugs fail - they become "membrane prisoners".' if value > 2.5 else
351
- 'Too water-loving. May have poor membrane permeability.'
352
- }
353
- """
354
- },
355
- 'TPSA': {
356
- 'value': value,
357
- 'status': '✅ Optimal' if 50 <= value <= 90 else ('⚠️ Outside optimal' if 40 <= value <= 140 else '🔴 Problematic'),
358
- 'meaning': f"""
359
- **What it means:** {value:.1f} Ų of polar surface area.
360
- {
361
- 'Goldilocks zone! Can cross membranes AND reach intracellular targets.' if 50 <= value <= 90 else
362
- f'TPSA of {value:.1f} is outside the sweet spot (50-90 Ų). ' + (
363
- 'Too polar - struggles to cross lipid membranes. Like trying to push a water balloon through oil.' if value > 140 else
364
- f'In the accessible range (40-140 Ų) where compounds can reach cytoplasmic stress pathways. This is why we see ARE activation at this TPSA.' if 40 <= value <= 140 else
365
- 'Very low polarity - will partition heavily into membranes, potentially disrupting them.'
366
- )
367
- }
368
- """
369
- },
370
- 'AromaticRings': {
371
- 'value': int(value),
372
- 'status': '✅ Safe' if value <= 2 else ('⚠️ Moderate' if value == 3 else '🔴 High Risk'),
373
- 'meaning': f"""
374
- **What it means:** {int(value)} aromatic ring(s) - flat, electron-rich structures.
375
- {
376
- 'Low aromatic content = lower toxicity risk. Aromatic rings are like flat plates that can slide between biological membranes.' if value <= 2 else
377
- f'🚨 WARNING: {int(value)} aromatic rings! Our data shows ≥3 rings cause 3.5x higher mitochondrial toxicity. Why? Aromatic systems can π-stack (like plates stacking) with membrane lipids and intercalate into DNA. They physically disrupt the delicate architecture of mitochondrial cristae where ATP is made. Think of it like putting cardboard sheets between the pages of a book - it disrupts the structure.' if value >= 3 else
378
- 'Moderate aromatic content. Monitor for membrane interactions.'
379
- }
380
- """
381
- }
382
- }
383
-
384
- return interpretations.get(prop_name, {'value': value, 'status': 'N/A', 'meaning': ''})
385
-
386
- def generate_deep_interpretation(result, props):
387
- """Generate deep, mechanistic interpretation"""
388
- overall = result['overall_toxicity']
389
- prob_are = result['oxidative_stress']['probability']
390
- prob_mmp = result['mitochondrial_dysfunction']['probability']
391
- prob_p53 = result['dna_damage']['probability']
392
-
393
- # Determine primary mechanism
394
- mechanisms = [
395
- ('Mitochondrial Dysfunction', prob_mmp, '⚡'),
396
- ('Oxidative Stress', prob_are, '🔥'),
397
- ('DNA Damage', prob_p53, '🧬')
398
- ]
399
- mechanisms.sort(key=lambda x: x[1], reverse=True)
400
- primary_mech = mechanisms[0]
401
-
402
- if overall['risk_level'] == 'LOW':
403
- return f"""
404
- <div class="safe-box">
405
- <div class="interpretation-title">
406
- <span class="interpretation-icon">✅</span>
407
- Safe Chemical Space
408
- </div>
409
-
410
- <p><strong>Your compound sits in the safe zone.</strong> It avoids the major toxicity triggers we identified in 11,306 compounds.</p>
411
-
412
- <div class="interpretation-section">
413
- <strong>Why it's safe:</strong>
414
- <ul>
415
- <li>{'✅ Optimal molecular weight (250-400 Da) - perfect for cellular handling' if 250 <= props['MW'] <= 400 else '✅ Manageable size'}</li>
416
- <li>{'✅ Balanced lipophilicity (LogP 0.5-2.5) - can travel without getting trapped' if 0.5 <= props['LogP'] <= 2.5 else '✅ Acceptable lipophilicity'}</li>
417
- <li>{'✅ Low aromatic content (≤2 rings) - won't disrupt membranes' if props['AromaticRings'] <= 2 else '✅ Moderate aromatic content'}</li>
418
- </ul>
419
- </div>
420
-
421
- <p><strong>Next steps:</strong> This computational prediction is promising, but remember - even "safe" compounds need experimental validation. Test in relevant cell lines, check for off-target effects, and validate the therapeutic window.</p>
422
- </div>
423
- """
424
-
425
- elif overall['risk_level'] == 'MODERATE':
426
- return f"""
427
- <div class="interpretation-box">
428
- <div class="interpretation-title">
429
- <span class="interpretation-icon">⚠️</span>
430
- Moderate Concerns: Mechanistic Analysis
431
- </div>
432
-
433
- <p><strong>Primary concern: {primary_mech[0]} ({primary_mech[1]:.0%})</strong></p>
434
-
435
- <div class="interpretation-section">
436
- <strong>🔬 What's happening at the molecular level:</strong>
437
- <br><br>
438
- {
439
- f"Your compound's LogP of {props['LogP']:.2f} suggests moderate membrane partitioning. While not in the critical 4-6 range, it may still accumulate in lipid-rich compartments over time. Combined with {int(props['AromaticRings'])} aromatic rings, there's potential for membrane perturbation." if prob_mmp > prob_are and prob_mmp > prob_p53 else
440
- f"Oxidative stress activation ({prob_are:.0%}) suggests your compound can generate or is susceptible to ROS. With {int(props['Heteroatoms'])} heteroatoms, there may be redox-active centers that cycle between oxidized/reduced states, producing superoxide as a byproduct." if prob_are > prob_mmp and prob_are > prob_p53 else
441
- f"DNA damage signaling ({prob_p53:.0%}) with {int(props['RotatableBonds'])} rotatable bonds suggests potential for DNA intercalation or formation of reactive intermediates that alkylate DNA bases."
442
- }
443
- </div>
444
-
445
- <div class="interpretation-section">
446
- <strong>💡 Medicinal chemistry strategies:</strong>
447
- <ul>
448
- {f'<li>Reduce LogP: Add polar groups (OH, NH2, carboxylic acid) to decrease membrane accumulation</li>' if props['LogP'] > 3 else ''}
449
- {f'<li>Reduce aromatic content: Replace one aromatic ring with a saturated heterocycle (piperidine, tetrahydropyran)</li>' if props['AromaticRings'] >= 3 else ''}
450
- {f'<li>Reduce molecular weight: Remove non-essential substituents. Each 50 Da reduction decreases toxicity risk</li>' if props['MW'] > 400 else ''}
451
- {f'<li>Add rigidity: Reduce rotatable bonds with cyclic constraints to prevent DNA intercalation</li>' if props['RotatableBonds'] > 7 else ''}
452
- <li>Consider prodrug strategy: Mask toxic features until metabolic activation at target site</li>
453
- </ul>
454
- </div>
455
-
456
- <p><strong>Clinical perspective:</strong> Moderate-risk compounds can sometimes be developed successfully if the therapeutic index is favorable. Focus on: (1) Identifying the therapeutic window, (2) Optimizing PK to minimize tissue accumulation, (3) Considering alternate dosing regimens.</p>
457
- </div>
458
- """
459
-
460
- else: # HIGH RISK
461
- # Identify the cascade
462
- cascade_active = []
463
- if prob_mmp > 0.6:
464
- cascade_active.append('Mitochondrial Damage')
465
- if prob_are > 0.6:
466
- cascade_active.append('Oxidative Stress')
467
- if prob_p53 > 0.6:
468
- cascade_active.append('DNA Damage')
469
-
470
- is_full_cascade = len(cascade_active) >= 2
471
-
472
- # Build the cascade content separately
473
- cascade_html = ""
474
- if is_full_cascade:
475
- cascade_html = f"""
476
- <div class="cascade-box">
477
- <p><strong>⚠️ FULL TOXICITY CASCADE DETECTED</strong></p>
478
- <p>Your compound triggers multiple mechanisms in sequence, exactly as our model predicted:</p>
479
- <div class="cascade-step">
480
- <span>Lipophilic Compound (LogP: {props["LogP"]:.2f})</span>
481
- <span class="cascade-arrow">→</span>
482
- <span>Mitochondrial Accumulation</span>
483
- </div>
484
- <div class="cascade-step">
485
- <span class="cascade-arrow">→</span>
486
- <span>Membrane Disruption (MMP: {prob_mmp:.0%})</span>
487
- </div>
488
- <div class="cascade-step">
489
- <span class="cascade-arrow">→</span>
490
- <span>ROS Production</span>
491
- </div>
492
- <div class="cascade-step">
493
- <span class="cascade-arrow">→</span>
494
- <span>Oxidative Stress (ARE: {prob_are:.0%})</span>
495
- </div>
496
- <div class="cascade-step">
497
- <span class="cascade-arrow">→</span>
498
- <span>DNA Damage (p53: {prob_p53:.0%})</span>
499
- </div>
500
- <p>This is the signature of compounds that cause systemic cellular failure.</p>
501
- </div>
502
- """
503
- else:
504
- cascade_html = f"<p><strong>Primary mechanism: {primary_mech[0]} at {primary_mech[1]:.0%}</strong></p>"
505
-
506
- return f"""
507
- <div class="warning-box">
508
- <div class="interpretation-title">
509
- <span class="interpretation-icon">🔴</span>
510
- High Toxicity Risk: The Complete Mechanistic Story
511
- </div>
512
-
513
- {cascade_html}
514
-
515
- <div class="interpretation-section">
516
- <strong>🔬 Root causes identified:</strong>
517
- <ul>
518
- {f'<li><strong>Critical LogP ({props["LogP"]:.2f}):</strong> In the 4-6 "danger zone". This causes 3-5x mitochondrial accumulation. Your compound will concentrate in the powerhouse of the cell and disrupt electron transport. LogP 4-6 compounds show 10x higher toxicity in our dataset.</li>' if 4 <= props['LogP'] <= 6 else ''}
519
- {f'<li><strong>High LogP ({props["LogP"]:.2f}):</strong> Extremely lipophilic. Will partition heavily into membranes and organelles, unable to distribute normally.</li>' if props['LogP'] > 6 else ''}
520
- {f'<li><strong>Multiple aromatic rings ({int(props["AromaticRings"])}):</strong> Each ring is a flat, electron-rich plate. ≥3 rings can stack (π-π interactions) with membrane lipids and intercalate between DNA bases. Our data: 3.5x higher mitochondrial toxicity with ≥3 rings.</li>' if props['AromaticRings'] >= 3 else ''}
521
- {f'<li><strong>Large molecular weight ({props["MW"]:.0f} Da):</strong> Exceeds the 400 Da safety threshold by {props["MW"]-400:.0f} Da. Large molecules accumulate because cellular machinery can't efficiently process them. They burden proteasomes and autophagy systems.</li>' if props['MW'] > 400 else ''}
522
- {f'<li><strong>High flexibility ({int(props["RotatableBonds"])} rotatable bonds):</strong> Can adopt conformations that fit between DNA base pairs, causing intercalation and strand breaks.</li>' if props['RotatableBonds'] > 7 else ''}
523
- {f'<li><strong>Heteroatom-rich ({int(props["Heteroatoms"])} heteroatoms):</strong> N, O, S atoms can undergo redox cycling, generating superoxide (O2•−) and other ROS. This is metabolic activation of toxicity.</li>' if props['Heteroatoms'] > 7 else ''}
524
- </ul>
525
- </div>
526
-
527
- <div class="interpretation-section">
528
- <strong>⚠️ Why this matters clinically:</strong>
529
- <p>Compounds with this profile typically:</p>
530
- <ul>
531
- <li>Show hepatotoxicity in preclinical models (liver has high mitochondrial density)</li>
532
- <li>Cause cardiotoxicity (heart relies 100% on mitochondrial ATP)</li>
533
- <li>Trigger idiosyncratic drug reactions (immune system recognizes damaged cells)</li>
534
- <li>Fail Phase I/II trials due to dose-limiting toxicities</li>
535
- <li>May carry black box warnings if approved (e.g., mitochondrial toxins like linezolid)</li>
536
- </ul>
537
- </div>
538
-
539
- <div class="interpretation-section">
540
- <strong>🔧 Rescue strategies (if therapeutic target is compelling):</strong>
541
- <ol>
542
- <li><strong>Dramatic LogP reduction:</strong> Target LogP <3. Add multiple polar groups. Consider zwitterions.</li>
543
- <li><strong>De-aromatization:</strong> Replace aromatic rings with saturated rings (cyclohexane, piperidine). Breaks π-stacking.</li>
544
- <li><strong>Size reduction:</strong> Remove ALL non-essential atoms. Target MW <400 Da. Use fragment-based approach.</li>
545
- <li><strong>Prodrug masking:</strong> Hide toxic features until enzymatic activation at target site. Converts systemic toxin into local therapeutic.</li>
546
- <li><strong>Targeted delivery:</strong> Nanoparticle, antibody-drug conjugate, or cell-penetrating peptide to restrict distribution.</li>
547
- </ol>
548
- </div>
549
-
550
- <p><strong>Honest assessment:</strong> {
551
- 'This compound would likely fail preclinical safety studies. Unless the therapeutic target is unprecedented (e.g., treating a fatal disease with no alternatives), recommend exploring alternative chemical series.' if is_full_cascade else
552
- 'This compound shows significant safety concerns. Consider whether the therapeutic benefit could justify the risk, or if alternative approaches exist.'
553
- }</p>
554
- </div>
555
- """
556
-
557
  def compute_features(smiles, feature_names):
558
  """Compute features"""
559
  try:
560
  mol = Chem.MolFromSmiles(smiles)
561
  if mol is None:
562
  return None, "Invalid SMILES", None
563
-
564
  features = {
565
  'MW': Descriptors.MolWt(mol),
566
  'LogP': Descriptors.MolLogP(mol),
@@ -586,10 +429,10 @@ def compute_features(smiles, feature_names):
586
  'NumAliphaticRings': Lipinski.NumAliphaticRings(mol),
587
  'FractionCsp3': Descriptors.FractionCsp3(mol) if hasattr(Descriptors, 'FractionCsp3') else 0.0,
588
  }
589
-
590
  fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)
591
  fp_array = np.array(fp)
592
-
593
  feature_vector = []
594
  for fname in feature_names:
595
  if fname.startswith('Morgan_'):
@@ -597,30 +440,30 @@ def compute_features(smiles, feature_names):
597
  feature_vector.append(fp_array[bit_idx])
598
  else:
599
  feature_vector.append(features.get(fname, 0))
600
-
601
  return np.array(feature_vector).reshape(1, -1), None, features
602
-
603
  except Exception as e:
604
  return None, f"Error: {str(e)}", None
605
 
606
  def predict_toxicity(smiles, models):
607
  """Predict toxicity"""
608
  X, error, raw_features = compute_features(smiles, models['feature_names'])
609
-
610
  if error:
611
  return {'error': error}
612
-
613
  try:
614
  X_are = models['scaler_are'].transform(X)
615
  X_mmp = models['scaler_mmp'].transform(X)
616
  X_p53 = models['scaler_p53'].transform(X)
617
-
618
  prob_are = float(models['model_are'].predict_proba(X_are)[0, 1])
619
  prob_mmp = float(models['model_mmp'].predict_proba(X_mmp)[0, 1])
620
  prob_p53 = float(models['model_p53'].predict_proba(X_p53)[0, 1])
621
-
622
  overall_prob = max(prob_are, prob_mmp, prob_p53)
623
-
624
  if overall_prob < 0.35:
625
  risk = "LOW"
626
  prediction = "NON-TOXIC"
@@ -630,7 +473,7 @@ def predict_toxicity(smiles, models):
630
  else:
631
  risk = "HIGH"
632
  prediction = "TOXIC"
633
-
634
  return {
635
  'overall_toxicity': {
636
  'prediction': prediction,
@@ -652,50 +495,50 @@ if models is None:
652
 
653
  # Header
654
  st.markdown('<p class="main-header">🧪 Multi-Endpoint Toxicity Predictor</p>', unsafe_allow_html=True)
655
- st.markdown('<p class="sub-header">Deep mechanistic insights into drug toxicity • Trained on 11,306 compounds</p>', unsafe_allow_html=True)
656
 
657
  # Tabs
658
- tab1, tab2, tab3 = st.tabs(["🔮 Predict", "🔬 Science", "📖 About"])
659
 
660
  with tab1:
661
- st.markdown("### Enter SMILES")
662
-
663
  smiles = st.text_input(
664
  "SMILES:",
665
  placeholder="e.g., CC(=O)Oc1ccccc1C(=O)O",
666
  label_visibility="collapsed"
667
  )
668
-
669
  st.markdown("**Examples:**")
670
-
671
  examples = {
672
  "Aspirin (Safe)": "CC(=O)Oc1ccccc1C(=O)O",
673
- "Caffeine (Safe)": "CN1C=NC2=C1C(=O)N(C(=O)N2C)C",
674
  "Doxorubicin (Toxic)": "COc1cccc2c1C(=O)c1c(O)c3c(c(O)c1C2=O)C[C@@](O)(C(=O)CO)C[C@@H]3O[C@H]1C[C@H](N)[C@H](O)[C@H](C)O1",
675
- "Tamoxifen (Toxic)": "CCC(=C(c1ccccc1)c1ccc(OCCN(C)C)cc1)c1ccccc1"
 
676
  }
677
-
678
  for name, smi in examples.items():
679
  st.code(smi, language=None)
680
  st.caption(name)
681
-
682
- if st.button("🔮 Analyze Compound", type="primary", use_container_width=True):
683
  if not smiles:
684
  st.warning("⚠️ Please enter a SMILES string")
685
  else:
686
  mol = Chem.MolFromSmiles(smiles)
687
-
688
  if mol is None:
689
  st.error("❌ Invalid SMILES")
690
  else:
691
- with st.spinner("🔬 Deep analysis in progress..."):
692
  result = predict_toxicity(smiles, models)
693
-
694
  if 'error' in result:
695
  st.error(f"❌ {result['error']}")
696
  else:
697
  st.markdown("---")
698
-
699
  # Structure
700
  st.markdown("### 🧬 Molecular Structure")
701
  col_struct = st.columns([1, 2, 1])
@@ -704,234 +547,147 @@ with tab1:
704
  img = Draw.MolToImage(mol, size=(500, 500))
705
  st.image(img, use_column_width=True)
706
  st.markdown('</div>', unsafe_allow_html=True)
707
-
708
  st.markdown("---")
709
-
710
- # Overall prediction
711
  overall = result['overall_toxicity']
712
- st.markdown("### 🎯 Overall Toxicity Assessment")
713
-
714
- col_risk = st.columns([1, 2, 1])
715
- with col_risk[1]:
716
- if overall['risk_level'] == 'LOW':
717
- st.markdown(f"""
718
- <div class="risk-card-low">
719
- <div style="font-size: 4rem;">✅</div>
720
- <div style="font-size: 2.5rem; font-weight: 900; margin: 1rem 0;">{overall['prediction']}</div>
721
- <div style="font-size: 1.3rem; opacity: 0.9;">Risk Level: {overall['risk_level']}</div>
722
- <div style="font-size: 3rem; font-weight: 900; margin-top: 1.5rem;">{overall['probability']:.1%}</div>
723
- <div style="font-size: 1rem; opacity: 0.8; margin-top: 0.5rem;">Confidence Score</div>
724
- </div>
725
- """, unsafe_allow_html=True)
726
- elif overall['risk_level'] == 'MODERATE':
727
- st.markdown(f"""
728
- <div class="risk-card-moderate">
729
- <div style="font-size: 4rem;">⚠️</div>
730
- <div style="font-size: 2.5rem; font-weight: 900; margin: 1rem 0;">{overall['prediction']}</div>
731
- <div style="font-size: 1.3rem; opacity: 0.9;">Risk Level: {overall['risk_level']}</div>
732
- <div style="font-size: 3rem; font-weight: 900; margin-top: 1.5rem;">{overall['probability']:.1%}</div>
733
- <div style="font-size: 1rem; opacity: 0.8; margin-top: 0.5rem;">Confidence Score</div>
734
- </div>
735
- """, unsafe_allow_html=True)
736
- else:
737
- st.markdown(f"""
738
- <div class="risk-card-high">
739
- <div style="font-size: 4rem;">🔴</div>
740
- <div style="font-size: 2.5rem; font-weight: 900; margin: 1rem 0;">{overall['prediction']}</div>
741
- <div style="font-size: 1.3rem; opacity: 0.9;">Risk Level: {overall['risk_level']}</div>
742
- <div style="font-size: 3rem; font-weight: 900; margin-top: 1.5rem;">{overall['probability']:.1%}</div>
743
- <div style="font-size: 1rem; opacity: 0.8; margin-top: 0.5rem;">Confidence Score</div>
744
- </div>
745
- """, unsafe_allow_html=True)
746
-
747
- st.markdown("---")
748
-
749
- # Mechanism breakdown
750
- st.markdown("### 📊 Mechanism-Specific Analysis")
751
-
752
  prob_are = result['oxidative_stress']['probability']
753
  prob_mmp = result['mitochondrial_dysfunction']['probability']
754
  prob_p53 = result['dna_damage']['probability']
755
-
756
- col1, col2, col3 = st.columns(3)
757
-
758
  with col1:
759
- st.markdown(f"""
760
- <div class="mechanism-card">
761
- <div class="mechanism-icon">🔥</div>
762
- <div class="mechanism-title">Oxidative Stress</div>
763
- <div class="mechanism-value">{prob_are:.0%}</div>
764
- <div style="color: #a0a6b8; font-size: 0.9rem;">ARE/Nrf2 Activation</div>
765
- <div class="mechanism-bar">
766
- <div class="mechanism-bar-fill" style="width: {prob_are*100}%"></div>
767
- </div>
768
- </div>
769
- """, unsafe_allow_html=True)
770
-
771
  with col2:
772
- st.markdown(f"""
773
- <div class="mechanism-card">
774
- <div class="mechanism-icon">⚡</div>
775
- <div class="mechanism-title">Mitochondrial</div>
776
- <div class="mechanism-value">{prob_mmp:.0%}</div>
777
- <div style="color: #a0a6b8; font-size: 0.9rem;">Membrane Potential Loss</div>
778
- <div class="mechanism-bar">
779
- <div class="mechanism-bar-fill" style="width: {prob_mmp*100}%"></div>
780
- </div>
781
- </div>
782
- """, unsafe_allow_html=True)
783
-
784
  with col3:
785
- st.markdown(f"""
786
- <div class="mechanism-card">
787
- <div class="mechanism-icon">🧬</div>
788
- <div class="mechanism-title">DNA Damage</div>
789
- <div class="mechanism-value">{prob_p53:.0%}</div>
790
- <div style="color: #a0a6b8; font-size: 0.9rem;">p53 Pathway Activation</div>
791
- <div class="mechanism-bar">
792
- <div class="mechanism-bar-fill" style="width: {prob_p53*100}%"></div>
793
- </div>
794
- </div>
795
- """, unsafe_allow_html=True)
796
-
797
  st.markdown("---")
798
-
799
- # Properties with deep interpretation
800
- st.markdown("### 🔬 Molecular Property Analysis")
801
-
802
  props = result['molecular_properties']
803
-
804
- key_props = ['MW', 'LogP', 'TPSA', 'AromaticRings']
805
-
806
- for prop in key_props:
807
- interp = get_property_interpretation(prop, props[prop], mol)
808
  st.markdown(f"""
809
- <div class="property-card">
810
- <div class="property-label">{prop.replace('_', ' ').title()}</div>
811
- <div class="property-value">{interp['value'] if isinstance(interp['value'], int) else f"{interp['value']:.2f}"}</div>
812
- <div style="margin: 0.5rem 0;">
813
- <span class="insight-badge">{interp['status']}</span>
 
 
 
 
 
 
 
 
 
 
 
 
814
  </div>
815
- <div class="property-interpretation">{interp['meaning']}</div>
816
- </div>
817
- """, unsafe_allow_html=True)
818
-
819
- # Deep interpretation
820
- st.markdown("### 💡 Deep Mechanistic Interpretation")
821
- st.markdown(generate_deep_interpretation(result, props), unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
822
 
823
  with tab2:
824
- st.markdown("### 🔬 The Science Behind the Predictions")
825
-
826
- st.markdown("""
827
- ## The Toxicity Cascade
828
-
829
- Our analysis of 11,306 compounds revealed that drug toxicity follows a predictable cascade through cellular compartments:
830
- """)
831
-
832
- st.markdown("""
833
- <div class="cascade-box">
834
- <div class="cascade-step">
835
- <span>🧪 Lipophilic Compound (LogP 4-6)</span>
836
- <span class="cascade-arrow">→</span>
837
- <span>Passive diffusion into mitochondria</span>
838
- </div>
839
- <div class="cascade-step">
840
- <span class="cascade-arrow">→</span>
841
- <span>⚡ Membrane Disruption</span>
842
- <span class="cascade-arrow">→</span>
843
- <span>3-5x accumulation in cristae</span>
844
- </div>
845
- <div class="cascade-step">
846
- <span class="cascade-arrow">→</span>
847
- <span>💥 Electron Transport Chain Inhibition</span>
848
- <span class="cascade-arrow">→</span>
849
- <span>Electrons leak</span>
850
- </div>
851
- <div class="cascade-step">
852
- <span class="cascade-arrow">→</span>
853
- <span>🔥 Massive ROS Production</span>
854
- <span class="cascade-arrow">→</span>
855
- <span>Superoxide, hydrogen peroxide</span>
856
- </div>
857
- <div class="cascade-step">
858
- <span class="cascade-arrow">→</span>
859
- <span>📢 Oxidative Stress (ARE Activation)</span>
860
- <span class="cascade-arrow">→</span>
861
- <span>Nrf2 translocates to nucleus</span>
862
- </div>
863
- <div class="cascade-step">
864
- <span class="cascade-arrow">→</span>
865
- <span>🧬 DNA Damage (p53 Activation)</span>
866
- <span class="cascade-arrow">→</span>
867
- <span>ROS attacks DNA bases</span>
868
- </div>
869
- <div class="cascade-step">
870
- <span class="cascade-arrow">→</span>
871
- <span>💀 Cell Death</span>
872
- <span class="cascade-arrow">→</span>
873
- <span>Apoptosis or necrosis</span>
874
- </div>
875
- </div>
876
- """, unsafe_allow_html=True)
877
-
878
- st.markdown("""
879
- ## Quantitative Evidence
880
-
881
- <div class="interpretation-box">
882
- <strong>Co-occurrence Analysis:</strong>
883
- <ul>
884
- <li><strong>57% of MMP+ compounds</strong> also show ARE activation (2.4x enrichment, p < 0.001)</li>
885
- <li><strong>53% of MMP+ compounds</strong> also show p53 activation (3.0x enrichment, p < 0.001)</li>
886
- <li>This is NOT random - it's a biological cascade</li>
887
- </ul>
888
-
889
- <strong>Chemical Property Thresholds:</strong>
890
- <ul>
891
- <li><strong>LogP 4-6:</strong> 10x higher overall toxicity vs LogP 0-2</li>
892
- <li><strong>≥3 Aromatic Rings:</strong> 3.5x higher mitochondrial toxicity</li>
893
- <li><strong>MW >400 Da:</strong> 2.4x higher DNA damage</li>
894
- <li><strong>>7 Rotatable Bonds:</strong> 1.6x higher p53 activation</li>
895
- </ul>
896
- </div>
897
- """, unsafe_allow_html=True)
898
-
899
- st.markdown("""
900
- ## Why This Matters
901
-
902
- Traditional QSAR models treat toxicity as a black box. We don't.
903
-
904
- **Our approach:**
905
- 1. ✅ **Mechanistic**: We know *why* compounds are toxic
906
- 2. ✅ **Actionable**: We can suggest *how* to fix them
907
- 3. ✅ **Validated**: 100% accuracy on known compounds
908
- 4. ✅ **Interpretable**: SHAP values explain every prediction
909
-
910
- This isn't just a model - it's a mechanistic framework validated on over 11,000 compounds.
911
- """)
912
-
913
- with tab3:
914
  st.markdown("""
915
  ### 📖 About This Tool
916
-
917
- **Trained on:** 11,306 compounds from EPA ToxCast
918
- **Performance:** ROC-AUC 0.82-0.93 across endpoints
919
- **Validation:** 100% accuracy on known toxic/safe compounds
920
-
 
 
 
 
 
 
 
 
921
  **Endpoints:**
922
- - 🔥 **Oxidative Stress (ARE/Nrf2)** - Cellular antioxidant response
923
- - ⚡ **Mitochondrial Dysfunction (MMP)** - Energy production failure
924
- - 🧬 **DNA Damage (p53)** - Genotoxic stress response
925
-
926
- **⚠️ Disclaimer:** For research only. Not for regulatory submissions. Validate experimentally.
927
-
928
- **Citation:**
929
- ```
930
- Multi-Endpoint Toxicity Predictor: A Mechanistic Framework
931
- EPA ToxCast Database (2024)
932
- https://huggingface.co/spaces/MlchaeI/Toxicity_2
933
- ```
934
  """)
935
 
936
  st.markdown("---")
937
- st.markdown('<p style="text-align: center; color: #8b92a8; font-size: 1rem;">Built with deep mechanistic understanding | Research use only</p>', unsafe_allow_html=True)
 
14
  initial_sidebar_state="collapsed"
15
  )
16
 
17
+ # [Keep all the beautiful CSS from before]
18
  st.markdown("""
19
  <style>
20
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&display=swap');
21
+
22
  * {
23
  font-family: 'Inter', sans-serif;
24
  }
25
+
26
  .main {
27
  background: linear-gradient(180deg, #0a0e27 0%, #1a1f3a 50%, #0a0e27 100%);
28
  }
29
+
30
  .main-header {
31
  font-size: 4rem;
32
  font-weight: 900;
 
37
  margin-bottom: 0.5rem;
38
  animation: glow 3s ease-in-out infinite alternate;
39
  }
40
+
41
  @keyframes glow {
42
  from { filter: drop-shadow(0 0 20px rgba(102, 126, 234, 0.3)); }
43
  to { filter: drop-shadow(0 0 40px rgba(118, 75, 162, 0.6)); }
44
  }
45
+
46
  .sub-header {
47
  font-size: 1.4rem;
48
  text-align: center;
49
  color: #a0a6b8;
50
  margin-bottom: 3rem;
51
  font-weight: 300;
 
52
  }
53
+
54
+ .hypothesis-box {
55
+ background: linear-gradient(135deg, rgba(102, 234, 170, 0.1) 0%, rgba(102, 126, 234, 0.1) 100%);
56
+ border: 2px solid rgba(102, 234, 170, 0.4);
57
+ border-radius: 20px;
58
+ padding: 2rem;
59
+ margin: 1.5rem 0;
60
+ color: #e8eaf0;
61
+ }
62
+
63
+ .hypothesis-title {
64
+ color: #66eaaa;
65
+ font-size: 1.5rem;
66
+ font-weight: 800;
67
+ margin-bottom: 1rem;
68
+ display: flex;
69
+ align-items: center;
70
+ }
71
+
72
+ .experiment-box {
73
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(240, 147, 251, 0.1) 100%);
74
+ border: 2px solid rgba(102, 126, 234, 0.3);
75
+ border-radius: 15px;
76
+ padding: 1.5rem;
77
+ margin: 1rem 0;
78
+ transition: all 0.3s ease;
79
+ }
80
+
81
+ .experiment-box:hover {
82
+ transform: translateX(10px);
83
+ border-color: rgba(102, 126, 234, 0.6);
84
+ box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
85
+ }
86
+
87
+ .experiment-title {
88
+ color: #667eea;
89
+ font-size: 1.2rem;
90
+ font-weight: 700;
91
+ margin-bottom: 0.5rem;
92
+ }
93
+
94
+ .prediction-badge {
95
+ display: inline-block;
96
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
97
+ color: white;
98
+ padding: 0.4rem 1rem;
99
+ border-radius: 15px;
100
+ font-size: 0.85rem;
101
+ font-weight: 700;
102
+ margin: 0.5rem 0.5rem 0.5rem 0;
103
+ }
104
+
105
  .risk-card-low {
106
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
107
  padding: 3rem;
 
111
  box-shadow: 0 20px 60px rgba(102, 126, 234, 0.5);
112
  animation: pulse-low 2s ease-in-out infinite;
113
  }
114
+
115
  @keyframes pulse-low {
116
  0%, 100% { transform: scale(1); }
117
+ 50% { transform: scale(1.02); }
118
  }
119
+
120
  .risk-card-moderate {
121
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
122
  padding: 3rem;
 
126
  box-shadow: 0 20px 60px rgba(240, 147, 251, 0.5);
127
  animation: pulse-moderate 1.5s ease-in-out infinite;
128
  }
129
+
130
  @keyframes pulse-moderate {
131
  0%, 100% { transform: scale(1); }
132
  50% { transform: scale(1.03); }
133
  }
134
+
135
  .risk-card-high {
136
  background: linear-gradient(135deg, #ff0844 0%, #ffb199 100%);
137
  padding: 3rem;
 
141
  box-shadow: 0 20px 60px rgba(255, 8, 68, 0.6);
142
  animation: pulse-high 1s ease-in-out infinite;
143
  }
144
+
145
  @keyframes pulse-high {
146
  0%, 100% { transform: scale(1); }
147
  50% { transform: scale(1.05); }
148
  }
149
+
150
  .property-card {
151
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
152
  border: 2px solid rgba(102, 126, 234, 0.3);
 
154
  border-radius: 20px;
155
  margin: 1rem 0;
156
  transition: all 0.3s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
+
159
  .property-card:hover {
160
  transform: translateY(-5px);
161
  border-color: rgba(102, 126, 234, 0.6);
162
  box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
163
  }
164
+
 
 
 
 
 
 
 
 
 
165
  .property-value {
166
  color: #ffffff;
167
  font-size: 2rem;
 
170
  -webkit-background-clip: text;
171
  -webkit-text-fill-color: transparent;
172
  }
173
+
 
 
 
 
 
 
 
 
174
  .mechanism-card {
175
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
176
  border: 2px solid rgba(102, 126, 234, 0.2);
 
178
  border-radius: 20px;
179
  text-align: center;
180
  transition: all 0.4s ease;
 
181
  }
182
+
183
  .mechanism-card:hover {
184
  transform: translateY(-10px) scale(1.05);
185
  box-shadow: 0 20px 50px rgba(102, 126, 234, 0.4);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  }
187
+
188
  .mechanism-value {
189
  font-size: 3rem;
190
  font-weight: 900;
191
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
192
  -webkit-background-clip: text;
193
  -webkit-text-fill-color: transparent;
 
194
  }
195
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  .structure-container {
197
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
198
  border: 2px solid rgba(102, 126, 234, 0.3);
199
  padding: 2rem;
200
  border-radius: 25px;
201
  text-align: center;
 
202
  }
203
+
204
+ .interpretation-box {
205
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
206
+ border-left: 5px solid #667eea;
207
+ padding: 2.5rem;
208
  border-radius: 20px;
209
  margin: 2rem 0;
210
+ color: #e8eaf0;
211
+ line-height: 2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
  </style>
214
  """, unsafe_allow_html=True)
215
 
216
+ def generate_testable_hypotheses(result, props, mol):
217
+ """Generate specific, testable hypotheses with experimental protocols"""
218
+
219
+ prob_are = result['oxidative_stress']['probability']
220
+ prob_mmp = result['mitochondrial_dysfunction']['probability']
221
+ prob_p53 = result['dna_damage']['probability']
222
+
223
+ hypotheses = []
224
+
225
+ # HYPOTHESIS 1: Mitochondrial Accumulation (if high LogP)
226
+ if props['LogP'] > 3:
227
+ hypotheses.append({
228
+ 'title': f"🎯 Hypothesis 1: Mitochondrial Accumulation via Lipophilicity",
229
+ 'hypothesis': f"Your compound (LogP = {props['LogP']:.2f}) will accumulate preferentially in mitochondria at 3-5x higher concentration than cytoplasm due to lipophilic partitioning into the inner mitochondrial membrane.",
230
+ 'rationale': f"Compounds with LogP {props['LogP']:.2f} partition heavily into lipid bilayers. Mitochondria have a highly negative membrane potential (-180 mV), creating an electrochemical gradient that traps lipophilic cations. This is the Nernst equation in action.",
231
+ 'experiments': [
232
+ {
233
+ 'name': "MitoTracker Colocalization",
234
+ 'protocol': "• Treat HepG2 cells with 10 µM compound for 2h\n• Co-stain with MitoTracker Red (100 nM)\n• Use confocal microscopy to measure Pearson correlation\n• Compare to positive control (rhodamine 123)",
235
+ 'readout': "Pearson coefficient >0.7 = strong mitochondrial accumulation",
236
+ 'expected': f"Predict >0.75 correlation based on LogP {props['LogP']:.2f}",
237
+ 'timeline': "2-3 days",
238
+ 'cost': "$500-800"
239
+ },
240
+ {
241
+ 'name': "Mitochondrial Isolation + LC-MS",
242
+ 'protocol': "• Treat cells with compound (10 µM, 4h)\n• Isolate mitochondria via differential centrifugation\n• Extract compound from mitochondrial vs cytosolic fractions\n• Quantify by LC-MS/MS",
243
+ 'readout': "Mitochondrial/cytosolic concentration ratio",
244
+ 'expected': f"Predict {3 if props['LogP'] < 5 else 5}-fold enrichment in mitochondria",
245
+ 'timeline': "1 week",
246
+ 'cost': "$2,000-3,000"
247
+ }
248
+ ]
249
+ })
250
+
251
+ # HYPOTHESIS 2: Direct Membrane Disruption (if aromatic)
252
+ if props['AromaticRings'] >= 3:
253
+ hypotheses.append({
254
+ 'title': f"🎯 Hypothesis 2: Direct Membrane Disruption via π-π Stacking",
255
+ 'hypothesis': f"Your compound ({int(props['AromaticRings'])} aromatic rings) will directly disrupt mitochondrial membranes through π-π interactions with membrane lipids and respiratory chain complexes.",
256
+ 'rationale': f"Aromatic rings are planar and electron-rich. With {int(props['AromaticRings'])} rings, your compound can intercalate between lipid acyl chains and stack with aromatic residues in Complex I and III. This is direct physical disruption, not just accumulation.",
257
+ 'experiments': [
258
+ {
259
+ 'name': "Isolated Mitochondria MMP Assay",
260
+ 'protocol': "• Isolate mitochondria from rat liver\n• Measure membrane potential with TMRM fluorescence\n• Add compound directly to isolated mitochondria (no cells)\n• Monitor depolarization in real-time",
261
+ 'readout': "% depolarization vs vehicle control",
262
+ 'expected': f"Predict {30 if props['AromaticRings'] == 3 else 50}% depolarization at 10 µM within 30 min",
263
+ 'timeline': "3-4 days",
264
+ 'cost': "$800-1,200"
265
+ },
266
+ {
267
+ 'name': "Liposome Permeability Assay",
268
+ 'protocol': "• Prepare cardiolipin-enriched liposomes (mimics inner mitochondrial membrane)\n• Load with calcein dye\n• Add compound and measure dye leakage\n• Compare to non-aromatic control",
269
+ 'readout': "Calcein release rate",
270
+ 'expected': f"{int(props['AromaticRings'])}x faster than non-aromatic analog",
271
+ 'timeline': "2-3 days",
272
+ 'cost': "$400-600"
273
+ }
274
+ ]
275
+ })
276
+
277
+ # HYPOTHESIS 3: ROS Generation (if high ARE + high MMP)
278
+ if prob_are > 0.5 and prob_mmp > 0.5:
279
+ hypotheses.append({
280
+ 'title': f"🎯 Hypothesis 3: ROS-Mediated Toxicity Cascade",
281
+ 'hypothesis': f"Your compound will cause mitochondrial dysfunction (predicted {prob_mmp:.0%}), leading to ROS production that activates the ARE/Nrf2 pathway (predicted {prob_are:.0%}). ROS is the mechanistic link.",
282
+ 'rationale': f"When electron transport is disrupted, electrons leak from Complex I and III, reducing O₂ to superoxide (O₂•⁻). This triggers Nrf2 nuclear translocation. Your high scores on both pathways suggest this cascade is active.",
283
+ 'experiments': [
284
+ {
285
+ 'name': "ROS Temporal Analysis",
286
+ 'protocol': "• Treat HepG2 cells with compound (10 µM)\n• Measure ROS with MitoSOX (mitochondrial O₂•⁻) every 30 min for 4h\n• Simultaneously measure ARE-luciferase reporter\n• Test if ROS peaks BEFORE ARE activation",
287
+ 'readout': "Time course: ROS peak → ARE activation",
288
+ 'expected': "ROS peaks at 1-2h, ARE activation at 3-4h (causal sequence)",
289
+ 'timeline': "1 week",
290
+ 'cost': "$1,500-2,000"
291
+ },
292
+ {
293
+ 'name': "ROS Scavenger Rescue",
294
+ 'protocol': "• Pre-treat cells with N-acetylcysteine (NAC, 5 mM)\n• Add compound + measure ARE activation\n• If ROS is causal, NAC should block ARE",
295
+ 'readout': "% reduction in ARE activation with NAC",
296
+ 'expected': "Predict >70% reduction (proves ROS causality)",
297
+ 'timeline': "3-4 days",
298
+ 'cost': "$600-800"
299
+ }
300
+ ]
301
+ })
302
+
303
+ # HYPOTHESIS 4: DNA Intercalation (if flexible + high p53)
304
+ if props['RotatableBonds'] > 7 and prob_p53 > 0.5:
305
+ hypotheses.append({
306
+ 'title': f"🎯 Hypothesis 4: DNA Intercalation via Molecular Flexibility",
307
+ 'hypothesis': f"Your compound ({int(props['RotatableBonds'])} rotatable bonds) will intercalate into DNA, causing double-strand breaks and p53 activation (predicted {prob_p53:.0%}).",
308
+ 'rationale': f"Flexible molecules can adopt planar conformations that fit between DNA base pairs. Classical intercalators like doxorubicin have 5-10 rotatable bonds. Your compound's flexibility suggests it can contort to fit.",
309
+ 'experiments': [
310
+ {
311
+ 'name': "Ethidium Bromide Displacement",
312
+ 'protocol': "• Incubate calf thymus DNA with ethidium bromide\n• Add increasing concentrations of your compound\n• Measure fluorescence quenching (EtBr displacement)\n• Calculate IC₅₀ for displacement",
313
+ 'readout': "IC₅₀ for EtBr displacement",
314
+ 'expected': "IC₅₀ < 50 µM indicates intercalation",
315
+ 'timeline': "2 days",
316
+ 'cost': "$300-400"
317
+ },
318
+ {
319
+ 'name': "γH2AX Foci Formation",
320
+ 'protocol': "• Treat cells with compound (2-10 µM, 24h)\n• Fix and stain for γH2AX (DNA double-strand break marker)\n• Count nuclear foci per cell\n• Compare to etoposide (positive control)",
321
+ 'readout': "Average foci per nucleus",
322
+ 'expected': f"Predict {5 if prob_p53 < 0.7 else 10}+ foci per cell",
323
+ 'timeline': "3-4 days",
324
+ 'cost': "$800-1,000"
325
+ }
326
+ ]
327
+ })
328
+
329
+ # HYPOTHESIS 5: Complex I Inhibition (if specific structural features)
330
+ if prob_mmp > 0.7 and props['LogP'] > 4:
331
+ hypotheses.append({
332
+ 'title': f"🎯 Hypothesis 5: Specific Complex I Inhibition",
333
+ 'hypothesis': f"Your compound will specifically inhibit mitochondrial Complex I (NADH dehydrogenase), similar to rotenone, due to structural complementarity with the ubiquinone binding site.",
334
+ 'rationale': f"LogP {props['LogP']:.2f} + aromatic character = structural similarity to known Complex I inhibitors (rotenone, MPP+). The ubiquinone binding pocket is hydrophobic and aromatic-rich.",
335
+ 'experiments': [
336
+ {
337
+ 'name': "Seahorse XF Complex I Stress Test",
338
+ 'protocol': "• Measure oxygen consumption rate (OCR) in HepG2\n• Sequential injection: Compound → Oligomycin → FCCP → Rotenone/Antimycin A\n• Quantify Complex I-dependent respiration",
339
+ 'readout': "% inhibition of Complex I vs total respiration",
340
+ 'expected': "Predict >50% Complex I-specific inhibition",
341
+ 'timeline': "1 week",
342
+ 'cost': "$2,000-2,500"
343
+ },
344
+ {
345
+ 'name': "Isolated Complex I Activity Assay",
346
+ 'protocol': "• Isolate Complex I from bovine heart mitochondria\n• Measure NADH:ubiquinone oxidoreductase activity\n• Add compound at 1-100 µM\n• Calculate IC₅₀",
347
+ 'readout': "IC₅₀ for Complex I inhibition",
348
+ 'expected': f"IC₅₀ {5 if props['LogP'] > 5 else 20} µM",
349
+ 'timeline': "1 week",
350
+ 'cost': "$1,500-2,000"
351
+ }
352
+ ]
353
+ })
354
+
355
+ # HYPOTHESIS 6: Metabolic Activation (if has oxidizable groups)
356
+ if props['NumN'] + props['NumO'] + props['NumS'] > 5:
357
+ hypotheses.append({
358
+ 'title': f"🎯 Hypothesis 6: Metabolic Activation to Reactive Metabolites",
359
+ 'hypothesis': f"Your compound contains {int(props['NumN'] + props['NumO'] + props['NumS'])} heteroatoms (N, O, S) that may be metabolically activated by CYP450s to reactive electrophiles or redox-cycling quinones.",
360
+ 'rationale': f"Heteroatoms are CYP450 substrates. Oxidation can generate reactive intermediates (epoxides, quinones, iminium ions) that covalently modify proteins or redox cycle to generate ROS. This is bioactivation toxicity.",
361
+ 'experiments': [
362
+ {
363
+ 'name': "Microsomal Stability + Metabolite ID",
364
+ 'protocol': "• Incubate compound with human liver microsomes + NADPH\n• Sample at 0, 15, 30, 60 min\n• Analyze by LC-MS/MS for parent disappearance + metabolite formation\n• Identify oxidative metabolites",
365
+ 'readout': "t₁/₂ + metabolite structures",
366
+ 'expected': "Predict t₁/₂ < 30 min (rapid metabolism) + N/O oxidation products",
367
+ 'timeline': "1-2 weeks",
368
+ 'cost': "$3,000-4,000"
369
+ },
370
+ {
371
+ 'name': "CYP450 Inhibitor Rescue",
372
+ 'protocol': "• Pre-treat cells with ketoconazole (pan-CYP inhibitor, 1 µM)\n• Add compound + measure toxicity (MTT assay)\n• If metabolism is required, ketoconazole will rescue",
373
+ 'readout': "% rescue by CYP inhibition",
374
+ 'expected': "Predict >50% rescue (proves bioactivation)",
375
+ 'timeline': "3-4 days",
376
+ 'cost': "$500-700"
377
+ }
378
+ ]
379
+ })
380
+
381
+ return hypotheses
382
+
383
  @st.cache_resource
384
  def load_models():
385
  """Load models"""
 
397
  st.error(f"Error: {str(e)}")
398
  return None
399
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  def compute_features(smiles, feature_names):
401
  """Compute features"""
402
  try:
403
  mol = Chem.MolFromSmiles(smiles)
404
  if mol is None:
405
  return None, "Invalid SMILES", None
406
+
407
  features = {
408
  'MW': Descriptors.MolWt(mol),
409
  'LogP': Descriptors.MolLogP(mol),
 
429
  'NumAliphaticRings': Lipinski.NumAliphaticRings(mol),
430
  'FractionCsp3': Descriptors.FractionCsp3(mol) if hasattr(Descriptors, 'FractionCsp3') else 0.0,
431
  }
432
+
433
  fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)
434
  fp_array = np.array(fp)
435
+
436
  feature_vector = []
437
  for fname in feature_names:
438
  if fname.startswith('Morgan_'):
 
440
  feature_vector.append(fp_array[bit_idx])
441
  else:
442
  feature_vector.append(features.get(fname, 0))
443
+
444
  return np.array(feature_vector).reshape(1, -1), None, features
445
+
446
  except Exception as e:
447
  return None, f"Error: {str(e)}", None
448
 
449
  def predict_toxicity(smiles, models):
450
  """Predict toxicity"""
451
  X, error, raw_features = compute_features(smiles, models['feature_names'])
452
+
453
  if error:
454
  return {'error': error}
455
+
456
  try:
457
  X_are = models['scaler_are'].transform(X)
458
  X_mmp = models['scaler_mmp'].transform(X)
459
  X_p53 = models['scaler_p53'].transform(X)
460
+
461
  prob_are = float(models['model_are'].predict_proba(X_are)[0, 1])
462
  prob_mmp = float(models['model_mmp'].predict_proba(X_mmp)[0, 1])
463
  prob_p53 = float(models['model_p53'].predict_proba(X_p53)[0, 1])
464
+
465
  overall_prob = max(prob_are, prob_mmp, prob_p53)
466
+
467
  if overall_prob < 0.35:
468
  risk = "LOW"
469
  prediction = "NON-TOXIC"
 
473
  else:
474
  risk = "HIGH"
475
  prediction = "TOXIC"
476
+
477
  return {
478
  'overall_toxicity': {
479
  'prediction': prediction,
 
495
 
496
  # Header
497
  st.markdown('<p class="main-header">🧪 Multi-Endpoint Toxicity Predictor</p>', unsafe_allow_html=True)
498
+ st.markdown('<p class="sub-header">AI-powered hypothesis generation for drug toxicity mechanisms</p>', unsafe_allow_html=True)
499
 
500
  # Tabs
501
+ tab1, tab2 = st.tabs(["🔮 Analyze Compound", "📖 About"])
502
 
503
  with tab1:
504
+ st.markdown("### Enter SMILES String")
505
+
506
  smiles = st.text_input(
507
  "SMILES:",
508
  placeholder="e.g., CC(=O)Oc1ccccc1C(=O)O",
509
  label_visibility="collapsed"
510
  )
511
+
512
  st.markdown("**Examples:**")
513
+
514
  examples = {
515
  "Aspirin (Safe)": "CC(=O)Oc1ccccc1C(=O)O",
 
516
  "Doxorubicin (Toxic)": "COc1cccc2c1C(=O)c1c(O)c3c(c(O)c1C2=O)C[C@@](O)(C(=O)CO)C[C@@H]3O[C@H]1C[C@H](N)[C@H](O)[C@H](C)O1",
517
+ "Tamoxifen (Toxic)": "CCC(=C(c1ccccc1)c1ccc(OCCN(C)C)cc1)c1ccccc1",
518
+ "Rotenone (Complex I)": "COc1cc(ccc1OC)[C@@H]1[C@H](C(=O)c2c3c(cc4c2OC[C@H]4C1)OCO3)C"
519
  }
520
+
521
  for name, smi in examples.items():
522
  st.code(smi, language=None)
523
  st.caption(name)
524
+
525
+ if st.button("🔬 Generate Hypotheses", type="primary", use_container_width=True):
526
  if not smiles:
527
  st.warning("⚠️ Please enter a SMILES string")
528
  else:
529
  mol = Chem.MolFromSmiles(smiles)
530
+
531
  if mol is None:
532
  st.error("❌ Invalid SMILES")
533
  else:
534
+ with st.spinner("🧬 Generating mechanistic hypotheses..."):
535
  result = predict_toxicity(smiles, models)
536
+
537
  if 'error' in result:
538
  st.error(f"❌ {result['error']}")
539
  else:
540
  st.markdown("---")
541
+
542
  # Structure
543
  st.markdown("### 🧬 Molecular Structure")
544
  col_struct = st.columns([1, 2, 1])
 
547
  img = Draw.MolToImage(mol, size=(500, 500))
548
  st.image(img, use_column_width=True)
549
  st.markdown('</div>', unsafe_allow_html=True)
550
+
551
  st.markdown("---")
552
+
553
+ # Quick prediction summary
554
  overall = result['overall_toxicity']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555
  prob_are = result['oxidative_stress']['probability']
556
  prob_mmp = result['mitochondrial_dysfunction']['probability']
557
  prob_p53 = result['dna_damage']['probability']
558
+
559
+ col1, col2, col3, col4 = st.columns(4)
560
+
561
  with col1:
562
+ st.metric("Overall Risk", overall['risk_level'],
563
+ delta=f"{overall['probability']:.0%}")
 
 
 
 
 
 
 
 
 
 
564
  with col2:
565
+ st.metric("🔥 Oxidative", f"{prob_are:.0%}")
 
 
 
 
 
 
 
 
 
 
 
566
  with col3:
567
+ st.metric("⚡ Mitochondrial", f"{prob_mmp:.0%}")
568
+ with col4:
569
+ st.metric("🧬 DNA Damage", f"{prob_p53:.0%}")
570
+
 
 
 
 
 
 
 
 
571
  st.markdown("---")
572
+
573
+ # HYPOTHESIS GENERATION
574
+ st.markdown("### 🔬 Testable Hypotheses & Experimental Protocols")
575
+
576
  props = result['molecular_properties']
577
+ hypotheses = generate_testable_hypotheses(result, props, mol)
578
+
579
+ if len(hypotheses) == 0:
580
+ st.info("✅ No major toxicity concerns detected. Standard safety testing recommended.")
581
+ else:
582
  st.markdown(f"""
583
+ **Generated {len(hypotheses)} mechanistic hypotheses based on your compound's structure and predicted toxicity profile.**
584
+
585
+ Each hypothesis includes:
586
+ - 🎯 Specific mechanistic prediction
587
+ - 🔬 Detailed experimental protocols
588
+ - 📊 Expected results
589
+ - ⏱️ Timeline and cost estimates
590
+ """)
591
+
592
+ for i, hyp in enumerate(hypotheses, 1):
593
+ st.markdown(f"""
594
+ <div class="hypothesis-box">
595
+ <div class="hypothesis-title">{hyp['title']}</div>
596
+
597
+ <p><strong>Hypothesis:</strong><br>{hyp['hypothesis']}</p>
598
+
599
+ <p><strong>Scientific Rationale:</strong><br>{hyp['rationale']}</p>
600
  </div>
601
+ """, unsafe_allow_html=True)
602
+
603
+ st.markdown(f"#### Proposed Experiments for Hypothesis {i}:")
604
+
605
+ for j, exp in enumerate(hyp['experiments'], 1):
606
+ with st.expander(f"**Experiment {i}.{j}: {exp['name']}**", expanded=(j==1)):
607
+ col_exp1, col_exp2 = st.columns([2, 1])
608
+
609
+ with col_exp1:
610
+ st.markdown(f"""
611
+ **Protocol:**
612
+ {exp['protocol']}
613
+
614
+ **Readout:**
615
+ {exp['readout']}
616
+
617
+ **Predicted Result:**
618
+ {exp['expected']}
619
+ """)
620
+
621
+ with col_exp2:
622
+ st.markdown(f"""
623
+ <div class="experiment-box">
624
+ <div class="experiment-title">⏱️ Timeline</div>
625
+ <p>{exp['timeline']}</p>
626
+
627
+ <div class="experiment-title">💰 Est. Cost</div>
628
+ <p>{exp['cost']}</p>
629
+ </div>
630
+ """, unsafe_allow_html=True)
631
+
632
+ st.markdown("---")
633
+
634
+ # Summary recommendations
635
+ st.markdown("### 💡 Research Roadmap")
636
+
637
+ total_cost = sum(
638
+ sum(
639
+ int(exp['cost'].split('-')[0].replace('$', '').replace(',', ''))
640
+ for exp in hyp['experiments']
641
+ )
642
+ for hyp in hypotheses
643
+ )
644
+
645
+ st.markdown(f"""
646
+ <div class="interpretation-box">
647
+ <p><strong>Recommended Testing Strategy:</strong></p>
648
+
649
+ <p><strong>Phase 1 (Week 1-2):</strong> Start with the fastest, cheapest experiments to validate primary hypotheses.
650
+ These will tell you if your compound behaves as predicted.</p>
651
+
652
+ <p><strong>Phase 2 (Week 3-4):</strong> If Phase 1 confirms toxicity, proceed to mechanistic experiments
653
+ (LC-MS, Seahorse, etc.) to understand the detailed mechanism.</p>
654
+
655
+ <p><strong>Phase 3 (Month 2):</strong> Use mechanistic insights to design and test optimized analogs
656
+ with reduced toxicity.</p>
657
+
658
+ <p><strong>Total estimated cost for complete hypothesis testing: ${total_cost:,} - ${total_cost + 5000:,}</strong></p>
659
+
660
+ <p><strong>Priority experiments (if budget-limited):</strong> Focus on Experiment 1 from each hypothesis -
661
+ these are designed to be quick validation studies.</p>
662
+ </div>
663
+ """, unsafe_allow_html=True)
664
 
665
  with tab2:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
  st.markdown("""
667
  ### 📖 About This Tool
668
+
669
+ This is not just a toxicity predictor - it's a **hypothesis generation engine**.
670
+
671
+ **What makes it unique:**
672
+ - 🎯 **Mechanistic predictions**: Not just "toxic" or "safe", but *why*
673
+ - 🔬 **Experimental protocols**: Ready-to-use laboratory procedures
674
+ - 📊 **Quantitative predictions**: Expected results with timelines and costs
675
+ - 💡 **Research roadmap**: Prioritized testing strategy
676
+
677
+ **Trained on:** 11,306 compounds from EPA ToxCast
678
+ **Performance:** ROC-AUC 0.82-0.93 across endpoints
679
+ **Validation:** 100% accuracy on known compounds
680
+
681
  **Endpoints:**
682
+ - 🔥 Oxidative Stress (ARE/Nrf2)
683
+ - ⚡ Mitochondrial Dysfunction (MMP)
684
+ - 🧬 DNA Damage (p53)
685
+
686
+ **⚠️ Disclaimer:** For research only. Validate computationally-generated hypotheses experimentally.
687
+
688
+ **For researchers:** These protocols are based on standard methods from the literature.
689
+ Adjust concentrations and timepoints based on your specific compound and cell system.
 
 
 
 
690
  """)
691
 
692
  st.markdown("---")
693
+ st.markdown('<p style="text-align: center; color: #8b92a8;">🔬 Accelerating drug safety research through AI-powered hypothesis generation</p>', unsafe_allow_html=True)