%%writefile app.py import gradio as gr import os, sys, subprocess, re, json, base64, io, contextlib from io import BytesIO from PIL import Image from huggingface_hub import hf_hub_download # ── HARDWARE ────────────────────────────────── try: result = subprocess.run(['nvidia-smi'], capture_output=True) HAS_GPU = result.returncode == 0 except: HAS_GPU = False # ── INSTALL ─────────────────────────────────── try: import llama_cpp import argostranslate.package import argostranslate.translate except ImportError: subprocess.check_call([sys.executable, '-m', 'pip', 'install', '-q', 'argostranslate', 'huggingface_hub', 'pillow']) subprocess.check_call([sys.executable, '-m', 'pip', 'install', '-q', 'llama-cpp-python', '--extra-index-url', 'https://abetlen.github.io/llama-cpp-python/whl/cu121' if HAS_GPU else 'https://abetlen.github.io/llama-cpp-python/whl/cpu' ]) import llama_cpp import argostranslate.package import argostranslate.translate from llama_cpp import Llama from llama_cpp.llama_chat_format import Llava15ChatHandler LANGUAGES = { "English": "en", "Urdu": "ur", "Arabic": "ar", "Hindi": "hi", "French": "fr", "Spanish": "es" } # ── LOAD MODEL ──────────────────────────────── def load_model(): print("📥 Downloading MedGemma GGUF...") text_gguf = hf_hub_download( repo_id="unsloth/medgemma-4b-it-GGUF", filename="medgemma-4b-it-Q4_K_M.gguf" ) vision_gguf = hf_hub_download( repo_id="kelkalot/medgemma-4b-it-GGUF", filename="mmproj-medgemma-4b-it-F16.gguf" ) chat_handler = Llava15ChatHandler(clip_model_path=vision_gguf) return Llama( model_path=text_gguf, chat_handler=chat_handler, chat_format="gemma", n_ctx=4096, n_threads=os.cpu_count() or 4, n_gpu_layers=-1 if HAS_GPU else 0, verbose=False ) model = load_model() # ── TRANSLATION ─────────────────────────────── def ensure_language_downloaded(target_code): if target_code == "en": return installed = argostranslate.package.get_installed_packages() if any(p.to_code == target_code and p.from_code == 'en' for p in installed): return argostranslate.package.update_package_index() available = argostranslate.package.get_available_packages() try: pkg = next(filter(lambda x: x.from_code == 'en' and x.to_code == target_code, available)) argostranslate.package.install_from_path(pkg.download()) except: pass def translate_text(text, target_code): if target_code == "en" or not text or text == "N/A": return text try: return argostranslate.translate.translate(text, "en", target_code) except: return text # ── JSON HELPERS ────────────────────────────── def fix_json(raw): raw = re.sub(r'```json|```', '', raw).strip() raw = re.sub(r':\s*true\b', ': "True"', raw, flags=re.IGNORECASE) raw = re.sub(r':\s*false\b', ': "False"', raw, flags=re.IGNORECASE) raw = re.sub(r':\s*high\b', ': "High"', raw, flags=re.IGNORECASE) raw = re.sub(r':\s*medium\b', ': "Medium"', raw, flags=re.IGNORECASE) raw = re.sub(r':\s*low\b', ': "Low"', raw, flags=re.IGNORECASE) return raw def parse_output(raw): try: return json.loads(fix_json(raw)) except: pass try: match = re.search(r'\{.*?\}', raw, re.DOTALL) if match: return json.loads(fix_json(match.group())) except: pass def extract(p, t, d='N/A'): m = re.search(p, t, re.IGNORECASE | re.DOTALL) return m.group(1).strip() if m else d return { 'claim': extract(r'"claim"\s*:\s*"([^"]+)"', raw), 'verdict': extract(r'"verdict"\s*:\s*["]?([^",}\n]+)', raw), 'reason': extract(r'"reason"\s*:\s*"([^"]+)"', raw), 'doctor_summary': extract(r'"doctor_summary"\s*:\s*"([^"]+)"', raw), 'risk_level': extract(r'"risk_level"\s*:\s*["]?([^",}\n]+)',raw), 'confidence': extract(r'"confidence"\s*:\s*["]?([0-9.]+)', raw, '0.0'), } # ── ANALYZE ─────────────────────────────────── def analyze(user_claim, symptoms, age, gender, condition, language, image): lang_code = LANGUAGES.get(language, "en") ensure_language_downloaded(lang_code) history = f"{age} Years Old {gender}, {condition}" image_b64 = None if image is not None: try: img = Image.fromarray(image).convert("RGB") buf = BytesIO() img.save(buf, format="JPEG") image_b64 = base64.b64encode(buf.getvalue()).decode('utf-8') except: pass raw_prompt = f"""user You are an expert Clinical Triage Specialist and Medical Fact-Checker. Return ONLY a valid JSON object. No extra text, no markdown outside the JSON. SCHEMA: {{"claim":"...","verdict":"True|False|Mixed|Insufficient Evidence","reason":"...","doctor_summary":"...","risk_level":"Low|Medium|High","confidence":"0.0-1.0"}} EXAMPLE: {{"claim":"Drinking bleach cures covid","verdict":"False","reason":"Bleach is caustic and toxic with no antiviral properties.","doctor_summary":"HIGH ALERT: toxic ingestion risk. Consult toxicology.","risk_level":"High","confidence":"0.98"}} NOW EVALUATE: Symptoms: {symptoms} Claim: "{user_claim}" Patient: {history} Image: {"Provided" if image_b64 else "Not provided"} OUTPUT JSON: model """ if not image_b64: sink = io.StringIO() with contextlib.redirect_stdout(sink): response = model.create_completion( prompt=raw_prompt, max_tokens=350, temperature=0.1, stop=[""] ) raw = response['choices'][0]['text'] else: content = [ {"type": "text", "text": raw_prompt}, {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_b64}"}} ] sink = io.StringIO() with contextlib.redirect_stdout(sink): response = model.create_chat_completion( messages=[{"role": "user", "content": content}], max_tokens=350, temperature=0.1, repeat_penalty=1.3 ) raw = response['choices'][0]['message']['content'] parsed = parse_output(raw) if lang_code != "en": parsed['reason'] = translate_text(parsed.get('reason', ''), lang_code) parsed['doctor_summary'] = translate_text(parsed.get('doctor_summary', ''), lang_code) # ── Format output ───────────────────────── verdict = parsed.get('verdict', 'N/A').strip().strip('"') risk = parsed.get('risk_level', 'N/A').strip().strip('"') reason = parsed.get('reason', 'N/A') doc_note = parsed.get('doctor_summary', 'N/A') confidence = parsed.get('confidence', '0.0') verdict_map = { "True": "✅ TRUE", "False": "❌ FALSE", "Mixed": "⚠️ MIXED", "Insufficient Evidence": "🔍 INSUFFICIENT EVIDENCE" } risk_map = {"High": "🔴 High", "Medium": "🟡 Medium", "Low": "🟢 Low"} verdict_str = verdict_map.get(verdict, f"❓ {verdict}") risk_str = risk_map.get(risk, f"⚪ {risk}") try: conf_pct = f"{float(confidence):.0%}" except: conf_pct = confidence return ( verdict_str, risk_str, conf_pct, reason, doc_note ) # ── EXAMPLES ────────────────────────────────── EXAMPLES = [ ["Drinking Dettol cures COVID-19", "Fever, cough", 30, "Male", "No chronic illness", "English", None], ["Eating oranges gives vitamin C and boosts immunity","Fatigue, frequent colds", 25, "Female", "No known conditions", "English", None], ["Jeera water permanently cures diabetes", "High blood sugar, fatigue", 70, "Male", "Type 2 Diabetes on Insulin", "English", None], ["Turmeric milk cures pneumonia completely", "Fever, chest pain", 60, "Female", "Hypertension", "English", None], ["Drinking cold water causes cancer", "No symptoms", 40, "Male", "Generally healthy", "English", None], ] # ── GRADIO UI ───────────────────────────────── with gr.Blocks( theme=gr.themes.Soft(primary_hue="blue"), title="🛡️ MedGuard" ) as demo: gr.HTML("""

