Spaces:
Running on Zero
Running on Zero
| """MedGemma/TxGemma adapters plus auditable deterministic fallbacks.""" | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import re | |
| from huggingface_hub import InferenceClient | |
| WOUND_TARGETS = { | |
| "FGF2": ("down", ["fibroblast growth factor", "fgf2"]), | |
| "VEGFA": ("down", ["vascular endothelial growth factor", "vegfa", "angiogenesis"]), | |
| "IL8": ("up", ["interleukin-8", "il-8", "il8"]), | |
| "TNF": ("up", ["tumor necrosis factor", "tnf-alpha", "tnf"]), | |
| "MMP9": ("up", ["matrix metalloproteinase-9", "mmp-9", "mmp9"]), | |
| "TGFB1": ("context-dependent", ["transforming growth factor beta", "tgf-beta", "tgfb1"]), | |
| "HIF1A": ("down", ["hypoxia-inducible factor", "hif-1", "hif1a"]), | |
| "EGF": ("down", ["epidermal growth factor", " egf"]), | |
| } | |
| def _extract_json(text: str): | |
| match = re.search(r"\[.*\]", text, re.DOTALL) | |
| return json.loads(match.group(0)) if match else None | |
| def medgemma_targets(disease: str, articles: list[dict], max_targets: int = 6, custom_prompt: str = "") -> tuple[list[dict], str]: | |
| endpoint = os.getenv("MEDGEMMA_ENDPOINT") | |
| token = os.getenv("HF_TOKEN") | |
| context = "\n\n".join(f"PMID {a['pmid']}: {a['title']}\n{a['abstract'][:1800]}" for a in articles[:12]) | |
| if endpoint: | |
| prompt = f"""{custom_prompt or 'Extract disease-relevant molecular targets and their direction in disease.'} | |
| Disease or phenotype: {disease}. | |
| Return only a JSON array. Each object must contain target (official gene symbol), disease_direction (up/down/context-dependent/unknown), confidence (0-1), pmids (array), and evidence_sentence. Never invent a PMID or claim. | |
| EVIDENCE:\n{context}""" | |
| try: | |
| client = InferenceClient(base_url=endpoint, token=token) | |
| result = client.chat.completions.create(messages=[{"role": "user", "content": prompt}], max_tokens=1200, temperature=0.1) | |
| parsed = _extract_json(result.choices[0].message.content) | |
| if parsed: | |
| return parsed[:max_targets], "MedGemma endpoint" | |
| except Exception: | |
| pass | |
| corpus = " ".join(f"{a['title']} {a['abstract']}" for a in articles).lower() | |
| ranked = [] | |
| for symbol, (direction, aliases) in WOUND_TARGETS.items(): | |
| count = sum(corpus.count(alias) for alias in aliases) | |
| if count: | |
| supporting = [a for a in articles if any(alias in (a["title"] + " " + a["abstract"]).lower() for alias in aliases)] | |
| ranked.append({"target": symbol, "disease_direction": direction, "confidence": min(0.82, 0.38 + 0.07 * count), "pmids": [a["pmid"] for a in supporting[:4]], "evidence_sentence": f"Mentioned in {len(supporting)} retrieved PubMed records; direction is a teaching prior requiring review."}) | |
| ranked.sort(key=lambda x: x["confidence"], reverse=True) | |
| if not ranked: | |
| ranked = [{"target": "VEGFA", "disease_direction": "down", "confidence": 0.25, "pmids": [], "evidence_sentence": "Fallback hypothesis; insufficient directional evidence retrieved."}] | |
| return ranked[:max_targets], "transparent literature heuristic" | |
| def txgemma_note(row: dict) -> tuple[str, str]: | |
| endpoint = os.getenv("TXGEMMA_ENDPOINT") | |
| token = os.getenv("HF_TOKEN") | |
| if endpoint: | |
| try: | |
| client = InferenceClient(base_url=endpoint, token=token) | |
| prompt = f"Assess this therapeutic hypothesis cautiously in <=45 words. Separate measured evidence from inference: {json.dumps(row, default=str)}" | |
| result = client.chat.completions.create(messages=[{"role": "user", "content": prompt}], max_tokens=100, temperature=0.1) | |
| return result.choices[0].message.content, "TxGemma endpoint" | |
| except Exception: | |
| pass | |
| return "Measured ChEMBL target activity supports molecular interaction; disease-specific direction, exposure, safety, and wound efficacy remain unproven.", "rules-based interpretation" | |