Saeed commited on
Commit
ce87324
·
verified ·
1 Parent(s): 0f9382c

procedure generation updated, UI changed

Browse files
Files changed (3) hide show
  1. app.py +444 -697
  2. biomaterials.py +124 -0
  3. cell_lines.py +178 -0
app.py CHANGED
@@ -1,697 +1,444 @@
1
- import os
2
- import streamlit as st
3
- import pandas as pd
4
- import joblib
5
- import optuna
6
- import numpy as np
7
- from catboost import CatBoostClassifier
8
-
9
-
10
- from google import genai
11
- from google.genai import types
12
-
13
-
14
-
15
- _original_number_input = st.number_input
16
-
17
- def safe_number_input(label, **kwargs):
18
- """
19
- Clamp `value` into [min_value, max_value] and warn if we had to adjust.
20
- Then call the real st.number_input with the clamped default.
21
- """
22
- min_value = kwargs.get("min_value", float("-inf"))
23
- max_value = kwargs.get("max_value", float("inf"))
24
- value = kwargs.get("value", min_value)
25
- clamped = min(max(value, min_value), max_value)
26
- if clamped != value:
27
- st.warning(
28
- f"⚠️ Default for “{label}” ({value}) was outside "
29
- f"[{min_value}, {max_value}]; using {clamped} instead."
30
- )
31
- kwargs["value"] = clamped
32
- return _original_number_input(label, **kwargs)
33
-
34
- st.number_input = safe_number_input
35
-
36
-
37
-
38
- gemini_key = os.getenv("GEMINI_API_KEY")
39
- if not gemini_key:
40
- st.error("Gemini API key not found. Please set GEMINI_API_KEY in your Space settings.")
41
- st.stop()
42
-
43
- client = genai.Client(api_key=gemini_key)
44
-
45
-
46
- def scaffold_quality_combined(printability, cell_response,
47
- weight_printability=0.3, weight_cell_response=0.7):
48
- if printability == 0:
49
- return 0.0
50
- if cell_response == 1:
51
- return 100 * (printability / 3.0)
52
- norm_p = printability / 3.0
53
- norm_c = (cell_response - 1) / 4.0
54
- hm = (weight_printability + weight_cell_response) / (
55
- (weight_printability / norm_p) +
56
- (weight_cell_response / norm_c)
57
- )
58
- mc = (norm_p**weight_printability) * (norm_c**weight_cell_response)
59
- return 100 * ((hm + mc) / 2.0)
60
-
61
- BIOMATERIAL_OPTIONS = [
62
- "Alginate (%w/v)",
63
- "PVA-HA (%w/v)",
64
- "CaSO4 (%w/v)",
65
- "Na2HPO4 (%w/v)",
66
- "Gelatin (%w/v)",
67
- "GelMA (%w/v)",
68
- "laponite (%w/v)",
69
- "graphene oxide (%w/v)",
70
- "hydroxyapatite (%w/v)",
71
- "Hyaluronic_Acid (%w/v)",
72
- "hyaluronan metacrylate (%w/v)",
73
- "NorHA (%w/v)",
74
- "Fibroin/Fibrinogen (%w/v)",
75
- "Pluronic P-123 (%w/v)",
76
- "Collagen (%w/v)",
77
- "Chitosan (%w/v)",
78
- "CS-AEMA (%w/v)",
79
- "RGD (mM)",
80
- "TCP (%w/v)",
81
- "Gellan (%w/v)",
82
- "bioactive glass (%w/v)",
83
- "Nano/Methycellulose (%w/v)",
84
- "PEGTA (%w/v)",
85
- "PEGMA (%w/v)",
86
- "PEGDA (%w/v)",
87
- "Agarose (%w/v)",
88
- " hyaluronic acid+ Ph moieties (%w/v)",
89
- "matrigel (%w/v)",
90
- "CaCl2(mM)",
91
- "NaCl(mM)",
92
- "BaCl2(mM)",
93
- "SrCl2(mM)",
94
- "CaCO3 (mM)",
95
- "Genipin (%w/v)",
96
- "PVA (%wt)",
97
- "trans-glutaminase (%w/v)",
98
- "alginate lyase (U/ml)",
99
- "D-glucose (%w/v)",
100
- "PLGA (%w/v)",
101
- "vascular tissued-derived dECM (%w/v)",
102
- "PEG-8-SH (mM)",
103
- "Alginate dialdehyde (%w/v)",
104
- "Alginate sulfate (%w/v)",
105
- "RGD-modified alginate (%w/v)",
106
- "poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)",
107
- "chondroitin sulfate methacrylate (%w/v)",
108
- "PCL (%w/v)",
109
- "alginate methacrylate (%w/v)",
110
- "HRP (U/ml)",
111
- "Pluronic F127 (%w/v)/Lutrol F127 (%w/v)",
112
- "Irgacure 2959 (%w/v)",
113
- "Eosin Y (%w/v)",
114
- "Ruthenium (mM)",
115
- "sodium persulfate (SPS) (mM)",
116
- "HEPES (mM)",
117
- "LAP (%w/v)",
118
- "glutaraldehyde (%w/v)",
119
- "PBS (M)",
120
- "glycerol (%w/v)",
121
- "cECM (%w/v)",
122
- "gel-fu(%w/v)",
123
- "Rose Bengal (%w/v)",
124
- "Vitamin B2(%w/v)",
125
- "VEGF(%w/v)",
126
- "Polypyrrole:PSS(%w/v)",
127
- "boratebioactiveglass(%w/v)",
128
- "astaxanthin(%w/v)",
129
- "PRP (%v/v)",
130
- "methacrylated collagen (%w/v)",
131
- -Toc (µM)",
132
- "ascorbic acid (mM)",
133
- "Liver dECM(%w/v)",
134
- "galactosylated alginate (%w/v)",
135
- "SC-PEG(%w/v)",
136
- "SFMA-L(%w/v)",
137
- "SFMA-M(%w/v)",
138
- "SFMA-H(%w/v)",
139
- "KdECMMA(%w/v)",
140
- "BA silk fibronin (%w/v)",
141
- "Carrageenan(%v)",
142
- "Carbopol ETD 2020 NF (%w/v)",
143
- "Carbopol Ultrez 10 NF(%w/v)",
144
- "Carbopol NF-980(%w/v)",
145
- "FBS (%v/v)",
146
- "MeTro (%w/v)",
147
- "Triethanolamine (%v/v)",
148
- "PEG-Fibrinogen (%w/v)",
149
- "polyethylene glycol dimethacrylate (%w/v)",
150
- "aprotinin (µg/ml)",
151
- "gold nanorod (mg/mL)",
152
- "egg white (w/v)",
153
- "1-Vinyl-2-Pyrrolidione (v/v)",
154
- "carboxyl functionalized carbon nanotubes (%w/v)",
155
- "polyHIPE (%w/v)",
156
- "β-D galactose (mM)",
157
- "hydrogen peroxide (H2O2) (%v/v)",
158
- "lactic acid v/v",
159
- "NorCol (%w/v)",
160
- "DDT (%w/v)",
161
- "ammonium persulfate (mM)",
162
- "diTyr-RGD (mM)",
163
- "PHEG-Tyr (%w/v)",
164
- "MMP2-degradable peptide (%w/v)",
165
- "KdECM (%w/v)",
166
- "EDC (mg)",
167
- "NHS (mg)",
168
- "VA086 (%w/v)",
169
- "PGS (%w/v)",
170
- "thiolated HA (%w/v)",
171
- "boron nitride nanotubes (%w/v)",
172
- "PEDOT:PSS (ul)",
173
- "KCl (mM)",
174
- "skeletal muscle ECM methacrylate (%w/v)",
175
- "PEO (%w/v)",
176
- "Carbon dots (mg/ml)",
177
- "Laminin (ug/ml)",
178
- "DF-PEG (%w/v)",
179
- "omenta ECM (%w/v)",
180
- "thrombin (unit/ml)",
181
- "Carbon nanotube (CNT) (w/v)",
182
- "Phytagel(%v)",
183
- "Laponite-XLG (%w/w)"
184
- ]
185
-
186
- CELL_LINE_OPTIONS = [
187
- "NoCellCultured",
188
- "chondrocyteyte",
189
- "HepG2",
190
- "bMSCs",
191
- "HUVECs",
192
- "NIH3T3",
193
- "MESCs",
194
- "hiPSC-CMs /ATCCs",
195
- "CPCs",
196
- "L929",
197
- "Myoblast cells",
198
- "hiPSCs",
199
- "HepaRG",
200
- "hESCs",
201
- "10T1/2",
202
- "Cardiac progenitor cells",
203
- "NSCLC PDX",
204
- "RAMECs",
205
- "hASCs",
206
- "HAVIC",
207
- "Primary mouse hepatocyte",
208
- "PTECs",
209
- "human nasoseptal chondrocytes",
210
- "PDX",
211
- "HPFs",
212
- "U87-MG",
213
- "ESCs",
214
- "HASSMC",
215
- "dermal fibroblasts",
216
- "MC3T3-E1",
217
- "Schwann cells",
218
- "hiPSC-CMs and HS-27A",
219
- "Saos-2 ",
220
- "SU3",
221
- "hTMSCs",
222
- "HACs",
223
- "HADMSCs",
224
- "HeLa",
225
- "human primary kidney cells",
226
- "myoblasts",
227
- "MSCs",
228
- "human primary kidneycells",
229
- "293FT",
230
- "HEK 293FT",
231
- "Wnt3a-293FT",
232
- "RSC96/HUVECs",
233
- "human adipogenic mesenchymal stem cells",
234
- "HEPG2/ECs",
235
- "HUVECs/MSCs",
236
- "RHECs",
237
- "Human non-small cell lung cancer line Calu-3 (Calu-3)",
238
- "HL-1",
239
- "mouse cardiac cells",
240
- "IMR-90",
241
- "EPCs",
242
- "MRC5",
243
- "rMSC",
244
- "basil plant cell",
245
- "hNCs",
246
- "A549",
247
- "human induced pluripotent stem cell-derived cardiomyocytes",
248
- "bMSCs/hACs",
249
- "EA.hy 926 cells",
250
- "HepG2/C3A",
251
- "human epithelial lung carcinoma cells",
252
- "Human cardiac fibroblasts",
253
- "hTERT-MSC",
254
- "cardiomyocytes",
255
- "Huh7",
256
- "NRCMs",
257
- "HCF",
258
- "Wnt reporter-293FT",
259
- "neonatal rat ventricular CFs",
260
- "human coronary artery endothelial cells",
261
- "hiPSC-CM / fibroblasts",
262
- "primary mouse hepatocyte",
263
- "NIH3T3/ HUVECs",
264
- "murine macrophage-like cell line",
265
- "Endothelial cells",
266
- "Human aortic VIC",
267
- "sADSC",
268
- "HUVECs/H9C2",
269
- "neonatal rat ventricular cardiomyocytes",
270
- "MG-63",
271
- "Neonatal mouse cardiomyocytes (NMVCMs)",
272
- "human hepatic stellate cell line",
273
- "HEK-293",
274
- "aHSC",
275
- "MFCs",
276
- "fibroblasts",
277
- "HNDF",
278
- "cardiomyocyte/MSCs",
279
- "ADSCs",
280
- "HCASMCs",
281
- "cardiomyocyte",
282
- "hCPCs",
283
- "Human CM /adult human fibroblasts ",
284
- "primary rat hepatocyte",
285
- "human cardiac progenitor cells",
286
- "SMC",
287
- "Human MSCs",
288
- "ACPCs",
289
- "Huh7/HepaRG",
290
- "Human umbilical vein endothelial cells",
291
- "ATDC5",
292
- "hESC-derived HLCs",
293
- "NIH 3T3",
294
- "n neonatal mouse ventricular cardiomyocytes",
295
- "CFs/CMs/HUVECs",
296
- "MRC-5",
297
- "VIC",
298
- "eHep",
299
- "hUVECs/NIH3T3",
300
- "MC3T3",
301
- "HLC",
302
- "hepatoma",
303
- "FB",
304
- "A549 GFP+",
305
- "HPAAF",
306
- "PMHs",
307
- "HUVSMCs",
308
- "Human CPCs",
309
- "Fibroblasts/THP-1",
310
- "rat ventricular cardiomyocytes",
311
- "iCMs/iCFs/iECs",
312
- "HUVEC/HHSC",
313
- "Human CPCs / MSCs",
314
- "HepaRG/LX-2 ",
315
- "iCMs/iCFs/iECs/iCMFs",
316
- "LX-2 ",
317
- "SMMC-7721",
318
- "Hepatoblast- single cell/iESC/iMSC",
319
- "iCMFs",
320
- "Hepatoblast- spheroid/iESC/iMSC",
321
- "hBM-MSCs",
322
- "BMSCs",
323
- "HUVECs and HHSCs",
324
- "Intrahepatic cholangiocarcinoma (ICC)",
325
- "VICs",
326
- "CMs/CFs",
327
- "human neonatal dermal fi broblasts",
328
- "10T1/2 fibroblast-laden cells",
329
- "human cardiac fibroblasts",
330
- "neonatal rat ventricular CMs",
331
- "HUVECs and hiPSC-CS",
332
- "iPSC-derived CM",
333
- "Human Umbilical Vein Endothelial Cells + iPSC-derived CM",
334
- "rabbit bone marrow mesenchymal stem cells",
335
- "Neonatal rat cardiomyocytes",
336
- "NIH 3T3 mouse fibroblasts",
337
- "Human CPCs & MSCs",
338
- "NSCLC PDX/CAFs",
339
- "hiPSCs-derived HLCs",
340
- "U87",
341
- "75% hepatoblast cells, 20% iEC and 5% iMSC",
342
- "SMCs",
343
- "A549/95-D cells",
344
- "NCI-H441",
345
- "pancreatic cancer cell",
346
- "prostate cancer stem cell",
347
- "human primary parathyroid cells ",
348
- "primary human hepatocytes",
349
- "CPCs / MSCs",
350
- "CPCs ",
351
- "cardiac fibroblasts",
352
- "iPSCs-derived cardiomyocytes",
353
- "cardiomyocytes/ fibroblasts",
354
- "hiPSC-CM",
355
- "iPSCs/HUVECs",
356
- "iPSC-CMs",
357
- "iPSCs",
358
- "H1395",
359
- "PC9",
360
- "H1650",
361
- "HULEC-5a",
362
- "NCI-H1703"
363
- ]
364
-
365
- PRINT_PARAM_NAMES = [
366
- "Physical Crosslinking Duration (s)",
367
- "Photo Crosslinking Duration (s)",
368
- "Extrusion Pressure (kPa)",
369
- "Nozzle Movement Speed (mm/s)",
370
- "Nozzle Diameter (µm)",
371
- "Syringe Temperature (°C)",
372
- "Substrate Temperature (°C)",
373
- ]
374
-
375
- @st.cache_resource
376
- def load_encoder():
377
- return joblib.load('cell_line_encoder.joblib')
378
-
379
- @st.cache_resource
380
- def load_scalers():
381
- return (
382
- joblib.load('scaler_printability.joblib'),
383
- joblib.load('scaler_cell_response.joblib')
384
- )
385
-
386
- @st.cache_resource
387
- def load_models():
388
- m_p = CatBoostClassifier(); m_p.load_model('catboost_printability.cbm')
389
- m_c = CatBoostClassifier(); m_c.load_model('catboost_cell_response.cbm')
390
- return m_p, m_c
391
-
392
- encoder = load_encoder()
393
- scaler_print, scaler_cell = load_scalers()
394
- model_print, model_cell = load_models()
395
-
396
- feature_order_print = list(scaler_print.feature_names_in_)
397
- feature_order_cell = list(scaler_cell.feature_names_in_)
398
-
399
- if 'bio_rows' not in st.session_state:
400
- st.session_state.bio_rows = [{
401
- 'mat': BIOMATERIAL_OPTIONS[0],
402
- 'min': 0.0, 'max': 10.0, 'step': 0.1
403
- }]
404
-
405
- if 'density_range' not in st.session_state:
406
- st.session_state.density_range = {'min': 0.0, 'max': 10.0, 'step': 0.1}
407
-
408
- if 'pp_ranges' not in st.session_state:
409
- st.session_state.pp_ranges = {
410
- "Physical Crosslinking Duration (s)": {'min': 0.0, 'max': 300.0, 'step': 5.0},
411
- "Photo Crosslinking Duration (s)": {'min': 0.0, 'max': 180.0, 'step': 5.0},
412
- "Extrusion Pressure (kPa)": {'min': 5.0, 'max': 200.0, 'step': 5.0},
413
- "Nozzle Movement Speed (mm/s)": {'min': 1.0, 'max': 20.0, 'step': 0.5},
414
- "Nozzle Diameter (µm)": {'min': 100.0,'max': 1000.0, 'step': 50.0}, # 100–1000 µm is practical
415
- "Syringe Temperature (°C)": {'min': 4.0, 'max': 40.0, 'step': 1.0},
416
- "Substrate Temperature (°C)": {'min': 4.0, 'max': 37.0, 'step': 1.0},
417
- }
418
-
419
- st.title("🧬 MLATE: Machine Learning Applications in Tissue Engieering")
420
- st.markdown(
421
- "<p style='font-size:1.2em; color:grey;'>"
422
- "A fully integrated Multi-Tissue, machine learning platform for prediction, optimization and generating procedures for fabricating 3D-(bio)printing scaffolds for tissue engineering. "
423
- "For more details, please refer to and cite our paper: "
424
- "<a href='https://doi.org/xxx' target='_blank'>https://doi.org/xxx</a>"
425
- "</p>",
426
- unsafe_allow_html=True
427
- )
428
-
429
- st.subheader("Biomaterials (enter range for each)")
430
- if st.button(" Add Biomaterial"):
431
- used = {r['mat'] for r in st.session_state.bio_rows}
432
- available = [m for m in BIOMATERIAL_OPTIONS if m not in used]
433
- if available:
434
- st.session_state.bio_rows.append({
435
- 'mat': available[0], 'min': 0.0, 'max': 10.0, 'step': 0.1
436
- })
437
- st.rerun()
438
-
439
- for i, row in enumerate(st.session_state.bio_rows):
440
- used_except_current = {
441
- r['mat'] for idx, r in enumerate(st.session_state.bio_rows) if idx != i
442
- }
443
- options = [m for m in BIOMATERIAL_OPTIONS if m not in used_except_current]
444
-
445
- c1, c2, c3, c4, c5 = st.columns([2, 1, 1, 1, 0.3])
446
- mat = c1.selectbox(
447
- "", options,
448
- index=options.index(row['mat']) if row['mat'] in options else 0,
449
- key=f"bio_mat_{i}"
450
- )
451
- st.session_state.bio_rows[i]['mat'] = mat
452
-
453
- mn = c2.number_input(
454
- "Min", min_value=0.0, max_value=row['max'],
455
- value=row['min'], step=row['step'], key=f"bio_min_{i}"
456
- )
457
- mx = c3.number_input(
458
- "Max", min_value=row['step'], max_value=100.0,
459
- value=max(row['max'], row['step']), step=row['step'],
460
- key=f"bio_max_{i}"
461
- )
462
- st.session_state.bio_rows[i].update(min=mn, max=mx)
463
-
464
- st.session_state.bio_rows[i]['step'] = c4.number_input(
465
- "Step", min_value=0.0,
466
- max_value=(mx - mn) if mx > mn else 0.1,
467
- value=row['step'], step=0.1, key=f"bio_step_{i}"
468
- )
469
-
470
- if c5.button("❌", key=f"rem_{i}"):
471
- st.session_state.bio_rows.pop(i)
472
- st.rerun()
473
-
474
- st.markdown("---")
475
-
476
- st.subheader("Cell Line & Density (10^6 cells/ml)")
477
- col1, col2, col3, col4 = st.columns([2,1,1,1])
478
- cell_line = col1.selectbox("Cell Line", CELL_LINE_OPTIONS)
479
-
480
- # ── Special handling for NoCellCultured (acellular 3D printing) ──
481
- if cell_line == "NoCellCultured":
482
- st.info("🧪 **Acellular 3D printing mode** – No cells will be included. Cell density is forced to 0.")
483
- st.session_state.density_range.update({'min': 0.0, 'max': 0.0, 'step': 0.0})
484
-
485
- col2.number_input("Min Density", value=0.0, disabled=True, key="cd_min")
486
- col3.number_input("Max Density", value=0.0, disabled=True, key="cd_max")
487
- col4.number_input("Step", value=0.0, disabled=True, key="cd_step")
488
- else:
489
- # Reset to sensible defaults if coming from NoCellCultured
490
- if st.session_state.density_range['max'] == 0.0:
491
- st.session_state.density_range.update({'min': 1.0, 'max': 20.0, 'step': 0.5})
492
-
493
- dr = st.session_state.density_range
494
- dmin = col2.number_input(
495
- "Min Density", min_value=0.0, max_value=dr['max'],
496
- value=dr['min'], step=dr['step'], key="cd_min"
497
- )
498
- dmax = col3.number_input(
499
- "Max Density", min_value=dr['step'], max_value=1000.0,
500
- value=max(dr['max'], dr['step']), step=dr['step'], key="cd_max"
501
- )
502
- dstep = col4.number_input(
503
- "Step", min_value=0.0,
504
- max_value=(dmax - dmin) if dmax > dmin else 0.1,
505
- value=dr['step'], step=0.1, key="cd_step"
506
- )
507
- st.session_state.density_range.update({'min': dmin, 'max': dmax, 'step': dstep})
508
-
509
- st.markdown("---")
510
-
511
- st.markdown("---")
512
- st.subheader("Crosslinking Settings")
513
-
514
- col_cross1, col_cross2 = st.columns(2)
515
-
516
- disable_physical = col_cross1.checkbox(
517
- "Disable Physical Crosslinking",
518
- value=False,
519
- help="Check if you do not want physical/ionic crosslinking (e.g. CaCl₂ bath, temperature-induced)"
520
- )
521
-
522
- disable_photo = col_cross2.checkbox(
523
- "Disable Photo Crosslinking",
524
- value=False,
525
- help="Check if you do not want UV/visible light crosslinking"
526
- )
527
-
528
- st.subheader("Printing Parameters (enter range)")
529
-
530
- for name in PRINT_PARAM_NAMES:
531
- # Special handling for crosslinking durations
532
- if name == "Physical Crosslinking Duration (s)" and disable_physical:
533
- st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
534
- c1, c2, c3, c4 = st.columns([2,1,1,1])
535
- c1.write(name + " (DISABLED)")
536
- c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
537
- c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
538
- c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
539
- continue
540
-
541
- elif name == "Photo Crosslinking Duration (s)" and disable_photo:
542
- st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
543
- c1, c2, c3, c4 = st.columns([2,1,1,1])
544
- c1.write(name + " (DISABLED)")
545
- c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
546
- c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
547
- c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
548
- continue
549
-
550
- # Normal handling for all other parameters
551
- pmin = st.session_state.pp_ranges[name]['min']
552
- pmax = st.session_state.pp_ranges[name]['max']
553
- pstep = st.session_state.pp_ranges[name]['step']
554
-
555
- c1, c2, c3, c4 = st.columns([2,1,1,1])
556
- c1.write(name)
557
-
558
- pmin = c2.number_input(
559
- "Min", min_value=0.0, max_value=pmax,
560
- value=pmin, step=pstep, key=f"pp_min_{name}"
561
- )
562
- pmax = c3.number_input(
563
- "Max", min_value=pstep, max_value=10000.0,
564
- value=max(pmax, pstep), step=pstep, key=f"pp_max_{name}"
565
- )
566
- pstep = c4.number_input(
567
- "Step", min_value=0.0,
568
- max_value=(pmax - pmin) if pmax > pmin else 1.0,
569
- value=pstep, step=max(1e-3, pstep/10),
570
- key=f"pp_step_{name}"
571
- )
572
- st.session_state.pp_ranges[name].update(min=pmin, max=pmax, step=pstep)
573
- st.markdown("---")
574
-
575
- if st.button("🛠️ Optimize WSSQ"):
576
- with st.spinner("Running Optuna…"):
577
- def objective(trial):
578
- bi_vals = {
579
- r['mat']: trial.suggest_float(
580
- f"bio__{r['mat']}", r['min'], r['max'], step=r['step']
581
- )
582
- for r in st.session_state.bio_rows
583
- }
584
- for m in BIOMATERIAL_OPTIONS:
585
- bi_vals.setdefault(m, 0.0)
586
-
587
- cd = 0.0 if cell_line=="NoCellCultured" else trial.suggest_float(
588
- "cell_density", dr['min'], dr['max'], step=dr['step']
589
- )
590
-
591
- pp_vals = {
592
- name: trial.suggest_float(
593
- f"pp__{name}",
594
- st.session_state.pp_ranges[name]['min'],
595
- st.session_state.pp_ranges[name]['max'],
596
- step=st.session_state.pp_ranges[name]['step']
597
- )
598
- for name in PRINT_PARAM_NAMES
599
- }
600
-
601
- feat = {**bi_vals, **pp_vals}
602
- feat["Cell Density (cells/mL)"] = cd
603
- feat.update(
604
- encoder.transform(pd.DataFrame({"Cell Line":[cell_line]}))
605
- .iloc[0].to_dict()
606
- )
607
-
608
- X = pd.DataFrame([feat])
609
- Xp = X.reindex(columns=feature_order_print, fill_value=0.0)
610
- Xc = X.reindex(columns=feature_order_cell, fill_value=0.0)
611
-
612
- p_proba = model_print.predict_proba(scaler_print.transform(Xp))[0]
613
- c_proba = model_cell .predict_proba(scaler_cell .transform(Xc))[0]
614
- exp_p = float(np.dot(p_proba, model_print.classes_.astype(float)))
615
- exp_c = float(np.dot(c_proba, model_cell .classes_.astype(float)))
616
-
617
- np.random.seed(42)
618
- return scaffold_quality_combined(exp_p, exp_c)
619
-
620
- sampler = optuna.samplers.TPESampler(
621
- seed=42,
622
- n_startup_trials=30,
623
- multivariate=True,
624
- group=True,
625
- consider_prior=True
626
- )
627
-
628
- study = optuna.create_study(
629
- direction="maximize",
630
- sampler=sampler,
631
- pruner=optuna.pruners.MedianPruner()
632
- )
633
- study.optimize(objective, n_trials=300)
634
- best = study.best_trial
635
-
636
- st.success(f"🏆 Best WSSQ: **{best.value:.3f}**")
637
-
638
- best_df = pd.Series(best.params, name="value") \
639
- .rename_axis("parameter") \
640
- .to_frame()
641
- st.table(best_df)
642
-
643
- st.markdown("## 🧪 Fabrication Procedure")
644
- with st.spinner("Generating rigorous fabrication procedure…"):
645
-
646
- formatted_params = "\n".join([
647
- f"- {k.replace('bio__', 'Biomaterial: ').replace('pp__', 'Print Setting: ')}: {v:.2f}"
648
- for k, v in best.params.items()
649
- ])
650
-
651
- prompt = (
652
- f"Please act as a senior tissue engineer with 15+ years of hands-on experience in 3D bioprinting for regenerative medicine. "
653
- f"Write a **highly practical, bench-ready laboratory fabrication protocol** for fabricating a scaffold. "
654
- f"Assume the reader is an experienced experimentalist who routinely works in a tissue engineering lab.\n\n"
655
-
656
- f"**Use exactly these inputs to tailor every step:**\n"
657
- f"Target Cell Line: {cell_line}\n"
658
- f"Parameters:\n{formatted_params}\n\n"
659
-
660
- f"**Critical requirements for the protocol (you MUST follow all of them):**\n"
661
- f"• If Target Cell Line is 'NoCellCultured', this is **acellular 3D printing** (not bioprinting). Remove all references to cells, cell viability, cell density, and cell culturing. The final scaffold is cell-free. Change section 6 title to 'Post-Printing Incubation & Storage Instructions' and adapt its content accordingly.\n"
662
- f"• If any suggested parameter is physically unrealistic (e.g. nozzle diameter 9 µm or syringe temp 2°C) or the nozzle diameter is very small relative to the cell diameter of the target cell line (when cells are used), adjust it slightly in the protocol and explicitly note the adjustment with justification.\n"
663
- f"• Every quantity must be given in precise, measurable lab units (e.g., 2.5 mL, 1.2 % w/v, 10 mg/mL, 37 °C, 5 min, 150 rpm).\n"
664
- f"• Include exact timings, temperatures, and workflow order to protect structural fidelity (and cell viability >85 % post-print when cells are used).\n"
665
- f"• Anticipate and explicitly address common bioprinting pitfalls relevant to the given parameters (nozzle clogging, shear-induced cell death, premature gelation, filament fusion, air bubbles, etc.) and give precise mitigation steps.\n"
666
- f"• Use only reagents and equipment that are standard in tissue engineering labs; if a specific brand/model is implied by the parameters, note a common equivalent.\n"
667
- f"• Include simple quality-control checkpoints (visual inspection, live/dead staining timing when cells are used, etc.).\n\n"
668
-
669
- f"Your response must be structured **exactly** with the following sections (no extra sections, no introductory text, no summary, no conclusions):\n"
670
- f"1. Required Materials & Equipment\n"
671
- f"2. Sterilization & Safety Precautions\n"
672
- f"3. Bioink Preparation\n"
673
- f"4. 3D Bioprinting Settings & Execution\n"
674
- f"5. Post-processing & Crosslinking\n"
675
- f"6. Cell Culturing & Incubation Instructions"
676
- )
677
-
678
- resp = client.models.generate_content(
679
- model="gemini-2.5-flash-lite",
680
- contents=prompt,
681
- config=types.GenerateContentConfig(
682
- system_instruction=(
683
- "You are a senior tissue engineer and expert experimentalist specializing in translating optimized bioprinting parameters into reproducible, high-viability laboratory protocols. "
684
- "Your protocols are used daily by PhD students and post-docs in regenerative medicine labs. "
685
- "You always prioritize: (1) maximum cell viability and function, (2) structural fidelity of the printed construct, (3) workflow efficiency under sterile conditions, and (4) safety. "
686
- "Write in clear, imperative, step-by-step language with numbered or bulleted sub-steps. "
687
- "Never be vague — give exact volumes, times, temperatures, speeds, and concentrations. "
688
- "Never add disclaimers or theoretical background unless explicitly asked."
689
- ),
690
- temperature=0.1, # lowered for maximum determinism
691
- top_p=0.85,
692
- max_output_tokens=6144
693
- )
694
- )
695
-
696
- procedure = resp.text
697
- st.markdown(procedure)
 
