""" G-MASS: Ghana Medical AI Safety Screen Gradio interface for open evaluation and demo use. This app is intentionally a thin UI over the production pipeline modules: models.router, scorer.scorer, and core.metrics. It does not define separate model or scorer behavior. """ from __future__ import annotations import html import json import os import re import sys import tempfile import time from contextlib import contextmanager from pathlib import Path import gradio as gr import pandas as pd import plotly.graph_objects as go from dotenv import load_dotenv try: import spaces except Exception: # pragma: no cover - spaces exists only on Hugging Face runtimes spaces = None APP_DIR = Path(__file__).resolve().parent ROOT = APP_DIR if (APP_DIR / "configs").exists() else APP_DIR.parent if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) load_dotenv(ROOT / ".env") try: from core.config import DOMAINS, FAILURE_CATEGORIES from core.metrics import full_model_profile from core.utils import ensure_dirs, load_jsonl, save_jsonl_line, utc_now from models.router import ( BIOMISTRAL_MODEL, GEMINI_MODEL, GPT4O_MODEL, PHI3_MODEL, build_prompt_with_language_instruction, call_model, ) from scorer.scorer import GMassScorer GMASS_AVAILABLE = True IMPORT_ERROR = "" except Exception as exc: # pragma: no cover - displayed in UI during bad deploys GMASS_AVAILABLE = False IMPORT_ERROR = str(exc) LANGUAGES = { "English": "english", "Ghanaian English": "ghanaian_en", "Twi": "twi", } MODEL_OPTIONS = { f"GPT-4o ({GPT4O_MODEL if GMASS_AVAILABLE else 'gpt-4o'})": "gpt4o", f"Gemini Flash ({GEMINI_MODEL if GMASS_AVAILABLE else 'gemini-2.5-flash'})": "gemini", f"Phi-3 Mini ({PHI3_MODEL if GMASS_AVAILABLE else 'microsoft/Phi-3-mini-4k-instruct'})": "phi3", f"BioMistral ({BIOMISTRAL_MODEL if GMASS_AVAILABLE else 'BioMistral/BioMistral-7B-SLERP'})": "biomistral", } REQUIRED_ENV_BY_MODEL = { "gpt4o": "OPENAI_API_KEY", "gemini": "GEMINI_API_KEY", "phi3": "HF_TOKEN", "biomistral": "HF_TOKEN", } APP_VERSION = "1.1.1" PUBLIC_METRICS_PATH = ROOT / "data" / "public_metrics" / "benchmark_summary.json" DEFAULT_RESULTS_PATH = ROOT / "data" / "eval_outputs" / "combined" / "all_models_scored.jsonl" COMMUNITY_FEEDBACK_PATH = ROOT / "data" / "community_feedback.jsonl" PROMPT_COLUMNS_BY_LANGUAGE = { "english": [ "prompt", "english_prompt", "prompt_en", "source_standard_english", "probe_en", "question_en", ], "twi": [ "prompt", "twi_prompt", "prompt_twi", "prompt_twi_validated", "final_approved_twi", "prompt_twi_draft", "probe_twi", "question_twi", ], "ghanaian_en": [ "prompt", "ghanaian_en_prompt", "gh_en_prompt", "prompt_ghanaian_en", "final_approved_ghanaian_english", "probe_gh_en", "question_gh_en", ], } LANGUAGE_ALIASES = { "en": "english", "eng": "english", "english": "english", "tw": "twi", "twi": "twi", "akan": "twi", "gh-en": "ghanaian_en", "gh_en": "ghanaian_en", "ghanaian_en": "ghanaian_en", "ghanaian english": "ghanaian_en", "ghanaian-english": "ghanaian_en", } if spaces is not None: @spaces.GPU def zerogpu_compatibility_probe(): """Satisfy ZeroGPU startup checks; G-MASS itself uses API/CPU calls.""" return "ready" else: def zerogpu_compatibility_probe(): return "ready" def _format_error_content(message: str) -> str: lines = [line.strip() for line in message.strip().split("\n")] html_parts = [] in_ul = False in_ol = False def close_lists(): nonlocal in_ul, in_ol if in_ul: html_parts.append("") in_ul = False if in_ol: html_parts.append("") in_ol = False def format_inline(text: str) -> str: escaped = html.escape(text) escaped = re.sub(r"`([^`]+)`", r"\1", escaped) escaped = re.sub(r"\*\*([^*]+)\*\*", r"\1", escaped) return escaped for line in lines: if not line: close_lists() continue ol_match = re.match(r"^(\d+)\.\s+(.*)$", line) ul_match = re.match(r"^[โ€ข\-\*]\s+(.*)$", line) if ol_match: if in_ul: html_parts.append("") in_ul = False if not in_ol: html_parts.append("
    ") in_ol = True content = format_inline(ol_match.group(2)) html_parts.append(f"
  1. {content}
  2. ") elif ul_match: if in_ol: html_parts.append("
") in_ol = False if not in_ul: html_parts.append("