File size: 27,468 Bytes
130fd1b
9c7753e
ec77913
9c7753e
ec77913
 
f362c64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c7753e
 
 
 
 
 
 
 
 
 
f362c64
 
 
9c7753e
 
 
 
 
 
f362c64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c7753e
 
f362c64
9c7753e
 
 
 
 
 
f362c64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c7753e
6dd3836
f362c64
9c7753e
 
 
 
3264d58
 
 
 
 
9c7753e
 
 
6dd3836
f362c64
9c7753e
 
 
 
3264d58
 
 
 
 
9c7753e
 
6dd3836
9c7753e
 
 
 
 
 
 
6dd3836
9c7753e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec77913
 
9c7753e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec77913
 
9c7753e
ec77913
 
 
 
 
 
 
 
 
9c7753e
 
 
ec77913
 
9c7753e
ec77913
 
9c7753e
ec77913
 
 
9c7753e
ec77913
9c7753e
130fd1b
 
ec77913
 
 
130fd1b
9c7753e
 
 
130fd1b
9c7753e
130fd1b
9c7753e
 
 
 
 
 
 
 
 
 
 
 
 
9178bf4
 
 
 
 
 
ec77913
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9178bf4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c7753e
ec77913
9c7753e
9178bf4
9c7753e
ec77913
9c7753e
9178bf4
 
 
 
 
ec77913
9178bf4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130fd1b
9178bf4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec77913
9178bf4
130fd1b
 
9178bf4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
import gradio as gr
import os
import time
import sys
from datetime import datetime

# Import model configuration
try:
    from model_paths import (
        BASE_MODEL_REPO,
        NOVEL_MODEL_REPO,
        BASE_MODEL_FILENAME,
        NOVEL_MODEL_FILENAME,
        MODELS_DIR
    )
except ImportError:
    # Default values if import fails
    BASE_MODEL_REPO = "unsloth/Qwen3-4B-GGUF"
    NOVEL_MODEL_REPO = "mradermacher/Clinician-Note-2.0a-i1-GGUF"
    BASE_MODEL_FILENAME = "Qwen3-4B-Q4_K_M.gguf"
    NOVEL_MODEL_FILENAME = "Clinician-Note-2.0a.i1-Q4_K_M.gguf"
    MODELS_DIR = "models"

    # Create models directory
    os.makedirs(MODELS_DIR, exist_ok=True)

# Try to import llama_cpp
try:
    from llama_cpp import Llama

    LLAMA_CPP_AVAILABLE = True
    print("llama_cpp is available!")
except ImportError:
    LLAMA_CPP_AVAILABLE = False
    print("llama_cpp is not available. Running in fallback mode.")

# Set up initial model paths
BASE_MODEL_PATH = os.path.join(MODELS_DIR, BASE_MODEL_FILENAME)
NOVEL_MODEL_PATH = os.path.join(MODELS_DIR, NOVEL_MODEL_FILENAME)

# Initialize models
base_model = None
novel_model = None


def download_model(model_repo_id, model_filename, progress=None, progress_start=0.0, progress_end=0.1):
    """Download a model from Hugging Face Hub if it doesn't exist locally"""
    try:
        from huggingface_hub import hf_hub_download
        import os

        # Create models directory if it doesn't exist
        os.makedirs("models", exist_ok=True)

        # Define the local path for the model
        local_path = os.path.join("models", model_filename)

        # Check if model already exists locally
        if os.path.exists(local_path):
            print(f"Model {model_filename} already exists at {local_path}")
            return local_path

        # If progress is provided, update it
        if progress is not None:
            progress(progress_start, desc=f"Downloading {model_filename}... This may take a while")

        print(f"Downloading {model_filename} from Hugging Face Hub...")

        # Download the model from Hugging Face
        downloaded_path = hf_hub_download(
            repo_id=model_repo_id,
            filename=model_filename,
            local_dir="models",
            local_dir_use_symlinks=False
        )

        # If progress is provided, update it
        if progress is not None:
            progress(progress_end, desc=f"Downloaded {model_filename}")

        print(f"Model downloaded to {downloaded_path}")
        return downloaded_path

    except Exception as e:
        print(f"Error downloading model {model_filename}: {str(e)}")
        if progress is not None:
            progress(progress_end, desc=f"Error downloading model: {str(e)}")
        return None


