Adi12345 commited on
Commit
f2bf7e3
·
verified ·
1 Parent(s): d072b6d

Upload app.py

Browse files
Files changed (1) hide show
  1. 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()