Spaces:
Running on Zero
Running on Zero
| """AI medical scribe: audio -> MedASR transcript -> MedGemma SOAP note.""" | |
| import os | |
| import gradio as gr | |
| from llm import run_pipeline | |
| _CLIPS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_audio", "clips") | |
| SAMPLES = [ | |
| ("Sample 1 — Psychiatric referral", os.path.join(_CLIPS_DIR, "D0420-S1-T01_clip.wav")), | |
| ("Sample 2 — Mood check-in", os.path.join(_CLIPS_DIR, "D0420-S2-T01_clip.wav")), | |
| ("Sample 3 — Sleep & nightmares", os.path.join(_CLIPS_DIR, "D0420-S3-T01_clip.wav")), | |
| ] | |
| THEME = gr.themes.Soft(primary_hue=gr.themes.colors.blue) | |
| CSS = """ | |
| .gradio-container { max-width: 1100px !important; } | |
| #hero { | |
| background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%); | |
| color: #fff; | |
| border-radius: 16px; | |
| padding: 28px; | |
| } | |
| #hero h1 { margin: 0 0 8px 0; font-size: 31px; font-weight: 800; } | |
| .pill { | |
| display: inline-block; | |
| background: rgba(255,255,255,.2); | |
| border: 1px solid rgba(255,255,255,.4); | |
| padding: 5px 13px; | |
| border-radius: 999px; | |
| font-size: 12.5px; | |
| margin: 0 8px 6px 0; | |
| } | |
| """ | |
| HERO = """ | |
| <div id="hero"> | |
| <h1>Medical Notes Using Medgemma</h1> | |
| <p>Turn a raw clinical recording into a structured SOAP note — nothing is | |
| fabricated beyond what was actually said.</p> | |
| </div> | |
| """ | |
| def timing_md(transcription_s: float, generation_s: float) -> str: | |
| total = transcription_s + generation_s | |
| return ( | |
| f"**Transcription:** {transcription_s:.2f}s · " | |
| f"**SOAP generation:** {generation_s:.2f}s · " | |
| f"**Total:** {total:.2f}s" | |
| ) | |
| PLACEHOLDER_TRANSCRIPT = "" | |
| PLACEHOLDER_SOAP = "*Select an audio file and click Run.*" | |
| PLACEHOLDER_TIMING = "" | |
| def run(audio_file): | |
| if audio_file is None: | |
| return "No audio file selected.", PLACEHOLDER_SOAP, PLACEHOLDER_TIMING | |
| result = run_pipeline(audio_file) | |
| return ( | |
| result.transcript, | |
| result.soap_note, | |
| timing_md(result.transcription_seconds, result.generation_seconds), | |
| ) | |
| def clear_audio(): | |
| return None, PLACEHOLDER_TRANSCRIPT, PLACEHOLDER_SOAP, PLACEHOLDER_TIMING | |
| with gr.Blocks(title="Medical Notes Using Medgemma") as demo: | |
| gr.HTML(HERO) | |
| with gr.Row(): | |
| sample_buttons = [gr.Button(label, size="sm") for label, _ in SAMPLES] | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| audio_input = gr.Audio( | |
| label="Audio recording", | |
| sources=["upload"], | |
| type="filepath", | |
| buttons=[], | |
| ) | |
| with gr.Column(scale=1, min_width=160): | |
| run_button = gr.Button("Run pipeline", variant="primary", size="lg") | |
| clear_button = gr.Button("Clear audio", size="sm") | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### MedASR Transcript") | |
| with gr.Group(): | |
| transcript_output = gr.Textbox( | |
| show_label=False, | |
| lines=15, | |
| container=False, | |
| placeholder="Transcript will appear here…", | |
| ) | |
| with gr.Column(): | |
| gr.Markdown("### SOAP Note — MedGemma 4B") | |
| with gr.Group(): | |
| soap_output = gr.Markdown(PLACEHOLDER_SOAP) | |
| timing_output = gr.Markdown(PLACEHOLDER_TIMING) | |
| for btn, (_, path) in zip(sample_buttons, SAMPLES): | |
| btn.click(lambda p=path: p, outputs=[audio_input]) | |
| audio_input.change( | |
| fn=run, | |
| inputs=[audio_input], | |
| outputs=[transcript_output, soap_output, timing_output], | |
| ) | |
| run_button.click( | |
| fn=run, | |
| inputs=[audio_input], | |
| outputs=[transcript_output, soap_output, timing_output], | |
| ) | |
| clear_button.click( | |
| fn=clear_audio, | |
| outputs=[audio_input, transcript_output, soap_output, timing_output], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(theme=THEME, css=CSS, footer_links=[]) | |