def load_models(progress=None):
    """Load the llama.cpp models"""
    global base_model, novel_model, BASE_MODEL_PATH, NOVEL_MODEL_PATH

    if not LLAMA_CPP_AVAILABLE:
        print("llama_cpp not available, cannot load models")
        return False

    try:
        # Download base model if needed
        base_model_repo = "unsloth/Qwen3-4B-GGUF"
        base_model_filename = "Qwen3-4B-Q4_K_M.gguf"

        if progress is not None:
            progress(0.1, desc="Checking for base model...")

        base_model_path = download_model(
            base_model_repo,
            base_model_filename,
            progress,
            0.1, 0.3
        )

        if not base_model_path:
            raise Exception(f"Failed to download base model {base_model_filename}")

        BASE_MODEL_PATH = base_model_path

        # Download novel model if needed
        novel_model_repo = "mradermacher/Clinician-Note-2.0a-i1-GGUF"
        novel_model_filename = "Clinician-Note-2.0a.i1-Q4_K_M.gguf"

        if progress is not None:
            progress(0.4, desc="Checking for novel model...")

        novel_model_path = download_model(
            novel_model_repo,
            novel_model_filename,
            progress,
            0.4, 0.6
        )

        if not novel_model_path:
            raise Exception(f"Failed to download novel model {novel_model_filename}")

        NOVEL_MODEL_PATH = novel_model_path

        # Load base model
        if progress is not None:
            progress(0.7, desc="Loading base model... This may take a few minutes")

        print(f"Loading base model from {BASE_MODEL_PATH}")
        base_model = Llama(
            model_path=BASE_MODEL_PATH,
            n_ctx=8092,  # Context window size
            n_threads=4,  # Number of CPU threads to use
            temperature=0.3,  # Temperature for sampling
            top_p=0.9,  # Top-p sampling
            top_k=20,  # Top-k sampling
        )

        # Load novel model
        if progress is not None:
            progress(0.9, desc="Loading novel model... This may take a few minutes")

        print(f"Loading novel model from {NOVEL_MODEL_PATH}")
        novel_model = Llama(
            model_path=NOVEL_MODEL_PATH,
            n_ctx=8092,  # Context window size
            n_threads=4,  # Number of CPU threads to use
            temperature=0.3,  # Temperature for sampling
            top_p=0.9,  # Top-p sampling
            top_k=20,  # Top-k sampling
        )

        if progress is not None:
            progress(1.0, desc="Models loaded successfully!")

        print("Models loaded successfully!")
        return True

    except Exception as e:
        print(f"Error loading models: {str(e)}")
        if progress is not None:
            progress(1.0, desc=f"Error loading models: {str(e)}")
        return False


def format_prompt_for_llama(prompt_template, transcript):
    """Format the prompt for llama.cpp models"""
    if not prompt_template.strip():
        # Use default prompt header if not provided
        prompt_template = """
**Role:** You are an AI assistant specialized in generating clinical SOAP notes.

**Task:** Generate a concise, accurate, and clinically relevant SOAP note based **STRICTLY AND SOLELY** on the provided doctor-patient interaction transcript.

**CRITICAL INSTRUCTIONS:**

1.  **Strict Transcript Adherence:** Generate the SOAP note using **ONLY** information **explicitly stated** within the provided transcript.
2.  **NO Assumptions or External Knowledge:** **DO NOT** infer information, add details not mentioned (even if clinically likely), make assumptions, or use external medical knowledge. Adherence to the transcript is paramount.
3.  **Standard SOAP Structure:** Organize the output clearly into the following sections using **EXACTLY** these headings:
    *   **S – Subjective**
    *   **O – Objective**
    *   **A – Assessment**
    *   **P – Plan**
4.  **NO Extraneous Text:** The output must contain **ONLY** the four section headings (S, O, A, P) and the corresponding content derived *directly* from the transcript. **DO NOT** include introductory sentences (e.g., "Here is the SOAP note:"), concluding remarks, disclaimers, notes about the generation process, metadata, or *any* other text before, between, or after the S/O/A/P sections.
"""

    # Simple chat template format for llama.cpp
    full_prompt = f"""<|im_start|>system
You are a medical assistant specialized in creating SOAP notes from doctor-patient transcripts.
<|im_end|>
<|im_start|>user
{prompt_template}

Transcript: {transcript}
<|im_end|>
<|im_start|>assistant
"""

    return full_prompt


