CoolFace
Apppublic

liammatt5/GLAM_Web_App

sourceHugging Facemitupdated 3mo agoView on Hugging Face
0likes
ui.py684 linesDownload Raw Back to root
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    )