Ali2206 commited on
Commit
6caaa86
Β·
verified Β·
1 Parent(s): 2e5b8b2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +172 -81
app.py CHANGED
@@ -301,6 +301,13 @@ class ClinicalOversightApp:
301
  logger.info("AI Agent Ready")
302
  return agent
303
 
 
 
 
 
 
 
 
304
  def process_response_stream(self, prompt: str, history: List[dict]) -> Generator[dict, None, None]:
305
  """Stream the agent's response with proper formatting"""
306
  full_response = ""
@@ -398,8 +405,7 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
398
  yield (chatbot_output, download_output, final_summary, progress_text)
399
 
400
  combined_response += f"--- Analysis for Chunk {chunk_idx} ---\n{chunk_response}\n"
401
- torch.cuda.empty_cache()
402
- gc.collect()
403
 
404
  # Generate final outputs
405
  final_summary = self.text_processor.summarize_results(combined_response)
@@ -423,14 +429,23 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
423
  progress_text = {"visible": False}
424
  yield (chatbot_output, download_output, final_summary, progress_text)
425
  finally:
426
- torch.cuda.empty_cache()
427
- gc.collect()
428
 
429
  def _update_progress(self, current: int, total: int, stage: str = "") -> Dict[str, Any]:
430
  """Format progress update for UI"""
431
  progress = f"{stage} - {current}/{total}" if stage else f"{current}/{total}"
432
  return {"value": progress, "visible": True}
433
 
 
 
 
 
 
 
 
 
 
 
434
  def create_interface(self):
435
  """Create Gradio interface with refined ChatGPT-like design"""
436
  css = """
@@ -442,7 +457,7 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
442
  .gradio-container {
443
  max-width: 800px;
444
  margin: 0 auto;
445
- padding: 20px;
446
  }
447
  .chat-container {
448
  background: var(--chat-bg);
@@ -450,7 +465,7 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
450
  padding: 24px;
451
  height: 80vh;
452
  overflow-y: auto;
453
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
454
  position: relative;
455
  }
456
  .message {
@@ -459,13 +474,15 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
459
  border-radius: 12px;
460
  max-width: 80%;
461
  transition: all 0.3s ease;
 
462
  position: relative;
463
  }
464
  .message:hover {
465
  transform: translateY(-2px);
 
466
  }
467
  .message.user {
468
- background: #007bff;
469
  color: white;
470
  margin-left: auto;
471
  }
@@ -482,11 +499,11 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
482
  .input-container {
483
  display: flex;
484
  align-items: center;
485
- margin-top: 20px;
486
  background: var(--chat-bg);
487
  padding: 12px 24px;
488
  border-radius: 30px;
489
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
490
  position: sticky;
491
  bottom: 0;
492
  }
@@ -497,34 +514,52 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
497
  color: var(--text-color);
498
  outline: none;
499
  font-size: 1em;
 
500
  }
501
  .send-btn {
502
- background: #007bff;
503
  color: white;
504
  border: none;
505
  border-radius: 20px;
506
  padding: 10px 20px;
507
  margin-left: 12px;
508
- transition: background 0.2s ease;
509
  }
510
  .send-btn:hover {
511
- background: #0056b3;
 
 
 
512
  }
513
  .sidebar {
514
  background: var(--sidebar-bg);
515
  padding: 24px;
516
  border-radius: 16px;
517
- margin-top: 20px;
518
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
519
- transition: transform 0.3s ease;
520
  transform: translateX(0);
521
- }
522
- .sidebar-hidden {
523
- transform: translateX(100%);
524
- position: absolute;
525
  right: 0;
526
  top: 100px;
527
  width: 300px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528
  }
529
  .header {
530
  text-align: center;
@@ -532,9 +567,9 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
532
  }
533
  .theme-toggle {
534
  position: absolute;
535
- top: 20px;
536
- right: 20px;
537
- background: #007bff;
538
  color: white;
539
  border: none;
540
  border-radius: 20px;
@@ -549,11 +584,25 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
549
  left: 50%;
550
  transform: translateX(-50%);
551
  font-size: 1.2em;
552
- animation: spin 1s linear infinite;
553
  }
554
- @keyframes spin {
555
- 0% { transform: translateX(-50%) rotate(0deg); }
556
- 100% { transform: translateX(-50%) rotate(360deg); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
  }
558
  :root {
559
  --background: #ffffff;
@@ -561,15 +610,14 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
561
  --chat-bg: #f9fafb;
562
  --message-bg: #e5e5ea;
563
  --sidebar-bg: #f1f3f5;
 
564
  }
565
- @media (prefers-color-scheme: dark) {
566
- :root {
567
- --background: #1e2a44;
568
- --text-color: #ffffff;
569
- --chat-bg: #2d3b55;
570
- --message-bg: #3e4c6a;
571
- --sidebar-bg: #2a3650;
572
- }
573
  }
574
  @media (max-width: 600px) {
575
  .gradio-container {
@@ -589,6 +637,7 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
589
  }
590
  .sidebar {
591
  width: 100%;
 
592
  }
593
  .sidebar-hidden {
594
  transform: translateX(100%);
@@ -597,16 +646,10 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
597
  """