def generate_soap_notes(transcript, prompt_template, temperature=0.3, top_p=0.9, top_k=20, progress=gr.Progress()):
    """Generate SOAP notes using llama.cpp models"""
    global base_model, novel_model

    # Check if llama_cpp is available
    if not LLAMA_CPP_AVAILABLE:
        progress(1.0, desc="llama_cpp not available. Running in demo mode.")
        return (
            "llama_cpp not available. Running in demo mode.",
            generate_fallback_soap_note("base"),
            "llama_cpp not available. Running in demo mode.",
            generate_fallback_soap_note("novel")
        )

    # Load models if not already loaded
    if base_model is None or novel_model is None:
        progress(0.1, desc="Loading models... This may take a few minutes")
        if not load_models(progress):
            progress(1.0, desc="Failed to load models. Running in demo mode.")
            return (
                "Failed to load models. Running in demo mode.",
                generate_fallback_soap_note("base"),
                "Failed to load models. Running in demo mode.",
                generate_fallback_soap_note("novel")
            )

    # Format prompt
    formatted_prompt = format_prompt_for_llama(prompt_template, transcript)

    try:
        # Generate with base model
        progress(0.4, desc="Generating with base model...")

        base_output = base_model(
            formatted_prompt,
            max_tokens=1024,
            temperature=temperature,
            top_p=top_p,
            top_k=int(top_k),
            stop=["<|im_end|>", "<|im_start|>"]
        )

        base_text = base_output["choices"][0]["text"] if "choices" in base_output else ""
        base_thinking = f"Generated using llama.cpp at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        base_content = format_soap_note(base_text)

        # Generate with novel model
        progress(0.8, desc="Generating with novel model...")

        novel_output = novel_model(
            formatted_prompt,
            max_tokens=1024,
            temperature=temperature,
            top_p=top_p,
            top_k=int(top_k),
            stop=["<|im_end|>", "<|im_start|>"]
        )

        novel_text = novel_output["choices"][0]["text"] if "choices" in novel_output else ""
        novel_thinking = f"Generated using llama.cpp at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        novel_content = format_soap_note(novel_text)

        progress(1.0, desc="Done!")

        return base_thinking, base_content, novel_thinking, novel_content

    except Exception as e:
        error_msg = f"Error generating SOAP notes: {str(e)}"
        print(error_msg)
        progress(1.0, desc=error_msg)

        return (
            error_msg,
            generate_fallback_soap_note("base"),
            error_msg,
            generate_fallback_soap_note("novel")
        )


def format_soap_note(text):
    """Format the output to ensure it follows SOAP structure"""
    # If the text is empty or very short, return a structured fallback
    if not text or len(text) < 50:
        return generate_fallback_soap_note("base")

    # Check if the text already has SOAP sections
    if "**S –" in text or "S –" in text or "**S -" in text:
        # Text already seems structured, return as is with minor cleaning
        # Remove any text before the first section
        for section_start in ["**S –", "S –", "**S -"]:
            if section_start in text:
                start_idx = text.find(section_start)
                if start_idx > 0:
                    text = text[start_idx:]
                break

        return text.strip()

    # If no structure found, extract content and format it manually
    lines = text.strip().split('\n')
    formatted_text = ""

    # Add structure if it's missing
    if "Subjective" in text or "SUBJECTIVE" in text:
        formatted_text += "**S – Subjective**\n"
    elif not any(s in text.upper() for s in ["S –", "S:", "SUBJECTIVE"]):
        formatted_text += "**S – Subjective**\n"

    if "Objective" in text or "OBJECTIVE" in text:
        formatted_text += "\n**O – Objective**\n"
    elif not any(s in text.upper() for s in ["O –", "O:", "OBJECTIVE"]):
        formatted_text += "\n**O – Objective**\n"

    if "Assessment" in text or "ASSESSMENT" in text:
        formatted_text += "\n**A – Assessment**\n"
    elif not any(s in text.upper() for s in ["A –", "A:", "ASSESSMENT"]):
        formatted_text += "\n**A – Assessment**\n"

    if "Plan" in text or "PLAN" in text:
        formatted_text += "\n**P – Plan**\n"
    elif not any(s in text.upper() for s in ["P –", "P:", "PLAN"]):
        formatted_text += "\n**P – Plan**\n"

    # If we had to add structure, the original text was not properly formatted
    # In this case, return a fallback
    if formatted_text and formatted_text != text:
        return generate_fallback_soap_note("base")

    return text.strip()


