Spaces:
Sleeping
Sleeping
| import json | |
| import math | |
| import time | |
| import hashlib | |
| from pathlib import Path | |
| from datetime import datetime, timezone | |
| from typing import Any, Dict, List, Tuple | |
| import gradio as gr | |
| from huggingface_hub import HfApi, hf_hub_download | |
| # ----------------------------------------------------------------------------- | |
| # CROVIA — CEP Terminal v2.0.0 (PRISM CONSOLE + OMISSION ORACLE) | |
| # Custom HTML/CSS/JS frontend, Gradio backend + events | |
| # Global Standard Release - EU AI Act Compliance | |
| # ----------------------------------------------------------------------------- | |
| CEP_DATASET_ID = "Crovia/cep-capsules" | |
| OMISSIONS_DATASET_ID = "Crovia/global-ai-training-omissions" | |
| OPEN_DEMO_MODE = True | |
| ENGINE_VERSION = "2.0.0" | |
| _CAPSULE_LIST_CACHE = {"ts": 0.0, "items": []} | |
| _CAPSULE_LIST_TTL_SEC = 300 # 5 min | |
| _OMISSIONS_CACHE = {"ts": 0.0, "data": {}} | |
| # ============================================================================= | |
| # OMISSION ORACLE v2.0.0 - Shadow Score Engine | |
| # ============================================================================= | |
| NECESSITY_CANON = { | |
| "NEC#1": {"name": "Missing data provenance", "severity": 75, "category": "provenance"}, | |
| "NEC#2": {"name": "Missing license attribution", "severity": 80, "category": "license"}, | |
| "NEC#7": {"name": "Missing usage scope", "severity": 45, "category": "scope"}, | |
| "NEC#10": {"name": "Missing temporal validity", "severity": 40, "category": "validity"}, | |
| "NEC#13": {"name": "Missing accountable entity", "severity": 70, "category": "identity"}, | |
| } | |
| def _load_omissions_ranking() -> Dict[str, Any]: | |
| """Load global omissions ranking from HuggingFace.""" | |
| now = time.time() | |
| if (now - _OMISSIONS_CACHE["ts"]) < _CAPSULE_LIST_TTL_SEC and _OMISSIONS_CACHE["data"]: | |
| return _OMISSIONS_CACHE["data"] | |
| try: | |
| path = hf_hub_download( | |
| repo_id=OMISSIONS_DATASET_ID, | |
| filename="EVIDENCE.json", | |
| repo_type="dataset", | |
| ) | |
| with open(path, "r", encoding="utf-8") as f: | |
| data = json.load(f) | |
| _OMISSIONS_CACHE["ts"] = now | |
| _OMISSIONS_CACHE["data"] = data | |
| return data | |
| except Exception: | |
| return {} | |
| def analyze_model_shadow_score(model_data: Dict[str, Any]) -> Dict[str, Any]: | |
| """ | |
| Omission Oracle: Analyze model for NEC# violations and compute Shadow Score. | |
| """ | |
| model_id = model_data.get("id", model_data.get("model_id", "unknown")) | |
| license_val = model_data.get("license", model_data.get("cardData", {}).get("license")) | |
| datasets = model_data.get("datasets", model_data.get("cardData", {}).get("datasets", [])) | |
| author = model_data.get("author", "") | |
| tags = model_data.get("tags", []) | |
| violations = [] | |
| # NEC#1: Missing data provenance | |
| if not datasets and "fine-tuned" not in " ".join(tags).lower(): | |
| violations.append({ | |
| "id": "NEC#1", | |
| "name": NECESSITY_CANON["NEC#1"]["name"], | |
| "severity": NECESSITY_CANON["NEC#1"]["severity"], | |
| "reason": "No training data provenance declared" | |
| }) | |
| # NEC#2: Missing license attribution | |
| if not license_val: | |
| violations.append({ | |
| "id": "NEC#2", | |
| "name": NECESSITY_CANON["NEC#2"]["name"], | |
| "severity": NECESSITY_CANON["NEC#2"]["severity"], | |
| "reason": "No license attribution declared" | |
| }) | |
| # NEC#7: Missing usage scope | |
| has_intended_use = any("intended" in t.lower() or "use" in t.lower() for t in tags) | |
| if not has_intended_use: | |
| violations.append({ | |
| "id": "NEC#7", | |
| "name": NECESSITY_CANON["NEC#7"]["name"], | |
| "severity": NECESSITY_CANON["NEC#7"]["severity"], | |
| "reason": "No intended use or usage scope declared" | |
| }) | |
| # NEC#13: Missing accountable entity | |
| if not author: | |
| violations.append({ | |
| "id": "NEC#13", | |
| "name": NECESSITY_CANON["NEC#13"]["name"], | |
| "severity": NECESSITY_CANON["NEC#13"]["severity"], | |
| "reason": "No accountable author or organization" | |
| }) | |
| # Compute Shadow Score (100 = perfect, 0 = critical) | |
| total_severity = sum(v["severity"] for v in violations) | |
| shadow_score = max(0, min(100, 100 - int(total_severity * 0.15))) | |
| # Determine badge | |
| if shadow_score >= 90: | |
| badge, badge_color = "GOLD", "#F59E0B" | |
| elif shadow_score >= 75: | |
| badge, badge_color = "SILVER", "#94A3B8" | |
| elif shadow_score >= 60: | |
| badge, badge_color = "BRONZE", "#D97706" | |
| else: | |
| badge, badge_color = "UNVERIFIED", "#EF4444" | |
| # Severity classification | |
| if not violations: | |
| severity = "CLEAN" | |
| elif shadow_score >= 75: | |
| severity = "LOW" | |
| elif shadow_score >= 60: | |
| severity = "MEDIUM" | |
| elif shadow_score >= 40: | |
| severity = "HIGH" | |
| else: | |
| severity = "CRITICAL" | |
| return { | |
| "model_id": model_id, | |
| "shadow_score": shadow_score, | |
| "badge": badge, | |
| "badge_color": badge_color, | |
| "severity": severity, | |
| "violations": violations, | |
| "violation_count": len(violations), | |
| "license": license_val or "not declared", | |
| "author": author or "unknown", | |
| "analyzed_at": _nowz(), | |
| "oracle_version": ENGINE_VERSION, | |
| } | |
| def _nowz() -> str: | |
| return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") | |
| def _canonical_json_bytes(obj: dict) -> bytes: | |
| return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") | |
| def _sha256_hex(b: bytes) -> str: | |
| return hashlib.sha256(b).hexdigest() | |
| def _short(s: str, n: int = 16) -> str: | |
| if not isinstance(s, str) or not s: | |
| return "" | |
| return s[:n] + "…" | |
| def _list_capsules() -> List[str]: | |
| now = time.time() | |
| if (now - _CAPSULE_LIST_CACHE["ts"]) < _CAPSULE_LIST_TTL_SEC and _CAPSULE_LIST_CACHE["items"]: | |
| return _CAPSULE_LIST_CACHE["items"] | |
| items: List[str] = [] | |
| try: | |
| files = HfApi().list_repo_files(repo_id=CEP_DATASET_ID, repo_type="dataset") | |
| for f in files: | |
| if f.endswith(".json") and f.startswith("CEP-") and not f.lower().endswith("index.json"): | |
| items.append(Path(f).stem) | |
| items = sorted(set(items))[:350] | |
| except Exception: | |
| items = [] | |
| _CAPSULE_LIST_CACHE["ts"] = now | |
| _CAPSULE_LIST_CACHE["items"] = items | |
| return items | |
| def fetch_capsule(cep_id: str) -> Dict[str, Any]: | |
| path = hf_hub_download( | |
| repo_id=CEP_DATASET_ID, | |
| filename=f"{cep_id}.json", | |
| repo_type="dataset", | |
| ) | |
| with open(path, "r", encoding="utf-8") as f: | |
| return json.load(f) | |
| def compute_trust(checks: Dict[str, bool]) -> Tuple[str, str, float]: | |
| missing = [k for k, v in checks.items() if not v] | |
| if not missing: | |
| return ("GREEN", "Fully anchored: evidence + signature + hashchain bound.", 1.0) | |
| if missing == ["hashchain_root"]: | |
| return ("YELLOW", "Evidence present, but not bound to a verifiable training run (missing hashchain).", 0.62) | |
| if missing == ["signature"]: | |
| return ("YELLOW", "Evidence present, but missing signature metadata (publisher not authenticated).", 0.58) | |
| if "evidence" in missing: | |
| return ("RED", "No evidence nodes inside capsule (cannot inspect annex/payout anchors).", 0.18) | |
| return ("RED", "Incomplete evidence: missing critical verification anchors.", 0.28) | |
| def make_terminal_and_inspector(capsule: Dict[str, Any], cep_id: str) -> Dict[str, Any]: | |
| terminal: List[str] = [] | |
| inspector: List[str] = [] | |
| schema = capsule.get("schema", "unknown") | |
| period = capsule.get("period", "unknown") | |
| model = capsule.get("model", {}) | |
| model_id = model.get("model_id", "unknown-model") if isinstance(model, dict) else "unknown-model" | |
| evidence = capsule.get("evidence", {}) | |
| if not isinstance(evidence, dict): | |
| evidence = {} | |
| meta = capsule.get("meta", {}) if isinstance(capsule.get("meta"), dict) else {} | |
| hashchain_root = meta.get("hashchain_sha256", "") | |
| hashchain_present = isinstance(hashchain_root, str) and bool(hashchain_root.strip()) | |
| sig_present = "signature" in capsule | |
| cap_sha = _sha256_hex(_canonical_json_bytes(capsule)) | |
| checks = { | |
| "schema": isinstance(schema, str) and schema != "unknown", | |
| "period": isinstance(period, str) and len(period) >= 4, | |
| "model_id": isinstance(model_id, str) and model_id != "unknown-model", | |
| "evidence": isinstance(evidence, dict) and len(evidence) > 0, | |
| "signature": bool(sig_present), | |
| "hashchain_root": bool(hashchain_present), | |
| } | |
| trust_level, trust_reason, trust_score = compute_trust(checks) | |
| terminal.append("CROVIA // PRISM CONSOLE v1") | |
| terminal.append(f"capsule {cep_id}") | |
| terminal.append(f"timestamp {_nowz()}") | |
| terminal.append(f"mode {'OPEN DEMO (light checks)' if OPEN_DEMO_MODE else 'FULL'}") | |
| terminal.append("") | |
| terminal.append("[STRUCTURE]") | |
| terminal.append(f"schema {schema}") | |
| terminal.append(f"model {model_id}") | |
| terminal.append(f"period {period}") | |
| terminal.append(f"evidence {len(evidence)} nodes") | |
| terminal.append("") | |
| terminal.append("[TRUST]") | |
| terminal.append(f"level {trust_level}") | |
| terminal.append(f"reason {trust_reason}") | |
| terminal.append("") | |
| terminal.append("[INTEGRITY]") | |
| terminal.append(f"capsule_sha256 {cap_sha}") | |
| terminal.append(f"signature {'present' if sig_present else 'missing'}") | |
| terminal.append(f"hashchain {('sha256:' + _short(hashchain_root, 16)) if hashchain_present else 'missing'}") | |
| terminal.append("") | |
| proof = f"crovia:v1;m={model_id};p={period};h={cap_sha};u=hf://{CEP_DATASET_ID}/{cep_id}.json" | |
| terminal.append("[PROOF STRING]") | |
| terminal.append(proof) | |
| terminal.append("") | |
| if trust_level != "GREEN": | |
| terminal.append("[WHY THIS MATTERS]") | |
| if not hashchain_present: | |
| terminal.append("- Not bound to a verifiable training run (no hashchain anchor).") | |
| if not sig_present: | |
| terminal.append("- Publisher not authenticated (no signature metadata).") | |
| if len(evidence) == 0: | |
| terminal.append("- No inspectable annex/payout nodes in evidence.") | |
| terminal.append("") | |
| inspector.append("INSPECTOR (real checks)") | |
| for k in ["schema", "period", "model_id", "evidence", "signature", "hashchain_root"]: | |
| inspector.append(f"- {k:13s} : {'OK' if checks[k] else 'FAIL'}") | |
| inspector.append("") | |
| inspector.append("TRUST SIGNAL") | |
| inspector.append(f"- level : {trust_level}") | |
| inspector.append(f"- score : {trust_score:.2f}") | |
| inspector.append(f"- note : {trust_reason}") | |
| inspector.append("") | |
| inspector.append("EVIDENCE NODES (first 18)") | |
| if evidence: | |
| for ek, ev in list(evidence.items())[:18]: | |
| if isinstance(ev, dict): | |
| sha = ev.get("sha256", "") | |
| url = ev.get("url", "") | |
| path = ev.get("path", "") | |
| line = f"- {ek}" | |
| if sha: | |
| line += f" | sha256:{_short(str(sha), 16)}" | |
| if url: | |
| line += f" | url:{str(url)[:88]}" | |
| if path: | |
| line += f" | path:{str(path)[:88]}" | |
| inspector.append(line) | |
| else: | |
| inspector.append(f"- {ek} | (non-object)") | |
| else: | |
| inspector.append("- (none)") | |
| nodes = list(evidence.keys()) | |
| anchors = ["receipts", "payouts", "signature", "hashchain"] | |
| for a in anchors: | |
| if a not in nodes: | |
| nodes.append(a) | |
| return { | |
| "cep_id": cep_id, | |
| "schema": schema, | |
| "period": period, | |
| "model_id": model_id, | |
| "trust_level": trust_level, | |
| "trust_reason": trust_reason, | |
| "trust_score": trust_score, | |
| "capsule_sha256": cap_sha, | |
| "signature_present": sig_present, | |
| "hashchain_present": hashchain_present, | |
| "hashchain_sha256": hashchain_root if hashchain_present else "", | |
| "proof": proof, | |
| "terminal": "\n".join(terminal), | |
| "inspector": "\n".join(inspector), | |
| "nodes": nodes, | |
| } | |
| def inspect_payload(cep_id: str) -> str: | |
| cep_id = (cep_id or "").strip() | |
| if not cep_id: | |
| return json.dumps({"error": "empty"}, ensure_ascii=False) | |
| try: | |
| cap = fetch_capsule(cep_id) | |
| except Exception as e: | |
| return json.dumps( | |
| { | |
| "error": "fetch_failed", | |
| "cep_id": cep_id, | |
| "detail": f"{type(e).__name__}: {e}", | |
| }, | |
| ensure_ascii=False, | |
| ) | |
| payload = make_terminal_and_inspector(cap, cep_id) | |
| return json.dumps(payload, ensure_ascii=False) | |
| CSS = r""" | |
| <style> | |
| :root{ | |
| --bg0:#05060a; | |
| --bg1:#070a12; | |
| --panel:#0b1020; | |
| --ink:#e7eaf1; | |
| --ink-strong:#f8fafc; | |
| --mut:#cbd5e1; | |
| --line:rgba(255,255,255,.10); | |
| --glow:rgba(56,189,248,.35); | |
| --good:#22c55e; | |
| --warn:#f59e0b; | |
| --bad:#ef4444; | |
| --cyan:#38bdf8; | |
| --shadow: 0 18px 55px rgba(0,0,0,.55); | |
| --radius:16px; | |
| --p:0.62; | |
| } | |
| html,body,#root,.gradio-container{ | |
| background:var(--bg0) !important; | |
| } | |
| .gradio-container{ | |
| max-width:1320px !important; | |
| margin:0 auto !important; | |
| padding:26px 18px 70px !important; | |
| font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Arial; | |
| color:var(--ink) !important; | |
| } | |
| footer{display:none !important;} | |
| .gradio-container .wrap{border:0 !important;} | |
| .gradio-container label{display:none !important;} | |
| .gradio-container .prose{max-width:none !important;} | |
| /* HARD VISIBILITY (HF dimming killer) */ | |
| .gradio-container, .gradio-container *{ | |
| opacity:1 !important; | |
| filter:none !important; | |
| mix-blend-mode:normal !important; | |
| visibility:visible !important; | |
| } | |
| /* HERO */ | |
| .crovia-hero{ | |
| position:relative; | |
| border-radius:var(--radius); | |
| border:1px solid var(--line); | |
| background: | |
| radial-gradient(1000px 320px at 22% 10%, rgba(56,189,248,.18), transparent 60%), | |
| radial-gradient(800px 320px at 70% 0%, rgba(34,197,94,.14), transparent 55%), | |
| linear-gradient(180deg,#070a12,#05060a); | |
| box-shadow:var(--shadow); | |
| padding:22px 22px 18px; | |
| overflow:hidden; | |
| isolation:isolate; | |
| } | |
| .hero-top{ | |
| display:flex; | |
| align-items:center; | |
| gap:14px; | |
| } | |
| .hero-logo{ | |
| width:64px;height:64px; | |
| border-radius:16px; | |
| background:#070a12; | |
| border:1px solid rgba(255,255,255,.14); | |
| box-shadow:0 0 34px rgba(56,189,248,.28); | |
| display:flex; | |
| align-items:center; | |
| justify-content:center; | |
| overflow:hidden; | |
| flex:0 0 auto; | |
| } | |
| .hero-logo img{width:100%;height:100%;object-fit:cover;display:block;} | |
| .hero-title{ | |
| margin:0; | |
| display:inline-block; | |
| padding:6px 10px; | |
| border-radius:12px; | |
| font-size:34px; | |
| font-weight:900; | |
| letter-spacing:.22em; | |
| text-transform:uppercase; | |
| color:#ffffff !important; | |
| background:rgba(0,0,0,.28); | |
| border:1px solid rgba(255,255,255,.08); | |
| text-shadow: | |
| 0 0 26px rgba(56,189,248,.55), | |
| 0 2px 14px rgba(0,0,0,.85); | |
| } | |
| .hero-sub{ | |
| margin:10px 0 0; | |
| color:#e5e7eb !important; | |
| font-size:15px; | |
| line-height:1.7; | |
| max-width:900px; | |
| } | |
| .hero-sub strong{color:var(--ink-strong) !important;font-weight:700;} | |
| .hero-row{ | |
| margin-top:14px; | |
| display:flex; | |
| gap:10px; | |
| flex-wrap:wrap; | |
| align-items:center; | |
| } | |
| .pill{ | |
| display:inline-flex; | |
| align-items:center; | |
| gap:8px; | |
| padding:6px 12px; | |
| border-radius:999px; | |
| border:1px solid rgba(255,255,255,.14); | |
| background:rgba(255,255,255,.05); | |
| color:var(--ink-strong) !important; | |
| font-size:11px; | |
| letter-spacing:.14em; | |
| text-transform:uppercase; | |
| } | |
| .dot{ | |
| width:8px;height:8px; | |
| border-radius:999px; | |
| background:var(--cyan); | |
| box-shadow:0 0 16px var(--glow); | |
| } | |
| .pill.good .dot{background:var(--good);box-shadow:0 0 16px rgba(34,197,94,.40);} | |
| .pill.warn .dot{background:var(--warn);box-shadow:0 0 16px rgba(245,158,11,.40);} | |
| .pill.bad .dot{background:var(--bad); box-shadow:0 0 16px rgba(239,68,68,.40);} | |
| .mini{ | |
| font-size:12.5px; | |
| color:#f8fafc !important; | |
| opacity:.85; | |
| } | |
| .status-line{ | |
| margin-top:14px; | |
| padding:8px 14px; | |
| border-radius:10px; | |
| border:1px solid rgba(255,255,255,.14); | |
| background:rgba(0,0,0,.35); | |
| font-size:12px; | |
| letter-spacing:.14em; | |
| text-transform:uppercase; | |
| color:#e5e7eb !important; | |
| box-shadow: inset 0 0 18px rgba(56,189,248,.12); | |
| } | |
| /* === CEP PICKER (HTML select) — NO GRADIO MOVE === */ | |
| #cep_picker{ | |
| margin-top:10px; | |
| width:620px; | |
| max-width:100%; | |
| } | |
| #cep_select{ | |
| width:100%; | |
| height:44px; | |
| border-radius:12px; | |
| background:#05060a; | |
| color:#f8fafc; | |
| border:1px solid rgba(255,255,255,.16); | |
| padding:0 42px 0 14px; | |
| outline:none; | |
| appearance:none; | |
| -webkit-appearance:none; | |
| -moz-appearance:none; | |
| cursor:pointer; | |
| } | |
| #cep_picker{ | |
| position:relative; | |
| } | |
| #cep_picker::after{ | |
| content:"⌄"; | |
| position:absolute; | |
| right:16px; | |
| top:50%; | |
| transform:translateY(-50%); | |
| pointer-events:none; | |
| color:#38bdf8; | |
| font-size:18px; | |
| opacity:.85; | |
| } | |
| /* GRID */ | |
| .grid{ | |
| margin-top:18px; | |
| display:grid; | |
| grid-template-columns:1fr 1fr; | |
| gap:16px; | |
| } | |
| @media (max-width:980px){ .grid{grid-template-columns:1fr;} } | |
| .card{ | |
| border-radius:var(--radius); | |
| border:1px solid var(--line); | |
| background: | |
| radial-gradient(600px 240px at 10% 10%, rgba(56,189,248,.10), transparent 55%), | |
| linear-gradient(180deg,#070a12,#05060a); | |
| box-shadow:var(--shadow); | |
| overflow:hidden; | |
| } | |
| .card-h{ | |
| padding:10px 14px; | |
| border-bottom:1px solid var(--line); | |
| letter-spacing:.18em; | |
| text-transform:uppercase; | |
| font-weight:800; | |
| font-size:12px; | |
| color:var(--ink-strong) !important; | |
| background:linear-gradient(90deg,rgba(255,255,255,.05),transparent 55%); | |
| } | |
| .card-b{padding:12px 14px;} | |
| pre.term{ | |
| margin:0; | |
| padding:12px; | |
| border-radius:12px; | |
| border:1px solid rgba(255,255,255,.10); | |
| background: | |
| radial-gradient(500px 220px at 20% 10%, rgba(56,189,248,.10), transparent 55%), | |
| #05060a; | |
| color:#e5e7eb !important; | |
| font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono",monospace; | |
| font-size:12.6px; | |
| line-height:1.55; | |
| min-height:330px; | |
| white-space:pre-wrap; | |
| } | |
| svg#constellation{ | |
| width:100%; | |
| min-height:330px; | |
| border-radius:12px; | |
| border:1px solid rgba(255,255,255,.10); | |
| background: | |
| radial-gradient(520px 240px at 25% 15%, rgba(56,189,248,.12), transparent 60%), | |
| radial-gradient(420px 240px at 75% 80%, rgba(34,197,94,.10), transparent 55%), | |
| #05060a; | |
| } | |
| /* TRUST pill — make state readable (container, not only dot) */ | |
| .pill.good{ | |
| background: rgba(34,197,94,.14) !important; | |
| border-color: rgba(34,197,94,.45) !important; | |
| } | |
| .pill.warn{ | |
| background: rgba(245,158,11,.14) !important; | |
| border-color: rgba(245,158,11,.45) !important; | |
| } | |
| .pill.bad{ | |
| background: rgba(239,68,68,.14) !important; | |
| border-color: rgba(239,68,68,.45) !important; | |
| } | |
| #trust_text{ | |
| color:#f8fafc !important; | |
| text-shadow:0 1px 12px rgba(0,0,0,.6); | |
| } | |
| </style> | |
| """ | |
| UI_HTML = r""" | |
| <div class="crovia-hero"> | |
| <div class="hero-top"> | |
| <div class="hero-logo"> | |
| <img src="https://huggingface.co/spaces/Crovia/cep-terminal/resolve/main/crovia-logo.png" alt="CROVIA logo"/> | |
| </div> | |
| <div> | |
| <h1 class="hero-title">CROVIA · CEP TERMINAL</h1> | |
| <p class="hero-sub"> | |
| Public inspection of AI training evidence capsules. | |
| <strong>No simulations. No assumptions.</strong> Only what exists. | |
| </p> | |
| <div class="hero-row"> | |
| <div id="pill_trust" class="pill warn"><span class="dot"></span><span id="trust_text">TRUST: —</span></div> | |
| <div class="pill"><span class="dot"></span><span>DATASET: Crovia/cep-capsules</span></div> | |
| <div class="pill"><span class="dot"></span><span>MODE: OPEN DEMO</span></div> | |
| </div> | |
| <div class="mini" id="mini_line"> | |
| ▶ Select a <strong>CEP fingerprint</strong> to inspect real training evidence. | |
| </div> | |
| <div class="status-line" id="status_line"> | |
| STATUS: — awaiting capsule selection | |
| </div> | |
| <div id="cep_picker"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="grid"> | |
| <div class="card"> | |
| <div class="card-h">C-LINE OUTPUT</div> | |
| <div class="card-b"> | |
| <pre id="crovia_terminal" class="term">Loading…</pre> | |
| <div class="mini" id="copy_hint">—</div> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <div class="card-h">PRISM MAP</div> | |
| <div class="card-b"> | |
| <svg id="constellation" viewBox="0 0 820 360" preserveAspectRatio="xMidYMid meet"></svg> | |
| </div> | |
| </div> | |
| <div class="card" style="grid-column: 1 / -1;"> | |
| <div class="card-h">INSPECTOR</div> | |
| <div class="card-b"> | |
| <pre id="crovia_inspector" class="term" style="min-height:240px;">—</pre> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| JS = r""" | |
| () => { | |
| const $ = (q) => document.querySelector(q); | |
| function setPill(level){ | |
| const pill = $("#pill_trust"); | |
| if(!pill) return; | |
| pill.classList.remove("good","warn","bad"); | |
| if(level==="GREEN") pill.classList.add("good"); | |
| else if(level==="YELLOW") pill.classList.add("warn"); | |
| else pill.classList.add("bad"); | |
| } | |
| function drawConstellation(payload){ | |
| const svg = $("#constellation"); | |
| if(!svg) return; | |
| while(svg.firstChild) svg.removeChild(svg.firstChild); | |
| const W = 820, H = 360; | |
| const cx = W/2, cy = H/2 + 6; | |
| const r = 130; | |
| const nodes = (payload.nodes || []).slice(0, 28); | |
| const anchors = new Set(["receipts","payouts","signature","hashchain"]); | |
| const sigOn = !!payload.signature_present; | |
| const hcOn = !!payload.hashchain_present; | |
| function colorOf(name){ | |
| if(name==="signature") return sigOn ? "#22c55e" : "rgba(255,255,255,.35)"; | |
| if(name==="hashchain") return hcOn ? "#22c55e" : "rgba(255,255,255,.35)"; | |
| if(name==="receipts" || name==="payouts") return "#38bdf8"; | |
| return anchors.has(name) ? "#38bdf8" : "rgba(231,234,241,.72)"; | |
| } | |
| const bg = document.createElementNS("http://www.w3.org/2000/svg","circle"); | |
| bg.setAttribute("cx", cx); bg.setAttribute("cy", cy); | |
| bg.setAttribute("r", 150); | |
| bg.setAttribute("fill", "rgba(56,189,248,.06)"); | |
| svg.appendChild(bg); | |
| nodes.forEach((name, i) => { | |
| const a = (Math.PI * 2) * (i / Math.max(1, nodes.length)); | |
| const x = cx + r * Math.cos(a); | |
| const y = cy + r * Math.sin(a); | |
| const line = document.createElementNS("http://www.w3.org/2000/svg","line"); | |
| line.setAttribute("x1", cx); line.setAttribute("y1", cy); | |
| line.setAttribute("x2", x); line.setAttribute("y2", y); | |
| line.setAttribute("stroke", "rgba(255,255,255,.16)"); | |
| line.setAttribute("stroke-width", "1"); | |
| svg.appendChild(line); | |
| }); | |
| const center = document.createElementNS("http://www.w3.org/2000/svg","circle"); | |
| center.setAttribute("cx", cx); center.setAttribute("cy", cy); | |
| center.setAttribute("r", 10); | |
| center.setAttribute("fill", "#38bdf8"); | |
| svg.appendChild(center); | |
| const ctext = document.createElementNS("http://www.w3.org/2000/svg","text"); | |
| ctext.setAttribute("x", cx); | |
| ctext.setAttribute("y", cy + 28); | |
| ctext.setAttribute("text-anchor", "middle"); | |
| ctext.setAttribute("fill", "rgba(231,234,241,.92)"); | |
| ctext.setAttribute("font-size", "12"); | |
| ctext.textContent = payload.cep_id || "CEP"; | |
| svg.appendChild(ctext); | |
| nodes.forEach((name, i) => { | |
| const a = (Math.PI * 2) * (i / Math.max(1, nodes.length)); | |
| const x = cx + r * Math.cos(a); | |
| const y = cy + r * Math.sin(a); | |
| const dot = document.createElementNS("http://www.w3.org/2000/svg","circle"); | |
| dot.setAttribute("cx", x); dot.setAttribute("cy", y); | |
| dot.setAttribute("r", anchors.has(name) ? 7 : 5); | |
| dot.setAttribute("fill", colorOf(name)); | |
| svg.appendChild(dot); | |
| const t = document.createElementNS("http://www.w3.org/2000/svg","text"); | |
| t.setAttribute("x", x); | |
| t.setAttribute("y", y - 10); | |
| t.setAttribute("text-anchor", "middle"); | |
| t.setAttribute("fill", "rgba(231,234,241,.78)"); | |
| t.setAttribute("font-size", "11"); | |
| t.textContent = name; | |
| svg.appendChild(t); | |
| }); | |
| } | |
| function updateUI(payload){ | |
| if(!payload || payload.error){ | |
| $("#crovia_terminal").textContent = | |
| "CROVIA // PRISM CONSOLE v1\n\n[ERROR]\n" + (payload?.detail || payload?.error || "unknown"); | |
| $("#crovia_inspector").textContent = "—"; | |
| $("#trust_text").textContent = "TRUST: ERROR"; | |
| setPill("RED"); | |
| drawConstellation({ | |
| cep_id: payload?.cep_id || "CEP", | |
| nodes:["receipts","payouts","signature","hashchain"], | |
| signature_present:false, | |
| hashchain_present:false | |
| }); | |
| return; | |
| } | |
| $("#crovia_terminal").textContent = payload.terminal || "—"; | |
| $("#crovia_inspector").textContent = payload.inspector || "—"; | |
| const level = payload.trust_level || "YELLOW"; | |
| $("#trust_text").textContent = "TRUST: " + level; | |
| setPill(level); | |
| $("#mini_line").textContent = payload.trust_reason || "Select a CEP fingerprint to light up the Prism."; | |
| drawConstellation(payload); | |
| } | |
| function attachPayloadWatcher(){ | |
| const root = document.querySelector("#crovia_payload"); | |
| if(!root) return false; | |
| const input = root.querySelector("textarea, input"); | |
| if(!input) return false; | |
| let last = ""; | |
| const tick = () => { | |
| const val = input.value || ""; | |
| if(val && val !== last){ | |
| last = val; | |
| try{ updateUI(JSON.parse(val)); }catch(e){} | |
| } | |
| }; | |
| input.addEventListener("input", tick); | |
| setInterval(tick, 250); | |
| return true; | |
| } | |
| // Build a REAL HTML <select> inside #cep_picker (no Gradio DOM move) | |
| function mountCepSelect(){ | |
| const host = $("#cep_picker"); | |
| if(!host) return false; | |
| // already mounted | |
| if(host.querySelector("#cep_select")) return true; | |
| const capsRoot = document.querySelector("#capsules_json"); | |
| const capsInput = capsRoot ? capsRoot.querySelector("textarea, input") : null; | |
| if(!capsInput) return false; | |
| let caps = []; | |
| try{ caps = JSON.parse(capsInput.value || "[]"); }catch(e){ caps = []; } | |
| const cepRoot = document.querySelector("#cep_in"); | |
| const cepInput = cepRoot ? cepRoot.querySelector("textarea, input") : null; | |
| if(!cepInput) return false; | |
| const sel = document.createElement("select"); | |
| sel.id = "cep_select"; | |
| caps.forEach((c) => { | |
| const opt = document.createElement("option"); | |
| opt.value = c; | |
| opt.textContent = c; | |
| sel.appendChild(opt); | |
| }); | |
| if(cepInput.value){ | |
| sel.value = cepInput.value; | |
| }else if(caps.length){ | |
| sel.value = caps[0]; | |
| cepInput.value = caps[0]; | |
| cepInput.dispatchEvent(new Event("input", { bubbles:true })); | |
| } | |
| sel.addEventListener("change", () => { | |
| cepInput.value = sel.value; | |
| // Trigger Gradio | |
| cepInput.dispatchEvent(new Event("input", { bubbles:true })); | |
| cepInput.dispatchEvent(new Event("change", { bubbles:true })); | |
| }); | |
| host.appendChild(sel); | |
| return true; | |
| } | |
| const boot = setInterval(() => { | |
| const ok1 = mountCepSelect(); | |
| const ok2 = attachPayloadWatcher(); | |
| if(ok1 && ok2) clearInterval(boot); | |
| }, 200); | |
| return []; | |
| } | |
| """ | |
| capsules = _list_capsules() | |
| default_cep = capsules[0] if capsules else "CEP-2511-K4I7X2" | |
| # ============================================================================= | |
| # ORACLE UI - Model Trust Analysis | |
| # ============================================================================= | |
| ORACLE_CSS = """ | |
| .oracle-section { | |
| margin-top: 24px; | |
| padding: 20px; | |
| background: linear-gradient(180deg, #0a0c14, #05060a); | |
| border: 1px solid rgba(139, 92, 246, 0.3); | |
| border-radius: 16px; | |
| } | |
| .oracle-title { | |
| color: #a78bfa !important; | |
| font-size: 18px; | |
| font-weight: 700; | |
| margin-bottom: 16px; | |
| letter-spacing: 0.1em; | |
| } | |
| .oracle-result { | |
| padding: 16px; | |
| background: rgba(0,0,0,0.4); | |
| border-radius: 12px; | |
| font-family: 'JetBrains Mono', monospace; | |
| } | |
| .score-gold { color: #F59E0B !important; } | |
| .score-silver { color: #94A3B8 !important; } | |
| .score-bronze { color: #D97706 !important; } | |
| .score-red { color: #EF4444 !important; } | |
| """ | |
| def analyze_hf_model(model_id: str) -> str: | |
| """Analyze a HuggingFace model with the Omission Oracle.""" | |
| model_id = (model_id or "").strip() | |
| if not model_id: | |
| return "Enter a model ID (e.g., meta-llama/Llama-3-8B)" | |
| try: | |
| api = HfApi() | |
| info = api.model_info(model_id, securityStatus=False) | |
| model_data = { | |
| "id": info.id, | |
| "author": info.author or "", | |
| "tags": info.tags or [], | |
| "license": info.card_data.get("license") if info.card_data else None, | |
| "cardData": info.card_data or {}, | |
| "downloads": info.downloads or 0, | |
| "likes": info.likes or 0, | |
| } | |
| result = analyze_model_shadow_score(model_data) | |
| # Format output | |
| badge_emoji = {"GOLD": "🟢", "SILVER": "🟡", "BRONZE": "🟠", "UNVERIFIED": "🔴"} | |
| emoji = badge_emoji.get(result["badge"], "⚪") | |
| violations_str = "" | |
| if result["violations"]: | |
| for v in result["violations"]: | |
| violations_str += f"\n • {v['id']}: {v['reason']}" | |
| else: | |
| violations_str = "\n ✓ No violations detected" | |
| output = f"""╔══════════════════════════════════════════════════════════════╗ | |
| ║ CROVIA OMISSION ORACLE v{ENGINE_VERSION} ║ | |
| ╚══════════════════════════════════════════════════════════════╝ | |
| MODEL: {result['model_id']} | |
| AUTHOR: {result['author']} | |
| ┌─────────────────────────────────────┐ | |
| │ SHADOW SCORE: {result['shadow_score']:3d}/100 {emoji} {result['badge']:12s}│ | |
| │ SEVERITY: {result['severity']:10s} │ | |
| └─────────────────────────────────────┘ | |
| DECLARATIONS: | |
| License: {result['license']} | |
| VIOLATIONS ({result['violation_count']}): {violations_str} | |
| ANALYSIS: {result['analyzed_at'][:19]}Z | |
| EVIDENCE: {hashlib.sha256(model_id.encode()).hexdigest()[:16]} | |
| ───────────────────────────────────── | |
| Powered by Crovia Omission Oracle | |
| https://huggingface.co/datasets/Crovia/global-ai-training-omissions | |
| """ | |
| return output | |
| except Exception as e: | |
| return f"Error analyzing model: {type(e).__name__}: {e}" | |
| with gr.Blocks( | |
| title="CROVIA · CEP Terminal v2.0", | |
| css=CSS + ORACLE_CSS, | |
| js=JS | |
| ) as demo: | |
| gr.HTML(UI_HTML) | |
| # Provide capsules list to JS via hidden textbox | |
| capsules_json = gr.Textbox(value=json.dumps(capsules, ensure_ascii=False), visible=False, elem_id="capsules_json") | |
| # CEP input for backend (JS writes into this) | |
| cep_in = gr.Textbox(value=default_cep, visible=False, elem_id="cep_in") | |
| # Payload output watched by JS | |
| payload = gr.Textbox(value="", visible=False, elem_id="crovia_payload") | |
| def _run(cep_id: str) -> str: | |
| return inspect_payload(cep_id) | |
| # When cep_in changes -> compute payload | |
| cep_in.change(_run, inputs=cep_in, outputs=payload) | |
| demo.load(_run, inputs=cep_in, outputs=payload) | |
| # ========================================================================== | |
| # OMISSION ORACLE SECTION | |
| # ========================================================================== | |
| gr.HTML(""" | |
| <div class="oracle-section"> | |
| <div class="oracle-title">🔮 OMISSION ORACLE — Model Trust Scanner</div> | |
| <p style="color:#a1a1aa;font-size:13px;margin-bottom:12px;"> | |
| Analyze any HuggingFace model for trust declaration gaps (NEC#1, NEC#2, NEC#7, NEC#13) | |
| </p> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| model_input = gr.Textbox( | |
| label="Model ID", | |
| placeholder="meta-llama/Llama-3-8B", | |
| scale=3 | |
| ) | |
| analyze_btn = gr.Button("🔍 Analyze", variant="primary", scale=1) | |
| oracle_output = gr.Code( | |
| label="Oracle Analysis", | |
| language=None, | |
| lines=25 | |
| ) | |
| analyze_btn.click(analyze_hf_model, inputs=model_input, outputs=oracle_output) | |
| model_input.submit(analyze_hf_model, inputs=model_input, outputs=oracle_output) | |
| demo.launch() | |