Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import json | |
| # ββ helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def chip(text: str, color: str = "#94a3b8") -> str: | |
| return ( | |
| f'<span style="display:inline-block;padding:2px 10px;border-radius:12px;' | |
| f'background:rgba(255,255,255,0.1);color:{color};font-size:0.75rem;' | |
| f'font-weight:600;letter-spacing:0.04em;margin:2px 3px;">{text}</span>' | |
| ) | |
| def badge(label: str, bg: str, color: str) -> str: | |
| return ( | |
| f'<span style="display:inline-block;padding:4px 14px;border-radius:20px;' | |
| f'background:{bg};color:{color};font-size:0.82rem;font-weight:700;' | |
| f'letter-spacing:0.05em;">{label}</span>' | |
| ) | |
| def acmg_badge(acmg_class: str) -> str: | |
| mapping = { | |
| "Pathogenic": ("rgba(239,68,68,0.35)", "#fca5a5"), | |
| "Likely Pathogenic":("rgba(249,115,22,0.35)", "#fdba74"), | |
| "VUS": ("rgba(234,179,8,0.35)", "#fde047"), | |
| "Likely Benign": ("rgba(34,197,94,0.35)", "#86efac"), | |
| "Benign": ("rgba(16,185,129,0.35)", "#6ee7b7"), | |
| } | |
| bg, color = mapping.get(acmg_class, ("rgba(100,100,120,0.35)", "#cbd5e1")) | |
| return badge(acmg_class, bg, color) | |
| def card_bg(acmg_class: str) -> str: | |
| mapping = { | |
| "Pathogenic": "rgba(239,68,68,0.12)", | |
| "Likely Pathogenic": "rgba(249,115,22,0.12)", | |
| "VUS": "rgba(234,179,8,0.10)", | |
| "Likely Benign": "rgba(34,197,94,0.10)", | |
| "Benign": "rgba(16,185,129,0.10)", | |
| } | |
| return mapping.get(acmg_class, "rgba(30,30,50,0.50)") | |
| def border_color(acmg_class: str) -> str: | |
| mapping = { | |
| "Pathogenic": "rgba(239,68,68,0.55)", | |
| "Likely Pathogenic": "rgba(249,115,22,0.55)", | |
| "VUS": "rgba(234,179,8,0.55)", | |
| "Likely Benign": "rgba(34,197,94,0.55)", | |
| "Benign": "rgba(16,185,129,0.55)", | |
| } | |
| return mapping.get(acmg_class, "rgba(148,163,184,0.30)") | |
| def render_variant_card( | |
| patient_name: str, | |
| gene: str, | |
| variant_notation: str, | |
| chromosome: str, | |
| position: str, | |
| zygosity: str, | |
| acmg_class: str, | |
| criteria_met: list, | |
| population_af: str, | |
| clinical_significance: str, | |
| recommended_action: str, | |
| pathogenicity_reasoning: str = "", | |
| population_assessment: str = "", | |
| ) -> str: | |
| criteria_chips = "".join(chip(c, "#a5f3fc") for c in criteria_met) | |
| bg = card_bg(acmg_class) | |
| border = border_color(acmg_class) | |
| pat_section = ( | |
| f'<div style="margin-bottom:6px;color:#94a3b8;font-size:0.82rem;">' | |
| f'Patient: <span style="color:#e2e8f0;font-weight:600;">{patient_name}</span></div>' | |
| if patient_name else "" | |
| ) | |
| reasoning_section = ( | |
| f'<div style="margin-top:10px;padding:10px 14px;border-radius:8px;' | |
| f'background:rgba(255,255,255,0.05);color:#94a3b8;font-size:0.82rem;line-height:1.6;">' | |
| f'<span style="color:#a5f3fc;font-weight:600;">Pathogenicity Reasoning:</span> {pathogenicity_reasoning}</div>' | |
| if pathogenicity_reasoning else "" | |
| ) | |
| pop_section = ( | |
| f'<div style="margin-top:6px;color:#94a3b8;font-size:0.82rem;">' | |
| f'<span style="color:#fcd34d;font-weight:600;">Population Assessment:</span> {population_assessment}</div>' | |
| if population_assessment else "" | |
| ) | |
| return f""" | |
| <div style="background:{bg};border:1px solid {border};border-radius:14px; | |
| padding:20px 24px;margin-bottom:20px;font-family:'Inter',sans-serif;"> | |
| {pat_section} | |
| <div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;margin-bottom:14px;"> | |
| <div> | |
| <span style="font-size:1.25rem;font-weight:700;color:#f1f5f9;">{gene}</span> | |
| <span style="color:#94a3b8;font-size:0.9rem;margin-left:10px;">{variant_notation}</span> | |
| </div> | |
| {acmg_badge(acmg_class)} | |
| </div> | |
| <div style="display:flex;flex-wrap:wrap;gap:18px;margin-bottom:14px;"> | |
| <div style="color:#94a3b8;font-size:0.82rem;"> | |
| Chromosome: <span style="color:#c4b5fd;font-weight:600;">{chromosome}</span> | |
| </div> | |
| <div style="color:#94a3b8;font-size:0.82rem;"> | |
| Position: <span style="color:#c4b5fd;font-weight:600;">{position}</span> | |
| </div> | |
| <div style="color:#94a3b8;font-size:0.82rem;"> | |
| Zygosity: <span style="color:#c4b5fd;font-weight:600;">{zygosity}</span> | |
| </div> | |
| <div style="color:#94a3b8;font-size:0.82rem;"> | |
| Population AF: <span style="color:#fcd34d;font-weight:600;">{population_af}</span> | |
| </div> | |
| </div> | |
| <div style="margin-bottom:10px;"> | |
| <span style="color:#94a3b8;font-size:0.8rem;font-weight:600;text-transform:uppercase; | |
| letter-spacing:0.06em;">ACMG Criteria Met</span> | |
| <div style="margin-top:6px;">{criteria_chips}</div> | |
| </div> | |
| <div style="padding:12px 16px;border-radius:10px;background:rgba(0,0,0,0.25);margin-bottom:12px;"> | |
| <div style="color:#7dd3fc;font-size:0.8rem;font-weight:600;text-transform:uppercase; | |
| letter-spacing:0.06em;margin-bottom:6px;">Clinical Significance</div> | |
| <div style="color:#e2e8f0;font-size:0.9rem;line-height:1.6;">{clinical_significance}</div> | |
| </div> | |
| <div style="padding:12px 16px;border-radius:10px;background:rgba(0,0,0,0.25);"> | |
| <div style="color:#86efac;font-size:0.8rem;font-weight:600;text-transform:uppercase; | |
| letter-spacing:0.06em;margin-bottom:6px;">Recommended Action</div> | |
| <div style="color:#e2e8f0;font-size:0.9rem;line-height:1.6;">{recommended_action}</div> | |
| </div> | |
| {reasoning_section} | |
| {pop_section} | |
| </div> | |
| """ | |
| # ββ pre-computed demo cases βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| DEMO_CASES = [ | |
| dict( | |
| patient_name="", | |
| gene="BRCA1", | |
| variant_notation="c.5266dupC (p.Gln1756ProfsTer74)", | |
| chromosome="chr17", | |
| position="41,245,466", | |
| zygosity="Heterozygous", | |
| acmg_class="Pathogenic", | |
| criteria_met=["PVS1", "PS3", "PM2", "PP3"], | |
| population_af="0.0001 (gnomAD)", | |
| clinical_significance=( | |
| "Frameshift causing premature stop codon at position 1756. " | |
| "Pathogenic for Hereditary Breast and Ovarian Cancer (HBOC) syndrome. " | |
| "Loss of BRCA1 function disrupts homologous recombination DNA repair." | |
| ), | |
| recommended_action=( | |
| "Refer to certified genetic counselor. Discuss prophylactic risk-reduction " | |
| "surgery options (bilateral mastectomy / salpingo-oophorectomy). " | |
| "Cascade testing of first-degree relatives strongly recommended." | |
| ), | |
| ), | |
| dict( | |
| patient_name="", | |
| gene="CFTR", | |
| variant_notation="c.1521_1523delCTT (p.Phe508del)", | |
| chromosome="chr7", | |
| position="117,548,628", | |
| zygosity="Homozygous", | |
| acmg_class="Pathogenic", | |
| criteria_met=["PVS1", "PS1", "PM3", "PP5"], | |
| population_af="0.0139 (carrier frequency)", | |
| clinical_significance=( | |
| "Most common Cystic Fibrosis-causing variant (β70% of CF alleles). " | |
| "Homozygous state is consistent with classic Cystic Fibrosis. " | |
| "Causes misfolding and premature degradation of CFTR protein." | |
| ), | |
| recommended_action=( | |
| "Diagnose Cystic Fibrosis. Refer to accredited CF Center. " | |
| "Initiate evaluation for CFTR modulator therapy (e.g., Elexacaftor/Tezacaftor/Ivacaftor). " | |
| "Baseline pulmonary function tests, sweat chloride, and pancreatic assessment." | |
| ), | |
| ), | |
| dict( | |
| patient_name="", | |
| gene="ATM", | |
| variant_notation="c.7271T>G (p.Val2424Gly)", | |
| chromosome="chr11", | |
| position="108,123,551", | |
| zygosity="Heterozygous", | |
| acmg_class="VUS", | |
| criteria_met=["PM1", "PM2", "PP3"], | |
| population_af="0.000089", | |
| clinical_significance=( | |
| "Missense variant located in the kinase domain of ATM. " | |
| "Functional studies remain inconclusive. PM1 supported by critical domain location; " | |
| "however, evidence is insufficient for pathogenic or benign classification at this time." | |
| ), | |
| recommended_action=( | |
| "Variant of Uncertain Significance β cannot be used for independent clinical decision-making. " | |
| "Recommend periodic reclassification as new functional data and population studies emerge. " | |
| "Consider segregation studies in affected family members." | |
| ), | |
| ), | |
| dict( | |
| patient_name="", | |
| gene="MLH1", | |
| variant_notation="c.116+5G>A", | |
| chromosome="chr3", | |
| position="37,034,801", | |
| zygosity="Heterozygous", | |
| acmg_class="Likely Benign", | |
| criteria_met=["BP4", "BP7", "BA1 (partial)"], | |
| population_af="0.0042", | |
| clinical_significance=( | |
| "Intronic splice-region variant. Multiple in silico computational tools " | |
| "(SpliceSiteFinder, MaxEntScan, NNSPLICE) predict no significant impact on splicing. " | |
| "Observed at appreciable frequency in the general population." | |
| ), | |
| recommended_action=( | |
| "Likely benign variant. No clinical action required based on this variant alone. " | |
| "Document in medical record. Re-evaluate if new evidence emerges or in context of " | |
| "strong family history of Lynch Syndrome." | |
| ), | |
| ), | |
| ] | |
| def build_demo_html() -> str: | |
| header = """ | |
| <div style="font-family:'Inter',sans-serif;padding:4px 0 18px;"> | |
| <div style="color:#a5f3fc;font-size:0.78rem;font-weight:600;text-transform:uppercase; | |
| letter-spacing:0.08em;margin-bottom:4px;">Pre-Computed Analysis Β· No API Key Required</div> | |
| <div style="color:#94a3b8;font-size:0.85rem;"> | |
| Four representative genomic variants illustrating the full ACMG classification spectrum. | |
| </div> | |
| </div> | |
| """ | |
| cards = "".join(render_variant_card(**c) for c in DEMO_CASES) | |
| return header + cards | |
| # ββ GPT-4o-mini classification ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| SYSTEM_PROMPT = """You are an expert clinical genomics scientist specializing in ACMG/AMP variant | |
| classification guidelines (2015 and 2019 ClinGen updates). Given a genomic variant and clinical | |
| context, perform a structured classification and return ONLY valid JSON with these keys: | |
| - acmg_class: one of "Pathogenic", "Likely Pathogenic", "VUS", "Likely Benign", "Benign" | |
| - criteria_met: array of ACMG criteria strings (e.g. ["PVS1","PM2","PP3"]) | |
| - clinical_significance: 2-4 sentence clinical interpretation | |
| - recommended_action: 2-4 sentence clinical recommendation | |
| - population_assessment: 1-2 sentence assessment of population frequency significance | |
| - pathogenicity_reasoning: 2-3 sentence detailed molecular reasoning | |
| Be precise, evidence-based, and medically accurate.""" | |
| def classify_variant( | |
| patient_name, gene, chromosome, position, ref_allele, alt_allele, | |
| zygosity, population_af, indication, panel_type, api_key | |
| ): | |
| if not api_key or not api_key.strip(): | |
| return "<div style='color:#fca5a5;padding:20px;font-family:Inter,sans-serif;'>Please enter your OpenAI API key in the field above.</div>" | |
| if not gene or not gene.strip(): | |
| return "<div style='color:#fcd34d;padding:20px;font-family:Inter,sans-serif;'>Please enter a gene name (e.g. BRCA1, TP53, CFTR).</div>" | |
| user_message = f"""Classify the following genomic variant: | |
| Gene: {gene.strip()} | |
| Chromosome: {chromosome} | |
| Position: {int(position) if position else 'Unknown'} | |
| Reference Allele: {ref_allele or 'N/A'} | |
| Alt Allele: {alt_allele or 'N/A'} | |
| Zygosity: {zygosity} | |
| Population AF: {population_af:.6f} | |
| Clinical Indication / Phenotype: {indication or 'Not specified'} | |
| Genetic Panel: {panel_type or 'Not specified'} | |
| Apply ACMG/AMP 2015 classification criteria. Return JSON only.""" | |
| try: | |
| from openai import OpenAI | |
| client = OpenAI(api_key=api_key.strip()) | |
| response = client.chat.completions.create( | |
| model="gpt-4o-mini", | |
| messages=[ | |
| {"role": "system", "content": SYSTEM_PROMPT}, | |
| {"role": "user", "content": user_message}, | |
| ], | |
| temperature=0.1, | |
| response_format={"type": "json_object"}, | |
| ) | |
| raw = response.choices[0].message.content | |
| data = json.loads(raw) | |
| except Exception as e: | |
| err = str(e) | |
| return ( | |
| f"<div style='color:#fca5a5;padding:20px 24px;border-radius:12px;" | |
| f"background:rgba(239,68,68,0.15);font-family:Inter,sans-serif;'>" | |
| f"<strong>Error:</strong> {err}</div>" | |
| ) | |
| variant_notation = f"{ref_allele or '?'}>{alt_allele or '?'}" | |
| return render_variant_card( | |
| patient_name=patient_name or "", | |
| gene=gene.strip(), | |
| variant_notation=variant_notation, | |
| chromosome=chromosome, | |
| position=str(int(position)) if position else "N/A", | |
| zygosity=zygosity, | |
| acmg_class=data.get("acmg_class", "VUS"), | |
| criteria_met=data.get("criteria_met", []), | |
| population_af=f"{population_af:.6f}", | |
| clinical_significance=data.get("clinical_significance", ""), | |
| recommended_action=data.get("recommended_action", ""), | |
| pathogenicity_reasoning=data.get("pathogenicity_reasoning", ""), | |
| population_assessment=data.get("population_assessment", ""), | |
| ) | |
| # ββ How It Works content ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| HOW_IT_WORKS_HTML = """ | |
| <div style="font-family:'Inter',sans-serif;color:#e2e8f0;max-width:860px;margin:0 auto;"> | |
| <!-- Workflow link --> | |
| <div style="background:rgba(99,102,241,0.18);border:1px solid rgba(99,102,241,0.45); | |
| border-radius:12px;padding:16px 22px;margin-bottom:28px;display:flex; | |
| align-items:center;gap:14px;flex-wrap:wrap;"> | |
| <div style="font-size:1.5rem;">π</div> | |
| <div> | |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.95rem;margin-bottom:3px;"> | |
| n8n Automation Workflow | |
| </div> | |
| <a href="https://aravind5.app.n8n.cloud/workflow/PLACEHOLDER_GENOMIC" | |
| target="_blank" | |
| style="color:#818cf8;font-size:0.85rem;word-break:break-all;"> | |
| https://aravind5.app.n8n.cloud/workflow/PLACEHOLDER_GENOMIC | |
| </a> | |
| </div> | |
| </div> | |
| <!-- Architecture --> | |
| <div style="margin-bottom:28px;"> | |
| <div style="color:#a5f3fc;font-size:0.78rem;font-weight:700;text-transform:uppercase; | |
| letter-spacing:0.08em;margin-bottom:14px;">n8n Workflow Architecture</div> | |
| <div style="display:flex;flex-direction:column;gap:10px;"> | |
| <!-- steps --> | |
| <div style="background:rgba(30,30,50,0.55);border:1px solid rgba(148,163,184,0.15); | |
| border-radius:10px;padding:14px 18px;display:flex;gap:14px;align-items:flex-start;"> | |
| <div style="background:rgba(99,102,241,0.35);color:#a5b4fc;font-weight:700; | |
| border-radius:8px;padding:4px 10px;font-size:0.8rem;white-space:nowrap;">Step 1</div> | |
| <div> | |
| <div style="color:#f1f5f9;font-weight:600;margin-bottom:3px;">Webhook Trigger</div> | |
| <div style="color:#94a3b8;font-size:0.85rem;">Receives variant data payload (gene, position, zygosity, clinical indication) from external LIMS or EHR system via HTTP POST.</div> | |
| </div> | |
| </div> | |
| <div style="background:rgba(30,30,50,0.55);border:1px solid rgba(148,163,184,0.15); | |
| border-radius:10px;padding:14px 18px;display:flex;gap:14px;align-items:flex-start;"> | |
| <div style="background:rgba(34,197,94,0.25);color:#86efac;font-weight:700; | |
| border-radius:8px;padding:4px 10px;font-size:0.8rem;white-space:nowrap;">Step 2</div> | |
| <div> | |
| <div style="color:#f1f5f9;font-weight:600;margin-bottom:3px;">Database Enrichment</div> | |
| <div style="color:#94a3b8;font-size:0.85rem;">Queries ClinVar, gnomAD, and ClinGen APIs for prior classification, population frequency data, and functional evidence annotations.</div> | |
| </div> | |
| </div> | |
| <div style="background:rgba(30,30,50,0.55);border:1px solid rgba(148,163,184,0.15); | |
| border-radius:10px;padding:14px 18px;display:flex;gap:14px;align-items:flex-start;"> | |
| <div style="background:rgba(249,115,22,0.25);color:#fdba74;font-weight:700; | |
| border-radius:8px;padding:4px 10px;font-size:0.8rem;white-space:nowrap;">Step 3</div> | |
| <div> | |
| <div style="color:#f1f5f9;font-weight:600;margin-bottom:3px;">GPT-4o-mini ACMG Classifier</div> | |
| <div style="color:#94a3b8;font-size:0.85rem;">Sends enriched variant context to GPT-4o-mini with structured ACMG/AMP prompt. Returns JSON classification with criteria evidence codes and clinical narrative.</div> | |
| </div> | |
| </div> | |
| <div style="background:rgba(30,30,50,0.55);border:1px solid rgba(148,163,184,0.15); | |
| border-radius:10px;padding:14px 18px;display:flex;gap:14px;align-items:flex-start;"> | |
| <div style="background:rgba(239,68,68,0.25);color:#fca5a5;font-weight:700; | |
| border-radius:8px;padding:4px 10px;font-size:0.8rem;white-space:nowrap;">Step 4</div> | |
| <div> | |
| <div style="color:#f1f5f9;font-weight:600;margin-bottom:3px;">Report Generation & Delivery</div> | |
| <div style="color:#94a3b8;font-size:0.85rem;">Generates structured clinical report, logs to Google Sheets for audit trail, and routes high-severity Pathogenic findings to clinical team via email/Slack alert.</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ACMG Criteria Table --> | |
| <div> | |
| <div style="color:#a5f3fc;font-size:0.78rem;font-weight:700;text-transform:uppercase; | |
| letter-spacing:0.08em;margin-bottom:14px;">ACMG/AMP Classification Criteria Reference</div> | |
| <div style="overflow-x:auto;"> | |
| <table style="width:100%;border-collapse:collapse;font-size:0.85rem;"> | |
| <thead> | |
| <tr style="background:rgba(99,102,241,0.20);"> | |
| <th style="padding:10px 14px;text-align:left;color:#a5b4fc;font-weight:700; | |
| border-bottom:1px solid rgba(148,163,184,0.2);">Code</th> | |
| <th style="padding:10px 14px;text-align:left;color:#a5b4fc;font-weight:700; | |
| border-bottom:1px solid rgba(148,163,184,0.2);">Strength</th> | |
| <th style="padding:10px 14px;text-align:left;color:#a5b4fc;font-weight:700; | |
| border-bottom:1px solid rgba(148,163,184,0.2);">Direction</th> | |
| <th style="padding:10px 14px;text-align:left;color:#a5b4fc;font-weight:700; | |
| border-bottom:1px solid rgba(148,163,184,0.2);">Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr style="border-bottom:1px solid rgba(148,163,184,0.1);"> | |
| <td style="padding:9px 14px;color:#fca5a5;font-weight:700;">PVS1</td> | |
| <td style="padding:9px 14px;color:#94a3b8;">Very Strong</td> | |
| <td style="padding:9px 14px;color:#fca5a5;">Pathogenic</td> | |
| <td style="padding:9px 14px;color:#cbd5e1;">Null variant (frameshift, nonsense, splice Β±1/2) in gene where LOF is disease mechanism</td> | |
| </tr> | |
| <tr style="background:rgba(255,255,255,0.03);border-bottom:1px solid rgba(148,163,184,0.1);"> | |
| <td style="padding:9px 14px;color:#fca5a5;font-weight:700;">PS1βPS4</td> | |
| <td style="padding:9px 14px;color:#94a3b8;">Strong</td> | |
| <td style="padding:9px 14px;color:#fca5a5;">Pathogenic</td> | |
| <td style="padding:9px 14px;color:#cbd5e1;">Same AA change as established pathogenic; functional studies; de novo; prevalence increase in affected</td> | |
| </tr> | |
| <tr style="border-bottom:1px solid rgba(148,163,184,0.1);"> | |
| <td style="padding:9px 14px;color:#fdba74;font-weight:700;">PM1βPM6</td> | |
| <td style="padding:9px 14px;color:#94a3b8;">Moderate</td> | |
| <td style="padding:9px 14px;color:#fdba74;">Pathogenic</td> | |
| <td style="padding:9px 14px;color:#cbd5e1;">Critical domain; absent in population; cosegregation; in trans with pathogenic; de novo (unconfirmed)</td> | |
| </tr> | |
| <tr style="background:rgba(255,255,255,0.03);border-bottom:1px solid rgba(148,163,184,0.1);"> | |
| <td style="padding:9px 14px;color:#fde047;font-weight:700;">PP1βPP5</td> | |
| <td style="padding:9px 14px;color:#94a3b8;">Supporting</td> | |
| <td style="padding:9px 14px;color:#fde047;">Pathogenic</td> | |
| <td style="padding:9px 14px;color:#cbd5e1;">Cosegregation; functional evidence; reputable source (ClinVar); in trans with pathogenic; in cis pathogenic</td> | |
| </tr> | |
| <tr style="border-bottom:1px solid rgba(148,163,184,0.1);"> | |
| <td style="padding:9px 14px;color:#6ee7b7;font-weight:700;">BA1</td> | |
| <td style="padding:9px 14px;color:#94a3b8;">Stand-Alone</td> | |
| <td style="padding:9px 14px;color:#6ee7b7;">Benign</td> | |
| <td style="padding:9px 14px;color:#cbd5e1;">Allele frequency >5% in population databases (gnomAD, 1000G, ESP)</td> | |
| </tr> | |
| <tr style="background:rgba(255,255,255,0.03);border-bottom:1px solid rgba(148,163,184,0.1);"> | |
| <td style="padding:9px 14px;color:#86efac;font-weight:700;">BS1βBS4</td> | |
| <td style="padding:9px 14px;color:#94a3b8;">Strong</td> | |
| <td style="padding:9px 14px;color:#86efac;">Benign</td> | |
| <td style="padding:9px 14px;color:#cbd5e1;">Allele frequency greater than expected; benign functional studies; nonsegregation; in trans VUS</td> | |
| </tr> | |
| <tr style="border-bottom:1px solid rgba(148,163,184,0.1);"> | |
| <td style="padding:9px 14px;color:#86efac;font-weight:700;">BP1βBP7</td> | |
| <td style="padding:9px 14px;color:#94a3b8;">Supporting</td> | |
| <td style="padding:9px 14px;color:#86efac;">Benign</td> | |
| <td style="padding:9px 14px;color:#cbd5e1;">Missense in gene with only truncating pathogenic; silent with no splice impact; in trans pathogenic; multiple lines benign computational</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Disclaimer --> | |
| <div style="margin-top:28px;padding:14px 18px;border-radius:10px; | |
| background:rgba(234,179,8,0.12);border:1px solid rgba(234,179,8,0.30);"> | |
| <div style="color:#fde047;font-weight:700;font-size:0.8rem;margin-bottom:4px;"> | |
| CLINICAL DISCLAIMER | |
| </div> | |
| <div style="color:#94a3b8;font-size:0.82rem;line-height:1.6;"> | |
| This tool is intended for research and educational purposes only. Classifications generated | |
| by AI should not be used as the sole basis for clinical decision-making. All variant | |
| classifications must be reviewed and confirmed by a board-certified clinical geneticist or | |
| molecular pathologist before clinical application. | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| # ββ Gradio UI βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CUSTOM_CSS = """ | |
| body, .gradio-container { | |
| background: #0f0f1a !important; | |
| font-family: 'Inter', sans-serif !important; | |
| } | |
| .gr-panel, .panel, .block { | |
| background: transparent !important; | |
| border: none !important; | |
| } | |
| .gr-box { | |
| background: rgba(30,30,50,0.5) !important; | |
| border: 1px solid rgba(148,163,184,0.2) !important; | |
| border-radius: 10px !important; | |
| } | |
| label, .gr-input-label { | |
| color: #94a3b8 !important; | |
| font-size: 0.82rem !important; | |
| font-weight: 600 !important; | |
| } | |
| input, textarea, select { | |
| background: rgba(15,15,30,0.8) !important; | |
| color: #e2e8f0 !important; | |
| border: 1px solid rgba(148,163,184,0.25) !important; | |
| border-radius: 8px !important; | |
| } | |
| button.primary { | |
| background: linear-gradient(135deg, rgba(99,102,241,0.8), rgba(139,92,246,0.8)) !important; | |
| color: #f1f5f9 !important; | |
| border: 1px solid rgba(139,92,246,0.5) !important; | |
| border-radius: 10px !important; | |
| font-weight: 700 !important; | |
| } | |
| .tab-nav button { | |
| color: #94a3b8 !important; | |
| background: transparent !important; | |
| } | |
| .tab-nav button.selected { | |
| color: #a5f3fc !important; | |
| border-bottom: 2px solid #a5f3fc !important; | |
| } | |
| """ | |
| HEADER_HTML = """ | |
| <div style="font-family:'Inter',sans-serif;padding:20px 0 8px;text-align:center;"> | |
| <div style="font-size:2rem;font-weight:800; | |
| background:linear-gradient(135deg,#a5f3fc,#818cf8,#c084fc); | |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent; | |
| background-clip:text;margin-bottom:8px;"> | |
| Genomic Variant Clinical Significance Classifier | |
| </div> | |
| <div style="color:#94a3b8;font-size:0.9rem;max-width:600px;margin:0 auto;"> | |
| ACMG/AMP 2015 guideline-based variant classification powered by GPT-4o-mini | |
| </div> | |
| </div> | |
| """ | |
| with gr.Blocks(theme=gr.themes.Soft(), css=CUSTOM_CSS, title="Genomic Variant Classifier") as demo: | |
| gr.HTML(HEADER_HTML) | |
| with gr.Tabs(): | |
| # ββ TAB 1: Live Demo βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Tab("Live Demo"): | |
| gr.HTML(build_demo_html()) | |
| # ββ TAB 2: Classify Variant ββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Tab("Classify Variant"): | |
| gr.HTML( | |
| '<div style="font-family:Inter,sans-serif;color:#94a3b8;font-size:0.85rem;' | |
| 'padding:6px 0 16px;">Enter variant details below and provide your OpenAI API key to run live ACMG classification.</div>' | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| api_key = gr.Textbox( | |
| type="password", | |
| label="OpenAI API Key", | |
| placeholder="sk-...", | |
| ) | |
| patient_name = gr.Textbox( | |
| label="Patient Name / ID (optional)", | |
| placeholder="e.g. Patient_001", | |
| ) | |
| gene = gr.Textbox( | |
| label="Gene Symbol", | |
| placeholder="e.g. BRCA1, TP53, CFTR", | |
| ) | |
| chromosome = gr.Dropdown( | |
| label="Chromosome", | |
| choices=[ | |
| "chr1","chr2","chr3","chr4","chr5","chr6", | |
| "chr7","chr8","chr9","chr10","chr11","chr12", | |
| "chr13","chr14","chr15","chr16","chr17","chr18", | |
| "chr19","chr20","chr21","chr22","chrX","chrY", | |
| ], | |
| value="chr17", | |
| ) | |
| position = gr.Number( | |
| label="Genomic Position (GRCh38)", | |
| value=41245466, | |
| precision=0, | |
| ) | |
| ref_allele = gr.Textbox( | |
| label="Reference Allele", | |
| placeholder="e.g. A, CTTT", | |
| ) | |
| alt_allele = gr.Textbox( | |
| label="Alternate Allele", | |
| placeholder="e.g. G, -", | |
| ) | |
| zygosity = gr.Radio( | |
| label="Zygosity", | |
| choices=["Heterozygous", "Homozygous"], | |
| value="Heterozygous", | |
| ) | |
| population_af = gr.Slider( | |
| minimum=0, | |
| maximum=0.05, | |
| step=0.0001, | |
| value=0.0001, | |
| label="Population Allele Frequency (gnomAD)", | |
| ) | |
| indication = gr.Textbox( | |
| label="Clinical Indication / Phenotype", | |
| placeholder="e.g. Hereditary breast/ovarian cancer, Lynch Syndrome", | |
| ) | |
| panel_type = gr.Textbox( | |
| label="Genetic Panel / Test Name", | |
| placeholder="e.g. BRCA1/2 Panel, Hereditary Cancer 47-gene", | |
| ) | |
| classify_btn = gr.Button("Classify Variant", variant="primary") | |
| with gr.Column(scale=1): | |
| result_html = gr.HTML( | |
| value=( | |
| '<div style="font-family:Inter,sans-serif;color:#475569;' | |
| 'padding:40px 20px;text-align:center;border:1px dashed rgba(148,163,184,0.2);' | |
| 'border-radius:14px;background:rgba(30,30,50,0.3);">' | |
| 'Classification result will appear here</div>' | |
| ) | |
| ) | |
| classify_btn.click( | |
| fn=classify_variant, | |
| inputs=[ | |
| patient_name, gene, chromosome, position, | |
| ref_allele, alt_allele, zygosity, population_af, | |
| indication, panel_type, api_key, | |
| ], | |
| outputs=result_html, | |
| ) | |
| # ββ TAB 3: How It Works ββββββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Tab("How It Works"): | |
| gr.HTML(HOW_IT_WORKS_HTML) | |
| if __name__ == "__main__": | |
| demo.launch() | |