def generate_fallback_soap_note(model_type):
    """Generate a fallback SOAP note when model generation fails"""
    if model_type == "base":
        return """**S – Subjective**
Patient complains of migraine for 10 hours, described as severe and unresponsive to medication. Reports experiencing migraines about once a month, sometimes more. Current migraine started with blurry vision and pain in right eye. Reports photophobia, phonophobia, and nausea. Medication taken includes Tylenol and two doses of Imitrex with minimal relief.

**O – Objective**
Lungs clear bilaterally. Heart sounds normal with no murmurs, rubs or gallops. Pupils equal, round, reactive to light and accommodation. No sinus tenderness. Normal lymph nodes. No tongue deviation. Normal movement and strength. Normal neurological exam.

**A – Assessment**
Migraine with aura, unresponsive to Imitrex.

**P – Plan**
1. Trial of Rizatriptan and oxygen therapy
2. Prescription for Rizatriptan
3. Recommendation to maintain migraine diary to identify triggers
4. Follow up with primary care physician"""
    else:
        return """**S – Subjective**
Patient reports migraine for 10 hours with blurry vision and right eye pain. Describes pain as worsening over time. Reports taking Tylenol and two doses of Imitrex with minimal relief. Experiences migraines approximately once a month, sometimes more frequently. Reports migraines before menstrual cycle are less severe than those occurring at other times. Describes photophobia, phonophobia, movement sensitivity, and nausea. Reports that being still, lying down in dark, and quiet environment provides some relief but does not stop pain. Reports seeing stars around lights and flashes behind closed eyes. Denies loss of consciousness, chest pain, or shortness of breath. Denies numbness or weakness in extremities.

**O – Objective**
Lungs clear bilaterally. Heart sounds normal without murmurs, rubs, or gallops. Extraocular movements intact. Pupils equal, round, reactive to light and accommodation. No sinus tenderness. Normal lymph nodes. No tongue deviation. Normal balance test. Normal strength and movement in upper and lower extremities, symmetrical. Previous CT/MRI from 5 years ago was normal per patient.

**A – Assessment**
Migraine with aura, unresponsive to current medication (Imitrex).

**P – Plan**
1. Trial of new triptan medication (Rizatriptan) and oxygen therapy
2. Prescription for Rizatriptan
3. Recommendation to maintain migraine diary to identify triggers
4. Follow up with primary care physician"""


