Saicharan21 commited on
Commit
58957c9
Β·
verified Β·
1 Parent(s): f2521ea

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +205 -223
app.py CHANGED
@@ -1,7 +1,14 @@
1
  import gradio as gr
2
  import os
3
  import requests
 
 
 
 
 
4
  from groq import Groq
 
 
5
 
6
  GROQ_KEY = os.environ.get("GROQ_API_KEY", "")
7
 
@@ -13,176 +20,20 @@ KNOWHOW = ("SJSU CardioLab: "
13
  "Equipment: Heska HT5 hematology analyzer time-resolved PIV Tygon tubing Arduino Uno.")
14
 
15
  CSS = """
16
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;900&display=swap');
17
-
18
- * { font-family: 'Inter', sans-serif !important; box-sizing: border-box; }
19
-
20
- body, .gradio-container {
21
- background: #f0f4f8 !important;
22
- color: #1a202c !important;
23
- }
24
-
25
- .gradio-container {
26
- max-width: 1200px !important;
27
- margin: 0 auto !important;
28
- padding: 0 !important;
29
- }
30
-
31
- /* HEADER */
32
- .main-header {
33
- background: linear-gradient(135deg, #1a237e 0%, #b71c1c 100%);
34
- border-bottom: 3px solid #e63946;
35
- padding: 30px 20px;
36
- text-align: center;
37
- margin-bottom: 0;
38
- }
39
-
40
- /* TABS - ALWAYS VISIBLE */
41
- .tab-nav {
42
- background: #ffffff !important;
43
- border-bottom: 2px solid #e2e8f0 !important;
44
- padding: 0 10px !important;
45
- display: flex !important;
46
- gap: 5px !important;
47
- overflow-x: auto !important;
48
- }
49
-
50
- .tab-nav button {
51
- background: #f7fafc !important;
52
- color: #2d3748 !important;
53
- border: 1px solid #e2e8f0 !important;
54
- border-radius: 8px 8px 0 0 !important;
55
- padding: 12px 20px !important;
56
- font-size: 0.9em !important;
57
- font-weight: 600 !important;
58
- cursor: pointer !important;
59
- transition: all 0.2s !important;
60
- white-space: nowrap !important;
61
- margin-top: 8px !important;
62
- }
63
-
64
- .tab-nav button:hover {
65
- background: #ebf4ff !important;
66
- color: #1a237e !important;
67
- border-color: #4361ee !important;
68
- }
69
-
70
- .tab-nav button.selected {
71
- background: linear-gradient(135deg, #e63946, #c1121f) !important;
72
- color: #ffffff !important;
73
- border-color: #e63946 !important;
74
- font-weight: 700 !important;
75
- }
76
-
77
- /* CONTENT AREA */
78
- .tabitem {
79
- background: #ffffff !important;
80
- padding: 20px !important;
81
- border: 1px solid #e2e8f0 !important;
82
- border-top: none !important;
83
- }
84
-
85
- /* INPUTS */
86
- textarea, input[type=number], input[type=text] {
87
- background: #f7fafc !important;
88
- color: #1a202c !important;
89
- border: 1px solid #cbd5e0 !important;
90
- border-radius: 8px !important;
91
- font-size: 0.95em !important;
92
- }
93
-
94
- textarea:focus, input:focus {
95
- border-color: #e63946 !important;
96
- outline: none !important;
97
- }
98
-
99
- textarea::placeholder {
100
- color: #a0aec0 !important;
101
- }
102
-
103
- /* BUTTONS */
104
- button.primary, .btn-primary {
105
- background: linear-gradient(135deg, #e63946 0%, #c1121f 100%) !important;
106
- color: white !important;
107
- border: none !important;
108
- border-radius: 8px !important;
109
- font-weight: 700 !important;
110
- padding: 12px 24px !important;
111
- font-size: 0.95em !important;
112
- cursor: pointer !important;
113
- transition: transform 0.1s !important;
114
- }
115
-
116
- button.primary:hover {
117
- transform: translateY(-1px) !important;
118
- box-shadow: 0 4px 15px rgba(230, 57, 70, 0.4) !important;
119
- }
120
-
121
- button.secondary {
122
- background: #edf2f7 !important;
123
- color: #4a5568 !important;
124
- border: 1px solid #cbd5e0 !important;
125
- border-radius: 8px !important;
126
- font-weight: 600 !important;
127
- }
128
-
129
- /* LABELS */
130
- label span, label, .label-wrap span {
131
- color: #2b6cb0 !important;
132
- font-weight: 600 !important;
133
- font-size: 0.85em !important;
134
- text-transform: uppercase !important;
135
- letter-spacing: 0.5px !important;
136
- }
137
-
138
- /* MARKDOWN TEXT */
139
- .md h1, .md h2, .md h3 { color: #ffffff !important; }
140
- .md p, .md li { color: #a0b3d6 !important; }
141
-
142
- /* CHATBOT */
143
- .chatbot {
144
- background: #0a1628 !important;
145
- border: 1px solid #1e3a6e !important;
146
- border-radius: 12px !important;
147
- }
148
-
149
- .message.user {
150
- background: linear-gradient(135deg, #e63946, #c1121f) !important;
151
- color: #ffffff !important;
152
- border-radius: 12px 12px 4px 12px !important;
153
- }
154
-
155
- .message.bot {
156
- background: #ebf4ff !important;
157
- color: #1a202c !important;
158
- border: 1px solid #bee3f8 !important;
159
- border-radius: 12px 12px 12px 4px !important;
160
- }
161
-
162
- /* RESULT BOXES */
163
- .gr-box, .block {
164
- background: #ffffff !important;
165
- border: 1px solid #e2e8f0 !important;
166
- border-radius: 12px !important;
167
- }
168
-
169
- /* NUMBERS */
170
- input[type=number] {
171
- color: #7eb8f7 !important;
172
- font-size: 1.1em !important;
173
- font-weight: 600 !important;
174
- }
175
-
176
- /* INFO TEXT */
177
- .info {
178
- color: #5a7aaa !important;
179
- font-size: 0.8em !important;
180
- }
181
-
182
- /* SCROLLBAR */
183
- ::-webkit-scrollbar { width: 6px; }
184
- ::-webkit-scrollbar-track { background: #f7fafc; }
185
- ::-webkit-scrollbar-thumb { background: #cbd5e0; border-radius: 3px; }
186
  """
