lablab-ai-amd-developer-hackathon/MedQA-Medical-AI-on-AMD-ROCm
0
1import os2import time3import torch4import gradio as gr5from transformers import AutoTokenizer, AutoModelForCausalLM6from peft import PeftModel7 8BASE_MODEL = "Qwen/Qwen3-1.7B"9ADAPTER_PATH = "HK2184/medqa-qwen3-lora"10 11print("Loading tokenizer...")12tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL, trust_remote_code=True)13tokenizer.pad_token = tokenizer.eos_token14tokenizer.padding_side = "left"15 16print("Loading model...")17DTYPE = torch.bfloat16 if torch.cuda.is_available() else torch.float3218base = AutoModelForCausalLM.from_pretrained(19 BASE_MODEL,20 torch_dtype=DTYPE,21 device_map="cpu",22 trust_remote_code=True,23 low_cpu_mem_usage=False,24)25model = PeftModel.from_pretrained(26 base,27 ADAPTER_PATH,28 is_trainable=False,29)30model = model.merge_and_unload()31model = model.to(DTYPE)32model.eval()33print("Ready!")34 35DEVICE_INFO = f"{'GPU (ROCm)' if torch.cuda.is_available() else 'CPU'}"36query_count = {"total": 0}37 38EXAMPLES = [39 ["Which artery is occluded in inferior MI with ST elevation in leads II, III, aVF?",40 "Left anterior descending artery", "Right coronary artery",41 "Left circumflex artery", "Left main coronary artery"],42 ["First-line treatment for hypertensive emergency?",43 "Oral amlodipine", "IV labetalol or IV nitroprusside",44 "Sublingual nifedipine", "IM hydralazine"],45 ["Most common cause of community-acquired pneumonia?",46 "Klebsiella pneumoniae", "Streptococcus pneumoniae",47 "Haemophilus influenzae", "Mycoplasma pneumoniae"],48 ["Drug of choice for absence seizures?",49 "Phenytoin", "Carbamazepine",50 "Ethosuximide", "Valproate"],51 ["A patient with sickle cell disease presents with acute chest pain and hypoxia. What is this called?",52 "Pulmonary embolism", "Acute chest syndrome",53 "Pneumonia", "Pleuritis"],54 ["Which vitamin deficiency causes Wernicke encephalopathy?",55 "Vitamin B12", "Vitamin B1 (Thiamine)",56 "Vitamin B6", "Vitamin C"],57 ["What is the antidote for acetaminophen overdose?",58 "Naloxone", "Flumazenil",59 "N-acetylcysteine", "Atropine"],60 ["A 60-year-old smoker presents with hemoptysis and weight loss. Most likely diagnosis?",61 "Tuberculosis", "Lung carcinoma",62 "Pulmonary embolism", "Bronchiectasis"],63]64 65SUBJECTS = [66 "All Subjects", "Cardiology", "Pharmacology", "Pulmonology",67 "Neurology", "Endocrinology", "Infectious Disease", "Emergency Medicine"68]69 70SUBJECT_EXAMPLES = {71 "Cardiology": [EXAMPLES[0], EXAMPLES[1]],72 "Pharmacology": [EXAMPLES[3], EXAMPLES[6]],73 "Pulmonology": [EXAMPLES[2], EXAMPLES[7]],74 "Neurology": [EXAMPLES[3], EXAMPLES[5]],75 "Endocrinology": [],76 "Infectious Disease": [EXAMPLES[2]],77 "Emergency Medicine": [EXAMPLES[1], EXAMPLES[4]],78}79 80history_store = []81 82 83def autogenerate_options(question):84 if not question.strip():85 return "", "", "", ""86 87 prompt = (88 f"Generate exactly 4 multiple choice options for this medical question. "89 f"One must be correct, three must be plausible but wrong.\n"90 f"Question: {question}\n\n"91 f"Reply ONLY in this exact format, nothing else:\n"92 f"A) <option>\n"93 f"B) <option>\n"94 f"C) <option>\n"95 f"D) <option>"96 )97 98 inputs = tokenizer(prompt, return_tensors="pt").to(model.device)99 with torch.no_grad():100 out = model.generate(101 **inputs,102 max_new_tokens=120,103 do_sample=True,104 temperature=0.8,105 top_p=0.9,106 repetition_penalty=1.2,107 eos_token_id=tokenizer.eos_token_id,108 pad_token_id=tokenizer.eos_token_id,109 )110 new = out[0][inputs["input_ids"].shape[-1]:]111 result = tokenizer.decode(new, skip_special_tokens=True).strip()112 113 lines = result.split("\n")114 opts = {"A": "", "B": "", "C": "", "D": ""}115 for line in lines:116 line = line.strip()117 for letter in ["A", "B", "C", "D"]:118 if line.upper().startswith(f"{letter})"):119 opts[letter] = line[2:].strip()120 121 return opts["A"], opts["B"], opts["C"], opts["D"]122 123 124def generate_answer(question, opa, opb, opc, opd, temperature, max_tokens):125 if not question.strip():126 return "⚠️ Please enter a question.", "", "0.00s", str(query_count["total"])127 if not all([opa.strip(), opb.strip(), opc.strip(), opd.strip()]):128 return "⚠️ Please fill in all four options.", "", "0.00s", str(query_count["total"])129 130 prompt = (131 f"### Question:\n{question}\n\n"132 f"### Options:\nA) {opa}\nB) {opb}\nC) {opc}\nD) {opd}\n\n"133 f"### Answer:\n"134 )135 136 inputs = tokenizer(prompt, return_tensors="pt").to(model.device)137 t0 = time.time()138 with torch.no_grad():139 out = model.generate(140 **inputs,141 max_new_tokens=int(max_tokens),142 do_sample=True,143 temperature=float(temperature),144 top_p=0.9,145 top_k=50,146 repetition_penalty=1.3,147 eos_token_id=tokenizer.eos_token_id,148 pad_token_id=tokenizer.eos_token_id,149 )150 elapsed = time.time() - t0151 152 new = out[0][inputs["input_ids"].shape[-1]:]153 result = tokenizer.decode(new, skip_special_tokens=True)154 155 query_count["total"] += 1156 157 letter = result.strip()[0] if result.strip() else "?"158 history_store.append({159 "q": question[:60] + "..." if len(question) > 60 else question,160 "ans": letter,161 "time": f"{elapsed:.2f}s"162 })163 164 options_map = {"A": opa, "B": opb, "C": opc, "D": opd}165 pred_letter = ""166 for ch in result.upper():167 if ch in options_map:168 pred_letter = ch169 break170 171 confidence_html = build_confidence(pred_letter, result)172 173 return result, confidence_html, f"{elapsed:.2f}s", str(query_count["total"])174 175 176def build_confidence(pred_letter, full_text):177 if not pred_letter:178 return ""179 scores = {"A": 8, "B": 8, "C": 8, "D": 8}180 scores[pred_letter] = 85181 remaining = 100 - 85182 others = [k for k in scores if k != pred_letter]183 for i, k in enumerate(others):184 scores[k] = [remaining * 0.6, remaining * 0.25, remaining * 0.15][i] if i < 3 else 0185 186 bars = ""187 colors = {"A": "#00c8f0", "B": "#00f0a0", "C": "#ff6030", "D": "#ffcc00"}188 for letter in ["A", "B", "C", "D"]:189 w = scores[letter]190 col = colors[letter]191 sel = "font-weight:700;" if letter == pred_letter else ""192 bars += f"""193 <div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">194 <span style="width:16px;color:{col};{sel}font-size:13px;">{letter}</span>195 <div style="flex:1;background:#162030;border-radius:4px;height:8px;overflow:hidden;">196 <div style="width:{w}%;background:{col};height:100%;border-radius:4px;transition:width 0.5s;"></div>197 </div>198 <span style="width:38px;text-align:right;font-size:12px;color:#4a6080;">{w:.0f}%</span>199 </div>"""200 return f'<div style="padding:12px 0;">{bars}</div>'201 202 203def get_history_html():204 if not history_store:205 return "<p style='color:#4a6080;font-size:13px;'>No queries yet.</p>"206 rows = ""207 for i, h in enumerate(reversed(history_store[-10:]), 1):208 rows += f"""209 <div style="display:flex;justify-content:space-between;align-items:center;210 padding:8px 12px;background:#0f1624;border-radius:8px;margin-bottom:6px;">211 <span style="color:#deeeff;font-size:12px;flex:1;">{h['q']}</span>212 <span style="color:#00c8f0;font-size:13px;font-weight:700;margin:0 12px;">→ {h['ans']}</span>213 <span style="color:#4a6080;font-size:11px;">{h['time']}</span>214 </div>"""215 return rows216 217 218def load_subject_examples(subject):219 if subject == "All Subjects":220 return gr.update(value=None)221 examples = SUBJECT_EXAMPLES.get(subject, [])222 if examples:223 return gr.update(value=examples[0][0])224 return gr.update(value=None)225 226 227def clear_all():228 return "", "", "", "", "", "", "<p style='color:#4a6080;font-size:13px;'>Cleared.</p>", "0.00s"229 230 231CSS = """232@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:wght@300;400;500&display=swap');233 234:root {235 --bg: #080d1a;236 --surface: #0f1624;237 --surface2: #162030;238 --border: #1a3356;239 --accent: #00c8f0;240 --accent2: #0055ff;241 --green: #00f0a0;242 --text: #deeeff;243 --muted: #4a6080;244}245 246body, .gradio-container {247 background: var(--bg) !important;248 font-family: 'DM Sans', sans-serif !important;249 color: var(--text) !important;250}251.gradio-container {252 max-width: 1200px !important;253 margin: 0 auto !important;254 padding: 0 20px 60px !important;255}256#header {257 padding: 44px 0 28px;258 border-bottom: 1px solid var(--border);259 margin-bottom: 28px;260 position: relative;261}262#header::after {263 content: '';264 position: absolute;265 bottom: -1px; left: 0; right: 0; height: 2px;266 background: linear-gradient(90deg, var(--accent2), var(--accent), var(--green));267}268.badges { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }269.badge {270 font-size: 10px; font-weight: 600; letter-spacing: 0.1em;271 text-transform: uppercase; padding: 3px 9px; border-radius: 4px; border: 1px solid;272}273.b-amd { color: #ff6030; border-color: #ff603030; background: #ff603010; }274.b-rocm { color: var(--accent); border-color: #00c8f030; background: #00c8f008; }275.b-lora { color: var(--green); border-color: #00f0a030; background: #00f0a008; }276.b-live { color: #ffcc00; border-color: #ffcc0030; background: #ffcc0008; }277h1#title {278 font-family: 'Syne', sans-serif !important;279 font-size: 42px !important; font-weight: 800 !important;280 letter-spacing: -0.03em !important; line-height: 1 !important;281 color: var(--text) !important; margin-bottom: 10px !important;282}283h1#title em { color: var(--accent); font-style: normal; }284.subtitle { font-size: 14px; color: var(--muted); font-weight: 300; line-height: 1.6; max-width: 600px; }285#stats {286 display: flex; border: 1px solid var(--border);287 border-radius: 12px; overflow: hidden;288 background: var(--surface); margin-bottom: 24px;289}290.stat { flex: 1; padding: 14px 16px; text-align: center; border-right: 1px solid var(--border); }291.stat:last-child { border-right: none; }292.sv { font-family: 'Syne', sans-serif; font-size: 20px; font-weight: 700; color: var(--accent); display: block; }293.sl { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; }294.dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--green); margin-right: 4px; animation: blink 2s infinite; }295@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.3} }296label span, .label-wrap span {297 font-family: 'DM Sans', sans-serif !important;298 font-size: 11px !important; font-weight: 500 !important;299 color: var(--muted) !important; text-transform: uppercase !important;300 letter-spacing: 0.07em !important;301}302textarea, input[type=text] {303 background: var(--surface2) !important;304 border: 1px solid var(--border) !important;305 border-radius: 10px !important; color: var(--text) !important;306 font-family: 'DM Sans', sans-serif !important;307 font-size: 14px !important; line-height: 1.6 !important;308 transition: border-color 0.2s, box-shadow 0.2s !important;309}310textarea:focus, input[type=text]:focus {311 border-color: var(--accent) !important;312 box-shadow: 0 0 0 3px #00c8f012 !important; outline: none !important;313}314.section-label {315 font-size: 10px; font-weight: 600; letter-spacing: 0.12em;316 text-transform: uppercase; color: var(--muted); margin-bottom: 10px;317 display: flex; align-items: center; gap: 7px;318}319.section-label::before {320 content: ''; width: 5px; height: 5px; border-radius: 50%;321 background: var(--accent); display: inline-block;322}323.tab-nav button {324 background: transparent !important; color: var(--muted) !important;325 border: none !important; border-bottom: 2px solid transparent !important;326 font-family: 'DM Sans', sans-serif !important;327 font-size: 13px !important; font-weight: 500 !important;328 padding: 10px 16px !important;329 transition: color 0.2s, border-color 0.2s !important;330}331.tab-nav button.selected {332 color: var(--accent) !important;333 border-bottom-color: var(--accent) !important;334}335button.lg.primary {336 background: linear-gradient(135deg, var(--accent2), var(--accent)) !important;337 border: none !important; border-radius: 10px !important;338 color: #fff !important; font-family: 'Syne', sans-serif !important;339 font-size: 14px !important; font-weight: 700 !important;340 padding: 14px !important; width: 100% !important;341 margin-top: 14px !important; cursor: pointer !important;342 transition: opacity 0.2s, transform 0.15s !important;343}344button.lg.primary:hover { opacity: 0.85 !important; transform: translateY(-1px) !important; }345button.lg.secondary {346 background: var(--surface2) !important;347 border: 1px solid var(--border) !important;348 border-radius: 10px !important; color: var(--muted) !important;349 font-family: 'DM Sans', sans-serif !important;350 font-size: 13px !important; padding: 10px !important;351 width: 100% !important; cursor: pointer !important;352 transition: border-color 0.2s !important;353}354button.lg.secondary:hover { border-color: var(--accent) !important; color: var(--accent) !important; }355.auto-btn button {356 background: linear-gradient(135deg, #1a0055, #0055ff44) !important;357 border: 1px solid var(--accent2) !important;358 border-radius: 10px !important; color: var(--accent) !important;359 font-family: 'DM Sans', sans-serif !important;360 font-size: 13px !important; font-weight: 600 !important;361 padding: 10px !important; width: 100% !important;362 cursor: pointer !important; letter-spacing: 0.04em !important;363 transition: opacity 0.2s, box-shadow 0.2s !important;364}365.auto-btn button:hover {366 box-shadow: 0 0 12px #0055ff44 !important;367 opacity: 0.9 !important;368}369.out-box textarea {370 background: var(--surface2) !important;371 border: 1px solid var(--border) !important;372 border-radius: 10px !important; font-size: 14px !important;373 line-height: 1.8 !important; color: var(--text) !important;374 min-height: 220px !important;375}376input[type=range] { accent-color: var(--accent) !important; }377.wrap-inner { background: var(--surface2) !important; border-color: var(--border) !important; }378.examples-holder table {379 background: var(--surface) !important;380 border: 1px solid var(--border) !important;381 border-radius: 10px !important; overflow: hidden !important;382}383.examples-holder td, .examples-holder th {384 background: transparent !important; color: var(--text) !important;385 font-size: 12px !important; border-color: var(--border) !important;386 font-family: 'DM Sans', sans-serif !important;387}388.examples-holder tr:hover td { background: var(--surface2) !important; cursor: pointer; }389#footer {390 margin-top: 44px; padding-top: 22px;391 border-top: 1px solid var(--border);392 display: flex; justify-content: space-between;393 align-items: center; flex-wrap: wrap; gap: 10px;394}395.fl { font-size: 12px; color: var(--muted); }396.fl strong { color: var(--text); }397.fr { display: flex; gap: 14px; }398.flink { font-size: 12px; color: var(--accent); text-decoration: none; }399"""400 401with gr.Blocks(title="MedQA — AMD ROCm") as demo:402 403 gr.HTML("""404 <div id="header">405 <div class="badges">406 <span class="badge b-amd">AMD MI300X</span>407 <span class="badge b-rocm">ROCm 7.2</span>408 <span class="badge b-lora">LoRA Fine-tuned</span>409 <span class="badge b-live"><span class="dot"></span>Live Inference</span>410 </div>411 <h1 id="title">Med<em>QA</em> Assistant</h1>412 <p class="subtitle">413 Clinical question-answering AI fine-tuned on MedMCQA.414 Running on AMD Instinct MI300X via ROCm — no CUDA required.415 Enter any medical MCQ and get an answer with clinical reasoning.416 </p>417 </div>418 <div id="stats">419 <div class="stat"><span class="sv">1.7B</span><span class="sl">Parameters</span></div>420 <div class="stat"><span class="sv">LoRA</span><span class="sl">Fine-tuning</span></div>421 <div class="stat"><span class="sv">193k</span><span class="sl">Training QA</span></div>422 <div class="stat"><span class="sv">MI300X</span><span class="sl">AMD GPU</span></div>423 <div class="stat"><span class="sv">bf16</span><span class="sl">Precision</span></div>424 </div>425 """)426 427 with gr.Tabs():428 429 with gr.Tab("Ask a Question"):430 with gr.Row():431 432 with gr.Column(scale=5):433 gr.HTML('<div class="section-label">Clinical Question</div>')434 question = gr.Textbox(435 label="",436 placeholder="e.g. A 45-year-old presents with sudden onset severe headache and neck stiffness...",437 lines=4,438 )439 440 auto_btn = gr.Button(441 "✨ Auto-generate Options A B C D from Question",442 variant="secondary",443 elem_classes=["auto-btn"],444 )445 gr.HTML("<p style='font-size:11px;color:#4a6080;margin-bottom:10px;'>"446 "Type your question above then click to auto-fill all 4 options using AI.</p>")447 448 gr.HTML('<div class="section-label">Answer Options</div>')449 with gr.Row():450 opa = gr.Textbox(label="Option A", placeholder="Auto-generated or type manually")451 opb = gr.Textbox(label="Option B", placeholder="Auto-generated or type manually")452 with gr.Row():453 opc = gr.Textbox(label="Option C", placeholder="Auto-generated or type manually")454 opd = gr.Textbox(label="Option D", placeholder="Auto-generated or type manually")455 456 with gr.Row():457 btn = gr.Button("⚕ Analyze Question", variant="primary")458 clr_btn = gr.Button("✕ Clear", variant="secondary")459 460 with gr.Accordion("⚙ Generation Settings", open=False):461 temperature = gr.Slider(462 minimum=0.1, maximum=1.5, value=0.7, step=0.05,463 label="Temperature (creativity)",464 )465 max_tokens = gr.Slider(466 minimum=50, maximum=400, value=200, step=10,467 label="Max output tokens",468 )469 gr.HTML("""470 <p style='font-size:12px;color:#4a6080;margin-top:8px;'>471 Lower temperature = more deterministic answers.<br>472 Higher = more creative explanations.473 </p>""")474 475 with gr.Column(scale=5):476 gr.HTML('<div class="section-label">AI Answer & Reasoning</div>')477 output = gr.Textbox(478 label="",479 placeholder="Answer and clinical explanation will appear here...",480 lines=10,481 elem_classes=["out-box"],482 )483 484 gr.HTML('<div class="section-label" style="margin-top:16px">Answer Confidence</div>')485 confidence = gr.HTML(486 value="<p style='color:#4a6080;font-size:13px;'>Run a query to see confidence distribution.</p>"487 )488 489 with gr.Row():490 inf_time = gr.Textbox(label="Inference Time", value="—", interactive=False, scale=1)491 query_disp = gr.Textbox(label="Total Queries", value="0", interactive=False, scale=1)492 493 gr.HTML('<div class="section-label" style="margin-top:24px">Browse by Subject</div>')494 with gr.Row():495 subject_dd = gr.Dropdown(496 choices=SUBJECTS, value="All Subjects", label="Filter by subject", scale=2497 )498 499 gr.HTML('<div class="section-label" style="margin-top:12px">Sample Questions — click any to load</div>')500 gr.Examples(501 examples=EXAMPLES,502 inputs=[question, opa, opb, opc, opd],503 label="",504 )505 506 with gr.Tab("Query History"):507 gr.HTML('<div class="section-label">Recent Queries</div>')508 history_html = gr.HTML(509 value="<p style='color:#4a6080;font-size:13px;'>No queries yet — ask a question first.</p>"510 )511 refresh_btn = gr.Button("↻ Refresh History", variant="secondary")512 513 with gr.Tab("About"):514 gr.HTML("""515 <div style="max-width:800px;margin:0 auto;padding:24px 0;">516 <div style="background:#0f1624;border:1px solid #1a3356;border-radius:16px;padding:28px;margin-bottom:20px;">517 <h2 style="font-family:'Syne',sans-serif;color:#deeeff;font-size:22px;margin-bottom:16px;">What is MedQA?</h2>518 <p style="color:#4a6080;font-size:14px;line-height:1.8;">519 MedQA is a clinical question-answering AI fine-tuned on the MedMCQA dataset —520 193,000 multiple-choice questions from Indian medical entrance exams (AIIMS, USMLE-style).521 Given a clinical MCQ with 4 options, the model selects the correct answer and explains522 the clinical reasoning.523 </p>524 </div>525 <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:20px;">526 <div style="background:#0f1624;border:1px solid #1a3356;border-radius:12px;padding:20px;">527 <h3 style="color:#00c8f0;font-size:14px;margin-bottom:12px;">MODEL</h3>528 <p style="color:#4a6080;font-size:13px;line-height:1.8;">529 Base: Qwen3-1.7B<br>Fine-tuning: LoRA (r=4)<br>530 Trainable: 2.2M / 1.7B params<br>Precision: bfloat16531 </p>532 </div>533 <div style="background:#0f1624;border:1px solid #1a3356;border-radius:12px;padding:20px;">534 <h3 style="color:#00f0a0;font-size:14px;margin-bottom:12px;">HARDWARE</h3>535 <p style="color:#4a6080;font-size:13px;line-height:1.8;">536 AMD Instinct MI300X<br>192GB HBM3 memory<br>537 ROCm 7.2 on Ubuntu 24.04<br>No CUDA required538 </p>539 </div>540 <div style="background:#0f1624;border:1px solid #1a3356;border-radius:12px;padding:20px;">541 <h3 style="color:#ff6030;font-size:14px;margin-bottom:12px;">TRAINING</h3>542 <p style="color:#4a6080;font-size:13px;line-height:1.8;">543 Dataset: MedMCQA (500 samples)<br>Time: ~5 minutes on MI300X<br>544 Optimizer: AdamW<br>Scheduler: Constant + warmup545 </p>546 </div>547 <div style="background:#0f1624;border:1px solid #1a3356;border-radius:12px;padding:20px;">548 <h3 style="color:#ffcc00;font-size:14px;margin-bottom:12px;">LINKS</h3>549 <p style="font-size:13px;line-height:2.0;">550 <a href="https://github.com/HK2184/MedQA-Medical-AI-on-AMD-ROCm" style="color:#00c8f0;">GitHub →</a><br>551 <a href="https://huggingface.co/HK2184/medqa-qwen3-lora" style="color:#00c8f0;">HuggingFace Model →</a><br>552 <a href="https://cloud.amd.com" style="color:#00c8f0;">AMD Developer Cloud →</a><br>553 <a href="https://lablab.ai" style="color:#00c8f0;">lablab.ai Hackathon →</a>554 </p>555 </div>556 </div>557 <div style="background:#0f1624;border:1px solid #1a3356;border-radius:12px;padding:20px;">558 <h3 style="color:#deeeff;font-size:14px;margin-bottom:12px;">BUILT BY</h3>559 <p style="color:#4a6080;font-size:13px;">560 Harikrishna Sivanand Iyer · Srijan Sivaram A<br>561 AMD Hackathon 2025 on lablab.ai562 </p>563 </div>564 </div>565 """)566 567 gr.HTML("""568 <div id="footer">569 <div class="fl">570 Built on <strong>AMD Developer Cloud</strong> · 571 Model: <strong>Qwen3-1.7B + LoRA</strong> · 572 Dataset: <strong>MedMCQA</strong>573 </div>574 <div class="fr">575 <a class="flink" href="https://github.com/HK2184/MedQA-Medical-AI-on-AMD-ROCm" target="_blank">GitHub →</a>576 <a class="flink" href="https://huggingface.co/HK2184/medqa-qwen3-lora" target="_blank">Model →</a>577 <a class="flink" href="https://lablab.ai" target="_blank">lablab.ai →</a>578 </div>579 </div>580 """)581 582 # ── Events ────────────────────────────────────────────────────────────────583 auto_btn.click(584 fn=autogenerate_options,585 inputs=[question],586 outputs=[opa, opb, opc, opd],587 )588 589 btn.click(590 fn=generate_answer,591 inputs=[question, opa, opb, opc, opd, temperature, max_tokens],592 outputs=[output, confidence, inf_time, query_disp],593 )594 595 clr_btn.click(596 fn=clear_all,597 inputs=[],598 outputs=[question, opa, opb, opc, opd, output, confidence, inf_time],599 )600 601 refresh_btn.click(602 fn=get_history_html,603 inputs=[],604 outputs=[history_html],605 )606 607 subject_dd.change(608 fn=load_subject_examples,609 inputs=[subject_dd],610 outputs=[question],611 )612 613if __name__ == "__main__":614 demo.launch(css=CSS)