lyimo commited on
Commit
2b4803b
·
verified ·
1 Parent(s): 9933a4f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +27 -817
app.py CHANGED
@@ -61,8 +61,9 @@ def process_patient_history(file):
61
  def analyze_ecg_image(image, vision_model="llama-3.2-90b-vision-preview"):
62
  # Fixed model - always use llama-3.2-90b-vision-preview
63
  vision_model = "llama-3.2-90b-vision-preview"
 
64
  if image is None:
65
- return "No image provided."
66
 
67
  # Convert to PIL Image if needed
68
  if not isinstance(image, Image.Image):
@@ -120,19 +121,25 @@ def analyze_ecg_image(image, vision_model="llama-3.2-90b-vision-preview"):
120
  )
121
 
122
  ecg_analysis = vision_completion.choices[0].message.content
123
-
124
  # Process the response to convert any remaining ** to HTML tags
125
  ecg_analysis = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', ecg_analysis)
126
 
127
- # Make sure all headers are properly formatted
128
- ecg_analysis = re.sub(r'^(#+)\s+(.+)
 
 
 
 
 
129
 
130
  # Generate medical assessment based on ECG readings and patient history
131
  def generate_assessment(ecg_analysis, patient_history=None, chat_model="llama-3.3-70b-versatile"):
132
  # Fixed model - always use llama-3.3-70b-versatile
133
  chat_model = "llama-3.3-70b-versatile"
134
- if not ecg_analysis or ecg_analysis.startswith("Error"):
135
- return "Please analyze an ECG image first."
 
136
 
137
  # Get current timestamp
138
  timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -222,14 +229,20 @@ Important formatting instructions:
222
  # Process the response to convert any remaining ** to HTML tags
223
  assessment_text = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', assessment_text)
224
 
225
- # Make sure all headers are properly formatted
226
- assessment_text = re.sub(r'^(#+)\s+(.+)
 
 
 
 
 
227
 
228
  # Doctor's chat interaction with the model about the patient
229
  def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
230
  # Fixed model - always use llama-3.3-70b-versatile
231
  chat_model = "llama-3.3-70b-versatile"
232
- if not ecg_analysis or ecg_analysis.startswith("Error"):
 
233
  return "Please analyze an ECG image first before starting a chat.", chat_history
234
 
235
  if not message.strip():
@@ -280,816 +293,15 @@ TIMESTAMP: {timestamp}
280
  )
281
 
282
  response = chat_completion.choices[0].message.content
283
- chat_history.append((message, response))
284
- return "", chat_history
285
-
286
- except Exception as e:
287
- error_message = f"Error in chat: {str(e)}"
288
- chat_history.append((message, error_message))
289
- return "", chat_history
290
-
291
- # Create Gradio interface
292
- with gr.Blocks(title="Cardiac ECG Analysis System", theme=gr.themes.Soft()) as app:
293
- # Session state to store data
294
- ecg_analysis_state = gr.State("")
295
-
296
- gr.Markdown("# 🫀 Cardiac ECG Analysis System")
297
- gr.Markdown("Upload an ECG image and optional patient history to get an automated analysis and assessment.")
298
-
299
- with gr.Tabs():
300
- with gr.TabItem("💻 Main Interface"):
301
- with gr.Row():
302
- with gr.Column(scale=1):
303
- # Input components
304
- with gr.Box():
305
- gr.Markdown("### 📊 ECG Image")
306
- ecg_image = gr.Image(type="pil", label="Upload ECG Image")
307
- # Display fixed model info
308
- gr.Markdown("**Vision Model:** llama-3.2-90b-vision-preview")
309
- analyze_button = gr.Button("Analyze ECG Image", variant="primary")
310
-
311
- with gr.Box():
312
- gr.Markdown("### 📋 Patient Information")
313
- patient_history_text = gr.Textbox(
314
- lines=8,
315
- label="Patient History (Manual Entry)",
316
- placeholder="Enter patient's medical history, age, sex, symptoms, medications, etc."
317
- )
318
- patient_history_file = gr.File(
319
- label="Upload Patient History File (Optional, .txt, .csv, or .xlsx)",
320
- file_types=[".txt", ".csv", ".xlsx", ".xls"]
321
- )
322
- load_history_button = gr.Button("Load Patient History from File")
323
-
324
- with gr.Box():
325
- gr.Markdown("### 🧠 Assessment Settings")
326
- # Display fixed model info
327
- gr.Markdown("**Chat Model:** llama-3.3-70b-versatile")
328
- assess_button = gr.Button("Generate Assessment", variant="primary")
329
-
330
- with gr.Column(scale=1):
331
- # Output components
332
- with gr.Box():
333
- gr.Markdown("### 📈 ECG Analysis Results")
334
- ecg_analysis_output = gr.HTML(label="ECG Analysis", elem_id="ecg-analysis")
335
-
336
- with gr.Box():
337
- gr.Markdown("### 📝 Medical Assessment")
338
- assessment_output = gr.HTML(label="Assessment", elem_id="assessment-output")
339
-
340
- gr.Markdown("## 👨‍⚕️ Doctor's Consultation")
341
- gr.Markdown("Ask follow-up questions about the patient's ECG results and medical condition.")
342
-
343
- with gr.Box():
344
- chatbot = gr.Chatbot(label="Consultation", height=400)
345
- with gr.Row():
346
- message = gr.Textbox(
347
- lines=2,
348
- label="Doctor's Question",
349
- placeholder="Ask a question about this patient's cardiac status...",
350
- scale=4
351
- )
352
- chat_button = gr.Button("Send", scale=1, variant="primary")
353
-
354
- with gr.TabItem("ℹ️ Instructions"):
355
- gr.Markdown("""
356
- ## How to Use This Application
357
-
358
- ### Step 1: Upload and Analyze ECG
359
- 1. Upload an ECG image using the file uploader in the Main Interface tab
360
- 2. Select the vision model (90b recommended for best results)
361
- 3. Click "Analyze ECG Image" to extract readings from the image
362
-
363
- ### Step 2: Add Patient Information (Optional)
364
- - Enter patient history directly in the text box, OR
365
- - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
366
-
367
- ### Step 3: Generate Assessment
368
- 1. Select the chat model (70b recommended for detailed analysis)
369
- 2. Click "Generate Assessment" to get an AI-assisted interpretation
370
-
371
- ### Step 4: Consultation
372
- - Use the chatbot interface to ask follow-up questions
373
- - The AI will consider the ECG analysis, patient history, and previous assessment in its responses
374
-
375
- ### Important Notes
376
- - This tool is designed to assist healthcare professionals, not replace clinical judgment
377
- - Always validate AI-generated medical interpretations with proper medical expertise
378
- - Patient data privacy should be maintained according to relevant regulations
379
- """)
380
-
381
- # Set up event handlers
382
- analyze_button.click(
383
- analyze_ecg_image,
384
- inputs=[ecg_image],
385
- outputs=ecg_analysis_output
386
- ).then(
387
- lambda x: x, # Pass through function to update state
388
- inputs=ecg_analysis_output,
389
- outputs=ecg_analysis_state
390
- )
391
-
392
- def process_and_update_history(file):
393
- if file is None:
394
- return "No file uploaded."
395
- processed_text = process_patient_history(file)
396
- return processed_text
397
-
398
- load_history_button.click(
399
- process_and_update_history,
400
- inputs=[patient_history_file],
401
- outputs=[patient_history_text]
402
- )
403
-
404
- assess_button.click(
405
- generate_assessment,
406
- inputs=[ecg_analysis_output, patient_history_text],
407
- outputs=assessment_output
408
- )
409
-
410
- chat_button.click(
411
- doctor_chat,
412
- inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
413
- outputs=[message, chatbot]
414
- )
415
-
416
- # Also trigger chat on Enter key
417
- message.submit(
418
- doctor_chat,
419
- inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
420
- outputs=[message, chatbot]
421
- )
422
-
423
- # Launch the app
424
- if __name__ == "__main__":
425
- app.launch()
426
- , r'<strong>\2</strong>', ecg_analysis, flags=re.MULTILINE)
427
 