598
 
599
  js = """
600
- function toggleTheme() {
601
- const root = document.documentElement;
602
- const isDark = root.style.getPropertyValue('--background') === '#1e2a44';
603
- root.style.setProperty('--background', isDark ? '#ffffff' : '#1e2a44');
604
- root.style.setProperty('--text-color', isDark ? '#333333' : '#ffffff');
605
- root.style.setProperty('--chat-bg', isDark ? '#f9fafb' : '#2d3b55');
606
- root.style.setProperty('--message-bg', isDark ? '#e5e5ea' : '#3e4c6a');
607
- root.style.setProperty('--sidebar-bg', isDark ? '#f1f3f5' : '#2a3650');
608
- localStorage.setItem('theme', isDark ? 'light' : 'dark');
609
- document.querySelector('.theme-toggle').innerHTML = isDark ? 'πŸŒ™ Dark Mode' : 'β˜€οΈ Light Mode';
610
  }
611
 
612
  function toggleSidebar() {
@@ -615,14 +658,16 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
615
  }
616
 
617
  document.addEventListener('DOMContentLoaded', () => {
618
- const savedTheme = localStorage.getItem('theme');
619
- if (savedTheme === 'dark') toggleTheme();
620
  document.querySelector('.sidebar').classList.add('sidebar-hidden');
621
- document.querySelector('.theme-toggle').innerHTML = savedTheme === 'dark' ? 'β˜€οΈ Light Mode' : 'πŸŒ™ Dark Mode';
622
  });
623
  """
624
 
625
  with gr.Blocks(theme=gr.themes.Default(), css=css, js=js, title="Clinical Oversight Assistant") as app:
 
 
 
626
  gr.HTML("""
627
  <div class='header'>
628
  <h1 style='color: var(--text-color);'>🩺 Clinical Oversight Assistant</h1>
@@ -630,10 +675,10 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
630
  AI-powered analysis of patient records for missed diagnoses
631
  </p>
632
  </div>
 
633
  """)
634
- gr.Button("πŸŒ™ Dark Mode", elem_classes="theme-toggle").click(
635
- None, None, None, _js="toggleTheme"
636
- )
637
 
638
  with gr.Column(elem_classes="chat-container"):
639
  chatbot = gr.Chatbot(
@@ -644,29 +689,34 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
644
  elem_classes="chatbot",
645
  render_markdown=True
646
  )
647
- gr.HTML("<div class='loading-spinner' style='display: none;'>⏳</div>")
 
648
 
649
  with gr.Row():
650
- gr.Button("πŸ“‚ Tools", variant="secondary").click(
651
- None, None, None, _js="toggleSidebar"
652
- )
653
 
654
  with gr.Column(elem_classes="sidebar"):
655
- gr.Markdown("### πŸ“Ž Upload Records")
656
  file_upload = gr.File(
657
  file_types=[".pdf", ".csv", ".xls", ".xlsx"],
658
  file_count="multiple",
659
- label="Patient Records"
 
 
660
  )