# Create Gradio interface
with gr.Blocks() as demo:
    gr.Markdown("# Clinical SOAP Note Generator")

    if not LLAMA_CPP_AVAILABLE:
        gr.Markdown("""
        ## ⚠️ Important: llama-cpp-python Not Installed

        This application requires the llama-cpp-python library. Please install it:

        ```bash
        pip install llama-cpp-python
        ```

        The interface below will operate in demo mode only.
        """)
    else:
        gr.Markdown("""
        ## GGUF Model-based SOAP Note Generator

        This app uses lightweight GGUF models via llama.cpp to generate SOAP notes from doctor-patient transcripts.
        Models will be loaded when you first generate a note.
        """)

    with gr.Row():
        with gr.Column():
            prompt_template = gr.Textbox(
                label="Prompt Template",
                placeholder="Enter SOAP note generation prompt/instructions here...",
                lines=10,
                value="""**Role:** You are an AI assistant specialized in generating clinical SOAP notes.

**Task:** Generate a concise, accurate, and clinically relevant SOAP note based **STRICTLY AND SOLELY** on the provided doctor-patient interaction transcript.

**CRITICAL INSTRUCTIONS:**

1.  **Strict Transcript Adherence:** Generate the SOAP note using **ONLY** information **explicitly stated** within the provided transcript.
2.  **NO Assumptions or External Knowledge:** **DO NOT** infer information, add details not mentioned (even if clinically likely), make assumptions, or use external medical knowledge. Adherence to the transcript is paramount.
3.  **Standard SOAP Structure:** Organize the output clearly into the following sections using **EXACTLY** these headings:
    *   **S – Subjective**
    *   **O – Objective**
    *   **A – Assessment**
    *   **P – Plan**
4.  **NO Extraneous Text:** The output must contain **ONLY** the four section headings (S, O, A, P) and the corresponding content derived *directly* from the transcript. **DO NOT** include introductory sentences (e.g., "Here is the SOAP note:"), concluding remarks, disclaimers, notes about the generation process, metadata, or *any* other text before, between, or after the S/O/A/P sections."""
            )
            transcript = gr.Textbox(
                label="Doctor-Patient Transcript",
                placeholder="Enter the doctor-patient transcript here...",
                lines=15
            )

            with gr.Row():
                temperature = gr.Slider(
                    minimum=0.1,
                    maximum=1.0,
                    value=0.3,
                    step=0.1,
                    label="Temperature"
                )
                top_p = gr.Slider(
                    minimum=0.1,
                    maximum=1.0,
                    value=0.9,
                    step=0.05,
                    label="Top-p"
                )
                top_k = gr.Slider(
                    minimum=1,
                    maximum=50,
                    value=20,
                    step=1,
                    label="Top-k"
                )

            generate_btn = gr.Button("Generate SOAP Notes", variant="primary")

        with gr.Column():
            with gr.Tab("Base Model (Qwen3-4B-GGUF)"):
                base_thinking = gr.Textbox(label="Model Process", lines=3, interactive=False)
                base_content = gr.Textbox(label="Base Model SOAP Note", lines=20, interactive=False)

            with gr.Tab("Novel Model (Clinician-Note-2.0a-GGUF)"):
                novel_thinking = gr.Textbox(label="Model Process", lines=3, interactive=False)
                novel_content = gr.Textbox(label="Novel Model SOAP Note", lines=20, interactive=False)

    # Connect the generate button
    generate_btn.click(
        generate_soap_notes,
        inputs=[transcript, prompt_template, temperature, top_p, top_k],
        outputs=[base_thinking, base_content, novel_thinking, novel_content]
    )

    # Add example inputs
    gr.Examples(
        [
            [
                """
Doctor: Hi Lisa, I'm Dr. Petros. How can I help you today?

Lisa: Hi doctor, I've had this migraine for hours and it's really frustrating me. I've taken Tylenol and my migraine meds but they aren't helping.

Doctor: How long have you been hurting?

Lisa: It's been about 10 hours now but it feels like forever. These usually only last a few hours. It started with my normal blurry vision and pain in my right eye. The pain just keeps getting worse.

Doctor: I'm sorry to hear that. I'm going to ask you some questions to get some more information. I'll try to keep this brief since I know you don't feel well. I see in your chart that you have had migraines previously. Does this occur often?

Lisa: About once a month, sometimes more.

Doctor: Okay, and you have a prescription that you take when the headache gets bad?

Lisa: Yes, except this one isn't getting any better with my medicine.

Doctor: Well that's no good. Do you know the name of the medication you're prescribed?

Lisa: Yes, it's called Imitrex.

Doctor: Do you use the pill or the injection?

Lisa: Pills. I have taken two doses so far but the medicine has barely helped.

Doctor: Do you have any triggers? Anything that you know brings on your migraines?

Lisa: Not really, except I usually get one just before my period, but those aren't as bad as the others. The ones I get at the other times, I don't know what causes them. They're usually worse.

Doctor: Alright, are you experiencing any other symptoms related to your migraine right now?

Lisa: Light and sound make it worse, and any movement makes it worse too. I felt a little nauseous but I haven't thrown up any.

Doctor: Let's dim the lights in here a little to try to make you more comfortable. Are all of those symptoms normal for your migraine as well?

Lisa: Thanks, that does help a little. And yeah, all of that is typical. Sometimes I do throw up when I have a migraine, but not always.

Doctor: Okay, is there anything else I should know? Has anything made it better at all?

Lisa: Being still and lying down in the dark has helped some. Quiet helps, but nothing makes it stop. The pain hasn't gone away, just gotten a little better, then worse again.

Doctor: Have you noticed any vision changes or any numbness or weakness in your arms or legs?

Lisa: My vision is a little blurry, but that's normal during these episodes. I usually see stars around lights and flashes behind my eyes when I close them. I haven't noticed any weakness though.

Doctor: It sounds like you typically have migraines with aura. That's the technical term for what you describe as stars and flashes. Have you had any loss of consciousness, chest pain, or shortness of breath?

Lisa: No, none of that.

Doctor: Okay, so other than the extended length of time, this is a typical migraine for you? No new symptoms?

Lisa: Yeah, pretty much.

Doctor: Have you ever had a migraine that did not respond to the Imitrex before?

Lisa: Yeah, this happens every so often. It's really annoying.

Doctor: Do you have another treatment that usually works for you?

Lisa: I don't know. I think it can be different every time.

Doctor: Okay, that's fine. We will figure out a solution. Do you mind if I do a quick physical exam?

Lisa: No, go ahead.

Doctor: Thank you. I'm going to listen to your heart and lungs. Just breathe normally. Lungs are clear bilaterally. Heart sounds are normal with no murmurs, rubs or gallops. Now can you look at me and follow my finger please with just your eyes?

Lisa: [complies]

Doctor: Okay, great. Now I'm sorry to do this, but I need to briefly check your eyes with the light. It's going to be a little unpleasant.

Lisa: It's okay, I understand.

Doctor: I am looking for your pupils' response to light. I just want to be sure I don't see any signs pointing to anything other than a migraine here, like a stroke or aneurysm. Good, pupils are equal, round, and react normally to light and accommodation. Now I'm going to press on your sinuses. You should feel a little pressure, but tell me if you feel any pain.

Lisa: Okay. No pain.

Doctor: Good. Let me check your neck for a moment. Okay, your lymph nodes are normal as well. Now open your mouth and stick your tongue out.

Lisa: [complies]

Doctor: Good, no deviation there. Now, have you ever had a CT or MRI since you started having these headaches?

Lisa: Yes, I had one about five years ago when they started happening more regularly. It was normal.

Doctor: Okay, that's good. Can you stand up for me? I will stand behind you in case you lose your balance. Close your eyes and hold your arms out to your sides. I'm just checking your inner ear and proprioception.

Lisa: [complies]

Doctor: Good, I don't see any problems. You can sit back down. Now I want you to put both your hands against mine and push them down.

Lisa: [complies]

Doctor: Good. Now do the same with your legs, try to push my hands up.

Lisa: [complies]

Doctor: Good, thank you. Your movement and strength are normal and symmetrical, so that's a good sign. You have a normal neuro exam. Alright Lisa, I don't see anything too concerning. It seems you have a migraine that is simply unresponsive to your normal medication. Let's see if we can help you with this headache. I'm going to put in some orders and I'll be back in a few minutes to talk with you again.

Lisa: Okay, sure. Thank you.

[Later]

Doctor: All right Lisa, I have a sample here of a newer type of triptan medication. You can chew it or let it dissolve in your mouth so it acts a little more quickly. I'd like to try it and see if it is any more effective than the Imitrex. I'm also going to put some low flow oxygen on you. There's evidence that some other types of headaches can mimic migraine symptoms and some of them respond to oxygen therapy.

Lisa: I'm okay with that. Anything that makes this better.

Doctor: Okay, I will turn the lights off and send the nurse in with the medication and oxygen. I'd like you to lie back and rest until we see if it helps. Do you have any questions for now?

Lisa: No, I don't. Thank you.

[Later]

Doctor: Hi Lisa, are you feeling any better?

Lisa: Actually, I am. I started to feel the pain fade about 15 minutes ago. It's down to a dull ache now.

Doctor: That's great. How's your vision now?

Lisa: Almost normal. Nausea completely gone.

Doctor: Wonderful!

Lisa: You have no idea.

Doctor: I'm glad you're feeling better. I will write you a prescription for the newer medication. It's called Rizatriptan. Your most up-to-date pharmacy is in our system, correct?

Lisa: Yes, it is.

Doctor: Good. So now that you can think a little better, let's talk about what might be causing these.

Lisa: Okay.

Doctor: Have you ever done a migraine diary?

Lisa: No, not really.

Doctor: I think it would be a really good idea. You can keep a calendar just for this on your phone or something like that. Whenever you have a migraine, write down everything you can remember about that day - what you ate or drank, where you were, how you slept, that kind of thing. Hopefully you will start to see a pattern in what sets them off. It might be a food allergy, stress, or lack of sleep. Many women have hormonal migraines. I know you said you have them monthly, but that the ones unrelated to your menstrual cycle are different?

Lisa: Yes, they are.

Doctor: So maybe concentrate on those. Migraines are much easier to prevent than to treat. If you can discover your triggers, there are some medications you could try as a preventative measure, but I hate to go there if you can figure out what causes them and just avoid those triggers. Some of the preventative medications have significant side effects. At your age, I would rather avoid them if possible.

Lisa: I totally agree. I will start keeping track of the headaches and try to find out what is causing them.

Doctor: Great. I suggest you follow up on that with your primary care physician. If you don't have one, I will be happy to help you, but your best option is for consistent follow-up with your own doctor. Does this all sound okay to you?

Lisa: Yes, that works for me. Thank you so much.

Doctor: You are very welcome. Do you have any other questions for me?

Lisa: No, I'm just ready to go back home.

Doctor: Sounds like a good plan. All right Lisa, take care and feel better.
"""
            ]
        ],
        inputs=[transcript]
    )

if __name__ == "__main__":
    demo.launch()