SamarpeetGarad commited on
Commit
a7528e7
Β·
verified Β·
1 Parent(s): 13619ad

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +531 -0
app.py ADDED
@@ -0,0 +1,531 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ RadioFlow: AI-Powered Radiology Workflow Agent
3
+ Main Gradio Application for MedGemma Impact Challenge
4
+
5
+ This application demonstrates a multi-agent system for chest X-ray analysis
6
+ using Google's Health AI Developer Foundations (HAI-DEF) models.
7
+ """
8
+
9
+ import gradio as gr
10
+ from PIL import Image
11
+ import time
12
+ from typing import Optional, Tuple, List, Dict
13
+ import json
14
+
15
+ # Import our modules
16
+ from orchestrator import RadioFlowOrchestrator, WorkflowResult, create_orchestrator
17
+ from utils.visualization import (
18
+ create_workflow_diagram,
19
+ create_radar_chart,
20
+ create_priority_gauge,
21
+ create_timeline_chart
22
+ )
23
+
24
+ # Global orchestrator instance
25
+ orchestrator: Optional[RadioFlowOrchestrator] = None
26
+
27
+
28
+ def initialize_system():
29
+ """Initialize the RadioFlow system."""
30
+ global orchestrator
31
+ if orchestrator is None:
32
+ orchestrator = create_orchestrator(demo_mode=True)
33
+ return "βœ… RadioFlow System Initialized"
34
+
35
+
36
+ def process_xray(
37
+ image: Optional[Image.Image],
38
+ clinical_history: str,
39
+ patient_age: str,
40
+ symptoms: str,
41
+ progress=gr.Progress()
42
+ ) -> Tuple[str, str, str, str, str, dict, dict, dict]:
43
+ """
44
+ Process a chest X-ray through the RadioFlow pipeline.
45
+
46
+ Returns:
47
+ Tuple of (report, priority_html, findings_json, metrics, status,
48
+ workflow_fig, radar_fig, priority_fig)
49
+ """
50
+ global orchestrator
51
+
52
+ if image is None:
53
+ return (
54
+ "⚠️ Please upload a chest X-ray image.",
55
+ "",
56
+ "{}",
57
+ "",
58
+ "No image uploaded",
59
+ None, None, None
60
+ )
61
+
62
+ # Initialize if needed
63
+ if orchestrator is None:
64
+ initialize_system()
65
+
66
+ # Prepare clinical context
67
+ context = {
68
+ "clinical_history": clinical_history or "Not provided",
69
+ "age": patient_age or "Not provided",
70
+ "symptoms": symptoms or "Not provided"
71
+ }
72
+
73
+ # Progress updates
74
+ progress(0.1, desc="πŸ”¬ Analyzing chest X-ray...")
75
+ time.sleep(0.2)
76
+
77
+ progress(0.3, desc="πŸ“‹ Interpreting findings...")
78
+
79
+ # Run the workflow
80
+ result = orchestrator.process(image, context)
81
+
82
+ progress(0.6, desc="πŸ“ Generating report...")
83
+ time.sleep(0.1)
84
+
85
+ progress(0.8, desc="🚦 Assessing priority...")
86
+ time.sleep(0.1)
87
+
88
+ progress(1.0, desc="βœ… Complete!")
89
+
90
+ # Format outputs
91
+ report = result.final_report if result.final_report else "Report generation failed."
92
+
93
+ # Priority HTML
94
+ priority_html = format_priority_display(result)
95
+
96
+ # Findings JSON
97
+ findings = []
98
+ if result.cxr_analysis and result.cxr_analysis.data:
99
+ findings = result.cxr_analysis.data.get("findings", [])
100
+ findings_json = json.dumps(findings, indent=2)
101
+
102
+ # Metrics
103
+ metrics = format_metrics(result)
104
+
105
+ # Status
106
+ status = f"βœ… Workflow {result.status.upper()} | {result.total_duration_ms:.0f}ms"
107
+
108
+ # Create visualizations
109
+ agent_results = []
110
+ for agent_result in [result.cxr_analysis, result.finding_interpretation,
111
+ result.report, result.priority_routing]:
112
+ if agent_result:
113
+ agent_results.append({
114
+ "name": agent_result.agent_name,
115
+ "status": agent_result.status,
116
+ "processing_time_ms": agent_result.processing_time_ms
117
+ })
118
+
119
+ workflow_fig = create_workflow_diagram(agent_results)
120
+
121
+ # Radar chart for analysis scores
122
+ if result.cxr_analysis and result.cxr_analysis.data:
123
+ region_analysis = result.cxr_analysis.data.get("region_analysis", {})
124
+ scores = {}
125
+ for region, data in list(region_analysis.items())[:5]:
126
+ clean_name = region.replace("_", " ").title()
127
+ scores[clean_name] = data.get("confidence", 0.5)
128
+ if scores:
129
+ radar_fig = create_radar_chart(scores, "Regional Confidence Scores")
130
+ else:
131
+ radar_fig = None
132
+ else:
133
+ radar_fig = None
134
+
135
+ # Priority gauge
136
+ priority_fig = create_priority_gauge(result.priority_score, result.priority_level)
137
+
138
+ return (
139
+ report,
140
+ priority_html,
141
+ findings_json,
142
+ metrics,
143
+ status,
144
+ workflow_fig,
145
+ radar_fig,
146
+ priority_fig
147
+ )
148
+
149
+
150
+ def format_priority_display(result: WorkflowResult) -> str:
151
+ """Format priority information as HTML."""
152
+ level = result.priority_level
153
+ score = result.priority_score
154
+
155
+ colors = {
156
+ "STAT": "#ef4444",
157
+ "URGENT": "#f59e0b",
158
+ "ROUTINE": "#22c55e"
159
+ }
160
+ color = colors.get(level, "#6b7280")
161
+
162
+ critical_html = ""
163
+ if result.critical_findings:
164
+ critical_html = f"""
165
+ <div style="margin-top: 10px; padding: 10px; background: #fef2f2; border-radius: 5px; border-left: 4px solid #ef4444;">
166
+ <strong>⚠️ Critical Findings:</strong>
167
+ <ul style="margin: 5px 0 0 20px;">
168
+ {"".join(f"<li>{f}</li>" for f in result.critical_findings)}
169
+ </ul>
170
+ </div>
171
+ """
172
+
173
+ routing_html = ""
174
+ if result.priority_routing and result.priority_routing.data:
175
+ routing = result.priority_routing.data.get("routing_recommendation", {})
176
+ if routing:
177
+ routing_html = f"""
178
+ <div style="margin-top: 10px; padding: 10px; background: #f0f9ff; border-radius: 5px;">
179
+ <strong>πŸ“ Routing:</strong> {routing.get("destination", "Standard Queue")}
180
+ </div>
181
+ """
182
+
183
+ return f"""
184
+ <div style="padding: 15px; border-radius: 10px; background: linear-gradient(135deg, {color}22, {color}11);">
185
+ <div style="display: flex; align-items: center; gap: 15px;">
186
+ <div style="
187
+ width: 60px;
188
+ height: 60px;
189
+ background: {color};
190
+ border-radius: 50%;
191
+ display: flex;
192
+ align-items: center;
193
+ justify-content: center;
194
+ color: white;
195
+ font-weight: bold;
196
+ font-size: 14px;
197
+ ">
198
+ {level}
199
+ </div>
200
+ <div>
201
+ <div style="font-size: 24px; font-weight: bold; color: {color};">
202
+ Priority Score: {score:.0%}
203
+ </div>
204
+ <div style="color: #666; font-size: 14px;">
205
+ {result.findings_count} finding(s) detected
206
+ </div>
207
+ </div>
208
+ </div>
209
+ {critical_html}
210
+ {routing_html}
211
+ </div>
212
+ """
213
+
214
+
215
+ def format_metrics(result: WorkflowResult) -> str:
216
+ """Format workflow metrics."""
217
+ lines = [
218
+ "## πŸ“Š Workflow Metrics",
219
+ "",
220
+ f"**Total Duration:** {result.total_duration_ms:.0f}ms",
221
+ f"**Status:** {result.status.upper()}",
222
+ f"**Findings Detected:** {result.findings_count}",
223
+ "",
224
+ "### Agent Performance",
225
+ ""
226
+ ]
227
+
228
+ agents = [
229
+ ("CXR Analyzer", result.cxr_analysis),
230
+ ("Finding Interpreter", result.finding_interpretation),
231
+ ("Report Generator", result.report),
232
+ ("Priority Router", result.priority_routing)
233
+ ]
234
+
235
+ for name, agent_result in agents:
236
+ if agent_result:
237
+ status_icon = "βœ…" if agent_result.status == "success" else "❌"
238
+ lines.append(f"- {status_icon} **{name}:** {agent_result.processing_time_ms:.0f}ms")
239
+
240
+ return "\n".join(lines)
241
+
242
+
243
+ def get_sample_image():
244
+ """Return a sample X-ray image for demo purposes."""
245
+ # Create a simple placeholder image
246
+ img = Image.new('RGB', (512, 512), color=(20, 20, 30))
247
+ from PIL import ImageDraw
248
+ draw = ImageDraw.Draw(img)
249
+
250
+ # Draw a simple chest outline
251
+ draw.ellipse([100, 150, 412, 450], outline=(60, 60, 70), width=3)
252
+ draw.ellipse([150, 180, 280, 350], outline=(50, 50, 60), width=2)
253
+ draw.ellipse([232, 180, 362, 350], outline=(50, 50, 60), width=2)
254
+
255
+ # Add text
256
+ draw.text((150, 50), "Sample CXR", fill=(80, 80, 90))
257
+ draw.text((120, 470), "Upload real X-ray for analysis", fill=(80, 80, 90))
258
+
259
+ return img
260
+
261
+
262
+ # ============================================
263
+ # GRADIO INTERFACE
264
+ # ============================================
265
+
266
+ # Custom CSS for professional styling
267
+ custom_css = """
268
+ /* Main container */
269
+ .gradio-container {
270
+ max-width: 1400px !important;
271
+ margin: auto;
272
+ }
273
+
274
+ /* Header styling */
275
+ .header-container {
276
+ background: linear-gradient(135deg, #1e3a5f 0%, #2d5a87 100%);
277
+ padding: 20px;
278
+ border-radius: 10px;
279
+ margin-bottom: 20px;
280
+ color: white;
281
+ }
282
+
283
+ /* Agent cards */
284
+ .agent-card {
285
+ background: white;
286
+ border-radius: 8px;
287
+ padding: 15px;
288
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
289
+ margin: 5px;
290
+ }
291
+
292
+ /* Priority badges */
293
+ .priority-stat { background: #ef4444; }
294
+ .priority-urgent { background: #f59e0b; }
295
+ .priority-routine { background: #22c55e; }
296
+
297
+ /* Code blocks */
298
+ pre {
299
+ background: #1e293b !important;
300
+ border-radius: 8px;
301
+ }
302
+
303
+ /* Tabs */
304
+ .tab-nav button {
305
+ font-weight: 600 !important;
306
+ }
307
+ """
308
+
309
+ # Create the Gradio interface
310
+ with gr.Blocks(
311
+ title="RadioFlow - AI Radiology Workflow"
312
+ ) as demo:
313
+
314
+ # Header
315
+ gr.HTML("""
316
+ <div style="
317
+ background: linear-gradient(135deg, #1e3a5f 0%, #2d5a87 100%);
318
+ padding: 25px 30px;
319
+ border-radius: 12px;
320
+ margin-bottom: 20px;
321
+ color: white;
322
+ ">
323
+ <div style="display: flex; align-items: center; gap: 15px;">
324
+ <div style="font-size: 40px;">🩻</div>
325
+ <div>
326
+ <h1 style="margin: 0; font-size: 28px; font-weight: 700;">RadioFlow</h1>
327
+ <p style="margin: 5px 0 0 0; opacity: 0.9; font-size: 16px;">
328
+ AI-Powered Radiology Workflow Agent | MedGemma Impact Challenge
329
+ </p>
330
+ </div>
331
+ </div>
332
+ <div style="
333
+ display: flex;
334
+ gap: 20px;
335
+ margin-top: 15px;
336
+ padding-top: 15px;
337
+ border-top: 1px solid rgba(255,255,255,0.2);
338
+ ">
339
+ <div>
340
+ <span style="opacity: 0.7;">Powered by</span>
341
+ <strong>MedGemma + CXR Foundation</strong>
342
+ </div>
343
+ <div>
344
+ <span style="opacity: 0.7;">Architecture</span>
345
+ <strong>4-Agent Pipeline</strong>
346
+ </div>
347
+ <div>
348
+ <span style="opacity: 0.7;">Prize Track</span>
349
+ <strong>Main + Agentic Workflow</strong>
350
+ </div>
351
+ </div>
352
+ </div>
353
+ """)
354
+
355
+ # Agent Pipeline Visualization
356
+ gr.HTML("""
357
+ <div style="
358
+ background: #f8fafc;
359
+ padding: 15px 20px;
360
+ border-radius: 10px;
361
+ margin-bottom: 20px;
362
+ border: 1px solid #e2e8f0;
363
+ ">
364
+ <div style="text-align: center; margin-bottom: 10px; font-weight: 600; color: #475569;">
365
+ Multi-Agent Pipeline
366
+ </div>
367
+ <div style="display: flex; justify-content: center; align-items: center; gap: 10px; flex-wrap: wrap;">
368
+ <div style="background: #3b82f6; color: white; padding: 10px 15px; border-radius: 8px; font-weight: 500;">
369
+ 1️⃣ CXR Analyzer
370
+ </div>
371
+ <div style="color: #94a3b8;">β†’</div>
372
+ <div style="background: #8b5cf6; color: white; padding: 10px 15px; border-radius: 8px; font-weight: 500;">
373
+ 2️⃣ Finding Interpreter
374
+ </div>
375
+ <div style="color: #94a3b8;">β†’</div>
376
+ <div style="background: #ec4899; color: white; padding: 10px 15px; border-radius: 8px; font-weight: 500;">
377
+ 3️⃣ Report Generator
378
+ </div>
379
+ <div style="color: #94a3b8;">β†’</div>
380
+ <div style="background: #f59e0b; color: white; padding: 10px 15px; border-radius: 8px; font-weight: 500;">
381
+ 4️⃣ Priority Router
382
+ </div>
383
+ </div>
384
+ </div>
385
+ """)
386
+
387
+ with gr.Row():
388
+ # Left Column - Input
389
+ with gr.Column(scale=1):
390
+ gr.Markdown("### πŸ“€ Input")
391
+
392
+ image_input = gr.Image(
393
+ label="Chest X-Ray Image",
394
+ type="pil",
395
+ height=300
396
+ )
397
+
398
+ with gr.Accordion("Clinical Context (Optional)", open=False):
399
+ clinical_history = gr.Textbox(
400
+ label="Clinical History",
401
+ placeholder="e.g., 65-year-old male with cough and fever for 3 days",
402
+ lines=2
403
+ )
404
+ patient_age = gr.Textbox(
405
+ label="Patient Age",
406
+ placeholder="e.g., 65"
407
+ )
408
+ symptoms = gr.Textbox(
409
+ label="Presenting Symptoms",
410
+ placeholder="e.g., Cough, fever, shortness of breath",
411
+ lines=2
412
+ )
413
+
414
+ with gr.Row():
415
+ analyze_btn = gr.Button(
416
+ "πŸ”¬ Analyze X-Ray",
417
+ variant="primary",
418
+ size="lg"
419
+ )
420
+ clear_btn = gr.Button("πŸ—‘οΈ Clear", size="lg")
421
+
422
+ status_display = gr.Textbox(
423
+ label="Status",
424
+ interactive=False,
425
+ value="Ready to analyze"
426
+ )
427
+
428
+ # Right Column - Output
429
+ with gr.Column(scale=2):
430
+ gr.Markdown("### πŸ“Š Analysis Results")
431
+
432
+ with gr.Tabs():
433
+ with gr.Tab("πŸ“‹ Report"):
434
+ priority_display = gr.HTML(label="Priority Assessment")
435
+ report_output = gr.Textbox(
436
+ label="Radiology Report",
437
+ lines=20,
438
+ max_lines=30,
439
+ interactive=False
440
+ )
441
+
442
+ with gr.Tab("πŸ“ˆ Visualizations"):
443
+ with gr.Row():
444
+ workflow_plot = gr.Plot(label="Agent Pipeline Status")
445
+ priority_plot = gr.Plot(label="Priority Gauge")
446
+ radar_plot = gr.Plot(label="Analysis Confidence")
447
+
448
+ with gr.Tab("πŸ” Findings"):
449
+ findings_output = gr.Code(
450
+ label="Detected Findings (JSON)",
451
+ language="json",
452
+ lines=15
453
+ )
454
+
455
+ with gr.Tab("⚑ Metrics"):
456
+ metrics_output = gr.Markdown()
457
+
458
+ # Footer
459
+ gr.HTML("""
460
+ <div style="
461
+ margin-top: 30px;
462
+ padding: 20px;
463
+ background: #f1f5f9;
464
+ border-radius: 10px;
465
+ text-align: center;
466
+ ">
467
+ <div style="font-weight: 600; margin-bottom: 10px;">
468
+ πŸ† MedGemma Impact Challenge Submission
469
+ </div>
470
+ <div style="color: #64748b; font-size: 14px;">
471
+ Built with Google HAI-DEF: MedGemma + CXR Foundation |
472
+ Targeting: Main Track + Agentic Workflow Prize
473
+ </div>
474
+ <div style="margin-top: 10px; font-size: 12px; color: #94a3b8;">
475
+ ⚠️ For demonstration purposes only. Not for clinical use.
476
+ This AI system requires radiologist verification.
477
+ </div>
478
+ </div>
479
+ """)
480
+
481
+ # Event handlers
482
+ analyze_btn.click(
483
+ fn=process_xray,
484
+ inputs=[image_input, clinical_history, patient_age, symptoms],
485
+ outputs=[
486
+ report_output,
487
+ priority_display,
488
+ findings_output,
489
+ metrics_output,
490
+ status_display,
491
+ workflow_plot,
492
+ radar_plot,
493
+ priority_plot
494
+ ]
495
+ )
496
+
497
+ clear_btn.click(
498
+ fn=lambda: (None, "", "", "", "Ready to analyze", None, None, None, None),
499
+ outputs=[
500
+ image_input,
501
+ report_output,
502
+ priority_display,
503
+ findings_output,
504
+ status_display,
505
+ workflow_plot,
506
+ radar_plot,
507
+ priority_plot
508
+ ]
509
+ )
510
+
511
+ # Initialize on load
512
+ demo.load(fn=initialize_system, outputs=[])
513
+
514
+
515
+ # ============================================
516
+ # MAIN ENTRY POINT
517
+ # ============================================
518
+
519
+ if __name__ == "__main__":
520
+ # Initialize the system
521
+ print("πŸš€ Starting RadioFlow...")
522
+ initialize_system()
523
+
524
+ # Launch the demo
525
+ demo.launch(
526
+ server_name="0.0.0.0",
527
+ server_port=7860,
528
+ share=False,
529
+ show_error=True,
530
+ css=custom_css
531
+ )