661
- gr.Markdown("### πŸ“ Analysis Summary")
662
  final_summary = gr.Markdown(
663
- "Analysis results will appear here..."
 
 
664
  )
665
- gr.Markdown("### πŸ“„ Full Report")
666
  download_output = gr.File(
667
  label="Download Report",
668
  visible=False,
669
- interactive=False
 
 
670
  )
671
 
672
  with gr.Row(elem_classes="input-container"):
@@ -686,50 +736,90 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
686
  progress_text = gr.Textbox(
687
  label="Progress Status",
688
  visible=False,
689
- interactive=False
 
690
  )
691
 
692
- def show_loading_spinner():
693
- return gr.update(value="<div class='loading-spinner'>⏳</div>", visible=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
 
695
- def hide_loading_spinner():
696
- return gr.update(value="<div class='loading-spinner' style='display: none;'>⏳</div>", visible=False)
 
 
 
 
 
697
 
 
698
  send_btn.click(
699
- show_loading_spinner,
700
- outputs=[chatbot],
701
- _js="() => { document.querySelector('.loading-spinner').style.display = 'block'; }"
 
 
 
 
702
  ).then(
703
  self.analyze,
704
  inputs=[msg_input, chatbot, file_upload],
705
  outputs=[chatbot, download_output, final_summary, progress_text],
706
  show_progress="hidden"
707
  ).then(
708
- hide_loading_spinner,
709
- outputs=[chatbot],
710
- _js="() => { document.querySelector('.loading-spinner').style.display = 'none'; }"
 
 
 
 
711
  )
712
 
713
  msg_input.submit(
714
- show_loading_spinner,
715
- outputs=[chatbot],
716
- _js="() => { document.querySelector('.loading-spinner').style.display = 'block'; }"
 
 
 
 
717
  ).then(
718
  self.analyze,
719
  inputs=[msg_input, chatbot, file_upload],
720
  outputs=[chatbot, download_output, final_summary, progress_text],
721
  show_progress="hidden"
722
  ).then(
723
- hide_loading_spinner,
724
- outputs=[chatbot],
725
- _js="() => { document.querySelector('.loading-spinner').style.display = 'none'; }"
 
 
 
 
726
  )
727
 
728
  app.load(
729
  lambda: [
730
- [], None, "", "", None, {"visible": False}
731
  ],
732
- outputs=[chatbot, download_output, final_summary, msg_input, file_upload, progress_text],
733
  queue=False
734
  )
735
 
@@ -737,6 +827,7 @@ Patient Record (Chunk {chunk_idx}/{len(chunks)}):
737
 
738
  # ==================== APPLICATION ENTRY POINT ====================
739
  if __name__ == "__main__":
 
740
  try:
741
  logger.info("Starting Clinical Oversight Assistant...")
742
  app = ClinicalOversightApp()
@@ -756,5 +847,5 @@ if __name__ == "__main__":
756
  logger.error(f"Application failed to start: {e}")
757
  raise
758
  finally:
759
- if torch.distributed.is_initialized():
760
- torch.distributed.destroy_process_group()
 
301
  logger.info("AI Agent Ready")
302
  return agent
303
 
304
+ def cleanup_resources(self):
305
+ """Clean up GPU memory and collect garbage"""
306
+ torch.cuda.empty_cache()
307
+ gc.collect()
308
+ if torch.distributed.is_initialized():
309
+ torch.distributed.destroy_process_group()
310
+
311
  def process_response_stream(self, prompt: str, history: List[dict]) -> Generator[dict, None, None]:
312
  """Stream the agent's response with proper formatting"""
313
  full_response = ""
 
405
  yield (chatbot_output, download_output, final_summary, progress_text)
406
 
407
  combined_response += f"--- Analysis for Chunk {chunk_idx} ---\n{chunk_response}\n"
408
+ self.cleanup_resources()
 
409
 
410
  # Generate final outputs
411
  final_summary = self.text_processor.summarize_results(combined_response)
 
429
  progress_text = {"visible": False}
430
  yield (chatbot_output, download_output, final_summary, progress_text)
431
  finally:
432
+ self.cleanup_resources()
 
433
 
434
  def _update_progress(self, current: int, total: int, stage: str = "") -> Dict[str, Any]:
435
  """Format progress update for UI"""
436
  progress = f"{stage} - {current}/{total}" if stage else f"{current}/{total}"
437
  return {"value": progress, "visible": True}
438
 
439
+ def toggle_theme(self, theme_state: str) -> tuple[str, str]:
440
+ """Toggle between light and dark themes"""
441
+ new_theme = "dark" if theme_state == "light" else "light"
442
+ button_text = "β˜€οΈ Light Mode" if new_theme == "dark" else "πŸŒ™ Dark Mode"
443
+ return new_theme, button_text
444
+
445
+ def toggle_sidebar(self, sidebar_state: bool) -> bool:
446
+ """Toggle sidebar visibility"""
447
+ return not sidebar_state
448
+
449
  def create_interface(self):
450
  """Create Gradio interface with refined ChatGPT-like design"""
451
  css = """
 
457
  .gradio-container {
458
  max-width: 800px;
459
  margin: 0 auto;
460
+ padding: 24px;
461
  }
462
  .chat-container {
463
  background: var(--chat-bg);
 
465
  padding: 24px;
466
  height: 80vh;
467
  overflow-y: auto;
468
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
469
  position: relative;
470
  }
471
  .message {
 
474
  border-radius: 12px;
475
  max-width: 80%;
476
  transition: all 0.3s ease;
477
+ background: var(--message-bg);
478
  position: relative;
479
  }
480
  .message:hover {
481
  transform: translateY(-2px);
482
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
483
  }
484
  .message.user {
485
+ background: linear-gradient(135deg, #007bff, #0056b3);
486
  color: white;
487
  margin-left: auto;
488
  }
 
499
  .input-container {
500
  display: flex;
501
  align-items: center;
502
+ margin-top: 24px;
503
  background: var(--chat-bg);
504
  padding: 12px 24px;
505
  border-radius: 30px;
506
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
507
  position: sticky;
508
  bottom: 0;
509
  }
 
514
  color: var(--text-color);
515
  outline: none;
516
  font-size: 1em;
517
+ placeholder-opacity: 0.6;
518
  }
519
  .send-btn {
520
+ background: linear-gradient(135deg, #007bff, #0056b3);
521
  color: white;
522
  border: none;
523
  border-radius: 20px;
524
  padding: 10px 20px;
525
  margin-left: 12px;
526
+ transition: transform 0.2s ease;
527
  }
528
  .send-btn:hover {
529
+ transform: scale(1.05);
530
+ }
531
+ .send-btn:active {
532
+ animation: pulse 0.3s ease;
533
  }
534
  .sidebar {
535
  background: var(--sidebar-bg);
536
  padding: 24px;
537
  border-radius: 16px;
538
+ margin-top: 24px;
539
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
540
+ transition: transform 0.4s ease;
541
  transform: translateX(0);
542
+ position: fixed;
 
 
 
543
  right: 0;
544
  top: 100px;
545
  width: 300px;
546
+ z-index: 1000;
547
+ }
548
+ .sidebar-hidden {
549
+ transform: translateX(100%);
550
+ }
551
+ .sidebar-backdrop {
552
+ position: fixed;
553
+ top: 0;
554
+ left: 0;
555
+ width: 100%;
556
+ height: 100%;
557
+ background: rgba(0,0,0,0.3);
558
+ z-index: 999;
559
+ display: none;
560
+ }
561
+ .sidebar:not(.sidebar-hidden) ~ .sidebar-backdrop {
562
+ display: block;
563
  }
564
  .header {
565
  text-align: center;
 
567
  }
568
  .theme-toggle {
569
  position: absolute;
570
+ top: 24px;
571
+ right: 24px;
572
+ background: linear-gradient(135deg, #007bff, #0056b3);
573
  color: white;
574
  border: none;
575
  border-radius: 20px;
 
584
  left: 50%;
585
  transform: translateX(-50%);
586
  font-size: 1.2em;
587
+ animation: pulse 1.5s ease infinite;
588
  }
589
+ .typing-indicator {
590
+ display: none;
591
+ font-size: 0.9em;
592
+ color: var(--text-color);
593
+ opacity: 0.7;
594
+ margin: 12px;
595
+ }
596
+ .typing-indicator.active {
597
+ display: block;
598
+ animation: blink 1s step-end infinite;
599
+ }
600
+ @keyframes pulse {
601
+ 0%, 100% { transform: translateX(-50%) scale(1); opacity: 1; }
602
+ 50% { transform: translateX(-50%) scale(1.2); opacity: 0.7; }
603
+ }
604
+ @keyframes blink {
605
+ 50% { opacity: 0.3; }
606
  }
607
  :root {
608
  --background: #ffffff;
 
610
  --chat-bg: #f9fafb;
611
  --message-bg: #e5e5ea;
612
  --sidebar-bg: #f1f3f5;
613
+ transition: background 0.3s ease, color 0.3s ease;
614
  }
615
+ [data-theme="dark"] {
616
+ --background: #1e2a44;
617
+ --text-color: #ffffff;
618
+ --chat-bg: #2d3b55;
619
+ --message-bg: #3e4c6a;
620
+ --sidebar-bg: #2a3650;
 
 
621
  }
622
  @media (max-width: 600px) {
623
  .gradio-container {
 
637
  }
638
  .sidebar {
639
  width: 100%;
640
+ top: 80px;
641
  }
642
  .sidebar-hidden {
643
  transform: translateX(100%);
 
646
  """
647
 
648
  js = """
649
+ function applyTheme(theme) {
650
+ document.documentElement.setAttribute('data-theme', theme);
651
+ localStorage.setItem('theme', theme);
652
+ document.querySelector('.theme-toggle').innerHTML = theme === 'dark' ? 'β˜€οΈ Light Mode' : 'πŸŒ™ Dark Mode';
 
 
 
 
 
 
653
  }
654
 
655
  function toggleSidebar() {
 
658
  }
659
 
660
  document.addEventListener('DOMContentLoaded', () => {
661
+ const savedTheme = localStorage.getItem('theme') || 'light';
662
+ applyTheme(savedTheme);
663
  document.querySelector('.sidebar').classList.add('sidebar-hidden');
 
664
  });
665
  """
666
 
667
  with gr.Blocks(theme=gr.themes.Default(), css=css, js=js, title="Clinical Oversight Assistant") as app:
668
+ theme_state = gr.State(value="light")
669
+ sidebar_state = gr.State(value=False)
670
+
671
  gr.HTML("""
672
  <div class='header'>
673
  <h1 style='color: var(--text-color);'>🩺 Clinical Oversight Assistant</h1>
 
675
  AI-powered analysis of patient records for missed diagnoses
676
  </p>
677
  </div>
678
+ <div class='sidebar-backdrop'></div>
679
  """)
680
+
681
+ theme_button = gr.Button("πŸŒ™ Dark Mode", elem_classes="theme-toggle")
 
682
 
683
  with gr.Column(elem_classes="chat-container"):
684
  chatbot = gr.Chatbot(
 
689
  elem_classes="chatbot",
690
  render_markdown=True
691
  )
692
+ gr.HTML("<div class='loading-spinner'>⏳</div>")
693
+ gr.HTML("<div class='typing-indicator'>Typing...</div>")
694
 
695
  with gr.Row():
696
+ tools_button = gr.Button("πŸ“‚ Tools", variant="secondary")
 
 
697
 
698
  with gr.Column(elem_classes="sidebar"):
699
+ gr.Markdown("### πŸ“Ž Upload Records", elem_classes="tooltip", elem_id="upload-tooltip")
700
  file_upload = gr.File(
701
  file_types=[".pdf", ".csv", ".xls", ".xlsx"],
702
  file_count="multiple",
703
+ label="Patient Records",
704
+ elem_classes="tooltip",
705
+ elem_id="upload-input"
706
  )
707
+ gr.Markdown("### πŸ“ Analysis Summary", elem_classes="tooltip", elem_id="summary-tooltip")
708
  final_summary = gr.Markdown(
709
+ "Analysis results will appear here...",
710
+ elem_classes="tooltip",
711
+ elem_id="summary-output"
712
  )
713
+ gr.Markdown("### πŸ“„ Full Report", elem_classes="tooltip", elem_id="report-tooltip")
714
  download_output = gr.File(
715
  label="Download Report",
716
  visible=False,
717
+ interactive=False,
718
+ elem_classes="tooltip",
719
+ elem_id="download-output"
720
  )
721
 
722
  with gr.Row(elem_classes="input-container"):
 
736
  progress_text = gr.Textbox(
737
  label="Progress Status",
738
  visible=False,
739
+ interactive=False,
740
+ elem_classes="progress-text"
741
  )
742
 
743
+ def show_loading(state: bool) -> dict:
744
+ return {
745
+ "value": "<div class='loading-spinner'>⏳</div>" if state else "<div class='loading-spinner' style='display: none;'>⏳</div>",
746
+ "visible": state
747
+ }
748
+
749
+ def show_typing(state: bool) -> dict:
750
+ return {
751
+ "value": f"<div class='typing-indicator{' active' if state else ''}'>Typing...</div>",
752
+ "visible": state
753
+ }
754
+
755
+ # Theme toggle handler
756
+ theme_button.click(
757
+ self.toggle_theme,
758
+ inputs=[theme_state],
759
+ outputs=[theme_state, theme_button],
760
+ _js="theme => applyTheme(theme)"
761
+ )
762
 
763
+ # Sidebar toggle handler
764
+ tools_button.click(
765
+ self.toggle_sidebar,
766
+ inputs=[sidebar_state],
767
+ outputs=[sidebar_state],
768
+ _js="() => toggleSidebar()"
769
+ )
770
 
771
+ # Analysis handlers
772
  send_btn.click(
773
+ show_loading,
774
+ inputs=[gr.State(value=True)],
775
+ outputs=[chatbot]
776
+ ).then(
777
+ show_typing,
778
+ inputs=[gr.State(value=True)],
779
+ outputs=[chatbot]
780
  ).then(
781
  self.analyze,
782
  inputs=[msg_input, chatbot, file_upload],
783
  outputs=[chatbot, download_output, final_summary, progress_text],
784
  show_progress="hidden"
785
  ).then(
786
+ show_loading,
787
+ inputs=[gr.State(value=False)],
788
+ outputs=[chatbot]
789
+ ).then(
790
+ show_typing,
791
+ inputs=[gr.State(value=False)],
792
+ outputs=[chatbot]
793
  )
794
 
795
  msg_input.submit(
796
+ show_loading,
797
+ inputs=[gr.State(value=True)],
798
+ outputs=[chatbot]
799
+ ).then(
800
+ show_typing,
801
+ inputs=[gr.State(value=True)],
802
+ outputs=[chatbot]
803
  ).then(
804
  self.analyze,
805
  inputs=[msg_input, chatbot, file_upload],
806
  outputs=[chatbot, download_output, final_summary, progress_text],
807
  show_progress="hidden"
808
  ).then(
809
+ show_loading,
810
+ inputs=[gr.State(value=False)],
811
+ outputs=[chatbot]
812
+ ).then(
813
+ show_typing,
814
+ inputs=[gr.State(value=False)],
815
+ outputs=[chatbot]
816
  )
817
 
818
  app.load(
819
  lambda: [
820
+ [], None, "", "", None, {"visible": False}, "light", False, "πŸŒ™ Dark Mode"
821
  ],
822
+ outputs=[chatbot, download_output, final_summary, msg_input, file_upload, progress_text, theme_state, sidebar_state, theme_button],
823
  queue=False
824
  )
825
 
 
827
 
828
  # ==================== APPLICATION ENTRY POINT ====================
829
  if __name__ == "__main__":
830
+ app = None
831
  try:
832
  logger.info("Starting Clinical Oversight Assistant...")
833
  app = ClinicalOversightApp()
 
847
  logger.error(f"Application failed to start: {e}")
848
  raise
849
  finally:
850
+ if app:
851
+ app.cleanup_resources()