1
+ import os
2
+ import streamlit as st
3
+ import pandas as pd
4
+ import joblib
5
+ import optuna
6
+ import numpy as np
7
+ from catboost import CatBoostClassifier
8
+
9
+ from google import genai
10
+ from google.genai import types
11
+
12
+ from biomaterials import BIOMATERIAL_OPTIONS
13
+ from cell_lines import CELL_LINE_OPTIONS
14
+
15
+ _original_number_input = st.number_input
16
+
17
+ def safe_number_input(label, **kwargs):
18
+ """
19
+ Clamp `value` into [min_value, max_value] and warn if we had to adjust.
20
+ Then call the real st.number_input with the clamped default.
21
+ """
22
+ min_value = kwargs.get("min_value", float("-inf"))
23
+ max_value = kwargs.get("max_value", float("inf"))
24
+ value = kwargs.get("value", min_value)
25
+ clamped = min(max(value, min_value), max_value)
26
+ if clamped != value:
27
+ st.warning(
28
+ f"⚠️ Default for “{label}” ({value}) was outside "
29
+ f"[{min_value}, {max_value}]; using {clamped} instead."
30
+ )
31
+ kwargs["value"] = clamped
32
+ return _original_number_input(label, **kwargs)
33
+
34
+ st.number_input = safe_number_input
35
+
36
+ gemini_key = 'kldashgja;fjde;fajed;j'
37
+ # gemini_key = os.getenv("GEMINI_API_KEY")
38
+ if not gemini_key:
39
+ st.error("Gemini API key not found. Please set GEMINI_API_KEY in your Space settings.")
40
+ st.stop()
41
+
42
+ client = genai.Client(api_key=gemini_key)
43
+
44
+ def scaffold_quality_combined(printability, cell_response,
45
+ weight_printability=0.3, weight_cell_response=0.7):
46
+ """
47
+ Calculates the Weighted Scaffold Synthesis Quality (WSSQ).
48
+ """
49
+ if printability == 0:
50
+ return 0.0
51
+
52
+ # Normalization
53
+ norm_p = printability / 3.0
54
+
55
+ # If cell_response is 1 (minimum), avoid division by zero in harmonic mean
56
+ if cell_response <= 1:
57
+ return 100 * norm_p
58
+
59
+ norm_c = (cell_response - 1) / 4.0
60
+
61
+ # Weighted Harmonic Mean
62
+ hm = (weight_printability + weight_cell_response) / (
63
+ (weight_printability / norm_p) +
64
+ (weight_cell_response / norm_c)
65
+ )
66
+
67
+ # Weighted Multiplicative Component
68
+ mc = (norm_p**weight_printability) * (norm_c**weight_cell_response)
69
+
70
+ return 100 * ((hm + mc) / 2.0)
71
+
72
+ PRINT_PARAM_NAMES = [
73
+ "Physical Crosslinking Duration (s)",
74
+ "Photo Crosslinking Duration (s)",
75
+ "Extrusion Pressure (kPa)",
76
+ "Nozzle Movement Speed (mm/s)",
77
+ "Nozzle Diameter (µm)",
78
+ "Syringe Temperature (°C)",
79
+ "Substrate Temperature (°C)",
80
+ ]
81
+
82
+ @st.cache_resource
83
+ def load_encoder():
84
+ return joblib.load('cell_line_encoder.joblib')
85
+
86
+ @st.cache_resource
87
+ def load_scalers():
88
+ return (
89
+ joblib.load('scaler_printability.joblib'),
90
+ joblib.load('scaler_cell_response.joblib')
91
+ )
92
+
93
+ @st.cache_resource
94
+ def load_models():
95
+ m_p = CatBoostClassifier(); m_p.load_model('catboost_printability.cbm')
96
+ m_c = CatBoostClassifier(); m_c.load_model('catboost_cell_response.cbm')
97
+ return m_p, m_c
98
+
99
+ encoder = load_encoder()
100
+ scaler_print, scaler_cell = load_scalers()
101
+ model_print, model_cell = load_models()
102
+
103
+ feature_order_print = list(scaler_print.feature_names_in_)
104
+ feature_order_cell = list(scaler_cell.feature_names_in_)
105
+
106
+ if 'bio_rows' not in st.session_state:
107
+ st.session_state.bio_rows = [{
108
+ 'mat': BIOMATERIAL_OPTIONS[0],
109
+ 'min': 0.0, 'max': 10.0, 'step': 0.1
110
+ }]
111
+
112
+ if 'density_range' not in st.session_state:
113
+ st.session_state.density_range = {'min': 0.0, 'max': 10.0, 'step': 0.1}
114
+
115
+ if 'pp_ranges' not in st.session_state:
116
+ st.session_state.pp_ranges = {
117
+ "Physical Crosslinking Duration (s)": {'min': 0.0, 'max': 300.0, 'step': 5.0},
118
+ "Photo Crosslinking Duration (s)": {'min': 0.0, 'max': 180.0, 'step': 5.0},
119
+ "Extrusion Pressure (kPa)": {'min': 5.0, 'max': 200.0, 'step': 5.0},
120
+ "Nozzle Movement Speed (mm/s)": {'min': 1.0, 'max': 20.0, 'step': 0.5},
121
+ "Nozzle Diameter (µm)": {'min': 100.0,'max': 1000.0, 'step': 50.0},
122
+ "Syringe Temperature (°C)": {'min': 4.0, 'max': 40.0, 'step': 1.0},
123
+ "Substrate Temperature (°C)": {'min': 4.0, 'max': 37.0, 'step': 1.0},
124
+ }
125
+
126
+ # --- Sidebar UI for Weights ---
127
+ st.sidebar.header("⚖️ Optimization Weights")
128
+ # User only controls Cell Response (0 to 100)
129
+ w_cell_pct = st.sidebar.slider("Cell Response Weight (%)", min_value=0, max_value=100, value=70, step=5)
130
+ # Printability is dynamically calculated and cannot be changed manually
131
+ w_print_pct = 100 - w_cell_pct
132
+ st.sidebar.number_input("Printability Weight (%)", value=w_print_pct, disabled=True, help="Auto-calculated to ensure sum is 100%")
133
+
134
+ # Convert back to 0.0 - 1.0 for the mathematical formula
135
+ w_cell = w_cell_pct / 100.0
136
+ w_print = w_print_pct / 100.0
137
+
138
+ st.title("🧬 MLATE: Machine Learning Applications in Tissue Engieering")
139
+ st.markdown(
140
+ "<p style='font-size:1.2em; color:grey;'>"
141
+ "A fully integrated Multi-Tissue, machine learning platform for prediction, optimization and generating procedures for fabricating 3D-(bio)printing scaffolds for tissue engineering. "
142
+ "For more details, please refer to and cite our paper: "
143
+ "<a href='https://doi.org/xxx' target='_blank'>https://doi.org/xxx</a>"
144
+ "</p>",
145
+ unsafe_allow_html=True
146
+ )
147
+
148
+ st.subheader("Biomaterials (enter range for each)")
149
+ if st.button(" Add Biomaterial"):
150
+ used = {r['mat'] for r in st.session_state.bio_rows}
151
+ available = [m for m in BIOMATERIAL_OPTIONS if m not in used]
152
+ if available:
153
+ st.session_state.bio_rows.append({
154
+ 'mat': available[0], 'min': 0.0, 'max': 10.0, 'step': 0.1
155
+ })
156
+ st.rerun()
157
+
158
+ for i, row in enumerate(st.session_state.bio_rows):
159
+ used_except_current = {
160
+ r['mat'] for idx, r in enumerate(st.session_state.bio_rows) if idx != i
161
+ }
162
+ options = [m for m in BIOMATERIAL_OPTIONS if m not in used_except_current]
163
+
164
+ c1, c2, c3, c4, c5 = st.columns([2, 1, 1, 1, 0.3])
165
+ mat = c1.selectbox(
166
+ "", options,
167
+ index=options.index(row['mat']) if row['mat'] in options else 0,
168
+ key=f"bio_mat_{i}"
169
+ )
170
+ st.session_state.bio_rows[i]['mat'] = mat
171
+
172
+ mn = c2.number_input(
173
+ "Min", min_value=0.0, max_value=row['max'],
174
+ value=row['min'], step=row['step'], key=f"bio_min_{i}"
175
+ )
176
+ mx = c3.number_input(
177
+ "Max", min_value=row['step'], max_value=100.0,
178
+ value=max(row['max'], row['step']), step=row['step'],
179
+ key=f"bio_max_{i}"
180
+ )
181
+ st.session_state.bio_rows[i].update(min=mn, max=mx)
182
+
183
+ st.session_state.bio_rows[i]['step'] = c4.number_input(
184
+ "Step", min_value=0.0,
185
+ max_value=(mx - mn) if mx > mn else 0.1,
186
+ value=row['step'], step=0.1, key=f"bio_step_{i}"
187
+ )
188
+
189
+ if c5.button("", key=f"rem_{i}"):
190
+ st.session_state.bio_rows.pop(i)
191
+ st.rerun()
192
+
193
+ st.markdown("---")
194
+
195
+ st.subheader("Cell Line & Density (10^6 cells/ml)")
196
+ col1, col2, col3, col4 = st.columns([2,1,1,1])
197
+
198
+ cell_line = col1.selectbox("Cell Line", CELL_LINE_OPTIONS, key="cell_line_select")
199
+
200
+ if cell_line == "NoCellCultured":
201
+ st.info("🧪 **Acellular 3D printing mode** – No cells will be included. Cell density is forced to 0.")
202
+ st.session_state.density_range.update({'min': 0.0, 'max': 0.0, 'step': 0.0})
203
+
204
+ col2.number_input("Min Density", value=0.0, disabled=True, key="cd_min")
205
+ col3.number_input("Max Density", value=0.0, disabled=True, key="cd_max")
206
+ col4.number_input("Step", value=0.0, disabled=True, key="cd_step")
207
+ else:
208
+ if st.session_state.density_range.get('max', 0) <= 0.1:
209
+ st.session_state.density_range.update({'min': 1.0, 'max': 20.0, 'step': 0.5})
210
+
211
+ dr = st.session_state.density_range
212
+ dmin = col2.number_input(
213
+ "Min Density",
214
+ min_value=0.0,
215
+ max_value=dr['max'],
216
+ value=dr['min'],
217
+ step=dr['step'],
218
+ key="cd_min"
219
+ )
220
+ dmax = col3.number_input(
221
+ "Max Density",
222
+ min_value=dr['step'],
223
+ max_value=1000.0,
224
+ value=max(dr['max'], dr['step']),
225
+ step=dr['step'],
226
+ key="cd_max"
227
+ )
228
+ dstep = col4.number_input(
229
+ "Step",
230
+ min_value=0.0,
231
+ max_value=(dmax - dmin) if dmax > dmin else 0.1,
232
+ value=dr['step'],
233
+ step=0.1,
234
+ key="cd_step"
235
+ )
236
+ st.session_state.density_range.update({'min': dmin, 'max': dmax, 'step': dstep})
237
+
238
+ st.markdown("---")
239
+
240
+ st.subheader("Crosslinking Settings")
241
+
242
+ col_cross1, col_cross2 = st.columns(2)
243
+
244
+ disable_physical = col_cross1.checkbox(
245
+ "Disable Physical Crosslinking",
246
+ value=False,
247
+ help="Check if you do not want physical/ionic crosslinking (e.g. CaCl₂ bath, temperature-induced)"
248
+ )
249
+
250
+ disable_photo = col_cross2.checkbox(
251
+ "Disable Photo Crosslinking",
252
+ value=False,
253
+ help="Check if you do not want UV/visible light crosslinking"
254
+ )
255
+
256
+ st.subheader("Printing Parameters (enter range)")
257
+
258
+ for name in PRINT_PARAM_NAMES:
259
+ if name == "Physical Crosslinking Duration (s)" and disable_physical:
260
+ st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
261
+ c1, c2, c3, c4 = st.columns([2,1,1,1])
262
+ c1.write(name + " (DISABLED)")
263
+ c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
264
+ c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
265
+ c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
266
+ continue
267
+
268
+ elif name == "Photo Crosslinking Duration (s)" and disable_photo:
269
+ st.session_state.pp_ranges[name].update({'min': 0.0, 'max': 0.0, 'step': 0.0})
270
+ c1, c2, c3, c4 = st.columns([2,1,1,1])
271
+ c1.write(name + " (DISABLED)")
272
+ c2.number_input("Min", value=0.0, disabled=True, key=f"pp_min_{name}")
273
+ c3.number_input("Max", value=0.0, disabled=True, key=f"pp_max_{name}")
274
+ c4.number_input("Step", value=0.0, disabled=True, key=f"pp_step_{name}")
275
+ continue
276
+
277
+ pmin = st.session_state.pp_ranges[name]['min']
278
+ pmax = st.session_state.pp_ranges[name]['max']
279
+ pstep = st.session_state.pp_ranges[name]['step']
280
+
281
+ c1, c2, c3, c4 = st.columns([2,1,1,1])
282
+ c1.write(name)
283
+
284
+ pmin = c2.number_input(
285
+ "Min", min_value=0.0, max_value=pmax,
286
+ value=pmin, step=pstep, key=f"pp_min_{name}"
287
+ )
288
+ pmax = c3.number_input(
289
+ "Max", min_value=pstep, max_value=10000.0,
290
+ value=max(pmax, pstep), step=pstep, key=f"pp_max_{name}"
291
+ )
292
+ pstep = c4.number_input(
293
+ "Step", min_value=0.0,
294
+ max_value=(pmax - pmin) if pmax > pmin else 1.0,
295
+ value=pstep, step=max(1e-3, pstep/10),
296
+ key=f"pp_step_{name}"
297
+ )
298
+ st.session_state.pp_ranges[name].update(min=pmin, max=pmax, step=pstep)
299
+ st.markdown("---")
300
+
301
+ if st.button("🛠️ Optimize WSSQ"):
302
+ with st.spinner("Running Optuna…"):
303
+ def objective(trial):
304
+ bi_vals = {
305
+ r['mat']: trial.suggest_float(
306
+ f"bio__{r['mat']}", r['min'], r['max'], step=r['step']
307
+ )
308
+ for r in st.session_state.bio_rows
309
+ }
310
+ for m in BIOMATERIAL_OPTIONS:
311
+ bi_vals.setdefault(m, 0.0)
312
+
313
+ cd = 0.0 if cell_line=="NoCellCultured" else trial.suggest_float(
314
+ "cell_density", dr['min'], dr['max'], step=dr['step']
315
+ )
316
+
317
+ pp_vals = {
318
+ name: trial.suggest_float(
319
+ f"pp__{name}",
320
+ st.session_state.pp_ranges[name]['min'],
321
+ st.session_state.pp_ranges[name]['max'],
322
+ step=st.session_state.pp_ranges[name]['step']
323
+ )
324
+ for name in PRINT_PARAM_NAMES
325
+ }
326
+
327
+ feat = {**bi_vals, **pp_vals}
328
+ feat["Cell Density (cells/mL)"] = cd
329
+ feat.update(
330
+ encoder.transform(pd.DataFrame({"Cell Line":[cell_line]}))
331
+ .iloc[0].to_dict()
332
+ )
333
+
334
+ X = pd.DataFrame([feat])
335
+ Xp = X.reindex(columns=feature_order_print, fill_value=0.0)
336
+ Xc = X.reindex(columns=feature_order_cell, fill_value=0.0)
337
+
338
+ p_proba = model_print.predict_proba(scaler_print.transform(Xp))[0]
339
+ c_proba = model_cell .predict_proba(scaler_cell .transform(Xc))[0]
340
+ exp_p = float(np.dot(p_proba, model_print.classes_.astype(float)))
341
+ exp_c = float(np.dot(c_proba, model_cell .classes_.astype(float)))
342
+
343
+ np.random.seed(42)
344
+ # Use dynamic weights from the sidebar sliders
345
+ return scaffold_quality_combined(
346
+ exp_p,
347
+ exp_c,
348
+ weight_printability=w_print,
349
+ weight_cell_response=w_cell
350
+ )
351
+
352
+ sampler = optuna.samplers.TPESampler(
353
+ seed=42,
354
+ n_startup_trials=30,
355
+ multivariate=True,
356
+ group=True,
357
+ consider_prior=True
358
+ )
359
+
360
+ study = optuna.create_study(
361
+ direction="maximize",
362
+ sampler=sampler,
363
+ pruner=optuna.pruners.MedianPruner()
364
+ )
365
+ study.optimize(objective, n_trials=300)
366
+
367
+ # Store results in session state to persist after rerun
368
+ st.session_state.best_params = study.best_trial.params
369
+ st.session_state.best_value = study.best_trial.value
370
+ st.session_state.optimized_cell_line = cell_line
371
+
372
+ if 'best_params' in st.session_state:
373
+ st.success(f"🏆 Best WSSQ: **{st.session_state.best_value:.3f}**")
374
+ best_df = pd.Series(st.session_state.best_params, name="value") \
375
+ .rename_axis("parameter") \
376
+ .to_frame()
377
+ st.table(best_df)
378
+
379
+ st.markdown("---")
380
+ st.subheader("📝 Customize Fabrication Protocol")
381
+ user_inquiry = st.text_area(
382
+ "Add specific limitations, equipment, or extra requirements:",
383
+ placeholder="e.g., I only have a 25G nozzle available, or I need to use a specific UV intensity of 10mW/cm²...",
384
+ key="user_inquiry"
385
+ )
386
+
387
+ if st.button("🚀 Generate Fabrication Procedure"):
388
+ with st.spinner("Generating rigorous fabrication procedure…"):
389
+
390
+ formatted_params = "\n".join([
391
+ f"- {k.replace('bio__', 'Biomaterial: ').replace('pp__', 'Print Setting: ')}: {v:.2f}"
392
+ for k, v in st.session_state.best_params.items()
393
+ ])
394
+
395
+ # Base prompt (remains unchanged)
396
+ base_prompt = (
397
+ f"Please act as a senior tissue engineer with 15+ years of hands-on experience in 3D bioprinting for regenerative medicine. "
398
+ f"Write a **highly practical, bench-ready laboratory fabrication protocol** for fabricating a scaffold. "
399
+ f"Assume the reader is an experienced experimentalist who routinely works in a tissue engineering lab.\n\n"
400
+ f"**Use exactly these inputs to tailor every step:**\n"
401
+ f"Target Cell Line: {st.session_state.optimized_cell_line}\n"
402
+ f"Parameters:\n{formatted_params}\n\n"
403
+ f"**Critical requirements for the protocol (you MUST follow all of them):**\n"
404
+ f"• If Target Cell Line is 'NoCellCultured', this is **acellular 3D printing** (not bioprinting). Remove all references to cells, cell viability, cell density, and cell culturing. The final scaffold is cell-free. Change section 6 title to 'Post-Printing Incubation & Storage Instructions' and adapt its content accordingly.\n"
405
+ f"• If any suggested parameter is physically unrealistic (e.g. nozzle diameter 9 µm or syringe temp 2°C) or the nozzle diameter is very small relative to the cell diameter of the target cell line (when cells are used), adjust it slightly in the protocol and explicitly note the adjustment with justification.\n"
406
+ f"• Every quantity must be given in precise, measurable lab units (e.g., 2.5 mL, 1.2 % w/v, 10 mg/mL, 37 °C, 5 min, 150 rpm).\n"
407
+ f"• Include exact timings, temperatures, and workflow order to protect structural fidelity (and cell viability >85 % post-print when cells are used).\n"
408
+ f"• Anticipate and explicitly address common bioprinting pitfalls relevant to the given parameters (nozzle clogging, shear-induced cell death, premature gelation, filament fusion, air bubbles, etc.) and give precise mitigation steps.\n"
409
+ f"• Use only reagents and equipment that are standard in tissue engineering labs; if a specific brand/model is implied by the parameters, note a common equivalent.\n"
410
+ f" Include simple quality-control checkpoints (visual inspection, live/dead staining timing when cells are used, etc.).\n\n"
411
+ f"Your response must be structured **exactly** with the following sections (no extra sections, no introductory text, no summary, no conclusions):\n"
412
+ f"1. Required Materials & Equipment\n"
413
+ f"2. Sterilization & Safety Precautions\n"
414
+ f"3. Bioink Preparation\n"
415
+ f"4. 3D Bioprinting Settings & Execution\n"
416
+ f"5. Post-processing & Crosslinking\n"
417
+ f"6. Cell Culturing & Incubation Instructions\n"
418
+ )
419
+
420
+ # Append user inquiry if provided
421
+ final_prompt = base_prompt
422
+ if user_inquiry:
423
+ final_prompt += f"\n**Additional User Constraints & Inquiries (Integrate these into the protocol):**\n{user_inquiry}"
424
+
425
+ resp = client.models.generate_content(
426
+ model="gemini-2.5-flash-lite",
427
+ contents=final_prompt,
428
+ config=types.GenerateContentConfig(
429
+ system_instruction=(
430
+ "You are a senior tissue engineer and expert experimentalist specializing in translating optimized bioprinting parameters into reproducible, high-viability laboratory protocols. "
431
+ "Your protocols are used daily by PhD students and post-docs in regenerative medicine labs. "
432
+ "You always prioritize: (1) maximum cell viability and function, (2) structural fidelity of the printed construct, (3) workflow efficiency under sterile conditions, and (4) safety. "
433
+ "Write in clear, imperative, step-by-step language with numbered or bulleted sub-steps. "
434
+ "Never be vague — give exact volumes, times, temperatures, speeds, and concentrations. "
435
+ "Never add disclaimers or theoretical background unless explicitly asked."
436
+ ),
437
+ temperature=0.1,
438
+ top_p=0.85,
439
+ max_output_tokens=6144
440
+ )
441
+ )
442
+
443
+ st.markdown("## 🧪 Fabrication Procedure")
444
+ st.markdown(resp.text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
biomaterials.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ BIOMATERIAL_OPTIONS = [
2
+ "Alginate (%w/v)",
3
+ "PVA-HA (%w/v)",
4
+ "CaSO4 (%w/v)",
5
+ "Na2HPO4 (%w/v)",
6
+ "Gelatin (%w/v)",
7
+ "GelMA (%w/v)",
8
+ "laponite (%w/v)",
9
+ "graphene oxide (%w/v)",
10
+ "hydroxyapatite (%w/v)",
11
+ "Hyaluronic_Acid (%w/v)",
12
+ "hyaluronan metacrylate (%w/v)",
13
+ "NorHA (%w/v)",
14
+ "Fibroin/Fibrinogen (%w/v)",
15
+ "Pluronic P-123 (%w/v)",
16
+ "Collagen (%w/v)",
17
+ "Chitosan (%w/v)",
18
+ "CS-AEMA (%w/v)",
19
+ "RGD (mM)",
20
+ "TCP (%w/v)",
21
+ "Gellan (%w/v)",
22
+ "bioactive glass (%w/v)",
23
+ "Nano/Methycellulose (%w/v)",
24
+ "PEGTA (%w/v)",
25
+ "PEGMA (%w/v)",
26
+ "PEGDA (%w/v)",
27
+ "Agarose (%w/v)",
28
+ " hyaluronic acid+ Ph moieties (%w/v)",
29
+ "matrigel (%w/v)",
30
+ "CaCl2(mM)",
31
+ "NaCl(mM)",
32
+ "BaCl2(mM)",
33
+ "SrCl2(mM)",
34
+ "CaCO3 (mM)",
35
+ "Genipin (%w/v)",
36
+ "PVA (%wt)",
37
+ "trans-glutaminase (%w/v)",
38
+ "alginate lyase (U/ml)",
39
+ "D-glucose (%w/v)",
40
+ "PLGA (%w/v)",
41
+ "vascular tissued-derived dECM (%w/v)",
42
+ "PEG-8-SH (mM)",
43
+ "Alginate dialdehyde (%w/v)",
44
+ "Alginate sulfate (%w/v)",
45
+ "RGD-modified alginate (%w/v)",
46
+ "poly(N-isopropylacrylamide) grafted hyaluronan (%w/v)",
47
+ "chondroitin sulfate methacrylate (%w/v)",
48
+ "PCL (%w/v)",
49
+ "alginate methacrylate (%w/v)",
50
+ "HRP (U/ml)",
51
+ "Pluronic F127 (%w/v)/Lutrol F127 (%w/v)",
52
+ "Irgacure 2959 (%w/v)",
53
+ "Eosin Y (%w/v)",
54
+ "Ruthenium (mM)",
55
+ "sodium persulfate (SPS) (mM)",
56
+ "HEPES (mM)",
57
+ "LAP (%w/v)",
58
+ "glutaraldehyde (%w/v)",
59
+ "PBS (M)",
60
+ "glycerol (%w/v)",
61
+ "cECM (%w/v)",
62
+ "gel-fu(%w/v)",
63
+ "Rose Bengal (%w/v)",
64
+ "Vitamin B2(%w/v)",
65
+ "VEGF(%w/v)",
66
+ "Polypyrrole:PSS(%w/v)",
67
+ "boratebioactiveglass(%w/v)",
68
+ "astaxanthin(%w/v)",
69
+ "PRP (%v/v)",
70
+ "methacrylated collagen (%w/v)",
71
+ "α-Toc (µM)",
72
+ "ascorbic acid (mM)",
73
+ "Liver dECM(%w/v)",
74
+ "galactosylated alginate (%w/v)",
75
+ "SC-PEG(%w/v)",
76
+ "SFMA-L(%w/v)",
77
+ "SFMA-M(%w/v)",
78
+ "SFMA-H(%w/v)",
79
+ "KdECMMA(%w/v)",
80
+ "BA silk fibronin (%w/v)",
81
+ "Carrageenan(%v)",
82
+ "Carbopol ETD 2020 NF (%w/v)",
83
+ "Carbopol Ultrez 10 NF(%w/v)",
84
+ "Carbopol NF-980(%w/v)",
85
+ "FBS (%v/v)",
86
+ "MeTro (%w/v)",
87
+ "Triethanolamine (%v/v)",
88
+ "PEG-Fibrinogen (%w/v)",
89
+ "polyethylene glycol dimethacrylate (%w/v)",
90
+ "aprotinin (µg/ml)",
91
+ "gold nanorod (mg/mL)",
92
+ "egg white (w/v)",
93
+ "1-Vinyl-2-Pyrrolidione (v/v)",
94
+ "carboxyl functionalized carbon nanotubes (%w/v)",
95
+ "polyHIPE (%w/v)",
96
+ "β-D galactose (mM)",
97
+ "hydrogen peroxide (H2O2) (%v/v)",
98
+ "lactic acid v/v",
99
+ "NorCol (%w/v)",
100
+ "DDT (%w/v)",
101
+ "ammonium persulfate (mM)",
102
+ "diTyr-RGD (mM)",
103
+ "PHEG-Tyr (%w/v)",
104
+ "MMP2-degradable peptide (%w/v)",
105
+ "KdECM (%w/v)",
106
+ "EDC (mg)",
107
+ "NHS (mg)",
108
+ "VA086 (%w/v)",
109
+ "PGS (%w/v)",
110
+ "thiolated HA (%w/v)",
111
+ "boron nitride nanotubes (%w/v)",
112
+ "PEDOT:PSS (ul)",
113
+ "KCl (mM)",
114
+ "skeletal muscle ECM methacrylate (%w/v)",
115
+ "PEO (%w/v)",
116
+ "Carbon dots (mg/ml)",
117
+ "Laminin (ug/ml)",
118
+ "DF-PEG (%w/v)",
119
+ "omenta ECM (%w/v)",
120
+ "thrombin (unit/ml)",
121
+ "Carbon nanotube (CNT) (w/v)",
122
+ "Phytagel(%v)",
123
+ "Laponite-XLG (%w/w)"
124
+ ]
cell_lines.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CELL_LINE_OPTIONS = [
2
+ "NoCellCultured",
3
+ "chondrocyteyte",
4
+ "HepG2",
5
+ "bMSCs",
6
+ "HUVECs",
7
+ "NIH3T3",
8
+ "MESCs",
9
+ "hiPSC-CMs /ATCCs",
10
+ "CPCs",
11
+ "L929",
12
+ "Myoblast cells",
13
+ "hiPSCs",
14
+ "HepaRG",
15
+ "hESCs",
16
+ "10T1/2",
17
+ "Cardiac progenitor cells",
18
+ "NSCLC PDX",
19
+ "RAMECs",
20
+ "hASCs",
21
+ "HAVIC",
22
+ "Primary mouse hepatocyte",
23
+ "PTECs",
24
+ "human nasoseptal chondrocytes",
25
+ "PDX",
26
+ "HPFs",
27
+ "U87-MG",
28
+ "ESCs",
29
+ "HASSMC",
30
+ "dermal fibroblasts",
31
+ "MC3T3-E1",
32
+ "Schwann cells",
33
+ "hiPSC-CMs and HS-27A",
34
+ "Saos-2 ",
35
+ "SU3",
36
+ "hTMSCs",
37
+ "HACs",
38
+ "HADMSCs",
39
+ "HeLa",
40
+ "human primary kidney cells",
41
+ "myoblasts",
42
+ "MSCs",
43
+ "human primary kidneycells",
44
+ "293FT",
45
+ "HEK 293FT",
46
+ "Wnt3a-293FT",
47
+ "RSC96/HUVECs",
48
+ "human adipogenic mesenchymal stem cells",
49
+ "HEPG2/ECs",
50
+ "HUVECs/MSCs",
51
+ "RHECs",
52
+ "Human non-small cell lung cancer line Calu-3 (Calu-3)",
53
+ "HL-1",
54
+ "mouse cardiac cells",
55
+ "IMR-90",
56
+ "EPCs",
57
+ "MRC5",
58
+ "rMSC",
59
+ "basil plant cell",
60
+ "hNCs",
61
+ "A549",
62
+ "human induced pluripotent stem cell-derived cardiomyocytes",
63
+ "bMSCs/hACs",
64
+ "EA.hy 926 cells",
65
+ "HepG2/C3A",
66
+ "human epithelial lung carcinoma cells",
67
+ "Human cardiac fibroblasts",
68
+ "hTERT-MSC",
69
+ "cardiomyocytes",
70
+ "Huh7",
71
+ "NRCMs",
72
+ "HCF",
73
+ "Wnt reporter-293FT",
74
+ "neonatal rat ventricular CFs",
75
+ "human coronary artery endothelial cells",
76
+ "hiPSC-CM / fibroblasts",
77
+ "primary mouse hepatocyte",
78
+ "NIH3T3/ HUVECs",
79
+ "murine macrophage-like cell line",
80
+ "Endothelial cells",
81
+ "Human aortic VIC",
82
+ "sADSC",
83
+ "HUVECs/H9C2",
84
+ "neonatal rat ventricular cardiomyocytes",
85
+ "MG-63",
86
+ "Neonatal mouse cardiomyocytes (NMVCMs)",
87
+ "human hepatic stellate cell line",
88
+ "HEK-293",
89
+ "aHSC",
90
+ "MFCs",
91
+ "fibroblasts",
92
+ "HNDF",
93
+ "cardiomyocyte/MSCs",
94
+ "ADSCs",
95
+ "HCASMCs",
96
+ "cardiomyocyte",
97
+ "hCPCs",
98
+ "Human CM /adult human fibroblasts ",
99
+ "primary rat hepatocyte",
100
+ "human cardiac progenitor cells",
101
+ "SMC",
102
+ "Human MSCs",
103
+ "ACPCs",
104
+ "Huh7/HepaRG",
105
+ "Human umbilical vein endothelial cells",
106
+ "ATDC5",
107
+ "hESC-derived HLCs",
108
+ "NIH 3T3",
109
+ "n neonatal mouse ventricular cardiomyocytes",
110
+ "CFs/CMs/HUVECs",
111
+ "MRC-5",
112
+ "VIC",
113
+ "eHep",
114
+ "hUVECs/NIH3T3",
115
+ "MC3T3",
116
+ "HLC",
117
+ "hepatoma",
118
+ "FB",
119
+ "A549 GFP+",
120
+ "HPAAF",
121
+ "PMHs",
122
+ "HUVSMCs",
123
+ "Human CPCs",
124
+ "Fibroblasts/THP-1",
125
+ "rat ventricular cardiomyocytes",
126
+ "iCMs/iCFs/iECs",
127
+ "HUVEC/HHSC",
128
+ "Human CPCs / MSCs",
129
+ "HepaRG/LX-2 ",
130
+ "iCMs/iCFs/iECs/iCMFs",
131
+ "LX-2 ",
132
+ "SMMC-7721",
133
+ "Hepatoblast- single cell/iESC/iMSC",
134
+ "iCMFs",
135
+ "Hepatoblast- spheroid/iESC/iMSC",
136
+ "hBM-MSCs",
137
+ "BMSCs",
138
+ "HUVECs and HHSCs",
139
+ "Intrahepatic cholangiocarcinoma (ICC)",
140
+ "VICs",
141
+ "CMs/CFs",
142
+ "human neonatal dermal fi broblasts",
143
+ "10T1/2 fibroblast-laden cells",
144
+ "human cardiac fibroblasts",
145
+ "neonatal rat ventricular CMs",
146
+ "HUVECs and hiPSC-CS",
147
+ "iPSC-derived CM",
148
+ "Human Umbilical Vein Endothelial Cells + iPSC-derived CM",
149
+ "rabbit bone marrow mesenchymal stem cells",
150
+ "Neonatal rat cardiomyocytes",
151
+ "NIH 3T3 mouse fibroblasts",
152
+ "Human CPCs & MSCs",
153
+ "NSCLC PDX/CAFs",
154
+ "hiPSCs-derived HLCs",
155
+ "U87",
156
+ "75% hepatoblast cells, 20% iEC and 5% iMSC",
157
+ "SMCs",
158
+ "A549/95-D cells",
159
+ "NCI-H441",
160
+ "pancreatic cancer cell",
161
+ "prostate cancer stem cell",
162
+ "human primary parathyroid cells ",
163
+ "primary human hepatocytes",
164
+ "CPCs / MSCs",
165
+ "CPCs ",
166
+ "cardiac fibroblasts",
167
+ "iPSCs-derived cardiomyocytes",
168
+ "cardiomyocytes/ fibroblasts",
169
+ "hiPSC-CM",
170
+ "iPSCs/HUVECs",
171
+ "iPSC-CMs",
172
+ "iPSCs",
173
+ "H1395",
174
+ "PC9",
175
+ "H1650",
176
+ "HULEC-5a",
177
+ "NCI-H1703"
178
+ ]