Spaces:
Sleeping
Sleeping
Upload app.py
Browse files- dvnc_ai_v2_hf/app.py +361 -0
dvnc_ai_v2_hf/app.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import pathlib
|
| 3 |
+
import gradio as gr
|
| 4 |
+
from .core.dvnc_engine import run_discovery
|
| 5 |
+
|
| 6 |
+
MODELS = ["DVNC Sovereign", "DVNC Atlas", "DVNC Curie"]
|
| 7 |
+
|
| 8 |
+
BASE_DIR = pathlib.Path(__file__).resolve().parent
|
| 9 |
+
|
| 10 |
+
#CONNECTOME_SNIPPET = (BASE_DIR / "ui" / "connectome.html").read_text(encoding="utf-8")
|
| 11 |
+
# replaced base connectome with enhanced connectome.
|
| 12 |
+
|
| 13 |
+
CONNECTOME_SNIPPET = (BASE_DIR / "ui" / "enhanced_connectome.html").read_text(encoding="utf-8")
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def build_chat_html(query, data):
|
| 18 |
+
return f'''
|
| 19 |
+
<div class="chat-panel panel">
|
| 20 |
+
<div class="bubble user"><span class="role">You</span><p>{query}</p></div>
|
| 21 |
+
<div class="bubble ai"><span class="role">DVNC</span><p>{data.get("summary","")}</p></div>
|
| 22 |
+
<div class="bubble system"><span class="role">Primary Hypothesis</span><p>{data.get("primary_hypothesis","")}</p></div>
|
| 23 |
+
</div>
|
| 24 |
+
'''
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def build_reasoning_html(steps):
|
| 28 |
+
chunks = []
|
| 29 |
+
for s in steps:
|
| 30 |
+
chunks.append(f'''
|
| 31 |
+
<details class="tray" {'open' if s['step']==1 else ''}>
|
| 32 |
+
<summary><span class="step">{s['step']}</span><div><strong>{s['agent']}</strong><small>{s['tag']}</small></div></summary>
|
| 33 |
+
<div class="tray-body">{s['summary']}</div>
|
| 34 |
+
</details>
|
| 35 |
+
''')
|
| 36 |
+
return '<div class="panel tray-panel">' + ''.join(chunks) + '</div>'
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def build_cards_html(cards):
|
| 40 |
+
html = []
|
| 41 |
+
for c in cards:
|
| 42 |
+
html.append(f'''
|
| 43 |
+
<article class="candidate-card" tabindex="0">
|
| 44 |
+
<div class="candidate-card-inner">
|
| 45 |
+
<div class="candidate-face front">
|
| 46 |
+
<div class="top"><span class="chip">{c['agent']}</span><span class="score">{c['score']}</span></div>
|
| 47 |
+
<h4>{c['title']}</h4>
|
| 48 |
+
<p>{c['front']}</p>
|
| 49 |
+
<div class="meta"><span>Novelty</span><strong>{c['novelty']}</strong></div>
|
| 50 |
+
</div>
|
| 51 |
+
<div class="candidate-face back">
|
| 52 |
+
<div class="top"><span class="chip alt">Alt path</span><span class="score">{c['score']}</span></div>
|
| 53 |
+
<h4>{c['title']}</h4>
|
| 54 |
+
<p>{c['back']}</p>
|
| 55 |
+
<div class="meta"><span>Swap state</span><strong>Ready</strong></div>
|
| 56 |
+
</div>
|
| 57 |
+
</div>
|
| 58 |
+
</article>
|
| 59 |
+
''')
|
| 60 |
+
return '<div class="panel cards-panel"><div class="candidate-grid">' + ''.join(html) + '</div></div>'
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def build_metrics_md(metrics, model_used):
|
| 64 |
+
lines = [f"# Discovery Output\n", f"**Claude model used:** {model_used}\n", "## Metrics"]
|
| 65 |
+
for k, v in metrics.items():
|
| 66 |
+
lines.append(f"- {k}: {v}/100")
|
| 67 |
+
lines.append("\n## Next action\n- Replace `core/dvnc_engine.py` with your production graph and evidence stack.")
|
| 68 |
+
return "\n".join(lines)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def build_frontend_payload(data):
|
| 72 |
+
return f"""
|
| 73 |
+
<script>
|
| 74 |
+
window.__DVNC_GRAPH__ = {json.dumps(data.get('graph', {}))};
|
| 75 |
+
</script>
|
| 76 |
+
{CONNECTOME_SNIPPET}
|
| 77 |
+
"""
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def run(query, model_name):
|
| 81 |
+
data = run_discovery(query, model_name)
|
| 82 |
+
return (
|
| 83 |
+
build_chat_html(query, data),
|
| 84 |
+
build_frontend_payload(data),
|
| 85 |
+
build_reasoning_html(data.get("reasoning", [])),
|
| 86 |
+
build_cards_html(data.get("cards", [])),
|
| 87 |
+
build_metrics_md(data.get("metrics", {}), data.get("model_used", "unknown")),
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
CSS = r'''
|
| 91 |
+
:root{--bg:#fffaf7;--panel:#ffffff;--panel2:#fff6f1;--line:rgba(17,24,39,.08);--text:#000;--muted:#5f6675;--orange:#ff6b35;--orange-light:#ff955f;--orange-pale:#fff3ec;--teal:#b9efe8;--blue:#8fb2ff;--radius:24px;--shadow:0 16px 44px rgba(17,24,39,.06)}
|
| 92 |
+
html,body,.gradio-container{background:linear-gradient(180deg,#fffaf7 0%,#fff 100%)!important;color:var(--text)!important;font-family:Inter,system-ui,sans-serif}
|
| 93 |
+
.gradio-container{max-width:1480px!important;padding:18px!important} footer{display:none!important}
|
| 94 |
+
#app-shell{border:1px solid var(--line);background:rgba(255,255,255,.92);backdrop-filter:blur(14px);box-shadow:var(--shadow);border-radius:32px;overflow:hidden}
|
| 95 |
+
#app-shell .grid{display:grid;grid-template-columns:1.08fr .92fr;min-height:72vh}.left,.right{padding:20px}.left{border-right:1px solid var(--line);display:flex;flex-direction:column;gap:14px}.right{display:grid;grid-template-rows:auto auto auto 1fr;gap:14px}
|
| 96 |
+
.hero{display:flex;justify-content:space-between;align-items:center;gap:12px}
|
| 97 |
+
.hero-plate{padding:22px 24px 18px 24px;min-height:160px;border-bottom:1px solid var(--line);background:linear-gradient(180deg,rgba(255,255,255,.92) 0%,rgba(255,247,243,.88) 100%)}
|
| 98 |
+
.hero-copy{display:flex;flex-direction:column;justify-content:space-between;gap:16px;min-height:118px}
|
| 99 |
+
.brand{display:flex;gap:14px;align-items:center}
|
| 100 |
+
.logo{width:50px;height:50px;border-radius:16px;display:grid;place-items:center;border:1px solid rgba(255,107,53,.2);background:linear-gradient(135deg,var(--orange-pale),rgba(255,107,53,.08))}
|
| 101 |
+
.logo svg{width:26px;height:26px;color:var(--orange)}
|
| 102 |
+
.brand h1{margin:0;font-size:1.18rem;color:var(--text)}
|
| 103 |
+
.brand p{margin:4px 0 0;color:var(--muted);font-size:.9rem}
|
| 104 |
+
.live{display:flex;gap:10px;align-items:center;color:var(--text)}
|
| 105 |
+
.live i{width:10px;height:10px;border-radius:50%;background:var(--orange);display:inline-block;box-shadow:0 0 18px rgba(255,107,53,.4);animation:pulse 2s ease-in-out infinite}
|
| 106 |
+
.hero-orb-wrap{margin-left:auto;display:flex;align-items:center;justify-content:center;min-width:180px}
|
| 107 |
+
.hero-orb{position:relative;width:110px;height:110px;display:grid;place-items:center}
|
| 108 |
+
.hero-orb-core{width:64px;height:64px;border-radius:50%;background:radial-gradient(circle at 30% 28%,#ffd2be 0%,#ff9a6f 30%,#ff6b35 68%,#d94d1c 100%);box-shadow:0 12px 34px rgba(255,107,53,.28), inset -8px -10px 18px rgba(100,35,10,.22), inset 8px 10px 16px rgba(255,255,255,.22);animation:heroOrbFloat 5s ease-in-out infinite}
|
| 109 |
+
.hero-orb-ring{position:absolute;border-radius:50%;border:1px solid rgba(255,107,53,.16)}
|
| 110 |
+
.hero-orb-ring.ring-a{width:86px;height:86px;animation:spinSlow 10s linear infinite}
|
| 111 |
+
.hero-orb-ring.ring-b{width:108px;height:108px;border-color:rgba(255,107,53,.10);animation:spinReverse 14s linear infinite}
|
| 112 |
+
.hero-orb-glow{position:absolute;width:120px;height:120px;border-radius:50%;background:radial-gradient(circle,rgba(255,107,53,.18) 0%,rgba(255,107,53,.08) 42%,rgba(255,107,53,0) 72%)}
|
| 113 |
+
@keyframes heroOrbFloat{0%,100%{transform:translateY(0) scale(1)}50%{transform:translateY(-5px) scale(1.03)}}
|
| 114 |
+
@keyframes spinSlow{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
|
| 115 |
+
@keyframes spinReverse{from{transform:rotate(360deg)}to{transform:rotate(0deg)}}
|
| 116 |
+
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.6;transform:scale(.9)}}
|
| 117 |
+
.panel{border:1px solid var(--line);background:#ffffff;border-radius:22px;box-shadow:0 2px 12px rgba(0,0,0,.04)}
|
| 118 |
+
.dvnc-model-cards .gr-radio{
|
| 119 |
+
display:grid!important;
|
| 120 |
+
grid-template-columns:repeat(3,1fr);
|
| 121 |
+
gap:12px;
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
.dvnc-model-cards label{
|
| 125 |
+
display:flex!important;
|
| 126 |
+
flex-direction:column;
|
| 127 |
+
gap:4px;
|
| 128 |
+
padding:14px!important;
|
| 129 |
+
border:1px solid var(--line)!important;
|
| 130 |
+
border-radius:18px!important;
|
| 131 |
+
background:#fafbfc!important;
|
| 132 |
+
cursor:pointer!important;
|
| 133 |
+
transition:all .2s ease!important;
|
| 134 |
+
min-height:84px;
|
| 135 |
+
justify-content:center;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
.dvnc-model-cards label:hover{
|
| 139 |
+
border-color:var(--orange)!important;
|
| 140 |
+
transform:translateY(-1px);
|
| 141 |
+
box-shadow:0 10px 22px rgba(255,107,53,.08);
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
.dvnc-model-cards input[type="radio"]{
|
| 145 |
+
margin-bottom:8px;
|
| 146 |
+
accent-color:var(--orange);
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
.dvnc-model-cards label:has(input[type="radio"]:checked){
|
| 150 |
+
border-color:var(--orange)!important;
|
| 151 |
+
background:var(--orange-pale)!important;
|
| 152 |
+
box-shadow:0 10px 24px rgba(255,107,53,.12);
|
| 153 |
+
}
|
| 154 |
+
.querybox,.querybox>div{background:#fff!important;border:1.5px solid rgba(255,107,53,.28)!important;border-radius:20px!important;box-shadow:0 6px 22px rgba(255,107,53,.08)!important}.querybox textarea{color:#000!important;background:#fff!important}.querybox:focus-within,.querybox>div:focus-within{border-color:#ff6b35!important;box-shadow:0 0 0 4px rgba(255,107,53,.10),0 8px 28px rgba(255,107,53,.10)!important}
|
| 155 |
+
.chat-panel{padding:14px;display:flex;flex-direction:column;gap:10px;min-height:240px;max-height:300px;overflow:auto}.bubble{max-width:86%;padding:14px 16px;border:1px solid var(--line);border-radius:20px}.bubble .role{font-size:.72rem;text-transform:uppercase;letter-spacing:.14em;color:var(--muted)}.bubble p{margin:6px 0 0;line-height:1.55;color:var(--text)}.bubble.user{margin-left:auto;background:var(--orange-pale);border-color:rgba(255,107,53,.22)}.bubble.ai{background:#fff}.bubble.system{background:var(--orange-pale);border-color:rgba(255,107,53,.22)}
|
| 156 |
+
.brain-shell{padding:14px}.brain-head{display:flex;justify-content:space-between;gap:16px;align-items:flex-end;margin-bottom:10px}.eyebrow{margin:0 0 4px;font-size:.72rem;letter-spacing:.16em;text-transform:uppercase;color:var(--orange)}.brain-head h3{margin:0;font-size:1.02rem;color:var(--text)}.brain-help{color:var(--muted);font-size:.8rem}#dvnc-three-root{height:300px;border-radius:20px;border:1px solid var(--line);overflow:hidden;background:linear-gradient(180deg,#fff7f3 0%,#fff 100%);position:relative}
|
| 157 |
+
.working-orb{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:80px;height:80px;border-radius:50%;background:radial-gradient(circle at 30% 30%,rgba(255,143,102,.9),var(--orange));box-shadow:0 8px 32px rgba(255,107,53,.4),inset 0 -4px 12px rgba(0,0,0,.15);animation:orbPulse 2.5s ease-in-out infinite,orbFloat 4s ease-in-out infinite}
|
| 158 |
+
@keyframes orbPulse{0%,100%{box-shadow:0 8px 32px rgba(255,107,53,.4),inset 0 -4px 12px rgba(0,0,0,.15)}50%{box-shadow:0 12px 48px rgba(255,107,53,.7),inset 0 -4px 12px rgba(0,0,0,.2)}}
|
| 159 |
+
@keyframes orbFloat{0%,100%{transform:translate(-50%,-50%) translateY(0)}50%{transform:translate(-50%,-50%) translateY(-12px)}}
|
| 160 |
+
.tray-panel{padding:14px;display:flex;flex-direction:column;gap:10px}.tray{border:1px solid var(--line);border-radius:18px;background:#ffffff;overflow:hidden}.tray summary{list-style:none;display:grid;grid-template-columns:42px 1fr;gap:12px;align-items:center;padding:12px 14px;cursor:pointer}.tray summary::-webkit-details-marker{display:none}.tray .step{width:42px;height:42px;border-radius:14px;display:grid;place-items:center;color:#ffffff;font-weight:700;background:var(--orange);border:1px solid var(--orange-light)}.tray summary strong{display:block;color:var(--text)}.tray summary small{color:var(--muted);text-transform:uppercase;letter-spacing:.12em}.tray-body{padding:0 14px 14px 68px;color:var(--text);line-height:1.6}
|
| 161 |
+
.cards-panel{padding:16px}.candidate-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.candidate-card{perspective:1200px;min-height:250px}.candidate-card-inner{position:relative;min-height:250px;height:100%;transform-style:preserve-3d;transition:transform .8s cubic-bezier(.2,.7,.1,1)}.candidate-card:hover .candidate-card-inner,.candidate-card:focus .candidate-card-inner{transform:rotateY(180deg)}.candidate-face{position:absolute;inset:0;padding:18px;border-radius:22px;border:1px solid var(--line);backface-visibility:hidden;display:flex;flex-direction:column;gap:12px;box-shadow:0 4px 16px rgba(0,0,0,.06);background:#ffffff}.candidate-face.back{transform:rotateY(180deg);background:var(--orange-pale)}.top,.meta{display:flex;justify-content:space-between;gap:12px}.chip{font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--orange);padding:7px 10px;border-radius:999px;border:1px solid rgba(255,107,53,.2);background:var(--orange-pale)}.chip.alt{color:var(--blue);border-color:rgba(37,99,235,.2);background:rgba(37,99,235,.08)}.score{font-weight:700;color:var(--orange)}.candidate-face h4{margin:0;color:var(--text)}.candidate-face p{margin:0;color:var(--text);line-height:1.6}.meta{margin-top:auto;color:var(--muted)}
|
| 162 |
+
.gr-button-primary{background:linear-gradient(135deg,var(--orange),var(--orange-light))!important;color:#fff!important;border:none!important;font-weight:700!important;box-shadow:0 10px 24px rgba(255,107,53,.22)!important}.gr-button-secondary{background:#fff!important;color:#000!important;border:1px solid rgba(255,107,53,.18)!important}
|
| 163 |
+
@media (max-width:1180px){#app-shell .grid{grid-template-columns:1fr}.left{border-right:0;border-bottom:1px solid var(--line)}.model-strip,.candidate-grid{grid-template-columns:1fr}}
|
| 164 |
+
'''
|
| 165 |
+
|
| 166 |
+
HEAD = '''
|
| 167 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 168 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 169 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 170 |
+
<script type="importmap">
|
| 171 |
+
{
|
| 172 |
+
"imports": {
|
| 173 |
+
"three": "https://unpkg.com/three@0.156.0/build/three.module.js",
|
| 174 |
+
"three/addons/": "https://unpkg.com/three@0.156.0/examples/jsm/"
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
</script>
|
| 178 |
+
<script type="module">
|
| 179 |
+
import * as THREE from 'three';
|
| 180 |
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
| 181 |
+
|
| 182 |
+
function initDVNC(){
|
| 183 |
+
const root = document.getElementById('dvnc-three-root');
|
| 184 |
+
const graph = window.__DVNC_GRAPH__;
|
| 185 |
+
if(!root || !graph || root.dataset.ready === '1') return;
|
| 186 |
+
root.dataset.ready = '1';
|
| 187 |
+
const scene = new THREE.Scene();
|
| 188 |
+
const camera = new THREE.PerspectiveCamera(52, root.clientWidth / root.clientHeight, 0.1, 1000);
|
| 189 |
+
camera.position.set(0, 0, 120);
|
| 190 |
+
const renderer = new THREE.WebGLRenderer({ antialias:true, alpha:true });
|
| 191 |
+
renderer.setSize(root.clientWidth, root.clientHeight);
|
| 192 |
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
| 193 |
+
root.innerHTML='';
|
| 194 |
+
root.appendChild(renderer.domElement);
|
| 195 |
+
|
| 196 |
+
const controls = new OrbitControls(camera, renderer.domElement);
|
| 197 |
+
controls.enableDamping = true;
|
| 198 |
+
controls.autoRotate = true;
|
| 199 |
+
controls.autoRotateSpeed = .45;
|
| 200 |
+
controls.enablePan = false;
|
| 201 |
+
|
| 202 |
+
const ambient = new THREE.AmbientLight(0xffffff, .7); scene.add(ambient);
|
| 203 |
+
const key = new THREE.PointLight(0xff6b35, 2.5, 260); key.position.set(30, 40, 50); scene.add(key);
|
| 204 |
+
const fill = new THREE.PointLight(0xff8f66, 2.0, 240); fill.position.set(-40, -20, 40); scene.add(fill);
|
| 205 |
+
|
| 206 |
+
const nodeMeshes = {};
|
| 207 |
+
const active = new Set(graph.active_path || []);
|
| 208 |
+
const palette = {core:0xff6b35, domain:0xff8f66, bridge:0xffa280, mechanism:0xffb399, outcome:0xffc2ad, candidate:0xff8f66};
|
| 209 |
+
|
| 210 |
+
const nodeGeom = new THREE.SphereGeometry(1.8, 22, 22);
|
| 211 |
+
const haloGeom = new THREE.SphereGeometry(3.4, 22, 22);
|
| 212 |
+
|
| 213 |
+
for(const n of graph.nodes || []){
|
| 214 |
+
const mat = new THREE.MeshStandardMaterial({ color: palette[n.group] || 0x9aa8c7, emissive: active.has(n.id) ? 0xff6b35 : 0x000000, emissiveIntensity: active.has(n.id) ? 1.2 : 0.15, roughness:.3, metalness:.15 });
|
| 215 |
+
const mesh = new THREE.Mesh(nodeGeom, mat);
|
| 216 |
+
mesh.position.set(n.x, n.y, n.z);
|
| 217 |
+
scene.add(mesh);
|
| 218 |
+
nodeMeshes[n.id] = mesh;
|
| 219 |
+
if(active.has(n.id)){
|
| 220 |
+
const halo = new THREE.Mesh(haloGeom, new THREE.MeshBasicMaterial({ color:0xff6b35, transparent:true, opacity:.08 }));
|
| 221 |
+
halo.position.copy(mesh.position);
|
| 222 |
+
scene.add(halo);
|
| 223 |
+
mesh.userData.halo = halo;
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
function edgeActive(a,b){
|
| 228 |
+
const path = graph.active_path || [];
|
| 229 |
+
const ia = path.indexOf(a), ib = path.indexOf(b);
|
| 230 |
+
return ia >= 0 && ib >= 0 && Math.abs(ia-ib) === 1;
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
for(const e of graph.edges || []){
|
| 234 |
+
const [a,b] = e;
|
| 235 |
+
if(!nodeMeshes[a] || !nodeMeshes[b]) continue;
|
| 236 |
+
const pts = [nodeMeshes[a].position, nodeMeshes[b].position];
|
| 237 |
+
const geo = new THREE.BufferGeometry().setFromPoints(pts);
|
| 238 |
+
const mat = new THREE.LineBasicMaterial({ color: edgeActive(a,b) ? 0xff6b35 : 0xcccccc, transparent:true, opacity: edgeActive(a,b) ? 1 : .45 }); const line = new THREE.Line(geo, mat);
|
| 239 |
+
line.userData.active = edgeActive(a,b);
|
| 240 |
+
scene.add(line);
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
const stars = new THREE.Points(
|
| 244 |
+
new THREE.BufferGeometry().setAttribute('position', new THREE.Float32BufferAttribute(Array.from({length:1200},()=> (Math.random()-.5)*220), 3)),
|
| 245 |
+
new THREE.PointsMaterial({ color:0x8da6d8, size:.35, transparent:true, opacity:.6 })
|
| 246 |
+
);
|
| 247 |
+
scene.add(stars);
|
| 248 |
+
|
| 249 |
+
const clock = new THREE.Clock();
|
| 250 |
+
function animate(){
|
| 251 |
+
requestAnimationFrame(animate);
|
| 252 |
+
const t = clock.getElapsedTime();
|
| 253 |
+
Object.values(nodeMeshes).forEach((m,i)=>{
|
| 254 |
+
m.scale.setScalar(1 + Math.sin(t*1.6 + i)*0.03);
|
| 255 |
+
if(m.userData.halo){
|
| 256 |
+
const s = 1 + (Math.sin(t*2.2 + i)+1)*0.08;
|
| 257 |
+
m.userData.halo.scale.setScalar(s);
|
| 258 |
+
}
|
| 259 |
+
});
|
| 260 |
+
controls.update();
|
| 261 |
+
renderer.render(scene,camera);
|
| 262 |
+
}
|
| 263 |
+
animate();
|
| 264 |
+
|
| 265 |
+
const resize = ()=>{
|
| 266 |
+
if(!root.clientWidth || !root.clientHeight) return;
|
| 267 |
+
camera.aspect = root.clientWidth / root.clientHeight;
|
| 268 |
+
camera.updateProjectionMatrix();
|
| 269 |
+
renderer.setSize(root.clientWidth, root.clientHeight);
|
| 270 |
+
};
|
| 271 |
+
window.addEventListener('resize', resize, { passive:true });
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
function syncModelCards(){
|
| 275 |
+
const root = document;
|
| 276 |
+
const radios = root.querySelectorAll('input[name="dvnc-model-card"]');
|
| 277 |
+
const gradioRadios = root.querySelectorAll('#dvnc-model-radio input[type="radio"]');
|
| 278 |
+
if(!radios.length || !gradioRadios.length) return;
|
| 279 |
+
|
| 280 |
+
const setActiveCard = (value) => {
|
| 281 |
+
root.querySelectorAll('.model-pill').forEach(card => card.classList.remove('active'));
|
| 282 |
+
const activeInput = root.querySelector(`input[name="dvnc-model-card"][value="${value}"]`);
|
| 283 |
+
if(activeInput){
|
| 284 |
+
activeInput.checked = true;
|
| 285 |
+
activeInput.closest('.model-pill')?.classList.add('active');
|
| 286 |
+
}
|
| 287 |
+
};
|
| 288 |
+
|
| 289 |
+
radios.forEach(radio => {
|
| 290 |
+
if(radio.dataset.bound === '1') return;
|
| 291 |
+
radio.dataset.bound = '1';
|
| 292 |
+
radio.addEventListener('change', () => {
|
| 293 |
+
const next = radio.value;
|
| 294 |
+
const target = Array.from(gradioRadios).find(r => r.value === next);
|
| 295 |
+
if(target){
|
| 296 |
+
target.click();
|
| 297 |
+
}
|
| 298 |
+
setActiveCard(next);
|
| 299 |
+
});
|
| 300 |
+
});
|
| 301 |
+
|
| 302 |
+
const checked = Array.from(gradioRadios).find(r => r.checked);
|
| 303 |
+
if(checked){
|
| 304 |
+
setActiveCard(checked.value);
|
| 305 |
+
}
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
new MutationObserver(()=>{initDVNC(); syncModelCards();}).observe(document.documentElement,{childList:true,subtree:true});
|
| 309 |
+
window.addEventListener('load', ()=>{initDVNC(); syncModelCards();});
|
| 310 |
+
setInterval(()=>{initDVNC(); syncModelCards();}, 1200);
|
| 311 |
+
</script>
|
| 312 |
+
'''
|
| 313 |
+
|
| 314 |
+
with gr.Blocks(css=CSS, head=HEAD, theme=gr.themes.Base(), fill_height=True) as demo:
|
| 315 |
+
gr.HTML('''
|
| 316 |
+
<div id="app-shell">
|
| 317 |
+
<div class="hero hero-plate">
|
| 318 |
+
<div class="hero-copy">
|
| 319 |
+
<div class="brand">
|
| 320 |
+
<div class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M5 17L12 4l7 13"/><path d="M8.5 12.5h7"/><circle cx="12" cy="12" r="1.8" fill="currentColor" stroke="none"/></svg></div>
|
| 321 |
+
<div>
|
| 322 |
+
<h1>DVNC.AI</h1>
|
| 323 |
+
<p>Connectome-native reasoning · premium scientific discovery interface</p>
|
| 324 |
+
</div>
|
| 325 |
+
</div>
|
| 326 |
+
<div class="live"><i></i><span>Claude-linked runtime</span></div>
|
| 327 |
+
</div>
|
| 328 |
+
<div class="hero-orb-wrap">
|
| 329 |
+
<div class="hero-orb">
|
| 330 |
+
<div class="hero-orb-core"></div>
|
| 331 |
+
<div class="hero-orb-ring ring-a"></div>
|
| 332 |
+
<div class="hero-orb-ring ring-b"></div>
|
| 333 |
+
<div class="hero-orb-glow"></div>
|
| 334 |
+
</div>
|
| 335 |
+
</div>
|
| 336 |
+
</div>
|
| 337 |
+
''')
|
| 338 |
+
model = gr.Radio(
|
| 339 |
+
choices=MODELS,
|
| 340 |
+
value="DVNC Sovereign",
|
| 341 |
+
label="Model family",
|
| 342 |
+
elem_id="dvnc-model-cards",
|
| 343 |
+
elem_classes=["dvnc-model-cards"]
|
| 344 |
+
)
|
| 345 |
+
query = gr.Textbox(label="Discovery query", lines=3, elem_classes=["querybox"], placeholder="Ask a scientific question, anomaly, mechanism, or hypothesis target…")
|
| 346 |
+
with gr.Row():
|
| 347 |
+
run_btn = gr.Button("Run discovery", variant="primary")
|
| 348 |
+
example_btn = gr.Button("Load example", variant="secondary")
|
| 349 |
+
chat = gr.HTML('<div class="chat-panel panel"><div class="bubble ai"><span class="role">DVNC</span><p>Enter a discovery prompt to trigger Claude-backed orchestration or demo mode if no API key is set.</p></div></div>')
|
| 350 |
+
gr.HTML('</section><section class="right">')
|
| 351 |
+
connectome = gr.HTML(CONNECTOME_SNIPPET)
|
| 352 |
+
trays = gr.HTML('<div class="panel tray-panel"></div>')
|
| 353 |
+
cards = gr.HTML('<div class="panel cards-panel"></div>')
|
| 354 |
+
output = gr.Markdown('# Discovery Output\n\nAwaiting query.')
|
| 355 |
+
gr.HTML('</section></div></div>')
|
| 356 |
+
|
| 357 |
+
example_btn.click(lambda: "How could a self-assembling conductive biomaterial improve cardiac repair by converting mechanical strain into regenerative signalling?", outputs=query)
|
| 358 |
+
run_btn.click(run, inputs=[query, model], outputs=[chat, connectome, trays, cards, output])
|
| 359 |
+
|
| 360 |
+
if __name__ == '__main__':
|
| 361 |
+
demo.launch()
|