cep-terminal / app.py
Bitsage
feat: Omission Oracle UI - Live model trust scanner with Shadow Score
50ca169
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()