187
 
188
  def get_pubmed(query, n=5):
@@ -228,8 +79,7 @@ def research_chat(message, history):
228
  try:
229
  client = Groq(api_key=GROQ_KEY)
230
  pubmed = get_pubmed(message, n=3)
231
- system_msg = "You are CardioLab AI assistant. Expert in MHV MCL PIV TGT uPAD CKD FSI. Remember full conversation. Never invent URLs. " + KNOWHOW
232
- msgs = [{"role":"system","content":system_msg}]
233
  for item in history:
234
  if isinstance(item, dict):
235
  msgs.append({"role":item["role"],"content":item["content"]})
@@ -272,15 +122,167 @@ def voice_chat(audio, history):
272
  return history
273
 
274
  def generate_diagram(topic):
275
- if not topic.strip(): return "Please enter a topic."
276
- try:
277
- client = Groq(api_key=GROQ_KEY)
278
- msgs = [{"role":"system","content":"You are a biomedical engineering expert for SJSU CardioLab. Generate a detailed ASCII diagram or technical schematic. Make it clear, labeled, and useful for researchers."}]
279
- msgs.append({"role":"user","content":"Create a detailed labeled diagram for: "+topic})
280
- resp = client.chat.completions.create(model="llama-3.3-70b-versatile",messages=msgs,max_tokens=600)
281
- return resp.choices[0].message.content
282
- except Exception as e:
283
- return "Error: "+str(e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
  def piv_tool(velocity, shear, hr):
286
  v = "HIGH - stenosis risk" if float(velocity)>2.0 else "NORMAL"
@@ -295,15 +297,13 @@ def piv_tool(velocity, shear, hr):
295
  def tgt_tool(tat,pf12,hemo,platelets,time):
296
  risk=sum([float(tat)>15,float(pf12)>2.0,float(hemo)>50,float(platelets)<150])
297
  r="HIGH THROMBOGENIC RISK" if risk>=3 else "MODERATE RISK" if risk>=2 else "LOW RISK"
298
- return ("TGT BLOOD ANALYSIS"+chr(10)+
299
- "━━━━━━━━━━━━━━━━━━━━"+chr(10)+
300
  "Time: "+str(time)+" min"+chr(10)+
301
  "TAT: "+str(tat)+" β†’ "+("HIGH" if float(tat)>15 else "NORMAL")+chr(10)+
302
  "PF1.2: "+str(pf12)+" β†’ "+("HIGH" if float(pf12)>2.0 else "NORMAL")+chr(10)+
303
  "Hemoglobin: "+str(hemo)+" β†’ "+("HIGH" if float(hemo)>50 else "NORMAL")+chr(10)+
304
  "Platelets: "+str(platelets)+" β†’ "+("LOW" if float(platelets)<150 else "NORMAL")+chr(10)+
305
- "━━━━━━━━━━━━━━━━━━━━"+chr(10)+
306
- "OVERALL: "+r)
307
 
308
  def upad_tool(r,g,b):
309
  c=max(0,round(0.02*(float(r)-float(b))-0.5,2))
@@ -312,20 +312,16 @@ def upad_tool(r,g,b):
312
  elif c<3.0: s="Stage 2 CKD"
313
  elif c<6.0: s="Stage 3-4 CKD"
314
  else: s="Stage 5 CKD - Kidney Failure"
315
- return ("uPAD CKD ANALYSIS"+chr(10)+
316
- "━━━━━━━━━━━━━━━━━━━━"+chr(10)+
317
- "RGB Input: R="+str(r)+" G="+str(g)+" B="+str(b)+chr(10)+
318
- "Orange Score: "+str(int(float(r)-float(b)))+chr(10)+
319
- "Creatinine: "+str(c)+" mg/dL"+chr(10)+
320
- "━━━━━━━━━━━━━━━━━━━━"+chr(10)+
321
- "CKD STAGE: "+s+chr(10)+
322
  "Confirm with: Heska Element HT5")
323
 
324
  with gr.Blocks(title="CardioLab AI", css=CSS) as demo:
325
-
326
  gr.HTML("""
327
  <div class="main-header">
328
- <div style="font-size:2.8em; font-weight:900; color:#e63946; letter-spacing:3px; text-shadow: 0 0 30px rgba(230,57,70,0.5);">
329
  ❀️ CardioLab AI
330
  </div>
331
  </div>
@@ -335,12 +331,7 @@ with gr.Blocks(title="CardioLab AI", css=CSS) as demo:
335
  with gr.Tab("πŸ’¬ Chat"):
336
  chatbot = gr.Chatbot(label="", height=450)
337
  with gr.Row():
338
- msg_box = gr.Textbox(
339
- placeholder="Ask anything about CardioLab research...",
340
- label="",
341
- lines=2,
342
- scale=4
343
- )
344
  with gr.Column(scale=1, min_width=100):
345
  send_btn = gr.Button("Send ➀", variant="primary")
346
  clear_btn = gr.Button("Clear", variant="secondary")
@@ -361,38 +352,30 @@ with gr.Blocks(title="CardioLab AI", css=CSS) as demo:
361
  with gr.Tab("πŸ” Papers"):
362
  gr.Markdown("### Search latest research papers with verified working links")
363
  with gr.Row():
364
- search_input = gr.Textbox(
365
- placeholder="e.g. mechanical heart valve thrombogenicity",
366
- label="Research Topic",
367
- scale=4
368
- )
369
  search_btn = gr.Button("Search ➀", variant="primary", scale=1)
370
  search_output = gr.Textbox(label="Results β€” Verified Links Only", lines=18)
371
  search_btn.click(quick_search, inputs=search_input, outputs=search_output)
372
  search_input.submit(quick_search, inputs=search_input, outputs=search_output)
373
- gr.Markdown("**Try:** `mechanical heart valve PIV` | `creatinine uPAD microfluidic` | `bileaflet MHV thrombogenicity`")
374
 
375
  with gr.Tab("🎨 Diagrams"):
376
- gr.Markdown("### Generate technical diagrams and schematics for CardioLab")
377
  with gr.Row():
378
- diagram_input = gr.Textbox(
379
- placeholder="e.g. Mock Circulatory Loop with PIV system",
380
- label="What to diagram",
381
- scale=4
382
- )
383
  diagram_btn = gr.Button("Generate ➀", variant="primary", scale=1)
384
- diagram_output = gr.Textbox(label="Technical Diagram", lines=18)
385
  diagram_btn.click(generate_diagram, inputs=diagram_input, outputs=diagram_output)
386
- gr.Markdown("**Try:** `TGT circuit diagram` | `uPAD fabrication steps` | `PIV optical setup` | `MCL flow loop`")
387
 
388
  with gr.Tab("πŸ“Š PIV"):
389
  gr.Markdown("### Analyze Particle Image Velocimetry data from Mock Circulatory Loop")
390
  with gr.Row():
391
  with gr.Column():
392
- v=gr.Number(label="Max Velocity (m/s)", value=1.8, info="Normal range: 0.5 - 2.0 m/s")
393
  s=gr.Number(label="Wall Shear Stress (Pa)", value=6.5, info="Normal: below 5 Pa")
394
- h=gr.Number(label="Heart Rate (bpm)", value=72, info="Normal: 60 - 100 bpm")
395
- piv_btn = gr.Button("Analyze PIV Data ➀", variant="primary")
396
  with gr.Column():
397
  piv_out=gr.Textbox(label="Analysis Result", lines=8)
398
  piv_btn.click(piv_tool, inputs=[v,s,h], outputs=piv_out)
@@ -404,9 +387,9 @@ with gr.Blocks(title="CardioLab AI", css=CSS) as demo:
404
  t1=gr.Number(label="TAT (ng/mL)", value=18, info="Normal: below 8")
405
  t2=gr.Number(label="PF1.2 (nmol/L)", value=2.5, info="Normal: below 2.0")
406
  t3=gr.Number(label="Free Hemoglobin (mg/L)", value=60, info="Normal: below 20")
407
- t4=gr.Number(label="Platelet Count (10Β³/ΞΌL)", value=140, info="Normal: above 150")
408
  t5=gr.Number(label="Time (minutes)", value=40)
409
- tgt_btn = gr.Button("Analyze TGT Results ➀", variant="primary")
410
  with gr.Column():
411
  out2=gr.Textbox(label="Analysis Result", lines=12)
412
  tgt_btn.click(tgt_tool, inputs=[t1,t2,t3,t4,t5], outputs=out2)
@@ -415,10 +398,9 @@ with gr.Blocks(title="CardioLab AI", css=CSS) as demo:
415
  gr.Markdown("### Analyze uPAD colorimetric result using Jaffe Reaction for CKD diagnosis")
416
  with gr.Row():
417
  with gr.Column():
418
- gr.Markdown("**Enter RGB values from 64x64 pixel detection zone**")
419
- r=gr.Number(label="R (Red)", value=210, info="Range: 0 - 255")
420
- g=gr.Number(label="G (Green)", value=140, info="Range: 0 - 255")
421
- b=gr.Number(label="B (Blue)", value=80, info="Range: 0 - 255")
422
  upad_btn = gr.Button("Analyze uPAD ➀", variant="primary")
423
  with gr.Column():
424
  out3=gr.Textbox(label="CKD Analysis Result", lines=10)
 
1
  import gradio as gr
2
  import os
3
  import requests
4
+ import matplotlib
5
+ matplotlib.use("Agg")
6
+ import matplotlib.pyplot as plt
7
+ import matplotlib.patches as mpatches
8
+ import numpy as np
9
  from groq import Groq
10
+ import io
11
+ from PIL import Image
12
 
13
  GROQ_KEY = os.environ.get("GROQ_API_KEY", "")
14
 
 
20
  "Equipment: Heska HT5 hematology analyzer time-resolved PIV Tygon tubing Arduino Uno.")
21
 
22
  CSS = """
23
+ body, .gradio-container { background: #f0f4f8 !important; color: #1a202c !important; }
24
+ .tab-nav { background: #ffffff !important; border-bottom: 2px solid #e2e8f0 !important; padding: 0 10px !important; }
25
+ .tab-nav button { background: #f7fafc !important; color: #2d3748 !important; border: 1px solid #e2e8f0 !important; border-radius: 8px 8px 0 0 !important; padding: 12px 18px !important; font-size: 0.9em !important; font-weight: 600 !important; margin-top: 6px !important; transition: all 0.2s !important; }
26
+ .tab-nav button:hover { background: #ebf4ff !important; color: #1a237e !important; border-color: #4361ee !important; }
27
+ .tab-nav button.selected { background: linear-gradient(135deg, #e63946, #c1121f) !important; color: #ffffff !important; border-color: #e63946 !important; font-weight: 700 !important; }
28
+ .tabitem { background: #ffffff !important; padding: 20px !important; border: 1px solid #e2e8f0 !important; border-top: none !important; border-radius: 0 0 12px 12px !important; }
29
+ .main-header { background: linear-gradient(135deg, #1a237e 0%, #b71c1c 100%); border-bottom: 3px solid #e63946; padding: 25px 20px; text-align: center; border-radius: 12px 12px 0 0; }
30
+ textarea, input[type=number], input[type=text] { background: #f7fafc !important; color: #1a202c !important; border: 1px solid #cbd5e0 !important; border-radius: 8px !important; }
31
+ textarea::placeholder { color: #a0aec0 !important; }
32
+ button.primary { background: linear-gradient(135deg, #e63946 0%, #c1121f 100%) !important; color: white !important; border: none !important; border-radius: 8px !important; font-weight: 700 !important; padding: 12px 24px !important; }
33
+ button.secondary { background: #edf2f7 !important; color: #4a5568 !important; border: 1px solid #cbd5e0 !important; border-radius: 8px !important; }
34
+ label span, .label-wrap span { color: #2b6cb0 !important; font-weight: 600 !important; font-size: 0.85em !important; text-transform: uppercase !important; }
35
+ .message.user { background: linear-gradient(135deg, #e63946, #c1121f) !important; color: white !important; border-radius: 12px 12px 4px 12px !important; }
36
+ .message.bot { background: #ebf4ff !important; color: #1a202c !important; border: 1px solid #bee3f8 !important; border-radius: 12px 12px 12px 4px !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  """
38
 
39
  def get_pubmed(query, n=5):
 
79
  try:
80
  client = Groq(api_key=GROQ_KEY)
81
  pubmed = get_pubmed(message, n=3)
82
+ msgs = [{"role":"system","content":"You are CardioLab AI assistant. Expert in MHV MCL PIV TGT uPAD CKD FSI. Remember full conversation. Never invent URLs. "+KNOWHOW}]
 
83
  for item in history:
84
  if isinstance(item, dict):
85
  msgs.append({"role":item["role"],"content":item["content"]})
 
122
  return history
123
 
124
  def generate_diagram(topic):
125
+ topic_lower = topic.lower()
126
+ fig, ax = plt.subplots(1, 1, figsize=(12, 8))
127
+ ax.set_xlim(0, 12)
128
+ ax.set_ylim(0, 8)
129
+ ax.axis("off")
130
+ fig.patch.set_facecolor("#0d1b3e")
131
+ ax.set_facecolor("#0d1b3e")
132
+
133
+ if "tgt" in topic_lower or "thrombogen" in topic_lower:
134
+ ax.set_title("Thrombogenicity Tester (TGT) β€” SJSU CardioLab", color="white", fontsize=14, fontweight="bold", pad=15)
135
+ components = [
136
+ (1, 6, 2, 1, "#e63946", "Arduino Uno
137
+ (Controller)"),
138
+ (4, 6, 2, 1, "#4361ee", "Motor Driver"),
139
+ (7, 6, 2, 1, "#2ecc71", "Stepper Motor"),
140
+ (4, 3.5, 4, 2, "#1a2744", "Test Chamber
141
+ 27mm SJM Regent MHV
142
+ (150mL Blood)"),
143
+ (1, 1, 2, 1, "#e67e22", "Heska HT5
144
+ Analyzer"),
145
+ (9, 1, 2, 1, "#9b59b6", "Data Logger"),
146
+ ]
147
+ for x, y, w, h, color, label in components:
148
+ rect = mpatches.FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.1", facecolor=color, edgecolor="white", linewidth=2)
149
+ ax.add_patch(rect)
150
+ ax.text(x+w/2, y+h/2, label, ha="center", va="center", color="white", fontsize=8, fontweight="bold")
151
+ arrows = [(2, 6.5, 4, 6.5), (5, 6.5, 7, 6.5), (8, 6, 8, 5.5), (6, 3.5, 6, 2), (4, 4.5, 2, 2), (8, 3.5, 10, 2)]
152
+ for x1,y1,x2,y2 in arrows:
153
+ ax.annotate("", xy=(x2,y2), xytext=(x1,y1), arrowprops=dict(arrowstyle="->", color="#7eb8f7", lw=2))
154
+ labels = [(3, 7, "Signal"), (5.5, 7, "Power"), (8.5, 5.7, "Rotate"), (6.5, 2.8, "Sample"), (2.8, 3.5, "Analyze"), (9, 2.8, "Record")]
155
+ for x, y, t in labels:
156
+ ax.text(x, y, t, color="#a8b2d8", fontsize=7, ha="center")
157
+ times = ["0 min", "20 min", "40 min", "60 min"]
158
+ for i, t in enumerate(times):
159
+ ax.text(1+i*2.5, 0.5, t, color="#e63946", fontsize=8, ha="center", fontweight="bold")
160
+ ax.text(6, 0.5, "Blood Sampling Times", color="#7eb8f7", fontsize=8, ha="center")
161
+
162
+ elif "mcl" in topic_lower or "circulatory" in topic_lower or "piv" in topic_lower:
163
+ ax.set_title("Mock Circulatory Loop (MCL) + PIV System β€” SJSU CardioLab", color="white", fontsize=13, fontweight="bold", pad=15)
164
+ loop_x = [2, 4, 6, 8, 10, 10, 8, 6, 4, 2, 2]
165
+ loop_y = [4, 6, 6.5, 6, 4, 3, 2, 1.5, 2, 3, 4]
166
+ ax.plot(loop_x, loop_y, color="#4361ee", linewidth=4, label="Flow Loop")
167
+ components = [
168
+ (5, 5.8, 2, 0.8, "#e63946", "MHV Test Section
169
+ (Sylgard 184)"),
170
+ (1, 3.5, 1.5, 0.8, "#2ecc71", "Pump"),
171
+ (8.5, 3.5, 1.5, 0.8, "#e67e22", "Compliance
172
+ Chamber"),
173
+ (4.5, 1.2, 2, 0.8, "#9b59b6", "Reservoir"),
174
+ ]
175
+ for x, y, w, h, color, label in components:
176
+ rect = mpatches.FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.1", facecolor=color, edgecolor="white", linewidth=2)
177
+ ax.add_patch(rect)
178
+ ax.text(x+w/2, y+h/2, label, ha="center", va="center", color="white", fontsize=7, fontweight="bold")
179
+ ax.annotate("", xy=(6, 7.5), xytext=(6, 6.5), arrowprops=dict(arrowstyle="->", color="#00ff88", lw=2))
180
+ ax.text(6, 7.7, "Green Laser (PIV)", color="#00ff88", fontsize=8, ha="center", fontweight="bold")
181
+ rect_laser = mpatches.FancyBboxPatch((5, 7.5), 2, 0.4, boxstyle="round,pad=0.05", facecolor="#00ff44", edgecolor="white", linewidth=1)
182
+ ax.add_patch(rect_laser)
183
+ ax.text(6, 7.7, "LASER", ha="center", va="center", color="black", fontsize=7, fontweight="bold")
184
+ rect_cam = mpatches.FancyBboxPatch((9.5, 5.5), 1.5, 0.8, boxstyle="round,pad=0.05", facecolor="#1a2744", edgecolor="#7eb8f7", linewidth=2)
185
+ ax.add_patch(rect_cam)
186
+ ax.text(10.25, 5.9, "Camera
187
+ (PIV)", ha="center", va="center", color="white", fontsize=7, fontweight="bold")
188
+
189
+ elif "upad" in topic_lower or "ckd" in topic_lower or "creatinine" in topic_lower:
190
+ ax.set_title("uPAD Fabrication and CKD Detection Flow β€” SJSU CardioLab", color="white", fontsize=13, fontweight="bold", pad=15)
191
+ steps = [
192
+ (0.5, 6, "#e63946", "1. Whatman
193
+ Paper"),
194
+ (2.5, 6, "#e67e22", "2. Wax
195
+ Printer"),
196
+ (4.5, 6, "#f1c40f", "3. Heat
197
+ 120Β°C"),
198
+ (6.5, 6, "#2ecc71", "4. Add
199
+ Reagents"),
200
+ (8.5, 6, "#4361ee", "5. Apply
201
+ Sample"),
202
+ (10, 6, "#9b59b6", "6. Image
203
+ Analysis"),
204
+ ]
205
+ for x, y, color, label in steps:
206
+ circle = plt.Circle((x+0.75, y+0.5), 0.7, color=color, zorder=5)
207
+ ax.add_patch(circle)
208
+ ax.text(x+0.75, y+0.5, label, ha="center", va="center", color="white", fontsize=7, fontweight="bold", zorder=6)
209
+ for i in range(len(steps)-1):
210
+ x1 = steps[i][0]+1.45
211
+ x2 = steps[i+1][0]+0.05
212
+ y = 6.5
213
+ ax.annotate("", xy=(x2,y), xytext=(x1,y), arrowprops=dict(arrowstyle="->", color="white", lw=2))
214
+ colors_ckd = ["#2ecc71", "#f1c40f", "#e67e22", "#e74c3c", "#c0392b"]
215
+ stages = ["Normal
216
+ <1.2", "Borderline
217
+ 1.2-1.5", "Stage 2
218
+ 1.5-3.0", "Stage 3-4
219
+ 3.0-6.0", "Stage 5
220
+ >6.0"]
221
+ for i, (color, stage) in enumerate(zip(colors_ckd, stages)):
222
+ rect = mpatches.FancyBboxPatch((0.5+i*2.2, 2), 2, 2.5, boxstyle="round,pad=0.1", facecolor=color, edgecolor="white", linewidth=2, alpha=0.8)
223
+ ax.add_patch(rect)
224
+ ax.text(1.5+i*2.2, 3.2, stage, ha="center", va="center", color="white", fontsize=8, fontweight="bold")
225
+ ax.text(6, 1.5, "Creatinine Level (mg/dL)", color="#7eb8f7", fontsize=9, ha="center", fontweight="bold")
226
+ ax.text(6, 4.8, "Jaffe Reaction: Creatinine + Picric Acid β†’ Orange-Red Color", color="#a8b2d8", fontsize=8, ha="center")
227
+
228
+ elif "mhv" in topic_lower or "valve" in topic_lower or "bileaflet" in topic_lower:
229
+ ax.set_title("Mechanical Heart Valve (MHV) β€” Bileaflet Design", color="white", fontsize=14, fontweight="bold", pad=15)
230
+ circle_outer = plt.Circle((6, 4), 3, color="#2d3a5a", fill=True, zorder=1)
231
+ circle_inner = plt.Circle((6, 4), 2.8, color="#1a2744", fill=True, zorder=2)
232
+ ax.add_patch(circle_outer)
233
+ ax.add_patch(circle_inner)
234
+ border = plt.Circle((6, 4), 3, color="#7eb8f7", fill=False, linewidth=3, zorder=3)
235
+ ax.add_patch(border)
236
+ leaflet1 = mpatches.FancyBboxPatch((4.5, 3), 1.3, 2, boxstyle="round,pad=0.1", facecolor="#e63946", edgecolor="white", linewidth=2, zorder=4)
237
+ leaflet2 = mpatches.FancyBboxPatch((6.2, 3), 1.3, 2, boxstyle="round,pad=0.1", facecolor="#e63946", edgecolor="white", linewidth=2, zorder=4)
238
+ ax.add_patch(leaflet1)
239
+ ax.add_patch(leaflet2)
240
+ ax.text(5.15, 4, "Leaflet
241
+ 1", ha="center", va="center", color="white", fontsize=8, fontweight="bold", zorder=5)
242
+ ax.text(6.85, 4, "Leaflet
243
+ 2", ha="center", va="center", color="white", fontsize=8, fontweight="bold", zorder=5)
244
+ ax.annotate("", xy=(6, 7.5), xytext=(6, 7), arrowprops=dict(arrowstyle="->", color="#4361ee", lw=3))
245
+ ax.text(6, 7.7, "Blood Flow", color="#4361ee", ha="center", fontsize=9, fontweight="bold")
246
+ ax.annotate("", xy=(6, 0.5), xytext=(6, 1), arrowprops=dict(arrowstyle="->", color="#4361ee", lw=3))
247
+ specs = ["Diameter: 27mm (SJM Regent)", "Material: Pyrolytic Carbon", "Leaflets: 2 (Bileaflet)", "Opening Angle: 85Β°"]
248
+ for i, spec in enumerate(specs):
249
+ ax.text(0.3, 7-i*0.7, "β€’ "+spec, color="#a8b2d8", fontsize=8)
250
+
251
+ else:
252
+ ax.set_title("CardioLab AI β€” Research Overview", color="white", fontsize=14, fontweight="bold", pad=15)
253
+ pillars = [
254
+ (2, 5, "#e63946", "Pillar 1
255
+ MHV Design
256
+ & Testing", ["MCL", "PIV", "FSI"]),
257
+ (5, 5, "#4361ee", "Pillar 2
258
+ Thrombosis
259
+ Research", ["TGT", "Blood Clots", "Hemolysis"]),
260
+ (8, 5, "#2ecc71", "Pillar 3
261
+ CKD
262
+ Diagnostics", ["uPAD", "Jaffe", "Creatinine"]),
263
+ ]
264
+ for x, y, color, title, items in pillars:
265
+ rect = mpatches.FancyBboxPatch((x-1, y-0.5), 2.5, 2.5, boxstyle="round,pad=0.2", facecolor=color, edgecolor="white", linewidth=2, alpha=0.9)
266
+ ax.add_patch(rect)
267
+ ax.text(x+0.25, y+1.7, title, ha="center", va="center", color="white", fontsize=9, fontweight="bold")
268
+ for j, item in enumerate(items):
269
+ ax.text(x+0.25, y+1-j*0.5, "β€’ "+item, ha="center", color="white", fontsize=8)
270
+ equipment = ["27mm SJM Regent MHV", "Arduino Uno + Stepper Motor", "Time-resolved PIV (Green Laser)", "Sylgard 184 Transparent Sections", "Heska Element HT5 Analyzer"]
271
+ rect_eq = mpatches.FancyBboxPatch((1, 1), 10, 2.5, boxstyle="round,pad=0.2", facecolor="#1a2744", edgecolor="#7eb8f7", linewidth=2)
272
+ ax.add_patch(rect_eq)
273
+ ax.text(6, 3.2, "KEY EQUIPMENT", ha="center", color="#7eb8f7", fontsize=10, fontweight="bold")
274
+ for i, eq in enumerate(equipment):
275
+ col = i % 2
276
+ row = i // 2
277
+ ax.text(2+col*5, 2.7-row*0.7, "β€’ "+eq, color="#e2e8f0", fontsize=7.5)
278
+
279
+ buf = io.BytesIO()
280
+ plt.tight_layout()
281
+ plt.savefig(buf, format="png", facecolor=fig.get_facecolor(), bbox_inches="tight", dpi=120)
282
+ buf.seek(0)
283
+ img = Image.open(buf)
284
+ plt.close()
285
+ return img
286
 
287
  def piv_tool(velocity, shear, hr):
288
  v = "HIGH - stenosis risk" if float(velocity)>2.0 else "NORMAL"
 
297
  def tgt_tool(tat,pf12,hemo,platelets,time):
298
  risk=sum([float(tat)>15,float(pf12)>2.0,float(hemo)>50,float(platelets)<150])
299
  r="HIGH THROMBOGENIC RISK" if risk>=3 else "MODERATE RISK" if risk>=2 else "LOW RISK"
300
+ return ("TGT BLOOD ANALYSIS"+chr(10)+"━━━━━━━━━━━━━━━━━━━━"+chr(10)+
 
301
  "Time: "+str(time)+" min"+chr(10)+
302
  "TAT: "+str(tat)+" β†’ "+("HIGH" if float(tat)>15 else "NORMAL")+chr(10)+
303
  "PF1.2: "+str(pf12)+" β†’ "+("HIGH" if float(pf12)>2.0 else "NORMAL")+chr(10)+
304
  "Hemoglobin: "+str(hemo)+" β†’ "+("HIGH" if float(hemo)>50 else "NORMAL")+chr(10)+
305
  "Platelets: "+str(platelets)+" β†’ "+("LOW" if float(platelets)<150 else "NORMAL")+chr(10)+
306
+ "━━━━━━━━━━━━━━━━━━━━"+chr(10)+"OVERALL: "+r)
 
307
 
308
  def upad_tool(r,g,b):
309
  c=max(0,round(0.02*(float(r)-float(b))-0.5,2))
 
312
  elif c<3.0: s="Stage 2 CKD"
313
  elif c<6.0: s="Stage 3-4 CKD"
314
  else: s="Stage 5 CKD - Kidney Failure"
315
+ return ("uPAD CKD ANALYSIS"+chr(10)+"━━━━━━━━━━━━━━━━━━━━"+chr(10)+
316
+ "RGB: R="+str(r)+" G="+str(g)+" B="+str(b)+chr(10)+
317
+ "Creatinine: "+str(c)+" mg/dL"+chr(10)+
318
+ "━━━━━━━━━━━━━━━━━━━━"+chr(10)+"CKD STAGE: "+s+chr(10)+
 
 
 
319
  "Confirm with: Heska Element HT5")
320
 
321
  with gr.Blocks(title="CardioLab AI", css=CSS) as demo:
 
322
  gr.HTML("""
323
  <div class="main-header">
324
+ <div style="font-size:2.8em;font-weight:900;color:#ffffff;letter-spacing:3px;text-shadow:0 0 30px rgba(255,255,255,0.3);">
325
  ❀️ CardioLab AI
326
  </div>
327
  </div>
 
331
  with gr.Tab("πŸ’¬ Chat"):
332
  chatbot = gr.Chatbot(label="", height=450)
333
  with gr.Row():
334
+ msg_box = gr.Textbox(placeholder="Ask anything about CardioLab research...", label="", lines=2, scale=4)
 
 
 
 
 
335
  with gr.Column(scale=1, min_width=100):
336
  send_btn = gr.Button("Send ➀", variant="primary")
337
  clear_btn = gr.Button("Clear", variant="secondary")
 
352
  with gr.Tab("πŸ” Papers"):
353
  gr.Markdown("### Search latest research papers with verified working links")
354
  with gr.Row():
355
+ search_input = gr.Textbox(placeholder="e.g. mechanical heart valve thrombogenicity", label="Research Topic", scale=4)
 
 
 
 
356
  search_btn = gr.Button("Search ➀", variant="primary", scale=1)
357
  search_output = gr.Textbox(label="Results β€” Verified Links Only", lines=18)
358
  search_btn.click(quick_search, inputs=search_input, outputs=search_output)
359
  search_input.submit(quick_search, inputs=search_input, outputs=search_output)
360
+ gr.Markdown("**Try:** `TGT thrombogenicity` | `creatinine uPAD` | `bileaflet MHV PIV` | `CKD point-of-care`")
361
 
362
  with gr.Tab("🎨 Diagrams"):
363
+ gr.Markdown("### Generate real visual diagrams of CardioLab equipment and systems")
364
  with gr.Row():
365
+ diagram_input = gr.Textbox(placeholder="e.g. TGT setup | MCL PIV system | uPAD CKD | MHV bileaflet valve | CardioLab overview", label="What to diagram", scale=4)
 
 
 
 
366
  diagram_btn = gr.Button("Generate ➀", variant="primary", scale=1)
367
+ diagram_output = gr.Image(label="Visual Diagram", type="pil")
368
  diagram_btn.click(generate_diagram, inputs=diagram_input, outputs=diagram_output)
369
+ gr.Markdown("**Try these:** `TGT setup` | `MCL PIV system` | `uPAD CKD flow` | `MHV bileaflet valve` | `CardioLab overview`")
370
 
371
  with gr.Tab("πŸ“Š PIV"):
372
  gr.Markdown("### Analyze Particle Image Velocimetry data from Mock Circulatory Loop")
373
  with gr.Row():
374
  with gr.Column():
375
+ v=gr.Number(label="Max Velocity (m/s)", value=1.8, info="Normal: 0.5-2.0 m/s")
376
  s=gr.Number(label="Wall Shear Stress (Pa)", value=6.5, info="Normal: below 5 Pa")
377
+ h=gr.Number(label="Heart Rate (bpm)", value=72, info="Normal: 60-100 bpm")
378
+ piv_btn = gr.Button("Analyze PIV ➀", variant="primary")
379
  with gr.Column():
380
  piv_out=gr.Textbox(label="Analysis Result", lines=8)
381
  piv_btn.click(piv_tool, inputs=[v,s,h], outputs=piv_out)
 
387
  t1=gr.Number(label="TAT (ng/mL)", value=18, info="Normal: below 8")
388
  t2=gr.Number(label="PF1.2 (nmol/L)", value=2.5, info="Normal: below 2.0")
389
  t3=gr.Number(label="Free Hemoglobin (mg/L)", value=60, info="Normal: below 20")
390
+ t4=gr.Number(label="Platelet Count", value=140, info="Normal: above 150")
391
  t5=gr.Number(label="Time (minutes)", value=40)
392
+ tgt_btn = gr.Button("Analyze TGT ➀", variant="primary")
393
  with gr.Column():
394
  out2=gr.Textbox(label="Analysis Result", lines=12)
395
  tgt_btn.click(tgt_tool, inputs=[t1,t2,t3,t4,t5], outputs=out2)
 
398
  gr.Markdown("### Analyze uPAD colorimetric result using Jaffe Reaction for CKD diagnosis")
399
  with gr.Row():
400
  with gr.Column():
401
+ r=gr.Number(label="R (Red)", value=210, info="Range: 0-255")
402
+ g=gr.Number(label="G (Green)", value=140, info="Range: 0-255")
403
+ b=gr.Number(label="B (Blue)", value=80, info="Range: 0-255")
 
404
  upad_btn = gr.Button("Analyze uPAD ➀", variant="primary")
405
  with gr.Column():
406
  out3=gr.Textbox(label="CKD Analysis Result", lines=10)