Adi12345 commited on
Commit
f0902f0
·
verified ·
1 Parent(s): f98ca1d

Upload app.py

Browse files
Files changed (1) hide show
  1. dvnc_ai_v2_hf/app.py +323 -422
dvnc_ai_v2_hf/app.py CHANGED
@@ -1,468 +1,369 @@
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
- graph = data.get("graph") or {}
73
- graph.setdefault("nodes", [])
74
- graph.setdefault("edges", [])
75
- graph.setdefault("active_path", [])
76
- return f"""
77
- <script>
78
- window.__DVNC_GRAPH__ = {json.dumps(graph)};
79
- </script>
80
- {CONNECTOME_SNIPPET}
81
- """
82
 
83
 
84
- def run(query, model_name):
85
- data = run_discovery(query, model_name)
86
- return (
87
- build_chat_html(query, data),
88
- build_frontend_payload(data),
89
- build_reasoning_html(data.get("reasoning", [])),
90
- build_cards_html(data.get("cards", [])),
91
- build_metrics_md(data.get("metrics", {}), data.get("model_used", "unknown")),
92
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  CSS = r'''
95
- :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)}
96
- html,body,.gradio-container{background:linear-gradient(180deg,#fffaf7 0%,#fff 100%)!important;color:var(--text)!important;font-family:Inter,system-ui,sans-serif}
97
- .gradio-container{max-width:1480px!important;padding:18px!important} footer{display:none!important}
98
- #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}
99
- #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}
100
- .hero{display:flex;justify-content:space-between;align-items:center;gap:12px}
101
- .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%)}
102
- .hero-copy{display:flex;flex-direction:column;justify-content:space-between;gap:16px;min-height:118px}
103
- .brand{display:flex;gap:14px;align-items:center}
104
- .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))}
105
- .logo svg{width:26px;height:26px;color:var(--orange)}
106
- .brand h1{margin:0;font-size:1.18rem;color:var(--text)}
107
- .brand p{margin:4px 0 0;color:var(--muted);font-size:.9rem}
108
- .live{display:flex;gap:10px;align-items:center;color:var(--text)}
109
- .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}
110
- .hero-orb-wrap{margin-left:auto;display:flex;align-items:center;justify-content:center;min-width:180px}
111
- .hero-orb{position:relative;width:110px;height:110px;display:grid;place-items:center}
112
- .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}
113
- .hero-orb-ring{position:absolute;border-radius:50%;border:1px solid rgba(255,107,53,.16)}
114
- .hero-orb-ring.ring-a{width:86px;height:86px;animation:spinSlow 10s linear infinite}
115
- .hero-orb-ring.ring-b{width:108px;height:108px;border-color:rgba(255,107,53,.10);animation:spinReverse 14s linear infinite}
116
- .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%)}
117
- @keyframes heroOrbFloat{0%,100%{transform:translateY(0) scale(1)}50%{transform:translateY(-5px) scale(1.03)}}
118
- @keyframes spinSlow{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
119
- @keyframes spinReverse{from{transform:rotate(360deg)}to{transform:rotate(0deg)}}
120
- @keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.6;transform:scale(.9)}}
121
- .panel{border:1px solid var(--line);background:#ffffff;border-radius:22px;box-shadow:0 2px 12px rgba(0,0,0,.04)}
122
- .dvnc-model-cards .gr-radio{
123
- display:grid!important;
124
- grid-template-columns:repeat(3,1fr);
125
- gap:12px;
126
- }
127
-
128
- .dvnc-model-cards label{
129
- display:flex!important;
130
- flex-direction:column;
131
- gap:4px;
132
- padding:14px!important;
133
- border:1px solid var(--line)!important;
134
- border-radius:18px!important;
135
- background:#fafbfc!important;
136
- cursor:pointer!important;
137
- transition:all .2s ease!important;
138
- min-height:84px;
139
- justify-content:center;
140
- }
141
-
142
- .dvnc-model-cards label:hover{
143
- border-color:var(--orange)!important;
144
- transform:translateY(-1px);
145
- box-shadow:0 10px 22px rgba(255,107,53,.08);
146
- }
147
-
148
- .dvnc-model-cards input[type="radio"]{
149
- margin-bottom:8px;
150
- accent-color:var(--orange);
151
- }
152
-
153
- .dvnc-model-cards label:has(input[type="radio"]:checked){
154
- border-color:var(--orange)!important;
155
- background:var(--orange-pale)!important;
156
- box-shadow:0 10px 24px rgba(255,107,53,.12);
157
  }
158
- .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}
159
- .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)}
160
- .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{
161
- height:340px;
162
- border-radius:20px;
163
- border:1px solid var(--line);
164
- overflow:hidden;
165
- background:
166
- radial-gradient(circle at 50% 45%, rgba(255,107,53,.10), rgba(255,107,53,0) 34%),
167
- linear-gradient(180deg,#fff8f5 0%, #fff 100%);
168
- position:relative;
169
- display:block;
170
  }
171
- .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}
172
- @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)}}
173
- @keyframes orbFloat{0%,100%{transform:translate(-50%,-50%) translateY(0)}50%{transform:translate(-50%,-50%) translateY(-12px)}}
174
- .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}
175
- .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)}
176
- .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}
177
- @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}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  '''
179
 
180
  HEAD = '''
181
  <link rel="preconnect" href="https://fonts.googleapis.com">
182
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
183
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
184
- <script type="importmap">
185
- {
186
- "imports": {
187
- "three": "https://unpkg.com/three@0.156.0/build/three.module.js",
188
- "three/addons/": "https://unpkg.com/three@0.156.0/examples/jsm/"
189
- }
190
- }
191
- </script>
192
- <script type="module">
193
- import * as THREE from 'three';
194
- import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
195
-
196
- function initDVNC(){
197
- const root = document.getElementById('dvnc-three-root');
198
- if(!root || root.clientWidth === 0 || root.clientHeight === 0) return;
199
-
200
- let graph = window.__DVNC_GRAPH__ || {};
201
- const hasRealGraph = Array.isArray(graph.nodes) && graph.nodes.length > 0;
202
-
203
- if(!hasRealGraph){
204
- graph = {
205
- nodes: [
206
- {id:"query", group:"core", x:0, y:0, z:0},
207
- {id:"mechanics", group:"domain", x:-26, y:16, z:-8},
208
- {id:"conductivity", group:"bridge", x:26, y:12, z:10},
209
- {id:"immune", group:"mechanism", x:-18, y:-18, z:14},
210
- {id:"repair", group:"outcome", x:24, y:-16, z:-12},
211
- {id:"candidate", group:"candidate", x:0, y:26, z:18}
212
- ],
213
- edges: [
214
- ["query","mechanics"],
215
- ["query","conductivity"],
216
- ["query","immune"],
217
- ["mechanics","candidate"],
218
- ["conductivity","candidate"],
219
- ["immune","repair"],
220
- ["candidate","repair"]
221
- ],
222
- active_path: ["query","conductivity","candidate","repair"]
223
- };
224
- }
225
-
226
- root.innerHTML = '';
227
- root.dataset.ready = '1';
228
-
229
- const scene = new THREE.Scene();
230
- const camera = new THREE.PerspectiveCamera(54, root.clientWidth / root.clientHeight, 0.1, 1000);
231
- camera.position.set(0, 0, 115);
232
-
233
- const renderer = new THREE.WebGLRenderer({ antialias:true, alpha:true });
234
- renderer.setSize(root.clientWidth, root.clientHeight);
235
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
236
- renderer.outputColorSpace = THREE.SRGBColorSpace;
237
- root.appendChild(renderer.domElement);
238
-
239
- const controls = new OrbitControls(camera, renderer.domElement);
240
- controls.enableDamping = true;
241
- controls.enablePan = false;
242
- controls.autoRotate = true;
243
- controls.autoRotateSpeed = 0.55;
244
-
245
- scene.add(new THREE.AmbientLight(0xffffff, 1.15));
246
-
247
- const key = new THREE.PointLight(0xff6b35, 4.2, 320);
248
- key.position.set(38, 42, 60);
249
- scene.add(key);
250
-
251
- const fill = new THREE.PointLight(0xffb089, 2.4, 280);
252
- fill.position.set(-45, -18, 42);
253
- scene.add(fill);
254
-
255
- const rim = new THREE.PointLight(0xffffff, 1.4, 260);
256
- rim.position.set(0, 0, 90);
257
- scene.add(rim);
258
-
259
- const active = new Set(graph.active_path || []);
260
- const nodeMeshes = {};
261
- const palette = {
262
- core: 0xff6b35,
263
- domain: 0xff955f,
264
- bridge: 0xff8a4c,
265
- mechanism: 0xffb38f,
266
- outcome: 0xffc7b3,
267
- candidate: 0xff7b47
268
- };
269
-
270
- const nodeGeom = new THREE.SphereGeometry(3.2, 28, 28);
271
- const haloGeom = new THREE.SphereGeometry(5.8, 28, 28);
272
-
273
- for(const n of graph.nodes || []){
274
- const isActive = active.has(n.id);
275
- const color = palette[n.group] || 0xd38d73;
276
-
277
- const mat = new THREE.MeshStandardMaterial({
278
- color,
279
- emissive: isActive ? 0xff6b35 : color,
280
- emissiveIntensity: isActive ? 1.9 : 0.28,
281
- roughness: 0.22,
282
- metalness: 0.1
283
- });
284
-
285
- const mesh = new THREE.Mesh(nodeGeom, mat);
286
- mesh.position.set(n.x || 0, n.y || 0, n.z || 0);
287
- scene.add(mesh);
288
- nodeMeshes[n.id] = mesh;
289
-
290
- const halo = new THREE.Mesh(
291
- haloGeom,
292
- new THREE.MeshBasicMaterial({
293
- color: isActive ? 0xff6b35 : color,
294
- transparent: true,
295
- opacity: isActive ? 0.14 : 0.05
296
- })
297
- );
298
- halo.position.copy(mesh.position);
299
- scene.add(halo);
300
- mesh.userData.halo = halo;
301
- }
302
-
303
- function edgeActive(a,b){
304
- const path = graph.active_path || [];
305
- const ia = path.indexOf(a), ib = path.indexOf(b);
306
- return ia >= 0 && ib >= 0 && Math.abs(ia - ib) === 1;
307
- }
308
-
309
- for(const e of graph.edges || []){
310
- const [a,b] = e;
311
- if(!nodeMeshes[a] || !nodeMeshes[b]) continue;
312
-
313
- const pts = [nodeMeshes[a].position, nodeMeshes[b].position];
314
- const geo = new THREE.BufferGeometry().setFromPoints(pts);
315
- const isActive = edgeActive(a,b);
316
-
317
- const line = new THREE.Line(
318
- geo,
319
- new THREE.LineBasicMaterial({
320
- color: isActive ? 0xff6b35 : 0xd9c4bb,
321
- transparent: true,
322
- opacity: isActive ? 1.0 : 0.72
323
- })
324
- );
325
-
326
- line.userData.active = isActive;
327
- scene.add(line);
328
- }
329
-
330
- const starPositions = [];
331
- for(let i = 0; i < 900; i++){
332
- starPositions.push((Math.random() - 0.5) * 220);
333
- starPositions.push((Math.random() - 0.5) * 220);
334
- starPositions.push((Math.random() - 0.5) * 220);
335
- }
336
-
337
- const stars = new THREE.Points(
338
- new THREE.BufferGeometry().setAttribute(
339
- 'position',
340
- new THREE.Float32BufferAttribute(starPositions, 3)
341
- ),
342
- new THREE.PointsMaterial({
343
- color: 0xf1b6a1,
344
- size: 0.85,
345
- transparent: true,
346
- opacity: 0.55
347
- })
348
- );
349
- scene.add(stars);
350
-
351
- const clock = new THREE.Clock();
352
- function animate(){
353
- requestAnimationFrame(animate);
354
- const t = clock.getElapsedTime();
355
-
356
- Object.values(nodeMeshes).forEach((m, i) => {
357
- const pulse = 1 + Math.sin(t * 1.8 + i) * 0.06;
358
- m.scale.setScalar(pulse);
359
- if(m.userData.halo){
360
- const hs = 1 + (Math.sin(t * 2.3 + i) + 1) * 0.08;
361
- m.userData.halo.scale.setScalar(hs);
362
- }
363
- });
364
-
365
- stars.rotation.y += 0.0008;
366
- controls.update();
367
- renderer.render(scene, camera);
368
- }
369
- animate();
370
-
371
- const resize = () => {
372
- if(!root.clientWidth || !root.clientHeight) return;
373
- camera.aspect = root.clientWidth / root.clientHeight;
374
- camera.updateProjectionMatrix();
375
- renderer.setSize(root.clientWidth, root.clientHeight);
376
- };
377
-
378
- window.addEventListener('resize', resize, { passive:true });
379
- }
380
-
381
- function syncModelCards(){
382
- const root = document;
383
- const radios = root.querySelectorAll('input[name="dvnc-model-card"]');
384
- const gradioRadios = root.querySelectorAll('#dvnc-model-radio input[type="radio"]');
385
- if(!radios.length || !gradioRadios.length) return;
386
-
387
- const setActiveCard = (value) => {
388
- root.querySelectorAll('.model-pill').forEach(card => card.classList.remove('active'));
389
- const activeInput = root.querySelector(`input[name="dvnc-model-card"][value="${value}"]`);
390
- if(activeInput){
391
- activeInput.checked = true;
392
- activeInput.closest('.model-pill')?.classList.add('active');
393
- }
394
- };
395
-
396
- radios.forEach(radio => {
397
- if(radio.dataset.bound === '1') return;
398
- radio.dataset.bound = '1';
399
- radio.addEventListener('change', () => {
400
- const next = radio.value;
401
- const target = Array.from(gradioRadios).find(r => r.value === next);
402
- if(target){
403
- target.click();
404
- }
405
- setActiveCard(next);
406
- });
407
- });
408
-
409
- const checked = Array.from(gradioRadios).find(r => r.checked);
410
- if(checked){
411
- setActiveCard(checked.value);
412
- }
413
- }
414
-
415
- new MutationObserver(()=>{initDVNC(); syncModelCards();}).observe(document.documentElement,{childList:true,subtree:true});
416
- window.addEventListener('load', ()=>{initDVNC(); syncModelCards();});
417
- setInterval(()=>{initDVNC(); syncModelCards();}, 1200);
418
- </script>
419
  '''
420
 
421
  with gr.Blocks(css=CSS, head=HEAD, theme=gr.themes.Base(), fill_height=True) as demo:
422
  gr.HTML('''
423
- <div id="app-shell">
424
- <div class="hero hero-plate">
425
- <div class="hero-copy">
426
- <div class="brand">
427
- <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>
428
- <div>
429
- <h1>DVNC.AI</h1>
430
- <p>Connectome-native reasoning · premium scientific discovery interface</p>
 
 
 
 
431
  </div>
 
432
  </div>
433
- <div class="live"><i></i><span>Claude-linked runtime</span></div>
434
- </div>
435
- <div class="hero-orb-wrap">
436
- <div class="hero-orb">
437
- <div class="hero-orb-core"></div>
438
- <div class="hero-orb-ring ring-a"></div>
439
- <div class="hero-orb-ring ring-b"></div>
440
- <div class="hero-orb-glow"></div>
441
- </div>
442
- </div>
443
- </div>
444
  ''')
445
- model = gr.Radio(
446
- choices=MODELS,
447
- value="DVNC Sovereign",
448
- label="Model family",
449
- elem_id="dvnc-model-cards",
450
- elem_classes=["dvnc-model-cards"]
451
- )
452
- query = gr.Textbox(label="Discovery query", lines=3, elem_classes=["querybox"], placeholder="Ask a scientific question, anomaly, mechanism, or hypothesis target…")
453
  with gr.Row():
454
  run_btn = gr.Button("Run discovery", variant="primary")
455
  example_btn = gr.Button("Load example", variant="secondary")
456
- 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>')
457
  gr.HTML('</section><section class="right">')
458
- connectome = gr.HTML(CONNECTOME_SNIPPET)
459
- trays = gr.HTML('<div class="panel tray-panel"></div>')
460
- cards = gr.HTML('<div class="panel cards-panel"></div>')
461
- output = gr.Markdown('# Discovery Output\n\nAwaiting query.')
462
  gr.HTML('</section></div></div>')
463
 
464
- example_btn.click(lambda: "How could a self-assembling conductive biomaterial improve cardiac repair by converting mechanical strain into regenerative signalling?", outputs=query)
465
- run_btn.click(run, inputs=[query, model], outputs=[chat, connectome, trays, cards, output])
 
 
 
466
 
467
- if __name__ == '__main__':
468
- demo.launch()
 
1
  import json
2
+ import math
3
+ import random
4
  import gradio as gr
 
5
 
6
+ MODELS = [
7
+ {"name": "DVNC Sovereign", "tag": "flagship", "desc": "Maximum depth orchestration for frontier discovery"},
8
+ {"name": "DVNC Atlas", "tag": "research", "desc": "Balanced reasoning, graph traversal, and synthesis"},
9
+ {"name": "DVNC Curie", "tag": "lab", "desc": "Experimental hypothesis generation for anomalous signals"},
10
+ ]
11
+
12
+ AGENTS = [
13
+ "Query Interpreter",
14
+ "Graph Divergence Mapper",
15
+ "Evidence Harvester",
16
+ "Analogy Engine",
17
+ "Hypothesis Composer",
18
+ "Adversarial Critic",
19
+ "Experimental Program Designer",
20
+ ]
21
+
22
+ NODES = [
23
+ {"id": "seed", "label": "Seed Query", "group": "core", "x": 0, "y": 0, "z": 0},
24
+ {"id": "bio", "label": "Biomaterials", "group": "domain", "x": 18, "y": 12, "z": -8},
25
+ {"id": "card", "label": "Cardiac Repair", "group": "domain", "x": 34, "y": 2, "z": 14},
26
+ {"id": "nano", "label": "Nanostructure", "group": "bridge", "x": 14, "y": -18, "z": 16},
27
+ {"id": "selfasm", "label": "Self-Assembly", "group": "bridge", "x": 30, "y": -16, "z": -16},
28
+ {"id": "electro", "label": "Electro-signalling", "group": "mechanism", "x": 48, "y": 12, "z": -10},
29
+ {"id": "immune", "label": "Immune Modulation", "group": "mechanism", "x": 54, "y": -8, "z": 10},
30
+ {"id": "trial", "label": "Validation Path", "group": "outcome", "x": 70, "y": 0, "z": 0},
31
+ {"id": "alt1", "label": "Piezoelectric Scaffold", "group": "candidate", "x": 44, "y": 28, "z": 14},
32
+ {"id": "alt2", "label": "Peptide Mesh", "group": "candidate", "x": 42, "y": -28, "z": -14},
33
+ ]
34
+
35
+ EDGES = [
36
+ ("seed", "bio"), ("seed", "nano"), ("bio", "card"), ("nano", "selfasm"),
37
+ ("selfasm", "electro"), ("card", "immune"), ("electro", "trial"), ("immune", "trial"),
38
+ ("card", "alt1"), ("selfasm", "alt2"), ("alt1", "trial"), ("alt2", "trial")
39
+ ]
40
+
41
+ DEFAULT_PATH = ["seed", "nano", "selfasm", "electro", "trial"]
42
+
43
+ CANDIDATES = [
44
+ {
45
+ "title": "Piezoelectric Scaffold Cascade",
46
+ "front": "Use mechano-electric scaffolds to convert cardiac strain into micro-current signalling.",
47
+ "back": "Discovery path: anomalous healing signal -> piezoelectric analog -> ion-channel entrainment -> tissue regeneration. Risk: power density and fibrosis coupling.",
48
+ "score": 92,
49
+ "novelty": "High",
50
+ "agent": "Hypothesis Composer"
51
+ },
52
+ {
53
+ "title": "Peptide Self-Assembly Mesh",
54
+ "front": "Deploy dynamic peptide meshes that self-assemble around damaged myocardium and guide repair.",
55
+ "back": "Discovery path: self-assembly -> local immune choreography -> regenerative substrate formation. Risk: degradation timing and targeting specificity.",
56
+ "score": 88,
57
+ "novelty": "High",
58
+ "agent": "Analogy Engine"
59
+ },
60
+ {
61
+ "title": "Immune-Tuned Conductive Hydrogel",
62
+ "front": "Blend conductivity with macrophage-state modulation to reduce scarring and restore conduction.",
63
+ "back": "Discovery path: inflammation mismatch -> conductive medium -> macrophage polarization -> synchronized healing. Risk: persistence and biocompatibility.",
64
+ "score": 85,
65
+ "novelty": "Medium-High",
66
+ "agent": "Adversarial Critic"
67
+ }
68
+ ]
69
+
70
+
71
+ def build_connectome_html(path_ids):
72
+ active = set(path_ids)
73
+ node_map = {n['id']: n for n in NODES}
74
+ lines = []
75
+ for a, b in EDGES:
76
+ na, nb = node_map[a], node_map[b]
77
+ active_edge = a in active and b in active and abs(path_ids.index(a) - path_ids.index(b)) == 1 if a in path_ids and b in path_ids else False
78
+ cls = "edge active" if active_edge else "edge"
79
+ lines.append(f'<line class="{cls}" x1="{na["x"]*6+420:.1f}" y1="{na["y"]*6+280:.1f}" x2="{nb["x"]*6+420:.1f}" y2="{nb["y"]*6+280:.1f}" />')
80
+ circles = []
81
+ labels = []
82
+ for n in NODES:
83
+ cx = n['x']*6 + 420
84
+ cy = n['y']*6 + 280
85
+ cls = f"node {n['group']} {'active' if n['id'] in active else ''}"
86
+ circles.append(f'<g class="node-wrap"><circle class="{cls}" cx="{cx:.1f}" cy="{cy:.1f}" r="{17 if n["id"] in active else 12}" /><circle class="halo {'active' if n['id'] in active else ''}" cx="{cx:.1f}" cy="{cy:.1f}" r="{28 if n["id"] in active else 0}" /></g>')
87
+ labels.append(f'<text class="label {'active' if n["id"] in active else ''}" x="{cx+16:.1f}" y="{cy-16:.1f}">{n["label"]}</text>')
88
  return f'''
89
+ <div class="brain-shell">
90
+ <div class="brain-header">
91
+ <div>
92
+ <p class="eyebrow">Connectome</p>
93
+ <h3>Concept Brain</h3>
94
+ </div>
95
+ <div class="brain-legend">
96
+ <span><i class="dot dot-live"></i> active route</span>
97
+ <span><i class="dot dot-node"></i> concept nodes</span>
98
+ </div>
99
+ </div>
100
+ <div class="brain-stage">
101
+ <div class="orb orb-a"></div><div class="orb orb-b"></div>
102
+ <svg viewBox="0 0 840 560" class="brain-svg" role="img" aria-label="DVNC connectome visualisation">
103
+ <defs>
104
+ <filter id="glow"><feGaussianBlur stdDeviation="4" result="c"/><feMerge><feMergeNode in="c"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
105
+ </defs>
106
+ {''.join(lines)}
107
+ {''.join(circles)}
108
+ {''.join(labels)}
109
+ </svg>
110
+ </div>
111
  </div>
112
  '''
113
 
114
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  def build_cards_html(cards):
116
+ items = []
117
+ for i, c in enumerate(cards):
118
+ items.append(f'''
119
  <article class="candidate-card" tabindex="0">
120
  <div class="candidate-card-inner">
121
+ <div class="candidate-face candidate-front">
122
+ <div class="candidate-top"><span class="chip">{c['agent']}</span><span class="score">{c['score']}</span></div>
123
  <h4>{c['title']}</h4>
124
  <p>{c['front']}</p>
125
+ <div class="meta-row"><span>Novelty</span><strong>{c['novelty']}</strong></div>
126
+ <button class="mini">Flip insight</button>
127
  </div>
128
+ <div class="candidate-face candidate-back">
129
+ <div class="candidate-top"><span class="chip alt">Alternative path</span><span class="score">{c['score']}</span></div>
130
  <h4>{c['title']}</h4>
131
  <p>{c['back']}</p>
132
+ <div class="meta-row"><span>Swap into route</span><strong>Enabled</strong></div>
133
+ <button class="mini">Return</button>
134
  </div>
135
  </div>
136
  </article>
137
  ''')
138
+ return '<div class="candidate-grid">' + ''.join(items) + '</div>'
139
+
140
+
141
+ def build_agent_timeline(reasoning):
142
+ rows = []
143
+ for r in reasoning:
144
+ rows.append(f'''
145
+ <div class="agent-step">
146
+ <div class="agent-index">{r['step']}</div>
147
+ <div class="agent-copy">
148
+ <div class="agent-head"><h4>{r['agent']}</h4><span>{r['tag']}</span></div>
149
+ <p>{r['summary']}</p>
150
+ </div>
151
+ </div>
152
+ ''')
153
+ return '<div class="timeline">' + ''.join(rows) + '</div>'
 
 
 
 
 
 
154
 
155
 
156
+ def build_chat_html(query, result):
157
+ bubbles = f'''
158
+ <div class="chat-thread">
159
+ <div class="bubble bubble-user">
160
+ <span class="role">You</span>
161
+ <p>{query}</p>
162
+ </div>
163
+ <div class="bubble bubble-ai">
164
+ <span class="role">DVNC Sovereign</span>
165
+ <p>{result['summary']}</p>
166
+ </div>
167
+ <div class="bubble bubble-system">
168
+ <span class="role">Discovery Signal</span>
169
+ <p><strong>Primary hypothesis:</strong> {result['primary_hypothesis']}</p>
170
+ </div>
171
+ </div>
172
+ '''
173
+ return bubbles
174
+
175
+
176
+ def build_models_html(selected):
177
+ items = []
178
+ for m in MODELS:
179
+ active = 'active' if m['name'] == selected else ''
180
+ items.append(f'''<div class="model-pill {active}"><span class="model-name">{m['name']}</span><span class="model-tag">{m['tag']}</span><small>{m['desc']}</small></div>''')
181
+ return '<div class="model-switcher">' + ''.join(items) + '</div>'
182
+
183
+
184
+ def run_discovery(query, model_name):
185
+ random.seed(len(query) + len(model_name))
186
+ if "curie" in query.lower() or "einstein" in query.lower():
187
+ primary = "Map the anomaly first, then force a distant analogy before composing the experimental programme."
188
+ path = ["seed", "bio", "card", "immune", "trial"]
189
+ else:
190
+ primary = "Use a self-assembling conductive scaffold that transforms mechanical strain into local regenerative signalling."
191
+ path = DEFAULT_PATH
192
+ summaries = [
193
+ "Normalises the user prompt into a graph-searchable seed and isolates the tension inside the question.",
194
+ "Finds remote conceptual bridges instead of staying near the starting domain cluster.",
195
+ "Pulls evidence packets and conflict signals required for grounded hypothesis formation.",
196
+ "Generates cross-domain analogies with a bias toward mechanism transfer rather than keyword similarity.",
197
+ "Composes the lead hypothesis and two structurally different variants.",
198
+ "Attacks weak assumptions, hidden confounders, and feasibility gaps.",
199
+ "Produces a staged validation plan with measurable falsification criteria."
200
+ ]
201
+ reasoning = [{"step": i+1, "agent": AGENTS[i], "tag": ["input","graph","evidence","analogy","compose","critique","experiment"][i], "summary": summaries[i]} for i in range(7)]
202
+ result = {
203
+ "summary": "A deeper route was chosen through the concept graph, with live alternatives preserved as candidate cards so the reasoning path can be swapped rather than hidden.",
204
+ "primary_hypothesis": primary,
205
+ "reasoning": reasoning,
206
+ "cards": CANDIDATES,
207
+ "path": path,
208
+ "metrics": {
209
+ "Novelty": 93,
210
+ "Mechanistic clarity": 89,
211
+ "Experimental tractability": 82,
212
+ "Cross-domain distance": 91
213
+ }
214
+ }
215
+ chat_html = build_chat_html(query, result)
216
+ connectome_html = build_connectome_html(path)
217
+ cards_html = build_cards_html(CANDIDATES)
218
+ timeline_html = build_agent_timeline(reasoning)
219
+ metrics = "\n".join([f"- {k}: {v}/100" for k, v in result["metrics"].items()])
220
+ hypothesis = f"""# Discovery Output\n\n**Model:** {model_name}\n\n**Primary hypothesis:** {result['primary_hypothesis']}\n\n## Scoring\n{metrics}\n\n## Experimental outline\n1. Construct the candidate material or protocol.\n2. Test mechanistic signal expression under controlled conditions.\n3. Compare against baseline and nearest-neighbour alternatives.\n4. Falsify using the adversarial risk criteria surfaced in the reasoning path.\n"""
221
+ return chat_html, connectome_html, timeline_html, cards_html, hypothesis, build_models_html(model_name)
222
 
223
  CSS = r'''
224
+ :root {
225
+ --bg: #08090c;
226
+ --bg2: #0d1015;
227
+ --panel: rgba(17,20,27,.78);
228
+ --panel-solid: #0f131a;
229
+ --panel-2: rgba(21,26,35,.86);
230
+ --line: rgba(255,255,255,.08);
231
+ --soft: rgba(255,255,255,.55);
232
+ --text: #f5f7fb;
233
+ --muted: #8f99ab;
234
+ --gold: #f2d293;
235
+ --teal: #7be7dd;
236
+ --blue: #7ea9ff;
237
+ --shadow: 0 20px 60px rgba(0,0,0,.38);
238
+ --radius: 22px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  }
240
+ html, body, .gradio-container {
241
+ background: radial-gradient(circle at top left, rgba(120,144,255,.14), transparent 26%),
242
+ radial-gradient(circle at 90% 10%, rgba(242,210,147,.11), transparent 22%),
243
+ linear-gradient(180deg, #050608 0%, #090b10 50%, #07080c 100%) !important;
244
+ color: var(--text) !important;
245
+ font-family: Inter, ui-sans-serif, system-ui, sans-serif;
 
 
 
 
 
 
246
  }
247
+ .gradio-container {max-width: 1550px !important; padding: 22px !important;}
248
+ #dvnc-shell {border: 1px solid var(--line); border-radius: 28px; overflow: hidden; background: linear-gradient(180deg, rgba(14,17,24,.86), rgba(7,8,12,.9)); box-shadow: var(--shadow); backdrop-filter: blur(18px);}
249
+ #dvnc-shell .wrap {display:grid; grid-template-columns: 1.12fr .88fr; min-height: 84vh;}
250
+ #dvnc-shell .left, #dvnc-shell .right {padding: 24px;}
251
+ #dvnc-shell .left {border-right: 1px solid var(--line); display:flex; flex-direction:column; gap:18px;}
252
+ #dvnc-shell .right {display:grid; grid-template-rows:auto auto 1fr auto; gap:18px;}
253
+ .hero-bar {display:flex; justify-content:space-between; align-items:center; gap:16px; padding-bottom:8px;}
254
+ .brand {display:flex; align-items:center; gap:14px;}
255
+ .logo {width:42px; height:42px; border-radius:14px; background: linear-gradient(135deg, rgba(242,210,147,.28), rgba(123,231,221,.16)); border:1px solid rgba(255,255,255,.1); display:grid; place-items:center; box-shadow: inset 0 1px 0 rgba(255,255,255,.08);}
256
+ .logo svg {width:24px; height:24px; color: var(--gold);}
257
+ .brand h1 {font-size: 1.05rem; margin:0; font-weight: 650; letter-spacing:.02em;}
258
+ .brand p {margin:3px 0 0; color:var(--muted); font-size:.84rem;}
259
+ .status {display:flex; gap:10px; align-items:center; color:var(--soft); font-size:.85rem;}
260
+ .status-dot {width:10px; height:10px; border-radius:50%; background:var(--teal); box-shadow:0 0 0 6px rgba(123,231,221,.08), 0 0 18px rgba(123,231,221,.4);}
261
+ .panel {background: linear-gradient(180deg, rgba(17,20,27,.82), rgba(12,14,19,.76)); border:1px solid var(--line); border-radius: 22px; box-shadow: inset 0 1px 0 rgba(255,255,255,.04);}
262
+ .chat-panel {padding:18px; display:flex; flex-direction:column; gap:14px; min-height: 340px;}
263
+ .querybox textarea, .querybox input {background: transparent !important; color: var(--text) !important;}
264
+ .querybox, .querybox > div {background: rgba(255,255,255,.02) !important; border-radius: 18px !important; border-color: var(--line) !important;}
265
+ .chat-thread {display:flex; flex-direction:column; gap:14px;}
266
+ .bubble {max-width: 86%; padding:16px 18px; border-radius: 22px; position:relative; border:1px solid var(--line);}
267
+ .bubble p {margin:8px 0 0; line-height:1.6; font-size:.96rem;}
268
+ .bubble .role {font-size:.72rem; letter-spacing:.12em; text-transform:uppercase; color:var(--muted);}
269
+ .bubble-user {align-self:flex-end; background: linear-gradient(135deg, rgba(126,169,255,.18), rgba(126,169,255,.08));}
270
+ .bubble-ai {align-self:flex-start; background: linear-gradient(135deg, rgba(255,255,255,.06), rgba(255,255,255,.03));}
271
+ .bubble-system {align-self:flex-start; background: linear-gradient(135deg, rgba(242,210,147,.12), rgba(242,210,147,.04));}
272
+ .model-switcher {display:grid; grid-template-columns:repeat(3,1fr); gap:12px;}
273
+ .model-pill {padding:14px; border:1px solid var(--line); border-radius:18px; background:rgba(255,255,255,.02); display:flex; flex-direction:column; gap:4px; min-height: 96px;}
274
+ .model-pill.active {border-color: rgba(242,210,147,.45); background: linear-gradient(135deg, rgba(242,210,147,.14), rgba(255,255,255,.03)); box-shadow: inset 0 0 0 1px rgba(242,210,147,.08);}
275
+ .model-name {font-weight:650;}
276
+ .model-tag {font-size:.76rem; text-transform:uppercase; letter-spacing:.12em; color:var(--gold);}
277
+ .model-pill small {color:var(--muted); line-height:1.45;}
278
+ .brain-shell {padding:18px; height:100%;}
279
+ .brain-header {display:flex; justify-content:space-between; align-items:flex-end; gap:16px; margin-bottom:10px;}
280
+ .eyebrow {font-size:.72rem; letter-spacing:.16em; text-transform:uppercase; color:var(--gold); margin:0 0 4px;}
281
+ .brain-header h3 {margin:0; font-size:1.12rem;}
282
+ .brain-legend {display:flex; gap:14px; color:var(--muted); font-size:.8rem; flex-wrap:wrap;}
283
+ .dot {width:10px; height:10px; display:inline-block; border-radius:50%; margin-right:6px;}
284
+ .dot-live {background:var(--gold); box-shadow:0 0 12px rgba(242,210,147,.65);} .dot-node {background:var(--teal);}
285
+ .brain-stage {position:relative; min-height: 360px; border-radius:20px; overflow:hidden; background: radial-gradient(circle at center, rgba(126,169,255,.08), transparent 35%), linear-gradient(180deg, rgba(255,255,255,.02), rgba(255,255,255,.01)); border:1px solid rgba(255,255,255,.05);}
286
+ .orb {position:absolute; border-radius:50%; filter: blur(40px); opacity:.28; pointer-events:none;}
287
+ .orb-a {width:180px; height:180px; background:rgba(123,231,221,.16); left:10%; top:18%;}
288
+ .orb-b {width:220px; height:220px; background:rgba(242,210,147,.12); right:8%; bottom:12%;}
289
+ .brain-svg {width:100%; height:100%; min-height:360px; transform: perspective(1200px) rotateX(10deg) rotateY(-8deg) scale(1.02);}
290
+ .edge {stroke: rgba(132,153,184,.18); stroke-width:2.2;}
291
+ .edge.active {stroke: var(--gold); stroke-width:3.4; filter:url(#glow); stroke-dasharray: 8 8; animation: pulseEdge 2.8s linear infinite;}
292
+ .node {fill:#7ea9ff; opacity:.88;}
293
+ .node.core {fill:#f5f7fb;} .node.domain {fill:#7be7dd;} .node.bridge {fill:#8ea4ff;} .node.mechanism {fill:#f2d293;} .node.outcome {fill:#ffd9a6;} .node.candidate {fill:#ddadff;}
294
+ .node.active {stroke:#fff7df; stroke-width:2.4; filter:url(#glow);}
295
+ .halo {fill:none;} .halo.active {stroke: rgba(242,210,147,.22); stroke-width:10;}
296
+ .label {fill: rgba(245,247,251,.68); font-size: 12px; letter-spacing: .02em;} .label.active {fill: #fff8e8;}
297
+ .timeline {display:flex; flex-direction:column; gap:10px;}
298
+ .agent-step {display:grid; grid-template-columns:42px 1fr; gap:12px; padding:12px; border:1px solid var(--line); border-radius:18px; background:rgba(255,255,255,.02);}
299
+ .agent-index {width:42px; height:42px; border-radius:14px; display:grid; place-items:center; font-weight:700; color:var(--gold); background:rgba(242,210,147,.08); border:1px solid rgba(242,210,147,.16);}
300
+ .agent-head {display:flex; justify-content:space-between; gap:12px; align-items:center; margin-bottom:4px;}
301
+ .agent-head h4 {margin:0; font-size:.98rem;} .agent-head span {font-size:.72rem; letter-spacing:.12em; text-transform:uppercase; color:var(--muted);} .agent-copy p {margin:0; color:#cbd3e2; font-size:.9rem; line-height:1.55;}
302
+ .candidate-grid {display:grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap:14px;}
303
+ .candidate-card {background:none; perspective:1200px; min-height: 250px;}
304
+ .candidate-card-inner {position:relative; width:100%; height:100%; min-height:250px; transition: transform .8s cubic-bezier(.2,.7,.1,1); transform-style:preserve-3d;}
305
+ .candidate-card:hover .candidate-card-inner, .candidate-card:focus .candidate-card-inner, .candidate-card:focus-within .candidate-card-inner {transform: rotateY(180deg) translateY(-4px);}
306
+ .candidate-face {position:absolute; inset:0; padding:18px; border-radius:22px; border:1px solid var(--line); background: linear-gradient(180deg, rgba(19,22,29,.96), rgba(11,13,18,.96)); backface-visibility:hidden; box-shadow: inset 0 1px 0 rgba(255,255,255,.04), var(--shadow); display:flex; flex-direction:column; gap:12px;}
307
+ .candidate-back {transform: rotateY(180deg); background: linear-gradient(180deg, rgba(29,23,16,.96), rgba(12,11,10,.96));}
308
+ .candidate-top {display:flex; justify-content:space-between; align-items:center; gap:8px;}
309
+ .chip {font-size:.72rem; text-transform:uppercase; letter-spacing:.12em; color:var(--teal); padding:7px 10px; border-radius:999px; background:rgba(123,231,221,.08); border:1px solid rgba(123,231,221,.18);} .chip.alt {color:var(--gold); background:rgba(242,210,147,.08); border-color: rgba(242,210,147,.18);}
310
+ .score {font-weight:700; color:var(--gold);}
311
+ .candidate-face h4 {margin:0; font-size:1rem; line-height:1.3;}
312
+ .candidate-face p {margin:0; color:#cbd3e2; line-height:1.6; font-size:.92rem;}
313
+ .meta-row {margin-top:auto; display:flex; justify-content:space-between; color:var(--muted); font-size:.84rem;}
314
+ .mini {margin-top:8px; align-self:flex-start; color:var(--text); padding:10px 12px; border-radius:14px; border:1px solid var(--line); background:rgba(255,255,255,.03);}
315
+ .prosebox {padding:18px; white-space:pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; line-height:1.55; color:#dfe6f3;}
316
+ .gr-button-primary {background: linear-gradient(135deg, rgba(242,210,147,.92), rgba(224,178,92,.92)) !important; color:#1b1408 !important; border: none !important;}
317
+ .gr-button-secondary {background: rgba(255,255,255,.05) !important; color: var(--text) !important; border: 1px solid var(--line) !important;}
318
+ footer {display:none !important;}
319
+ @keyframes pulseEdge {to {stroke-dashoffset: -32;}}
320
+ @media (max-width: 1180px){#dvnc-shell .wrap{grid-template-columns:1fr;}#dvnc-shell .left{border-right:0;border-bottom:1px solid var(--line)}.candidate-grid,.model-switcher{grid-template-columns:1fr;} }
321
  '''
322
 
323
  HEAD = '''
324
  <link rel="preconnect" href="https://fonts.googleapis.com">
325
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
326
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  '''
328
 
329
  with gr.Blocks(css=CSS, head=HEAD, theme=gr.themes.Base(), fill_height=True) as demo:
330
  gr.HTML('''
331
+ <div id="dvnc-shell">
332
+ <div class="wrap">
333
+ <section class="left">
334
+ <div class="hero-bar">
335
+ <div class="brand">
336
+ <div class="logo" aria-hidden="true">
337
+ <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>
338
+ </div>
339
+ <div>
340
+ <h1>DVNC.AI</h1>
341
+ <p>Sovereign discovery interface · connectome-native reasoning</p>
342
+ </div>
343
  </div>
344
+ <div class="status"><span class="status-dot"></span><span>Live orchestration</span></div>
345
  </div>
 
 
 
 
 
 
 
 
 
 
 
346
  ''')
347
+ model_html = gr.HTML(build_models_html("DVNC Sovereign"))
348
+ with gr.Row():
349
+ model = gr.Dropdown(choices=[m["name"] for m in MODELS], value="DVNC Sovereign", label="Model tier")
350
+ query = gr.Textbox(label="Discovery query", elem_classes=["querybox"], placeholder="Enter a scientific question, anomaly, or breakthrough direction…", lines=4)
 
 
 
 
351
  with gr.Row():
352
  run_btn = gr.Button("Run discovery", variant="primary")
353
  example_btn = gr.Button("Load example", variant="secondary")
354
+ chat = gr.HTML('<div class="panel chat-panel"><div class="chat-thread"><div class="bubble bubble-ai"><span class="role">DVNC</span><p>Enter a query to activate the 7-agent discovery stack and illuminate the chosen path through the concept brain.</p></div></div></div>')
355
  gr.HTML('</section><section class="right">')
356
+ connectome = gr.HTML(build_connectome_html(DEFAULT_PATH))
357
+ timeline = gr.HTML('<div class="panel" style="padding:18px"><div class="timeline"></div></div>')
358
+ cards = gr.HTML('<div class="panel" style="padding:18px"><div class="candidate-grid"></div></div>')
359
+ output = gr.Markdown("# Discovery Output\n\nAwaiting query.")
360
  gr.HTML('</section></div></div>')
361
 
362
+ def load_example():
363
+ return "How could a self-assembling conductive biomaterial improve cardiac tissue regeneration by converting mechanical strain into repair signalling?"
364
+
365
+ example_btn.click(fn=load_example, outputs=query)
366
+ run_btn.click(fn=run_discovery, inputs=[query, model], outputs=[chat, connectome, timeline, cards, output, model_html])
367
 
368
+ if __name__ == "__main__":
369
+ demo.launch()