liammatt5/GLAM_Web_App
0
1import os2import time3import tempfile4import traceback5import numpy as np6import soundfile as sf7import gradio as gr8 9# --- Pipeline & Model Imports ---10# (Kept intact to connect with your backend framework logic)11from interface import (12 load_patient_registry, save_patient_registry, separate_audio, monitoring_table_rows, _copy_audio_to_storage,13 run_end_to_end, search_history_records, search_reasoning_records, _initialize_models_for_live_processing, RESULTS_DIR,14 process_audio_chunk_for_separation, infer_on_separated_chunk, _live_sr,15 LIVE_PROCESSING_WINDOW_SECONDS, LIVE_OVERLAP_SECONDS, resolve_patient_names, save_audio_file, predict_sources16)17from gnn import EnhancedPatientStateManager, ClinicalAlertSystem 18from notifications import build_live_notification_html19 20 21# ==========================================22# 1. STYLING CONFIGURATION23# ==========================================24def load_css():25 css_path = os.path.join(os.path.dirname(__file__), "style.css")26 if os.path.exists(css_path):27 with open(css_path, "r") as f:28 return f.read()29 return ""30 31css_styles = load_css()32 33 34# ==========================================35# 2. CORE BUSINESS & UTILITY LOGIC36# ==========================================37def register_patients(ref_audio_1, ref_audio_2, ref_audio_3, patient_name_1, patient_name_2, patient_name_3):38 raw_names = [patient_name_1, patient_name_2, patient_name_3]39 audio_paths = [ref_audio_1, ref_audio_2, ref_audio_3]40 patient_entries = []41 for idx, (name, audio_path) in enumerate(zip(raw_names, audio_paths), start=1):42 entry = {43 "patient_id": f"patient_{idx}",44 "name": name.strip() if name else f"Patient {idx}",45 "reference_audio": str(audio_path) if audio_path else None,46 }47 patient_entries.append(entry)48 49 save_result, error_message = save_patient_registry(patient_entries)50 51 if error_message:52 message = error_message53 choices = get_registered_patient_choices()54 selected_values = [choices[i + 1] if i + 1 < len(choices) else "Unassigned" for i in range(6)]55 return (56 message,57 gr.update(choices=choices, value=selected_values[0]),58 gr.update(choices=choices, value=selected_values[1]),59 gr.update(choices=choices, value=selected_values[2]),60 gr.update(choices=choices, value=selected_values[0]),61 gr.update(choices=choices, value=selected_values[1]),62 gr.update(choices=choices, value=selected_values[2]),63 )64 65 registered = [f"Patient {idx}: {entry['name']}" for idx, entry in enumerate(patient_entries, start=1)]66 message = (67 "Patients registered successfully. Reference audio files are saved for each patient.\n"68 + "\n".join(registered)69 + "\nSaved to pipeline_results/patient_registry.json"70 )71 choices = get_registered_patient_choices()72 selected_values = [choices[i + 1] if i + 1 < len(choices) else "Unassigned" for i in range(6)]73 return (74 message,75 gr.update(choices=choices, value=selected_values[0]),76 gr.update(choices=choices, value=selected_values[1]),77 gr.update(choices=choices, value=selected_values[2]),78 gr.update(choices=choices, value=selected_values[0]),79 gr.update(choices=choices, value=selected_values[1]),80 gr.update(choices=choices, value=selected_values[2]),81 )82 83 84def get_registered_patient_choices(default_count=3):85 registry = load_patient_registry()86 names = [entry.get("name") or f"Patient {idx + 1}" for idx, entry in enumerate(registry)]87 names = list(dict.fromkeys(names))88 if not names:89 names = [f"Patient {i}" for i in range(1, default_count + 1)]90 return ["Unassigned"] + names91 92 93def filter_patient_choices(query, default_count=3):94 if query is None:95 return gr.update(choices=[], value=None)96 97 query_text = str(query).strip()98 if not query_text:99 return gr.update(choices=[], value=None)100 101 choices = get_registered_patient_choices(default_count)102 query_lower = query_text.lower()103 filtered = [name for name in choices if name != "Unassigned" and name.lower().startswith(query_lower)]104 return gr.update(choices=filtered)105 106 107def get_selected_patient_name(value, default_count=3):108 if not value:109 return None110 if isinstance(value, list):111 return value[0] if value else None112 return value113 114 115def normalize_live_patient_names(selected_names):116 normalized = []117 for name in selected_names:118 if name and name != "Unassigned":119 normalized.append(name)120 else:121 normalized.append(None)122 return normalized123 124 125def round_monitor_value(value):126 if value is None:127 return None128 if isinstance(value, (int, float)):129 return round(value, 2)130 try:131 return round(float(value), 2)132 except (ValueError, TypeError):133 return value134 135 136def update_separation_output_labels(p1, p2, p3):137 selected = [get_selected_patient_name(p1), get_selected_patient_name(p2), get_selected_patient_name(p3)]138 139 audio_updates = []140 wave_updates = []141 142 for i, name in enumerate(selected, start=1):143 if not name or name == "Unassigned":144 display = f"Patient {i}"145 else:146 display = name147 148 audio_updates.append(gr.update(label=f"{display} Audio"))149 wave_updates.append(gr.update(label=f"{display} Waveform"))150 151 return (152 audio_updates[0],153 audio_updates[1],154 audio_updates[2],155 wave_updates[0],156 wave_updates[1],157 wave_updates[2],158 )159 160 161def update_live_monitor_output_labels(p1, p2, p3):162 selected = [get_selected_patient_name(p1), get_selected_patient_name(p2), get_selected_patient_name(p3)]163 164 updates = []165 for i, name in enumerate(selected, start=1):166 if not name or name == "Unassigned":167 display = f"Live Patient {i}"168 else:169 display = name170 updates.append(gr.update(label=display))171 172 return updates[0], updates[1], updates[2]173 174 175def predict(mix_audio, p1, p2, p3): # p1, p2, p3 are patient names176 selected_names = [get_selected_patient_name(n) for n in [p1, p2, p3]]177 selected_names = [n if n and n != "Unassigned" else None for n in selected_names]178 179 registry = load_patient_registry()180 reference_audio_paths = [None, None, None]181 for i, name in enumerate(selected_names):182 if name:183 for entry in registry:184 if entry.get("name") == name:185 reference_audio_paths[i] = entry.get("local_reference_audio") or entry.get("reference_audio")186 break187 try:188 outputs, reasoning_summaries, history_record = run_end_to_end(189 mix_audio, 190 patient_names=selected_names,191 reference_audio_paths=reference_audio_paths192 )193 except Exception as exc:194 error_message = f"Pipeline error: {exc}"195 error_trace = traceback.format_exc()196 empty = [None, None, None, None, None, None]197 return empty + [error_message, [[]], f"Error at {time.strftime('%H:%M:%S')}", error_trace]198 199 monitor_rows = monitoring_table_rows()200 history_status = f"Completed at {history_record.get('timestamp', 'unknown')}. {len(reasoning_summaries)} patient(s) processed."201 message = f"Full pipeline complete. {len(reasoning_summaries)} reasoning summaries available."202 return outputs + [message] + [monitor_rows] + [history_status]203 204 205def refresh_monitoring():206 rows = monitoring_table_rows()207 if not rows:208 return [], "No reasoning summary available. Run the pipeline and make sure pipeline_results/reasoning_summary.json exists."209 return rows, f"Loaded {len(rows)} patient states from reasoning summary."210 211 212def search_history(query):213 if not query:214 return [], "Enter a patient name or ID to search history."215 rows = search_reasoning_records(query)216 if not rows:217 return [], f"No clinical findings found matching '{query}'."218 return rows, f"Found {len(rows)} matching clinical record(s)."219 220 221# ==========================================222# 3. LIVE STREAMING CONTROLLERS223# ==========================================224def start_live_monitoring_session(selected_patient_1, selected_patient_2, selected_patient_3):225 global _live_processor, _live_wav2vec_model, _live_gnn_model, _live_device226 _live_processor, _live_wav2vec_model, _live_gnn_model, _live_device = _initialize_models_for_live_processing()227 selected_names = normalize_live_patient_names([selected_patient_1, selected_patient_2, selected_patient_3])228 managers = [229 EnhancedPatientStateManager(),230 EnhancedPatientStateManager(),231 EnhancedPatientStateManager(),232 ]233 empty_audio = np.array([], dtype=np.float32)234 empty_buffers = [np.array([], dtype=np.float32) for _ in range(3)]235 empty_table = []236 status_names = [name for name in selected_names if name is not None]237 if status_names:238 status = f"Live monitoring initialized for: {', '.join(status_names)}. Click microphone to start."239 else:240 status = "No patients selected. Select at least one patient to monitor."241 return (242 empty_audio,243 empty_buffers,244 managers,245 [0.0, 0.0, 0.0],246 empty_table,247 selected_names,248 status,249 gr.update(value=None, interactive=True),250 gr.update(interactive=False),251 build_live_notification_html(empty_table, selected_names),252 )253 254 255def process_live_audio_stream(audio_chunk, live_audio_buffer, live_separated_buffers, live_patient_managers, live_current_timestamps, live_patient_names):256 if audio_chunk is None:257 return (258 live_audio_buffer,259 live_separated_buffers,260 live_patient_managers,261 live_current_timestamps,262 [],263 "No audio received.",264 None,265 None,266 None,267 live_patient_names or [None, None, None],268 build_live_notification_html([], live_patient_names),269 )270 271 import librosa272 import torch273 sr, np_audio = audio_chunk274 if sr is None or np_audio is None:275 return (276 live_audio_buffer,277 live_separated_buffers,278 live_patient_managers,279 live_current_timestamps,280 [],281 "Invalid audio chunk received.",282 None,283 None,284 None,285 live_patient_names or [None, None, None],286 build_live_notification_html([], live_patient_names),287 )288 289 mono = np.mean(np_audio, axis=-1) if np_audio.ndim > 1 else np_audio290 target_sr = _live_sr291 if sr != target_sr:292 mono = librosa.resample(mono.astype(np.float32), orig_sr=sr, target_sr=target_sr)293 mono = mono.astype(np.float32)294 295 current_audio_base = live_audio_buffer if live_audio_buffer is not None else np.array([], dtype=np.float32)296 current_audio = np.concatenate([current_audio_base, mono]) if current_audio_base.size > 0 else mono297 max_buffer = int(LIVE_PROCESSING_WINDOW_SECONDS * target_sr)298 if current_audio.size > max_buffer:299 current_audio = current_audio[-max_buffer:]300 301 window_samples = int(LIVE_PROCESSING_WINDOW_SECONDS * target_sr)302 overlap_samples = int(LIVE_OVERLAP_SECONDS * target_sr)303 step = window_samples - overlap_samples304 305 new_buffers = [buf.copy() for buf in live_separated_buffers] if live_separated_buffers else [np.array([], dtype=np.float32) for _ in range(3)]306 new_timestamps = list(live_current_timestamps)307 audios_out = [None, None, None]308 309 active_slots = [i for i, name in enumerate(live_patient_names) if name is not None] if live_patient_names else []310 if current_audio.size >= window_samples and active_slots:311 chunk = current_audio[-window_samples:]312 prediction, _ = predict_sources(torch.from_numpy(chunk).unsqueeze(0), _live_sr)313 separated = prediction.cpu().numpy()314 for i in range(min(3, separated.shape[0])):315 selected_name = live_patient_names[i] if i < len(live_patient_names) else None316 if selected_name is None:317 new_buffers[i] = np.array([], dtype=np.float32)318 audios_out[i] = None319 continue320 separated_i = separated[i].astype(np.float32)321 hop = step if step > 0 else window_samples322 if separated_i.size > hop:323 new_buffers[i] = separated_i[-hop:]324 else:325 new_buffers[i] = separated_i326 new_timestamps[i] = new_timestamps[i] + hop / target_sr if new_timestamps[i] > 0 else hop / target_sr327 328 # Performance Note: Disk I/O (sf.write/_copy_audio_to_storage) removed to reduce live latency329 try:330 audios_out[i] = (target_sr, separated_i)331 except Exception:332 audios_out[i] = None333 334 rows = []335 for i, manager in enumerate(live_patient_managers):336 selected_name = live_patient_names[i] if i < len(live_patient_names) else None337 if selected_name is None or manager is None:338 continue339 separated_chunk = new_buffers[i]340 timestamp = new_timestamps[i] if new_timestamps[i] > 0 else 0.0341 if separated_chunk.size >= target_sr:342 state = infer_on_separated_chunk(343 separated_chunk,344 _live_gnn_model,345 _live_processor,346 _live_wav2vec_model,347 _live_device,348 manager,349 f"live_patient_{i+1}",350 timestamp,351 )352 rows.append([353 selected_name,354 round_monitor_value(manager.patient_data.get(f"live_patient_{i+1}", {}).get("wheeze_ema")),355 round_monitor_value(manager.patient_data.get(f"live_patient_{i+1}", {}).get("crackle_ema")),356 round_monitor_value(state.get("breathing_rate_mean")),357 state.get("comment", ""),358 ])359 360 return current_audio, new_buffers, live_patient_managers, new_timestamps, rows, "Processing live audio...", audios_out[0], audios_out[1], audios_out[2], live_patient_names, build_live_notification_html(rows, live_patient_names)361 362 363def stop_live_monitoring_session():364 empty_audio = np.array([], dtype=np.float32)365 empty_buffers = [np.array([], dtype=np.float32) for _ in range(3)]366 cleared_managers = [None, None, None]367 cleared_timestamps = [0.0, 0.0, 0.0]368 return empty_audio, empty_buffers, cleared_managers, cleared_timestamps, [], "Live monitoring stopped.", gr.update(value=None, interactive=False), gr.update(interactive=True), None, None, None, build_live_notification_html([], [None, None, None])369 370 371# ==========================================372# 4. INTERFACE BUILDING METHOD373# ==========================================374def create_ui():375 # Pass structural embedded CSS string variable safely inside Blocks376 with gr.Blocks() as demo:377 gr.HTML("<div class='header-box'><h1>Patient Monitoring System</h1></div>")378 379 with gr.Row():380 # Sidebar Menu381 with gr.Column(scale=1, variant="panel"):382 gr.Markdown("### Navigation")383 btn_register = gr.Button("Register Patients", variant="secondary", elem_classes="sidebar-btn")384 btn_separation = gr.Button("Audio Separation", variant="secondary", elem_classes="sidebar-btn")385 btn_live_mon = gr.Button("Live Monitoring", variant="secondary", elem_classes="sidebar-btn") 386 btn_history = gr.Button("View History", variant="secondary", elem_classes="sidebar-btn")387 monitor_alerts_sidebar = gr.HTML(value=build_live_notification_html([], [None, None, None]))388 389 # Content Area390 with gr.Column(scale=4):391 live_audio_buffer_state = gr.State(value=None)392 live_separated_buffers_state = gr.State(value=[])393 live_patient_managers_state = gr.State(value=[None, None, None])394 live_current_timestamps_state = gr.State(value=[0.0, 0.0, 0.0])395 live_patient_names_state = gr.State(value=[None, None, None])396 397 # Registration Page398 with gr.Column(visible=True) as reg_page:399 gr.Markdown("### Patient Registration")400 with gr.Row():401 with gr.Column(variant="panel"):402 gr.Markdown("#### Patient 1")403 patient_name_1 = gr.Textbox(label="Name", placeholder="Enter name", elem_classes="vibrant-status")404 ref_audio_1 = gr.Audio(label="Ref Audio", type="filepath")405 with gr.Column(variant="panel"):406 gr.Markdown("#### Patient 2")407 patient_name_2 = gr.Textbox(label="Name", placeholder="Enter name", elem_classes="vibrant-status")408 ref_audio_2 = gr.Audio(label="Ref Audio", type="filepath")409 with gr.Column(variant="panel"):410 gr.Markdown("#### Patient 3")411 patient_name_3 = gr.Textbox(label="Name", placeholder="Enter name", elem_classes="vibrant-status")412 ref_audio_3 = gr.Audio(label="Ref Audio", type="filepath")413 414 register_btn = gr.Button("Submit Registration", variant="primary", size="lg")415 register_status = gr.Textbox(label="Status", interactive=False, elem_classes="vibrant-status")416 417 # Separation Page418 with gr.Column(visible=False) as sep_page:419 gr.Markdown("### Source Separation & Inference")420 with gr.Row():421 with gr.Column(scale=2, variant="panel"):422 mix_audio = gr.Audio(label="Upload Mixture (Multiple Patients)", type="filepath")423 gr.Markdown("#### Patient Assignment")424 with gr.Row():425 sep_p1 = gr.Dropdown(426 label="Source 1",427 choices=[],428 value=None,429 interactive=True,430 allow_custom_value=True,431 )432 sep_p2 = gr.Dropdown(433 label="Source 2",434 choices=[],435 value=None,436 interactive=True,437 allow_custom_value=True,438 )439 sep_p3 = gr.Dropdown(440 label="Source 3",441 choices=[],442 value=None,443 interactive=True,444 allow_custom_value=True,445 )446 submit_btn = gr.Button("Run Separation Pipeline", variant="primary")447 with gr.Column(scale=1):448 status_text = gr.Textbox(label="Process Status", interactive=False, elem_classes="vibrant-status")449 history_status_text = gr.Textbox(label="History Logging", interactive=False, elem_classes="vibrant-status")450 451 gr.Markdown("#### Separated Patient Data")452 with gr.Row():453 with gr.Column(variant="panel"):454 out_audio_1 = gr.Audio(label="Patient 1 Audio", type="filepath")455 out_wave_1 = gr.Image(label="Waveform 1", type="filepath")456 with gr.Column(variant="panel"):457 out_audio_2 = gr.Audio(label="Patient 2 Audio", type="filepath")458 out_wave_2 = gr.Image(label="Waveform 2", type="filepath")459 with gr.Column(variant="panel"):460 out_audio_3 = gr.Audio(label="Patient 3 Audio", type="filepath")461 out_wave_3 = gr.Image(label="Waveform 3", type="filepath")462 463 gr.Markdown("#### Immediate Findings")464 monitor_table_small = gr.Dataframe(465 headers=["Patient Name", "mean_wheeze_prob", "mean_crackle_prob", "breathing_rate_mean", "comment"],466 datatype=["str", "number", "number", "number", "str"],467 interactive=False,468 )469 470 # History Page471 with gr.Column(visible=False) as history_page:472 gr.Markdown('<div class="page-title">Patient History Search</div>', elem_classes="page-container")473 with gr.Row():474 search_query = gr.Textbox(label="Search by Patient Name or Audio ID", placeholder="Enter name...", elem_classes="vibrant-status")475 search_button = gr.Button("Search History", variant="primary")476 477 history_results = gr.Dataframe(478 headers=["Patient Names", "Overall", "mean_wheeze_prob", "mean_crackle_prob", "breathing_rate_mean", "comment"],479 datatype=["str", "str", "str", "number"],480 interactive=False,481 )482 history_msg = gr.Textbox(label="Search Results", interactive=False, elem_classes="vibrant-status")483 484 # Live Monitoring Page485 with gr.Column(visible=False) as live_mon_page:486 gr.Markdown("## Live Audio Monitoring")487 488 # Start/Stop buttons at the very top alone489 with gr.Row():490 start_live_btn = gr.Button("Start Live Monitoring", variant="primary")491 stop_live_btn = gr.Button("Stop Live Monitoring", variant="stop")492 493 # Monitor Slots row - Stretching along one line494 with gr.Row():495 select_patient_1 = gr.Dropdown(496 label="Monitor Slot 1",497 choices=[],498 value=None,499 interactive=True,500 allow_custom_value=True,501 )502 select_patient_2 = gr.Dropdown(503 label="Monitor Slot 2",504 choices=[],505 value=None,506 interactive=True,507 allow_custom_value=True,508 )509 select_patient_3 = gr.Dropdown(510 label="Monitor Slot 3",511 choices=[],512 value=None,513 interactive=True,514 allow_custom_value=True,515 )516 517 with gr.Row():518 with gr.Column(scale=1):519 live_mic_input = gr.Audio(520 sources=["microphone"],521 streaming=True,522 label="Live Microphone Input",523 type="numpy",524 interactive=True,525 )526 with gr.Column(scale=2):527 gr.Markdown("#### Live Separated Sources")528 with gr.Row():529 live_out_audio_1 = gr.Audio(label="Live Patient 1", interactive=False, type="numpy")530 live_out_audio_2 = gr.Audio(label="Live Patient 2", interactive=False, type="numpy")531 live_out_audio_3 = gr.Audio(label="Live Patient 3", interactive=False, type="numpy")532 533 gr.Markdown("#### Live Findings")534 live_monitor_table = gr.Dataframe(535 headers=["Patient Name", "mean_wheeze_prob", "mean_crackle_prob", "breathing_rate_mean", "comment"],536 datatype=["str", "number", "number", "number", "str"],537 interactive=False,538 )539 live_monitor_status = gr.Textbox(label="Live Status", interactive=False, elem_classes="vibrant-status")540 541 # --- Tab Routing Mechanics ---542 def nav_reg():543 return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(variant="primary"), gr.update(variant="secondary"), gr.update(variant="secondary"), gr.update(variant="secondary")544 545 def nav_sep():546 return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(variant="secondary"), gr.update(variant="primary"), gr.update(variant="secondary"), gr.update(variant="secondary"), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None)547 548 def nav_his():549 return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(variant="secondary"), gr.update(variant="secondary"), gr.update(variant="secondary"), gr.update(variant="primary")550 551 def nav_live():552 return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), gr.update(variant="secondary"), gr.update(variant="secondary"), gr.update(variant="primary"), gr.update(variant="secondary"), gr.update(choices=[], value=None), gr.update(choices=[], value=None), gr.update(choices=[], value=None)553 554 btn_register.click(555 nav_reg,556 outputs=[reg_page, sep_page, history_page, live_mon_page, btn_register, btn_separation, btn_history, btn_live_mon],557 queue=False,558 )559 btn_separation.click(560 nav_sep,561 outputs=[reg_page, sep_page, history_page, live_mon_page, btn_register, btn_separation, btn_history, btn_live_mon, sep_p1, sep_p2, sep_p3],562 queue=False,563 )564 btn_history.click(565 nav_his,566 outputs=[reg_page, sep_page, history_page, live_mon_page, btn_register, btn_separation, btn_history, btn_live_mon],567 queue=False,568 )569 btn_live_mon.click(570 nav_live,571 outputs=[reg_page, sep_page, history_page, live_mon_page, btn_register, btn_separation, btn_history, btn_live_mon, select_patient_1, select_patient_2, select_patient_3],572 queue=False,573 )574 575 # --- Interactive Trigger Bindings ---576 register_btn.click(577 fn=register_patients,578 inputs=[ref_audio_1, ref_audio_2, ref_audio_3, patient_name_1, patient_name_2, patient_name_3],579 outputs=[register_status, select_patient_1, select_patient_2, select_patient_3, sep_p1, sep_p2, sep_p3],580 )581 582 submit_btn.click(583 fn=predict,584 inputs=[mix_audio, sep_p1, sep_p2, sep_p3],585 outputs=[out_audio_1, out_audio_2, out_audio_3, out_wave_1, out_wave_2, out_wave_3, status_text, monitor_table_small, history_status_text],586 )587 588 589 for dropdown in [sep_p1, sep_p2, sep_p3]:590 dropdown.change(591 fn=update_separation_output_labels,592 inputs=[sep_p1, sep_p2, sep_p3],593 outputs=[out_audio_1, out_audio_2, out_audio_3, out_wave_1, out_wave_2, out_wave_3,],594 queue=False,595 )596 597 for dropdown in [select_patient_1, select_patient_2, select_patient_3]:598 dropdown.change(599 fn=update_live_monitor_output_labels,600 inputs=[select_patient_1, select_patient_2, select_patient_3],601 outputs=[live_out_audio_1, live_out_audio_2, live_out_audio_3],602 queue=False,603 )604 605 for dropdown in [sep_p1, sep_p2, sep_p3, select_patient_1, select_patient_2, select_patient_3]:606 dropdown.input(607 fn=filter_patient_choices,608 inputs=[dropdown],609 outputs=[dropdown],610 queue=False,611 )612 613 def clear_choices_after_select(dropdown):614 return gr.update(choices=[])615 616 for dropdown in [sep_p1, sep_p2, sep_p3, select_patient_1, select_patient_2, select_patient_3]:617 dropdown.select(618 fn=clear_choices_after_select,619 inputs=[dropdown],620 outputs=[dropdown],621 queue=False,622 )623 624 search_button.click(625 fn=search_history,626 inputs=[search_query],627 outputs=[history_results, history_msg],628 )629 630 start_live_btn.click(631 fn=start_live_monitoring_session,632 inputs=[select_patient_1, select_patient_2, select_patient_3],633 outputs=[634 live_audio_buffer_state,635 live_separated_buffers_state,636 live_patient_managers_state,637 live_current_timestamps_state,638 live_monitor_table,639 live_patient_names_state,640 live_monitor_status,641 live_mic_input, 642 start_live_btn,643 monitor_alerts_sidebar,644 ],645queue=False,646 )647 648 live_mic_input.stream(649 fn=process_live_audio_stream,650 inputs=[651 live_mic_input,652 live_audio_buffer_state,653 live_separated_buffers_state,654 live_patient_managers_state,655 live_current_timestamps_state,656 live_patient_names_state,657 ],658 outputs=[live_audio_buffer_state, live_separated_buffers_state, live_patient_managers_state, live_current_timestamps_state, live_monitor_table, live_monitor_status, live_out_audio_1, live_out_audio_2, live_out_audio_3, live_patient_names_state, monitor_alerts_sidebar],659 concurrency_limit=5,660 )661 662 stop_live_btn.click(663 fn=stop_live_monitoring_session,664 inputs=[],665 outputs=[live_audio_buffer_state, live_separated_buffers_state, live_patient_managers_state, live_current_timestamps_state, live_monitor_table, live_monitor_status, live_mic_input, start_live_btn, live_out_audio_1, live_out_audio_2, live_out_audio_3, monitor_alerts_sidebar],666 queue=False,667 )668 669 return demo670 671 672app = create_ui()673 674if __name__ == "__main__":675 host = "0.0.0.0"676 677 app.launch(678 share=False,679 server_name=host,680 server_port=int(os.environ.get("PORT", 7860)),681 theme=gr.themes.Soft(),682 css=css_styles,683 allowed_paths=[str(RESULTS_DIR.resolve())],684 )