428
- return ecg_analysis
429
-
430
- except Exception as e:
431
- return f"<strong style='color:red'>Error analyzing ECG image:</strong> {str(e)}"
432
-
433
- # Generate medical assessment based on ECG readings and patient history
434
- def generate_assessment(ecg_analysis, patient_history=None, chat_model="llama-3.3-70b-versatile"):
435
- # Fixed model - always use llama-3.3-70b-versatile
436
- chat_model = "llama-3.3-70b-versatile"
437
- if not ecg_analysis or ecg_analysis.startswith("Error"):
438
- return "Please analyze an ECG image first."
439
-
440
- # Get current timestamp
441
- timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
442
-
443
- # Construct prompt based on available information
444
- if patient_history and patient_history.strip():
445
- prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below and the patient's history, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
446
-
447
- ECG ANALYSIS:
448
- {ecg_analysis}
449
-
450
- PATIENT HISTORY:
451
- {patient_history}
452
-
453
- TIMESTAMP: {timestamp}
454
-
455
- Provide your assessment with proper formatting:
456
- <strong>Summary of Findings</strong>
457
- (Your summary here)
458
-
459
- <strong>Key Abnormalities</strong>
460
- (List any abnormalities here)
461
-
462
- <strong>Potential Clinical Implications</strong>
463
- (Describe implications here)
464
-
465
- <strong>Recommendation</strong>
466
- (Include urgency level)
467
-
468
- <strong>Differential Considerations</strong>
469
- (List differentials here)
470
-
471
- Important formatting instructions:
472
- - Use proper HTML formatting with <strong> tags for headings
473
- - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
474
- - For any urgent findings, use <strong style="color:red"> to highlight them
475
- """
476
- else:
477
- prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
478
-
479
- ECG ANALYSIS:
480
- {ecg_analysis}
481
-
482
- TIMESTAMP: {timestamp}
483
-
484
- Provide your assessment with proper formatting:
485
- <strong>Summary of Findings</strong>
486
- (Your summary here)
487
-
488
- <strong>Key Abnormalities</strong>
489
- (List any abnormalities here)
490
-
491
- <strong>Potential Clinical Implications</strong>
492
- (Describe implications here)
493
-
494
- <strong>Recommendation</strong>
495
- (Include urgency level)
496
-
497
- <strong>Differential Considerations</strong>
498
- (List differentials here)
499
-
500
- Important formatting instructions:
501
- - Use proper HTML formatting with <strong> tags for headings
502
- - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
503
- - For any urgent findings, use <strong style="color:red"> to highlight them
504
- """
505
-
506
- try:
507
- assessment_completion = client.chat.completions.create(
508
- messages=[
509
- {
510
- "role": "system",
511
- "content": "You are a medical AI assistant specialized in cardiology. Provide accurate, clinically relevant interpretations of ECG data. If there are concerning findings that might indicate a medical emergency, clearly highlight them. Avoid definitive diagnoses but provide reasoned medical assessments based on the data provided."
512
- },
513
- {
514
- "role": "user",
515
- "content": prompt
516
- }
517
- ],
518
- model=chat_model,
519
- temperature=0.2, # Lower temperature for more factual responses
520
- max_completion_tokens=2048,
521
- )
522
 