🛡️ MedGuard

AI-Powered Medical Misinformation Defense · MedGemma 4B-IT · Edge AI

⚕️ MedGemma 🚀 Edge AI 🌍 Multilingual 🩺 Triage Ready
""") with gr.Row(): # ── LEFT COLUMN ─────────────────────── with gr.Column(scale=1): gr.Markdown("### 📋 Patient Information") user_claim = gr.Textbox( label="🔍 Health Claim to Verify", placeholder="Paste a WhatsApp health claim, home remedy, or medical advice...", lines=3 ) symptoms = gr.Textbox( label="🤒 Patient Symptoms", placeholder="e.g. fever, fatigue, chest pain", lines=2 ) with gr.Row(): age = gr.Number(label="🎂 Age", value=45, minimum=1, maximum=120) gender = gr.Dropdown( label="⚧ Gender", choices=["Male", "Female", "Other"], value="Male" ) condition = gr.Textbox( label="🏥 Medical History", value="No known conditions" ) language = gr.Dropdown( label="🌍 Output Language", choices=list(LANGUAGES.keys()), value="English" ) image = gr.Image( label="🖼️ Upload X-Ray or Medical Image (Optional)", type="numpy" ) analyze_btn = gr.Button( "🛡️ Run MedGuard Analysis", variant="primary", size="lg" ) gr.Markdown("### 💡 Quick Examples") gr.Examples( examples=EXAMPLES, inputs=[user_claim, symptoms, age, gender, condition, language, image], label="Click to load" ) # ── RIGHT COLUMN ────────────────────── with gr.Column(scale=1): gr.Markdown("### 📊 MedGuard Analysis Result") verdict_out = gr.Textbox(label="⚖️ Verdict", interactive=False) risk_out = gr.Textbox(label="🚨 Risk Level", interactive=False) confidence_out = gr.Textbox(label="📊 Confidence", interactive=False) reason_out = gr.Textbox(label="🧠 Medical Reasoning", lines=6, interactive=False) doctor_out = gr.Textbox(label="👨‍⚕️ Doctor Triage Note", lines=5, interactive=False) gr.HTML("""
⚠️ MedGuard is an AI triage support tool. It does not replace qualified medical professionals. Always consult a doctor.
""") analyze_btn.click( fn=analyze, inputs=[user_claim, symptoms, age, gender, condition, language, image], outputs=[verdict_out, risk_out, confidence_out, reason_out, doctor_out] ) demo.launch()