523
- return assessment_completion.choices[0].message.content
524
-
525
- except Exception as e:
526
- return f"Error generating assessment: {str(e)}"
527
-
528
- # Doctor's chat interaction with the model about the patient
529
- def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
530
- # Fixed model - always use llama-3.3-70b-versatile
531
- chat_model = "llama-3.3-70b-versatile"
532
- if not ecg_analysis or ecg_analysis.startswith("Error"):
533
- return "Please analyze an ECG image first before starting a chat.", chat_history
534
-
535
- if not message.strip():
536
- return "", chat_history
537
-
538
- # Get current timestamp
539
- timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
540
-
541
- # Prepare chat context
542
- context = f"""ECG ANALYSIS:
543
- {ecg_analysis}
544
-
545
- MEDICAL ASSESSMENT:
546
- {assessment}
547
-
548
- TIMESTAMP: {timestamp}
549
-
550
- """
551
-
552
- if patient_history and patient_history.strip():
553
- context += f"""PATIENT HISTORY:
554
- {patient_history}
555
-
556
- """
557
-
558
- # Construct full chat history for context
559
- messages = [
560
- {
561
- "role": "system",
562
- "content": f"You are a medical AI assistant specialized in cardiology. You are helping a doctor interpret ECG results and patient data. Answer the doctor's questions based on the following information:\n\n{context}"
563
- }
564
- ]
565
-
566
- # Add chat history to the context (limited to last 10 exchanges to avoid token limits)
567
- for entry in chat_history[-10:]:
568
- messages.append({"role": "user", "content": entry[0]})
569
- messages.append({"role": "assistant", "content": entry[1]})
570
-
571
- # Add the current message
572
- messages.append({"role": "user", "content": message})
573
-
574
- try:
575
- chat_completion = client.chat.completions.create(
576
- messages=messages,
577
- model=chat_model,
578
- temperature=0.3,
579
- max_completion_tokens=1024,
580
- )
581
-
582
- response = chat_completion.choices[0].message.content
583
- chat_history.append((message, response))
584
- return "", chat_history
585
-
586
- except Exception as e:
587
- error_message = f"Error in chat: {str(e)}"
588
- chat_history.append((message, error_message))
589
- return "", chat_history
590
-
591
- # Create Gradio interface
592
- with gr.Blocks(title="Cardiac ECG Analysis System", theme=gr.themes.Soft()) as app:
593
- # Session state to store data
594
- ecg_analysis_state = gr.State("")
595
-
596
- gr.Markdown("# 🫀 Cardiac ECG Analysis System")
597
- gr.Markdown("Upload an ECG image and optional patient history to get an automated analysis and assessment.")
598
-
599
- with gr.Tabs():
600
- with gr.TabItem("💻 Main Interface"):
601
- with gr.Row():
602
- with gr.Column(scale=1):
603
- # Input components
604
- with gr.Box():
605
- gr.Markdown("### 📊 ECG Image")
606
- ecg_image = gr.Image(type="pil", label="Upload ECG Image")
607
- # Display fixed model info
608
- gr.Markdown("**Vision Model:** llama-3.2-90b-vision-preview")
609
- analyze_button = gr.Button("Analyze ECG Image", variant="primary")
610
-
611
- with gr.Box():
612
- gr.Markdown("### 📋 Patient Information")
613
- patient_history_text = gr.Textbox(
614
- lines=8,
615
- label="Patient History (Manual Entry)",
616
- placeholder="Enter patient's medical history, age, sex, symptoms, medications, etc."
617
- )
618
- patient_history_file = gr.File(
619
- label="Upload Patient History File (Optional, .txt, .csv, or .xlsx)",
620
- file_types=[".txt", ".csv", ".xlsx", ".xls"]
621
- )
622
- load_history_button = gr.Button("Load Patient History from File")
623
-
624
- with gr.Box():
625
- gr.Markdown("### 🧠 Assessment Settings")
626
- # Display fixed model info
627
- gr.Markdown("**Chat Model:** llama-3.3-70b-versatile")
628
- assess_button = gr.Button("Generate Assessment", variant="primary")
629
-
630
- with gr.Column(scale=1):
631
- # Output components
632
- with gr.Box():
633
- gr.Markdown("### 📈 ECG Analysis Results")
634
- ecg_analysis_output = gr.HTML(label="ECG Analysis", elem_id="ecg-analysis")
635
-
636
- with gr.Box():
637
- gr.Markdown("### 📝 Medical Assessment")
638
- assessment_output = gr.HTML(label="Assessment", elem_id="assessment-output")
639
-
640
- gr.Markdown("## 👨‍⚕️ Doctor's Consultation")
641
- gr.Markdown("Ask follow-up questions about the patient's ECG results and medical condition.")
642
-
643
- with gr.Box():
644
- chatbot = gr.Chatbot(label="Consultation", height=400)
645
- with gr.Row():
646
- message = gr.Textbox(
647
- lines=2,
648
- label="Doctor's Question",
649
- placeholder="Ask a question about this patient's cardiac status...",
650
- scale=4
651
- )
652
- chat_button = gr.Button("Send", scale=1, variant="primary")
653
-
654
- with gr.TabItem("ℹ️ Instructions"):
655
- gr.Markdown("""
656
- ## How to Use This Application
657
-
658
- ### Step 1: Upload and Analyze ECG
659
- 1. Upload an ECG image using the file uploader in the Main Interface tab
660
- 2. Select the vision model (90b recommended for best results)
661
- 3. Click "Analyze ECG Image" to extract readings from the image
662
-
663
- ### Step 2: Add Patient Information (Optional)
664
- - Enter patient history directly in the text box, OR
665
- - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
666
-
667
- ### Step 3: Generate Assessment
668
- 1. Select the chat model (70b recommended for detailed analysis)
669
- 2. Click "Generate Assessment" to get an AI-assisted interpretation
670
-
671
- ### Step 4: Consultation
672
- - Use the chatbot interface to ask follow-up questions
673
- - The AI will consider the ECG analysis, patient history, and previous assessment in its responses
674
-
675
- ### Important Notes
676
- - This tool is designed to assist healthcare professionals, not replace clinical judgment
677
- - Always validate AI-generated medical interpretations with proper medical expertise
678
- - Patient data privacy should be maintained according to relevant regulations
679
- """)
680
-
681
- # Set up event handlers
682
- analyze_button.click(
683
- analyze_ecg_image,
684
- inputs=[ecg_image],
685
- outputs=ecg_analysis_output
686
- ).then(
687
- lambda x: x, # Pass through function to update state
688
- inputs=ecg_analysis_output,
689
- outputs=ecg_analysis_state
690
- )
691
-
692
- def process_and_update_history(file):
693
- if file is None:
694
- return "No file uploaded."
695
- processed_text = process_patient_history(file)
696
- return processed_text
697
-
698
- load_history_button.click(
699
- process_and_update_history,
700
- inputs=[patient_history_file],
701
- outputs=[patient_history_text]
702
- )
703
-
704
- assess_button.click(
705
- generate_assessment,
706
- inputs=[ecg_analysis_output, patient_history_text],
707
- outputs=assessment_output
708
- )
709
-
710
- chat_button.click(
711
- doctor_chat,
712
- inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
713
- outputs=[message, chatbot]
714
- )
715
-
716
- # Also trigger chat on Enter key
717
- message.submit(
718
- doctor_chat,
719
- inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
720
- outputs=[message, chatbot]
721
- )
722
-
723
- # Launch the app
724
- if __name__ == "__main__":
725
- app.launch()
726
- , r'<strong>\2</strong>', assessment_text, flags=re.MULTILINE)
727
-
728
- return assessment_text
729
-
730
- except Exception as e:
731
- return f"<strong style='color:red'>Error generating assessment:</strong> {str(e)}"
732
-
733
- # Doctor's chat interaction with the model about the patient
734
- def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
735
- # Fixed model - always use llama-3.3-70b-versatile
736
- chat_model = "llama-3.3-70b-versatile"
737
- if not ecg_analysis or ecg_analysis.startswith("Error"):
738
- return "Please analyze an ECG image first before starting a chat.", chat_history
739
-
740
- if not message.strip():
741
- return "", chat_history
742
-
743
- # Get current timestamp
744
- timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
745
-
746
- # Prepare chat context
747
- context = f"""ECG ANALYSIS:
748
- {ecg_analysis}
749
-
750
- MEDICAL ASSESSMENT:
751
- {assessment}
752
-
753
- TIMESTAMP: {timestamp}
754
-
755
- """
756
-
757
- if patient_history and patient_history.strip():
758
- context += f"""PATIENT HISTORY:
759
- {patient_history}
760
-
761
- """
762
-
763
- # Construct full chat history for context
764
- messages = [
765
- {
766
- "role": "system",
767
- "content": f"You are a medical AI assistant specialized in cardiology. You are helping a doctor interpret ECG results and patient data. Answer the doctor's questions based on the following information:\n\n{context}"
768
- }
769
- ]
770
-
771
- # Add chat history to the context (limited to last 10 exchanges to avoid token limits)
772
- for entry in chat_history[-10:]:
773
- messages.append({"role": "user", "content": entry[0]})
774
- messages.append({"role": "assistant", "content": entry[1]})
775
-
776
- # Add the current message
777
- messages.append({"role": "user", "content": message})
778
-
779
- try:
780
- chat_completion = client.chat.completions.create(
781
- messages=messages,
782
- model=chat_model,
783
- temperature=0.3,
784
- max_completion_tokens=1024,
785
- )
786
-
787
- response = chat_completion.choices[0].message.content
788
- chat_history.append((message, response))
789
- return "", chat_history
790
-
791
- except Exception as e:
792
- error_message = f"Error in chat: {str(e)}"
793
- chat_history.append((message, error_message))
794
- return "", chat_history
795
-
796
- # Create Gradio interface
797
- with gr.Blocks(title="Cardiac ECG Analysis System", theme=gr.themes.Soft()) as app:
798
- # Session state to store data
799
- ecg_analysis_state = gr.State("")
800
-
801
- gr.Markdown("# 🫀 Cardiac ECG Analysis System")
802
- gr.Markdown("Upload an ECG image and optional patient history to get an automated analysis and assessment.")
803
-
804
- with gr.Tabs():
805
- with gr.TabItem("💻 Main Interface"):
806
- with gr.Row():
807
- with gr.Column(scale=1):
808
- # Input components
809
- with gr.Box():
810
- gr.Markdown("### 📊 ECG Image")
811
- ecg_image = gr.Image(type="pil", label="Upload ECG Image")
812
- # Display fixed model info
813
- gr.Markdown("**Vision Model:** llama-3.2-90b-vision-preview")
814
- analyze_button = gr.Button("Analyze ECG Image", variant="primary")
815
-
816
- with gr.Box():
817
- gr.Markdown("### 📋 Patient Information")
818
- patient_history_text = gr.Textbox(
819
- lines=8,
820
- label="Patient History (Manual Entry)",
821
- placeholder="Enter patient's medical history, age, sex, symptoms, medications, etc."
822
- )
823
- patient_history_file = gr.File(
824
- label="Upload Patient History File (Optional, .txt, .csv, or .xlsx)",
825
- file_types=[".txt", ".csv", ".xlsx", ".xls"]
826
- )
827
- load_history_button = gr.Button("Load Patient History from File")
828
-
829
- with gr.Box():
830
- gr.Markdown("### 🧠 Assessment Settings")
831
- # Display fixed model info
832
- gr.Markdown("**Chat Model:** llama-3.3-70b-versatile")
833
- assess_button = gr.Button("Generate Assessment", variant="primary")
834
-
835
- with gr.Column(scale=1):
836
- # Output components
837
- with gr.Box():
838
- gr.Markdown("### 📈 ECG Analysis Results")
839
- ecg_analysis_output = gr.HTML(label="ECG Analysis", elem_id="ecg-analysis")
840
-
841
- with gr.Box():
842
- gr.Markdown("### 📝 Medical Assessment")
843
- assessment_output = gr.HTML(label="Assessment", elem_id="assessment-output")
844
-
845
- gr.Markdown("## 👨‍⚕️ Doctor's Consultation")
846
- gr.Markdown("Ask follow-up questions about the patient's ECG results and medical condition.")
847
-
848
- with gr.Box():
849
- chatbot = gr.Chatbot(label="Consultation", height=400)
850
- with gr.Row():
851
- message = gr.Textbox(
852
- lines=2,
853
- label="Doctor's Question",
854
- placeholder="Ask a question about this patient's cardiac status...",
855
- scale=4
856
- )
857
- chat_button = gr.Button("Send", scale=1, variant="primary")
858
-
859
- with gr.TabItem("ℹ️ Instructions"):
860
- gr.Markdown("""
861
- ## How to Use This Application
862
-
863
- ### Step 1: Upload and Analyze ECG
864
- 1. Upload an ECG image using the file uploader in the Main Interface tab
865
- 2. Select the vision model (90b recommended for best results)
866
- 3. Click "Analyze ECG Image" to extract readings from the image
867
-
868
- ### Step 2: Add Patient Information (Optional)
869
- - Enter patient history directly in the text box, OR
870
- - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
871
-
872
- ### Step 3: Generate Assessment
873
- 1. Select the chat model (70b recommended for detailed analysis)
874
- 2. Click "Generate Assessment" to get an AI-assisted interpretation
875
-
876
- ### Step 4: Consultation
877
- - Use the chatbot interface to ask follow-up questions
878
- - The AI will consider the ECG analysis, patient history, and previous assessment in its responses
879
-
880
- ### Important Notes
881
- - This tool is designed to assist healthcare professionals, not replace clinical judgment
882
- - Always validate AI-generated medical interpretations with proper medical expertise
883
- - Patient data privacy should be maintained according to relevant regulations
884
- """)
885
-
886
- # Set up event handlers
887
- analyze_button.click(
888
- analyze_ecg_image,
889
- inputs=[ecg_image],
890
- outputs=ecg_analysis_output
891
- ).then(
892
- lambda x: x, # Pass through function to update state
893
- inputs=ecg_analysis_output,
894
- outputs=ecg_analysis_state
895
- )
896
-
897
- def process_and_update_history(file):
898
- if file is None:
899
- return "No file uploaded."
900
- processed_text = process_patient_history(file)
901
- return processed_text
902
-
903
- load_history_button.click(
904
- process_and_update_history,
905
- inputs=[patient_history_file],
906
- outputs=[patient_history_text]
907
- )
908
-
909
- assess_button.click(
910
- generate_assessment,
911
- inputs=[ecg_analysis_output, patient_history_text],
912
- outputs=assessment_output
913
- )
914
-
915
- chat_button.click(
916
- doctor_chat,
917
- inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
918
- outputs=[message, chatbot]
919
- )
920
-
921
- # Also trigger chat on Enter key
922
- message.submit(
923
- doctor_chat,
924
- inputs=[message, chatbot, ecg_analysis_output, patient_history_text, assessment_output],
925
- outputs=[message, chatbot]
926
- )
927
-
928
- # Launch the app
929
- if __name__ == "__main__":
930
- app.launch()
931
- , r'<strong>\2</strong>', ecg_analysis, flags=re.MULTILINE)
932
-
933
- return ecg_analysis
934
-
935
- except Exception as e:
936
- return f"<strong style='color:red'>Error analyzing ECG image:</strong> {str(e)}"
937
-
938
- # Generate medical assessment based on ECG readings and patient history
939
- def generate_assessment(ecg_analysis, patient_history=None, chat_model="llama-3.3-70b-versatile"):
940
- # Fixed model - always use llama-3.3-70b-versatile
941
- chat_model = "llama-3.3-70b-versatile"
942
- if not ecg_analysis or ecg_analysis.startswith("Error"):
943
- return "Please analyze an ECG image first."
944
-
945
- # Get current timestamp
946
- timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
947
-
948
- # Construct prompt based on available information
949
- if patient_history and patient_history.strip():
950
- prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below and the patient's history, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
951
-
952
- ECG ANALYSIS:
953
- {ecg_analysis}
954
-
955
- PATIENT HISTORY:
956
- {patient_history}
957
-
958
- TIMESTAMP: {timestamp}
959
-
960
- Provide your assessment with proper formatting:
961
- <strong>Summary of Findings</strong>
962
- (Your summary here)
963
-
964
- <strong>Key Abnormalities</strong>
965
- (List any abnormalities here)
966
-
967
- <strong>Potential Clinical Implications</strong>
968
- (Describe implications here)
969
-
970
- <strong>Recommendation</strong>
971
- (Include urgency level)
972
-
973
- <strong>Differential Considerations</strong>
974
- (List differentials here)
975
-
976
- Important formatting instructions:
977
- - Use proper HTML formatting with <strong> tags for headings
978
- - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
979
- - For any urgent findings, use <strong style="color:red"> to highlight them
980
- """
981
- else:
982
- prompt = f"""You are a highly trained cardiologist assistant. Based on the ECG analysis below, provide a comprehensive assessment of the patient's cardiac status. Indicate clearly if there are any concerning findings that require immediate medical attention.
983
-
984
- ECG ANALYSIS:
985
- {ecg_analysis}
986
-
987
- TIMESTAMP: {timestamp}
988
-
989
- Provide your assessment with proper formatting:
990
- <strong>Summary of Findings</strong>
991
- (Your summary here)
992
-
993
- <strong>Key Abnormalities</strong>
994
- (List any abnormalities here)
995
-
996
- <strong>Potential Clinical Implications</strong>
997
- (Describe implications here)
998
-
999
- <strong>Recommendation</strong>
1000
- (Include urgency level)
1001
-
1002
- <strong>Differential Considerations</strong>
1003
- (List differentials here)
1004
-
1005
- Important formatting instructions:
1006
- - Use proper HTML formatting with <strong> tags for headings
1007
- - Do not use asterisks (**) for emphasis - use proper HTML formatting instead
1008
- - For any urgent findings, use <strong style="color:red"> to highlight them
1009
- """
1010
-
1011
- try:
1012
- assessment_completion = client.chat.completions.create(
1013
- messages=[
1014
- {
1015
- "role": "system",
1016
- "content": "You are a medical AI assistant specialized in cardiology. Provide accurate, clinically relevant interpretations of ECG data. If there are concerning findings that might indicate a medical emergency, clearly highlight them. Avoid definitive diagnoses but provide reasoned medical assessments based on the data provided."
1017
- },
1018
- {
1019
- "role": "user",
1020
- "content": prompt
1021
- }
1022
- ],
1023
- model=chat_model,
1024
- temperature=0.2, # Lower temperature for more factual responses
1025
- max_completion_tokens=2048,
1026
- )
1027
-
1028
- return assessment_completion.choices[0].message.content
1029
-
1030
- except Exception as e:
1031
- return f"Error generating assessment: {str(e)}"
1032
-
1033
- # Doctor's chat interaction with the model about the patient
1034
- def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
1035
- # Fixed model - always use llama-3.3-70b-versatile
1036
- chat_model = "llama-3.3-70b-versatile"
1037
- if not ecg_analysis or ecg_analysis.startswith("Error"):
1038
- return "Please analyze an ECG image first before starting a chat.", chat_history
1039
-
1040
- if not message.strip():
1041
- return "", chat_history
1042
-
1043
- # Get current timestamp
1044
- timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1045
-
1046
- # Prepare chat context
1047
- context = f"""ECG ANALYSIS:
1048
- {ecg_analysis}
1049
-
1050
- MEDICAL ASSESSMENT:
1051
- {assessment}
1052
-
1053
- TIMESTAMP: {timestamp}
1054
-
1055
- """
1056
-
1057
- if patient_history and patient_history.strip():
1058
- context += f"""PATIENT HISTORY:
1059
- {patient_history}
1060
-
1061
- """
1062
-
1063
- # Construct full chat history for context
1064
- messages = [
1065
- {
1066
- "role": "system",
1067
- "content": f"You are a medical AI assistant specialized in cardiology. You are helping a doctor interpret ECG results and patient data. Answer the doctor's questions based on the following information:\n\n{context}"
1068
- }
1069
- ]
1070
-
1071
- # Add chat history to the context (limited to last 10 exchanges to avoid token limits)
1072
- for entry in chat_history[-10:]:
1073
- messages.append({"role": "user", "content": entry[0]})
1074
- messages.append({"role": "assistant", "content": entry[1]})
1075
-
1076
- # Add the current message
1077
- messages.append({"role": "user", "content": message})
1078
-
1079
- try:
1080
- chat_completion = client.chat.completions.create(
1081
- messages=messages,
1082
- model=chat_model,
1083
- temperature=0.3,
1084
- max_completion_tokens=1024,
1085
- )
1086
-
1087
- response = chat_completion.choices[0].message.content
1088
  chat_history.append((message, response))
1089
  return "", chat_history
1090
 
1091
  except Exception as e:
1092
- error_message = f"Error in chat: {str(e)}"
1093
  chat_history.append((message, error_message))
1094
  return "", chat_history
1095
 
@@ -1162,16 +374,14 @@ with gr.Blocks(title="Cardiac ECG Analysis System", theme=gr.themes.Soft()) as a
1162
 
1163
  ### Step 1: Upload and Analyze ECG
1164
  1. Upload an ECG image using the file uploader in the Main Interface tab
1165
- 2. Select the vision model (90b recommended for best results)
1166
- 3. Click "Analyze ECG Image" to extract readings from the image
1167
 
1168
  ### Step 2: Add Patient Information (Optional)
1169
  - Enter patient history directly in the text box, OR
1170
  - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
1171
 
1172
  ### Step 3: Generate Assessment
1173
- 1. Select the chat model (70b recommended for detailed analysis)
1174
- 2. Click "Generate Assessment" to get an AI-assisted interpretation
1175
 
1176
  ### Step 4: Consultation
1177
  - Use the chatbot interface to ask follow-up questions
 
61
  def analyze_ecg_image(image, vision_model="llama-3.2-90b-vision-preview"):
62
  # Fixed model - always use llama-3.2-90b-vision-preview
63
  vision_model = "llama-3.2-90b-vision-preview"
64
+
65
  if image is None:
66
+ return "<strong style='color:red'>No image provided.</strong>"
67
 
68
  # Convert to PIL Image if needed
69
  if not isinstance(image, Image.Image):
 
121
  )
122
 
123
  ecg_analysis = vision_completion.choices[0].message.content
124
+
125
  # Process the response to convert any remaining ** to HTML tags
126
  ecg_analysis = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', ecg_analysis)
127
 
128
+ # Make sure all headers are properly formatted (with complete pattern)
129
+ ecg_analysis = re.sub(r'^(#+)\s+(.+)$', r'<strong>\2</strong>', ecg_analysis, flags=re.MULTILINE)
130
+
131
+ return ecg_analysis
132
+
133
+ except Exception as e:
134
+ return f"<strong style='color:red'>Error analyzing ECG image:</strong> {str(e)}"
135
 
136
  # Generate medical assessment based on ECG readings and patient history
137
  def generate_assessment(ecg_analysis, patient_history=None, chat_model="llama-3.3-70b-versatile"):
138
  # Fixed model - always use llama-3.3-70b-versatile
139
  chat_model = "llama-3.3-70b-versatile"
140
+
141
+ if not ecg_analysis or ecg_analysis.startswith("<strong style='color:red'>Error"):
142
+ return "<strong style='color:red'>Please analyze an ECG image first.</strong>"
143
 
144
  # Get current timestamp
145
  timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
229
  # Process the response to convert any remaining ** to HTML tags
230
  assessment_text = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', assessment_text)
231
 
232
+ # Make sure all headers are properly formatted (with complete pattern)
233
+ assessment_text = re.sub(r'^(#+)\s+(.+)$', r'<strong>\2</strong>', assessment_text, flags=re.MULTILINE)
234
+
235
+ return assessment_text
236
+
237
+ except Exception as e:
238
+ return f"<strong style='color:red'>Error generating assessment:</strong> {str(e)}"
239
 
240
  # Doctor's chat interaction with the model about the patient
241
  def doctor_chat(message, chat_history, ecg_analysis, patient_history, assessment, chat_model="llama-3.3-70b-versatile"):
242
  # Fixed model - always use llama-3.3-70b-versatile
243
  chat_model = "llama-3.3-70b-versatile"
244
+
245
+ if not ecg_analysis or ecg_analysis.startswith("<strong style='color:red'>Error"):
246
  return "Please analyze an ECG image first before starting a chat.", chat_history
247
 
248
  if not message.strip():
 
293
  )
294
 
295
  response = chat_completion.choices[0].message.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
 
297
+ # Process any remaining asterisks to HTML tags in the response
298
+ response = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  chat_history.append((message, response))
301
  return "", chat_history
302
 
303
  except Exception as e:
304
+ error_message = f"<strong style='color:red'>Error in chat:</strong> {str(e)}"
305
  chat_history.append((message, error_message))
306
  return "", chat_history
307
 
 
374
 
375
  ### Step 1: Upload and Analyze ECG
376
  1. Upload an ECG image using the file uploader in the Main Interface tab
377
+ 2. Click "Analyze ECG Image" to extract readings from the image
 
378
 
379
  ### Step 2: Add Patient Information (Optional)
380
  - Enter patient history directly in the text box, OR
381
  - Upload a patient history file (.txt, .csv, or .xlsx) and click "Load Patient History from File"
382
 
383
  ### Step 3: Generate Assessment
384
+ - Click "Generate Assessment" to get an AI-assisted interpretation
 
385
 
386
  ### Step 4: Consultation
387
  - Use the chatbot interface to ask follow-up questions